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.
|
2019-04-15 00:00:20 +01:00
|
|
|
|
|
|
|
|
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:
|
Add a Core Lightning node to the regtest docker fixture
The fixture had only bitcoind + three LND nodes, so RTL's Core Lightning screens
had no backend to exercise. Add a `cln` node (official elementsproject/lightningd
image, multi-arch) wired to RTL over clnrest with rune auth, and have the seed
open a cln->alice channel so the CLN channel/peer screens have real data.
- docker-compose.yml: cln service (clnrest on 0.0.0.0:3010, https), a healthcheck
gated on the rune file so rtl waits for it, and rtl now mounts the cln volume
read-only and depends on cln being healthy. The rtl image is parameterized via
${RTL_IMAGE:-...} so an unreleased build can be tested against the fixture.
- cln/poststart.d/create-rune.sh: once the RPC is up, create a master rune and
write it as LIGHTNING_RUNE="..." where RTL reads it (runePath). Polls for RPC
readiness because the image entrypoint can invoke poststart before the socket
exists.
- RTL-Config.regtest.json: add node index 4 (CLN, rune auth, https://cln:3010).
- seed.sh: fund cln, connect to alice, open a 4,000,000 sat channel, wait active.
- README + release notes updated.
Used to verify the CLN channel connection-status fix (#1606) end-to-end.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-17 10:03:34 -07:00
|
|
|
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
|
2019-04-15 00:00:20 +01:00
|
|
|
|
|
|
|
|
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
|
2019-04-15 00:00:20 +01:00
|
|
|
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}"
|
2019-04-15 00:00:20 +01:00
|
|
|
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
|
2019-04-15 00:00:20 +01:00
|
|
|
|
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
|
2019-04-15 00:00:20 +01:00
|
|
|
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}"
|
2019-04-15 00:00:20 +01:00
|
|
|
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
|
2019-04-15 00:00:20 +01:00
|
|
|
|
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
|
2021-02-21 14:02:31 -05:00
|
|
|
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}"
|
2021-02-21 14:02:31 -05:00
|
|
|
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
|
|
|
|
|
|
Add a Core Lightning node to the regtest docker fixture
The fixture had only bitcoind + three LND nodes, so RTL's Core Lightning screens
had no backend to exercise. Add a `cln` node (official elementsproject/lightningd
image, multi-arch) wired to RTL over clnrest with rune auth, and have the seed
open a cln->alice channel so the CLN channel/peer screens have real data.
- docker-compose.yml: cln service (clnrest on 0.0.0.0:3010, https), a healthcheck
gated on the rune file so rtl waits for it, and rtl now mounts the cln volume
read-only and depends on cln being healthy. The rtl image is parameterized via
${RTL_IMAGE:-...} so an unreleased build can be tested against the fixture.
- cln/poststart.d/create-rune.sh: once the RPC is up, create a master rune and
write it as LIGHTNING_RUNE="..." where RTL reads it (runePath). Polls for RPC
readiness because the image entrypoint can invoke poststart before the socket
exists.
- RTL-Config.regtest.json: add node index 4 (CLN, rune auth, https://cln:3010).
- seed.sh: fund cln, connect to alice, open a 4,000,000 sat channel, wait active.
- README + release notes updated.
Used to verify the CLN channel connection-status fix (#1606) end-to-end.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-17 10:03:34 -07:00
|
|
|
# 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
|
2026-07-17 11:54:55 -07:00
|
|
|
# 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.
|
Add a Core Lightning node to the regtest docker fixture
The fixture had only bitcoind + three LND nodes, so RTL's Core Lightning screens
had no backend to exercise. Add a `cln` node (official elementsproject/lightningd
image, multi-arch) wired to RTL over clnrest with rune auth, and have the seed
open a cln->alice channel so the CLN channel/peer screens have real data.
- docker-compose.yml: cln service (clnrest on 0.0.0.0:3010, https), a healthcheck
gated on the rune file so rtl waits for it, and rtl now mounts the cln volume
read-only and depends on cln being healthy. The rtl image is parameterized via
${RTL_IMAGE:-...} so an unreleased build can be tested against the fixture.
- cln/poststart.d/create-rune.sh: once the RPC is up, create a master rune and
write it as LIGHTNING_RUNE="..." where RTL reads it (runePath). Polls for RPC
readiness because the image entrypoint can invoke poststart before the socket
exists.
- RTL-Config.regtest.json: add node index 4 (CLN, rune auth, https://cln:3010).
- seed.sh: fund cln, connect to alice, open a 4,000,000 sat channel, wait active.
- README + release notes updated.
Used to verify the CLN channel connection-status fix (#1606) end-to-end.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-17 10:03:34 -07:00
|
|
|
# --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
|
2026-07-17 11:54:55 -07:00
|
|
|
- ./cln/create-rune.sh:/opt/create-rune.sh:ro
|
Add a Core Lightning node to the regtest docker fixture
The fixture had only bitcoind + three LND nodes, so RTL's Core Lightning screens
had no backend to exercise. Add a `cln` node (official elementsproject/lightningd
image, multi-arch) wired to RTL over clnrest with rune auth, and have the seed
open a cln->alice channel so the CLN channel/peer screens have real data.
- docker-compose.yml: cln service (clnrest on 0.0.0.0:3010, https), a healthcheck
gated on the rune file so rtl waits for it, and rtl now mounts the cln volume
read-only and depends on cln being healthy. The rtl image is parameterized via
${RTL_IMAGE:-...} so an unreleased build can be tested against the fixture.
- cln/poststart.d/create-rune.sh: once the RPC is up, create a master rune and
write it as LIGHTNING_RUNE="..." where RTL reads it (runePath). Polls for RPC
readiness because the image entrypoint can invoke poststart before the socket
exists.
- RTL-Config.regtest.json: add node index 4 (CLN, rune auth, https://cln:3010).
- seed.sh: fund cln, connect to alice, open a 4,000,000 sat channel, wait active.
- README + release notes updated.
Used to verify the CLN channel connection-status fix (#1606) end-to-end.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-17 10:03:34 -07:00
|
|
|
healthcheck:
|
2026-07-17 11:54:55 -07:00
|
|
|
# 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"]
|
Add a Core Lightning node to the regtest docker fixture
The fixture had only bitcoind + three LND nodes, so RTL's Core Lightning screens
had no backend to exercise. Add a `cln` node (official elementsproject/lightningd
image, multi-arch) wired to RTL over clnrest with rune auth, and have the seed
open a cln->alice channel so the CLN channel/peer screens have real data.
- docker-compose.yml: cln service (clnrest on 0.0.0.0:3010, https), a healthcheck
gated on the rune file so rtl waits for it, and rtl now mounts the cln volume
read-only and depends on cln being healthy. The rtl image is parameterized via
${RTL_IMAGE:-...} so an unreleased build can be tested against the fixture.
- cln/poststart.d/create-rune.sh: once the RPC is up, create a master rune and
write it as LIGHTNING_RUNE="..." where RTL reads it (runePath). Polls for RPC
readiness because the image entrypoint can invoke poststart before the socket
exists.
- RTL-Config.regtest.json: add node index 4 (CLN, rune auth, https://cln:3010).
- seed.sh: fund cln, connect to alice, open a 4,000,000 sat channel, wait active.
- README + release notes updated.
Used to verify the CLN channel connection-status fix (#1606) end-to-end.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-17 10:03:34 -07:00
|
|
|
interval: 5s
|
2026-07-17 11:54:55 -07:00
|
|
|
timeout: 10s
|
Add a Core Lightning node to the regtest docker fixture
The fixture had only bitcoind + three LND nodes, so RTL's Core Lightning screens
had no backend to exercise. Add a `cln` node (official elementsproject/lightningd
image, multi-arch) wired to RTL over clnrest with rune auth, and have the seed
open a cln->alice channel so the CLN channel/peer screens have real data.
- docker-compose.yml: cln service (clnrest on 0.0.0.0:3010, https), a healthcheck
gated on the rune file so rtl waits for it, and rtl now mounts the cln volume
read-only and depends on cln being healthy. The rtl image is parameterized via
${RTL_IMAGE:-...} so an unreleased build can be tested against the fixture.
- cln/poststart.d/create-rune.sh: once the RPC is up, create a master rune and
write it as LIGHTNING_RUNE="..." where RTL reads it (runePath). Polls for RPC
readiness because the image entrypoint can invoke poststart before the socket
exists.
- RTL-Config.regtest.json: add node index 4 (CLN, rune auth, https://cln:3010).
- seed.sh: fund cln, connect to alice, open a 4,000,000 sat channel, wait active.
- README + release notes updated.
Used to verify the CLN channel connection-status fix (#1606) end-to-end.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-17 10:03:34 -07:00
|
|
|
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
|
|
|
|
|
|
2019-04-15 00:00:20 +01:00
|
|
|
rtl:
|
|
|
|
|
container_name: ${COMPOSE_PROJECT_NAME}_rtl
|
Add a Core Lightning node to the regtest docker fixture
The fixture had only bitcoind + three LND nodes, so RTL's Core Lightning screens
had no backend to exercise. Add a `cln` node (official elementsproject/lightningd
image, multi-arch) wired to RTL over clnrest with rune auth, and have the seed
open a cln->alice channel so the CLN channel/peer screens have real data.
- docker-compose.yml: cln service (clnrest on 0.0.0.0:3010, https), a healthcheck
gated on the rune file so rtl waits for it, and rtl now mounts the cln volume
read-only and depends on cln being healthy. The rtl image is parameterized via
${RTL_IMAGE:-...} so an unreleased build can be tested against the fixture.
- cln/poststart.d/create-rune.sh: once the RPC is up, create a master rune and
write it as LIGHTNING_RUNE="..." where RTL reads it (runePath). Polls for RPC
readiness because the image entrypoint can invoke poststart before the socket
exists.
- RTL-Config.regtest.json: add node index 4 (CLN, rune auth, https://cln:3010).
- seed.sh: fund cln, connect to alice, open a 4,000,000 sat channel, wait active.
- README + release notes updated.
Used to verify the CLN channel connection-status fix (#1606) end-to-end.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-17 10:03:34 -07:00
|
|
|
# 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}
|
2019-04-15 00:00:20 +01:00
|
|
|
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
|
Add a Core Lightning node to the regtest docker fixture
The fixture had only bitcoind + three LND nodes, so RTL's Core Lightning screens
had no backend to exercise. Add a `cln` node (official elementsproject/lightningd
image, multi-arch) wired to RTL over clnrest with rune auth, and have the seed
open a cln->alice channel so the CLN channel/peer screens have real data.
- docker-compose.yml: cln service (clnrest on 0.0.0.0:3010, https), a healthcheck
gated on the rune file so rtl waits for it, and rtl now mounts the cln volume
read-only and depends on cln being healthy. The rtl image is parameterized via
${RTL_IMAGE:-...} so an unreleased build can be tested against the fixture.
- cln/poststart.d/create-rune.sh: once the RPC is up, create a master rune and
write it as LIGHTNING_RUNE="..." where RTL reads it (runePath). Polls for RPC
readiness because the image entrypoint can invoke poststart before the socket
exists.
- RTL-Config.regtest.json: add node index 4 (CLN, rune auth, https://cln:3010).
- seed.sh: fund cln, connect to alice, open a 4,000,000 sat channel, wait active.
- README + release notes updated.
Used to verify the CLN channel connection-status fix (#1606) end-to-end.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-17 10:03:34 -07:00
|
|
|
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
|
2019-04-15 00:00:20 +01:00
|
|
|
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
|
Add a Core Lightning node to the regtest docker fixture
The fixture had only bitcoind + three LND nodes, so RTL's Core Lightning screens
had no backend to exercise. Add a `cln` node (official elementsproject/lightningd
image, multi-arch) wired to RTL over clnrest with rune auth, and have the seed
open a cln->alice channel so the CLN channel/peer screens have real data.
- docker-compose.yml: cln service (clnrest on 0.0.0.0:3010, https), a healthcheck
gated on the rune file so rtl waits for it, and rtl now mounts the cln volume
read-only and depends on cln being healthy. The rtl image is parameterized via
${RTL_IMAGE:-...} so an unreleased build can be tested against the fixture.
- cln/poststart.d/create-rune.sh: once the RPC is up, create a master rune and
write it as LIGHTNING_RUNE="..." where RTL reads it (runePath). Polls for RPC
readiness because the image entrypoint can invoke poststart before the socket
exists.
- RTL-Config.regtest.json: add node index 4 (CLN, rune auth, https://cln:3010).
- seed.sh: fund cln, connect to alice, open a 4,000,000 sat channel, wait active.
- README + release notes updated.
Used to verify the CLN channel connection-status fix (#1606) end-to-end.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-17 10:03:34 -07:00
|
|
|
- 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
|