Metadata-Version: 2.4
Name: queldrex
Version: 0.2.0
Summary: Neutral trust layer for AI agents: in-path enforcement + offline-verifiable signed receipts.
Project-URL: Homepage, https://queldrex.com/verify
Project-URL: Documentation, https://queldrex.com/verify/api
Author: Queldrex
License: MIT
Keywords: agent,ai,enforcement,governance,langchain,mcp,prompt-injection,security,zero-trust
Requires-Python: >=3.8
Provides-Extra: verify
Requires-Dist: cryptography>=3.4; extra == 'verify'
Description-Content-Type: text/markdown

# queldrex (Python)

The neutral trust layer for AI agents, for Python. Put Queldrex in the path of
your agent so every tool call is authorized **before** it runs (allow / require
approval / deny), scan tool output for injected instructions, and verify the
signed receipts offline. Zero dependencies for enforcement; `cryptography` is an
optional extra only for offline receipt verification.

Works with LangChain, LlamaIndex, the MCP Python SDK, or any custom agent loop.

```bash
pip install queldrex            # enforcement, zero deps
pip install "queldrex[verify]"  # + offline receipt verification
```

## Enforce a tool call

```python
from queldrex.enforce import Enforcer

q = Enforcer(api_key="qdx_...")          # free key from queldrex.com/console

# Ask before the tool runs. Pass the real call args so a poisoned value on a
# clean tool (an SSRF metadata URL, an SSH-key path) is caught, not just a
# poisoned tool definition.
d = q.authorize(
    {"name": "fetch_url", "description": "Fetches a URL"},
    args={"url": "http://169.254.169.254/latest/meta-data/"},
)
if d["decision"] != "allow":
    ...  # block, or route to a human. d["reasons"] explains why.

# Or throw on a block:
q.guard(tool, args=call_args)            # raises EnforcementDenied unless allowed

# Or wrap a tool callable so every call is authorized first:
safe_fn = q.wrap_tool(descriptor, my_tool_fn)
```

**Async agents** (asyncio, LangChain, the MCP SDK) use the `_async` variants,
which never block the event loop and need no extra dependency:

```python
d = await q.authorize_async(tool, args=call_args)
await q.guard_async(tool, args=call_args)
res = await q.scan_output_async(tool_result)
```

**Fail-safe by design.** If Queldrex is unreachable it never silently allows; it
degrades to your `fail_mode` (default `"ask"` → require approval).

## Scan tool output (indirect prompt injection)

```python
res = q.scan_output(tool_result, source="web_fetch")
if not res["safe"]:
    ...  # treat as untrusted DATA, do not follow instructions inside it
```

High-confidence secrets (AWS, Stripe, GitHub, OpenAI, Slack, JWT, ...) are
redacted **locally** before any output is sent, so credentials never leave your
machine.

## Verify a receipt offline (don't trust us, verify us)

```python
from queldrex.verify import verify_receipt, is_expired, verify_issuer

verify_receipt(receipt)          # Ed25519 signature over the canonical payload
is_expired(receipt)              # past its expiresAt?
verify_issuer(receipt)           # is the key Queldrex's published key? (1 network call)
```

The receipt format is an open spec: <https://queldrex.com/verify/spec>. MIT.
