Chio/Docs
LOGIN · JOIN

Anchoring

Fix your receipt history in ledgers no single operator controls, and verify any receipt’s inclusion against them offline.

Concept chio@5f63e6e

On this page

A signed receipt proves what a kernel decided. It cannot prove that the operator kept every receipt, or that the history an auditor reads today is the one a partner read last month. Transparency logs settle that question for certificates and software releases by committing each state of a log to a short value that anyone can hold and compare. Anchoring does the same for the receipt log: it publishes the roots that commit to it into systems no single operator controls, so a rewritten history disagrees with a root already out of the operator’s reach.

The kernel already commits its receipts. Each batch becomes a checkpoint, an RFC 6962 Merkle root over the receipts, signed by the kernel and chained to the checkpoint before it. Anchoring carries those roots past the kernel’s own storage to four places, and each one establishes something different.

Checkpoints commit receipts, and anchors commit checkpoints

A checkpoint covers 100 receipts by default. Its body names the checkpoint_seq, the first and last receipt sequence numbers, the tree size, the Merkle root, the issue time, the kernel’s public key, and the SHA-256 of the previous checkpoint, and the kernel signs that body with Ed25519.

Checkpoint anchorsChoose an anchor.
ρ × 100c1seq 41c2seq 42c3seq 43c4seq 44checkpointsRbatch rootEVMChioRootRegistryprimary laneSOLSolana memosecondary laneBTCOpenTimestampssecondary laneRekorRekorpublic witnesscheckpoints of 100 receipts ρc1seq 41c2seq 42c3seq 43c4seq 44Rbatch rootEVMChioRootRegistrySOLSolana memoBTCOpenTimestampsRekorRekor

The registry holds every checkpoint root, in order

The primary lane. Each checkpoint root is published on its own with publishRoot, and settlement releases that verify a receipt by Merkle proof verify against this lane only.

Proves
The operator, or a delegate the registry authorizes on chain, published root n directly after n−1 under the operator’s registered key hash. verifyInclusionDetailedForKeyHash checks a receipt’s RFC 6962 path against the stored root.
Finality
Chio confirms the RootPublished log in the transaction receipt and reads the stored entry back at that block. The confirmations a verifier waits for are the deployment’s Web3FinalityMode.
Refused
An unauthorized publisher, a sequence that repeats or regresses, a receipt range with a gap, or a root already seen for the key.
Four kernel-signed checkpoints of 100 receipts each and the four anchors they reach. Sequence numbers and the batch of four are illustrative; key-binding certificates, proof bundles, and the Chainlink Functions fallback are left out.

The anchors take checkpoints in two shapes. The EVM registry and the Solana memo take each checkpoint root on its own, in sequence. OpenTimestamps and Rekor take one root over a run of checkpoints, so one external record covers the whole batch. A receipt therefore proves inclusion in its checkpoint with one audit path, and the checkpoint proves inclusion in a batch root with a second.

An anchor batch signs one root over many checkpoints

build_anchor_batch takes a list of checkpoint ids, a witness, an issue time, and the kernel keypair. It hashes each id as an RFC 6962 leaf, builds the tree, writes one inclusion proof per checkpoint, and signs the result as a chio.anchor_batch.v1 record whose witness state starts at Pending. An anchor profile names the lanes a deployment publishes to:

anchor-profile.tomltoml
[evm]
chain_id = "eip155:8453"
rpc_url = "https://mainnet.base.org"
contract_address = "0x5e1f…c0a4"
operator_address = "0x3b9d…7e21"
publisher_address = "0x3b9d…7e21"

[solana]
memo = true

[ots]
calendars = ["https://a.pool.opentimestamps.org", "https://b.pool.opentimestamps.org"]

[rekor]
endpoint = "https://rekor.sigstore.dev"
Terminalbash
chio anchor batch --profile ./anchor-profile.toml --through 44
Output
batch      chio.anchor_batch.v1, checkpoints 41 to 44, receipts 4001 to 4400
tree_root  9f2c…e710, signed by kernel 4f1c…a2e9
evm        publishRootBatch on eip155:8453, roots 41 to 44 in one transaction
           RootPublished confirmed, block 21904113, stored entries read back
solana     4 memos, Chio:41:0x5d0b…a913:1791813600 through Chio:44, confirmed
ots        super-root 6b0d…19c4 sent to 2 calendars, Bitcoin attestation pending
rekor      witnessed, logIndex 168204551, integratedTime 2026-10-12T14:02:09Z

