All posts
·6 min read·#audit#cryptography

What 'tamper-evident' actually means at audit time.

SHA-256, append-only Postgres triggers, and an offline verify CLI. A regulator-grade audit pitch is a cryptographic claim; here's the substrate.

“Tamper-evident” is a marketing word until you can write down what it would mean for the claim to fail. We built Yelt's audit ledger so that the failure mode is concrete and the verification is independent.

Three properties hold

  • Append-only. Postgres triggers reject UPDATE and DELETE on the audit_events table. That's the database layer, not just the application layer. Direct SQL access can't silently amend history.
  • Hash-chained. Each row contains prev_hash (SHA-256 of the previous row's canonical payload) and hash (SHA-256 of this row's canonical payload). Mutating row N — even the single byte — changes its hash and breaks every downstream row's chain link.
  • Per-org sequences. Each org has its own Postgres SEQUENCE backing seq_num. Concurrent writes serialize on a chain-head FOR UPDATE lock; sequence gaps are immediately observable.

What ‘canonical payload’ means

The hash is computed over a deterministic byte serialization of each event. Every field of the row that contributes to integrity — kind, action, decision, evidence_refs, timestamp, prev_hash, sequence number — is included. JSON keys are recursively sorted. Strings are NFC-normalized. Bytes are hex-encoded. BigInts are rendered as decimal strings. Two implementations of the spec produce bit-identical bytes for the same input. (The full spec lives in docs/design/audit-hash-chain.md.)

Independent verification

The cryptographic claim only matters if the customer can verify it without trusting Yelt. Two paths:

  • Online. The dashboard's Audit tab exposes a verify chain button that walks the chain end-to-end against the canonical serializer.
  • Offline. The yelt audit verify CLI takes a JSON export, runs the same algorithm locally, and confirms chain integrity without network access. An auditor can do this on their own machine.
The thing that distinguishes a regulator-grade audit pitch from “trust us” is the offline verification path. Yelt ships the offline path.

What we don't do

  • Auto-skip corruption. If chain validation fails, we freeze ingestion for that org and page ops. There's no “accept new events on a forked chain” path. Better to be down than corrupt.
  • On-chain anchoring (yet). v1 ships per-event chains. v2 enterprise SKU adds Merkle root commits to a public chain via OpenTimestamps; the per-event chain stays as-is.

Why this matters

AI agents are about to do a lot more financial work. Auditors already know what they want to see, even if they don't know yet that they want to see it from agents. Yelt's job is to give them the artifact they recognize — append-only ledger, hash chain, independent verification — for the new substrate.