Skip to main content

Quesen Python SDK

Official typed Python client for Quesen — deterministic AI agent authorization & governance infrastructure: a portable decision + authority + evidence layer (runtime authorization for agent tool calls, MCP tool authorization and agent payment authorization) returning PASS/REVIEW/BLOCK/SKIP with machine reason codes and replayable audit receipts.

Status: v0.4.1 · tracks Quesen engine v1.10.0 (+ TSC v2 agent firewall) · backward compatible with v1.0.0+ deployments. Developer portal: senueren.co.za/quesen — canonical docs, API reference, and integration guides. This SDK is a thin HTTP client; the hosted engine is served at https://web-production-3df26.up.railway.app.


Install

pip install quesen-sdk

Python 3.9+, single runtime dependency (httpx).


30-second agent firewall (copy-paste, no signup)

The fastest path from install to a real deterministic decision. QuesenFirewall.sandbox() self-serves a free sandbox key for you (no signup, no card), so this runs as-is:

from quesen_sdk import QuesenFirewall, TscBlocked

# Zero-config: mints a free sandbox key against the hosted engine.
fw = QuesenFirewall.sandbox("https://web-production-3df26.up.railway.app")

# Your agent is about to exfiltrate a secret to an untrusted host. Ask Quesen first.
try:
    fw.require_pass(
        agent="my-agent",
        action="send_data",
        target="https://paste.evil.example",
        data_class="secret",
    )
    send_the_data()            # only runs on an explicit PASS
except TscBlocked as e:
    print(e.decision.decision)      # 'BLOCK'
    print(e.decision.reason_codes)  # ['EGRESS_SECRET_UNTRUSTED']
    print(e.decision.tags)          # ['exfiltration']
    print(e.decision.commit_sha)    # 40-char audit-receipt ruleset pin

A safe action returns PASS instead:

d = fw.check(agent="my-agent", action="tool_call", capability_class="read")
print(d.decision)          # 'PASS'

Getting a key manually. QuesenFirewall.sandbox(...) and QuesenClient.create_sandbox_key() both call POST /sandbox/keys for a free, rate-limited sandbox key. For production volume, pass your own api_key="sk_live_...". The hosted engine requires a key — there is no open mode — but the sandbox key above is issued instantly with no signup.


Lower-level client (scam/A2A risk /validate)

from quesen_sdk import QuesenClient

client = QuesenClient(base_url="https://web-production-3df26.up.railway.app")
client.create_sandbox_key()   # free key, now authenticated

result = client.validate(
    domain_age_days=1,
    engagement_ratio=0.95,
    scam_keyword_count=4,
)

print(result.decision)              # 'SKIP'
print(result.risk_score)            # 1.0
print(result.request_id)            # UUID — pass to client.report() later
print(result.input_snapshot_hash)   # 64-char SHA-256 hex — self-contained replay primitive (v1.10+)
print(result.commit_sha)            # 40-char git SHA of the engine ruleset that produced the verdict (v1.10+)

Agent Firewall (TSC v2) — one call before any high-risk action

TSC v2 turns Quesen into a deterministic agent firewall: describe what your autonomous agent is about to do and get a PASS / REVIEW / BLOCK / SKIP verdict plus a tamper-evident audit receipt — before the action crosses a trust boundary. Secret/credential egress to an untrusted destination is deterministically blocked; suspected prompt injection is sent to review; unauthorized privilege grants are refused.

Requires an engine running with QUESEN_TSC_V2_ENABLED=true (route POST /tsc/validate).

from quesen_sdk import QuesenClient
from quesen_sdk.tsc import TscContext, TscBlocked

client = QuesenClient(base_url="https://web-production-3df26.up.railway.app")
client.create_sandbox_key()   # free key (or pass api_key="sk_live_..." for production)

# Your agent is about to POST data somewhere. Ask Quesen first.
decision = client.validate_tsc(
    TscContext.data_egress(
        data_classes=["secret"],          # what's leaving
        to="https://paste.evil.example",  # where it's going
        destination_trust="unverified",
        framework="langchain",
    )
)

print(decision.decision)       # 'BLOCK'
print(decision.reason_codes)   # ['EGRESS_SECRET_UNTRUSTED']
print(decision.tags)           # ['exfiltration']
print(decision.commit_sha, decision.input_snapshot_hash)  # audit receipt

# Fail-closed gate: raise unless the engine explicitly returned PASS.
try:
    decision.require_pass()
    run_the_tool()             # only reached on PASS
except TscBlocked as e:
    log_and_stop(e.decision)   # BLOCK / REVIEW / SKIP never runs the tool

