Architecture

How Winnow actually works.

Five choices that make a private wallet practical on a phone. Each one is a real trade, drawn rather than asserted.

1 · It reads summaries, not the chain.

Every block gets a compact filter — a small summary that answers “might this block touch one of your scripts?” Winnow checks that on the phone. Only a hit costs a full block download, and nobody is ever told which scripts it asked about.

Every block · always

filter · newestchecked
filter · newerchecked
filter · olderchecked
filter · olderchecked
filter · oldestchecked

the same public filter any compact-filter client can request

MATCHED HERE against your own scripts, on the device

On a hit only · rare

a whole block the one block that matched

only when a filter matches

The asymmetry is the design. A small public filter for each block, a whole block only on a match, and no query that names an address — so there is nothing for a wallet server to log, because there is no wallet server.

2 · It listens hard, but only briefly.

Filters describe blocks, so a payment would normally be invisible until it confirms. While the Receive screen is open — exactly when you are expecting money — Winnow subscribes to full relay traffic and matches locally, then closes the window when you leave.

block
block
block
RECEIVE SCREEN OPEN
yours — seen unconfirmed while Receive is open
everyone's relay traffic — asked for wholesale, never per address your payment, spotted on the device

Asking for everything is what keeps it private — asking for one address is what would give you away. The cost is bandwidth, and it is bounded by a screen you actually have open.

3 · It never looks backwards.

Recovering an old wallet privately would mean re-downloading years of filters. Winnow refuses that: a new wallet scans forward from the day it was made, and an imported one arrives with its own history plus the height it was true as of.

New walletcreated at height H · nothing before it exists
never scanned
scanning forward →
Imported walletbundle is true as of height H · verify forward from there
the old wallet already did this
bundle ↓ UTXOs + history
verifying →

Import cost is proportional to how stale the bundle is, not how old the wallet is. A bundle exported at the tip is a JSON parse and a zero-length scan — and it's why Winnow writes one of its own.

4 · Two signatures that look like one.

Elena and Mateo hold the family reserve together — a 2-of-2 MuSig2 vault, coordinated over PSBTs with no wallet server in the middle. Two rounds, public nonces then partial signatures, collapse into a single Schnorr signature.

Round 1 · nonces

Each phone publishes a public nonce. The secret halves stay in the signing screen and never persist — leaving before round 2 abandons the session.

Round 2 · partials

Elena and Mateo each sign the same sighash, producing a partial signature that is verified before it is attached. Signing zeroes the secret nonce.

Aggregate

The two partials combine, and the result is checked against the vault's aggregate key before it is trusted.

On chain

64 bytes · P2TR key-path

Identical in form to an ordinary single-signature spend. Watchers cannot tell a vault from an everyday wallet.

Both-of-two costs the chain one signature, so the on-chain witness shape and fee are those of an everyday spend. That is a statement about the chain only — coordination between cosigners and network-level privacy are separate matters. The k-of-n vault is the honest opposite: it uses a script path, which is visibly multisig — a cost paid deliberately for “k of us, and nobody holds an escape hatch.”

5 · It asks several strangers the same question.

A peer can lie by leaving your transaction out of a filter. You cannot prove which peer lied — but you can notice when they disagree. Winnow asks up to three of them for the same filter checkpoint and compares those answers before trusting any of them.

peer A cfcheckpt 000000a3f8…e21c ✓ agrees
peer B cfcheckpt 000000a3f8…e21c ✓ agrees
peer C cfcheckpt 0000004b17…9d02 ✕ dropped

Among checkpoint answers a strict majority wins, and the odd peer out is dropped and replaced. When two disagree with no majority the lie is unattributable, so Winnow drops all the queried peers and re-dials rather than guess. This majority rule is the checkpoint comparison specifically — it does not govern every filter-header batch.

Detection, not proof — and not operator independence. Peers are dialled by whichever candidates answer fastest, so nothing yet stops one operator supplying more than one slot; pinning slots by source is planned, not shipped. What ships is the comparison and the refusal to proceed on a tie — and a fully eclipsed phone can still be told one coordinated lie.