Skip to content

Anchoring

The registry’s whole value is a date. And until you can check that date without asking us, it is our word.

So every day the registry publishes a receipt: a short text file with a cryptographic summary of every version of every record it holds, and of every version of every agent’s instructions. That receipt is stamped into the Bitcoin blockchain through OpenTimestamps, and independently by two RFC 3161 timestamp authorities. None of the three is run by us.

From then on the date is not ours to move. If we changed a registration date tomorrow, yesterday’s receipt would stop matching, and yesterday’s receipt is already outside our reach.

It provesIt does not prove
That this data existed before a specific Bitcoin blockThat whoever registered is the rightful owner
That its content was exactly that on that dateThat the declared registration date is true, only that it existed before the anchor
That later edits did not erase the historyAnything about records created after the most recent anchor

Anchors are daily, so between registering and anchoring there is a window of up to 24 hours in which you still have to take our word for it. We would rather say so than let you find out.

A plain text file, and these bytes are the artefact: not a rendering of it, the thing itself. One trailing space would change its digest and break every proof.

TheHumanBehind registry anchor
anchor_version: 2
recipe_version: 3
cutoff_utc: 2026-08-11T03:45:00.000000Z
records: 101
versions: 128
agent_versions: 14
merkle_root: 3a02599887336f2ab822d678b50edb05bad62eff392ba826093960faaaa543cb
prev_receipt_sha256: genesis
recipe: https://thehumanbehind.com/docs/anchoring

prev_receipt_sha256 is the SHA-256 of the previous day’s receipt, which chains the series: a receipt cannot be replaced without breaking every one that came after it. The first receipt of all says genesis.

records counts distinct registry codes, versions counts versions of record content, and agent_versions counts versions of agent instructions. The total number of leaves in the tree is versions + agent_versions, which is what you need to know how many leaves there should have been.

Anchors from 5 to 10 August 2026 carry format 1, which has no recipe_version and no agent_versions line and says anchor_version: 1. It is kept byte for byte because those receipts are already stamped.

Every line, in both formats, is key: value, one per line, and is meant to be read by prefix. A reader written for format 1 finds everything it knew about inside a format 2 receipt; it simply ignores two lines it has never seen. If you parse receipts by line number instead, this is the change that will break you.

recipe_version moved INSIDE the stamped bytes on purpose. Until format 2 it lived only in a column of our database. Inside the receipt, it is Bitcoin that seals it.

Every receipt is served forever at {SITE.platformUrl}/api/v0/anchors/{"{date}"}.txt, and the list of anchors, with their Bitcoin block, at {SITE.platformUrl}/api/v0/anchors.json.

This is a contract. A proof stamped today has to verify in ten years, so none of what follows can ever change. If a new version is ever needed, it gets its own prefix and the previous one keeps working forever, so no proof is ever orphaned.

Three recipes have existed, and all three are alive. Every anchor publishes its own recipe_version in /api/v0/anchors.json and is verified with the recipe it was sealed with.

RecipeAnchorsLeaf preimage
v1only 5 Aug 2026THB-ANCHOR-v1|<registry_code>|<version>|<content_hash>
v26 to 10 Aug 2026THB-ANCHOR-v2|<registry_code>|<version>|<content_hash>|<leaf_salt>
v3from 11 Aug 2026THB-ANCHOR-v3|<kind>|<registry_code>|<version>|<digest>|<leaf_salt>

Since v3 a leaf can attest to two different things, and it says which one in field 2, before any other data:

THB-ANCHOR-v3|record|<registry_code>|<version>|<content_hash>|<leaf_salt>
THB-ANCHOR-v3|agent|<registry_code>|<version>|<agent_sha256>|<leaf_salt>

The leaf is the SHA-256 of those bytes, UTF-8.

A record leaf attests that a record’s public content was exactly that at that version. An agent leaf attests that a set of agent instructions existed and was exactly those, at that version.

The kind is in there because without it a proof of instructions would be indistinguishable from a proof of record content, and anyone could present the first as if it were the second. It sits in field 2, ahead of every value that comes from a user, so two leaves of different kinds always differ no matter what the rest holds.

The text of the instructions never enters the leaf. Its hash does. So the proof survives even if that text one day has to be withdrawn for containing personal data: the digest, the date, the version and the salt stay, and the inclusion path keeps verifying.

