Skip to content

Verify the seal yourself

Every voucher carries an append-only chain of sealed events: minted, handed over, reserved, redeemed. Each event’s hash covers the one before it, so an edit anywhere breaks everything after it.

You can check that without asking us anything.

const result = await bm.integrity.verify(code);
// → { verdict: 'verified', chainIntact: true, events: [...] }

verify() fetches the chain and recomputes the hashes locally. The word verified is your conclusion, not our claim.

They fail for different reasons, and conflating them hides which one broke.

Chain — event N’s previousHash must equal event N−1’s recordHash, and the first must link to sixty-four zeros. Needs no payloads.

Content — SHA-256 over five canonical pieces must reproduce recordHash:

SHA-256( "{entityType}:events", entityId, payload, previousHash, sequenceNumber )

with two more fields when the event was sealed with an actor. Needs the payloads, which only your own key can fetch — the public endpoint withholds them, because a voucher payload contains redeem codes.

verdictmeaning
verifiedeverything links and everything re-hashes
chain-brokenan event does not link to the one before it
tampereda payload no longer produces its stored hash
legacysee below
unsealedan event has not been anchored yet
uncheckedpayloads were not supplied, so only the links were checked

Events sealed before 2026-08-02 11:00 UTC were stored in a jsonb column, and Postgres normalised their JSON on the way in — reordering keys, changing spacing. The canonical string those rows were hashed from no longer exists, so they can never re-hash, even though nobody touched them.

Any correct implementation reports those as legacy, never tampered. The safe direction of error is to call a genuine row legacy; the unsafe one accuses a brand of forgery over a schema migration.

The algorithm is public and the conformance vectors are in the repository. The one thing that catches everybody: the server serializes with .NET’s System.Text.Json default encoder, which does not agree with JSON.stringify.