Skip to content

audit: state that the log is volatile, and trim CLAUDE.md - #124

Merged
douglasmun merged 2 commits into
mainfrom
doc/audit-log-volatility
Aug 23, 2026
Merged

audit: state that the log is volatile, and trim CLAUDE.md#124
douglasmun merged 2 commits into
mainfrom
doc/audit-log-volatility

Conversation

@douglasmun

Copy link
Copy Markdown
Owner

Finishes the two items left open from the EDR/kprintf work: the audit-log persistence question, and the CLAUDE.md trim.

1. Audit log: decided to stay volatile, and now says so

The Check Point BTR Reforged write-up (root-level attacker blinds the EDR) raised the follow-up: would our audit log show it afterwards? It would not — reboot erases it — which makes persistence look like the fix. It isn't, for two reasons I verified rather than assumed:

The HMAC key is regenerated every boot. audit_init() (src/audit.c:81) draws it from the CSPRNG and stores it nowhere, so records persisted across a reboot cannot be authenticated afterwards. Persistence would ship a tamper-evident log whose tamper-evidence is void exactly across the boundary persistence exists to cross — and [OK] integrity verified would print either way, which is the status-surface lie class. Fixing it needs a key root cannot read, i.e. sealed storage. No TPM here.

The one persistence precedent has never persisted anything. entropy.c:442 targets /boot/entropy.seed via ramfs_open; nothing creates /boot, and ramfs is RAM. Confirmed from three live boot logs that every boot prints Cannot create seed file / Entropy will reset on next reboot. Copying that model reproduces the bug more quietly.

Neither is a small change, so the interim fix is to stop the volatility being a surprise:

  • auditlog -s printed Total events logged, which reads as an all-time figure this counter structurally cannot report → Events logged (this boot), remaining labels re-aligned, plus an explicit Log is VOLATILE: held in RAM, cleared on reboot.
  • auditlog --help now states the log is not written to disk.

doc/AUDIT_LOG_PERSISTENCE.md records the decision and the three things that would have to be true to revisit it.

Verification

Both surfaces driven live under QEMU rather than read off the diff:

=== Audit Log Statistics ===
Events logged (this boot): 3
Events in buffer:          3
...
Log is VOLATILE: held in RAM, cleared on reboot.

and the --help note renders. Full rebuild rc=0, zero diagnostics under -Werror.

2. CLAUDE.md: 426 → 378

The split rule is "constraints stay inline, history moves out", so this cuts narrative that doc/ already carries and leaves the rules alone. Mechanically verified nothing was lost — no rule heading, doc/ pointer, harness name or build flag present before is absent after (comm against the base on all four axes). Rule count went 21 → 23 because su and edit became their own bullets.

Also: added index rows for NETWORK_ISOLATION.md and NETDAEMON_DESIGN.md (cited by the rules but never listed); dropped the inline EDR grep/sed incantations, which are superseded by verify/edr-rejoin.sh and whose reproduction here invites a sixth hand-copy (all five earlier ones had defects that FAILed a correct kernel); noted verify/run-all.sh; and checked every harness filename it names exists — one was wrong.

Still above the ~150 target. Getting there means deleting rules rather than prose, and each is the residue of a real bug, so I stopped rather than buy the line count with content.

🤖 Generated with Claude Code

https://claude.ai/code/session_01CEkhAhgTxbE5TgifyYf8v4

douglasmun and others added 2 commits August 23, 2026 17:50
The Check Point BTR Reforged write-up (root-level attacker blinds the EDR)
raised the obvious follow-up: would our audit log show it afterwards? It
would not -- `reboot` erases it -- which makes persistence look like the
fix. It is not, and the reasons are specific enough to be worth writing
down before someone (me) proposes it again.

Two blockers, both verified rather than assumed:

1. audit_init() (audit.c:81) regenerates the HMAC key from the CSPRNG on
   every boot and stores it nowhere. Records persisted across a reboot
   therefore cannot be authenticated afterwards. Persistence would ship a
   tamper-evident log whose tamper-evidence is void exactly across the
   boundary persistence exists to cross -- and `[OK] integrity verified`
   would print either way, which is the status-surface lie class.

2. The only persistence precedent here has never persisted anything.
   entropy.c:442 targets "/boot/entropy.seed" via ramfs_open; nothing
   creates /boot and ramfs is RAM. Confirmed from three live boot logs
   that every boot prints "Cannot create seed file" / "Entropy will reset
   on next reboot". Copying that model reproduces the bug more quietly.

Neither is fixable by a small change (1 needs a key root cannot read,
i.e. sealed storage, which is a hardware question), so the honest interim
fix is to stop the volatility being a surprise:

- `auditlog -s` printed "Total events logged", which reads as an all-time
  figure the counter structurally cannot report. Now "Events logged (this
  boot)", with the remaining labels re-aligned to the wider column, plus
  an explicit "Log is VOLATILE: held in RAM, cleared on reboot."
- `auditlog --help` says the log is not written to disk.

Verified live under QEMU, both surfaces (stats block and --help), rather
than by reading the diff. Build clean under -Werror.

doc/AUDIT_LOG_PERSISTENCE.md records the decision and what would have to
be true to revisit it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CEkhAhgTxbE5TgifyYf8v4
426 -> 378 lines. The split rule is "constraints stay inline, history moves
out", so this cuts supporting narrative that doc/ already carries and leaves
the rules themselves alone.

Mechanically verified that nothing was lost -- no rule heading, doc/ pointer,
harness name or build flag present before is absent after (comm against
HEAD:CLAUDE.md on all four). Rule count went 21 -> 23 because `su` and `edit`
became their own bullets.

Also:
- Added table rows for NETWORK_ISOLATION.md and NETDAEMON_DESIGN.md, which the
  rules already cited without the index listing them, plus the new
  AUDIT_LOG_PERSISTENCE.md.
- The EDR-tear entry no longer inlines grep/sed incantations. Those are
  superseded by the shared splice in verify/edr-rejoin.sh, and reproducing them
  here invites a sixth hand-copy -- all five earlier ones had defects that
  FAILed a correct kernel.
- Noted verify/run-all.sh, which CLAUDE.md never mentioned.
- Confirmed every harness filename it names actually exists (one was wrong:
  verify-edr-rejoin-test.sh -> verify/edr-rejoin-test.sh).

Still above the ~150 target. Getting there means deleting rules rather than
prose, and each is the residue of a real bug, so I stopped here rather than
buy the line count with content.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CEkhAhgTxbE5TgifyYf8v4
@douglasmun
douglasmun merged commit 8eb6aea into main Aug 23, 2026
2 checks passed
@douglasmun
douglasmun deleted the doc/audit-log-volatility branch August 23, 2026 14:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant