Standalone verifier
Copy-paste-ready Node.js verifier for Akaeon Registry records. No SDK. Standard library only. No Akaeon code in the verify path.
The verifier is a single file. It uses Node's built-in crypto module — no third-party SDK, no proprietary cryptography. Three independent checks, run in the lab's environment, against the public Arweave network.
For the production-grade reference with full RFC 6962 odd-count-promotion handling and canonical JSON re-serialization, see the integration runbook §5. The version on this page is the condensed three-check verifier — faithful to the spec, easier to read, and what you'd paste into a smoke test.
The verifier
// verifier.mjs — three independent checks. Standard library only.
import crypto from 'node:crypto'
const SPKI_HEADER = Buffer.from('302a300506032b6570032100', 'hex')
const sha256 = (b) => crypto.createHash('sha256').update(b).digest()
const r = await fetch(`https://api.akaeon.com/v1/lookup?domain=${domain}`).then(x => x.json())
const o = r.optouts[0]
// 1. Ed25519 signature on the registry's canonical message
const pk = crypto.createPublicKey({
key: Buffer.concat([SPKI_HEADER, Buffer.from(o.registry_signature.public_key, 'base64')]),
format: 'der', type: 'spki',
})
const sigOk = crypto.verify(null,
Buffer.from(o.registry_signature.canonical_message, 'utf8'),
pk,
Buffer.from(o.registry_signature.signature, 'base64'))
// 2. Merkle inclusion proof reconstructs the claimed root
let c = Buffer.from(o.merkle_inclusion.leaf_hash, 'hex'), i = o.merkle_inclusion.leaf_index
for (const s of o.merkle_inclusion.merkle_proof) {
const [l, ri] = (i & 1) === 0 ? [c, Buffer.from(s, 'hex')] : [Buffer.from(s, 'hex'), c]
c = sha256(Buffer.concat([Buffer.from([0x01]), l, ri]))
i >>= 1
}
const merkleOk = c.toString('hex') === o.merkle_inclusion.merkle_root
// 3. Arweave-anchored batch payload contains the same root
const ar = await fetch(o.merkle_inclusion.arweave_url).then(x => x.json())
const anchorOk = ar.merkle_root_sha256_hex === o.merkle_inclusion.merkle_root
console.log({ sigOk, merkleOk, anchorOk })
What each check is doing
| Check | What it proves |
|---|---|
| 1. Ed25519 signature | The registry actually attested to this opt-out. The canonical_message and signature are bound to the registry's published public key. |
| 2. Merkle inclusion | This specific opt-out was part of the claimed batch. The proof reconstructs the claimed root from the leaf alone. |
| 3. Arweave anchor | The batch root was actually anchored, at the public-network timestamp. The Arweave transaction body must contain the same root the proof reconstructs. |
If all three are true, the chain from publisher-stated opt-out to Arweave-anchored public timestamp is intact.
What's not in this condensed version
The production reference at integration §5 handles three details this condensed teaser elides:
- Odd-count Merkle promotion. RFC 6962-style trees promote the last node at an odd-count level unchanged, rather than duplicating it (Bitcoin-style). The condensed loop above assumes the leaf path doesn't pass through a promoted node. For non-power-of-2 batch sizes, use the runbook version, which takes
tree_sizeand walks the right edge correctly. - Canonical record re-hashing. The runbook version recomputes the leaf hash from the canonical record bytes (lexicographic key sort, no whitespace, UTF-8) and compares it to
merkle_inclusion.leaf_hash. The condensed version trusts the registry's leaf hash. For the strongest audit posture, re-hash. - Arweave batch signature verification. The runbook version also verifies the registry's signature on the Arweave-anchored batch payload itself, not just the per-record signature.
The condensed version above passes the verify-in-15-lines test for the happy path; the production version passes it for adversarial inputs as well. Use the production version in your audit pipeline.
Why this matters
Because the substrate — Ed25519, SHA-256, Arweave — carries the cryptographic work. The verifier just composes the primitives. If your verifier needs to import an Akaeon SDK, you're no longer verifying against the public substrate; you're verifying against us, and the trust property degrades. The whole point is that you don't have to.