Every leaf commits to the registry code, not to any internal identifier, for two reasons: internal identifiers have no business leaving the database, and you have to be able to recompute your own leaf from what you already hold. All the values are in your record’s downloadable proof.

The version number is in there because without it two versions of the same record with identical content would collapse into a single leaf.

leaf_salt is 16 random bytes, in lowercase hex, and it is what makes this scheme private. Each version gets its own, it never leaves our server, and you receive yours, and only yours, inside your proof.

It is there because a hash only hides its input when that input is hard to guess. Registry codes are sequential and a record has few fields, so an unblinded leaf would be a commitment over low-entropy data, and those can be attacked by trying candidates until one matches. The 16 random bytes remove that possibility: there is nothing left to try. It matters more for instructions than for record content: prompt templates repeat between clients, so unblinded they would be very guessable indeed.

Ascending by registry_code, then by the kind’s rank (record = 0, agent = 1), then by version as a number.

The rank is a number and not the word, deliberately: agent sorts before record alphabetically, so ordering by the literal would put a record’s instructions ahead of its content, and the published order would depend on how the kinds happen to be spelled.

So a record with two content versions and two instruction versions contributes, in this order:

THB-2026-00144 · record · 1
THB-2026-00144 · record · 2
THB-2026-00144 · agent  · 1
THB-2026-00144 · agent  · 2

Versions are compared as numbers: sorted as text, version 10 would land between 1 and 2.

Registry codes are compared as text, by their bytes. Until 31 July 2026 the number was fixed width and that was the same as comparing numbers; it no longer is, because the width became a minimum rather than a fixed size, so past 99.999 records in one year THB-2026-100000 sorts before THB-2026-99999. That is fine: the rule is byte order on the code, which is well defined and reproducible by anyone. This page used to claim the two orders were equivalent, and that sentence was wrong.

A binary Merkle tree. Each interior node is the SHA-256 of its two children concatenated as raw bytes, not as hexadecimal text.

When a level holds an odd number of nodes, the last one is promoted unchanged to the next level. It is not duplicated. Duplicating it is what Bitcoin does, and it is what made CVE-2012-2459 possible: with duplication, two different sets of leaves can produce the same root, which in a registry would mean two different histories behind one proof.

The root of the top level is the merkle_root in the receipt.

Your record’s proof file gives you the siblings needed to climb from your leaf to the root, each with the side it goes on. Start from your leaf, and at each step hash the pair in the stated order. If you land on the receipt’s merkle_root, your record was in that anchor.

The same code verifies both kinds of leaf, because the only difference is what the preimage says. If your record is an agent, anchor.agent_instructions in your proof carries one entry per anchored version of your instructions, each with its own preimage, its own salt and its own path, all climbing to the same root.

import hashlib

# Straight from your proof.json: `leaf_preimage` comes already joined, so there
# is nothing to assemble. This one is an instructions leaf.
preimage = "THB-ANCHOR-v3|agent|THB-2026-00144|2|0acb5e6a…50|5051…5f"
path = [  # also from your proof.json, in order
    ("b1ef4d226bd74200ffdcc71b40b0b361280c96c5005fab3c3e8cb511e4b0044e", "right"),
    # ...
]

current = hashlib.sha256(preimage.encode()).digest()
for sibling_hex, side in path:
    sibling = bytes.fromhex(sibling_hex)
    pair = sibling + current if side == "left" else current + sibling
    current = hashlib.sha256(pair).digest()

print(current.hex())   # must equal the merkle_root in that day's receipt

Everything above proves where a record stands today. What a claim usually turns on is what it said before, and when it changed. Both are kept, and both are anchored.

A record has two append-only ledgers, and each one produces its own leaves:

SeriesOne version each time…Leaf kind
Contentany of the eight sealed fields changesrecord
Instructionsan agent’s instructions are replacedagent

A version’s leaf is the same recipe you already read, with that version’s number in field 4. Nothing special happens for old versions: version 1 of a record is a leaf of the tree in exactly the way version 9 is, and it stays in every anchor from the day it was written onwards.

Recomputing an old version’s hash, with nothing but a browser

Section titled “Recomputing an old version’s hash, with nothing but a browser”

The public history of a verified record is open:

curl {SITE.platformUrl}/api/v0/records/laia-torres/history.json

Every entry of the content series carries the exact snapshot that was hashed. Hash it the way verifying a seal describes and you must get that version’s content_hash back. No account, no permission, nothing to take on trust.

