Skip to main content

Module audit

Module audit 

Source
Expand description

Enterprise audit trail (PR-5 of issue #487).

Every memory-mutation call site in the binary — HTTP handlers, MCP tool dispatch, CLI write commands, and ai-memory boot — emits an AuditEvent to a hash-chained, append-only JSON log when the audit subsystem is enabled. The schema is stable, versioned, and framework-agnostic (NOT bound to OCSF or CEF — see docs/security/audit-schema.md). SIEMs ingest the lines as-is.

§Design properties

  1. Default-OFF for privacy. Operators opt in via [audit] enabled = true in config.toml (or AI_MEMORY_AUDIT_DIR=<dir> env var for one-off runs — see src/log_paths.rs::AUDIT_DIR_ENV for the canonical name; the audit log file is written as <dir>/audit.log).
  2. Hash-chained, tamper-evident. Each line carries a prev_hash that matches the prior line’s self_hash. ai-memory audit verify recomputes the chain and exits non-zero on mismatch.
  3. Append-only OS hint. Best-effort chflags(2) (BSD/macOS) or FS_IOC_SETFLAGS ioctl (Linux). Documented as defense in depth; the chain is the load-bearing tamper-evidence.
  4. Privacy by default. Audit captures (memory_id, namespace, title, action, outcome, actor). Memory content is never emittedredact_content = true is the only supported mode in the v1 schema; the field is reserved in AuditTarget for future compliance contexts that mandate content capture.
  5. Per-process monotonic sequence, independent of the chain. Lets a SIEM detect dropped lines even before the chain check.
  6. No backpressure on the caller. Emission is synchronous (one write per line so the chain is consistent across processes concurrently appending — the file is opened with O_APPEND), but failures inside emit are swallowed and logged via tracing. A broken audit pipeline never blocks a memory operation.

Modules§

synthesis_sources
#1558 batch 5 wave 3 — canonical AuditActor::synthesis_source provenance values. One spelling per value; every production writer (MCP dispatch, MCP store/delete tools, HTTP handlers, CLI crud/store/update) references these consts instead of scattering the literal. The vocabulary doc on synthesis_source above stays the narrative SSOT; this mod is the mechanical one.

Structs§

AuditActor
Who performed the action.
AuditAuth
Authentication context for HTTP-originated events. Stdio (CLI / MCP) invocations omit this block entirely.
AuditEvent
One audit event. The serialized form is one JSON object per line (NDJSON). Field order is stable for chain reproducibility.
AuditSink
Initialised audit sink — writer handle protected by a mutex so the chain head update + write are atomic across emission threads. The writer is dyn Write + Send so tests can substitute an in-memory Vec<u8> for the production File.
AuditTarget
What was acted upon.
EventBuilder
Builder for an audit event. Most call sites use one of the convenience helpers ([emit_store], [emit_recall], etc.) but the builder is public so unusual flows (consolidate-many, deferred import) can fill in custom targets.
VerifyFailure
VerifyReport
Outcome of verify_chain.

Enums§

AuditAction
Canonical action vocabulary. Adding a variant is a non-breaking schema change; renaming or removing one IS breaking.
AuditOutcome
Outcome of the action.
VerifyFailureKind
Why the per-line audit-event hash chain (AuditEvent JSONL files under audit/) failed to verify.

Constants§

CHAIN_HEAD_PREV_HASH
Sentinel prev_hash for the first line in a fresh chain. Hex-encoded 32-byte zero buffer — picked so a chain head is unambiguous on inspection.
SCHEMA_VERSION
Stable schema version stamped on every emitted line. Bump only when a field’s semantics change in a way SIEM parsers care about (renaming, removing, or repurposing). Adding optional fields does NOT bump the version. See docs/security/audit-schema.md §Version policy for the full contract.

Functions§

actor
Construct an AuditActor from an agent_id + synthesis source + optional scope. The synthesis source is informational metadata and MUST be one of the documented strings in AuditActor.
emit
Write an event to the configured sink. No-op when audit is disabled. Failures are logged via tracing::error! and dropped — audit is never allowed to fail a memory operation.
init
Initialise the audit sink. Called at most once per process from init_from_config; subsequent calls replace the prior sink so test-only callers can swap targets.
init_from_config
Initialise the audit sink from a parsed crate::config::AuditConfig. Returns Ok(()) whether or not audit is enabled — it is a no-op when disabled.
is_enabled
Whether the audit subsystem is currently enabled. Cheap.
resolve_audit_path
Resolve the audit log file path from the config, honouring the user-mandated precedence ladder: CLI > env (AI_MEMORY_AUDIT_DIR)
resolve_audit_path_with_override
Strict variant: takes an optional --audit-dir override, returns the resolved file path (with audit.log appended when the input resolves to a directory) plus the crate::log_paths::PathSource used.
target_memory
Construct an AuditTarget for a single memory.
target_sweep
Construct an AuditTarget for a multi-row sweep operation.
verify_chain
Walk an audit log file and verify the chain. Returns a structured report; the binary’s audit verify subcommand turns this into an exit code.
verify_chain_from_reader
Verify a chain from any Read source. Lets tests run against in-memory buffers without touching the filesystem.