Glossary

The terms used across Canary's docs, screens and code, in alphabetical order. Each entry gives the plain meaning first. Where a screen shows the idea under its own label, the entry names that label. Where the design document uses a different name, the entry gives that too.

Exact byte layouts and JSON fields live in v1 formats. For the whole system on one page, read How Canary works.


Accountable

Canary's claim about index servers. A server that leaves out data it signed for gets named, so hiding a payment stops being silent. This is weaker than trustless, because you still rely on two conditions. At least one honest server publishes signed records, and you have an uncensored path to a relay that carries them.

Byte order

Txids and block hashes have two byte orders. Internal order is how the bytes sit inside a serialized transaction or block header, and every hash Canary computes uses it. Display order reverses the bytes, the way block explorers and bitcoin-cli print them. JSON, URLs and Nostr tags use display order. Each Canary program converts in one place, because mixing the two orders gives a different root for the same data.

Example: the internal-order txid 75d59e36…66ae displays as ae667139…d575.

Canonical set

The complete list of entries for one block, in transaction order, with nothing filtered out. It includes every transaction BIP-352 makes eligible, and ignores the optional rule that lets a server skip spent ones. It depends only on the block and the outputs its transactions spend, so anyone with a full node can recompute it.

In the design: canonical set, written T_base(block) in older text. The roadmap calls it the "full list".

Commitment

The design document's name for a signed record.

Coverage

Canary's main output: a state for every block checked, reported as ranges of heights. An alarm that never fires looks like a tool that does nothing, so Canary reports what it checked on every run. Coverage also supports the one sentence worth remembering about a light wallet's balance. A balance computed over blocks you could not check is a lower bound, not a balance.

On screen: the coverage strip and the range table on the dashboard's Blocks page.

Cut-through

Honest filtering that drops a transaction's entry once all of its taproot outputs are spent. BIP-352 allows it: "spent transactions optionally can be skipped". blindbit-oracle offers it as an option, and silentiumd does it by default. Cut-through is one reason two honest servers serve different lists for the same block.

In a server's policy: prunes_spent. The design folds cut-through and unspent-only indexing into that one flag, because a client sees the same result.

Dust filter

Honest filtering that drops transactions whose taproot outputs all fall below a threshold in satoshis. A server declares it as dust_threshold_sat in its policy, where 0 means none. Canary cannot verify the threshold, because an entry carries no amounts, and no state depends on it. canary check always asks for the unfiltered list.

Entry

One transaction's pair of txid and tweak: 32 + 33 = 65 bytes. Canary hashes each entry with the tag canary/leaf/v1 to build the Merkle root. An entry carries no amount and no output keys, which is why Canary v1 does not check output data.

In the design: leaf.

Event

A signed Nostr message. Canary's signed records are events of kind 1352. That is a regular kind, so no later event can overwrite one in place. Canary never uses a replaceable kind, since a server could then overwrite an old record. A server can still send a NIP-09 deletion request, which many relays honour, so the design relies on copies that clients and other relays keep. v1 publishes no events to relays. The b tag holds the block hash in display order, and relays index it so a client can fetch records by block hash. The event's created_at time is set by the signer and can be backdated.

Evidence file

A JSON file that lets anyone check a Data withheld finding offline, with canary verify and no network. It holds the server's signed record, the receipt, the exact bytes served, the left-out entry and an inclusion proof. It stores no result. canary verify recomputes everything from the signed data. Without a receipt, the file shows only that the server signed for the entry, not that it left the entry out.

On screen: "Checks out", "Inclusion only: you can be sure of this, you can't yet prove it to others.", "Does not check out" and "Can't read this file". Format: canary-evidence/1. In the design: evidence artifact.

Expected payment

A payment you declare to canary check so it can act as a tripwire. Also called a declared payment.

Finding

A fact about a server and a block that Canary records and carries forward from run to run. There are three kinds:

  • withheld names one server and comes from a Data withheld result. It is an accusation.
  • disagree names two servers and comes from a Servers disagree result. It says one of them lied, not which.
  • warning names one server that did something an honest server would not, where nothing it signed shows it. It is not an accusation and changes no block's state.

