Skip to content

The hash-chained audit log explained

Published May 23, 2026

Every governed action in Kommit writes a row to a tenant-scoped audit log. The log is hash-chained — each row's hash includes the previous row's hash — so retroactive edits or deletions are detectable on inspection. This is the surface auditors look at when they want evidence that controls have actually run.

What's in a row

Each row has at least:

FieldPurpose
idUUID.
org_idTenant binding. Rows are scoped per org by RLS.
actorWho did the action — a Kommit user, an agent, or system.
actionA typed event name (e.g. policy.update, agent.run.approve).
targetWhat the action operated on.
metadataJSONB with action-specific context (before/after for updates, severity for incidents, etc.).
created_atServer-side timestamp, NOT NULL.
prev_hashThe hash of the previous row in the chain.
row_hashSHA-256 of the canonical encoding of this row + prev_hash.
severityOne of info, notice, warning, critical.

The first row in a tenant's audit log has prev_hash = '0' * 64. Every subsequent row's prev_hash is the previous row's row_hash.

What this gives you

  • Tamper-evidence. If anyone — including a Kommit platform admin — edits or deletes a row, the chain breaks at that point. Re-hashing the chain detects the break on the next verification pass.
  • Ordering. Rows have a strict order determined by the chain, not by created_at alone. Two rows with the same timestamp still have a deterministic predecessor relationship.
  • Append-only contract. The application enforces append-only inserts via DB-level triggers; there is no API path that permits update or delete on a single row.

What it doesn't give you

  • It is not a notarised timestamp service. The created_at comes from the application server's clock, not a third-party timestamping authority. If you need cryptographic time attestation (e.g. to defend against backdated entries), you'd layer that on top — RFC 3161 anchoring of the chain head on a cadence is on the roadmap.
  • The hash chain does not replace access control. Anyone with read access to a tenant's audit log sees every row in it; we don't currently support row-level redaction.

Verifying the chain yourself

You can verify the chain integrity from the Kommit dashboard (Compliance → Audit log → Verify chain), or via the API:

POST /api/v1/audit-log/verify

The endpoint walks the chain head-to-tail and returns either { ok: true, rowCount } or { ok: false, brokeAt: <row_id> }. We recommend you run this on a schedule (daily or weekly) and alert on a non-ok response. See [#exporting-audit-logs] for the export format.