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:
| Field | Purpose |
|---|---|
id | UUID. |
org_id | Tenant binding. Rows are scoped per org by RLS. |
actor | Who did the action — a Kommit user, an agent, or system. |
action | A typed event name (e.g. policy.update, agent.run.approve). |
target | What the action operated on. |
metadata | JSONB with action-specific context (before/after for updates, severity for incidents, etc.). |
created_at | Server-side timestamp, NOT NULL. |
prev_hash | The hash of the previous row in the chain. |
row_hash | SHA-256 of the canonical encoding of this row + prev_hash. |
severity | One 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_atalone. 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_atcomes 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.