Contents

The audit trail

Every supervisor decision as append-only JSONL, and the hash-chained sink that makes the trail itself tamper-evident — verified by make audit-verify.

The watchdog writes down everything it sees and does, one line per event. The chained variant links each line to the one before it by hash, so editing or deleting any line is detectable — the same trick the ledger itself uses.

Source src/ops/audit.tsscripts/ops-audit-verify.tsscripts/ops-audit-worm.ts

Every pass the supervisor runs emits AuditRecords — at, signature, tier, phase (detected, decided, acted, verified, escalated), subject, detail — to a sink the host injects. The sink is the trail’s whole persistence story: detector state is deliberately in-memory, so the audit trail is the only durable account of what the supervisor saw and did.

The plain sink

jsonlAuditSink writes one JSON line per record. Bigint money fields (a prove report’s drift amounts, a saga’s reserve) encode as {"$bigint":"..."} — the operation-journal convention — so every line parses with plain JSON.parse.

The chained sink

An audit trail that a tamperer can edit is testimony, not evidence. hashChainedAuditSink applies the ledger chain’s fixed-preimage discipline to the trail itself: each line carries prev (the prior line’s hash; the first line carries the all-zero genesis) and hash, computed over the line’s own bytes. Editing a record, rewriting a prev, dropping a line, or reordering breaks the chain at exactly that line.

const sink = hashChainedAuditSink(
  (line) => appendFileSync(segment, `${line}\n`, { flag: 'a' }),
  systemDigest(),
);
// ... records flow during the process's life ...
await sink.flush(); // the digest is async; flush before exit

verifyAuditChain re-derives a trail and reports in the prover’s shape — intact, the first break (line number and reason: malformed, broken-link, or tampered-hash), and the count verified. make audit-verify FILE=<segment>.jsonl wraps it and exits nonzero on a break.

Shipping to write-once storage

A chain proves tampering happened; write-once storage keeps the honest copy. The worked example in scripts/ops-audit-worm.ts appends through the chained sink to a segment file opened O_APPEND, seals the segment by renaming it *.sealed.jsonl, and leaves shipping — an object-lock bucket, a retention-locked store — to the host. The last line’s hash is the segment’s fingerprint: record it off-host and the shipped copy, the local copy, and the fingerprint must all agree. The observer must be as tamper-evident as the observed.

See also