Guide

How a circle is built, signed and read back

Every number on this page is taken from the vectors committed in test-vectors/v1/. Nothing here is illustrative.

The idea

Imagine four people who want a durable record that they were all party to the same moment. They do not want a token, a website account, or a database row that someone can edit. Each of them puts one confirmed Taproot coin into a single Bitcoin transaction, signs the whole transaction, and gets that coin back to the same key minus a quarter of the miner fee. Once it confirms, anyone with a Bitcoin node can check the record, and nobody can revise it.

That is all Witness Circles does. The circle is the transaction. The membership record is the set of input keys. The proof is the four signatures, each of which commits to every input and every output.

Session lifecycle

The protocol has nothing to say about how participants find each other. That is application territory. The reference implementation ships a planner that produces the frozen unsigned transaction, and a signing-intent inspector that a wallet runs before it signs.

  1. Context. Someone writes a small JSON manifest (title, nonce, created, expires) and hashes it with RFC 8785. That hash goes in the marker.
  2. Plan. buildCirclePlan sorts the participant inputs, estimates the virtual size, multiplies by the chosen fee rate, splits the fee, and emits an unsigned transaction plus its fingerprint. It refuses any participant whose fee share would exceed their declared cap (FEE_CAP_EXCEEDED) or whose successor would fall under 1,000 sats (SUCCESSOR_DUST).
  3. Freeze. The unsigned transaction is now immutable. Its SHA-256 fingerprint is what each wallet compares against the invitation.
  4. Inspect. Each wallet calls inspectUnsignedSigningIntent, which re-validates the whole grammar in unsigned mode and returns the signer's own outpoint, value, successor, fee share, total fee, fee rate, context hash, and two fixed warnings about public linkage and the claim boundary.
  5. Sign. Each participant returns a non-finalized PSBT_IN_TAP_KEY_SIG. The coordinator verifies each contribution before merging. A coordinator never holds a key.
  6. Broadcast and confirm. At one confirmation in the best chain the Circle is authoritative and the state transition applies.

Because every signature commits to every input and output, a replacement transaction needs a new signature from every participant. There is no way for one member to change the fee or drop out after signing without the whole group signing again.

Example A: a genesis circle for three participants

This is test-vectors/v1/golden-circle.json in full. Signet, block height 200, three participants who have never been in a Circle before.

The context manifest

{"created":"2026-08-01T18:00:00Z","expires":"2026-08-01T19:00:00Z","kind":"circle",
 "nonce":"90e8f5cf27f04c89a6657bc9c60e3021","orbit":"signet-builders","protocol":"witc",
 "title":"Reference genesis circle","version":1}

That is the RFC 8785 serialization exactly: object keys sorted, no whitespace. Its SHA-256 is ad2608134839e732280cb93bdd5b8682626dce5748a65c437a39cdcb680c2a82, which is the last 32 bytes of the marker.

The inputs

SlotOutpointValuescriptPubKeyHeight
01111…1111:030,0005120 79be667e…16f81798199
12222…2222:140,0005120 c6047f94…5c709ee5199
23333…3333:250,0005120 f9308a01…bce036f9199

The slot order is not a choice. Rule 6.9 sorts inputs by display txid bytes then vout, so any two implementations independently derive the same slots and therefore the same fee assignment.

The arithmetic

N       = 3
weight  = 246 + 402 * 3          = 1452
vbytes  = ceil(1452 / 4)         = 363
fee     = 363 vB * 10 sat/vB     = 3630 sats
q       = floor(3630 / 3)        = 1210
r       = 3630 mod 3             = 0

successor(0) = 30000 - 1210 = 28790
successor(1) = 40000 - 1210 = 38790
successor(2) = 50000 - 1210 = 48790

Because the remainder is zero every share is identical. When the fee does not divide evenly, the first r slots each pay one extra satoshi. That rule is what makes the allocation reproducible rather than negotiated.

The result

FieldValue
txid3703d5b332ebf2871a0a24066f7ea4b84dd3e9ebca1b1a879140e844c64b0f65
wtxid772b6d1d179e6f5ba1c4699486a754ef7681b6299ee0e023fc2a5dd0c11245ab
Virtual size363 vB
Fee3,630 sats
Lineages created4bc0f5c8…, 63c22eae…, ee745631…
State hash after transition07b97f0c2cbaa172e66a2137fefbee64c5a05f03e03967d46936cd14070523f1

The lineage identifiers are not derived from the Circle. Each one is SHA256("WITC/lineage/v1" || wire_outpoint) of the participant's own genesis outpoint, so a participant can compute their lineage identifier before the Circle exists.

