Winnow

A Private Bitcoin Wallet on a Phone

Framing paper for Winnow. The other papers — read, write, vaults, import — each own one job. This one owns the device.


1. The constraint

A Bitcoin wallet is keys plus four questions about the chain (what’s mine, where’s the tip, what fee, did my transaction get out). On a desktop, any of the usual answers work. On a phone, most of them don’t.

A phone is not a full node: it will not download, store, or validate the chain. A phone is also a poor thin client of someone else’s node: it has a stable identity (an IP, an Apple ID, a push token), it is almost always on a network that can be logged, and the user will not read a privacy policy before tapping Receive.

The product lives in the space between those two refusals. The phone does the matching. Nobody is asked which addresses are yours. The chain is not stored. Work happens while the app is open. Everything else — Taproot-only, fresh wallets, import-with-history, bounded mempool windows, and warned external explorer links — is a consequence of that sentence, not a feature list.

This paper states the device constraints, the architecture they force, and which paper answers which question. It does not re-argue compact filters; that is the read side.


2. What a phone actually is

Constraints that are load-bearing, not stylistic:

Bandwidth and battery are real but secondary. Steady-state compact filters are a few megabytes a day (approx.; see read-side §2.5). The worse mobile cost is policy: an always-on mempool subscription, a historical back-scan, or a server that answers getbalance. Those are refused even when the radio would allow them.


3. The four jobs, and two more

Job Default on this phone Paper
What’s mine? BIP157/158 filters, matched on-device, from creation height forward read-side
Where’s the tip? 80-byte headers, PoW + chainwork read-side
What fee? User override, else median of our confirmed spends, else presets; clamped by BIP133 feefilter write-side
Did my tx get out? invgetdatatx to the peer pool, rebroadcast until a filter match, echo-watch while Send is open write-side
Who holds the keys? This-device Keychain; Taproot key-path for the everyday wallet this paper, write-side
Shared custody? MuSig2 n-of-n or multi_a k-of-n, coordinated by PSBTv2, no server vaults
Moving a wallet in? A history bundle. There is no back-scan. import

One user-controlled escape hatch crosses several rows: a selected external explorer website. Opening an exact address or transaction requires a warning; its response never becomes wallet state.


4. What the device forces onto the wallet

4.1 Fresh, and forward-only

A new wallet and its recovery phrase are created immediately, offline. Header synchronization continues while the user secures the phrase; the wallet starts scanning from the last already-validated height so the interval cannot be missed. Recovering an old wallet privately from the chain would mean gigabytes of historical filters on a radio the user pays for. The product does not do that. Import requires the previous wallet’s answers to come along — descriptor, UTXOs, history, a last-known height — and the phone verifies them by scanning forward from that height. The argument and the bundle format are import.

This is the constraint that makes the rest cheap. It is not a v1 shortcut.

4.2 Taproot only

Receiving is BIP86 P2TR. There is no legacy / nested-segwit / P2WPKH path, and no ECDSA signing path. One output type means one watch-list shape, one sighash, one witness size for fee math (66 bytes for a key-path spend), and no “which address type did this contact use?” branch in the UI. The planned 0.7 P2WSH Safe is a deliberately scoped exception: an opt-in wsh(pk(KEY)) store with its own hardened keys, its own watch list and an ECDSA/BIP143 signing path, added beside the Taproot account rather than in place of it. Nothing about the account described here changes; see the Safe’s design and release criteria.

The phone can pay standard P2PKH, P2SH, P2WPKH, P2WSH and P2TR addresses. Recovery supports descriptor-derived coins; wallet files requiring unsupported signing data are refused. Keep the original wallet and backup for any unsupported coin types. See import and recovery.

4.3 Two modern multisig flavors, same read path

A vault is another descriptor and another watch list. The phone does not run a coordinator, a coinjoin server, or a notification service for cosigners. The PSBT is the coordinator; AirDrop, a file, a message are the transport. Vaults.

4.4 Default network is signet

Development and the shipped TestFlight path target signet. Mainnet is in the Settings picker and has its own on-device wallet; it is not the network the e2e suite, the screenshots, or the timings are taken on. The papers describe the protocol as it runs on any network; the numbers in screenshots/timings.json are signet.


5. Trust on a phone

What the user is asked to trust, stated as mechanisms rather than intentions:

What the user is not asked to trust: an indexer operator’s retention policy, a push-notification vendor, iCloud Keychain, or the app target. Wallet, protocol, and crypto logic are forbidden from moving into WinnowApp.


6. Deliberate absences

These are product decisions, not missing tickets:

Each absence has a paper that says what you get instead. The landing page lists the user-visible costs (slower news, coarser fees, bring your history, sending still shows at the relay peers). This list is the engineering version of the same honesty.


7. How the implementation is checked

The validation appendix of the read side covers the stack. The mobile-specific claim — that the app does what the libraries promise — is the signet UI suite: it drives the real binary, mines to it, spends from it, cosigns a vault, verifies an import, and writes the screenshots on this site plus screenshots/timings.json. Latest run (iPhone 17 Pro Max simulator, 2026-08-17): wallet create 2.74s, address shown 2.16s, mined→filter-detected 0.69s, form→broadcast 0.66s.

The suite is ~200 tests plus those UI scenarios, on every push.


8. Status

Protocol core, both vault schemes, and the P2P read/write path are complete and vector-tested. The app runs on signet. Mainnet is available and treated as needing more care than a picker flip.

v1 is therefore: a foreground iOS client, Taproot-only, fresh-wallet, one dependency, talking only to NODE_COMPACT_FILTERS peers unless the user opts into a named server — with the four jobs split across the papers that follow.