The instruction series publishes how many versions there have been and when, and nothing else. Not the text, which is the author’s, and not its fingerprint either: agent instructions are often templates that repeat between clients, so a published digest would let anyone holding a candidate confirm that this record uses it. That is a confirmation oracle, and the whole reason the v1 recipe had to be replaced. The digest travels to the record’s owner, inside their own proof, and nowhere else: the database does not hand that column to anonymous readers either, because a promise the database does not keep is not a promise.

Following one version all the way to Bitcoin

Section titled “Following one version all the way to Bitcoin”

For that you need the leaf, and the leaf needs the salt, and the salts are never public — if they were, blinding would buy nothing and the attack that killed v1 would be back.

So the complete path lives in the owner’s proof.json, which now carries one entry per version, in two places that mirror the two series:

  • Content versions are in history.versions, each with the snapshot that was hashed and a leaf object holding the preimage already assembled, the leaf hash and the inclusion_path.
  • Instruction versions are in anchor.agent_instructions.versions, each with its leaf_preimage, its leaf and its inclusion_path. Their entries in history.versions carry leaf: null and a proof_in pointing here, so the same proof is not written down twice.

That file is the owner’s to publish wherever they like, and whoever receives it needs nothing from us to check it: recompute each leaf from its preimage, walk its path, land on the anchor’s Merkle root, and check that root against the receipt and the .ots, both public. The only thing you take from anyone is the file itself.

Versions written after the last anchor come back as pending_next_anchor with no path, because they genuinely are not in that tree yet. Their salt and preimage are handed over anyway, so they can be kept today and verified against tomorrow’s anchor.

Two ways. Use the first if you want it over in a minute, the second if you want it to work in twenty years.

Download the receipt and its proof:

curl -O {SITE.platformUrl}/api/v0/anchors/2026-08-06.txt
curl -O {SITE.platformUrl}/api/v0/anchors/2026-08-06.ots

Drop both files on opentimestamps.org. It will tell you which Bitcoin block contains that receipt. The hash is computed in your browser; the file never leaves your machine.

That website runs on a JavaScript library that has not had a functional commit since 2022. So the anchor list also publishes, for every anchor, the Bitcoin block height and that block’s Merkle root. Look the block up in any explorer, compare the Merkle root, and you are done. No tooling, no library, no website that has to still exist.

With the OpenTimestamps client installed, the same two values come from:

ots --no-bitcoin verify 2026-08-06.txt.ots

The same receipt, stamped by two independent authorities. They verify instantly, with no blockchain and no waiting:

curl -O "{SITE.platformUrl}/api/v0/anchors/2026-08-06.tsr?tsa=freetsa"
openssl ts -verify -data 2026-08-06.txt -in 2026-08-06.tsr -CAfile cacert.pem

They exist alongside the Bitcoin anchor, not instead of it, because the two fail in opposite ways. If the OpenTimestamps calendars vanished before a pending proof was completed, the tokens would still stand. If an authority shut down or its key were compromised, Bitcoin would still stand.

OpenTimestamps does not put one transaction on the chain per file. Calendars collect thousands of digests, build one tree, and publish a single root. So a fresh proof says pending for a few hours until that transaction confirms, and the registry upgrades it automatically once it does. confirmed: false in the anchor list is simply a proof on its way: the normal state for part of the day.

The calendars never see your data. Before anything is sent, the client appends 16 random bytes to the digest and hashes it again, so what travels is an opaque value from which neither the receipt nor its hash can be recovered.

The receipt itself contains no personal data either: only counts and one root.

And the siblings in your inclusion path reveal nothing about the records they belong to, because each leaf is blinded with its own salt. That is the whole job of leaf_salt: without 128 bits of randomness that exist nowhere public, there is no candidate to test against a sibling, so a path proves your record is in the tree without saying anything about anyone else’s.

What the counts do disclose, and we would rather say it

Section titled “What the counts do disclose, and we would rather say it”

The receipt publishes how many records, how many content versions and how many instruction versions the registry held that day. Registry codes are sequential and public, so the totals were never a secret, and with blinded leaves a count confirms nothing about any particular record.

But while the registry is small, a change in agent_versions from one day to the next says that somebody edited their agent’s instructions that day. Not who, not what: the text stays private and the hash stays blinded. The fact and the date of an edit are public. If that matters to you, it is worth knowing before you register, not after.