Each finding records whether others can check it, in its provable field. On screen, warnings end with "Not an accusation." In the design: alarm.

Gap

A position in a tweak list that does not carry the entry in full. The server sends either the entry's 32-byte hash or nothing at all, which the formats call hash and absent. Canary tries to fill every gap from another source before it recomputes the root.

On screen: a block whose root matched after gaps reads "Checked, gap filled". In the design: a position sent as nothing is a hole. The roadmap says hash-only slot and empty slot.

Inclusion proof

The sibling hashes that link one entry to a signed root. A proof for a 2,000-entry block is 11 hashes of 32 bytes, 352 bytes in all. It shows that the server signed for that entry without shipping the whole list.

In the design: Merkle proof. In the evidence file: the proof field.

Index server

A server that computes the entries for each block and serves them to light wallets. Examples are blindbit-oracle, Cake Wallet's electrs, shroud-indexer and Canary's own reference indexer. The docs also say indexer, or just server.

In the design: indexer.

Light wallet

A wallet without its own full node. To receive silent payments it asks a server for tweaks, because computing a tweak needs the outputs a transaction spent, and a light wallet does not have them.

Merkle root

A 32-byte hash over all of a block's entries, built as a Merkle tree of BIP-340 tagged hashes. Canary's root also binds the network, the block hash and the entry count n, so nobody can reuse it for another block or another length. A block with no entries still gets a root and a signed record.

On screen: "fingerprint", on detail pages only. In the design and the formats: root.

Nostr

An open protocol for signed messages, passed around by relays. Canary uses it as a public bulletin board for signed records. Nostr gives publication, not timestamping. Canary orders records by block hash, never by the time an event claims. In v1 each server hands out its own records over HTTP. Publishing them to relays comes after v1.

Omission

Leaving out data a client needed; here, an entry for a block. The docs also say withholding or hiding. Canary looks for omission only. The opposite attack, commission, adds fake entries, for example to make a wallet fetch a block and reveal its IP address. Canary does not defend against commission.

Output data

The data a wallet matches against, once it has a tweak, to decide whether a payment exists. On BlindBit v1 that is the new-UTXO filter and the /utxos list with its spent flag. On BlindBit v2 it is each transaction's 8-byte output-key prefixes, outputs_short. Canary v1 commits to entries only, so a server can serve the right tweak, hide the output, and the block still reads Checked. Adding output keys to the entry is planned for v2. A false spent flag stays out of reach even then.

Policy

A server's declared filtering: prunes_spent, dust_threshold_sat, dust_configurable and a start height. Every field can only remove entries. In v1 the policy comes from the server's unsigned /info response, so Canary uses it for display and warnings, never as evidence.

In the design: policy declaration, which a server signs and cites by event id in the record's policy_ref tag. v1 publishes no policy event, so policy_ref is 64 zeros.

Receipt

The server's signature over one tweak list and the request that produced it. It is 178 bytes, sent as 356 hex characters in the X-Canary-Receipt response header. It signs the network, the block hash, the dust threshold asked for, the server's tip height and hash, and the SHA-256 of the exact bytes served. A signed record says what exists in a block. A receipt says what the server gave you. With both, anyone can check an omission.

Reference indexer

canary-indexer, the small index server that ships with v1 and runs on regtest. It signs a record for every block, serves tweak lists with receipts, and has a --withhold-txid switch that makes it leave one transaction out on purpose, for the demo. It reuses Canary's own canonical package. So v1 does not test two independent implementations against each other.

Regtest, signet and mainnet

Bitcoin networks. Mainnet is the real one. Signet is a public test network where a designated signer authorizes each block, so proof-of-work gives it no integrity. Regtest is a private chain on your own machine, where you mine blocks on command.

Canary v1 is built and tested on regtest only. canary check accepts regtest and mainnet only. On mainnet it prints a notice that v1 is tested on regtest only. It refuses signet and every other chain until a flag can name the network. Canary identifies each network by its 4-byte message-start magic, which keeps two custom signets apart. Regtest's magic is fabfb5da, written 3669344250 in JSON and in Nostr tags.

Relay

