Sigilbase is in early access, free while we're in beta. Your data and evidence are permanent.

Writing

How to implement provable audit logs, step by step

Making an audit log cryptographically provable takes four ingredients: a hash chain that links every event to all history before it, Merkle checkpoints that seal batches of events, signatures that pin each checkpoint to an identity and a moment, and a verifier that outside parties can run without trusting you. None of the ingredients is exotic. The difficulty is in the joints between them, and in the operational discipline that keeps the guarantees honest.

This guide walks the implementation step by step, in build order, for the engineer who has been asked to make the company's audit trail stand up to hostile scrutiny, and for the compliance lead who wants to understand what they are asking for. If you want the conceptual grounding first, start with our explainer on tamper-evident audit logs.

Step 1: define the event record

Everything downstream hashes bytes, so the record structure comes first and changes are expensive later. Per event, capture the actor (user, service, or API identity), the action as a stable dotted name such as invoice.voided, the resource affected, the outcome, a UTC timestamp from a synchronised clock, the origin, and a payload object for action-specific detail. Assign each event a sequence number from a single writer per stream, because a total order is what a chain is, and distributed writers that each believe they hold the next sequence number produce forks, not logs.

Two decisions deserve more care than they usually get. First, version the schema from day one, with an explicit version field on every event, because the log is immutable and version two will otherwise have to coexist with unmarked version one records forever. Second, decide what never enters the payload: raw secrets, card numbers, and personal data that retention rules will later want deleted have no place in a record designed to be undeletable. Store a hash of a sensitive value when you need to prove it was present without storing it.

Step 2: make the write path append-only

Before any cryptography, enforce the discipline it will attest to. The log store accepts inserts and nothing else: the writing service's credentials get INSERT but not UPDATE or DELETE, no human holds standing write access, and administrative access to the underlying storage is itself an audited event. Writes must be idempotent, keyed on an idempotency key the producer supplies, so that retries after a timeout cannot create duplicate history. This layer does not make the log provable, but the cryptography below detects violations rather than preventing them, and an append-only write path is what makes violations rare enough that detection is the backstop instead of the routine.

Step 3: chain every event

On write, each event is hashed, and the hash input includes the previous event's hash, so the log becomes a linked sequence in which no historical record can change without breaking every link after it.

The step that ruins more implementations than any other is serialisation. A hash function takes bytes, and an event is a structure, so there must be exactly one way to turn the structure into bytes: fixed field order, fixed encoding, one representation for integers, floats, nulls, and timestamps, no locale anywhere near it. Write this rule down as a specification, because the verifier in step 6 must reproduce it exactly, from a different codebase, possibly in a different language, years later. Every ambiguity you leave becomes a verification failure on records nobody touched, and a verifier that cries wolf is worse than none.

Store the hash alongside each event, and recompute rather than trust on read. The chain is now tamper-evident against everyone except the operator, who could still rewrite history and recompute every hash. The next two steps close that hole.

Step 4: seal batches into signed checkpoints

On a short interval, minutes rather than days, build a Merkle tree over the events since the last checkpoint and record a checkpoint containing the range covered, the Merkle root, the timestamp, and a signature over it all. Sign with an asymmetric key, Ed25519 being the common modern choice, and publish the public key.

The checkpoint does three jobs at once. It makes proofs cheap: a Merkle inclusion proof shows any single event is committed under a signed root using a handful of hashes rather than a replay of the whole chain. It makes rewriting detectable even by the operator: history under a checkpoint that has been signed and shared cannot change without a signature mismatch against copies already in other hands. And it bounds the exposure window, since events are fully protected once sealed, which is the argument for sealing every few minutes rather than nightly.

Key handling decides whether the signatures mean anything. The private key lives where the sealing service can use it but people cannot extract it, a KMS or HSM in practice. Rotation is designed in from the start: checkpoints record which key signed them, and the succession of keys is itself recorded in the log, so a verifier can establish which key was legitimate for which period without asking you.

Step 5: prove inclusion and consistency

A provable log answers two questions on demand. Is this specific event in the log? An inclusion proof, the path from the event to a signed checkpoint root, answers it in a few hashes. Is the log I saw last quarter a prefix of the log I am looking at now? A consistency proof between two checkpoint roots answers that, and it is the question that catches an operator who deleted a range and re-sealed, because the new history no longer extends the old one. Exports should package all of it: the events in a range, their chain hashes, the inclusion proofs, the covering signed checkpoints, and the public keys, as one self-contained bundle that needs no access to your systems.

Step 6: make verification independent

This step decides whether you built evidence or decoration. The verifier must be a separate implementation that reads a bundle and recomputes everything, the serialisation, the chain, the Merkle roots, the signatures, sharing no code with the system that writes the log, so that a bug cannot hide in both. It must run offline, with no call back to you, because a verification that depends on the operator's API is a trust decision wearing a costume. And it should be source-available to the people relying on it, since "run our binary" reintroduces the trust the whole design exists to remove. This is why Sigilbase ships its verifier as a standalone open-source tool inside every evidence bundle rather than as an endpoint: an auditor who cannot check the checker has just moved their trust, not eliminated it.

