AI agent governance - audit trails, policy enforcement, compliance
Project description
asqav
Python SDK for asqav.com, the evidence layer for AI agents. All ML-DSA cryptography runs server-side. Drop-in audit trails, approvals, policy gates, and compliance reports.
Install
pip install asqav
Quick start
import asqav
asqav.init(api_key="sk_...")
agent = asqav.Agent.create("my-agent")
sig = agent.sign(
"payment.wire_transfer",
{"amount_eur": 850000, "beneficiary_iban": "DE89370400440532013000"},
receipt_type="protectmcp:decision",
risk_class="high",
issuer_id="legal:Acme GmbH",
iteration_id="task-2026-Q2-4821",
)
print(sig.compliance_mode) # True (default; pass compliance_mode=False to opt out)
print(sig.action_ref) # "sha256:..." over the JCS-canonical action
print(sig.previous_receipt_hash) # 64 hex; "0"*64 on the first record per agent
print(sig.verification_url)
Each signed action lands on a Compliance Receipt under IETF Internet-Draft draft-marques-asqav-compliance-receipts by default: ML-DSA-65 (FIPS 204) signature, chain hash, retained policy_digest, fail-closed anchoring, and a public verification URL. Pass compliance_mode=False if you want a non-Compliance receipt.
CLI
The package ships an asqav CLI mirroring the Python API. Set ASQAV_API_KEY and run:
asqav verify <signature_id> [--output json] # IETF axes when present
asqav sign --agent-id ID --action-type T --action-json action.json \
--compliance-mode --receipt-type protectmcp:decision \
--risk-class high --issuer-id legal:Acme
asqav agents list / create / revoke
asqav sessions list / end
asqav replay <agent_id> <session_id> # Pro
asqav replay-verify <agent_id> <session_id> [--strict] # IETF chain
asqav preflight <agent_id> <action_type> # Pro
asqav budget check / record # Pro
asqav approve <session_id> <entity_id> # Pro
asqav compliance frameworks / export # Business
asqav audit-pack export --start ISO --end ISO --output-file bundle.json
asqav audit-pack policy <sha256:hex>
asqav payloads erase <signature_id> # P4: GDPR right-to-erasure
asqav org set-compliance-strict <org_id> --enable|--disable
asqav keys generate --algorithm ed25519|es256 [--out priv.pem]
asqav migrate run v3-20|v3-21|v3-22 # X-Maintenance-Key required
asqav policies / webhooks list / create / delete # Pro
Pro and Business commands are gated client-side via GET /account so a free-tier key gets a clean upgrade message instead of a mid-pipeline 402.
The IETF Compliance Receipts profile commands (sign --compliance-mode, audit-pack export, audit-pack policy, payloads erase, replay-verify --strict, org set-compliance-strict) match the SDK kwargs on Agent.sign(...) and verify_compliance_receipt(...). See docs/CLI.md for full flag reference.
Roadmap
Six-line view of what is shipped on Asqav:
- Hash-only mode for cloud - Today (default for
*.asqav.com). - Self-hosted signer (split-trust) - Today.
- Bring-your-own KMS (AWS KMS / GCP KMS) - Today, Enterprise tier.
- Customer-owned storage - Today (self-hosted; relay payload allowlist enforced in code).
- SCITT / COSE_Sign1 receipt export - Today (public
GET /api/v1/signatures/{id}/cosereturnsapplication/cose). - Air-gapped / on-prem mode - Today (offline license + zero-egress, see
docs/airgapped-mode.mdin the backend repo).
See the docs at https://asqav.com/docs for the current feature set.
Standards
Asqav's compliance receipts are profiled in IETF Internet-Draft draft-marques-asqav-compliance-receipts, profiling the upstream draft-farley-acta-signed-receipts for EU AI Act Articles 12 and 26, and DORA Article 17 bindings.
Compliance receipts (IETF profile)
Compliance Receipts are the SDK default. Each agent.sign(...) call produces a receipt that conforms to draft-marques-asqav-compliance-receipts: ML-DSA-65 signature, RFC 3161 + OpenTimestamps anchors, retained policy_digest, hash-chained previousReceiptHash. Opt out with compliance_mode=False if you want the older shape.
The four envelope extensions most callers reach for:
receipt_type-protectmcp:decision,protectmcp:restraint,protectmcp:lifecycle,protectmcp:lifecycle:configuration_change,protectmcp:acknowledgment,protectmcp:observation, orprotectmcp:observation:result_bound(observation receipts that bind tool output viaresult_digest).risk_class- controlled vocabulary:unacceptable | high | limited | minimal | gpai | low | medium | unknown.iteration_id- logical task id, distinct from session.sandbox_state-enabled | disabled | unavailablefor high-risk gating.incident_class- DORA / NYDFS / CIRCIA token (or array of tokens).issuer_id- LEI (ISO 17442), EIN, CIK, or a W3C DID for non-LEI deployers.
Shadow AI capture (passive_telemetry)
Two receipt_type values cover the gating axis: protectmcp:decision records that a policy ran and gated the action; protectmcp:observation records that a passive monitor saw the event without gating it. Pick observation when the producer never had the option to block (SIEM forwarder, browser extension in observe-only mode, NetFlow-style proxy with no enforcement hook).
Set capture_topology='passive_telemetry' to declare the producer is observing after the fact. The SDK client-side check pre-flights the Asqav cloud's full rule 8 gate: a capture_topology='passive_telemetry' receipt MUST use receipt_type='protectmcp:observation'. Any other receipt_type paired with passive_telemetry (:decision, :restraint, :lifecycle, :lifecycle:configuration_change, :acknowledgment) raises ValueError with the verbatim false_attestation_guard: capture_topology=passive_telemetry receipts must use receipt_type=protectmcp:observation, not :<offending> (rule 8) message before the HTTP roundtrip (see false_attestation_guard in python/src/asqav/client.py).
sig = agent.sign(
"mcp:tool_call",
{"server": "filesystem", "tool": "read"},
receipt_type="protectmcp:observation",
capture_topology="passive_telemetry",
issuer_id="legal:Acme GmbH",
)
capture_topology is stamped on the audit-pack manifest entry but never on the signed payload. The other accepted topologies are in_process_sdk, network_proxy, browser_extension, ebpf_observer, and mcp_proxy; only passive_telemetry triggers the false-attestation guard. The full topology semantics live in the cloud's docs/capture-topology.md, and the wire vocabulary is published live at https://api.asqav.com/.well-known/governance.json for discovery.
Configuration change receipts (rule 9)
A receipt_type='protectmcp:lifecycle:configuration_change' receipt declares the agent's runtime configuration was mutated. The SDK pre-flights the Asqav cloud's rule 9 cross-field gate (NSA CSI U/OO/6030316-26 alignment): the receipt MUST carry config_manifest_digest. Omitting it raises ValueError with the verbatim false_attestation_guard: receipt_type=protectmcp:lifecycle:configuration_change requires config_manifest_digest (rule 9) message before the HTTP roundtrip.
sig = agent.sign(
"mcp:config_update",
{"server": "filesystem", "delta": "tool_added"},
receipt_type="protectmcp:lifecycle:configuration_change",
policy_decision="none",
config_manifest_digest="sha256:<hex of manifest>",
cve_inventory_digest="sha256:<hex of cve snapshot>",
)
Five new wire fields land on sign() for NSA CSI alignment: result_digest (tool output hash), expires_at (validity horizon), tool_fingerprint (sha256:<hex> over {tool_name, schema} - auto-derived when tool_name + tool_schema are present), config_manifest_digest and cve_inventory_digest. The nonce field is auto-generated as 12 random bytes when the caller omits one; the cloud verifier rejects duplicates inside the validity window.
Expiry precedence (rule 10)
valid_seconds (legacy, server computes valid_until = signed_at + valid_seconds) and expires_at (caller-supplied horizon) are mutually exclusive. Pass exactly one of the two: valid_seconds=3600 for "expire one hour after signing", or expires_at="2026-06-01T00:00:00Z" for an explicit horizon. Passing both raises ValueError with the verbatim expiry_collision_guard: pass either valid_seconds or expires_at, not both (rule 10) message before the HTTP roundtrip. Passing neither falls back to the server-side default (valid_seconds=86400).
Digest format (rule 11)
Every caller-supplied digest field (tool_fingerprint, config_manifest_digest, cve_inventory_digest) MUST match the regex ^sha256:[a-f0-9]{64}$. Anything else raises ValueError with the verbatim digest_format_guard: <field> must match sha256:<64-hex> (rule 11) message before the HTTP roundtrip. To avoid wire drift, use the SDK's deterministic helpers (each is byte-deterministic under JCS / RFC 8785):
from asqav.client import (
_compute_tool_fingerprint,
_compute_config_manifest_digest,
_compute_cve_inventory_digest,
)
fp = _compute_tool_fingerprint("search", {"args": {"q": "string"}})
cfg = _compute_config_manifest_digest({"server": "filesystem", "tools": ["read"]})
cve = _compute_cve_inventory_digest([{"id": "CVE-2026-0001", "severity": "high"}])
Result-bound observation receipts
The protectmcp:observation:result_bound receipt_type variant signals an observation receipt that carries result_digest (a sha256:<hex> of the tool output). Use it when the producer wants to bind the receipt to a specific tool result without claiming the policy gated the call. The cloud accepts the same vocabulary; auditors can index result-bound observations without a wider scan.
Audit Pack export
The cloud signs a Compliance Audit Pack (per IETF -03 Section 7) over a window of receipts. The SDK wraps the endpoint:
pack = asqav.fetch_audit_pack(start="2026-05-01T00:00Z", end="2026-06-01T00:00Z")
print(pack["bundle_digest"]) # sha256:<hex>
print(pack["bundle_signature"]) # base64 ML-DSA-65 sig over the bundle
print(pack["regime_mapping"]) # {regime_token: [record_id, ...]}
print(pack["algorithm_registry_version"]) # registry version pinned at issuance
asqav.export_bundle(signatures, framework="dora") is the offline alternative for air-gapped flows: it computes a Merkle root over an in-memory list of receipts without calling the cloud. Use fetch_audit_pack whenever the cloud is reachable, since only the cloud signature gives the auditor a tamper-evident manifest.
Local-side sanity checks (presence of REQUIRED fields, namespace, 300s skew bound, predecessor rederivation) are available as asqav.verify_compliance_receipt(envelope, predecessor_envelope=...). The cloud is the authoritative verifier; this helper is a convenience.
Algorithm agility per profile section 10.8 is exposed via asqav.SUPPORTED_ALGORITHMS. Pass algorithm="ed25519" or "es256" to Agent.create(...) for non-post-quantum identities, or asqav.generate_local_keypair("ed25519") for offline scenarios.
Documentation
- Repository: https://github.com/jagmarques/asqav-sdk
- Full docs: https://asqav.com/docs
License
MIT. Get an API key at asqav.com.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file asqav-0.5.0.tar.gz.
File metadata
- Download URL: asqav-0.5.0.tar.gz
- Upload date:
- Size: 97.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.9.15 {"installer":{"name":"uv","version":"0.9.15","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
69581cbb16350ca176517d6dba9e06076ba4aff03a3d0bfa3e4cc176ebc5fd36
|
|
| MD5 |
757161928bea090d9ea369afa6281df8
|
|
| BLAKE2b-256 |
cfbaad657f86bb653d08d03558f55d92ba37a5de70a22ab458c25abc1b492741
|
File details
Details for the file asqav-0.5.0-py3-none-any.whl.
File metadata
- Download URL: asqav-0.5.0-py3-none-any.whl
- Upload date:
- Size: 115.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.9.15 {"installer":{"name":"uv","version":"0.9.15","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
80586190505a7c6bba23f93670ac5d296b355a6ec9129cf17ead9647edccb207
|
|
| MD5 |
ceb7afa0eb9affddc86e05460c681380
|
|
| BLAKE2b-256 |
86bd566f5883264b18910a99bbd6c75fc400369880ba6cfe7369ebcab171b698
|