Bitcoin node in your pocket in under 20 minutes.
Turn any Android phone into a fully-validating Bitcoin full node. No server dependency, no ongoing tethering — your phone becomes a sovereign Bitcoin node.
- Direct chainstate copy — full node at chain tip in ~20 minutes (height 936,822, 4 peers, instant)
- 167 million UTXOs loaded via AssumeUTXO on a Pixel 7 Pro
- Syncing from block 910,000 to chain tip
- Phone stays cool, runs overnight without issues
- ~13 GB total disk usage (11 GB chainstate + 2 GB pruned blocks)
- BWT Electrum server — BlueWallet connects to local node for private transaction queries
Running on a Pixel 7 Pro with GrapheneOS
| Syncing from node | Electrum server | BlueWallet connected | Wallet ready |
|---|---|---|---|
![]() |
![]() |
![]() |
![]() |
Two bootstrap paths — choose speed or trustlessness:
- App connects to your home node (Umbrel, Start9, any Bitcoin node) via SSH
- Briefly stops bitcoind, copies chainstate + block index + xor.dat + tip blocks
- Creates stub files for historical blocks, starts bitcoind with
checklevel=0 - Instant full node at chain tip — no background validation, no catch-up
- Download a UTXO snapshot (~9 GB) from your home node over LAN or the internet
- App loads it via
loadtxoutset— cryptographically verified by Bitcoin Core - Phone syncs forward from the snapshot height (~25 min to load, ~2 hours to reach tip)
- Background validation confirms everything independently from genesis
See Direct Chainstate Copy for a detailed comparison.
- Two bootstrap paths — direct chainstate copy (~20 min) or AssumeUTXO (~3 hours)
- BWT Electrum server — run a local Electrum server so BlueWallet can query your own node
- Wallet integration — ConnectWalletScreen guides BlueWallet connection setup
- AssumeUTXO fast sync — full node in under 3 hours, not days
- Snapshot validation — verifies block hash before loading, auto-redownloads if invalid
- Non-blocking snapshot load — progress tracking during the ~25 min load process
- Network-aware sync — auto-pauses on cellular, resumes on WiFi
- VPN support — WireGuard/VPN connections treated as WiFi (connected)
- Data budgets — separate WiFi and cellular monthly limits
- Secure node pairing — restricted SFTP account with zero access to your bitcoin data
- Setup checklist — Config mode with auto-detection of completed steps
- Dashboard — block height, sync progress with ETA, peers, mempool, disk usage
- Auto-start — resumes on app launch if node was previously running
- Privacy — partial mempool (50 MB) for fee estimation and cover traffic
The app connects to your home node via SSH, briefly stops bitcoind, and copies:
chainstate/— the UTXO set (~12 GB)blocks/index/— block metadata (~2 GB)blocks/xor.dat— block file obfuscation key- Tip block/rev files — latest block data
Total transfer ~15 GB over LAN (~5 min). Node operational in ~20 minutes 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 needed.
Download from https://utxo.download/utxo-910000.dat (9 GB). Same loadtxoutset flow, just a different download source.
┌─────────────────────────────────────────────┐
│ Android App (Kotlin) │
│ │
│ ┌──────────┐ ┌───────────┐ ┌───────────┐ │
│ │ Chainstate│ │ Network │ │ Sync │ │
│ │ Manager │ │ Monitor │ │ Controller│ │
│ └────┬─────┘ └─────┬─────┘ └─────┬─────┘ │
│ │ │ │ │
│ ┌────┴─────────────┴─────────────┴──────┐ │
│ │ bitcoind v28.1 (ARM64) │ │
│ │ Foreground service, local RPC │ │
│ └──────────────┬────────────────────────┘ │
│ │ │
│ ┌──────────────┴──────────────────┐ │
│ │ BWT (Electrum server) │ │
│ │ Local Electrum protocol │ │
│ └──────────────┬──────────────────┘ │
│ │ │
└─────────────────┼───────────────────────────┘
│
┌────────────┼────────────┐
│ │ │
Bitcoin P2P BlueWallet Electrum
Network (local) clients
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 — they copy 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 only to127.0.0.1(local RPC)- bitcoind runs as
libbitcoind.soinjniLibs/for GrapheneOS W^X compliance - No internet-facing ports — RPC is localhost only, BWT Electrum is localhost only
- OS: GrapheneOS (or any Android 10+)
- Hardware: Google Pixel devices (ARM64)
- Bitcoin Core: v28.1 (patched with additional AssumeUTXO heights)
- Why v28.1: Non-controversial, universal acceptance (avoids Core 30 OP_RETURN policy changes)
- AssumeUTXO heights: 840k (upstream) + 880k, 910k (backported from Core 30)
- macOS or Linux build machine
- Android SDK + NDK r27
- JDK 17
- Bitcoin Core v28.1 source (with chainparams patch)
See docs/build-android-arm64.md
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
│ ├── BwtService.kt # BWT Electrum server service
│ └── SyncController.kt # Network-aware sync pause/resume
├── network/
│ └── NetworkMonitor.kt # WiFi/cellular/VPN detection + data tracking
├── snapshot/
│ ├── ChainstateManager.kt # AssumeUTXO snapshot flow (generate/download/load)
│ ├── NodeSetupManager.kt # SSH setup + teardown
│ └── SnapshotDownloader.kt # SFTP download with progress
├── rpc/
│ └── BitcoinRpcClient.kt # Local bitcoind JSON-RPC (configurable timeouts)
├── ui/
│ ├── PocketNodeApp.kt # Navigation + top-level routing
│ ├── NodeStatusScreen.kt # Main dashboard
│ ├── SetupChecklistScreen.kt # Config mode setup wizard
│ ├── SnapshotSourceScreen.kt # Source picker
│ ├── ChainstateCopyScreen.kt # Snapshot load progress (4-step flow)
│ ├── ConnectWalletScreen.kt # BlueWallet / Electrum wallet connection guide
│ ├── DataUsageScreen.kt # Data usage breakdown
│ ├── NetworkSettingsScreen.kt # Cellular/WiFi budgets
│ ├── NodeAccessScreen.kt # View/remove node access
│ ├── NodeConnectionScreen.kt # Remote node connection setup
│ └── components/
│ ├── NetworkStatusBar.kt # Sync status banner
│ └── AdminCredentialsDialog.kt # SSH creds prompt
└── util/
├── ConfigGenerator.kt # Mobile-optimized bitcoin.conf
├── BinaryExtractor.kt # Extract bitcoind from nativeLibraryDir
└── SetupChecker.kt # Auto-detect completed setup steps
| Device | SoC | Result |
|---|---|---|
| Pixel 7 Pro | Tensor G2 | ✅ Direct chainstate copy to chain tip, 167M UTXOs loaded via AssumeUTXO, phone stays cool |
- 16KB page alignment warning on GrapheneOS — cosmetic only
getblockchaininforeports background validation progress, not snapshot chain tip (AssumeUTXO path only)- ARM64 Android emulator cannot run on x86 Mac — all testing requires real device
- Direct chainstate copy: pruning ~5000 stub files takes ~15 minutes on first startup (optimizable)
MIT



