Skip to content

ADR 0029 — Sealed audit hash-chain (WORM) + admission cross-correlation

  • Status: Accepted (formalized 2026-05-12 with v3.7.0 — Wave 10 + 10-B shipping)
  • Date: 2026-05-11 (locked), 2026-05-12 (formalized)
  • Deciders: TLSStress.Art project
  • Targets: v3.7.0 (sealed chain + cross-correlation); v6.0+ (PCR-hash inclusion via TPM)
  • Patent claim family: claim #20 — sealed audit hash-chain with cross-correlation bridge to admission decisions
  • Umbrella ADR: 0026

Context

Audit logs are the substrate every customer compliance audit relies on. Every prior insider-operator attack pattern includes "and then I edited the audit log": NTP-rewind + replay, delete + gap-conceal, write-then-overwrite via the same root shell that ran the rogue workload. A flat append-only file is a fig leaf — a root shell beats it.

What we need:

  1. Tamper-evident — any post-hoc edit detectable at the next verification pass, ideally without re-reading the entire log
  2. Append-only by construction — not by policy, but by the storage layout: appending costs O(1) and overwriting requires re-deriving every subsequent hash
  3. Cross-correlated with Pod admission decisions so a "rogue pod admitted at 02:14 UTC" lights up the same timestamp in the audit history (forces an attacker who tampers with one to tamper with both)
  4. Survives WORM storage — works on append-only S3, object-lock buckets, immudb, anything that refuses overwrites at the storage layer

Decision

Implement a hash-chain audit log with cross-correlation:

  • Each audit entry contains:
  • Monotonic sequence number
  • UTC timestamp (from a trusted clock proxy, see Wave 10-B)
  • Event payload (admission decision, licence check, DLP event, …)
  • prev_hash = SHA-256 of the previous entry
  • entry_hash = SHA-256 of this entry's serialised contents (canonical encoding from ADR 0027)

  • Verification is O(n) on log length but incremental: a verifier that already trusts entry N only needs entries N+1..M to extend trust to M. Any in-place edit at entry K breaks the chain at K+1.

  • Cross-correlation bridge (Wave 10-B): the K8s admission webhook (pkg/ztp-prem-admission/) writes its decision to its own ring buffer and emits an event into the sealed audit log with the same sequence anchor. The dashboard reader (dashboard/src/lib/ztp-prem/admission-correlate.ts) walks both and surfaces correlation gaps as alerts — not silent reconciliations.

  • WORM-friendly: the on-disk format is a JSONL stream appended one entry at a time. A delete + re-create pattern fails because (a) S3 object lock refuses overwrite, (b) the chain breaks at the seam.

Consequences

Pros - Tampering one log requires tampering the other in a way that re-canonicalises consistently → cost goes from "minutes of root" to "rewriting both stores with valid chains" - Customer SOC 2 audit gets a verifiable artifact instead of a trust-us README - Forensic post-mortem on a real incident becomes possible (you can prove what did happen and what did not) - Foundation for v6.0+ PCR-hash inclusion (the TPM probe will write a measured-boot anchor into the chain at boot, so rebooting into a doctored kernel detectably breaks the chain)

Cons - Verification cost grows linearly with log length — at ~1M entries/day this matters for retention >90d; we plan periodic signed checkpoints in v6.0+ - Cross-correlation gap alerts are noisy if NTP is unreliable — Wave 10-B mitigates with a 30s tolerance window - Append-only means accidental sensitive data can't be redacted — the DLP pre-filter (ADR 0032) has to catch it before the write, not after

Reversibility: medium. The on-disk format can be migrated by shipping a converter + a chain-rebase migration. Cross-correlation semantics could in theory be loosened, but doing so reduces the patent claim and the customer-visible promise.


Last verified against shipping code: v3.7.0 (2026-05-12).