Canary names the silent-payments server that leaves out a recent entry it signed for.

A light wallet can't tell "nobody paid you" from "the server left your payment out." Canary checks the tweak list a server sends against the record the same server signed for that block. If the list leaves out an entry the record includes, and the block is less than 144 blocks deep, Canary names the server and the block.

It makes a server accountable. It does not remove the need to trust one. The claim needs at least one honest server that publishes signed records, and a path to those records that nobody censors. Version 1 is built and tested on regtest only.

Check an evidence file

This site makes no outside requests.

An evidence file holds a server's signed record, the exact bytes it served and its signature over them. canary verify checks it with no network, and trusts nothing the file says about itself.

The sample comes from our recorded regtest run on 1 Oct 2026, against Bitcoin Core v31.1.0. See the recorded run

The tampered copy changes one byte of the original. Byte 2285, inside proof.siblings[0], reads "1" where the original has "0". That one change makes the Inclusion step fail.

The checker needs JavaScript. Until it starts, download a file and run canary verify on it in a terminal. canary verify FILE

The check runs these eight steps in order, and stops at the first that fails.

  1. Read the file
  2. Record signature
  3. Signer
  4. Block
  5. Inclusion
  6. Receipt
  7. Served list
  8. Retention window

The problem

A silent-payments light wallet asks a server for the tweaks it scans with, because it lacks the data to compute them. If the server leaves out the tweak for your payment, the wallet shows the balance it would show if nobody had paid you. There is no error.

A server can't tell which transactions pay you without your scan key. So hiding one payment takes outside knowledge of it, and the sender always has that knowledge.

The exchange that pays you can also run the server that tells you whether you were paid.

How it works

Honest servers filter differently, so comparing what two servers send raises false alarms. Canary asks each server to sign for the complete list, then lets it serve less.

  1. When a server indexes a block, it signs a record: the entry count and a Merkle root over every eligible transaction.

  2. It serves a tweak list with exactly that many positions. Each carries the entry, its hash or nothing, and a receipt signs the exact bytes.

  3. Canary fills every gap it can from another server or a payment you declared. Only then does it recompute the root and compare it with the signed one.

  4. An entry sent as nothing while the block is less than 144 blocks deep is an omission. Canary names the server and, when it can recover the entry, writes an evidence file.

Every block ends in one of six states, shown as a symbol, a word and a colour together.

  • Checked

    The tweak list matched the record its server signed.

  • Checked, gap filled

    The tweak list matched its signed record once Canary filled the gaps.

  • Can't be checked

    Canary could not recompute the root for this block.

  • Not checked

    There was no signed record to check against.

  • Servers disagree

    Two servers signed different records for this block.

  • Data withheld

    A named server left out an entry it had signed for, or the entry for a payment you declared.

A balance computed over blocks you could not check is a lower bound, not a balance.

Follow one block through every check

What it proves, and what it does not

Canary separates knowing from proving to others. You know whatever your own check saw. Someone else can confirm a finding only from signed data they check themselves.

Others can check it
A server signed for an entry, then sent nothing for it inside the 144-block window and signed a receipt for what it sent. canary verify checks the evidence file offline.
Others see inclusion only
The same omission without a receipt. The file shows that the server signed for the entry, not what it sent.
Only you know it
A false chain claim, a declared payment left out, or a list of the wrong length. Version 1 has no evidence format for these.
Two servers, nobody accused
Servers disagree means two servers signed different roots for the same block. At least one is wrong, and Canary does not say which.
No accusation
Can't be checked is neither a pass nor an accusation. A warning names a server that did something odd, and proves nothing.

Limits of version 1

Each claim stops somewhere, and the limit belongs next to it.

What Canary saysWhere it stops
A block reads CheckedCanary checked the tweak list, not the output data a wallet matches against. A server can send the right tweak and hide the output, and the block still reads Checked. Output keys in the entry are planned for version 2.
A server that serves less than it signed for gets namedNot when it sends the entry's hash under a declared pruning policy. The block then reads Checked, gap filled.
Omission is detectedOnly when you run canary check. Version 1 has no wallet in the loop, so nothing stops a wallet from using a block Canary flagged.
Detection worksVersion 1 is built and tested on regtest only, against its own reference indexer, which shares the checker's canonical package. No deployed server speaks this protocol yet.
One server is enoughOne server alone can't show its record is complete. If the record leaves out your entry, the block still reads Checked. A second server makes that block read Servers disagree, accusing nobody. A payment you declared names the server, but you can't prove it to others.
Several servers catch a lying oneIf every server colludes, only a tripwire helps, and only for payments you know exist.
Records are publicVersion 1 fetches each server's records from that server over HTTP, and publishes them nowhere else yet.
A lying server gets caughtCanary looks for entries left out, never for fake ones. Adding fake entries is a different attack.

Every limit, with the reasoning

Run it yourself

Build Canary once with a network. Then turn the network off and check the evidence file in the repository. canary verify opens no connection.

  1. git clone https://github.com/Sky-walkerX/canary && cd canary
  2. go build -o canary ./cmd/canary
  3. ./canary verify evidence/omission-regtest-351-ad56b9bb-db614560.json

You need git and Go. For the real file, canary verify prints "Checks out." For a tampered copy, it names the step that failed.