What a hash-chained audit log actually proves
When an agent acts with real credentials, "what happened?" is a security question. Not only was this action allowed, but can anyone quietly rewrite the story afterwards. AgentLaw's answer is a hash-chained audit log, specified in RFC-0002. This post is about what that structure actually guarantees — because "cryptographic audit trail" is the kind of phrase that gets waved around, and precision is cheaper than disappointment.
The mechanism
Every decision the policy engine makes — allows, approvals, and refusals alike — is appended to a log. Each entry carries a hash of its own content, and each entry also carries the hash of the entry before it. That second part is the chain: entry n is committed to entry n−1, which is committed to n−2, and so on back to the first record.
Verification is a walk: recompute every hash, check every link, confirm the sequence numbers do not skip. The agentlaw audit verify command does exactly this, and nothing more exotic.
Now suppose someone edits an old entry — say, a refusal is rewritten to read as allowed. The entry's content no longer matches its stored hash, so verification points straight at it. Suppose they also recompute that entry's hash to cover the edit. Then the next entry's link — which committed to the old hash — no longer matches, and verification points there instead. To hide one edit cleanly, every subsequent entry must be rewritten too. The chain converts "quietly change one line" into "forge the entire history from that point forward," which is a much louder act.
The live demo on the AgentLaw site lets anyone try this: rewrite an entry and watch verification name it, together with everything after it.
Tamper-evident, not tamper-proof
Here is the honest part. Nothing in this structure prevents tampering. An attacker with write access to the log can edit it; an attacker who controls the whole file can delete it, truncate it, or regenerate it from scratch with valid hashes throughout. What the chain guarantees is narrower and still valuable: an alteration inside a log you retain cannot be selective and silent. Either the story is intact, or verification tells you where it stopped being intact.
The guarantee is also conditional on where the log lives. A chain regenerated end-to-end is only detectable if some earlier copy — a mirrored sink, a backup, a previously recorded head hash — exists to disagree with it. This is why the audit package treats external sinks as first-class: a copy on a machine the agent's host cannot reach turns "forge the entire history" from expensive into visible.
So, stated precisely:
- It proves: that the log you hold has not been selectively edited since it was written; that any break has a location; that refusals were recorded, not just successes.
- It does not prove: that no one with full control of the host behaved well; that a log an attacker fully regenerated is authentic, absent an external reference; that the actions themselves were wise — only that they were decided and recorded as written.
Why refusals are in the log
A detail that is easy to miss: denied calls are appended too. An audit trail that only records what happened is half a trail. The attempts that were stopped are often the interesting part — a burst of denied payment calls says something a clean log of allowed ones never will. Silent drift in an agent's behavior shows up here first, which is why the audit trail is listed in the project's scope as a defense in its own right.
The spec is the product
The chain format, the entry fields, and the verification procedure are specified in RFC-0002 so that an independent implementation can verify a log without running any AgentLaw code. That is deliberate: an audit format you can only check with the tool that wrote it is asking for the same trust it was built to remove. The RFC is in Draft, the implementation is @agentlaw/audit, both are Apache-2.0, and disagreement with the spec is welcome in the open — that is what Draft means.