Metadata-Version: 2.4
Name: wytness-ai
Version: 1.0.0
Summary: 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-URL: Homepage, https://wytness.ai
Project-URL: Source, https://github.com/imwickkd/wytness
Author-email: Wytness <support@wytness.ai>
License: MIT
License-File: LICENSE
Keywords: agent-governance-toolkit,agents,agt,ai,audit,compliance,pii
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Libraries
Classifier: Topic :: System :: Logging
Requires-Python: >=3.11
Requires-Dist: agent-governance-toolkit[full]==3.7.0
Requires-Dist: cryptography==42.0.7
Requires-Dist: httpx==0.27.0
Requires-Dist: pydantic<3,>=2.10.6
Provides-Extra: dev
Requires-Dist: pytest-asyncio==0.23.6; extra == 'dev'
Requires-Dist: pytest-cov==5.0.0; extra == 'dev'
Requires-Dist: pytest==8.2.0; extra == 'dev'
Requires-Dist: ruff==0.4.4; extra == 'dev'
Description-Content-Type: text/markdown

# 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`](../docs/agt-wrapper-design.md) §2.5 for the full layered model.

Mirrors [`wytness_ai`](../sdk-typescript-agt/) (TypeScript) — same wire format, same `init()` / `shutdown()` lifecycle, same env-var contract.

## Status

**v1.0.0 — first stable release.** Coordinated launch with `wytness 1.0.0` (TypeScript). `WytnessAuditSink`, `WytnessEventSink`, `WytnessTransport`, `init()`, `shutdown()`, the Ed25519 envelope (`envelope_version: 2` — signature covers `data` + envelope id/time/type), the X25519 + ChaCha20-Poly1305 PII token-map with auto-attach, the typed exception hierarchy (`WytnessSDKError` + subclasses), the top-level `stats()` / `health()` / `wire_status()` accessors, and the AGT `AuditLog` monkey-patch all ship as working code. `agent-governance-toolkit[full]==3.7.0` is a hard dependency since the wrapper monkey-patches AGT's `AuditLog` symbol on `init({auto_wire=True})` (the default) so every audit entry AGT chains is automatically forwarded into the Wytness envelope path with full 3-layer evidence (AGT identity + AGT Merkle chain + Wytness envelope).

Tests: 68 pytest green covering config validation (XOR PII pairing guard, 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, and end-to-end framework-adapter compatibility against AGT 3.7.0's 12 integrations (LangChain, CrewAI, LangGraph, MCP, etc.). Backend dual-accepts envelope_version 1 + 2 during the deprecation window — see [CHANGELOG.md](CHANGELOG.md) for the v1 → v2 wire-format migration.

## Install

```bash
pip install wytness-ai
```

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

## Hello world

```python
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 3.7.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 3.7.0 | |

## Wire format

Each POST batch is a JSON array of CloudEvents envelopes:

```json
[
  {
    "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": 1,
      "key_id": "<16 chars base64url SHA-256(pubkey)>",
      "signature_ed25519": "<base64 Ed25519 sig over canonicalJson(data)>",
      "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.

## License

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