Each lane reports on its own terms. The registry confirms its log and reads every stored entry back at the confirming block; the memos are confirmed; the Bitcoin attestation arrives once a calendar’s transaction is mined; Rekor returns its log index at once. The batch verifier recomputes the tree from the checkpoint ids and rejects a batch whose stored root, inclusion order, or leaf hashes disagree with them, and it refuses an empty list.

The registry accepts each checkpoint once, in order

ChioRootRegistry stores one entry per checkpoint: the Merkle root, checkpointSeq, the receipt range, the tree size, the publication time, and the operator’s key hash and epoch. It accepts checkpoint n only directly after n−1, with a receipt range that starts where the last one ended, under a key hash that matches the identity registry, from the operator or one of at most three delegates the registry authorizes. A root already seen for the key is refused, so the same history cannot be published twice under two sequence numbers.

Inclusion is checked against a key hash. verifyInclusionDetailedForKeyHash runs the RFC 6962 audit path against the stored root on chain, and the registry’s shorter verification entry points revert with ProofMetadataRequired. Settlement that releases funds on a Merkle proof verifies against this lane alone; Bitcoin and Solana evidence never releases settlement by itself. On-chain settlement follows a Merkle release from root to escrow.

Operators pause the lanes with AnchorEmergencyMode: PublishPaused stops new publications, ProofImportOnly accepts external proofs and publishes nothing, and RecoveryOnly and Halted narrow further. A lane that observes a chain reorganization is classified Recovering.

A pending witness does not count

The default witness policy requires a public witness. A batch in the Pending state is refused. A Witnessed batch passes once its witness root, kind, and body hash match the batch and the witness client verifies its own inclusion: for Rekor, the Signed Entry Timestamp against Rekor’s pinned key, the log index that timestamp signs, and the log’s inclusion proof.

A Stale witness passes only if this verifier checked the same batch body hash itself, no more than 24 hours earlier. A witnessed state that a batch asserts about itself is refused with SelfAssertedWitnessed. OpenTimestamps cannot satisfy the policy, because its client never verifies inclusion on its own; Bitcoin evidence enters through the proof bundle instead, as a secondary lane tied to the super-root digest.

A receipt’s inclusion verifies offline, lane by lane

An auditor who holds your kernel’s public key and a proof bundle needs nothing else from you. Build the bundle for one of your receipts and verify it:

Terminalbash
chio anchor prove rcpt-01a8…5d17 --output ./proof.json
chio anchor verify ./proof.json --trusted-kernel-pubkey 4f1c…a2e9
Output
receipt     rcpt-01a8…5d17, signature verified against 4f1c…a2e9
checkpoint  seq 42, receipts 4101 to 4200, signed by 4f1c…a2e9
inclusion   leaf 56 of 100, audit path of 7 hashes, root 3a7e…c2d1
evm         root 3a7e…c2d1 at checkpointSeq 42 on eip155:8453, key hash matches
bitcoin     super-root 6b0d…19c4 attested at height 917402
solana      memo Chio:42:0x3a7e…c2d1:1791813600 matches the primary proof
bundle      chio.anchor-proof-bundle.v2 verified, 3 lanes

The verifier works in a fixed order: the bundle schema, the receipt’s signature, the key binding, the checkpoint statement’s signature, the agreement of all three kernel keys, then the receipt’s leaf hash against the checkpoint root. Each declared lane then verifies against that root. A version 2 bundle must carry the chain anchor and at least one secondary lane, and every declared lane must match the data present exactly, so one bad lane rejects the whole bundle.

Edit one byte of the receipt and the path no longer reaches the signed root:

Terminalbash
chio anchor verify ./proof-edited.json --trusted-kernel-pubkey 4f1c…a2e9
Output
refused     receipt inclusion merkle_root must match checkpoint statement
bundle      rejected, no lane verified

An attestation about a root is not an inclusion proof

An EAS or Verax attestation states that someone vouched for a value. Chio can project one into a chio.agent-web-proof-envelope.v1 record for display, and neither the attestation nor the envelope deserializes as an AnchorInclusionProof. A root copied from an attestation into an otherwise genuine proof fails at the checkpoint statement, because the receipt’s inclusion root must match the root the kernel signed.

The same separation governs payments: a verified x402 payment establishes that money moved, and never that a tool call was authorized. Approval-Bound Payments draws the three checks apart.

Next: release settlement on an anchored root

Anchoring · Chio Docs