Expand description
Structured audit log for SOC 2 / HIPAA / ISO 27001 deploys.
Replaces the previous free-form record(action, principal, target, result, details) API with a stable JSON-Lines schema keyed on
event_id, ts, principal, tenant, action, resource,
outcome, detail, remote_addr, and correlation_id so external
tooling (Splunk, Datadog HEC, ELK, BigQuery, Athena) can ingest the
file without per-deploy regex.
Operational properties:
- Async-write: emit sites push onto a bounded
std::sync::mpscchannel; a dedicated thread owns the file handle and flushes on a periodic timer (default 250 ms) or per-event whenRED_AUDIT_FSYNC=every. If the channel fills the emit site falls back to a direct sync write — losing throughput is preferable to dropping audit lines. - Rotation: when the active file exceeds
RED_AUDIT_MAX_BYTES(default 64 MiB) the writer renames it to.audit.log.<ms>.zst, zstd-compresses it, and starts a fresh active file. The repo already pullszstd; we don’t add a gzip dependency. - Hash chain (tamper-evidence): each event carries a
prev_hashfield — the sha256 of the previous JSON line. An auditor verifying the file recomputes the chain; a single edit anywhere in the file breaks every subsequent hash. Does not defend against an attacker withroot + write(they could rebuild the chain), but it does defend against accidental edits and most insider tampering. - SIEM streaming: when
RED_AUDIT_STREAM_URLis set every line is also POSTed to that URL fire-and-forget.
Pre-1.0: the file format breaks from the previous shape. Old
.audit.log files are NOT readable by the new query endpoint.
That’s an accepted regression — operators upgrading should rotate
the file before the deploy.
Structs§
- Audit
Event - Structured audit event. Serialised as one JSONL row per call.
- Audit
Event Builder - Builder for
AuditEvent. Generated byAuditEvent::builder(). - Audit
Field - A single typed audit field (key + typed value). Construction is
gated by
AuditFieldEscaper::fieldso the typed value cannot be bypassed — there is nopubconstructor for the field that accepts a free-form string value. - Audit
Field Escaper - Typed-field guard for audit emission (ADR 0010).
- Audit
Logger
Enums§
- Audit
Auth Source - Auth pathway that produced the principal. Decoupled from
crate::auth::AuthSourceso we can record System / Anonymous / Session / ApiKey lanes that aren’t surfaced by the runtime auth enum (which only covers Password / ClientCert / Oauth today). - Audit
Value - Typed value variants accepted by the audit-field guard. The
serializer (
to_json_line) owns the framing; anAuditValuecannot smuggle structural bytes past the canonical encoder (crate::serde_json::Value::escape_string, RFC 8259 §7) because the variant is consumed as a typed value, not as an interpolated string. Adversarial corpora (CRLF, NUL, quote, semicolon, JSON-in-JSON, control bytes 0x00..0x20) survive the boundary because the encoder emits\u00XXescapes for every byte below 0x20. - Outcome
- Outcome of the audited action.