ยทUses

What you can prove with Satsignal.

Three verifiable outcomes, three live primitives, one bearer-auth API. Each outcome is an audit-trail artifact an outside auditor or regulator can verify without trusting Satsignal — co-attested by Bitcoin SV miners, verified in any browser. Works for any file, too.

All API examples below are against https://app.satsignal.cloud; we elide the host in body copy.

01Three outcomes

Three things a receipt proves.

Each maps to a live API primitive. Two of them — what an agent was allowed to do, and what evidence it used or produced — build in selective disclosure: reveal one component or one row to an auditor while the rest stay sealed.

1

What an agent committed to

Anchor a hashed decision before anyone reveals theirs. Sealed bids, model evaluations, prediction markets, multi-agent voting — nobody, including the operator, can rewrite when the commitment was made.

How: commit-reveal →
Worked demo: Sealed Bid →

2

What an agent was allowed to do

Anchor a snapshot of the system policy, user instruction, tool permissions, budget, and model config the agent was running under. Auditors verify any single component without seeing the others.

How: policy snapshots →

3

What evidence it used or produced

Anchor a batch of up to 10,000 items in one on-chain receipt. Selective disclosure: any holder later proves a single item was in the batch — without revealing the others. Right pattern for high-volume agent runs; same shape via merkle-row-sealed-v1 for tabular data.

How: manifest receipts →

Two worked demos with real on-chain receipts: the Agent Evaluation demo anchors a policy snapshot before a real eval, then anchors a result manifest after; the Sealed Bid demo anchors five sealed bids as one Merkle root, then discloses a single bidder’s row to an auditor while the other four stay HMAC-sealed.

02What to look for

Four properties worth checking when you evaluate a receipt format.

Plenty of products in this space stop at “a hash existed at a moment.” That’s table stakes; what comes after is where formats diverge. Four properties that matter operationally:

1

Selective disclosure of low-entropy rows

A timestamp on a 50KB document is fine; a timestamp on a single bid amount, yes/no vote, or one row of a small enum leaks the answer to anyone who can enumerate the candidate set. Look for a scheme that HMACs each leaf under a per-leaf salt so the holder can reveal one row of a sealed table without unsealing the others.

Worked demo: five sealed bids, one row revealed →
Scheme reference →

2

Verification that doesn’t depend on us

If a receipt only verifies through the issuer’s servers, the issuer staying online is part of your trust story. Anchoring on a public chain pushes that dependency onto the chain itself. The shape to look for: stdlib-only verifier, public-explorer lookup, no SDK requirement, no account.

In-browser verifier →

3

Byte-identical helpers across runtimes

If the receipt format quietly canonicalizes differently in Python and JavaScript, a Python-anchored commitment won’t verify in JS and the spec is the issuer’s SDK rather than its docs. Look for a published byte-level spec and matching helpers in at least two runtimes that produce the same canonical bytes from the same inputs.

Helpers reference →

4

Receipts small enough to print, scan, or text

Most chain-anchored receipt formats ship a multi-megabyte signed PDF or a JSON-LD certificate envelope. That fits in an inbox; it doesn’t fit on a paper invoice, a printed lab result, or in a QR code on a field inspector’s clipboard. Look for a format whose entire receipt bundle is small enough to print on the artifact it’s proving — under a kilobyte for typical commitments. The mobile-first behaviors (scan-to-verify, deep links, offline distribution) fall out of the size for free.

Mobile + edge integration guide →

03How it works

Committed in seconds. Confirmed on-chain. Verified forever.

Three states, one receipt. None of them require trusting us.

Step 1 — Committed

Hash — locally.

Your client (browser, agent, or script) hashes a commitment, snapshot, manifest, or file and POSTs the fingerprint. The payload itself never leaves your device. An independent miner accepts and signs the anchor in seconds.

Step 2 — Confirmed

Included — in a block.

The anchor is mined into a Bitcoin SV block (about ten minutes). Your filename, notes, payload, and any of the items inside a manifest never appear on chain — only the fingerprint.

Step 3 — Verified

Anyone — reverifies.

Drop the receipt and the original payload into the in-browser verifier. It re-hashes, walks the Merkle path if needed, fetches the on-chain transaction from a public explorer, and renders three pills: Committed (miner signed it), Confirmed (chain says so), Verified (your copy matches).

