A Bitcoin wallet for iOS with on-device compact-filter matching, written in Swift.
Download on the App Store — pre-release builds ship through TestFlight. Mainnet by default; signet is one Advanced-mode toggle away. The broader beta is planned for 0.9; see the release roadmap.
- Pure P2P by default. The read side is BIP157/158 compact block filters served by full-node peers — the wallet does not send its watch list to a wallet-history server. Peers still observe requests, relay traffic and connection metadata. Bounded mempool windows (short full-relay subscriptions while the Receive or Send screen is open) give 0-conf payment detection and propagation tracking without any server. Read how it works.
- Taproot today. Current receiving uses P2TR (BIP86), with no ECDSA signing path. The planned 0.7 P2WSH Safe is a separate, opt-in addition.
- Pay people and share control. Save a public payment card or Bitcoin address. From Wallet, create shared savings with a threshold such as 2-of-3: any two keys can spend while one is unavailable. A 1-of-n policy permits one key to spend. Cards carry public keys only.
- Require another signing device. Advanced mode offers MuSig2 accounts where every key must participate. The app journey uses Bitcoin Core 31.1 as the second signer, confirms one Taproot key-path signature, and restores the account from a backup. Hardware-wallet compatibility needs its own test. Compare the two signing journeys and their limits.
- Bitcoin cryptography:
swift-secp256k1(Bitcoin Core's libsecp256k1), pinned; the only third-party dependency. The embedded Tor client shipped in 0.6 through 0.7.0 was removed in 0.7.1 (its source is kept on thearchive/tor-0.7.0branch); Tor and I2P are on the roadmap. - Warned explorer links — choose mempool.space or a custom Esplora website; Winnow opens it only after a tap and privacy warning, never as a wallet backend.
The public roadmap keeps planned work separate from current features. Each milestone describes the user outcome, the journeys that must pass, and unresolved work. Milestones are targets, not shipped capabilities or promised dates.
The app, Bitcoin implementation, CLI, fuzz harness, debugging tools and website live in this repository. The app and all development tools use one root Swift package and dependency lockfile. There is one release version and one source revision.
The app owns the user flows. WalletCore contains Bitcoin primitives, wallet state, and P2P networking in one target. The winnow-debug tool and fuzz harness use that same implementation. Explorer links open an external website after a warning.
The testing and feature policy ties supported features to actual app journeys and names the lower-level invariants worth keeping. The homepage and Advanced page are generated from docs/journeys.json and the app test source; run scripts/build-site after changing either. CI rejects missing or undocumented app scenarios.
Each guide explains what its directory owns, why the app or debugging needs it, and which consumers and tests support that purpose. Every guide links back here. When adding an owned directory with code, data, or documentation, add its README and index link together. Parent folders with their own files need a guide too; asset catalogs are documented by their owner, outside the bundle.
- App and inspection: iPhone app, debugging and offline inspection.
- WalletCore primitives: crypto/encodings, descriptors, scripts.
- WalletCore: keys and secret storage, PSBTs, transactions, wallet policy.
- Network synchronization: filters, headers, peer selection.
- Network communication: relay, mempool, wire formats, transport.
- User experience: app decisions, GUI journeys.
- Rules and interoperability: Bitcoin primitives, wallet rules, network cases, Core comparisons.
- Shared fixtures: TestSupport, real nodes, controlled peers.
- Reference data: Bitcoin vectors, MuSig2 vectors, wallet/network vectors.
- Development tools: tool tests, filter regressions, PSBT regressions.
- GUI/network debugging: runbook, implementation, release-data generators.
- Fuzzing: runbook, executable, shared invariants.
- Website: pages and generation, selected screenshots.
- Security: reports and claims, retained soak evidence.
- Operations: public contributor runbooks, workflows, scripts, tooling regressions.
swift test
swift run winnow-debug inspect tx <hex>
xcodegen
scripts/ci-app-tests /tmp/winnow-app-tests
scripts/ci-debugUse XcodeGen 2.46.0. scripts/install-xcodegen downloads and verifies that
version; locally it prints the executable path to use. The generated Xcode
project is ignored. Run app tests with a fresh results directory.
Test fixtures live once, in the TestSupport library
(Tests/Support); it imports neither Testing nor
XCTest, so the swift-testing targets and the Xcode bundles share it and every
assertion stays at the call site.
scripts/check-dependencies --xcode /path/to/DerivedData verifies that Xcode uses the root
package and its committed third-party dependency revision.
CI and release operations describes the shared validation gates, node fixture, TestFlight recovery and website deployment. The LOC workflow publishes JSON, CSV and Markdown reports for every PR, main push and manual run, retained for 90 days subject to organization limits. Its summary separates app/library/CLI source, tests, webpages, tooling and other text; generated peers, binaries and LFS pointers are excluded. See the counting policy.
Mainnet is the default network, and the app starts in beginner mode: one
screen with the balance, Receive, Send, your activity, shared savings once you
have any, and the backup — no settings. Open a payment to save or rename its
recipient, then pick them from Saved recipients in Send. The same Send form
handles every account: choose the account, recipient, and amount, then review
the fee. Shared accounts continue to their required approvals before anything
is sent. Advanced, in the corner of that screen, switches to the three-tab
interface — Wallet, Send, Settings — with the test network, your own peers,
chain verification, the block explorer, custom fees, fee bumping, build
details and the raw vault tools; Simple, on the Wallet tab, switches back and
keeps every setting. Switch to signet there for development. Point the app at
your own filter-serving node (Settings → Manual peers); the node needs
blockfilterindex=1 and peerblockfilters=1.
GUI diagnostics live in Tools/Debug. Use
scripts/winnow-debug doctor for environment checks and
scripts/winnow-debug diagnostics for local simulator logs and screenshots.
The demo and publication workflow has been retired; functional GUI journeys
remain in UITests.
Screenshot PNGs in docs/screenshots/ are stored in Git LFS. After cloning,
install Git LFS and download the image objects before viewing or publishing them:
git lfs install --local
git lfs pullThe website workflow fetches LFS objects before publishing. The node workflow writes new screenshots into its run artifacts. Local test runs use fresh temporary directories. Select public images deliberately from a successful run; Git stores LFS pointers when those selected PNGs are added.
Winnow is available under the MIT License.