← All posts
2026-05-15Engineering· The RiskSonnar team

Why we hash-chain every audit event

Every action in RiskSonnar — analyst disposition, MLRO step-up, agent decision — is hashed, signed, and chained. Here is exactly how and why.

The threat model

Financial-crime systems get audited. When an examiner asks "who disposed alert 8472 on March 14th, with what evidence, and was the record altered after the fact?", you need an answer the examiner can verify without trusting you. That is the whole point of an audit chain.

We picked a Merkle-style hash chain with Ed25519 receipts as the substrate for every auditable event in RiskSonnar. This post explains the construction, the threat model, the verify endpoint, and the trade-offs we made along the way.

The construction

Each audit event is a JSON object. Before we write it, we:

  1. Canonicalize the JSON. We use RFC-8785 JSON Canonicalization Scheme (JCS) so the byte representation is stable across machines, languages, and clock skew. Two analysts producing the "same" event on two pods produce the same canonical bytes.
  2. Hash the canonical bytes with SHA-256. Call this event_hash.
  3. Chain it. The new event carries prev_chain_hash = the previous event's chain_hash for that tenant. We compute chain_hash = SHA256(prev_chain_hash || event_hash). The very first event uses a tenant-pinned genesis hash.
  4. Sign the chain_hash with the orchestrator's Ed25519 key. The signature lands in the receipt.

The result: every event is bound to every event that came before it. Flip one byte of one historical event and every subsequent chain hash diverges — verifiable in O(n) by anyone with the manifest.

Why JCS, not "just JSON.stringify"

JSON.stringify is not deterministic across languages: key order, number representation (1.0 vs 1), and Unicode escapes all differ. RFC-8785 pins all of those decisions. Auditors with a Python verifier and engineers with a TypeScript producer end up with identical bytes — which is the only way the hash check is meaningful.

The verify endpoint

We expose a public /v1/trust/{tenant_slug} endpoint that returns:

  • chain_length — number of events
  • chain_verify_ok — boolean
  • chain_last_event_utc — most recent event timestamp
  • manifest_sha256 — root hash anyone can independently recompute
  • trust_score — derived signal, intentionally PII-free

That endpoint backs the public /trust/<slug> dashboard. Customers publish the URL on their status pages and LinkedIn; auditors and prospects verify the chain in real time without logging in.

What this is not

Hash-chaining proves integrity, not truth. If an analyst writes a wrong disposition, the chain happily records the wrong disposition forever. The point is that no one — not the analyst, not the MLRO, not the platform operator — can alter the record after the fact without being caught.

That is exactly the property the FATF and AMLD6 audit expectations care about: not that everyone is right all the time, but that the record of what each party did is tamper-evident.

Trade-offs we considered

  • Blockchain anchoring: we considered notarizing daily roots to a public chain (Bitcoin, Ethereum). Useful, but the verify story without it is already strong: the per-tenant manifest is reproducible from the events alone. We may add anchoring as an Enterprise-tier option.
  • HMAC instead of Ed25519: simpler key management but weaker — HMAC requires the verifier to hold the key. Ed25519 lets the verifier hold only the public key. For a customer-facing trust dashboard that matters.
  • Aggregate signatures: we batch some high-volume event families (per minute) under a single signature to keep cost down without losing per-event integrity (each event still chains).

What to do with this

If you are evaluating RiskSonnar, point your browser at /trust/<your-tenant-slug> once you have one and watch the chain length tick. Hand the URL to your external auditor and ask them to verify the manifest from the raw events. That conversation is the whole reason we built it this way.


Read more from the engineering team on the /blog index, or skip straight to the Trust dashboard primer.

Read more from the team →

More engineering deep-dives, compliance commentary, and product updates on the blog index.