Step 7: anchor checkpoints externally

A signature proves who sealed history, not when. The final hole is the operator who rewrites history and re-signs all of it with the legitimate key. Closing it requires putting checkpoint roots beyond the operator's reach: publish them to a public transparency feed, obtain trusted timestamps over them, or hand them periodically to the parties who will one day verify, an auditor being the natural recipient. Any copy of a checkpoint held outside the operator's control turns wholesale rewriting from undetectable into provable.

Operate it honestly

Three operational habits keep the guarantees real. Alert on silence: a sealing process that stops, a sequence that gaps, or a chain verification that fails is an incident, not a log line, and PCI DSS explicitly treats logging stoppage as a reportable event class. Run your own verifier continuously against your own log, because finding a break first is considerably better than an auditor finding it. And document the claims precisely, including what a passing verification does not prove, covered in the honest limits of tamper evidence, since discovered overclaiming costs more credibility than modest claims ever earn.

Build or buy

Everything above is buildable by a competent team, and for some, platform teams with transparency-log experience, regulated firms with bespoke requirements, building is the right call. The honest cost estimate is not the hash chain, which is a week, but the canonical serialisation specification, the key ceremony and rotation design, the consistency proofs, the independent verifier maintained as a separate codebase, and the anchoring infrastructure, which together are a quarter or more of skilled work plus permanent ownership. That calculus is why we built Sigilbase: the chain, checkpoints, signatures, evidence bundles, and the open-source offline verifier as a service behind one ingestion API. If you would rather not build it, our comparison of cryptographic audit log solutions covers the managed services and open-source building blocks side by side. Whichever route you take, the test to hold the result to is the same one an auditor will apply: can someone who does not trust you check that history is intact, without your help?

For what the surrounding frameworks require from the log you build, see our guide to audit log requirements for SOC 2, ISO 27001 and PCI DSS; for wiring producers into an ingestion API well, see our audit event ingestion best practices. And if the log you are building is the one your enterprise customers will ask to see, the enterprise audit logs page maps what their security reviews expect of it.

Sigilbase turns audit logs into provable evidence. Start free and record provable history from the first event.

FAQ

Frequently asked questions

How do you make an audit log cryptographically provable?

Chain every event to the previous one with a cryptographic hash, seal batches of events into Merkle checkpoints, sign each checkpoint root with a private key, and give outside parties a way to recompute all of it independently. Alteration of any historical record then breaks the chain or the signatures, and verification becomes a computation rather than a trust decision.

What is canonical serialisation and why does it matter for hashing?

Canonical serialisation is a fixed, deterministic way of turning an event into bytes before hashing, covering field order, encoding, and the representation of numbers and timestamps. Without it, the same logical event can produce different hashes on different runs or platforms, and verification fails on records that were never touched. It is the most common implementation mistake in hash-chained logs.

What is a Merkle checkpoint in an audit log?

A checkpoint seals a batch of recent events by building a Merkle tree over them and recording the root hash, which is then signed. It gives a compact, dated commitment to the entire history at that moment, lets individual events be proven present with short inclusion proofs, and prevents the operator from silently recomputing the chain, since rewriting history would invalidate signatures already issued.

Why do signed checkpoints need external anchoring?

A signature proves who sealed the history but not when, and an operator who controls both the log and the keys could re-sign a rewritten history. Publishing checkpoint roots somewhere the operator cannot retro-edit, such as a transparency feed or a trusted timestamping service, pins each checkpoint to a point in time and makes wholesale rewriting detectable by anyone holding an earlier copy.

Can you retrofit tamper evidence onto an existing audit log?

You can start chaining and sealing from a cut-over point, and everything after that point gets the full guarantees. History before the cut-over cannot honestly be made tamper-evident, because there is no way to prove it was not altered before the chaining began. The honest approach is to seal the legacy history as a one-off signed snapshot and state clearly when the guarantees begin.

What keys are needed to sign audit log checkpoints?

One asymmetric signing key pair, commonly Ed25519, held so the log-writing service can sign but application and operations staff cannot extract the private key, ideally in a KMS or HSM. The public key is published so anyone can verify. Plan for rotation from the start, since verifiers must know which key was valid for which period without trusting the operator's word for it.

Start recording provable history

Chained, sealed, independently verifiable audit logs, from the first event. Free while Sigilbase is in beta.

Start free

Questions first? Write to hello@sigilbase.io.

Privacy

This site runs no analytics and no trackers.

The site itself collects nothing. If you create a Sigilbase account, the data that involves is described in the privacy policy.

To have your email removed, contact hello@sigilbase.io.

Read the full privacy policy

Last updated July 2026