vda-witness (Python SDK)
Seal your AI agent's decisions into tamper-evident, Ed25519-signed, independently-verifiable evidence — the records EU AI Act Article 12 requires. Part of the Verified Digital Agents (VDA) platform; VDA Witness is its evidence layer.
- Fail-open: sealing never raises and never blocks your agent.
- Zero-config, zero-dependency: one import, set
WITNESS_API_KEY, standard library only. - Independently verifiable: anyone can verify a record offline, without trusting VDA.
⚠️ Read this first — what you get out of the box (custody)
Out of the box, sealing goes to the hosted Witness service on the custodial / ephemeral (test) tier: records are signed by the Witness service, not by a key you control. Every such record honestly carries custody: "custodial" and says so in plain text.
This is for building and testing. It is NOT customer-controlled signing and is NOT compliance-grade EU AI Act Article-12 evidence — do not present ephemeral custodial records to an auditor as your own controlled evidence.
For compliance-grade evidence, use customer-managed signing — you control the private key, records carry custody: "customer-managed" ("customer-controlled signing"), and Witness never sees your key. That is the tier you put in front of an auditor.
| Tier | Who holds the key | Record label | Use for |
|---|---|---|---|
| Custodial (default/ephemeral) | Witness service | custodial |
build & test |
| Customer-managed | You | customer-managed |
compliance-grade evidence |
Install
pip install vda-witness # sealing — zero dependencies (standard library only)
pip install "vda-witness[verify]" # + offline verification (adds cryptography + asn1crypto)
First seal (copy-paste — works as-is)
from vda_witness import Witness
witness = Witness() # reads WITNESS_API_KEY
witness.seal(
decision={"agent": "Refund Agent", "inputs": {"amount_eur": 150},
"verdict": "PASS", "reasoning": "<= 200 and account in good standing"},
governing_rule={"ruleId": "refund.auto",
"ruleText": "Agents MAY auto-approve refunds up to EUR200 where the account is in good standing."},
)
Decorate an existing agent (code unchanged beyond the decorator)
@witness.witnessed(
to_decision=lambda args, kwargs, result: {
"agent": "Refund Agent", "inputs": kwargs,
"verdict": result["verdict"], "reasoning": result["why"],
},
rule={"ruleId": "refund.auto", "ruleText": "Agents MAY auto-approve refunds up to EUR200 ..."},
)
def decide(**request):
... # your agent, untouched — sealed automatically after it returns (fail-open, bounded)
Framework adapters
from vda_witness import witness_openai
witness_openai(client, witness, {"ruleId": "assistant.sop", "ruleText": "..."}) # seals each chat.completions.create
Anthropic, LangChain, CrewAI, and the Vercel AI SDK use the same wrap-and-seal pattern (witness.witnessed(...)); typed adapters ship per release.
Verify a record offline — the moat (v1.0)
Independent, three-state verification with zero calls to a Witness server (needs the [verify] extra). It checks the Ed25519 signature + hash-chain + the historical DID key, and the anchor against public infrastructure (Sigstore Rekor + an RFC-3161 TSA). Pure Python — no openssl shell (cryptography + asn1crypto).
verdict = witness.verify_offline(record, chain, did_document, anchor=anchor) # mode="bundled" | "live-public"
# verdict["state"] is one of:
# "ANCHORED_VALID" — signed, chained, and the head is anchored (Rekor + >=1 TSA)
# "SIGNED_PENDING" — signed + chained, honestly not-yet-anchored (within cadence)
# "BROKEN" — verdict["reason"] in {signature, chain, key, anchor, malformed}
if verdict["state"] == "ANCHORED_VALID":
... # trust it
- Modes:
mode="bundled"(air-gappable — verifies the captured Rekor SET + TSA token with no network) ormode="live-public"(re-fetches the Rekor inclusion fromrekor.sigstore.devdirectly). Neither ever contacts a Witness endpoint;did_documentis always supplied in the bundle. - Anchor quorum: Rekor (required) + >=1 TSA — DigiCert is pinned; Sectigo is not yet pinned, so DigiCert satisfies the TSA leg today.
- Verdicts are byte-for-byte identical to the JS SDK on the same input.
Deprecated:
witness.verify(record=...)(boolean, server round-trip) is now an alias ofwitness.verify_via_server(...)and requires the Witness server — not independent verification. Migrate toverify_offline. A boolean can't expressSIGNED_PENDINGwithout overclaiming or false-alarming.
Options
Witness(api_key=None, base_url="https://witness.getvda.ai", timeout=3.0, fire_and_forget=False, on_error=None). Set fire_and_forget=True for zero added latency (seal on a background thread).
Honest scope: Witness produces the evidence (Art. 12 record-keeping), not a compliance certificate. The Article 12 Evidence Report is generated from your sealed trail; Compliance Officer attestation makes it a regulatory artefact.
License
Apache-2.0 licensed — see LICENSE.
Metadata
Release files for vda-witness 1.0.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 | |
|---|---|---|---|
| vda_witness-1.0.0.tar.gz | 21.2 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| vda_witness-1.0.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 40.9 kB
Release files / vda_witness-1.0.0.tar.gz
| Download URL | vda_witness-1.0.0.tar.gz |
|---|---|
| Size | 21.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
2949e35749af477e4f8df9299bb50765d19d85524283828acb0440eab1e70297
|
|
BLAKE2b-256 checksum How to use checksums |
f76e387c4100187ef6820c9c1a910f6d762c3a040701e7a2e282aba2fc887f25
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.10
|
Release files / vda_witness-1.0.0-py3-none-any.whl
| Download URL | vda_witness-1.0.0-py3-none-any.whl |
|---|---|
| Size | 19.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
c60f9b21dd00aec2d94bba0a2d2ffd2c7aed68d6f9fb163da27538fd325fd70c
|
|
BLAKE2b-256 checksum How to use checksums |
9ec44ca774152ac8fd5af27713e8afdaa1eb88cf6d530520a7ee829b33b57a7c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.10
|