Skip to main content

Wytness — the blackbox recorder for AI agents. Customer-held PII keys, per-event Ed25519 signing, audit-ready evidence packs. Built on Microsoft's Agent Governance Toolkit.

Project description

wytness

Wytness wrapper for Microsoft's Agent Governance Toolkit (AGT). Adds the two security properties AGT does not ship:

  • Non-repudiation — per-event Ed25519 signature with a customer-held private key. Asymmetric verification: anyone with the public key can verify, no shared secret.
  • Zero-knowledge PII — HMAC-SHA256 pseudonyms + X25519 + ChaCha20-Poly1305 token-map encryption. Wytness never sees raw PII.

These compose with AGT's own primitives (Ed25519 agent identity, SHA-256 Merkle hash chain). See docs/agt-wrapper-design.md §2.5 for the full layered model.

Mirrors wytness_ai (TypeScript) — same wire format, same init() / shutdown() lifecycle, same env-var contract.

Status

Current version: 1.0.2. Depends on agent-governance-toolkit[full]==4.0.0 (hard dependency, installed automatically). Mirrors @wytness/ai 1.0.2 (TypeScript) — same wire format, same env-var contract.

Public surface: init(), shutdown(), WytnessConfig, WytnessAuditSink, WytnessEventSink, stats(), health(), wire_status(), AuditLog (re-exported from AGT), and the typed exception hierarchy (WytnessSDKErrorWytnessConfigError / WytnessInitError / WytnessTransportError). Wire envelope is envelope_version: 2; signature covers canonical-JSON of {data, id, time, type}.

Tests: 106 pytest green covering config validation (XOR PII pairing, signing-key prefix/length tolerance, env-var fallback), envelope signing + tamper-detection, PII pseudonymisation + per-sink token-map encryption + auto-attach, transport batching + retry + dead-letter file + jittered backoff, init/shutdown idempotency, stats/health pre-init safety, framework-adapter compatibility against AGT 4.0.0's 12 integrations (LangChain, CrewAI, LangGraph, MCP, Haystack, Flowise, Langflow, Swarm, AI Card, A2A, Django middleware, HTTP middleware), and event-processor resolution regression tests covering the agent_os.event_sink candidate path.

Install

pip install wytness-ai

agent-governance-toolkit[full]==4.0.0 is a hard dependency — installed automatically. No extras to remember. Python >=3.11.

Hello world

from wytness_ai import init, shutdown, AuditLog

init(
    api_key="wyt_...",
    signing_key="...",     # browser-generated Ed25519 (always required)
    pii_pubkey="...",      # browser-generated X25519 (optional, paired with pii_secret)
    pii_secret="...",      # browser-generated HMAC secret (optional, paired with pii_pubkey)
    pii_fields=["customer.email"],
)

# AGT owns identity + Merkle chain. The wrapper has already monkey-patched
# AuditLog — every entry below also flows to Wytness.
audit = AuditLog()
audit.log(
    event_type="tool_invocation",
    agent_id="my-first-agent",
    data={
        "customer": {"email": "alice@example.com"},
        "query": "best espresso",
    },
)

shutdown()

AuditLog is re-exported from wytness_ai for convenience, so a single import line covers both lifecycle and audit. The original from agentmesh.governance.audit import AuditLog is identical and still works.


