Your audit log is a story, not evidence
Most audit trails are rows in a table the operator can edit. That gap stays invisible until the day it matters — and by then the log is all anyone has.
Every system claims to have an audit log. Almost none of them have evidence.
The difference is not academic. An audit log answers what does the system say happened. Evidence answers what can you prove happened, to someone who does not trust you. Most implementations quietly deliver the first while everyone in the room assumes the second.
The shape of the problem
A typical audit trail is a table. It has a timestamp, an actor, an action, and maybe a JSON blob of context. It is written by the same application that performs the action, into the same database, under the same credentials.
Which means anyone who can reach that database can write to it. That set is larger than most teams admit: the application itself, the ops engineer with production access, the backup restore process, the migration script someone ran at 2am, and whoever compromised any of the above.
There is a second, quieter failure. Deletion leaves no trace. If a row is removed, the table is still perfectly consistent — it simply describes a world where that event never occurred. Tampering with an append-only story is easy when nothing outside the story counts the pages.
What “sealed” has to mean
For a record to function as evidence, three properties have to hold at once:
- Each record commits to its own contents. A hash over the canonical serialization of the record, computed at write time. Change a field later and the hash no longer matches.
- Each record commits to the ones before it. Chaining hashes means you cannot remove a record without breaking every link after it. Deletion stops being silent.
- The commitment is signed by a key the writer does not hold. This is the part that gets skipped. A hash chain the application can recompute is a hash chain the application can rewrite. Someone other than the operator has to attest to the chain’s state at a point in time.
Miss the third and you have built an integrity system that protects you from accidents but not from adversaries — including the adversary who is you, six months from now, under pressure, with a lawyer in the room.
Why this gets skipped
Because the first two are cheap and the third is a distributed systems problem.
Hashing a record costs microseconds. Chaining costs a lookup. But signing under a key the writing system cannot access means a separate trust boundary: a different key custodian, a different failure mode, a latency budget, and a decision about what happens when the signer is unreachable. That is real engineering, so it turns into a ticket, and the ticket turns into next quarter.
Meanwhile the audit page ships. It looks correct. It is correct, right up until the moment its correctness is contested.
The test
There is a single question that separates the two categories, and it takes ten seconds to ask:
If someone with production database access changed one field in a record from last March, would anything anywhere notice?
If the honest answer is no, you have a log. It is a useful log — for debugging, for support, for reconstructing what went wrong. It is simply not the thing you will need on the day someone disputes what happened.
That day arrives rarely. When it does, the log is usually all that remains, and its usefulness was decided long before by an architectural choice nobody flagged as important.
Axowl seals identity, approval, and audit events as they are written — hash-chained, and anchored under a key the writing service does not hold. See how it works.