The immutability pitch
Many logging solutions advertise "immutable logs" or "append-only storage." The pitch is: once an event is written, it can't be deleted. This sounds like tamper-evidence. It isn't.
Immutability prevents deletion. It doesn't prevent:
- Pre-write alteration: an event is modified before being written to the immutable store
- Selective omission: the application decides not to write certain events (bugs, deliberate choices, or race conditions)
- Sequence manipulation: events are written to the immutable store out of order, or with gaps
- Metadata modification: the application writes an event with altered actor, timestamp, or metadata
Write-once storage is a property of the storage layer. Tamper-evidence is a property of the event chain. These are different things.
The attacker model
Immutable storage assumes the attacker is trying to delete records after the fact. The more realistic threat in B2B SaaS is a bug, a migration, or an insider who controls the application layer — not the storage layer. Immutability doesn't help you there.
What append-only tables give you
PostgreSQL append-only tables, S3 Object Lock, AWS WORM storage — these ensure that written records can't be overwritten or deleted through normal operations.
This is genuinely useful for:
- Preventing accidental deletion via
DELETE FROM logs WHERE ... - Meeting compliance requirements that specify data retention
- Reducing the blast radius of a compromised application layer
What they don't give you: any way to prove that the records that were written are complete and unaltered. An application with write access to an append-only table can still write events with wrong timestamps, altered metadata, or missing fields. There's no chain to validate.
The difference: cryptographic chain verification
unTamper's hash chain adds something that immutable storage cannot: a mathematical relationship between events that makes incompleteness and alteration detectable.
Event[n]: hash(canonical_payload[n] + prevHash[n-1]) = H[n]
Event[n+1]: hash(canonical_payload[n+1] + H[n]) = H[n+1]
To alter event N, you must recompute H(N), which changes H(N+1), which changes every subsequent hash. The chain is broken. The break is detectable.
To omit an event, the prevHash reference in the following event points to a hash that doesn't exist in the chain. The gap is detectable.
What the chain gives you
The ability to answer "is this chain complete and unmodified?" — not by trusting the storage infrastructure, but by verifying the math.
Common misconceptions
"Our database has row-level security — only the app can write events." Row-level security prevents unauthorized writes. It doesn't prevent the authorized application layer from writing incorrect data, or from having a bug that drops events.
"We log to S3 with Object Lock — that's immutable." S3 Object Lock prevents deletion and overwrite after write. It doesn't verify that what was written was correct, complete, or in the right sequence.
"We use a dedicated logging service that's separate from our main database." Separation is good for defense-in-depth. But if the logging service doesn't implement hash chaining, it still can't prove completeness or sequence integrity.
"Our logs are cryptographically signed." Signing an individual log file or event proves it hasn't been modified since it was signed. It doesn't prove the event was correctly written in the first place, or that events between signatures weren't selectively omitted.
The standard to meet
The test is simple: can you hand your audit log to an independent auditor, with no access to your infrastructure, and have them verify that the log is complete and unaltered?
If the answer requires "trust that our storage is append-only" — you have immutability, not tamper-evidence.
If the answer is "verify the hash chain in the export file" — that's provable integrity.
Immutable logs are a necessary foundation. They're not sufficient for tamper-evidence. unTamper builds the cryptographic layer on top.