PII is pseudonymised before AGT computes its chain hash (so AGT's hashes commit to the redacted bytes — chain verification still works), the canonical-JSON envelope is signed with your Ed25519 key, and the batch lands at `api.wytness.ai/ingest`. Failed POSTs land in `~/.wytness/wytness-deadletter.jsonl`.

`init()` reads `WYTNESS_API_KEY` / `WYTNESS_SIGNING_KEY` / `WYTNESS_PII_PUBKEY` / `WYTNESS_PII_SECRET` / `WYTNESS_ENDPOINT` from the environment when kwargs aren't supplied. Browser-generated keys come from the [Wytness dashboard's Keys page](https://app.wytness.ai/keys) (Signing Key, PII Encryption Key, and PII HMAC Key cards); the dashboard never sees your private halves.

A complete runnable example lives at [`examples/hello-world/`](examples/hello-world/) — it generates ephemeral demo keys if no `WYTNESS_*` env vars are set, so you can `python examples/hello-world/main.py` immediately after install.

### Manual wiring (no monkey-patch)

If you'd rather wire explicitly, pass `auto_wire=False` and use the audit sink directly. The sink accepts AGT `AuditEntry` instances + dicts shaped like one.

```python
from wytness_ai import init, get_transport
from wytness_ai.sinks import WytnessAuditSink

config = init(auto_wire=False, signing_key="...", api_key="...")
transport = get_transport()
sink = WytnessAuditSink(transport=transport, config=config)
sink.write({
    "event_type": "tool_invocation",
    "agent_id": "agent-1",
    "data": {"query": "…"},
})

Feature parity with wytness_ai (TypeScript)

Both SDKs ship the same wire format (snake_case canonical shape, identical envelope structure, byte-comparable for the same logical input). Behavioural surface differences:

Surface Python TypeScript Notes
AGT AuditLog auto-wire ✓ — patches agentmesh.governance.audit.AuditLog ✓ — patches AuditLogger.prototype.log from @microsoft/agent-governance-sdk Both default auto_wire=True / autoWire: true.
AGT GovernanceEvent auto-wire ✓ — registers WytnessEventSink on GovernanceEventProcessor ✗ — manual (getEventSink().emit(...)) AGT-TS 4.0.0 has no GovernanceEventProcessor registry. Tracked as V2-AGT-TS-EVENT-SINK; will wire automatically when Microsoft ships the registry.
Wire-format byte parity Cross-language fixture in sdk-typescript-agt/tests/integration/wireFormat.parity.test.ts.
Ed25519 envelope signing Identical canonical-JSON encoding + sig scope.
PII pseudonymisation Identical HMAC + X25519 + ChaCha20-Poly1305 primitives.
Framework adapter compatibility tests ✓ — 12 integrations × 3 axes (import / monkey-patch survival / PII scrub) ✗ — AGT-TS ships zero framework adapters in 4.0.0

Wire format

Each POST batch is a JSON array of CloudEvents envelopes:

[
  {
    "specversion": "1.0",
    "id": "<uuid>",
    "source": "wytness",
    "type": "AuditEntry",
    "datacontenttype": "application/json",
    "time": "2026-05-25T00:00:00.000Z",
    "data": {
      "timestamp": "2026-05-25T00:00:00.000Z",
      "agent_id": "my-first-agent",
      "event_type": "tool_invocation",
      "entry_hash": "<AGT-supplied SHA-256 hex>",
      "previous_hash": "<prior entry's hash>",
      "data": { "query": "…", "customer": { "email": "EMAIL_<token>" } }
    },
    "wytness_envelope": {
      "envelope_version": 2,
      "key_id": "<16 chars base64url SHA-256(pubkey)>",
      "signature_ed25519": "<base64 Ed25519 sig over canonicalJson({data, id, time, type})>",
      "pseudonymization_version": "1"
    }
  }
]

Content-Type: application/vnd.wytness.agt+json — every envelope is signed. Wytness has no unsigned mode; signing is the product's value prop.

Idempotency / deduplication

The SDK gives you network-retry idempotency for free: when a POST fails and is retried, the same envelope id is re-sent, so the backend deduplicates the duplicate write.

The SDK does not automatically deduplicate customer-side retries — a logical action retried twice in your app code produces two distinct envelopes with two distinct entry_ids, which means two CH rows. If you need that guarantee, thread a stable per-action key through your event payload:

audit.log(
    event_type="tool_invocation",
    agent_id="my-first-agent",
    data={
        "entry_id": "tool-invocation-2026-06-01-uuid-abc",
        "customer": {"email": "alice@example.com"},
        "query": "best espresso",
    },
)

When data["entry_id"] is present at the boundary the SDK preserves it through canonicalisation; the backend then uses (org_id, entry_id) as the dedup tuple. Absent the field, the SDK synthesises a fresh UUID per write() call.

License

MIT. Built on agent-governance-toolkit (MIT, Microsoft).

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

wytness_ai-1.0.2.tar.gz (172.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

wytness_ai-1.0.2-py3-none-any.whl (39.3 kB view details)

Uploaded Python 3

File details

Details for the file wytness_ai-1.0.2.tar.gz.

File metadata

  • Download URL: wytness_ai-1.0.2.tar.gz
  • Upload date:
  • Size: 172.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for wytness_ai-1.0.2.tar.gz
Algorithm Hash digest
SHA256 7310ed952c684c4f94fb7b240315975e8afa4709538429ca964736e09491ed32
MD5 0457cde62783aab5f59a7c05f4d95779
BLAKE2b-256 c6162978aa7694061a8e9a212278fc605dc437b159e81d9e2762eeba3d3548a4

See more details on using hashes here.

Provenance

The following attestation bundles were made for wytness_ai-1.0.2.tar.gz:

Publisher: publish-wrapper-sdk.yml on imwickkd/wytness

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file wytness_ai-1.0.2-py3-none-any.whl.

File metadata

  • Download URL: wytness_ai-1.0.2-py3-none-any.whl
  • Upload date:
  • Size: 39.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for wytness_ai-1.0.2-py3-none-any.whl
Algorithm Hash digest
SHA256 1336f4027c6c9bafa0123a0c81251bac91cb27439c76755059822c12c4c76862
MD5 0e8a84fc8e0a16c0ebc146188cca5230
BLAKE2b-256 1bf3714795407b7e92980634f3c5e0aa57e3654c352d026edd3ef5511ef47e0a

See more details on using hashes here.

Provenance

The following attestation bundles were made for wytness_ai-1.0.2-py3-none-any.whl:

Publisher: publish-wrapper-sdk.yml on imwickkd/wytness

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page