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 (167M UTXOs, 4 peers, instant)
- AssumeUTXO alternative: full node in ~3 hours via cryptographically verified UTXO snapshot
- 3 Bitcoin implementations: Core 28.1, Core 30, Knots 29.3 (with BIP 110 toggle). Switch with one tap, same chainstate
- Phone stays cool, runs overnight without issues
- ~26 GB total disk with Lightning (11 GB chainstate + 2 GB pruned blocks + 13 GB block filters), ~13 GB without
- Pure Kotlin Electrum server for BlueWallet connectivity to your own node
Running on a Pixel 7 Pro with GrapheneOS
First run
![]() |
|---|
Node + On-chain wallet
| Setup checklist | Electrum server | BlueWallet connected | BlueWallet wallet |
|---|---|---|---|
![]() |
![]() |
![]() |
![]() |
Lightning + Version Selection
| Lightning enabled | Connect Wallet | Version picker |
|---|---|---|
![]() |
![]() |
![]() |
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.
Your node, your rules. Choose which Bitcoin implementation runs on your phone:
| Implementation | Size | Policy |
|---|---|---|
| Bitcoin Core 28.1 | 13 MB | Neutral: standard relay rules |
| Bitcoin Core 30 | 8.6 MB | Permissive: larger OP_RETURN data allowed |
| Bitcoin Knots 29.3 | 9 MB | Restrictive: filters non-standard transactions. Optional BIP 110 toggle |
All three share the same chainstate format. Switch without re-syncing. Tap "Change" on the dashboard, confirm, and the node restarts with the new binary.
BIP 110 (bip110.dev) temporarily limits arbitrary data embedding at the consensus level. When running Knots, a toggle enables version bit 4 signaling and peer preference for reduced data carriers. Built from Dathon Ohm's reference implementation with a 55% activation threshold.
See Version Selection Design and BIP 110 Research for details.
- 3 Bitcoin implementations with one-tap switching: Core 28.1, Core 30, Knots 29.3 (BIP 110 toggle)
- Two bootstrap paths: direct chainstate copy (~20 min) or AssumeUTXO (~3 hours)
- Pure Kotlin Electrum server so BlueWallet can query your own node (no native dependencies)
- Lightning support via block filter copy from your home node (Zeus with embedded LND)
- Home node watchtower detection and setup for Lightning channel protection
- 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 setup guide for BlueWallet connection
- Snapshot validation checks block hash before loading, auto-redownloads if wrong
- Non-blocking snapshot load with progress tracking
- Network-aware sync that auto-pauses on cellular and resumes on WiFi
- VPN support: WireGuard/VPN connections treated as WiFi
- Data budgets for WiFi and cellular
- Battery saver pauses sync when unplugged below 50%
- 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)
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 ~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 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: This path provides an on-chain node only. Lightning support requires block filters (~13 GB) which can be added later by copying from a home node via SSH.
┌─────────────────────────────────────────────┐
│ Android App (Kotlin) │
│ │
│ ┌──────────┐ ┌───────────┐ ┌───────────┐ │
│ │ Chainstate│ │ Network │ │ Sync │ │
│ │ Manager │ │ Monitor │ │ Controller│ │
│ └────┬─────┘ └─────┬─────┘ └─────┬─────┘ │
│ │ │ │ │
│ ┌────┴─────────────┴─────────────┴──────┐ │
│ │ bitcoind (ARM64), user selects: │ │
│ │ Core 28.1 | Core 30 | Knots (+BIP110)│ │
│ │ Foreground service, local RPC │ │
│ └──────────────┬────────────────────────┘ │
│ │ │
│ ┌──────────────┴──────────────────┐ │
│ │ Electrum server (Kotlin) │ │
│ │ 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, 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 only to127.0.0.1(local RPC)- bitcoind runs as
libbitcoind.soinjniLibs/for GrapheneOS W^X compliance - No internet-facing ports. RPC and Electrum server are both localhost only
The app copies BIP 157/158 block filter indexes (~13 GB) from your home node, enabling Lightning wallet support on the phone.
- Tap "Add Lightning Support" on the dashboard
- Enter your home node SSH credentials
- The app detects block filters on your node, stops it briefly, archives filters alongside chainstate, downloads everything, and restarts your home node
- Your phone's bitcoind restarts with
blockfilterindex=1andpeerblockfilters=1 - Install a Lightning wallet (Zeus with embedded LND) and it syncs via Neutrino
Zeus's embedded LND uses Neutrino for chain sync. Neutrino requires peers to advertise NODE_NETWORK (service bit 0), but our pruned bitcoind only advertises NODE_NETWORK_LIMITED (bit 10). Neutrino silently rejects the local node during the P2P handshake, so Zeus syncs via internet peers instead. This works, and Neutrino is privacy-preserving (it doesn't reveal which addresses you're watching), but it means Zeus doesn't use the local bitcoind for P2P. Your local node still handles all on-chain validation, Electrum wallet tracking, mempool, and fee estimation.
The planned LDK (Lightning Dev Kit) migration eliminates this limitation entirely. LDK connects to bitcoind via RPC (not P2P Neutrino), so pruned nodes work natively. No service bit checks, no cross-app restrictions, no duplicate sync engine. See LDK Research.
- OS: Android 7+ (tested on GrapheneOS, EMUI, Samsung OneUI)
- Hardware: Any ARM64 device (tested on Pixel, Samsung, Huawei)
- Default: Bitcoin Core v28.1 (non-controversial baseline)
- Also bundled: Core 30, Knots 29.3 with BIP 110 toggle (user selects from dashboard)
- 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 # Electrum server lifecycle management
│ └── 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)
│ ├── BlockFilterManager.kt # Lightning block filter copy/remove
│ ├── NodeSetupManager.kt # SSH setup + teardown
│ └── SnapshotDownloader.kt # SFTP download with progress
├── ssh/
│ └── SshUtils.kt # Shared SSH/SFTP utilities
├── 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 / Zeus wallet connection guide
│ ├── BlockFilterUpgradeScreen.kt # Lightning block filter management
│ ├── 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
├── oracle/
│ └── UTXOracle.kt # Sovereign price discovery from on-chain data
└── util/
├── ConfigGenerator.kt # Mobile-optimized bitcoin.conf
├── BinaryExtractor.kt # Version selection, 4 bundled bitcoind binaries
└── SetupChecker.kt # Auto-detect completed setup steps
- Build Guide
- 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
- Desktop Port Design
- Desktop port: Same app on Linux, macOS, Windows via Compose Multiplatform. Same UI, same chainstate copy, same version selection. See design doc.
- Home node watchtower: Your home node watches your phone's Lightning channels when you're away. Enabled automatically during setup. See design doc.
- Policy settings: Expose Knots datacarrier flags as toggleable settings.
- LDK migration: Replace Zeus embedded LND with LDK for native in-process Lightning. Solves the NODE_NETWORK service bit limitation (pruned nodes can't serve Neutrino) by using bitcoind RPC directly.
| Device | SoC | OS | Result |
|---|---|---|---|
| Pixel 7 Pro | Tensor G2 | GrapheneOS | ✅ Full stack: chainstate copy, Lightning, BIP 110, 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 |
- 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







