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(...)andQuesenClient.create_sandbox_key()both callPOST /sandbox/keysfor a free, rate-limited sandbox key. For production volume, pass your ownapi_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(routePOST /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_receiptreports 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, withclient_request_idexcluded 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 ofShxnque/quesenHEAD 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_idpropagation — the UUID emitted by/validateis what you pass to/report.X-Request-IDheader — set once, echoed everywhere, useful for tracing across your stack./simulatehelper for the free counterfactual sales asset.- Receipt provenance surfaced as typed fields —
input_snapshot_hash+commit_shaare first-class attributes onValidateResult(v0.2.0+). - Fail-closed policy — timeouts / network errors surface as
QuesenTimeoutandQuesenTransportErrorso the caller can decide (recommendation: treat asSKIP). - 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.
httpxonly. - Fail-closed. Timeouts and network errors surface as exceptions. Callers should treat them as
SKIP. - Request-ID propagation. Every call sets
X-Request-IDso your/reportcalls are correlatable to the original/validate. - Receipt provenance forwarded.
input_snapshot_hash+commit_shaare 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)
| File | Size | Uploaded | |
|---|---|---|---|
| quesen_sdk-0.7.0.tar.gz | 42.2 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| 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
|