Module Algostream_infrastructure_persistence.Audit_log

Append-only, hash-chained audit log.

Why this is not Event_log

Event_log already frames records with a length and a CRC32 and reads back independently, so reusing it looks obvious. Four things rule it out, each on its own sufficient:

The framing shape is borrowed on purpose, so the two files read as siblings.

The chain

hash_0 = SHA-256("algostream-audit-v1\x00" || prev_file_hash)
hash_n = SHA-256(hash_(n-1) || canonical_n)

On disk each frame is u32 length | canonical | 32-byte hash. The previous hash is not stored alongside — it is the previous frame's hash, and writing it twice would create a second thing that can disagree with the first.

Files rotate daily as audit-YYYYMMDD.log. Each header carries the previous file's final hash and name, so the chain survives rotation and Verify.directory can walk a whole directory.

What this cannot do, and what the anchor is for

Read on its own, verify catches a modified record, a deleted interior record and a spliced-in frame — each shows up as a chain mismatch at the first record the attacker did not also rewrite.

But the chain is unkeyed, so anyone who can write the file can recompute every hash from the point they changed onward and hand you a file that verifies perfectly. Two consequences follow, and both are real:

So the chain's actual guarantee is narrower than "the file is tamper-proof", and stating it plainly matters more than the property sounding strong: any change to the log moves the head hash. That is what makes it evidence.

Which is why the out-of-band anchor is not optional. algostream-auditctl head prints the current sequence number and head hash; record it somewhere the daemon — and therefore anyone who compromises the daemon — cannot write. A later verify compared against that anchor closes every gap above. Without an anchor this is a corruption check with good manners, not tamper-evidence.

HMAC-chaining with a secret was considered and rejected: the key would have to live on the machine the attacker owns, so it moves the problem rather than solving it.

This log is not certified against any regulatory regime. It is a defensible record of control actions, not a compliance claim.

val magic : int32
val version : int32
val header_size : int
module Writer : sig ... end

Append-only writer. One per directory; not thread-safe, so the daemon owns exactly one and calls it from the single dispatcher fiber.

module Reader : sig ... end
module Verify : sig ... end