Why this chain? BSV is well-suited to anchoring small, structured records — that’s why it’s here. Your bundle records which chain it lives on, so verification doesn’t depend on Satsignal staying online or on any single block explorer.
04Commit-reveal

Commit now, reveal later — without trusting a clock.

Sealed bids, evaluation scores, model benchmarks, prediction markets, multi-agent voting: anywhere two parties need to commit BEFORE either reveals, the public chain is doing something nothing cheaper can. Anchor a hash now; publish the payload later. Anyone can prove the commitment predates the reveal — without a trusted timestamp service, without either party being online, without anyone able to edit history later.

Step 1 — Commit

Wrap, hash, anchor.

Wrap your payload (a bid, a score, a vote — anything JSON-shaped) with a fresh 32-byte random nonce. Canonicalize. Hash with SHA-256. Anchor the hash via POST /api/v1/anchors with category: "commitment". Keep the wrapped form private.

Step 2 — Reveal

Publish the wrapped form.

After the deadline (the auction closes, the evaluation ends, the prediction window expires), share the wrapped form. The nonce is what makes this safe: without it, an attacker could enumerate likely values; with it, the hash binds you to one specific committed thing.

Step 3 — Verify

Anyone re-hashes and checks.

A verifier canonicalizes the revealed wrapped form, SHA-256s it, and confirms the hash matches the on-chain commitment. The block timestamp proves the commit predates the reveal. No trust in either party, no trust in Satsignal.

Why this only works on a public chain. Cheaper infrastructure (signed logs, append-only DBs, transparency logs) can almost match what Satsignal does — for most use cases. But commit-reveal is the place where a public chain is uniquely valuable: the threat model includes the operator, the agent platform, and any single service. With a chain anchor, even the issuer can’t rewrite when you committed.
Helpers. Drop-in libraries that produce the same canonical bytes byte-for-byte across runtimes:
  • /commit_reveal.py — Python 3 stdlib only. CLI with commit / reveal / verify subcommands.
  • /commit-reveal.js — ES module for browser + Node 18+. Exports makeCommit and verifyReveal.
The two implementations cross-verify: a Python commit reveals cleanly to a JS verifier and vice versa.
05Policy snapshots

Prove what an agent was allowed to do at the time it acted.

An incident review — a regulator, a customer, a court — will ask: what system policy was in force, what instruction was the agent responding to, which tools could it call, what was its budget, and which model and config was it running? A policy snapshot answers all five at once with a single on-chain commitment.

The snapshot is a JSON object with a SHA-256 fingerprint of each component. Anchor the snapshot via POST /api/v1/anchors with category: "policy_snapshot"; the receipt binds to all five hashes simultaneously, so an auditor with just the system prompt can verify the system_policy_hash matches the receipt without ever seeing the budget config — selective disclosure of the agent’s configuration.

The five canonical fields (any subset is fine).
  • system_policy_hash — sha256 of the system prompt or policy document.
  • user_instruction_hash — sha256 of the user-supplied task or query.
  • tool_permissions_hash — sha256 over the canonicalized tool list / call permissions.
  • budget_limits_hash — sha256 over the canonicalized budget caps (USD, tokens, time, etc.).
  • model_config_hash — sha256 over the canonicalized model + sampling parameters.
Step 1 — Snapshot

Hash each component, build the snapshot.

Before the agent acts, the runtime hashes the five components and assembles a snapshot JSON. The whole snapshot is canonicalized + sha256’d — that hash is what gets anchored.

Step 2 — Anchor

POST with category=policy_snapshot.

Hand the snapshot’s sha256 to the existing /api/v1/anchors endpoint. The receipt binds the on-chain timestamp to the entire five-field configuration. Save the receipt’s bundle_id alongside every action the agent takes from this point on.

Step 3 — Audit

Anyone with one component can verify.

An auditor receives the snapshot JSON and any of the original components. They re-hash and check — without ever needing to see the others. The chain timestamp proves the configuration predates the action.

Helpers + worked example.
  • /policy_snapshot.py — Python 3 stdlib-only CLI. Subcommands: hash-component, build, verify. Selective-disclosure verify (one field at a time) just works.
  • /example_agent_snapshot.py — runnable agent example. Hashes its own five components, optionally POSTs the snapshot to /api/v1/anchors when SATSIGNAL_API_KEY is set, then takes a (deterministic) action. Replace the decide() stub with a real agent loop and you have an audit-trail-clean configuration anchor in ~30 lines of glue.
