From a Pruned Bitcoin Node to a Paid Service
A builder's log: what we set up on our server, and an honest plan for turning it into revenue.
1. What we built
Bitcoin Core 31.1 running on a server, Ubuntu under WSL2, installed from the official snap. We first started it in full mode, then switched to pruned mode with a 10 GB target, so it validates the whole chain itself but keeps only recent blocks instead of about 700 GB.
# install the official build
sudo snap install bitcoin-core
# first start (full mode), then stop it
bitcoin-core.daemon -daemon
bitcoin-core.cli stop
# restart in pruned mode, ~10 GB of blocks kept
bitcoin-core.daemon -daemon -prune=10000
# verify
bitcoin-core.cli getblockchaininfo
What the check showed on the first run:
"chain": "main",
"blocks": 136479,
"headers": 971251,
"verificationprogress": 0.00073,
"initialblockdownload": true,
"size_on_disk": 467629319,
"pruned": true,
"automatic_pruning": true,
"prune_target_size": 10485760000
In plain terms, it's on the real Bitcoin network, pruning is on, and it's early in the first sync. It's done when initialblockdownload turns false. To make pruning permanent:
echo "prune=10000" >> ~/snap/bitcoin-core/common/.bitcoin/bitcoin.conf
2b. Live component: pay-per-call node data (x402)
A small Python service, btc_x402.py, runs next to the node. It calls the local node through bitcoin-core.cli and sells two read-only RPC results over HTTP, priced per call with x402 and settled through the genesis402.com facilitator.
| Endpoint | Bitcoin Core RPC | Price |
|---|---|---|
GET /blockcount | getblockcount | 0.001 USDC per call |
GET /feeestimate | estimatesmartfee 6 | 0.001 USDC per call |
GET /health | getblockcount | free |
Run it on the node machine:
export FACILITATOR_URL="https://<your genesis402 facilitator>"
export PAY_TO="0xYourReceivingAddress"
python3 btc_x402.py # listens on 127.0.0.1:8402
Payment flow:
- The client calls
/blockcountand gets HTTP 402 with the price, asset, network and receiving address. - The client signs a payment for that exact amount and retries with an
X-PAYMENTheader. - The service sends it to the facilitator's
/verify. If it's valid, it runs the RPC call on the local Bitcoin node. - It settles through
/settleand returns the result with anX-PAYMENT-RESPONSEreceipt.
Try it:
curl -i http://127.0.0.1:8402/health
curl -i http://127.0.0.1:8402/blockcount # returns 402 + payment requirements
The node's RPC never leaves the machine: the service binds to localhost and only runs two whitelisted read-only calls. Put it behind a tunnel or reverse proxy with TLS before exposing it publicly. Results stay meaningful once the node finishes its initial block download.
2c. Full architecture: node data, two payment rails, one registry
The live system has four layers. The Bitcoin node supplies the chain data. Two rails take payment: x402 for one-off calls, and Lightning for recurring use. The UnyKorn chain (chain.unykorn.ai) holds the public offer and the payment-verification records.
offer manifest · prices · hashed payment receipts
X-PAYMENT · 0.001 USDC · facilitator verify/settle
API key · BOLT11 top-ups · monthly plans in sats
btc_x402.py on 127.0.0.1:8402local RPC via bitcoin-core.cli · read-only whitelist: getblockcount, estimatesmartfee
Code
| File | Role |
|---|---|
btc_x402.py | The HTTP server. Paid routes accept x402 or an API key. Loads btc402.env. |
lightning.py | API keys, BOLT11 invoices (phoenixd, LND REST or LNbits), SQLite sat ledger, plans. |
registry.py | Offer manifest and payment-verification records, signed with HMAC. |
btc_wallet.sh | The node's receiving wallet, btc402: setup, address, balance, watch, and withdraw (which asks you to type YES first). Its bech32 address is BTC_PAY_TO, the on-chain destination for the sats rail. The HTTP server never calls the wallet. x402 PAY_TO stays a Base USDC (EVM) address. |
btc402.env.example | Every setting, with no secrets in it. |
Run it
cp btc402.env.example btc402.env && chmod 600 btc402.env # fill in values
python3 btc_x402.py
curl http://127.0.0.1:8402/health
curl http://127.0.0.1:8402/offer
Flow 1: x402, pay per call
GET /blockcountreturns 402 with the price (1000 USDC atomic units) and the payTo address.- The client retries with a signed
X-PAYMENT. The facilitator runs/verify, the node answers, then the facilitator runs/settle. - The response carries an
X-PAYMENT-RESPONSEreceipt, and a hash of the settlement goes to the registry.
Flow 2: Lightning, prepaid sats and subscriptions
curl -X POST localhost:8402/keys # -> {"key":"btk_..."}
curl -X POST localhost:8402/topup -d '{"key":"btk_...","sats":1000}' # -> BOLT11 invoice
curl localhost:8402/topup/<payment_hash> # credits sats once paid
curl -X POST localhost:8402/subscribe -d '{"key":"btk_...","plan":"basic"}' # 5,000 sats / 30 days
curl -H "Authorization: Bearer btk_..." localhost:8402/blockcount # 1 sat per call, or plan quota
Each call uses up the plan quota first, then the sat balance. A settled invoice is credited exactly once. Every credit and every debit is written to the SQLite ledger.
Registry role
The registry publishes the offer manifest at startup, covering endpoints, prices in USDC atomic units and in sats, the two rails, and node info. After each payment it adds a verification record: the SHA-256 of the x402 settlement or of the Lightning payment hash, the amount and the resource. The records hold no keys, preimages or payloads. Status: the registry API for the UnyKorn chain is live at https://registry.unykorn.org. It accepts POST /records with HMAC-verified envelopes, and you can look up GET /records/:id and GET /offers. Records are also kept in a local append-only registry.jsonl, and failed writes are queued and retried. These are registry-API records, not on-chain writes yet: chain.unykorn.ai itself stays read-only, and anchoring on the UnyKorn chain is pending. Code: registry_worker.js. Every paid response carries an X-UnyKorn-Record header with the offer ID and the record ID, so each sale can be traced to its registry entry. That makes the registry the value-capture layer.
Security
- Bitcoin RPC stays on localhost. The server runs only two whitelisted read-only RPCs and never makes wallet calls.
- phoenixd listens on 127.0.0.1 only, and the server uses its limited-access password, which can create invoices but cannot pay. For LND, use
invoice.macaroon, never admin. For LNbits, use the invoice/read key. The code only creates and looks up invoices. It never pays. - API keys are stored as SHA-256 hashes, and the Authorization header is never logged. Keep
btc402.envat chmod 600. - The server binds to 127.0.0.1. Put TLS and a rate limit in front of it before exposing it publicly.
Compliance: this is a data service. Sats and USDC pay for API calls, and prepaid balances are service credit, not deposits. The service never holds, sends or exchanges customer funds. Get legal advice before offering refunds, withdrawals or anything custodial. This is not financial advice.
2. What a pruned node can and can't do
| Good at | Not good at |
|---|---|
| Current chain tip, fees, mempool, new blocks as they arrive | Looking up any old transaction (needs txindex, which pruning disables) |
| Validating and broadcasting transactions without trusting anyone | A full historical block explorer |
| Watching chosen addresses from now on (with a wallet or descriptors) | Address history from before it started watching |
That matches how bitcoin.org describes running a full node: your node verifies every rule itself. So the realistic products are about live data and trustless verification, not full history. If a product needs full history later, add a 1 TB disk and run an unpruned node with an indexer like Electrs or Fulcrum.
3. Running a Bitcoin node as infrastructure
Your node is a fully validating peer on Bitcoin mainnet. The value is in serving trustworthy chain data and transaction relay to people who do not run their own node, behind authenticated RPC access.
A. Paid RPC data service
Read-only endpoints built on Bitcoin Core RPC:estimatesmartfee, getmempoolinfo, getblockchaininfo, getbestblockhash, and sendrawtransaction for broadcasting. Charged per request in sats over the Lightning Network (for example with L402), or with a prepaid API key.B. Alerts and monitoring
"Tell me when this address gets paid," "tell me when fees drop under X," or "tell me about every new block." Uses-blocknotify, -walletnotify or ZMQ notifications from Bitcoin Core, sold for a small amount of sats per alert or a monthly plan. A pruned node handles this well, because it only needs data from now on.C. Portfolio and payment checker
Lets a merchant or user verify incoming BTC payments against their own node instead of a third party. Charged as a subscription in bitcoin.D. Node or RPC access for a small group
Gives builders authenticated RPC access for testing and broadcasting. Keep it to a whitelist of methods, and never expose wallet calls.4. Step-by-step plan
- Keep it synced and healthy: leave the daemon running, make pruning permanent, and check
getblockchaininfoevery day. - Never expose raw RPC. Bind RPC to localhost only and use a strong
rpcauthpassword. - Put a small wrapper in front: a little API service that calls
bitcoin-clion approved methods only, with rate limits and caching. - Add payment: charge in bitcoin. Pay-per-request over Lightning (L402 or invoices), or prepaid sats tied to an API key.
- Publish it: docs, prices in sats, and example calls for each RPC method you offer.
- Measure: track paid calls per endpoint and drop the ones nobody buys.
5. Compliance basics
Selling data and API access is generally low-risk. Holding or moving other people's money is not: custody, exchange or money transmission can need licenses and KYC. Keep these products data-only, keep the wallet off the public side, and get legal advice before anything that touches customer funds. Nothing here is financial advice, and revenue isn't guaranteed.
6. Honest expectations
A single pruned node is cheap to run and a solid base, but the money comes from the service built on top of it, and from people actually finding and using it. Start with one or two endpoints, price them small, and grow based on what gets paid for.
Attribution
Built on Bitcoin Core (unmodified). This is not a fork of Bitcoin Core. We run the official, unmodified Bitcoin Core snap build. Credit goes to bitcoin.org, bitcoincore.org and the Bitcoin Core contributors. Bitcoin Core is released under the MIT license.
What we added on top: the pruned-node deployment, the x402 per-call layer, the Lightning rail, the UnyKorn chain registry and the wallet script. NOTICE · Announcement
Code: btc_x402.py · lightning.py · registry.py · btc_wallet.sh · btc402.env.example · registry_worker.js · README