Docs · verification spec v1

Verify a stamp without trusting us

Everything needed is public: the proof bundle at GET /api/v1/verify/<stamp-id> (or /api/v1/verify/by-seq?stream=<uuid>&seq=<n>) and any RPC endpoint of the anchoring ledger (Base). All hashes are lowercase hex; all hashing is SHA-256.

1 · Canonical payload

A payload has exactly one byte encoding — canonical JSON, UTF-8:

2 · Commitment

commit_hash = SHA256( canonical_payload_utf8 || salt )   # salt: 32 bytes, published at reveal

While a stamp is sealed, only its inclusion and chain position are verifiable — that is the point of sealed commitments.

3 · Append-only chain

prev equals the previous stamp's commit_hash in the same stream (32 zero bytes for seq 1); seq increases by exactly one. Hiding any record breaks every later commitment.

4 · Merkle inclusion

leaf = SHA256(0x00 || commit_hash)
node = SHA256(0x01 || left || right)

acc = leaf
for step in proof.path:
    acc = SHA256(0x01 || step.hash || acc)   if step.side == "left"
    acc = SHA256(0x01 || acc || step.hash)   if step.side == "right"
assert acc == merkle_root

5 · Ledger anchor

tx = eth_getTransactionByHash(tx_hash)
assert tx.input == "0x" + merkle_root
block = eth_getBlockByNumber(tx.blockNumber)
assert block.timestamp == anchored_at   # the authoritative timestamp

Reference verifier

A complete reference implementation (Node, zero dependencies, no Presaid imports) ships in the repository as docs/verify-independently.mjs:

node verify-independently.mjs https://presaid.io/api/v1/verify/<id> \
     --rpc https://mainnet.base.org --chain

A stamp is verified when the commitment recomputes (once revealed), the chain links, the Merkle path folds to the root, and the ledger transaction carries that root. Any failing step means the record does not hold.