Quickstart: Python
Wire wytness_ai into your AGT-instrumented agent in about five minutes. Install, set four env values (plus the optional endpoint), call wytness_ai.init(). Every audit entry AGT chains is auto-forwarded to Wytness with PII pseudonymisation and a per-event Ed25519 envelope signed with your customer-held key.
1. Install the wrapper
Microsoft's Agent Governance Toolkit (AGT) is a hard dependency of wytness-ai — installed automatically. One command gets you the open-source identity, hash-chain, and policy primitives, plus the Wytness compliance layer on top.
pip install wytness-aipip install agent-governance-toolkit and point its native CloudEvents sink at api.wytness.ai/ingest. You get the dashboard but not customer-held PII keys or Evidence Packs.2. Generate keys
Wytness adds two customer-held primitives on top of AGT — per-event Ed25519 non-repudiation and zero-knowledge PII pseudonymisation. Both rely on keys you generate in your browser; the private halves never reach Wytness.
Open the Keys page. Click Generate on each of the Signing, Encryption, and HMAC tabs, then download each private half. The public halves auto-register against your org. The four env values you need are then shown as a copy-block on the Onboarding "Instrument" step:
WYTNESS_API_KEY— transport credential (server-issued, prefixwyt_api_live_…)WYTNESS_SIGNING_KEY— base64 Ed25519 private key, 32 bytes decoded (always required — non-repudiation is the product promise)WYTNESS_PII_PUBKEY— base64 X25519 public key, 32 bytes (optional; required together with the secret to enable PII pseudonymisation)WYTNESS_PII_SECRET— base64 HMAC secret, ≥16 bytes (optional; required together with the pubkey)
3. Set the environment
Set the four values as environment variables. In production use your secrets manager (Azure Key Vault, AWS Secrets Manager, Vercel env, k8s secrets, etc.); in local dev a .env file loaded by python-dotenv is fine. wytness_ai.init() reads them directly — no constructor boilerplate. If you use a .env file locally, add it to .gitignore before the first git add.
WYTNESS_API_KEY=wyt_api_live_...WYTNESS_ENDPOINT=https://api.wytness.aiWYTNESS_SIGNING_KEY=<base64 Ed25519 private key>WYTNESS_PII_PUBKEY=<base64 X25519 public key>WYTNESS_PII_SECRET=<base64 HMAC secret>4. Wire it in
Call wytness_ai.init() once at process start, before AGT's AuditLog is first instantiated. The wrapper hooks AGT's audit log cleanly so every entry it chains flows through the Wytness path — your existing AGT code stays unchanged.
import wytness_aifrom wytness_ai import AuditLog # Reads WYTNESS_* env vars by default. pii_fields lists the dotted paths# whose values should be pseudonymised (emails, customer names, etc.).wytness_ai.init(pii_fields=["customer.email", "customer.name"]) audit = AuditLog()audit.log( event_type="tool_invocation", agent_did="billing-agent", action="send_invoice", policy_decision="allow", data={ "customer": {"email": "alice@example.com", "name": "Alice"}, "amount": 99.99, },) # Drain the in-flight batch on exit.wytness_ai.shutdown()wytness_ai.init(auto_wire=False) if you'd rather attach the audit sink yourself — see the SDK API reference.5. Run it
Run your agent normally. The wrapper batches events (default 100 or 5 seconds, whichever first), pseudonymises declared PII fields, signs each event with your Ed25519 key, and POSTs the canonical-shape envelope to api.wytness.ai/ingest. Failed POSTs are written to a local dead-letter file for replay once connectivity returns.
python agent.pyOpen your app.wytness.ai dashboard within ~5 seconds of the first event to confirm it arrived. Events show the AGT identity, the AGT-supplied hash chain, and the Wytness envelope signature side-by-side.
What you'll see in the dashboard
- Each AGT
AuditEntryas a row in the Events page - Three-layer verdict: AGT identity (Ed25519 DID), AGT-supplied hash chain, Wytness envelope (Ed25519)
- PII fields rendered as typed tokens (
EMAIL_3c866e6f, etc.); reveal in-browser with your X25519 private key - Source format chip showing
wytness_agt_py(Python wrapper) on every event - Per-agent Identity Badge pulled from AGT's DID + trust score
Full working example
Save as test_audit.py, then run with python test_audit.py.
import wytness_aifrom wytness_ai import AuditLog wytness_ai.init(pii_fields=["customer.email"]) audit = AuditLog() audit.log( event_type="tool_invocation", agent_did="test-agent", action="hello_world", policy_decision="allow", data={"customer": {"email": "alice@example.com"}, "message": "Hi"},) audit.log( event_type="tool_invocation", agent_did="test-agent", action="add_numbers", policy_decision="allow", data={"a": 40, "b": 2, "result": 42},) wytness_ai.shutdown()print("Done — check your dashboard.")What happens under the hood
- AGT runs unchanged — identity check, policy evaluation, hash-chain entry
- The wrapper's
WytnessAuditSinkreceives the entry, applies yourpii_fieldspseudonymisation - The canonical-shape
datapayload is signed with your Ed25519 key, wrapped in a CloudEvents 1.0 envelope - The batch is POSTed to
api.wytness.ai/ingestasapplication/vnd.wytness.agt+json - Failed POSTs are written to a local dead-letter file for replay once connectivity returns
Automatic LLM capture
Prefer not to write AuditLog().log(...) by hand? Point init() at your model client with wrap= and every Anthropic Messages API call (including the beta namespace) and every OpenAI Chat Completions and Responses call — streaming included — is recorded automatically — prompt, reply, reasoning (Anthropic extended thinking), latency, and token usage. Token usage needs no opt-in: on OpenAI streams the SDK requests it and hides the resulting usage-only chunk, so your loop is unchanged. Other providers are not captured. Captured calls ride the same signed, PII-pseudonymised path as manual entries.
import anthropicimport wytness_ai wytness_ai.init( wrap=anthropic, # or "openai", a list, or True to auto-detect installed providers agent_did="did:web:acme.example:support-bot", pii_fields=["customer.email"],) client = anthropic.Anthropic()client.messages.create( # recorded automatically — no audit.log() needed model="claude-haiku-4-5", max_tokens=512, messages=[{"role": "user", "content": "Summarise ticket #4821"}],) wytness_ai.shutdown()stream=True and messages.stream(...)) are captured too — one event per call when the stream ends. If you stop a raw stream early, close it (or use a with block) — breaking out of a bare loop without closing skips capture. Helper streams are captured at context exit. OpenAI streamed usage needs no opt-in — the SDK requests it and hides the resulting usage-only chunk, so your loop is unchanged.Using LangChain, CrewAI, or another framework?
AGT 4.1.0 ships 12 first-party framework integrations: LangChain, LangGraph, CrewAI, Haystack, MCP, Flowise, Langflow, Swarm, A2A, AI Card, plus HTTP and Django middleware. These adapters perform trust verification (gating tool execution against trust scores) — for audit emission, the wrapper picks up every entry AGT's AuditLog().log(...) chains regardless of how you wired AGT in. See the framework integrations guide for the wire-up pattern.
Next steps
- PII Protection — declare custom pseudonymisation fields, generate keys, reveal tokens in the dashboard
- SDK API reference — full
init()/shutdown()options, manual wiring, batch tuning - Event schema — canonical-shape
datapayload + Wytness envelope layout - Quickstart (TypeScript) — same shape for
@wytness/ai(npm)
Ready to audit your AI agents?
Start recording