Bitcoin node in your pocket in under an hour.
Turn any Android phone into a fully-validating Bitcoin full node. No server dependency, no ongoing tethering. Your phone becomes a sovereign Bitcoin node.
📖 Project overview and how chainstate copy works
- Two proven bootstrap paths: sync from home node (under 1 hour) or download from internet (3-6 hours, on-chain only)
- Phone-to-phone and relay sharing: scan a QR code, get a full node. Resume on failure. Works over WiFi or LAN
- Two Bitcoin Core versions: Core 29.3 (default) and Core 30. One-tap switching, same chainstate
- No thermal load: phone shows no sign of load or overheat during normal operation
- ~13 GB total disk (11 GB chainstate + 2 GB pruned blocks). Optional: +13 GB for block filter index (Neutrino wallets only, not needed for Lightning)
- Pure Kotlin Electrum server with wallet tracking: balance, transactions, UTXOs all served from your own pruned node
- Built-in Lightning wallet powered by LDK: send, receive, BOLT12 offers, open/close channels, peer browser, QR codes
- LNDHub API for external Lightning wallets (BlueWallet)
- P2P port exposed on localhost:8333 for Neutrino-compatible wallets
- Proactive prune recovery re-downloads missed blocks on startup when node was offline
- Full Tor integration with one-tap toggle: routes bitcoind P2P, Lightning peers, and all HTTP calls through embedded Arti SOCKS proxy. Your ISP sees only Tor traffic. No Orbot needed.
- Channel safety: WAL checkpoint after every state change, Static Channel Backup (SCB) for recovery, circuit breaker for crash loops
- Power modes: Max (continuous), Low (15 min bursts), Away (60 min bursts) with auto-detect based on WiFi/cellular/battery
- Channel probe scanner: discover which Lightning nodes accept small channels before committing funds
- Relay sharing: pull chainstate from any Pocket Node over LAN, resume on failure, session progress tracking
- Cooperative close fix: configurable fee floor prevents force-closes over minor fee disagreements
- Detailed balance view: on-chain spendable, fee reserve, lightning, pending close, all accounted for
Running on a Pixel 7 Pro with GrapheneOS
First run
| Setup screen | Setup checklist | Version picker |
![]() |
![]() |
![]() |
Node + On-chain wallet
| Node dashboard | Electrum server | BlueWallet connected | BlueWallet wallet |
![]() |
![]() |
![]() |
![]() |
Lightning Wallet
| Dashboard | Lightning wallet | Watchtower + Seed | Peer browser |
![]() |
![]() |
![]() |
![]() |
Three bootstrap paths. Pick whichever suits your situation:
- Someone sharing the freedom opens Share, which shows a QR code
- You scan it (or enter the IP manually)
- Chainstate transfers directly over WiFi or LAN. Resume on failure.
- Full node with Lightning. No home node needed. Zero accounts, zero servers.
- App connects to your home node (Umbrel, Start9, any Bitcoin node) via SSH
- Briefly stops bitcoind, archives chainstate + block index
- Downloads the archive (~11 GB), extracts, starts bitcoind
- Full node at chain tip. Optionally includes block filters for Neutrino wallets.
- Download a UTXO snapshot (~9 GB) from utxo.download
- App loads it via
loadtxoutset(cryptographically verified by Bitcoin Core) - Phone syncs forward from the snapshot height (~30 min to load, 2-5 hours to reach tip)
- Background validation confirms everything independently from genesis
Note: Lightning (LDK) works immediately on all paths. Block filter indexes (~13 GB) are optional and only needed for Neutrino wallet connections (Zeus).
See Direct Chainstate Copy for a detailed comparison.
Two Bitcoin Core versions with one-tap switching, differing only in relay / OP_RETURN policy.
| Implementation | Size | Policy |
|---|---|---|
| Bitcoin Core 29.3 (default) | 8 MB | Standard relay rules and OP_RETURN limits |
| Bitcoin Core 30 | 8.6 MB | Permissive: larger OP_RETURN data allowed |
Both share the same chainstate format. Switch without re-syncing. Tap "Change" on the dashboard, confirm, and the node restarts with the new binary.
See Version Selection Design for details.
- Two Bitcoin Core versions with one-tap switching: Core 29.3 (default) and Core 30
- Two proven bootstrap paths: home node or internet download (phone-to-phone built, untested)
- Pure Kotlin Electrum server purpose-built for pruned nodes: the only Electrum server that works with
prune=2048. Balances from the UTXO set, transaction history persisted forever (survives pruning), unsolicited notifications push new transactions to BlueWallet in real time - Built-in Lightning node powered by LDK (send, receive, channels, peer browser, seed backup/restore with automatic fund recovery)
- LNDHub API on localhost:3000 for external wallet connectivity (BlueWallet)
- P2P port exposed on localhost:8333 for Neutrino-compatible wallets
- Wallet birthday recovery: automatic fund discovery on seed restore via UTXO scan with live progress, instant restore for wallets with saved birthday
- Home node watchtower with automatic channel protection via LDK-to-LND bridge over Tor
- Full Tor privacy with one toggle: bitcoind peers, Lightning peers (with stream isolation), HTTP API calls, and watchtower all route through embedded Arti SOCKS proxy. 🧅 indicators on all screens when active
- BOLT12 support: send to offers, create reusable offers, variable-amount offers
- QR codes: generate on receive, scan with camera on send (CameraX + ZXing, no Google Play)
- Sovereign price discovery using UTXOracle (BTC/USD from on-chain data, no exchange APIs)
- Mempool viewer with fee estimates, projected blocks, and transaction search
- Wallet tracking with history recovery from mempool.space, transaction hex caching, and gap limit discovery
- BlueWallet integration tested: balance display, transaction history, pull-to-refresh all working on pruned node
- Snapshot validation checks block hash before loading, auto-redownloads if wrong
- Non-blocking snapshot load with progress tracking
- Network-aware sync with automatic power mode detection
- VPN-aware networking: Detects actual connection type behind VPN (cellular vs WiFi)
- Data budgets for WiFi and cellular
- Power modes: Max Data, Low Data, Away Mode with burst sync for mobile efficiency
- Auto data mode: detects WiFi/cellular and charging state, adjusts automatically
- Auto-start on boot
- Secure node pairing with restricted SFTP account (no access to your bitcoin data)
- Setup checklist with auto-detection of completed steps
- Live dashboard showing block height, sync progress, peers, mempool, disk usage
- Partial mempool (50 MB) with persistence across restarts (survives nightly reboot)
- Embedded Tor (Arti 0.39.0) with one-tap toggle: routes bitcoind P2P, Lightning peers (stream-isolated), HTTP API calls, and watchtower through SOCKS proxy. No Orbot dependency. Persists across force-kill. 🧅 indicators on notification, badges, peer browser, and connected peers dialog
The app connects to your home node via SSH, briefly stops bitcoind, and copies:
chainstate/(the UTXO set, ~11 GB)blocks/index/(block metadata, ~2 GB)blocks/xor.dat(block file obfuscation key)- Tip block/rev files (latest block data)
Total transfer ~13 GB over LAN (~5 min). Node operational in under an hour including setup.
- Generates a UTXO snapshot using
dumptxoutset rollback - Downloads via SFTP over LAN (~5 min for 9 GB)
- Loads via
loadtxoutset
The app tries saved pocketnode SFTP credentials first. If a snapshot already exists on the server, no admin credentials are needed.
Download from https://utxo.download/utxo-910000.dat (9 GB). Same loadtxoutset flow, just a different download source. The snapshot is cryptographically verified against the block hash compiled into Bitcoin Core before loading.
Note: Lightning (LDK) works immediately on this path. Block filters (~13 GB) are optional and only needed for Neutrino wallet connections (Zeus).
┌───────────────────────────────────────────────────────┐
│ Android App (Kotlin) │
│ │
│ ┌──────────┐ ┌───────────┐ ┌───────────┐ │
│ │Chainstate│ │ Network │ │ Sync │ │
│ │ Manager │ │ Monitor │ │ Controller│ │
│ └────┬─────┘ └─────┬─────┘ └─────┬─────┘ │
│ │ │ │ │
│ ┌────┴──────────────┴──────────────┴──────────────┐ │
│ │ bitcoind (ARM64), user selects: │ │
│ │ Core 29.3 (default) | Core 30 │ │
│ │ Foreground service, local RPC │ │
│ │ Tor mode: -proxy=127.0.0.1:9050 -onlynet=onion│ │
│ └────────────────┬────────────────────────────────┘ │
│ │ RPC │
│ ┌────────────┼────────────┐ │
│ │ │ │ │
│ ┌───┴──────┐ ┌───┴──────┐ ┌──┴───────────┐ │
│ │ Electrum │ │ ldk-node │ │ UTXOracle │ │
│ │ :50001 │ │(in-proc) │ │ price feed │ │
│ └───┬──────┘ └───┬──────┘ └──────────────┘ │
│ │ │ │
│ │ ┌──────┴───────┐ ┌────────────────────┐ │
│ │ │ LNDHub API │ │ Embedded Arti 🧅 │ │
│ │ │ :3000 │ │ SOCKS :9050 │ │
│ │ └──────┬───────┘ │ All traffic when │ │
│ │ │ │ Tor enabled │ │
│ │ │ └────────────────────┘ │
└──────┼────────────┼───────────────────────────────────┘
│ │
BlueWallet BlueWallet
(on-chain) (Lightning)
When you pair with your home node, the app creates a restricted pocketnode user:
- SFTP-only. Cannot run commands, no shell access
- Chroot jailed. Can only see
/home/pocketnode/, nothing else - Zero data access. Cannot read your bitcoin data directory, wallet, configs, or logs
- Root-owned copy scripts bridge the gap, copying only snapshot files to the SFTP location
Admin SSH credentials are never saved (username is saved for pre-fill convenience). Always prompted, used once, discarded.
You can view the pocketnode credentials and fully remove access from the app at any time.
- Snapshots are verified against block hashes compiled into the Bitcoin Core binary
- The app also validates the snapshot file header before attempting to load
- A tampered or wrong-height snapshot is rejected before any data is used
- Background IBD independently validates everything from genesis (AssumeUTXO path)
network_security_config.xmlallows cleartext HTTP to127.0.0.1(local RPC) and.oniondomains (Tor provides encryption)- bitcoind runs as
libbitcoind.soinjniLibs/for GrapheneOS W^X compliance - No internet-facing ports. RPC and Electrum server are both localhost only
- When Tor enabled, all outbound connections route through embedded Arti SOCKS proxy with stream isolation
Built-in Lightning wallet powered by LDK (ldk-node 0.7.0). Runs in-process, connects to your local bitcoind via RPC. No external apps needed.
- Start your Bitcoin node and wait for sync
- Open the Lightning wallet from the dashboard
- Fund your on-chain wallet (receive bitcoin to the displayed address)
- Browse peers (Most Connected, Largest, or search) and open a channel
- Send and receive Lightning payments
bitcoind ← RPC → ldk-node (in-process)
│
┌───────┴────────┐
│ │
Built-in UI LNDHub API (:3000)
(send/receive/ │
channels) External wallets
(BlueWallet)
Why LDK? Earlier versions used Zeus with embedded LND, which required BIP 157/158 block filters and had a NODE_NETWORK service bit limitation with pruned nodes. LDK connects via RPC directly, so pruned nodes work natively. No service bit checks, no cross-app restrictions, no duplicate sync engine.
The app runs an LNDHub-compatible API server on localhost:3000. Connect BlueWallet in LNDHub mode to use your Lightning node from another app on the same phone.
Built-in peer browser using mempool.space API. Browse nodes by:
- Most Connected: highest channel count
- Largest: biggest total capacity
- Search: find nodes by name or pubkey
- OS: Android 7+ (tested on GrapheneOS, EMUI, Samsung OneUI)
- Hardware: Any ARM64 device (tested on Pixel, Samsung, Huawei)
- Default: Bitcoin Core 29.3 (standard relay rules and OP_RETURN limits)
- Also bundled: Bitcoin Core 30 (user selects from dashboard; permissive OP_RETURN)
- AssumeUTXO heights: 840k (upstream) + 880k, 910k (backported from Core 30)
- macOS or Linux build machine
- Android SDK + NDK r27
- JDK 17
- Bitcoin Core 29.3 source (with chainparams patches)
See docs/cross-compile-android.md
See docs/ldk-node-android-build.md. Covers the Rust cross-compile, UniFFI binding generation, and AAR packaging.
export ANDROID_HOME=/path/to/android-sdk
export JAVA_HOME=/path/to/jdk-17
./gradlew assembleDebugadb install -r app/build/outputs/apk/debug/app-debug.apkapp/src/main/java/com/pocketnode/
├── service/
│ ├── BitcoindService.kt # Foreground service managing bitcoind
│ ├── ElectrumService.kt # Electrum server lifecycle management
│ ├── SyncController.kt # Network-aware sync pause/resume
│ ├── BatteryMonitor.kt # Battery level monitoring
│ ├── BootReceiver.kt # Auto-start on device boot
│ └── WatchtowerManager.kt # Watchtower service lifecycle
├── lightning/
│ ├── LightningService.kt # ldk-node singleton wrapper (start/stop, state)
│ ├── PaymentManager.kt # Send payments, routing fee budget, retry logic
│ ├── PaymentTracker.kt # Live route tracking, hop display, attempt history
│ ├── ChannelManager.kt # Channel open/close operations
│ ├── OnchainWallet.kt # On-chain send/sweep, address management
│ ├── RecoveryManager.kt # Seed backup/restore, recovery scan, chain state
│ ├── NodeDirectory.kt # Peer directory and node info
│ ├── LndHubServer.kt # LNDHub-compatible API server (:3000)
│ ├── WatchtowerBridge.kt # LDK-to-LND watchtower push via Brontide
│ ├── WatchtowerNative.kt # JNA bindings to native Rust watchtower client
│ ├── WalletRecoveryService.kt # Background wallet recovery
│ └── Bip39.kt # Pure Kotlin BIP39 (mnemonic <-> entropy)
├── electrum/
│ ├── ElectrumServer.kt # Electrum protocol TCP server
│ ├── ElectrumMethods.kt # RPC method handlers (pruned-node tx fallbacks)
│ ├── AddressIndex.kt # Descriptor wallet, UTXO cache, tx hex cache
│ ├── HistoryRecovery.kt # mempool.space history recovery with gap limit
│ └── SubscriptionManager.kt # Address/header subscription notifications
├── mempool/
│ ├── MempoolService.kt # Local mempool monitoring
│ ├── GbtGenerator.kt # Block template generation
│ ├── GbtResult.kt # Block template result types
│ ├── MempoolEntry.kt # Mempool transaction entry
│ ├── ProjectedBlockInfo.kt # Projected block fee info
│ ├── ThreadAcceleration.kt # CPFP/RBF acceleration detection
│ └── ThreadTransaction.kt # Transaction thread grouping
├── power/
│ └── PowerModeManager.kt # Max/Low/Away modes, burst sync, network holds
├── network/
│ └── NetworkMonitor.kt # WiFi/cellular/VPN detection + data tracking
├── snapshot/
│ ├── ChainstateManager.kt # AssumeUTXO snapshot flow
│ ├── SnapshotManager.kt # Snapshot state management
│ ├── BlockFilterManager.kt # Lightning block filter copy/remove
│ ├── NodeSetupManager.kt # SSH setup + teardown
│ ├── NodeConnectionManager.kt # Remote node connection management
│ └── SnapshotDownloader.kt # SFTP download with progress
├── share/
│ ├── ShareServer.kt # Phone-to-phone chainstate sharing (server)
│ └── ShareClient.kt # Phone-to-phone chainstate sharing (client)
├── ssh/
│ └── SshUtils.kt # Shared SSH/SFTP utilities
├── rpc/
│ └── BitcoinRpcClient.kt # Local bitcoind JSON-RPC
├── notification/
│ └── TransactionNotificationManager.kt # Transaction push notifications
├── tor/
│ ├── TorManager.kt # Arti lifecycle, SOCKS proxy, persistent preference
│ └── TorAwareHttp.kt # Routes OkHttp through SOCKS, .onion URL mapping
├── storage/
│ └── WatchListManager.kt # Address watch list persistence
├── ui/
│ ├── PocketNodeApp.kt # Navigation + top-level routing
│ ├── NodeStatusScreen.kt # Main dashboard
│ ├── LightningScreen.kt # Lightning management (channels, watchtower)
│ ├── SetupChecklistScreen.kt # Config mode setup wizard
│ ├── SnapshotSourceScreen.kt # Source picker
│ ├── SnapshotProgressScreen.kt # Snapshot load progress
│ ├── ChainstateCopyScreen.kt # Chainstate copy progress (4-step flow)
│ ├── ConnectWalletScreen.kt # RPC / Electrum / LNDHub connection guide
│ ├── BlockFilterUpgradeScreen.kt # Lightning block filter management
│ ├── WatchtowerScreen.kt # Home node watchtower setup
│ ├── DataUsageScreen.kt # Data usage breakdown
│ ├── NetworkSettingsScreen.kt # Cellular/WiFi budgets
│ ├── NodeAccessScreen.kt # View/remove node access
│ ├── NodeConnectionScreen.kt # Remote node connection setup
│ ├── NodeSetupScreen.kt # Initial node setup
│ ├── NearbyNodeScreen.kt # Discover nearby nodes
│ ├── ShareScreen.kt # Phone-to-phone sharing UI
│ ├── InternetDownloadScreen.kt # HTTPS snapshot download
│ ├── PowerModeSelector.kt # Three-segment power mode toggle + burst banner
│ ├── lightning/
│ │ ├── LightningPayScreen.kt # Lightning Pay home (send/receive)
│ │ ├── SendPaymentScreen.kt # Pay BOLT11/BOLT12, route tree display
│ │ ├── SendOnchainScreen.kt # On-chain send/sweep
│ │ ├── ReceivePaymentScreen.kt # Generate invoices
│ │ ├── PaymentHistoryScreen.kt # Payment list
│ │ ├── OpenChannelScreen.kt # Open channel to peer
│ │ ├── PeerBrowserScreen.kt # Browse/search Lightning peers
│ │ ├── SeedBackupScreen.kt # BIP39 seed view and restore
│ │ ├── QrCode.kt # QR code generation (ZXing)
│ │ └── QrScannerScreen.kt # Camera QR scanner (CameraX + ZXing)
│ ├── mempool/
│ │ ├── MempoolScreen.kt # Mempool viewer
│ │ ├── MempoolViewModel.kt # Mempool state management
│ │ ├── FeeEstimatePanel.kt # Fee rate visualization
│ │ ├── TransactionSearchScreen.kt # Transaction lookup
│ │ └── TransactionSearchViewModel.kt # Search state management
│ └── components/
│ ├── NetworkStatusBar.kt # Sync status banner
│ ├── FairTradeCard.kt # Fair trade info display
│ ├── OracleCard.kt # UTXOracle price display
│ └── AdminCredentialsDialog.kt # SSH creds prompt
├── oracle/
│ └── UTXOracle.kt # Sovereign price discovery from on-chain data
└── util/
├── ConfigGenerator.kt # Mobile-optimized bitcoin.conf
├── BinaryExtractor.kt # Version selection, 2 bundled bitcoind binaries
├── UpdateChecker.kt # GitHub release update checker
└── SetupChecker.kt # Auto-detect completed setup steps
- Build Guide
- Cross-Compile Guide
- ldk-node Android Build
- Tor Integration
- Chainparams Patch
- Direct Chainstate Copy
- Snapshot Testing
- Umbrel Integration
- Block Filter Design
- Block Index Consistency
- Version Selection Design
- BIP 110 Research
- LDK Research
- Watchtower Mesh Design
- LDK-to-LND Watchtower Bridge
- Desktop Port Design
- Power Modes Design
- Pruned Node Risk Analysis
- LDK Upstream Contribution
- LDK Anchor Downgrade Bug
- Ark Integration Plan
- Built-in Tor Design
- iOS Port Feasibility
- Phone-to-Phone Sharing
- LDK upstream contribution: improving the watchtower API in rust-lightning ChannelMonitor (#813). Retention-list foundation pushed to the rust-lightning Forgejo, awaiting review.
- Upstream PRs: corepc #533 (bitreq SOCKS5 — merged), rust-lightning #4453 (watchtower justice-tx API — active on Forgejo), ldk-node #822 (wallet birthday — superseded by upstream #884)
- Desktop port: Same app on Linux, macOS, Windows via Compose Multiplatform. See design doc
- iOS port: Burst sync + watchtower + in-process LDK make iOS viable. See feasibility analysis
| Device | SoC | OS | Result |
|---|---|---|---|
| Pixel 9 | Tensor G4 | GrapheneOS | ✅ Full stack: chainstate copy, LDK Lightning, all features verified |
| Samsung Galaxy Z Fold | Snapdragon | Android | ✅ Dual-pane foldable layout working, IBD syncing |
| Huawei Mate 20 Lite | Kirin 710 | EMUI | ✅ Clean install, IBD syncing from genesis |
| Pixel 9 | Tensor G4 | GrapheneOS | ✅ Lightning send/receive verified, 7-day uptime confirmed by independent tester |
- 16KB page alignment warning on GrapheneOS (cosmetic only)
getblockchaininforeports background validation progress, not snapshot chain tip (AssumeUTXO path only)
MIT










