Audit Attestation

The audit log is hash-chained per Org, but a chain is only useful if you can prove a specific entry is in it without trusting our infrastructure. The attestation subsystem publishes RFC 6962-shaped Merkle roots over windows of audit_log.this_hash entries, signs them with a KMS-managed key, and serves inclusion proofs that an offline verifier can check against the published root.

Why RFC 6962

RFC 6962 is the Certificate Transparency Merkle construction. We adopt the same domain-separation rules so anyone can plug Tetrapus roots into existing CT-compatible verification tooling.

  • Leaf hash: H(0x00 || leaf_bytes)
  • Inner node: H(0x01 || left || right)
  • Odd nodes are promoted up the tree, never duplicated (matches RFC 6962 §2.1)
  • MerkleProof::verify requires leaf_count to reconstruct the same promotion pattern

Root construction

A scheduled job inside tetrapus-server reads the next window of audit_log rows for an Org, hashes each this_hash as a leaf, builds the tree, signs the root with the Org's audit-scope KMS binding, and persists the result.

graph LR L0["leaf seq=N+0"] --> H0 L1["leaf seq=N+1"] --> H0["inner H0"] L2["leaf seq=N+2"] --> H1 L3["leaf seq=N+3"] --> H1["inner H1"] H0 --> R["root R"] H1 --> R R --> SIG["KMS sign_es256(R)"] SIG --> ROW["attestation_roots row"]

attestation_roots table

Column Type Notes
idUUIDPK
org_idUUIDFK orgs(id), cascade
period_start / period_endTIMESTAMPTZWindow covered
audit_seq_start / audit_seq_endBIGINTSequence range
merkle_rootBYTEA32 bytes (SHA-256)
signatureBYTEAES256 r||s, 64 bytes
signing_key_kidTEXTJWKS kid for verification
published_atTIMESTAMPTZDEFAULT now()

REST routes

All three endpoints are mounted under /api/v1/admin/attestation. Reading roots requires the standard admin role; /verify is unauthenticated by design — it is intended to be called from an offline auditor laptop.

Method + path Purpose
GET /admin/attestation/rootsPaged list of published roots for the caller's Org
GET /admin/attestation/roots/{id}/proof?seq=NMerkle inclusion proof for audit row at seq N
POST /admin/attestation/verifyOffline verifier endpoint — returns OK iff proof reconstructs root

Inclusion proof shape

A proof is the ordered list of sibling hashes a verifier needs to walk from the leaf back to the root, plus the leaf index and total leaf count.

graph BT LEAF["target leaf"] --> S0 S0["sibling at depth 0"] --> NODE0["recomputed inner"] NODE0 --> S1 S1["sibling at depth 1"] --> NODE1["recomputed inner"] NODE1 --> S2 S2["sibling at depth 2"] --> R["root"] R --> CMP{"== published root?"} CMP -->|yes| OK["inclusion proven"]

Offline CLI

tetrapus-admin attestation verify wraps the same logic that /admin/attestation/verify implements. Auditors can pull a root + proof + leaf hash from the API and verify offline against the published JWKS.

Bash
tetrapus-admin attestation verify \
  --root  ./roots/2026-04-01.json \
  --proof ./proofs/seq-12345.json \
  --leaf  $(tetrapus-admin audit hash --seq 12345)
# OK: leaf 12345 is included under root id=ae2d… signed by kid=jwk_audit_2026q2

Related

Questions?

Reach out for help with integration, deployment, or custom domain codecs.