·Demo

Five sealed bids, one anchor, one row revealed.

Five bidders sealed an amount each. The whole table was committed on chain in a single transaction (merkle-row-sealed-v1). Later, only one row was disclosed — the other four stay HMAC-sealed under salts the auditor never sees. Selective disclosure is the property a plain log can’t give you.

01Why this only works on chain

Cheaper infrastructure can’t do all three.

Signed logs, append-only DBs, transparency logs — any of those can give you tamper-evidence. None of them can give you tamper-evidence plus a public timestamp plus selective disclosure to a counterparty who doesn’t trust you. Anchoring one Merkle root binds all five bids to a moment in BSV chain order; per-leaf salts let you reveal one bidder without unsealing any other.

What this prevents.
  • Late edits to any bid. No bidder can change a bid after the anchor — every row is folded into the Merkle root, and the root’s SHA-256 is already in a Bitcoin SV block.
  • Pre-reveal copying. No bidder could have read another’s amount before the anchor — each leaf is a salted HMAC the holder keeps private.

Operator collusion and sibling-unsealing are covered the same way — full threat model in the spec: what sealed mode protects against and what it does not.

What this does not prevent: a holder choosing a smaller table (dropping a bidder before anchoring) or relabelling rows. If you need to bind the table’s scope first, anchor the bidder list with commit-reveal before sealing the bids. Then the holder is bound to disclose those rows.

One anchor · many items · one revealed

Selective disclosure manifest

One manifest anchor binds many items. Later, disclose only the row or artifact under review — the rest stay sealed.

  1. Row 0 Sealed
    commit
    9f3a…c1b2
    salt
    sealed
  2. Row 1 Sealed
    commit
    7b1e…d3f4
    salt
    sealed
  3. Row 2 Disclosed
    commit
    5c7d…e9a0
    salt
    f3b7…2cd1
    payload
    re-hashable by reviewer
  4. Row 3 Sealed
    commit
    2d8a…b6c7
    salt
    sealed
  5. Row 4 Sealed
    commit
    1a9f…8d2e
    salt
    sealed

Merkle root — one on-chain anchor

7d9f2e4b6a3c1d0e9b8a7c6d5e4f3a2b1c0d

Committed once on the public chain (BSV). The bundle carries the proof path; a verifier recomputes the root locally — no Satsignal account.

What gets revealed

  • Only Row 2’s payload and its salt
  • Only the sibling commitments on its Merkle path
  • Rows 0, 1, 3, 4 stay opaque — commitments only

Why this matters

  • Sealed bids & eval rows audited one line at a time
  • Claims packets, contracts, change orders, photos
  • Prove one record without exposing the batch

What a reviewer does

  • Re-hash Row 2’s payload with its salt
  • Walk the proof path to the committed root
  • Confirm the root’s on-chain anchor

Privacy by design·Cryptographically anchored·Publicly verifiable

02Phase 1 — Commit (one anchor for all five)

Five bids became one Merkle root.

Each bid was sealed into one leaf, the five leaves were folded into a single Merkle root, and a tiny commit doc binding that root and the leaf count was anchored on chain — one transaction, one OP_RETURN, the whole table sealed.

Commit-doc shape + byte-level construction (per-leaf HKDF salts, HMAC leaves, the canonical commit doc) → /spec-merkle-row.

On-chain anchor

One transaction. One Merkle root. Five sealed leaves.

Scheme
merkle-row-sealed-v1
Bundle ID
007dff5816814594
Commit-doc SHA-256
f3dd7420…608ef3584
Merkle root
a67956a2…ae477bf7
Leaf count
5
Transaction
d623cb94…53a6c4dcc8
Anchored
2026-05-08 (BSV mainnet)
Download commit doc (JSON)

Public lookup, no auth

Resolve commit-doc SHA → bundle + txid.

Anyone can hit /lookup_hash on the public lookup endpoint with the commit-doc SHA-256 and get back the bundle id, txid, and anchor time — no API key, no Satsignal account required.

curl 'https://proof.satsignal.cloud/lookup_hash?sha=f3dd742039a4c287d23f735cb59833f669d1ff96dc50fab0c173a24608ef3584'
Open the live lookup →
03Phase 2 — Disclose row 2 only

One bidder disclosed. The other four stay sealed.

To answer one auditor question (“what was bidder gamma’s amount?”) the holder publishes a single reveal payload: the row, its canonical bytes, the per-leaf salt for index 2 only, the leaf’s commitment, and the Merkle inclusion path back to the root. Nothing about rows 0, 1, 3, or 4 is exposed — not their bidders, not their amounts, not their salts.

Single-row reveal payload (JSON)
{
  "version":           "satsignal-merkle-row-sealed-v1",
  "leaf_index":        2,
  "leaf_count":        5,
  "label":             "row-2",
  "row":               { "bidder": "gamma", "bid_minor": 48 },
  "row_canonical_b64": "eyJiaWRfbWlub3IiOjQ4LCJiaWRkZXIiOiJnYW1tYSJ9",
  "salt_b64":          "2BempkijPVIw5QHTSqw4hSh34j15Xgx4fEONhjc1tRE=",
  "commitment_hex":    "fe1a6ecc7e0ecfecab0335e3f18aa189d8c9cce35b7fb642148d9ae551208586",
  "proof": [
    {"sib": "42b18e56...0587eb", "side": "R"},
    {"sib": "80119518...bce7f3", "side": "L"},
    {"sib": "97912b95...182d99", "side": "R"}
  ],
  "root_hex":          "a67956a2d0ca871b665da9d99fb1ac2dc3690d9484bcac23dd09c2c5ae477bf7"
}

Full reveal at /samples/merkle-row/sealed-reveal-row-2.json (proofs un-elided). Field reference → /spec-merkle-row.

What the auditor learns — and what they don’t.
  • Bidder gamma is checkable: re-canonicalize the row, HMAC under the supplied salt, walk the Merkle path — the result is the on-chain root.
  • Bidders 0, 1, 3, 4 stay opaque: only their sibling commitments appear — salted HMACs with a 2256 salt space and no candidate set to brute-force.

Per-leaf salts are HKDF-derived from a master salt the holder keeps; revealing one cannot derive the others — why HKDF per-leaf salts preserve secrecy.

Holder responsibility. The master salt and each row’s canonical bytes must be preserved by the commit holder. Lose either and the corresponding row can no longer be revealed — the on-chain commitment stays permanent but becomes opaque even to the holder.

04Phase 3 — Verify

Anyone can re-run the four binding checks.

Verification is fully client-side; nothing leaves the auditor’s machine except the one /lookup_hash call that resolves the commit-doc SHA-256 to the on-chain transaction. Three independent paths:

Open /verify and find the Sealed-row reveal card. Click Try the live sample to auto-load this demo’s commit doc + reveal, or drop your own pair of files. All four binding checks (root match, row binding, leaf commitment, Merkle path) run client-side; the on-chain commitment then resolves via the public /lookup_hash.

See it in the verifier →

Prefer the command line or another language? The stdlib helper and the byte-level four-check procedure are in the spec → /spec-merkle-row.

The selective-disclosure property. The four checks are independently protective — a lie about the row, the salt, or the proof each fails its own check. Together they make the auditor’s confidence in row 2 not depend on access to the other four rows.

05Next

Build this in your own integration.

Everything on this page was produced by a stdlib-only helper and the public anchor API — nothing to install.

Build your own: merkle_row.py  ·  API quickstart →