This runbook describes a persistent product-testing environment where a user clicks Fund via Fiber, a real Fiber testnet invoice settles end to end, and the underlying channel can be inspected on a public block explorer without requiring terminal access during the payment flow.
It complements (does not replace) the turnkey simulated path
(docker compose up) used for local development.
A Fiber payment is off-chain — an HTLC update inside a payment channel. It does not appear on any block explorer, by design. What is on-chain and publicly verifiable is the channel funding transaction. So the honest, checkable proof we surface is:
GET /api/fiber/live-proof).fibt1… invoice; the balance is
credited only after the API’s /verify confirms Fiber settled it.Together, these surfaces show that the deployment uses a funded CKB testnet channel and that FiberMeter moves the customer balance only after settlement.
Going live needs two funded testnet nodes + an open channel. Funding comes from the CKB faucet (external, rate-limited) and a channel open needs on-chain confirmation. Local application startup cannot create faucet capacity or an on-chain channel automatically, so operators complete this bootstrap once and persist the node state.
┌─────────────── your droplet ───────────────┐
│ Docker: postgres · api(live) · dashboard │
│ · demo-service · autopay │
│ │
│ Host: fnn payee node RPC :8237 ◄── api (issues invoices)
│ fnn payer node RPC :8247 ◄── autopay (pays invoices)
└─────────────────────────────────────────────┘
│ │
└─ public CKB testnet RPC (testnet.ckbapp.dev) ─┘
autopay settles invoices from it.allow_self_payment off). See 08-fiber-integration.md.fnn
nodes use a public CKB RPC, so you do not sync a full CKB node — disk
stays small.fnn + fnn-cli binaries from the
Fiber releases.Give each node its own directory, config, and CKB key. Use distinct ports so they can coexist and the containers can reach each:
| Node | Fiber P2P | Fiber RPC | data dir |
|---|---|---|---|
| payee | 8238 |
8237 |
./fiber-payee |
| payer | 8248 |
8247 |
./fiber-payer |
In each config.yml, set chain: testnet, point ckb.rpc_url at a public
testnet RPC (https://testnet.ckbapp.dev/), and set rpc.listening_addr to the
port above. Keep RPC bound to 127.0.0.1 — it is dangerous to expose.
(The repo’s fiber-payee/config.yml is a working reference.)
FIBER_SECRET_KEY_PASSWORD='password' ./fiber-payee/fnn -c ./fiber-payee/config.yml -d ./fiber-payee &
FIBER_SECRET_KEY_PASSWORD='password' ./fiber-payer/fnn -c ./fiber-payer/config.yml -d ./fiber-payer &
Confirm each responds:
curl -s -X POST http://127.0.0.1:8237 -H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"node_info","params":[{}]}' | jq .result.node_id
curl -s -X POST http://127.0.0.1:8247 -H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"node_info","params":[{}]}' | jq .result.node_id
The payer funds the channel (its balance is what gets pushed to the payee on
each top-up), so it needs on-chain CKB. Get the payer node’s CKB address (via
ckb-cli / your wallet against the node’s ckb/key) and request testnet CKB at
https://faucet.nervos.org/. Fund with enough for the channel + fees (e.g.
300+ CKB).
Use fnn-cli against each node — it tracks the current RPC parameter shapes
across fnn versions, so prefer it over hand-rolled JSON:
# connect payer → payee (payee multiaddr = /ip4/127.0.0.1/tcp/8238/p2p/<payee node_id>)
./fiber-payer/fnn-cli --rpc http://127.0.0.1:8247 connect-peer <payee-multiaddr>
# open a funded channel from payer → payee (gives the payee inbound capacity to receive top-ups)
./fiber-payer/fnn-cli --rpc http://127.0.0.1:8247 open-channel --peer <payee-node-id> --funding-amount 20000000000
Wait until the channel is ChannelReady:
curl -s -X POST http://127.0.0.1:8247 -H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"list_channels","params":[{}]}' | jq '.result.channels[].state'
Exact
fnn-clisubcommand names/flags can differ by release — run./fnn-cli --help. The RPC methods areconnect_peer,open_channel,list_channels. This is the only manual, on-chain step; everything after is automated.
docker compose -f docker-compose.yml -f docker-compose.live.yml up -d
This runs postgres + API in live mode (invoice creation/verification →
payee :8237, preflight → payer :8247) + dashboard + demo-service +
autopay (→ payer :8247). The API auto-migrates and seeds on boot.
demo@fibermeter.dev / password123).fibt1… invoice.No terminal, no manual send_payment.
scripts/autopay.mjs (npm: pnpm autopay) logs into the API, polls
GET /api/payment-requests, and for each live + pending request calls
send_payment on the payer node, then polls the API’s /verify until Fiber
confirms. Config via env (see the file header): API_URL, API_EMAIL,
API_PASSWORD, PAYER_RPC_URL, POLL_MS, MAX_PAYMENT_CKB. It never pays the
same request twice.
:8237 / :8247 (Fiber RPC) publicly. Only the containers
(via host.docker.internal) and localhost should reach them.FIBER_DEMO_AUTOPAY=true restricted to Fibt testnet. The API and payer
independently enforce FIBER_DEMO_MAX_PAYMENT_CKB / MAX_PAYMENT_CKB (5 CKB
in the supplied Compose overlay)../fiber-payee and ./fiber-payer — they hold node
keys and channel state; losing them can strand channel funds.