Same shape as research pre-registration. Anchor the eval set, system prompt, scoring rubric, and decoding params before a run; anchor the result manifest after. Anyone reading the published outputs later can confirm the design was committed before the data — the structural property pre-registration gives in clinical trials and replication studies, applied to model evaluations. Worked framing for AI evaluation research: Pre-registration and selective disclosure for AI evaluations →.
06Manifest receipts

Anchor a batch — reveal one item at a time.

Per-action anchoring doesn’t scale. Manifest mode is the structural answer. Up to ten thousand items (decisions, tool calls, outputs, evidence files) collapse into one receipt — and any holder can later prove a single item was in the batch without revealing the others.

Each item is just a {label, sha256_hex} pair. The server combines the leaves into a Merkle tree and anchors the root. The receipt bundle ships every leaf with its inclusion path, so a recipient who only sees one item can still verify it belongs to the receipt — no need to see the other 9,999.

Step 1 — Batch

Hash each item, collect the leaves.

Your client hashes each artifact (a tool-call log, a page of output, an evidence file) into a leaf with a readable label. Only the leaf fingerprints reach our server — the underlying bytes stay with you.

Step 2 — Anchor

POST the leaves; one chain spend.

Send the leaves to /api/v1/anchors as {items: [...]}. The server combines them into a Merkle tree and anchors the root. One miner-signed anchor covers up to 10,000 items.

Step 3 — Disclose one item

Recipient verifies that one item.

Hand a single item plus its inclusion path to whoever needs to verify it. They reproduce the leaf hash, walk the path to the root, and confirm the root matches what was anchored on chain. The other 9,999 stay private.

When this is the right shape. Long-running agents. Hourly tool-call logs. Evaluation runs that produce hundreds of judged outputs. Evidence packages where each file is sensitive but the overall manifest is the deliverable. The common thread: many private items, one public commitment, selective disclosure later.
Tabular data — named schemes

Two row-level schemes you can pin against.

For tables (CSV rows, JSON records, eval results, vote ledgers) where the disclosure unit is a single row, two documented schemes ride on top of the API:

  • merkle-row-v1 — leaf is sha256(canonicalize(row)); anchored via manifest mode. Right when row contents are high-entropy (free-text bodies, UUIDs, large payloads).
  • merkle-row-sealed-v1 — leaf is HMAC-SHA256(salt_i, canonicalize(row)) with per-leaf salts derived via HKDF; anchored via category=commitment over a tiny commit doc. Right when row contents are low-entropy ("yes"/"no", single bids, small enums) where a plain SHA-256 leaf would be brute-forceable.

Both schemes verify selectively: revealing one row never requires revealing any other. The full byte-level spec is at /spec-merkle-row; stdlib-only helpers with byte-identical canonical bytes across runtimes:

Live samples, anchored on chain 2026-05-08: all txids + artifact index; merkle-row-v1 txid 4fc609d8…, merkle-row-sealed-v1 txid d623cb94…. The client helpers reproduce both Merkle roots byte-for-byte from the row inputs; the on-chain commitment sha for the sealed sample resolves publicly via /lookup_hash.

Foreign-format interop (AAR, C2PA, Visa TAP, RFC 3161 dual-attest) lives at /samples/chain-anchor/RECEIPTS.md — five live BSV-mainnet samples for the chain-anchor/v1 scheme.

07For any file

The same receipt works for ordinary files.

The three outcomes above are the agent-shaped wedge. Satsignal still does what it always did: tamper-evident receipts for any file, with format-aware fingerprints so a re-saved PDF or re-exported CSV still verifies against the content hash even when the byte-exact hash changes.

FileContent fingerprintSection breakdown
PDFextracted text, normalizedper page
JSONRFC 8785 canonicalper top-level key
CSVRFC 4180 normalizedper data row
Source / textNFC-normalized, line-ending stableper line
ZIP / DOCX / XLSXentry manifestper contained file
Image (JPG/PNG/WebP)raw pixel data, metadata strippedper tile
Anything elsebyte-exact hash onlyn/a

See the receipt gallery → for one anchored sample per format, each linking to a live on-chain receipt.