The wallet on your phone
Your phone matches payments locally, keeps its keys in the device keychain, and connects to Bitcoin peers, directly or through your own Tor and I2P gateways, while the app is open and in short background checks iOS allows.
What this means for you
Unexpected payments may appear on the next sync. Fees are estimates, and a backup restores signing only when it holds this phone’s words, which the iCloud copy always does; see what a backup cannot restore. Direct connections expose your IP address to peers; your own Tor or I2P gateways can carry peer connections instead. The read guide, payment guide and recovery guide explain these tradeoffs.
2. What a phone actually is
Constraints that are load-bearing, not stylistic:
- Outbound TCP only. The app talks to full-node peers over
Network.framework. No public inbound listener. Backgrounding stops networking; a background check iOS starts builds it again for one bounded scan. Peers are a small outbound pool (default 3): manual endpoints first, then a persisted good-peers list, then a fresh signed downloaded census, then DNS-seed results — seeds resolve over DNS-over-HTTPS (Cloudflare dns-json at 1.1.1.1, configurable) and fall back togetaddrinfoif DoH is empty or down; RFC1918/loopback answers are dropped on public networks. Dials race a batch of candidates at a time with a short per-attempt timeout, and report exhaustion instead of spinning forever. Peer routing (Automatic, Direct only or Manual) decides which networks are eligible; DNS seeds are used only when clearnet is, and Tor or I2P candidates only through their gateway. No peer-address list ships in the app. Mainnet automatically fetches the signed census when its cached catalog is missing or expired; saved-peer and DNS discovery continue independently. With DNS as the only source, the diversity policy permits two of the three default slots. No addr gossip, no scoring buckets — a misbehaving peer is dropped and replaced. - Foreground, plus short background checks. Winnow syncs while open, and asks iOS for background checks (
BGAppRefreshTaskandBGProcessingTask, at most every 15 minutes). Each is one scan of up to 2,000 blocks inside the time iOS grants, about 25 seconds for a refresh and up to two minutes for processing, with the foreground's routing, peers and checks; then networking stops. iOS decides when and whether they run; they do not run while the phone is off, after Winnow is force-quit, or with Background App Refresh off. Automatic routing that last reached peers through a gateway skips a check when no gateway answers rather than connect directly with no banner to say so. There is no standing connection and no push: push notifications would require a server that knows your addresses or your txids. A payment you are not looking at is found at the next check or the next open. - This-device keychain. Secrets live in the iOS Keychain as
kSecAttrAccessibleWhenUnlockedThisDeviceOnlybehind a.userPresenceaccess control, with iCloud Keychain sync off. They are not in the app’s ordinary JSON state or device backup. Automatic iCloud recovery creates a separate encrypted copy after device authentication unless disabled in Advanced; its wrapping key synchronizes through iCloud Keychain. Recovery words appear only behind an explicit Advanced control gated on device-owner authentication (Back up wallet → Show recovery phrase, fail-closed when no passcode is set), and the Keychain runs the same passcode-or-biometric check itself before it releases the secret, so the gate does not rest on the app’s call sites alone; the check the app passed is handed to that read so you are asked once, not twice. A wallet stored by 0.7.1 or earlier gets the access control on its next open, in place. The recovery-word screen offers an explicit copy action; its pasteboard item is local-only, expires after two minutes, and warns that paper is more private. Signing loads the secret for the call and drops it. - Cryptography and transport dependencies.
swift-secp256k1— Bitcoin Core’s libsecp256k1. Swift never does raw curve math on secrets. Filters, headers, PSBT, descriptors and Bitcoin P2P are in-tree; there is no other third-party code. - App Store reality. iOS 17 or later. The app targets iPhone and iPad, with the same iOS app available on Apple silicon Mac. Wallet and protocol behavior lives in the package; app units and one recorded iPhone journey check the interface and its integration.
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? | inv → getdata → tx 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 and automatic backup continue after onboarding; 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 proposed 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 Mainnet by default, signet for testing
The app opens on mainnet by default; signet is available in Advanced settings with its own wallet state. The focused UI integration test and its continuous video use a disposable signet fixture. The numbers in screenshots/timings.json are historical signet measurements.
5. Trust on a phone
What the user is asked to trust, stated as mechanisms rather than intentions:
- The keychain and the device. The default backup file excludes recovery words. An export made with Include recovery phrase contains your phone’s signing secret. Protect that file like the words themselves. Anyone able to unlock your phone may be able to authorize signing or secret access.
- libsecp256k1. Curve math on secrets is not our Swift. Side-channel claims against the wallet’s signing path are claims against that library, or against how we call it (we pass 32-byte auxiliary randomness from
SystemRandomNumberGeneratorper BIP340). - The peers you can reach. Compact filters are not consensus-committed. Multi-peer
cfcheckpt/cfheaderscomparison detects disagreement; it cannot prove which peer lied, and a fully eclipsed phone can be shown a consistent lie. Manual peers exist so a user who has a node they trust can skip DNS seeds. The residual risk is read-side §2.7.1 and §2.9. - No one, on the read path, by default. No server is handed an address, an xpub, or a scripthash. A network observer sees a compact-filter client syncing from a height — the same bytes every such client downloads.
What the user is not asked to trust: an indexer operator’s retention policy or a push-notification vendor. Optional iCloud recovery adds trust in the Apple Account and iCloud Keychain recovery path. The app target enforces device authentication and the persistent backup preference. Wallet, protocol, and crypto logic are forbidden from moving into WinnowApp.
6. Deliberate absences
These are product decisions, not missing tickets:
- Always-on mempool / background 0-conf / live fee histograms
- Historical filter back-scan
- BIP37, Electrum, or any “ask a node about my address”
- An embedded consensus engine or fraud-proof verifier
- A vault coordinator server
The focused guides explain the consequences: delayed payment discovery, coarser fees, a required history backup, and transaction disclosure to relay peers.
7. How the implementation is checked
The validation appendix of the read side covers the stack. The focused signet UI integration test drives ordinary, MuSig2, and script-path 2-of-3 receive/send flows through the real binary while the host records a continuous video. The fixture provides Core cosigners and mines confirmation blocks automatically. Detailed behavior and import rules have focused app, wallet, and protocol tests. Guide illustrations and screenshots/timings.json retain their historical provenance; they are not current-run evidence.
CI combines package tests, app unit tests and the recorded iPhone journey. Website-only changes may reuse successful media when the app and test inputs match; the recording retains its original provenance.
8. Status
The app defaults to mainnet and also supports signet. Current signing choices are ordinary Taproot, MuSig2 and script-path threshold accounts. Planned features remain on the roadmap.