Engineering

How we hash-chain the audit log

Every action you take is verifiable, client-side, without trusting our servers. A look at the Merkle structure underneath.

AJAkshay J.·May 5, 2026·12 min read
How we hash-chain the audit log

Key takeaways

  • Each log entry commits to a hash of the previous entry, forming a tamper-evident chain
  • You can verify the full chain client-side with our open-source audit log reader
  • Any deletion or modification breaks the chain at that point — detectable without trusting Inktally
  • Export the root hash to snapshot your log; compare it later to detect retroactive changes

The audit log is only useful if it is trustworthy. An audit log that the operator can quietly edit — removing or altering records — is not an audit log; it is a story the operator tells about the past. We designed Inktally's audit log so that tampering is detectable by anyone, without trusting us.

Why a log needs to be tamper-evident

A simple append-only database is not enough. The operator who controls the database can delete rows, update timestamps, or insert fabricated events. The only way to make a log truly trustworthy without a third party is to make each entry cryptographically dependent on all previous entries — so that changing any entry requires recomputing all subsequent entries, and the final hash can be checked against a prior checkpoint.

This is the same insight behind Bitcoin's blockchain, git's object model, and Certificate Transparency. It is a well-understood structure.

The hash chain structure

Each audit log entry contains: the event data (action, resource, timestamp), a sequence number, and a chain hash. The chain hash is SHA-256(prevChainHash || entryData) — the SHA-256 of the previous entry's chain hash concatenated with this entry's content.

The first entry uses a genesis hash (a fixed, published constant) as its “previous” hash. Every subsequent entry is cryptographically linked to all entries before it.

Merkle roots

We also maintain a rolling Merkle root over the last 1,000 entries, published every hour to our public transparency endpoint. This gives external checkpointing: anyone can record the Merkle root at any time and later verify it has not changed.

See this in practice.

Your vault is encrypted before it leaves your device. Inktally never sees your keys.

Try Inktally free

Client-side verification

The “Verify chain” function in your audit log downloads the full log from our API, recomputes each chain hash from scratch in your browser, and checks that:

  • Each hash correctly follows from the previous.
  • No sequence numbers are missing or duplicated.
  • The final hash matches the most recently published Merkle root.

Verification runs in milliseconds for typical logs. For users with very long histories, we batch the computation across animation frames to avoid blocking the UI.

What it proves and what it does not

A valid chain proves that the log has not been altered since the last checkpoint you trust. It does not prove that every event in the log actually happened — a corrupted server could log false events that hash-chain correctly. For the audit log to be fully trustworthy, you need to verify it against independent evidence (receipts you received, emails you were sent, timestamps you remember).

This is the honest limit of the design. The hash chain prevents the subtler attack — silent deletion or rewriting of real events — which is the attack a motivated operator would actually attempt.

Share this article

Common questions

Questions about how we hash-chain the audit log

01

Questions about how we hash-chain the audit log

Settings → Audit log → Verify chain. The verification runs entirely in your browser against the raw log entries we return. A green checkmark means no entries have been deleted, reordered, or altered.

Get the security writing in your inbox.

No marketing. One email when something worth reading publishes.

No tracking pixels. Unsubscribe any time.