Rebuild this exact circle in the simulator. The default state of the builder is this vector, so you can watch what happens when you change one value.

Example B: two of the three come back

At height 201 the holders of slots 0 and 1 form a second Circle by spending their successor shards from example A. This is the continuation section of state-lifecycle.json.

FieldValue
Inputs3703…0f65:1 (28,790) and 3703…0f65:2 (38,790)
Participants2, so 263 vB
Fee1,315 sats, q = 657, r = 1
Fee sharesslot 0 pays 658, slot 1 pays 657
txidf7c7ecb3be4ee1925fc97111afde797633de0fae797d0a12383aecb6868328ef
State hashb29662d78f73f3f8940b2dccaf935dc2a7f1ceba21ef6b1c5b11a7d7f3f5a6fc

This is the odd-remainder case: 1315 is not divisible by 2, so slot 0 pays the extra satoshi. Note what did not happen. No new lineage was created. The two existing lineages advanced, their circleCount went from 1 to 2, and the indexer recorded two continuation edges pointing from the genesis Circle to this one. The third lineage was untouched and stays active.

Lineages advancing through two Circles and one closure Three lineages are created by the genesis Circle at height 200. Two of them continue into a second Circle at height 201, which the indexer records as continuation edges. At height 202 an ordinary Bitcoin spend consumes one active shard, which closes that lineage permanently. The third lineage never continues and stays active. height 200 height 201 height 202 circle 3703…0f65 circle f7c7…28ef ordinary spend lineage 4bc0… continues lineage 63c2… continues lineage ee74… stays active, never continued shard spent outside the protocol Closure is a property of the UTXO set, not of parser opinion.
The lifecycle committed in test-vectors/v1/state-lifecycle.json: one genesis Circle, one continuation, and one ordinary spend that closes a lineage. A consensus-valid transaction that spends an active shard without satisfying every Circle rule closes that lineage. No Bitcoin is burned, and the historical Circles remain in the best-chain record.

Example C: an ordinary spend ends a lineage

At height 202 a transaction that is not a Circle spends f7c7…28ef:1, one of the active shards. It might be the owner moving funds, paying someone, or consolidating. The protocol does not care what it was.

  • That lineage becomes closed, its currentOutpoint becomes null, and closedByTxid records the spending transaction.
  • The revision still increments, because a closure is a state transition.
  • No Bitcoin is burned or forfeited. The coin simply left the protocol.
  • Every historical Circle that lineage took part in remains in the record.

The state hash after that closure is faf7f730b9b48b39c4509dc3ccb23c94cac6979d334a57fbd1eda20dab7af84c. Roll the three transitions back in reverse and you land on the continuation hash, then the genesis hash, then the empty-state hash. That round trip is exactly what an indexer performs during a chain reorganization, and it is committed in the vector so two implementations cannot silently diverge.

What actually supports Witness Circles today

Only claims that can be checked in Bitcoin Universe source code appear here.

ComponentStatusEvidence
Reference SDK and CLIAvailablesrc/sdk.ts, src/cli.ts in this repository, package @bitcoinuniverse/witness-circles 0.1.0. No reviewed registry release yet.
Reference state engineAvailablesrc/state.ts, with rollback and a committed state hash.
Dedicated indexerAvailable as sourcebitcoinuniverseio/index-witness-circles.
Wallet integrationNot currently supported in Bitcoin Universe productsNo wallet in the organization implements the section 13 signing flow. The requirements are specified so that any wallet can.
Marketplace or trade pathNot applicable and not implementedWitness Circles has no entry in the marketplace protocol registry. The protocol defines no transferable asset, so there is nothing a marketplace could list.
Mainnet creationDisabledencodeMarkerPayload and buildCirclePlan reject any network other than Signet and regtest with INVALID_NETWORK.

Common mistakes

Using a wallet's change output as a participant input
A Circle publicly links every participating input forever. Use a dedicated UTXO whose history you are willing to publish. Dedicated inputs reduce unrelated exposure; they do not create privacy.
Letting a wallet auto-select an active shard
An active shard is an ordinary UTXO to a wallet. If coin selection spends it in an unrelated payment, the lineage closes permanently. Wallets that support the protocol should protect active shards from automatic selection.
Reusing one key for two slots
Rule 6.8 rejects it with DUPLICATE_SCRIPT. Two slots controlled by one person is not detectable, but two slots with one key is, and the protocol refuses it.
Treating the manifest as truth
The manifest is an untrusted label committed by hash. It can be withheld or lost without affecting validity, and its titles and aliases must be escaped before display.
Expecting a fee bump
There is no CPFP workflow and no single-party RBF. If the fee rate was too low, every participant signs again or the transaction waits.