Expand description
The write-ahead journal (§12, §12c).
Frame (format 2): len: u32 | crc32c: u32 | seq: u64 | prev: [u8; 32] | payload, little-endian, where len counts prev and the payload. The
CRC covers a format tag, seq, prev and the payload.
Hash chain (I18). prev is the chain head before this record, and the
head after it is SHA-256(prev ‖ seq ‖ payload); the first record after
an empty journal chains from zero, and the checkpoint carries the head at
its seq in its own prev. A scan accepts frames only while each is
intact, its seq is exactly one more than the last, and its prev is the
current head. An intact frame that does not chain is an integrity failure.
A frame that fails its CRC is the torn tail only if no intact record with
a later seq follows it; then its header is zeroed and it is never
interpreted. Otherwise a record in the middle was damaged or edited, which
is an integrity failure too, and nothing is truncated: the evidence stays.
The chain has no key: it detects damage and naive edits, not an attacker with root who rewrites every later record and the checkpoint consistently (measured boot, Phase E, anchors the head outside the disk).
Compaction is checkpoint-then-truncate. The ledger is written as the checkpoint (atomic replace), then the log restarts at offset 0. Bytes left over from before compaction all carry seq ≤ the checkpoint’s seq, so they can never pass as the next record.
Several handles (the daemon and the watchdog) may share a store. Every append takes the store lock and first catches up on records others wrote.
Structs§
- Frame 🔒
- An intact frame: its seq, the chain head before it, its payload, and its size.
- Journal
- Locked
- A journal handle with the store lock held (see
Journal::locked).
Enums§
Constants§
Functions§
- chain
- The chain head after a record (I18).
- could_
follow 🔒 - Whether
bytescould start a frame written afterlast_seq, judged on the header alone, before any CRC (the scan’s search for a later record). Its length fits, and its seq is pastlast_seqby no more than the number of framescapacityholds: every record this segment can contain is within that, and leftovers from before a compaction carry seqs at or below the checkpoint’s. And its payload starts as every appended one does, with the entry’s own seq ({"seq":<seq>,,Entry’s first field): at a misaligned offset inside a leftover frame, a length and a seq can both look plausible, but not that too. Without these the search ran a CRC over the claimed length at every byte offset, quadratic in the store after a compaction (stale seqs and hashes read as plausible lengths). - crc 🔒
- delete
- Fault injection (tests and the gym): remove the
nthrecord and re-frame every record after it one seq lower, each with its ownprevand a valid CRC, so every frame is intact and the seqs run on; zero what is left at the end. The chain then breaks at the record after the deleted one (I18). False if there is no such record. - frame 🔒
- legacy_
frame 🔒 - Whether
bytesstart with an intact frame of format 1 (no chain, plain CRC). - sentinel_
txn 🔒 - The transaction a sentinel names (
RecoveryFailed txn=<id> …), if any. - tamper
- Fault injection (tests and the gym): rewrite the payload of the
nthrecord in the log (0-based, from offset 0) withedit, keeping itsprevand recomputing its CRC, as someone editing the file would. The chain then breaks at the record after it (I18). False if there is no such record or the edit changes the payload’s length. - unframe 🔒
- Parse a frame header + body from
bytes;Noneif it is not intact.