Audit Trail
Opt-in HMAC-SHA256 hash-chained audit trail for tamper-evident AI agent logging. Maps to EU AI Act Article 12 record-keeping.
gov.enforce() always writes audit events to your storage adapter. Hash-chaining is opt-in — when enabled, every event's hash includes the previous event's hash, creating a chain where any tampering is immediately detectable. The chain maps to EU AI Act Article 12 record-keeping.
Setup
The simplest way to enable tamper-evident audit is the integrityAudit config option on createGovernance — every event the SDK writes (registrations, enforcement decisions, audit.log() calls, kill-switch events) is intercepted and appended to the signed chain.
If you need finer control (e.g. only chaining a subset of events), use the lower-level createIntegrityAudit() wrapper from governance-sdk/audit-integrity.
Warning: The signing key is used to compute HMAC hashes. If it leaks, history is rewritable by the attacker. Store it as an environment variable, rotate it regularly, and pair with an external anchor (object-storage immutability, blockchain anchoring) for defence in depth.
Honesty note: Only events routed through this
governanceinstance get chained. Host-level logging your application does independently (pino,winston, etc.) is not covered.
What gets chained (with integrityAudit)
When integrityAudit is set, every audit write the SDK makes is HMAC-chained — no separate wrapper, no ceremony.
| Event type | Written by | Captures |
|---|---|---|
agent_registered | gov.register() | name, framework, owner, initial score |
policy_evaluation | gov.enforce() | agent, action, tool, rule matched, outcome, reason |
policy_evaluation_preprocess / _postprocess | gov.enforcePreprocess() / Postprocess() | stage-scoped enforcement result |
action_outcome | gov.recordOutcome() or runWithOutcome() | success / failure, duration, tokens, output summary, error |
agent_killed | killSwitch.kill() | agent, reason, killedBy |
| (caller-supplied) | gov.audit.log() | any eventType you pass — custom LLM calls, approvals, etc. |
What is NOT chained: anything you log directly via storage.createAuditEvent() (bypasses the chain), anything your host app does outside governance (raw fetch(), filesystem I/O outside governed tools), and anything the agent did between enforce() calls without invoking enforce() or recordOutcome() itself.
Recording post-execution outcomes
gov.enforce() records the decision ("is the agent allowed to call search?"). To also record what happened AFTER the action ran, use recordOutcome() or its one-line wrapper runWithOutcome(). When integrityAudit is on, the outcome event joins the chain alongside every other SDK write.
Or call recordOutcome() directly when you need finer control (token counts, custom detail):
Tip: Pass a
summarizefunction torunWithOutcome({ summarize })to redact output before it hits the audit log.
Audit Logging
Every gov.enforce() call automatically writes an audit event to your storage adapter. You don't need to log enforcement decisions manually. When integrityAudit is configured, those events are also hash-chained.
Custom Events
Log additional events for business logic, tool execution results, or any other auditable action.
Note: Events are serialized deterministically (all keys recursively sorted) before hashing. This ensures the same event always produces the same hash regardless of property insertion order.
Chain Verification
Export the chain from the governance instance, then re-verify it anywhere — even on a separate auditor machine — using the standalone verifyAuditIntegrity function.
Note:
gov.integrityChainis only populated whenintegrityAuditis configured oncreateGovernance. For the lower-levelcreateIntegrityAudit()wrapper (which has an in-instance.verify()method), seegovernance-sdk/audit-integrity.
Tamper Detection
| Attack | How It's Detected |
|---|---|
| Event modification | Hash mismatch — recomputed hash differs from stored hash |
| Event insertion | Missing integrity record for the inserted event |
| Event deletion | Chain break — previousHash of event N+1 doesn't match hash of event N |
| Event reordering | Chain continuity break at the reordered position |
Export & Statistics
Export the full chain for compliance review, external auditors, or archival. Filter by agent, time range, or event type.
Known Limitations
Warning: In-memory chain: The hash chain state is held in process memory. It does not survive process restarts without re-hydrating from persistent storage. Use the PostgreSQL storage adapter for durable audit.
Warning: Concurrent writes: The chain uses an internal serialization queue to prevent hash forks from concurrent
log()calls. This means writes are serialized within a single process.