Technical · 4 of 8

The audit spine and the hash chain

One append-only events table per organization, each row sealed with a SHA-256 digest of the row before it, verifiable end to end by a single function.

Append-only, with no escape

events, auth_events, security_events and agent_actions have BEFORE UPDATE and BEFORE DELETE triggers that raise. There is no flag, role or setting that turns them off — the function is three lines and takes no arguments. Every business action writes an event: a phase change, a document received, a step satisfied, a case closed, a handover, a plan change.

The chain

On insert, a trigger takes the organization's latest row_hash under a per-organization lock, and sets the new row's prev_hash to it and row_hash to hex(sha256(prev_hash | seq | ts | org_id | event_type | entity_id | payload)), fields joined with a vertical bar. lt_verify_event_chain(org) walks the organization's events in sequence and returns whether it is intact, how many rows it checked, and the first broken sequence number if any.

Because the digest is a stated formula over exported columns, the chain can be re-verified outside the platform from the CSV in the everything-export. That is the point: trust the arithmetic, not us.

What a superuser can do
Read it. The stress suite shows a superuser's UPDATE and DELETE on an event are refused.
What survives a tenant closing
The spine. Files and access are removed; events are sealed and unreachable. Stated in the terms.
Corrections
Never in place. A correction is a new event that references what it corrects.
Where to verify
  • · db/001, db/041, db/069, db/011
  • · test/audit-chain.test.mjs, test/stress.test.mjs
  • · /api/export/audit and the events.csv in /api/portability/export

File names refer to the codebase. A customer under agreement can be walked through any of them.