vda-witness
v1.0.0
Published
VDA Witness SDK — seal your AI agent's decisions into tamper-evident, Ed25519-signed, independently-verifiable evidence (EU AI Act Art. 12), and verify records OFFLINE against public infrastructure. Fail-open, zero-dependency sealing. Part of the Verified
Maintainers
Readme
vda-witness (JS/TS 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 throws and never blocks your agent. If Witness is unreachable, your agent keeps working.
- Zero-config: one import, set
WITNESS_API_KEY, done. - 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
npm i vda-witnessFirst seal (copy-paste — works as-is)
import { Witness } from "vda-witness";
const witness = new Witness({ apiKey: process.env.WITNESS_API_KEY });
await witness.seal(
{ agent: "Refund Agent", inputs: { amountEur: 150 }, verdict: "PASS", reasoning: "≤ €200 and account in good standing." },
{ ruleId: "refund.auto", ruleText: "Agents MAY auto-approve refunds up to €200 where the account is in good standing." },
);Wrap an existing agent (code unchanged beyond the wrapper)
const decide = witness.witnessed(myAgentFn, {
toDecision: (args, result) => ({
agent: "Refund Agent",
inputs: args[0],
verdict: result.verdict,
reasoning: result.reason,
}),
rule: { ruleId: "refund.auto", ruleText: "Agents MAY auto-approve refunds up to €200 …" },
});
const result = await decide(request); // runs your agent, then seals — fail-open, boundedFramework adapters
import { witnessOpenAI, witnessAnthropic } from "vda-witness";
witnessOpenAI(openai, witness, { ruleId: "assistant.sop", ruleText: "…" }); // seals each chat.completions.create
witnessAnthropic(anthropic, witness, { ruleId: "assistant.sop", ruleText: "…" }); // seals each messages.createLangChain, CrewAI, and the Vercel AI SDK use the same witness.witnessed(fn, …) pattern around the tool/step function; typed adapter shims ship per release.
Verify a record offline — the moat (v1.0)
Independent, three-state verification with zero calls to a Witness server. It checks the Ed25519 signature + hash-chain + the historical DID key, and the anchor against public infrastructure (Sigstore Rekor + an RFC-3161 TSA). Node only — it uses node:crypto and the openssl CLI; the seal path above stays browser-safe and zero-dependency.
import { offlineVerify } from "vda-witness/verify";
const verdict = await offlineVerify({ record, chain, didDocument, anchor });
// 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 ∈ 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; thedidDocumentis 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.
- Browser: partial. Signature + chain + Rekor are portable; RFC-3161 (TSA) verification needs Node until a pure-JS CMS check lands.
Deprecated:
witness.verify({ record })(boolean, server round-trip) still works but requires the Witness server for the verdict — that is not independent verification. Migrate toofflineVerifyabove. A boolean can't expressSIGNED_PENDINGwithout overclaiming (calling it valid) or false-alarming (calling it broken).
Options
new Witness({ apiKey, baseUrl?, timeoutMs = 3000, fireAndForget = false, onError? }). Default baseUrl is https://witness.getvda.ai. Set fireAndForget: true for zero added latency (seal in the background).
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.
