RTL/docker/docker-compose.yml

401 lines
16 KiB
YAML
Raw Normal View History

Rebuild the regtest dev fixture: bitcoind + 3 LND nodes + RTL The setup in docker/ has been unable to start since Feb 2021. The boltz service added in f817ae39 references BOLTZ_* variables that are not in .env (so its ports render as ":" and compose rejects the file with "invalid proto"), a ./boltz build context that has never existed in the repo, and boltz_data/boltz_shared volumes that are never declared. Because compose validates the whole project up front, this broke every service: "docker compose up -d bitcoind", the first command in the README, failed too. Rather than repair a five-year-old file pinned to bitcoind 0.19.0 and lnd 0.12.0-beta, this replaces it. What changed: - bitcoind 30.0 and 3x lnd 0.20.0-beta, using Polar's images. They are multi-arch, so nothing is built locally and this works on arm64. The old setup built bitcoind and lnd from local Dockerfiles. - Three nodes, not one: alice -> bob -> carol. bob forwards, so RTL's routing and forwarding screens have data. Two nodes leave them empty. - scripts/seed.sh funds the nodes, opens channels, and makes payments with fixed amounts. A fresh run reproduces identical state, so screenshots taken before and after a change differ only by the change. It is deliberately not idempotent and refuses to run against a seeded network, since re-running would double-fund it. - rtl/RTL-Config.regtest.json configures all three nodes. RTL rewrites its config on startup, so an init container stages a copy into a volume: a read-only bind mount makes RTL exit with EROFS, and a writable one would let RTL modify a tracked file. It is not named RTL-Config.json because .gitignore matches that bare name at any depth. - bin/ln-cli now takes a node name and passes --lnddir=/home/lnd/.lnd, because 'docker compose exec' lands as root while lnd's datadir is under /home/lnd. Both helpers use compose v2. - README rewritten to match. Boltz, Core Lightning and Eclair are left out of this pass. Polar publishes multi-arch clightning and eclair images, so adding them later needs compose services, config entries and seed adapters, but no image building. Verified from a clean 'down -v': all nodes sync, channels go active, 5/5 payments route through bob, bob records 5 forwards, and RTL serves the UI with all three nodes configured. Two independent from-scratch runs produced identical balances. The old bitcoind/ and lnd/ build contexts are now unreferenced but left in place for a follow-up.
2026-07-15 23:09:03 -07:00
# Regtest dev fixture for RTL: bitcoind + 3 LND nodes + RTL.
#
# NOT suitable for production. All credentials are throwaway.
#
# Topology is alice -> bob -> carol, so bob forwards payments and RTL's
# routing/forwarding screens have data in them. See README.md.
#
# Node images come from Polar (https://lightningpolar.com), which publishes
# multi-arch (amd64 + arm64) builds. Nothing is built locally.
volumes:
Rebuild the regtest dev fixture: bitcoind + 3 LND nodes + RTL The setup in docker/ has been unable to start since Feb 2021. The boltz service added in f817ae39 references BOLTZ_* variables that are not in .env (so its ports render as ":" and compose rejects the file with "invalid proto"), a ./boltz build context that has never existed in the repo, and boltz_data/boltz_shared volumes that are never declared. Because compose validates the whole project up front, this broke every service: "docker compose up -d bitcoind", the first command in the README, failed too. Rather than repair a five-year-old file pinned to bitcoind 0.19.0 and lnd 0.12.0-beta, this replaces it. What changed: - bitcoind 30.0 and 3x lnd 0.20.0-beta, using Polar's images. They are multi-arch, so nothing is built locally and this works on arm64. The old setup built bitcoind and lnd from local Dockerfiles. - Three nodes, not one: alice -> bob -> carol. bob forwards, so RTL's routing and forwarding screens have data. Two nodes leave them empty. - scripts/seed.sh funds the nodes, opens channels, and makes payments with fixed amounts. A fresh run reproduces identical state, so screenshots taken before and after a change differ only by the change. It is deliberately not idempotent and refuses to run against a seeded network, since re-running would double-fund it. - rtl/RTL-Config.regtest.json configures all three nodes. RTL rewrites its config on startup, so an init container stages a copy into a volume: a read-only bind mount makes RTL exit with EROFS, and a writable one would let RTL modify a tracked file. It is not named RTL-Config.json because .gitignore matches that bare name at any depth. - bin/ln-cli now takes a node name and passes --lnddir=/home/lnd/.lnd, because 'docker compose exec' lands as root while lnd's datadir is under /home/lnd. Both helpers use compose v2. - README rewritten to match. Boltz, Core Lightning and Eclair are left out of this pass. Polar publishes multi-arch clightning and eclair images, so adding them later needs compose services, config entries and seed adapters, but no image building. Verified from a clean 'down -v': all nodes sync, channels go active, 5/5 payments route through bob, bob records 5 forwards, and RTL serves the UI with all three nodes configured. Two independent from-scratch runs produced identical balances. The old bitcoind/ and lnd/ build contexts are now unreferenced but left in place for a follow-up.
2026-07-15 23:09:03 -07:00
bitcoind_data:
alice_data:
bob_data:
carol_data:
cln_data:
Add an Eclair node to the regtest docker fixture Completes backend coverage of RTL's three implementations in the docker/ dev fixture: an eclair node (polarlightning/eclair 0.13.1) joins the three LND nodes and the CLN node, wired to RTL over its HTTP API with basic auth, and the seed opens an eclair->bob channel (3.5M sats, 1M pushed), sends two direct payments and leaves one open invoice. Non-obvious plumbing this needed: - polarlightning/eclair instead of acinq/eclair: the official image is amd64-only (useless on Apple Silicon) and its newest versioned tag is years stale; Polar builds the same ACINQ source multi-arch. - Eclair has no on-chain wallet of its own -- it drives a bitcoind wallet. A new eclair-wallet-init container creates a dedicated "eclair" wallet before the node starts; without it eclair attaches to "the default loaded wallet", i.e. the rtldev mining wallet. - bitcoind now also publishes a zmqpubhashblock endpoint (28336): eclair's bitcoind.zmqblock consumes the hashblock topic, not the rawblock one LND uses. Wired to rawblock, eclair never sees new blocks and channels hang in WAIT_FOR_FUNDING_CONFIRMED. - Eclair confirms channels at 8 blocks (channel.min-depth-blocks), not 6, and 'open' returns before the funding tx is broadcast -- the seed waits for the mempool and mines 8 blocks for this channel. Adds a bin/e-cli helper (eclair-cli with the API password), updates the README, and verified end-to-end: seed completes, the channel reaches NORMAL, both payments settle, and RTL's /rtl/api/ecl endpoints return the node, channel and invoice data.
2026-07-18 00:23:08 -07:00
eclair_data:
2021-12-29 18:08:41 -05:00
rtl_db:
Rebuild the regtest dev fixture: bitcoind + 3 LND nodes + RTL The setup in docker/ has been unable to start since Feb 2021. The boltz service added in f817ae39 references BOLTZ_* variables that are not in .env (so its ports render as ":" and compose rejects the file with "invalid proto"), a ./boltz build context that has never existed in the repo, and boltz_data/boltz_shared volumes that are never declared. Because compose validates the whole project up front, this broke every service: "docker compose up -d bitcoind", the first command in the README, failed too. Rather than repair a five-year-old file pinned to bitcoind 0.19.0 and lnd 0.12.0-beta, this replaces it. What changed: - bitcoind 30.0 and 3x lnd 0.20.0-beta, using Polar's images. They are multi-arch, so nothing is built locally and this works on arm64. The old setup built bitcoind and lnd from local Dockerfiles. - Three nodes, not one: alice -> bob -> carol. bob forwards, so RTL's routing and forwarding screens have data. Two nodes leave them empty. - scripts/seed.sh funds the nodes, opens channels, and makes payments with fixed amounts. A fresh run reproduces identical state, so screenshots taken before and after a change differ only by the change. It is deliberately not idempotent and refuses to run against a seeded network, since re-running would double-fund it. - rtl/RTL-Config.regtest.json configures all three nodes. RTL rewrites its config on startup, so an init container stages a copy into a volume: a read-only bind mount makes RTL exit with EROFS, and a writable one would let RTL modify a tracked file. It is not named RTL-Config.json because .gitignore matches that bare name at any depth. - bin/ln-cli now takes a node name and passes --lnddir=/home/lnd/.lnd, because 'docker compose exec' lands as root while lnd's datadir is under /home/lnd. Both helpers use compose v2. - README rewritten to match. Boltz, Core Lightning and Eclair are left out of this pass. Polar publishes multi-arch clightning and eclair images, so adding them later needs compose services, config entries and seed adapters, but no image building. Verified from a clean 'down -v': all nodes sync, channels go active, 5/5 payments route through bob, bob records 5 forwards, and RTL serves the UI with all three nodes configured. Two independent from-scratch runs produced identical balances. The old bitcoind/ and lnd/ build contexts are now unreferenced but left in place for a follow-up.
2026-07-15 23:09:03 -07:00
rtl_config:
Add a BTCPay Server SSO harness to the docker fixture (#1669) * Add a BTCPay Server SSO harness to the docker fixture BTCPay bundles RTL and runs it in single-sign-on mode, reached over an entry path the standalone login never exercises: no password, a rotating cookie file, an unregistered /rtl/api/authenticate/cookie URL that falls through to the catch-all in server/utils/app.ts, and a reverse proxy in front. Regressions on that path have previously gone unnoticed until they reached BTCPay users. Adds an "sso" compose profile, so a plain `docker compose up -d` is unchanged: - rtl-sso, a second RTL running with RTL_SSO=1, RTL_COOKIE_PATH and LOGOUT_REDIRECT_LINK -- the environment block lifted verbatim from BTCPay's own compose fragment, so this exercises the env-driven SSO path BTCPay actually uses. A second container is required because RTL selects one authentication mode at startup, so SSO and password login cannot coexist in one instance. - rtl-sso-config-init, staging rtl/RTL-Config.sso.json into a volume -- the same copy-into-a-volume dance the standalone RTL already needs, because RTL rewrites its config on startup. - rtl-sso-proxy, nginx standing in for BTCPay's traefik, routing only /rtl and /rtl/* exactly as BTCPay's router rule does. There is no prefix stripping anywhere: RTL is built with <base href="/rtl/"> and mounts every route under baseHref '/rtl', so the prefix is passed through unmodified. Everything outside /rtl 404s, so a request escaping the prefix surfaces as a failure rather than being quietly served. scripts/verify-sso.sh asserts the whole flow in 11 checks -- prefix routing, CSRF token minting on the catch-all, the sha256 access-key handshake, an authenticated node call, cookie rotation on login, and rejection of a wrong key -- and exits non-zero so it can gate a change. bin/sso-url prints the link BTCPay renders on its Services page. RTL_IMAGE overrides both RTL containers at once, so a branch build gets tested through both entry paths. BTCPay itself (postgres, nbxplorer, btcpayserver) is deliberately not included; the README documents what that leaves untested and how to run against BTCPay's own regtest stack when the question is BTCPay's behaviour rather than RTL's. Also bumps the fixture's default RTL image from v0.15.8 to v0.15.10. * Document the SSO harness in the rtl-docker-fixture skill * Point CLAUDE.md at the BTCPay SSO harness * Note that no CI runs on an open PR
2026-08-04 18:17:19 -07:00
rtl_sso_db:
rtl_sso_config:
rtl_sso_cookie:
Rebuild the regtest dev fixture: bitcoind + 3 LND nodes + RTL The setup in docker/ has been unable to start since Feb 2021. The boltz service added in f817ae39 references BOLTZ_* variables that are not in .env (so its ports render as ":" and compose rejects the file with "invalid proto"), a ./boltz build context that has never existed in the repo, and boltz_data/boltz_shared volumes that are never declared. Because compose validates the whole project up front, this broke every service: "docker compose up -d bitcoind", the first command in the README, failed too. Rather than repair a five-year-old file pinned to bitcoind 0.19.0 and lnd 0.12.0-beta, this replaces it. What changed: - bitcoind 30.0 and 3x lnd 0.20.0-beta, using Polar's images. They are multi-arch, so nothing is built locally and this works on arm64. The old setup built bitcoind and lnd from local Dockerfiles. - Three nodes, not one: alice -> bob -> carol. bob forwards, so RTL's routing and forwarding screens have data. Two nodes leave them empty. - scripts/seed.sh funds the nodes, opens channels, and makes payments with fixed amounts. A fresh run reproduces identical state, so screenshots taken before and after a change differ only by the change. It is deliberately not idempotent and refuses to run against a seeded network, since re-running would double-fund it. - rtl/RTL-Config.regtest.json configures all three nodes. RTL rewrites its config on startup, so an init container stages a copy into a volume: a read-only bind mount makes RTL exit with EROFS, and a writable one would let RTL modify a tracked file. It is not named RTL-Config.json because .gitignore matches that bare name at any depth. - bin/ln-cli now takes a node name and passes --lnddir=/home/lnd/.lnd, because 'docker compose exec' lands as root while lnd's datadir is under /home/lnd. Both helpers use compose v2. - README rewritten to match. Boltz, Core Lightning and Eclair are left out of this pass. Polar publishes multi-arch clightning and eclair images, so adding them later needs compose services, config entries and seed adapters, but no image building. Verified from a clean 'down -v': all nodes sync, channels go active, 5/5 payments route through bob, bob records 5 forwards, and RTL serves the UI with all three nodes configured. Two independent from-scratch runs produced identical balances. The old bitcoind/ and lnd/ build contexts are now unreferenced but left in place for a follow-up.
2026-07-15 23:09:03 -07:00
x-lnd: &lnd
image: polarlightning/lnd:0.20.0-beta
restart: unless-stopped
depends_on:
- bitcoind
services:
bitcoind:
container_name: ${COMPOSE_PROJECT_NAME}_bitcoind
Rebuild the regtest dev fixture: bitcoind + 3 LND nodes + RTL The setup in docker/ has been unable to start since Feb 2021. The boltz service added in f817ae39 references BOLTZ_* variables that are not in .env (so its ports render as ":" and compose rejects the file with "invalid proto"), a ./boltz build context that has never existed in the repo, and boltz_data/boltz_shared volumes that are never declared. Because compose validates the whole project up front, this broke every service: "docker compose up -d bitcoind", the first command in the README, failed too. Rather than repair a five-year-old file pinned to bitcoind 0.19.0 and lnd 0.12.0-beta, this replaces it. What changed: - bitcoind 30.0 and 3x lnd 0.20.0-beta, using Polar's images. They are multi-arch, so nothing is built locally and this works on arm64. The old setup built bitcoind and lnd from local Dockerfiles. - Three nodes, not one: alice -> bob -> carol. bob forwards, so RTL's routing and forwarding screens have data. Two nodes leave them empty. - scripts/seed.sh funds the nodes, opens channels, and makes payments with fixed amounts. A fresh run reproduces identical state, so screenshots taken before and after a change differ only by the change. It is deliberately not idempotent and refuses to run against a seeded network, since re-running would double-fund it. - rtl/RTL-Config.regtest.json configures all three nodes. RTL rewrites its config on startup, so an init container stages a copy into a volume: a read-only bind mount makes RTL exit with EROFS, and a writable one would let RTL modify a tracked file. It is not named RTL-Config.json because .gitignore matches that bare name at any depth. - bin/ln-cli now takes a node name and passes --lnddir=/home/lnd/.lnd, because 'docker compose exec' lands as root while lnd's datadir is under /home/lnd. Both helpers use compose v2. - README rewritten to match. Boltz, Core Lightning and Eclair are left out of this pass. Polar publishes multi-arch clightning and eclair images, so adding them later needs compose services, config entries and seed adapters, but no image building. Verified from a clean 'down -v': all nodes sync, channels go active, 5/5 payments route through bob, bob records 5 forwards, and RTL serves the UI with all three nodes configured. Two independent from-scratch runs produced identical balances. The old bitcoind/ and lnd/ build contexts are now unreferenced but left in place for a follow-up.
2026-07-15 23:09:03 -07:00
image: polarlightning/bitcoind:30.0
restart: unless-stopped
command:
- bitcoind
- -server=1
- -regtest=1
# rpcauth hash for ${BITCOIN_RPC_USER}/${BITCOIN_RPC_PASSWORD}. '$$' escapes
# compose interpolation and reaches bitcoind as a single '$'.
- -rpcauth=rtldev:8a1f2c3d4e5b6a7c8d9e0f1a2b3c4d5e$$010df4b32c5e9a556cba1857eb5865990c983d8a56dadc0fdbf457cf90073c6c
- -zmqpubrawblock=tcp://0.0.0.0:${BITCOIN_ZMQ_BLOCK_PORT}
- -zmqpubrawtx=tcp://0.0.0.0:${BITCOIN_ZMQ_TX_PORT}
Add an Eclair node to the regtest docker fixture Completes backend coverage of RTL's three implementations in the docker/ dev fixture: an eclair node (polarlightning/eclair 0.13.1) joins the three LND nodes and the CLN node, wired to RTL over its HTTP API with basic auth, and the seed opens an eclair->bob channel (3.5M sats, 1M pushed), sends two direct payments and leaves one open invoice. Non-obvious plumbing this needed: - polarlightning/eclair instead of acinq/eclair: the official image is amd64-only (useless on Apple Silicon) and its newest versioned tag is years stale; Polar builds the same ACINQ source multi-arch. - Eclair has no on-chain wallet of its own -- it drives a bitcoind wallet. A new eclair-wallet-init container creates a dedicated "eclair" wallet before the node starts; without it eclair attaches to "the default loaded wallet", i.e. the rtldev mining wallet. - bitcoind now also publishes a zmqpubhashblock endpoint (28336): eclair's bitcoind.zmqblock consumes the hashblock topic, not the rawblock one LND uses. Wired to rawblock, eclair never sees new blocks and channels hang in WAIT_FOR_FUNDING_CONFIRMED. - Eclair confirms channels at 8 blocks (channel.min-depth-blocks), not 6, and 'open' returns before the funding tx is broadcast -- the seed waits for the mempool and mines 8 blocks for this channel. Adds a bin/e-cli helper (eclair-cli with the API password), updates the README, and verified end-to-end: seed completes, the channel reaches NORMAL, both payments settle, and RTL's /rtl/api/ecl endpoints return the node, channel and invoice data.
2026-07-18 00:23:08 -07:00
# eclair's zmqblock consumes the hashblock topic, not rawblock (LND uses
# rawblock/rawtx above); without this endpoint eclair never sees new
# blocks and channels stay in WAIT_FOR_FUNDING_CONFIRMED forever.
- -zmqpubhashblock=tcp://0.0.0.0:${BITCOIN_ZMQ_HASHBLOCK_PORT:-28336}
Rebuild the regtest dev fixture: bitcoind + 3 LND nodes + RTL The setup in docker/ has been unable to start since Feb 2021. The boltz service added in f817ae39 references BOLTZ_* variables that are not in .env (so its ports render as ":" and compose rejects the file with "invalid proto"), a ./boltz build context that has never existed in the repo, and boltz_data/boltz_shared volumes that are never declared. Because compose validates the whole project up front, this broke every service: "docker compose up -d bitcoind", the first command in the README, failed too. Rather than repair a five-year-old file pinned to bitcoind 0.19.0 and lnd 0.12.0-beta, this replaces it. What changed: - bitcoind 30.0 and 3x lnd 0.20.0-beta, using Polar's images. They are multi-arch, so nothing is built locally and this works on arm64. The old setup built bitcoind and lnd from local Dockerfiles. - Three nodes, not one: alice -> bob -> carol. bob forwards, so RTL's routing and forwarding screens have data. Two nodes leave them empty. - scripts/seed.sh funds the nodes, opens channels, and makes payments with fixed amounts. A fresh run reproduces identical state, so screenshots taken before and after a change differ only by the change. It is deliberately not idempotent and refuses to run against a seeded network, since re-running would double-fund it. - rtl/RTL-Config.regtest.json configures all three nodes. RTL rewrites its config on startup, so an init container stages a copy into a volume: a read-only bind mount makes RTL exit with EROFS, and a writable one would let RTL modify a tracked file. It is not named RTL-Config.json because .gitignore matches that bare name at any depth. - bin/ln-cli now takes a node name and passes --lnddir=/home/lnd/.lnd, because 'docker compose exec' lands as root while lnd's datadir is under /home/lnd. Both helpers use compose v2. - README rewritten to match. Boltz, Core Lightning and Eclair are left out of this pass. Polar publishes multi-arch clightning and eclair images, so adding them later needs compose services, config entries and seed adapters, but no image building. Verified from a clean 'down -v': all nodes sync, channels go active, 5/5 payments route through bob, bob records 5 forwards, and RTL serves the UI with all three nodes configured. Two independent from-scratch runs produced identical balances. The old bitcoind/ and lnd/ build contexts are now unreferenced but left in place for a follow-up.
2026-07-15 23:09:03 -07:00
- -txindex=1
- -dnsseed=0
- -rpcbind=0.0.0.0
- -rpcallowip=0.0.0.0/0
- -rpcport=${BITCOIN_RPC_PORT}
- -listen=1
- -listenonion=0
- -fallbackfee=0.0002
ports:
Rebuild the regtest dev fixture: bitcoind + 3 LND nodes + RTL The setup in docker/ has been unable to start since Feb 2021. The boltz service added in f817ae39 references BOLTZ_* variables that are not in .env (so its ports render as ":" and compose rejects the file with "invalid proto"), a ./boltz build context that has never existed in the repo, and boltz_data/boltz_shared volumes that are never declared. Because compose validates the whole project up front, this broke every service: "docker compose up -d bitcoind", the first command in the README, failed too. Rather than repair a five-year-old file pinned to bitcoind 0.19.0 and lnd 0.12.0-beta, this replaces it. What changed: - bitcoind 30.0 and 3x lnd 0.20.0-beta, using Polar's images. They are multi-arch, so nothing is built locally and this works on arm64. The old setup built bitcoind and lnd from local Dockerfiles. - Three nodes, not one: alice -> bob -> carol. bob forwards, so RTL's routing and forwarding screens have data. Two nodes leave them empty. - scripts/seed.sh funds the nodes, opens channels, and makes payments with fixed amounts. A fresh run reproduces identical state, so screenshots taken before and after a change differ only by the change. It is deliberately not idempotent and refuses to run against a seeded network, since re-running would double-fund it. - rtl/RTL-Config.regtest.json configures all three nodes. RTL rewrites its config on startup, so an init container stages a copy into a volume: a read-only bind mount makes RTL exit with EROFS, and a writable one would let RTL modify a tracked file. It is not named RTL-Config.json because .gitignore matches that bare name at any depth. - bin/ln-cli now takes a node name and passes --lnddir=/home/lnd/.lnd, because 'docker compose exec' lands as root while lnd's datadir is under /home/lnd. Both helpers use compose v2. - README rewritten to match. Boltz, Core Lightning and Eclair are left out of this pass. Polar publishes multi-arch clightning and eclair images, so adding them later needs compose services, config entries and seed adapters, but no image building. Verified from a clean 'down -v': all nodes sync, channels go active, 5/5 payments route through bob, bob records 5 forwards, and RTL serves the UI with all three nodes configured. Two independent from-scratch runs produced identical balances. The old bitcoind/ and lnd/ build contexts are now unreferenced but left in place for a follow-up.
2026-07-15 23:09:03 -07:00
- "${BITCOIN_RPC_PORT}:${BITCOIN_RPC_PORT}"
volumes:
Rebuild the regtest dev fixture: bitcoind + 3 LND nodes + RTL The setup in docker/ has been unable to start since Feb 2021. The boltz service added in f817ae39 references BOLTZ_* variables that are not in .env (so its ports render as ":" and compose rejects the file with "invalid proto"), a ./boltz build context that has never existed in the repo, and boltz_data/boltz_shared volumes that are never declared. Because compose validates the whole project up front, this broke every service: "docker compose up -d bitcoind", the first command in the README, failed too. Rather than repair a five-year-old file pinned to bitcoind 0.19.0 and lnd 0.12.0-beta, this replaces it. What changed: - bitcoind 30.0 and 3x lnd 0.20.0-beta, using Polar's images. They are multi-arch, so nothing is built locally and this works on arm64. The old setup built bitcoind and lnd from local Dockerfiles. - Three nodes, not one: alice -> bob -> carol. bob forwards, so RTL's routing and forwarding screens have data. Two nodes leave them empty. - scripts/seed.sh funds the nodes, opens channels, and makes payments with fixed amounts. A fresh run reproduces identical state, so screenshots taken before and after a change differ only by the change. It is deliberately not idempotent and refuses to run against a seeded network, since re-running would double-fund it. - rtl/RTL-Config.regtest.json configures all three nodes. RTL rewrites its config on startup, so an init container stages a copy into a volume: a read-only bind mount makes RTL exit with EROFS, and a writable one would let RTL modify a tracked file. It is not named RTL-Config.json because .gitignore matches that bare name at any depth. - bin/ln-cli now takes a node name and passes --lnddir=/home/lnd/.lnd, because 'docker compose exec' lands as root while lnd's datadir is under /home/lnd. Both helpers use compose v2. - README rewritten to match. Boltz, Core Lightning and Eclair are left out of this pass. Polar publishes multi-arch clightning and eclair images, so adding them later needs compose services, config entries and seed adapters, but no image building. Verified from a clean 'down -v': all nodes sync, channels go active, 5/5 payments route through bob, bob records 5 forwards, and RTL serves the UI with all three nodes configured. Two independent from-scratch runs produced identical balances. The old bitcoind/ and lnd/ build contexts are now unreferenced but left in place for a follow-up.
2026-07-15 23:09:03 -07:00
- bitcoind_data:/home/bitcoin/.bitcoin
Rebuild the regtest dev fixture: bitcoind + 3 LND nodes + RTL The setup in docker/ has been unable to start since Feb 2021. The boltz service added in f817ae39 references BOLTZ_* variables that are not in .env (so its ports render as ":" and compose rejects the file with "invalid proto"), a ./boltz build context that has never existed in the repo, and boltz_data/boltz_shared volumes that are never declared. Because compose validates the whole project up front, this broke every service: "docker compose up -d bitcoind", the first command in the README, failed too. Rather than repair a five-year-old file pinned to bitcoind 0.19.0 and lnd 0.12.0-beta, this replaces it. What changed: - bitcoind 30.0 and 3x lnd 0.20.0-beta, using Polar's images. They are multi-arch, so nothing is built locally and this works on arm64. The old setup built bitcoind and lnd from local Dockerfiles. - Three nodes, not one: alice -> bob -> carol. bob forwards, so RTL's routing and forwarding screens have data. Two nodes leave them empty. - scripts/seed.sh funds the nodes, opens channels, and makes payments with fixed amounts. A fresh run reproduces identical state, so screenshots taken before and after a change differ only by the change. It is deliberately not idempotent and refuses to run against a seeded network, since re-running would double-fund it. - rtl/RTL-Config.regtest.json configures all three nodes. RTL rewrites its config on startup, so an init container stages a copy into a volume: a read-only bind mount makes RTL exit with EROFS, and a writable one would let RTL modify a tracked file. It is not named RTL-Config.json because .gitignore matches that bare name at any depth. - bin/ln-cli now takes a node name and passes --lnddir=/home/lnd/.lnd, because 'docker compose exec' lands as root while lnd's datadir is under /home/lnd. Both helpers use compose v2. - README rewritten to match. Boltz, Core Lightning and Eclair are left out of this pass. Polar publishes multi-arch clightning and eclair images, so adding them later needs compose services, config entries and seed adapters, but no image building. Verified from a clean 'down -v': all nodes sync, channels go active, 5/5 payments route through bob, bob records 5 forwards, and RTL serves the UI with all three nodes configured. Two independent from-scratch runs produced identical balances. The old bitcoind/ and lnd/ build contexts are now unreferenced but left in place for a follow-up.
2026-07-15 23:09:03 -07:00
# --alias / --externalip / --tlsextradomain are per-node on purpose: the alias
# is what RTL displays, and the extradomain must match the hostname RTL dials
# (https://alice:8080) or TLS validation fails.
alice:
<<: *lnd
container_name: ${COMPOSE_PROJECT_NAME}_alice
command:
- lnd
- --noseedbackup
- --trickledelay=5000
- --alias=alice
- --externalip=alice
- --tlsextradomain=alice
- --tlsextradomain=${COMPOSE_PROJECT_NAME}_alice
- --tlsextradomain=host.docker.internal
- --listen=0.0.0.0:${LIGHTNING_P2P_PORT}
- --rpclisten=0.0.0.0:${LIGHTNING_RPC_PORT}
- --restlisten=0.0.0.0:${LIGHTNING_REST_PORT}
- --bitcoin.active
- --bitcoin.regtest
- --bitcoin.node=bitcoind
- --bitcoind.rpchost=${BITCOIN_HOST}:${BITCOIN_RPC_PORT}
- --bitcoind.rpcuser=${BITCOIN_RPC_USER}
- --bitcoind.rpcpass=${BITCOIN_RPC_PASSWORD}
- --bitcoind.zmqpubrawblock=tcp://${BITCOIN_HOST}:${BITCOIN_ZMQ_BLOCK_PORT}
- --bitcoind.zmqpubrawtx=tcp://${BITCOIN_HOST}:${BITCOIN_ZMQ_TX_PORT}
- --accept-keysend
- --accept-amp
ports:
Rebuild the regtest dev fixture: bitcoind + 3 LND nodes + RTL The setup in docker/ has been unable to start since Feb 2021. The boltz service added in f817ae39 references BOLTZ_* variables that are not in .env (so its ports render as ":" and compose rejects the file with "invalid proto"), a ./boltz build context that has never existed in the repo, and boltz_data/boltz_shared volumes that are never declared. Because compose validates the whole project up front, this broke every service: "docker compose up -d bitcoind", the first command in the README, failed too. Rather than repair a five-year-old file pinned to bitcoind 0.19.0 and lnd 0.12.0-beta, this replaces it. What changed: - bitcoind 30.0 and 3x lnd 0.20.0-beta, using Polar's images. They are multi-arch, so nothing is built locally and this works on arm64. The old setup built bitcoind and lnd from local Dockerfiles. - Three nodes, not one: alice -> bob -> carol. bob forwards, so RTL's routing and forwarding screens have data. Two nodes leave them empty. - scripts/seed.sh funds the nodes, opens channels, and makes payments with fixed amounts. A fresh run reproduces identical state, so screenshots taken before and after a change differ only by the change. It is deliberately not idempotent and refuses to run against a seeded network, since re-running would double-fund it. - rtl/RTL-Config.regtest.json configures all three nodes. RTL rewrites its config on startup, so an init container stages a copy into a volume: a read-only bind mount makes RTL exit with EROFS, and a writable one would let RTL modify a tracked file. It is not named RTL-Config.json because .gitignore matches that bare name at any depth. - bin/ln-cli now takes a node name and passes --lnddir=/home/lnd/.lnd, because 'docker compose exec' lands as root while lnd's datadir is under /home/lnd. Both helpers use compose v2. - README rewritten to match. Boltz, Core Lightning and Eclair are left out of this pass. Polar publishes multi-arch clightning and eclair images, so adding them later needs compose services, config entries and seed adapters, but no image building. Verified from a clean 'down -v': all nodes sync, channels go active, 5/5 payments route through bob, bob records 5 forwards, and RTL serves the UI with all three nodes configured. Two independent from-scratch runs produced identical balances. The old bitcoind/ and lnd/ build contexts are now unreferenced but left in place for a follow-up.
2026-07-15 23:09:03 -07:00
- "${ALICE_REST_PORT}:${LIGHTNING_REST_PORT}"
volumes:
Rebuild the regtest dev fixture: bitcoind + 3 LND nodes + RTL The setup in docker/ has been unable to start since Feb 2021. The boltz service added in f817ae39 references BOLTZ_* variables that are not in .env (so its ports render as ":" and compose rejects the file with "invalid proto"), a ./boltz build context that has never existed in the repo, and boltz_data/boltz_shared volumes that are never declared. Because compose validates the whole project up front, this broke every service: "docker compose up -d bitcoind", the first command in the README, failed too. Rather than repair a five-year-old file pinned to bitcoind 0.19.0 and lnd 0.12.0-beta, this replaces it. What changed: - bitcoind 30.0 and 3x lnd 0.20.0-beta, using Polar's images. They are multi-arch, so nothing is built locally and this works on arm64. The old setup built bitcoind and lnd from local Dockerfiles. - Three nodes, not one: alice -> bob -> carol. bob forwards, so RTL's routing and forwarding screens have data. Two nodes leave them empty. - scripts/seed.sh funds the nodes, opens channels, and makes payments with fixed amounts. A fresh run reproduces identical state, so screenshots taken before and after a change differ only by the change. It is deliberately not idempotent and refuses to run against a seeded network, since re-running would double-fund it. - rtl/RTL-Config.regtest.json configures all three nodes. RTL rewrites its config on startup, so an init container stages a copy into a volume: a read-only bind mount makes RTL exit with EROFS, and a writable one would let RTL modify a tracked file. It is not named RTL-Config.json because .gitignore matches that bare name at any depth. - bin/ln-cli now takes a node name and passes --lnddir=/home/lnd/.lnd, because 'docker compose exec' lands as root while lnd's datadir is under /home/lnd. Both helpers use compose v2. - README rewritten to match. Boltz, Core Lightning and Eclair are left out of this pass. Polar publishes multi-arch clightning and eclair images, so adding them later needs compose services, config entries and seed adapters, but no image building. Verified from a clean 'down -v': all nodes sync, channels go active, 5/5 payments route through bob, bob records 5 forwards, and RTL serves the UI with all three nodes configured. Two independent from-scratch runs produced identical balances. The old bitcoind/ and lnd/ build contexts are now unreferenced but left in place for a follow-up.
2026-07-15 23:09:03 -07:00
- alice_data:/home/lnd/.lnd
Rebuild the regtest dev fixture: bitcoind + 3 LND nodes + RTL The setup in docker/ has been unable to start since Feb 2021. The boltz service added in f817ae39 references BOLTZ_* variables that are not in .env (so its ports render as ":" and compose rejects the file with "invalid proto"), a ./boltz build context that has never existed in the repo, and boltz_data/boltz_shared volumes that are never declared. Because compose validates the whole project up front, this broke every service: "docker compose up -d bitcoind", the first command in the README, failed too. Rather than repair a five-year-old file pinned to bitcoind 0.19.0 and lnd 0.12.0-beta, this replaces it. What changed: - bitcoind 30.0 and 3x lnd 0.20.0-beta, using Polar's images. They are multi-arch, so nothing is built locally and this works on arm64. The old setup built bitcoind and lnd from local Dockerfiles. - Three nodes, not one: alice -> bob -> carol. bob forwards, so RTL's routing and forwarding screens have data. Two nodes leave them empty. - scripts/seed.sh funds the nodes, opens channels, and makes payments with fixed amounts. A fresh run reproduces identical state, so screenshots taken before and after a change differ only by the change. It is deliberately not idempotent and refuses to run against a seeded network, since re-running would double-fund it. - rtl/RTL-Config.regtest.json configures all three nodes. RTL rewrites its config on startup, so an init container stages a copy into a volume: a read-only bind mount makes RTL exit with EROFS, and a writable one would let RTL modify a tracked file. It is not named RTL-Config.json because .gitignore matches that bare name at any depth. - bin/ln-cli now takes a node name and passes --lnddir=/home/lnd/.lnd, because 'docker compose exec' lands as root while lnd's datadir is under /home/lnd. Both helpers use compose v2. - README rewritten to match. Boltz, Core Lightning and Eclair are left out of this pass. Polar publishes multi-arch clightning and eclair images, so adding them later needs compose services, config entries and seed adapters, but no image building. Verified from a clean 'down -v': all nodes sync, channels go active, 5/5 payments route through bob, bob records 5 forwards, and RTL serves the UI with all three nodes configured. Two independent from-scratch runs produced identical balances. The old bitcoind/ and lnd/ build contexts are now unreferenced but left in place for a follow-up.
2026-07-15 23:09:03 -07:00
bob:
<<: *lnd
container_name: ${COMPOSE_PROJECT_NAME}_bob
command:
- lnd
- --noseedbackup
- --trickledelay=5000
- --alias=bob
- --externalip=bob
- --tlsextradomain=bob
- --tlsextradomain=${COMPOSE_PROJECT_NAME}_bob
- --tlsextradomain=host.docker.internal
- --listen=0.0.0.0:${LIGHTNING_P2P_PORT}
- --rpclisten=0.0.0.0:${LIGHTNING_RPC_PORT}
- --restlisten=0.0.0.0:${LIGHTNING_REST_PORT}
- --bitcoin.active
- --bitcoin.regtest
- --bitcoin.node=bitcoind
- --bitcoind.rpchost=${BITCOIN_HOST}:${BITCOIN_RPC_PORT}
- --bitcoind.rpcuser=${BITCOIN_RPC_USER}
- --bitcoind.rpcpass=${BITCOIN_RPC_PASSWORD}
- --bitcoind.zmqpubrawblock=tcp://${BITCOIN_HOST}:${BITCOIN_ZMQ_BLOCK_PORT}
- --bitcoind.zmqpubrawtx=tcp://${BITCOIN_HOST}:${BITCOIN_ZMQ_TX_PORT}
- --accept-keysend
- --accept-amp
ports:
Rebuild the regtest dev fixture: bitcoind + 3 LND nodes + RTL The setup in docker/ has been unable to start since Feb 2021. The boltz service added in f817ae39 references BOLTZ_* variables that are not in .env (so its ports render as ":" and compose rejects the file with "invalid proto"), a ./boltz build context that has never existed in the repo, and boltz_data/boltz_shared volumes that are never declared. Because compose validates the whole project up front, this broke every service: "docker compose up -d bitcoind", the first command in the README, failed too. Rather than repair a five-year-old file pinned to bitcoind 0.19.0 and lnd 0.12.0-beta, this replaces it. What changed: - bitcoind 30.0 and 3x lnd 0.20.0-beta, using Polar's images. They are multi-arch, so nothing is built locally and this works on arm64. The old setup built bitcoind and lnd from local Dockerfiles. - Three nodes, not one: alice -> bob -> carol. bob forwards, so RTL's routing and forwarding screens have data. Two nodes leave them empty. - scripts/seed.sh funds the nodes, opens channels, and makes payments with fixed amounts. A fresh run reproduces identical state, so screenshots taken before and after a change differ only by the change. It is deliberately not idempotent and refuses to run against a seeded network, since re-running would double-fund it. - rtl/RTL-Config.regtest.json configures all three nodes. RTL rewrites its config on startup, so an init container stages a copy into a volume: a read-only bind mount makes RTL exit with EROFS, and a writable one would let RTL modify a tracked file. It is not named RTL-Config.json because .gitignore matches that bare name at any depth. - bin/ln-cli now takes a node name and passes --lnddir=/home/lnd/.lnd, because 'docker compose exec' lands as root while lnd's datadir is under /home/lnd. Both helpers use compose v2. - README rewritten to match. Boltz, Core Lightning and Eclair are left out of this pass. Polar publishes multi-arch clightning and eclair images, so adding them later needs compose services, config entries and seed adapters, but no image building. Verified from a clean 'down -v': all nodes sync, channels go active, 5/5 payments route through bob, bob records 5 forwards, and RTL serves the UI with all three nodes configured. Two independent from-scratch runs produced identical balances. The old bitcoind/ and lnd/ build contexts are now unreferenced but left in place for a follow-up.
2026-07-15 23:09:03 -07:00
- "${BOB_REST_PORT}:${LIGHTNING_REST_PORT}"
volumes:
Rebuild the regtest dev fixture: bitcoind + 3 LND nodes + RTL The setup in docker/ has been unable to start since Feb 2021. The boltz service added in f817ae39 references BOLTZ_* variables that are not in .env (so its ports render as ":" and compose rejects the file with "invalid proto"), a ./boltz build context that has never existed in the repo, and boltz_data/boltz_shared volumes that are never declared. Because compose validates the whole project up front, this broke every service: "docker compose up -d bitcoind", the first command in the README, failed too. Rather than repair a five-year-old file pinned to bitcoind 0.19.0 and lnd 0.12.0-beta, this replaces it. What changed: - bitcoind 30.0 and 3x lnd 0.20.0-beta, using Polar's images. They are multi-arch, so nothing is built locally and this works on arm64. The old setup built bitcoind and lnd from local Dockerfiles. - Three nodes, not one: alice -> bob -> carol. bob forwards, so RTL's routing and forwarding screens have data. Two nodes leave them empty. - scripts/seed.sh funds the nodes, opens channels, and makes payments with fixed amounts. A fresh run reproduces identical state, so screenshots taken before and after a change differ only by the change. It is deliberately not idempotent and refuses to run against a seeded network, since re-running would double-fund it. - rtl/RTL-Config.regtest.json configures all three nodes. RTL rewrites its config on startup, so an init container stages a copy into a volume: a read-only bind mount makes RTL exit with EROFS, and a writable one would let RTL modify a tracked file. It is not named RTL-Config.json because .gitignore matches that bare name at any depth. - bin/ln-cli now takes a node name and passes --lnddir=/home/lnd/.lnd, because 'docker compose exec' lands as root while lnd's datadir is under /home/lnd. Both helpers use compose v2. - README rewritten to match. Boltz, Core Lightning and Eclair are left out of this pass. Polar publishes multi-arch clightning and eclair images, so adding them later needs compose services, config entries and seed adapters, but no image building. Verified from a clean 'down -v': all nodes sync, channels go active, 5/5 payments route through bob, bob records 5 forwards, and RTL serves the UI with all three nodes configured. Two independent from-scratch runs produced identical balances. The old bitcoind/ and lnd/ build contexts are now unreferenced but left in place for a follow-up.
2026-07-15 23:09:03 -07:00
- bob_data:/home/lnd/.lnd
carol:
<<: *lnd
container_name: ${COMPOSE_PROJECT_NAME}_carol
command:
- lnd
- --noseedbackup
- --trickledelay=5000
- --alias=carol
- --externalip=carol
- --tlsextradomain=carol
- --tlsextradomain=${COMPOSE_PROJECT_NAME}_carol
- --tlsextradomain=host.docker.internal
- --listen=0.0.0.0:${LIGHTNING_P2P_PORT}
- --rpclisten=0.0.0.0:${LIGHTNING_RPC_PORT}
- --restlisten=0.0.0.0:${LIGHTNING_REST_PORT}
- --bitcoin.active
- --bitcoin.regtest
- --bitcoin.node=bitcoind
- --bitcoind.rpchost=${BITCOIN_HOST}:${BITCOIN_RPC_PORT}
- --bitcoind.rpcuser=${BITCOIN_RPC_USER}
- --bitcoind.rpcpass=${BITCOIN_RPC_PASSWORD}
- --bitcoind.zmqpubrawblock=tcp://${BITCOIN_HOST}:${BITCOIN_ZMQ_BLOCK_PORT}
- --bitcoind.zmqpubrawtx=tcp://${BITCOIN_HOST}:${BITCOIN_ZMQ_TX_PORT}
- --accept-keysend
- --accept-amp
ports:
- "${CAROL_REST_PORT}:${LIGHTNING_REST_PORT}"
volumes:
- carol_data:/home/lnd/.lnd
# Core Lightning node. Unlike the LND nodes it talks to RTL over clnrest (the
# built-in REST plugin) using rune auth, so it needs --clnrest-* options and a
# rune written where RTL can read it. create-rune.sh (run from the healthcheck)
# creates the rune and writes it to /root/.lightning/rtl.rune (RTL mounts that
# read-only); the healthcheck is unhealthy until it exists, so rtl waits for it.
# --clnrest-host=0.0.0.0 is required so the rtl container can reach it; the default
# 127.0.0.1 would only be reachable from inside this container. Protocol stays https
# (clnrest default, self-signed) — RTL connects with rejectUnauthorized:false.
cln:
image: elementsproject/lightningd:v25.09
container_name: ${COMPOSE_PROJECT_NAME}_cln
restart: unless-stopped
depends_on:
- bitcoind
environment:
LIGHTNINGD_NETWORK: regtest
command:
- --alias=cln
- --bitcoin-rpcconnect=${BITCOIN_HOST}
- --bitcoin-rpcport=${BITCOIN_RPC_PORT}
- --bitcoin-rpcuser=${BITCOIN_RPC_USER}
- --bitcoin-rpcpassword=${BITCOIN_RPC_PASSWORD}
- --bitcoin-retry-timeout=3600
- --addr=0.0.0.0:9735
- --announce-addr=cln:9735
- --large-channels
- --clnrest-host=0.0.0.0
- --clnrest-port=3010
ports:
- "${CLN_REST_PORT:-3010}:3010"
volumes:
- cln_data:/root/.lightning
- ./cln/create-rune.sh:/opt/create-rune.sh:ro
healthcheck:
# create-rune.sh ensures the rune exists (idempotent, one quick attempt) and
# this reports healthy only once it does. Driving it from the healthcheck — which
# retries on its interval — means a transient RPC-startup race self-heals instead
# of a one-shot script permanently wedging the stack. rtl waits on this via
# depends_on: condition: service_healthy before reading the rune at startup.
test: ["CMD-SHELL", "sh /opt/create-rune.sh && test -f /root/.lightning/rtl.rune"]
interval: 5s
timeout: 10s
retries: 40
Add an Eclair node to the regtest docker fixture Completes backend coverage of RTL's three implementations in the docker/ dev fixture: an eclair node (polarlightning/eclair 0.13.1) joins the three LND nodes and the CLN node, wired to RTL over its HTTP API with basic auth, and the seed opens an eclair->bob channel (3.5M sats, 1M pushed), sends two direct payments and leaves one open invoice. Non-obvious plumbing this needed: - polarlightning/eclair instead of acinq/eclair: the official image is amd64-only (useless on Apple Silicon) and its newest versioned tag is years stale; Polar builds the same ACINQ source multi-arch. - Eclair has no on-chain wallet of its own -- it drives a bitcoind wallet. A new eclair-wallet-init container creates a dedicated "eclair" wallet before the node starts; without it eclair attaches to "the default loaded wallet", i.e. the rtldev mining wallet. - bitcoind now also publishes a zmqpubhashblock endpoint (28336): eclair's bitcoind.zmqblock consumes the hashblock topic, not the rawblock one LND uses. Wired to rawblock, eclair never sees new blocks and channels hang in WAIT_FOR_FUNDING_CONFIRMED. - Eclair confirms channels at 8 blocks (channel.min-depth-blocks), not 6, and 'open' returns before the funding tx is broadcast -- the seed waits for the mempool and mines 8 blocks for this channel. Adds a bin/e-cli helper (eclair-cli with the API password), updates the README, and verified end-to-end: seed completes, the channel reaches NORMAL, both payments settle, and RTL's /rtl/api/ecl endpoints return the node, channel and invoice data.
2026-07-18 00:23:08 -07:00
# Eclair has no on-chain wallet of its own -- it drives a bitcoind wallet over
# RPC. Without a dedicated wallet it would grab "the default loaded wallet",
# which here is the rtldev mining wallet, and eclair's on-chain balance would
# show the miner's coins. This init container creates (or reloads) a wallet
# named "eclair" before the eclair node starts; load_on_startup survives
# bitcoind restarts.
eclair-wallet-init:
container_name: ${COMPOSE_PROJECT_NAME}_eclair_wallet_init
image: polarlightning/bitcoind:30.0
depends_on:
- bitcoind
entrypoint: ["/bin/sh", "-c"]
command:
- |
bcli() { bitcoin-cli -regtest -rpcconnect=${BITCOIN_HOST} -rpcport=${BITCOIN_RPC_PORT} -rpcuser=${BITCOIN_RPC_USER} -rpcpassword=${BITCOIN_RPC_PASSWORD} "$$@"; }
i=0
until bcli getblockchaininfo >/dev/null 2>&1; do
i=$$((i+1)); [ "$$i" -ge 60 ] && echo "bitcoind never came up" && exit 1
sleep 1
done
bcli -named createwallet wallet_name=eclair load_on_startup=true >/dev/null 2>&1 \
|| bcli loadwallet eclair true >/dev/null 2>&1 \
|| true
bcli -rpcwallet=eclair getwalletinfo >/dev/null
echo "eclair wallet ready"
# Eclair node. Talks to RTL over its HTTP API with basic auth (the
# lnApiPassword in RTL's config). The polarlightning image is used because
# acinq/eclair on Docker Hub is amd64-only and its newest versioned tag is
# years stale; Polar builds the same ACINQ source multi-arch (amd64 + arm64).
# The image entrypoint translates each --key=value into -Declair.key=value.
# The entrypoint also overrides server.public-ips.0 with the container IP, but
# the arg must still be present for that substitution to happen.
eclair:
image: polarlightning/eclair:0.13.1
container_name: ${COMPOSE_PROJECT_NAME}_eclair
restart: unless-stopped
depends_on:
bitcoind:
condition: service_started
eclair-wallet-init:
condition: service_completed_successfully
command:
- polar-eclair
- --node-alias=eclair
- --server.public-ips.0=eclair
- --server.port=9735
- --api.enabled=true
- --api.binding-ip=0.0.0.0
- --api.port=8080
- --api.password=${ECLAIR_API_PASSWORD:-rtldev}
- --chain=regtest
- --bitcoind.host=${BITCOIN_HOST}
- --bitcoind.rpcport=${BITCOIN_RPC_PORT}
- --bitcoind.rpcuser=${BITCOIN_RPC_USER}
- --bitcoind.rpcpassword=${BITCOIN_RPC_PASSWORD}
# zmqblock must be bitcoind's *hashblock* endpoint (eclair subscribes to
# that topic); pointing it at the rawblock endpoint the LND nodes use
# leaves eclair blind to new blocks.
- --bitcoind.zmqblock=tcp://${BITCOIN_HOST}:${BITCOIN_ZMQ_HASHBLOCK_PORT:-28336}
- --bitcoind.zmqtx=tcp://${BITCOIN_HOST}:${BITCOIN_ZMQ_TX_PORT}
- --bitcoind.wallet=eclair
- --datadir=/home/eclair/.eclair
- --printToConsole=true
# Regtest feerates are far from mainnet estimates; without a wide
# tolerance eclair closes channels over feerate disagreements.
- --on-chain-fees.feerate-tolerance.ratio-low=0.00001
- --on-chain-fees.feerate-tolerance.ratio-high=10000.0
ports:
- "${ECLAIR_REST_PORT:-8281}:8080"
volumes:
- eclair_data:/home/eclair
Rebuild the regtest dev fixture: bitcoind + 3 LND nodes + RTL The setup in docker/ has been unable to start since Feb 2021. The boltz service added in f817ae39 references BOLTZ_* variables that are not in .env (so its ports render as ":" and compose rejects the file with "invalid proto"), a ./boltz build context that has never existed in the repo, and boltz_data/boltz_shared volumes that are never declared. Because compose validates the whole project up front, this broke every service: "docker compose up -d bitcoind", the first command in the README, failed too. Rather than repair a five-year-old file pinned to bitcoind 0.19.0 and lnd 0.12.0-beta, this replaces it. What changed: - bitcoind 30.0 and 3x lnd 0.20.0-beta, using Polar's images. They are multi-arch, so nothing is built locally and this works on arm64. The old setup built bitcoind and lnd from local Dockerfiles. - Three nodes, not one: alice -> bob -> carol. bob forwards, so RTL's routing and forwarding screens have data. Two nodes leave them empty. - scripts/seed.sh funds the nodes, opens channels, and makes payments with fixed amounts. A fresh run reproduces identical state, so screenshots taken before and after a change differ only by the change. It is deliberately not idempotent and refuses to run against a seeded network, since re-running would double-fund it. - rtl/RTL-Config.regtest.json configures all three nodes. RTL rewrites its config on startup, so an init container stages a copy into a volume: a read-only bind mount makes RTL exit with EROFS, and a writable one would let RTL modify a tracked file. It is not named RTL-Config.json because .gitignore matches that bare name at any depth. - bin/ln-cli now takes a node name and passes --lnddir=/home/lnd/.lnd, because 'docker compose exec' lands as root while lnd's datadir is under /home/lnd. Both helpers use compose v2. - README rewritten to match. Boltz, Core Lightning and Eclair are left out of this pass. Polar publishes multi-arch clightning and eclair images, so adding them later needs compose services, config entries and seed adapters, but no image building. Verified from a clean 'down -v': all nodes sync, channels go active, 5/5 payments route through bob, bob records 5 forwards, and RTL serves the UI with all three nodes configured. Two independent from-scratch runs produced identical balances. The old bitcoind/ and lnd/ build contexts are now unreferenced but left in place for a follow-up.
2026-07-15 23:09:03 -07:00
# RTL rewrites its config file on startup, so it cannot be given the tracked
# template directly: a bind mount would either be read-only (RTL exits with
# EROFS) or would let RTL scribble into a version-controlled file. Instead the
# template is copied into a volume that 'down -v' discards, which keeps the
# source pristine and every run starting from identical config.
rtl-config-init:
container_name: ${COMPOSE_PROJECT_NAME}_rtl_config_init
image: busybox:1.36
command: >
sh -c "cp /template/RTL-Config.regtest.json /config/RTL-Config.json &&
chmod 644 /config/RTL-Config.json &&
echo 'config staged'"
volumes:
- ./rtl/RTL-Config.regtest.json:/template/RTL-Config.regtest.json:ro
- rtl_config:/config
rtl:
container_name: ${COMPOSE_PROJECT_NAME}_rtl
# Defaults to the published image; override with RTL_IMAGE (e.g. a locally built
# branch image) to test unreleased changes: RTL_IMAGE=rtl:pr1625 docker compose up.
Add a BTCPay Server SSO harness to the docker fixture (#1669) * Add a BTCPay Server SSO harness to the docker fixture BTCPay bundles RTL and runs it in single-sign-on mode, reached over an entry path the standalone login never exercises: no password, a rotating cookie file, an unregistered /rtl/api/authenticate/cookie URL that falls through to the catch-all in server/utils/app.ts, and a reverse proxy in front. Regressions on that path have previously gone unnoticed until they reached BTCPay users. Adds an "sso" compose profile, so a plain `docker compose up -d` is unchanged: - rtl-sso, a second RTL running with RTL_SSO=1, RTL_COOKIE_PATH and LOGOUT_REDIRECT_LINK -- the environment block lifted verbatim from BTCPay's own compose fragment, so this exercises the env-driven SSO path BTCPay actually uses. A second container is required because RTL selects one authentication mode at startup, so SSO and password login cannot coexist in one instance. - rtl-sso-config-init, staging rtl/RTL-Config.sso.json into a volume -- the same copy-into-a-volume dance the standalone RTL already needs, because RTL rewrites its config on startup. - rtl-sso-proxy, nginx standing in for BTCPay's traefik, routing only /rtl and /rtl/* exactly as BTCPay's router rule does. There is no prefix stripping anywhere: RTL is built with <base href="/rtl/"> and mounts every route under baseHref '/rtl', so the prefix is passed through unmodified. Everything outside /rtl 404s, so a request escaping the prefix surfaces as a failure rather than being quietly served. scripts/verify-sso.sh asserts the whole flow in 11 checks -- prefix routing, CSRF token minting on the catch-all, the sha256 access-key handshake, an authenticated node call, cookie rotation on login, and rejection of a wrong key -- and exits non-zero so it can gate a change. bin/sso-url prints the link BTCPay renders on its Services page. RTL_IMAGE overrides both RTL containers at once, so a branch build gets tested through both entry paths. BTCPay itself (postgres, nbxplorer, btcpayserver) is deliberately not included; the README documents what that leaves untested and how to run against BTCPay's own regtest stack when the question is BTCPay's behaviour rather than RTL's. Also bumps the fixture's default RTL image from v0.15.8 to v0.15.10. * Document the SSO harness in the rtl-docker-fixture skill * Point CLAUDE.md at the BTCPay SSO harness * Note that no CI runs on an open PR
2026-08-04 18:17:19 -07:00
image: ${RTL_IMAGE:-shahanafarooqui/rtl:v0.15.10}
restart: unless-stopped
depends_on:
Rebuild the regtest dev fixture: bitcoind + 3 LND nodes + RTL The setup in docker/ has been unable to start since Feb 2021. The boltz service added in f817ae39 references BOLTZ_* variables that are not in .env (so its ports render as ":" and compose rejects the file with "invalid proto"), a ./boltz build context that has never existed in the repo, and boltz_data/boltz_shared volumes that are never declared. Because compose validates the whole project up front, this broke every service: "docker compose up -d bitcoind", the first command in the README, failed too. Rather than repair a five-year-old file pinned to bitcoind 0.19.0 and lnd 0.12.0-beta, this replaces it. What changed: - bitcoind 30.0 and 3x lnd 0.20.0-beta, using Polar's images. They are multi-arch, so nothing is built locally and this works on arm64. The old setup built bitcoind and lnd from local Dockerfiles. - Three nodes, not one: alice -> bob -> carol. bob forwards, so RTL's routing and forwarding screens have data. Two nodes leave them empty. - scripts/seed.sh funds the nodes, opens channels, and makes payments with fixed amounts. A fresh run reproduces identical state, so screenshots taken before and after a change differ only by the change. It is deliberately not idempotent and refuses to run against a seeded network, since re-running would double-fund it. - rtl/RTL-Config.regtest.json configures all three nodes. RTL rewrites its config on startup, so an init container stages a copy into a volume: a read-only bind mount makes RTL exit with EROFS, and a writable one would let RTL modify a tracked file. It is not named RTL-Config.json because .gitignore matches that bare name at any depth. - bin/ln-cli now takes a node name and passes --lnddir=/home/lnd/.lnd, because 'docker compose exec' lands as root while lnd's datadir is under /home/lnd. Both helpers use compose v2. - README rewritten to match. Boltz, Core Lightning and Eclair are left out of this pass. Polar publishes multi-arch clightning and eclair images, so adding them later needs compose services, config entries and seed adapters, but no image building. Verified from a clean 'down -v': all nodes sync, channels go active, 5/5 payments route through bob, bob records 5 forwards, and RTL serves the UI with all three nodes configured. Two independent from-scratch runs produced identical balances. The old bitcoind/ and lnd/ build contexts are now unreferenced but left in place for a follow-up.
2026-07-15 23:09:03 -07:00
rtl-config-init:
condition: service_completed_successfully
alice:
condition: service_started
bob:
condition: service_started
carol:
condition: service_started
cln:
condition: service_healthy
Add an Eclair node to the regtest docker fixture Completes backend coverage of RTL's three implementations in the docker/ dev fixture: an eclair node (polarlightning/eclair 0.13.1) joins the three LND nodes and the CLN node, wired to RTL over its HTTP API with basic auth, and the seed opens an eclair->bob channel (3.5M sats, 1M pushed), sends two direct payments and leaves one open invoice. Non-obvious plumbing this needed: - polarlightning/eclair instead of acinq/eclair: the official image is amd64-only (useless on Apple Silicon) and its newest versioned tag is years stale; Polar builds the same ACINQ source multi-arch. - Eclair has no on-chain wallet of its own -- it drives a bitcoind wallet. A new eclair-wallet-init container creates a dedicated "eclair" wallet before the node starts; without it eclair attaches to "the default loaded wallet", i.e. the rtldev mining wallet. - bitcoind now also publishes a zmqpubhashblock endpoint (28336): eclair's bitcoind.zmqblock consumes the hashblock topic, not the rawblock one LND uses. Wired to rawblock, eclair never sees new blocks and channels hang in WAIT_FOR_FUNDING_CONFIRMED. - Eclair confirms channels at 8 blocks (channel.min-depth-blocks), not 6, and 'open' returns before the funding tx is broadcast -- the seed waits for the mempool and mines 8 blocks for this channel. Adds a bin/e-cli helper (eclair-cli with the API password), updates the README, and verified end-to-end: seed completes, the channel reaches NORMAL, both payments settle, and RTL's /rtl/api/ecl endpoints return the node, channel and invoice data.
2026-07-18 00:23:08 -07:00
eclair:
condition: service_started
ports:
- "${RTL_PORT}:${RTL_PORT}"
environment:
Rebuild the regtest dev fixture: bitcoind + 3 LND nodes + RTL The setup in docker/ has been unable to start since Feb 2021. The boltz service added in f817ae39 references BOLTZ_* variables that are not in .env (so its ports render as ":" and compose rejects the file with "invalid proto"), a ./boltz build context that has never existed in the repo, and boltz_data/boltz_shared volumes that are never declared. Because compose validates the whole project up front, this broke every service: "docker compose up -d bitcoind", the first command in the README, failed too. Rather than repair a five-year-old file pinned to bitcoind 0.19.0 and lnd 0.12.0-beta, this replaces it. What changed: - bitcoind 30.0 and 3x lnd 0.20.0-beta, using Polar's images. They are multi-arch, so nothing is built locally and this works on arm64. The old setup built bitcoind and lnd from local Dockerfiles. - Three nodes, not one: alice -> bob -> carol. bob forwards, so RTL's routing and forwarding screens have data. Two nodes leave them empty. - scripts/seed.sh funds the nodes, opens channels, and makes payments with fixed amounts. A fresh run reproduces identical state, so screenshots taken before and after a change differ only by the change. It is deliberately not idempotent and refuses to run against a seeded network, since re-running would double-fund it. - rtl/RTL-Config.regtest.json configures all three nodes. RTL rewrites its config on startup, so an init container stages a copy into a volume: a read-only bind mount makes RTL exit with EROFS, and a writable one would let RTL modify a tracked file. It is not named RTL-Config.json because .gitignore matches that bare name at any depth. - bin/ln-cli now takes a node name and passes --lnddir=/home/lnd/.lnd, because 'docker compose exec' lands as root while lnd's datadir is under /home/lnd. Both helpers use compose v2. - README rewritten to match. Boltz, Core Lightning and Eclair are left out of this pass. Polar publishes multi-arch clightning and eclair images, so adding them later needs compose services, config entries and seed adapters, but no image building. Verified from a clean 'down -v': all nodes sync, channels go active, 5/5 payments route through bob, bob records 5 forwards, and RTL serves the UI with all three nodes configured. Two independent from-scratch runs produced identical balances. The old bitcoind/ and lnd/ build contexts are now unreferenced but left in place for a follow-up.
2026-07-15 23:09:03 -07:00
RTL_CONFIG_PATH: /RTL/config
volumes:
- rtl_config:/RTL/config
- alice_data:/lnd/alice:ro
- bob_data:/lnd/bob:ro
- carol_data:/lnd/carol:ro
- cln_data:/cln:ro
Rebuild the regtest dev fixture: bitcoind + 3 LND nodes + RTL The setup in docker/ has been unable to start since Feb 2021. The boltz service added in f817ae39 references BOLTZ_* variables that are not in .env (so its ports render as ":" and compose rejects the file with "invalid proto"), a ./boltz build context that has never existed in the repo, and boltz_data/boltz_shared volumes that are never declared. Because compose validates the whole project up front, this broke every service: "docker compose up -d bitcoind", the first command in the README, failed too. Rather than repair a five-year-old file pinned to bitcoind 0.19.0 and lnd 0.12.0-beta, this replaces it. What changed: - bitcoind 30.0 and 3x lnd 0.20.0-beta, using Polar's images. They are multi-arch, so nothing is built locally and this works on arm64. The old setup built bitcoind and lnd from local Dockerfiles. - Three nodes, not one: alice -> bob -> carol. bob forwards, so RTL's routing and forwarding screens have data. Two nodes leave them empty. - scripts/seed.sh funds the nodes, opens channels, and makes payments with fixed amounts. A fresh run reproduces identical state, so screenshots taken before and after a change differ only by the change. It is deliberately not idempotent and refuses to run against a seeded network, since re-running would double-fund it. - rtl/RTL-Config.regtest.json configures all three nodes. RTL rewrites its config on startup, so an init container stages a copy into a volume: a read-only bind mount makes RTL exit with EROFS, and a writable one would let RTL modify a tracked file. It is not named RTL-Config.json because .gitignore matches that bare name at any depth. - bin/ln-cli now takes a node name and passes --lnddir=/home/lnd/.lnd, because 'docker compose exec' lands as root while lnd's datadir is under /home/lnd. Both helpers use compose v2. - README rewritten to match. Boltz, Core Lightning and Eclair are left out of this pass. Polar publishes multi-arch clightning and eclair images, so adding them later needs compose services, config entries and seed adapters, but no image building. Verified from a clean 'down -v': all nodes sync, channels go active, 5/5 payments route through bob, bob records 5 forwards, and RTL serves the UI with all three nodes configured. Two independent from-scratch runs produced identical balances. The old bitcoind/ and lnd/ build contexts are now unreferenced but left in place for a follow-up.
2026-07-15 23:09:03 -07:00
- rtl_db:/RTL/database
Add a BTCPay Server SSO harness to the docker fixture (#1669) * Add a BTCPay Server SSO harness to the docker fixture BTCPay bundles RTL and runs it in single-sign-on mode, reached over an entry path the standalone login never exercises: no password, a rotating cookie file, an unregistered /rtl/api/authenticate/cookie URL that falls through to the catch-all in server/utils/app.ts, and a reverse proxy in front. Regressions on that path have previously gone unnoticed until they reached BTCPay users. Adds an "sso" compose profile, so a plain `docker compose up -d` is unchanged: - rtl-sso, a second RTL running with RTL_SSO=1, RTL_COOKIE_PATH and LOGOUT_REDIRECT_LINK -- the environment block lifted verbatim from BTCPay's own compose fragment, so this exercises the env-driven SSO path BTCPay actually uses. A second container is required because RTL selects one authentication mode at startup, so SSO and password login cannot coexist in one instance. - rtl-sso-config-init, staging rtl/RTL-Config.sso.json into a volume -- the same copy-into-a-volume dance the standalone RTL already needs, because RTL rewrites its config on startup. - rtl-sso-proxy, nginx standing in for BTCPay's traefik, routing only /rtl and /rtl/* exactly as BTCPay's router rule does. There is no prefix stripping anywhere: RTL is built with <base href="/rtl/"> and mounts every route under baseHref '/rtl', so the prefix is passed through unmodified. Everything outside /rtl 404s, so a request escaping the prefix surfaces as a failure rather than being quietly served. scripts/verify-sso.sh asserts the whole flow in 11 checks -- prefix routing, CSRF token minting on the catch-all, the sha256 access-key handshake, an authenticated node call, cookie rotation on login, and rejection of a wrong key -- and exits non-zero so it can gate a change. bin/sso-url prints the link BTCPay renders on its Services page. RTL_IMAGE overrides both RTL containers at once, so a branch build gets tested through both entry paths. BTCPay itself (postgres, nbxplorer, btcpayserver) is deliberately not included; the README documents what that leaves untested and how to run against BTCPay's own regtest stack when the question is BTCPay's behaviour rather than RTL's. Also bumps the fixture's default RTL image from v0.15.8 to v0.15.10. * Document the SSO harness in the rtl-docker-fixture skill * Point CLAUDE.md at the BTCPay SSO harness * Note that no CI runs on an open PR
2026-08-04 18:17:19 -07:00
# ---------------------------------------------------------------------------
# BTCPay Server SSO harness -- profile "sso", so a plain 'up' does not start it:
#
# docker compose --profile sso up -d
# bin/sso-url
#
# BTCPay bundles RTL as a service and runs it in single-sign-on mode. RTL
# writes a random cookie to RTL_COOKIE_PATH; BTCPay reads that file and renders
# a link to /rtl/api/authenticate/cookie?access-key=<cookie> on its Services
# page. That path is not a registered route -- it falls through to RTL's
# catch-all, which mints the XSRF-TOKEN cookie and serves index.html, and the
# SPA then reads access-key from the query string and posts it as a password
# login. Reproducing that entry path is the whole point of this harness: it is
# where RTL's auth, CSRF and static-serving behaviour meet an external caller,
# and it has broken before without the standalone flow noticing.
#
# BTCPay itself (postgres + nbxplorer + btcpayserver) is deliberately not here.
# See README.md, "BTCPay SSO harness", for what that leaves untested and how to
# run against a real BTCPay when it matters.
# ---------------------------------------------------------------------------
# Same copy-into-a-volume dance as rtl-config-init above, and for the same
# reason: RTL rewrites its config on startup.
rtl-sso-config-init:
container_name: ${COMPOSE_PROJECT_NAME}_rtl_sso_config_init
profiles: ["sso"]
image: busybox:1.36
command: >
sh -c "cp /template/RTL-Config.sso.json /config/RTL-Config.json &&
chmod 644 /config/RTL-Config.json &&
echo 'sso config staged'"
volumes:
- ./rtl/RTL-Config.sso.json:/template/RTL-Config.sso.json:ro
- rtl_sso_config:/config
# A second RTL rather than a flag on the first: RTL picks one authentication
# mode at startup, so SSO and the password login cannot coexist in one
# instance. Only alice is wired up, matching BTCPay's one-node-per-RTL layout.
#
# The environment block is copied from BTCPay's own compose fragment
# (docker-compose-generator/docker-fragments/bitcoin-lnd.yml in
# btcpayserver-docker), so this exercises the env-driven SSO path BTCPay
# actually uses rather than the config-file equivalent -- which is why
# RTL-Config.sso.json leaves its SSO block zeroed and carries no multiPass.
#
# Only 'expose'd, never published: all access goes through the proxy, as it
# does under BTCPay.
rtl-sso:
container_name: ${COMPOSE_PROJECT_NAME}_rtl_sso
profiles: ["sso"]
image: ${RTL_IMAGE:-shahanafarooqui/rtl:v0.15.10}
restart: unless-stopped
depends_on:
rtl-sso-config-init:
condition: service_completed_successfully
alice:
condition: service_started
environment:
RTL_CONFIG_PATH: /RTL/config
RTL_SSO: 1
RTL_COOKIE_PATH: /RTL/cookie/.cookie
LOGOUT_REDIRECT_LINK: /server/services
expose:
- "3000"
volumes:
- rtl_sso_config:/RTL/config
- rtl_sso_cookie:/RTL/cookie
- alice_data:/lnd/alice:ro
- rtl_sso_db:/RTL/database
# Stands in for BTCPay's traefik. Routes only /rtl and /rtl/*, mirroring the
# label BTCPay puts on its RTL container; see nginx/rtl-sso.conf.
rtl-sso-proxy:
container_name: ${COMPOSE_PROJECT_NAME}_rtl_sso_proxy
profiles: ["sso"]
image: nginx:1.27-alpine
restart: unless-stopped
depends_on:
- rtl-sso
ports:
- "${RTL_SSO_PORT:-3001}:80"
volumes:
- ./nginx/rtl-sso.conf:/etc/nginx/conf.d/default.conf:ro