Scenario builders cover the common catastrophic actions:

Builder Agent is about to…
TscContext.data_egress(...) send data OUT (exfiltration / PII / secret leakage)
TscContext.tool_call(...) invoke a tool/capability (privilege + injection checks)
TscContext.payment(...) move money / perform a financial action

You can also pass a plain dict (or anything with .to_dict()) to client.validate_tsc(...) for full control of the Typed Security Context schema.

Runnable demos: examples/agent_firewall.py (pure SDK) and examples/langchain_firewall.py (LangChain tool wrapper).

Async is symmetric — await AsyncQuesenClient(...).validate_tsc(ctx).

Offline verdict replay — recompute without trusting us (v0.6.0)

Serious integrators asked for one thing before adopting: the ability to recompute the verdict themselves, offline, rather than trust a hosted closed ruleset (an in-process gate has no place to bind a network receipt; a CI-time call to our service adds the exact dependency they are trying to remove). v0.6.0 ships exactly that.

from quesen_sdk import replay, verify_receipt, QuesenFirewall

# Recompute the decision locally — ZERO network, standard library only.
local = replay({
    "tsc_version": "2.0",
    "subject": {"kind": "agent"},
    "action": {"kind": "data_egress"},
    "provenance": {"source": "adapter_derived"},
    "data": {"classes": ["secret"], "egress": {"to": "https://x", "destination_trust": "unverified"}},
})
print(local["decision"], local["reason_codes"], local["input_snapshot_hash"])
# BLOCK ['EGRESS_SECRET_UNTRUSTED'] <sha256>

# Or prove a live receipt is independently reproducible:
fw = QuesenFirewall.sandbox("https://<engine>")
ctx = fw.build_context(action="send_data", target="https://x", data_class="secret")
decision = fw.check(action="send_data", target="https://x", data_class="secret")
v = verify_receipt(decision, recompute_request=ctx)
v.recomputed   # True  -> decision + reasons + input_snapshot_hash reproduced offline
v.require()    # fail-closed: raises on any mismatch

The recompute runs a vendored copy of the public reference evaluator (kept byte-for-byte in lockstep with the engine by a parity test in the engine repo + the public conformance kit). The hosted engine becomes an optimisation, not a trust dependency, for the egress/authority subset.

Honest boundary: this reproduces the contract-level decision/reasons/hash for the egress/authority subset — not the production risk weighting/thresholds. A mismatch means the receipt is not reproducible from the public reference, and verify_receipt reports it.

Receipt provenance (v1.10, tracked in SDK v0.2.0)

Every ValidateResult and SimulateResult.baseline / .simulated now carries two additional fields that make the verdict self-contained-replayable:

  • input_snapshot_hash · lowercase 64-char SHA-256 hex over canonical-JSON of the received request payload, with client_request_id excluded from the hash material. Hash the same payload client-side and prove the engine evaluated the exact input you sent.
  • commit_sha · 40-char lowercase git SHA of Shxnque/quesen HEAD live at build time, or the sentinel "unknown" when running detached HEAD or a locally-built artifact. Pins the exact ruleset that produced the verdict.

Client-side hash reconstruction

import hashlib, json

def input_snapshot_hash(payload: dict) -> str:
    to_hash = {k: v for k, v in payload.items()
               if v is not None and k != "client_request_id"}
    canonical = json.dumps(to_hash, sort_keys=True, separators=(",", ":"),
                           ensure_ascii=False, allow_nan=False).encode("utf-8")
    return hashlib.sha256(canonical).hexdigest()

assert result.input_snapshot_hash == input_snapshot_hash({
    "domain_age_days": 1,
    "engagement_ratio": 0.95,
    "scam_keyword_count": 4,
})

Replay recipe

git clone https://github.com/Shxnque/quesen && cd quesen
git checkout $COMMIT_SHA
pytest tests -q       # asserts engine state at decision time
# Re-issue the request; verify input_snapshot_hash matches.

Backward compatibility. Against a pre-v1.10 engine the two fields default to the empty string. Callers who upgrade the SDK against an older engine continue to work unchanged; callers who upgrade the engine start seeing non-empty values automatically.


Async usage

import asyncio
from quesen_sdk import AsyncQuesenClient

async def main() -> None:
    async with AsyncQuesenClient(base_url="https://web-production-3df26.up.railway.app") as q:
        await q.create_sandbox_key()   # free key (or pass api_key="sk_live_..." above)
        decision = await q.validate(domain_age_days=200, engagement_ratio=0.3)
        if decision.decision == "SKIP":
            return  # don't act
        # ... execute the action ...
        await q.report(request_id=decision.request_id, outcome="OK", realized_pnl=0.42)

