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.

ts

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 governance instance 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 typeWritten byCaptures
agent_registeredgov.register()name, framework, owner, initial score
policy_evaluationgov.enforce()agent, action, tool, rule matched, outcome, reason
policy_evaluation_preprocess / _postprocessgov.enforcePreprocess() / Postprocess()stage-scoped enforcement result
action_outcomegov.recordOutcome() or runWithOutcome()success / failure, duration, tokens, output summary, error
agent_killedkillSwitch.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.

ts

Or call recordOutcome() directly when you need finer control (token counts, custom detail):

ts

Tip: Pass a summarize function to runWithOutcome({ 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.

ts

Custom Events

Log additional events for business logic, tool execution results, or any other auditable action.

ts

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.

ts

Note: gov.integrityChain is only populated when integrityAudit is configured on createGovernance. For the lower-level createIntegrityAudit() wrapper (which has an in-instance .verify() method), see governance-sdk/audit-integrity.

Tamper Detection

AttackHow It's Detected
Event modificationHash mismatch — recomputed hash differs from stored hash
Event insertionMissing integrity record for the inserted event
Event deletionChain break — previousHash of event N+1 doesn't match hash of event N
Event reorderingChain 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.

ts

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.