Decision Trace
Available on every plan. Public verification is part of the Public Evidence Verification feature.
What this is
A Decision Trace is the causal chain-of-custody artifact that Behavry emits for every proxied tool call. Because the proxy sits inline on the execution path, every trace is a first-hand record of who called what, what policy fired, and what the outcome was. Traces are SHA-256 hash-chained — any tampered or out-of-order row breaks the chain and fails verification.
This is the primary artifact auditors, security teams, and incident responders use when they need to prove — or disprove — that an AI agent took a specific action.
Anatomy of a trace
Every trace is one row in the audit_events TimescaleDB hypertable (backend/behavry/audit/service.py). The row captures:
| Section | Fields |
|---|---|
| Identity | agent_id, session_id, tenant_id, workflow_id, requester_id |
| Action | tool_name, mcp_server, action, target, input_hash |
| Policy | policy_result (allow / deny / escalate), policy_id, policy_reason |
| Behavior | behavioral_score (z-score at time of event) |
| DLP | dlp_findings, dlp_action |
| Integrity | event_hash, previous_hash |
| Outcome | response_code, latency_ms, error |
| Extra | extra JSONB for anything module-specific |
input_hash is a SHA-256 over the normalized tool arguments — we don't store the arguments themselves by default, so a trace proves which payload was processed without exposing sensitive content.
The hash chain
Each event is hashed together with the previous event's hash:
event_hash = sha256(
canonical(event_fields_excluding_hashes) || previous_hash
)
canonical() is a stable JSON serializer (sorted keys, no whitespace, UTF-8). If any row between t0 and tN is modified, deleted, or reordered, event_hash_N no longer matches a recomputation from the earlier rows and the chain breaks.
The verifier lives at backend/behavry/audit/verify.py and can recompute the chain for any time range.
Verifying a trace
Internally (admin UI)
Audit → Events → row detail → "Verify chain" recomputes the chain from the event backwards and reports pass / fail with the first breaking row.
Programmatically
curl -H "Authorization: Bearer $ADMIN_TOKEN" \
"$BEHAVRY_URL/api/v1/audit/verify?start=2026-04-01T00:00:00Z&end=2026-04-02T00:00:00Z"
Returns:
{
"events_verified": 12843,
"chain_intact": true,
"first_bad_row": null,
"duration_ms": 412
}
Externally (public verification)
A verifier outside your tenant can prove that a specific event hash exists in your audit log without Behavry credentials:
GET /api/v1/public/verify?hash=<event_hash>
Returns { "found": true, "timestamp": "...", "chain_intact": true } if the hash is in the canonical log. This endpoint is rate-limited and exposes no event content — only existence and integrity. See Public Evidence Verification.
Where traces come from
Every module that makes a policy-relevant decision publishes through the event bus and lands in audit_events:
- MCP proxy (
backend/behavry/proxy/engine.py) — tool calls,tools/listfiltering, blast-radius checks - Behavioral monitor — baseline updates, anomaly alerts
- DLP scanner — pattern matches, quarantines
- SIEM fan-out — outbound delivery success/failure
- Admin — policy changes, user invites, kill-switch actions
Retention & export
- Default retention: 90 days on Professional, 1 year on Enterprise (configurable per-tenant)
- Compressed automatically by TimescaleDB after 7 days
- Export to CSV / JSON / NDJSON via Audit → Export or
GET /api/v1/audit/export - Forwarded to SIEM via the SIEM Connectors fan-out
Related
- Audit & Integrity — schema and retention details
- SIEM Connectors — streaming traces to Splunk / Sentinel / Chronicle / QRadar / Syslog / Webhook
- Public Evidence Verification — external verification endpoint