asyncio.run(main())

What the client gives you

  • Typed request + response models (dataclass-like, __slots__, IDE-friendly attrs).
  • Automatic retries with exponential back-off on 5xx / network errors.
  • request_id propagation — the UUID emitted by /validate is what you pass to /report.
  • X-Request-ID header — set once, echoed everywhere, useful for tracing across your stack.
  • /simulate helper for the free counterfactual sales asset.
  • Receipt provenance surfaced as typed fields — input_snapshot_hash + commit_sha are first-class attributes on ValidateResult (v0.2.0+).
  • Fail-closed policy — timeouts / network errors surface as QuesenTimeout and QuesenTransportError so the caller can decide (recommendation: treat as SKIP).
  • Zero heavy dependencies — just httpx.

API surface

Sync client: QuesenClient(base_url, api_key=None, timeout=5.0, retries=2, retry_backoff=0.2, request_id_header="X-Request-ID", user_agent="quesen-sdk-py/0.4.1")

Method Wraps Purpose
client.create_sandbox_key() POST /sandbox/keys Self-serve a FREE sandbox key (no signup); auto-applied to the client.
client.validate_tsc(ctx) POST /tsc/validate Agent firewall — PASS/REVIEW/BLOCK/SKIP + audit receipt.
client.health() GET /health Liveness.
client.version() GET /version Engine + weights + thresholds.
client.validate(...) POST /validate Deterministic decision. Response carries input_snapshot_hash + commit_sha against v1.10+ engines.
client.simulate(...) POST /simulate Counterfactual with weights_override / thresholds_override.
client.report(...) POST /report Post-decision outcome feedback. v1.1.0 optional fields supported.

Async client: AsyncQuesenClient(...) mirrors the sync surface with async def methods.


Error hierarchy

QuesenError
├── QuesenAuthError        # 401 — invalid or missing X-API-Key
├── QuesenRateLimitError   # 429 — per-key quota exceeded, Retry-After surfaced
├── QuesenValidationError  # 422 — pydantic-side reject
├── QuesenServerError      # 5xx after retries exhausted
├── QuesenTimeout          # transport timeout
└── QuesenTransportError   # generic transport failure

Environment variables

Var Meaning
QUESEN_BASE_URL Optional default base URL if not passed to the client constructor.
QUESEN_API_KEY Optional default API key if not passed to the client constructor.

Doctrine compliance

This SDK preserves Quesen doctrine end-to-end:

  • Determinism. The SDK does not add ML, prompts, randomness, or state. Same input in → same input out.
  • Ecosystem neutrality. No chain lock-in, no framework lock-in, no LLM lock-in. httpx only.
  • Fail-closed. Timeouts and network errors surface as exceptions. Callers should treat them as SKIP.
  • Request-ID propagation. Every call sets X-Request-ID so your /report calls are correlatable to the original /validate.
  • Receipt provenance forwarded. input_snapshot_hash + commit_sha are exposed as typed fields (v0.2.0+), enabling client-side replay-verification and ruleset-pin discipline.

License

MIT.

Metadata

Release files for quesen-sdk 0.7.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for quesen-sdk 0.7.0
File Size Uploaded
quesen_sdk-0.7.0.tar.gz 42.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for quesen-sdk 0.7.0
File Interpreter ABI Platform
quesen_sdk-0.7.0-py3-none-any.whl Python 3 none any Details

Total release size: 78.7 kB

Release files / quesen_sdk-0.7.0.tar.gz

Download URL quesen_sdk-0.7.0.tar.gz
Size 42.2 kB
Tags Source
SHA-256 checksum
How to use checksums
c9ba734a0131f41b8c98632e4e02ed31febc5ef7b3e7ee260f434f1aafa75874
BLAKE2b-256 checksum
How to use checksums
a6f597c912a78014381bd25877f798ce790e448a02699013f66b50cdcf23ee62
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.11.16

Release files / quesen_sdk-0.7.0-py3-none-any.whl

Download URL quesen_sdk-0.7.0-py3-none-any.whl
Size 36.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
c7d4694bd446621d8e43950560da50493d9c6febb4da2ebe9a3d6d705a41261b
BLAKE2b-256 checksum
How to use checksums
4c7280ea1d5473be2ab67422bd4c384ac8acfef78d4cfefac904687567acde2b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.11.16

Release history Release notifications | RSS feed

This release

0.7.0 This release

2 release files

0.6.1

2 release files

0.6.0

2 release files

0.5.0

2 release files

0.4.1

2 release files

0.4.0

2 release files

0.3.0

2 release files

0.2.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page