A server that stores and forwards Nostr events. A relay may drop, delay or censor events, and relays differ in how long they keep them. The event's signature shows who signed it. A relay's copy adds only that the event was published, not when.

Retention window

A server's signed tip and the 143 blocks below it (depth 0 to 143), about one day at 10 minutes per block. Inside it, a server must keep at least the hash of every entry. So an entry sent as nothing is an omission, and Canary names the server as Data withheld. The window is a fixed rule of the protocol. No server can declare its own.

depth  = tip height − block height
inside = depth < 144

The tip height comes from the server's receipt, and the block height from its signed record. For a block at height 205 served with a signed tip of 212, the depth is 7, inside the window. That block leaves the window when the server's tip reaches 205 + 144 = 349.

Scan key

The private key a silent-payments recipient uses to find payments, by combining it with each transaction's tweak. A server without your scan key cannot tell which transactions pay you. Hiding one specific payment therefore takes outside knowledge of it, which the sender has. Handing your scan key to a server lets it scan for you, and also shows it every payment you receive.

Signed record

A server's signed statement about one block: the entry count n and the Merkle root over the canonical set. It is a Nostr event of kind 1352, served at GET /commitment/{blockhash}. A server signs it once, when it indexes the block, and never signs a second one for the same block. Two records for one block from one key contradict each other. A record is about 690 bytes of JSON. The 1 Oct run's record for block 351 is 691 bytes.

In the design: commitment.

Silent payments

The BIP-352 scheme for reusable payment addresses that never appear on chain. Each payment goes to a fresh taproot output, derived from the recipient's address and the sender's input keys. The recipient finds payments by scanning each eligible transaction with its scan key and that transaction's tweak.

SPCOMMIT

Rob Segers's commitment scheme for tweak indexes, live on mainnet since 1 Sep 2026. It hashes each block's unfiltered tweak list into a chain, and signs only the head of the chain, posted to Nostr every 6 hours. Its v2 also covers output prefixes and spent outputs, which Canary v1 does not. How Canary works compares the two.

States

The six results Canary gives a block, one per block per run. A screen always shows a state as its label, a symbol and a colour together.

On screen Code Design name Meaning
Checked verified Verified The root recomputed over every position matched the server's signed root, and no position needed filling. The tweak list was checked, never the payments
Checked, gap filled resolved Resolved As Checked, but some positions came as hashes or were filled from another source before the root was recomputed
Can't be checked unresolvable Unresolvable Canary could not recompute the root. Neither a pass nor an accusation
Not checked unverified Unverified There was no signed record to check against. Nothing about the block was checked
Servers disagree disputed Disputed Two servers signed different roots for the same block hash. At least one lied, and Canary does not know which
Data withheld compromised Compromised One named server left out an entry it had signed for, or the entry for a payment you declared

Each state carries a reason code, such as absent_in_window or hash_retained. v1 formats lists them all.

The design's comparison procedure names three outcomes for one server and one block. Clean becomes Checked or Checked, gap filled. Omission detected becomes Data withheld. Unresolvable stays Can't be checked.

Tripwire

A payment you know exists, declared so that Canary checks every server reports it. In v1 you make a payment from your own Bitcoin Core wallet and pass --expect TXID to canary check. Canary computes the payment's entry from your node. The tripwire still works when every server colludes, and it covers only payments you know about. v1 checks the entry only, not the payment's output data. Scheduled test payments at random times and amounts come after v1.

In the design: tripwire, or expected payment. In the state file: expected_payments.

Tweak

A 33-byte public key computed for each eligible transaction: the sum of its input public keys, multiplied by a hash of that sum and its smallest outpoint. Computing it needs the outputs the transaction spends, which a light wallet lacks, so the wallet asks a server. The wallet then combines each tweak with its scan key to find its own outputs.

Tweak list

What a server returns for one block at GET /tweaks/{blockhash}. It holds exactly n positions in canonical order after a 4-byte count. Each position carries the full entry, the entry's hash, or nothing. With every position full, it is 4 + 66n bytes, so 132,004 bytes at n = 2,000. A receipt covers the exact bytes.

In the design: response, or served set.

This page is built from a file in the repository: docs/glossary.md