* Update version 0.15.10
* Update project dependencies to resolve Dependabot security alerts
Applies the fixes from the open Dependabot PRs (#1648, #1649, #1650) in a
single pass on the release branch, regenerating the lockfile from scratch.
axios 1.16.0 -> 1.18.1 was the only production exposure (10 advisories).
Transitive deps moved to their fixed in-range versions (fast-uri 3.1.4,
form-data, qs, tough-cookie, tar, del, globby); dev toolchain took safe
bumps (nodemon 3.1.14, eslint 9.39.5, @typescript-eslint 8.65.0).
Drops the unused protractor devDependency: no e2e directory, no config and
no e2e target in angular.json, but 100 packages and the deprecated request
stack behind it. That clears both critical advisories.
npm audit: 50 (2 critical) -> 29 (0 critical); production deps 1 -> 0.
Remaining findings are dev-only tooling needing an Angular 21 migration
rather than a version bump.
Verified: lint, 204 frontend specs, backend + frontend production builds,
and 19 API checks against the docker regtest fixture covering LND, Core
Lightning and Eclair (getinfo, channels, peers, invoices, payments and
forwarding history).
* Fill in PR number in release note (#1653)
* Harden login request validation (#1654)
Tightens server-side validation of authentication requests, guards the password-reset route behind an authenticated session, and wires the backend regression suite (test/backend/) into npm run test. Users with two-factor authentication enabled are encouraged to update promptly.
Verified: backend specs 12/12, lint green, frontend specs 204/204, and the full authentication matrix end-to-end on the docker regtest fixture.
* Reduce exposure of authentication secrets in logs and config responses (#1659)
* Reduce exposure of authentication secrets in logs and config responses
* Fill in PR number in release note (#1659)
* Harden redaction helpers and secret restore paths
* Pin deployment auth switches server-side and harden settings persistence
* Contain backup file reads and harden config persistence
* Pin backup containment root and preserve config file mode on save
* Update Angular framework packages to 20.3.27 (#1661)
* Update Angular framework packages to 20.3.27
Batches the three Dependabot PRs open against master for the Angular framework
(@angular/core #1658, @angular/compiler #1657, @angular/common #1655) into one
update on the release branch. The framework packages are pinned to exact
versions and their peer ranges require them to move together, so all nine
20.3.26 packages go to 20.3.27: animations, common, compiler, compiler-cli,
core, forms, platform-browser, platform-browser-dynamic and router.
Patch-level upstream fixes only, no advisories. The update stays inside Angular
20 - @angular/build and @angular/cli (20.3.32) and @angular/cdk/@angular/material
(20.2.14) are already at the top of their v20 lines - so it does not pull in the
Angular 21 migration tracked by #1650.
Rebuilt frontend/ for the new framework code. backend/ is unchanged, as no
server/ source moved.
* Fill in PR number in release note (#1661)
* Bound remaining unbounded alias-resolution fan-outs in LND graph.ts and channels.ts Fixes#1630 (#1651)
* Bound remaining unbounded alias-resolution fan-outs in LND graph.ts and channels.ts
Fixes#1630
* Address review feedback: fix options race, error handling, release notes
* Improve release notes entry to cover full PR scope
* Address review feedback: per-task options copy, exclude qs from alias requests
* Stop logging the eclair auth header at DEBUG level (#1664)
* Stop logging the eclair auth header at DEBUG level
getChannels in the eclair channels controller logged its whole request
options object. Eclair authenticates with HTTP basic auth, so those
options carry the configured lnApiPassword in an authorization header -
raising an eclair node's logLevel to DEBUG wrote
"authorization":"Basic <base64>" into the node log file, which is a
recoverable form of the credential and is routinely shared when
debugging.
The log now carries only the request url and form, matching every other
DEBUG log in the controllers. This was the only site in server/ passing a
whole options object to the logger; the rest log options.form, .url,
.body or .qs, none of which hold credentials.
Present since 0.12.0 and only reachable by opting in to DEBUG (the
default log level is ERROR), but it contradicted the logging guarantee
stated for #1659.
Found by scanning node logs at DEBUG while verifying the 0.15.10 branch
against the regtest fixture. Regression test added in
test/backend/eclair-channels.test.mjs; it fails on the previous code with
"auth header key must not reach the node log".
* Fill in PR number in release note (#1664)
---------
Co-authored-by: Osuji <weezdomosuji@gmail.com>
The singular outgoing_chan_id query parameter on LND's QueryRoutes is
deprecated as of lnd 0.20.0 in favor of the plural outgoing_chan_ids.
The code path was unreachable in RTL anyway: no caller of the
GetQueryRoutes action ever populated outgoingChanId, so the query
parameter was never sent. Drop the unused field, the effect's
conditional URL builder, and the server-side passthrough.
The frontend build pulled in crypto-browserify, stream-browserify and
vm-browserify (via tsconfig paths) only because otplib's
@otplib/plugin-crypto requires Node's crypto. That chain carried the
last production npm audit findings - the elliptic advisory
(GHSA-848j-6mx2-7j84, no fixed release) plus browserify-sign/create-ecdh
(issue #1634, item 3).
The two-factor-auth settings dialog is the only browser consumer of
otplib. It now uses a small WebCrypto TOTP service
(src/app/shared/services/totp.service.ts, RFC 6238: HMAC-SHA1, 6 digits,
30s step) instead, so otplib is no longer bundled and the three
polyfills plus their tsconfig path mappings are removed.
The backend still verifies login tokens with otplib, so the new service
must match it exactly - verified byte-for-byte against otplib and the
RFC 6238 test vectors (generateSecret/keyuri/generate/check parity).
Existing authenticator enrollments keep working. token check() is now
async (WebCrypto's digest is promise-based); the dialog's verify handler
was updated to match, and the value was never used for control flow.
Production npm audit now reports zero vulnerabilities (from 13, incl. 2
critical, at the start of this cleanup series). Verified on the docker
fixture: enrolled a 2FA secret from the new service, confirmed the
backend otplib accepts a token it produces at login, rejected
wrong/absent tokens. Unit spec covers RFC 6238 vectors, keyuri parity
and base32 round-trip; both API suites and the full frontend suite (204
specs) pass.
csurf has been deprecated since 2022 and pins an old cookie release
with a known advisory; npm's only fix is a downgrade (issue #1634,
item 2). csrf-csrf v4 implements the same double-submit-cookie pattern
with an HMAC-signed, session-bound token keyed on the existing boot
secret (common.secret_key).
The frontend contract is unchanged: the token still arrives via the
XSRF-TOKEN cookie/header and is echoed as x-xsrf-token (all token
sources csurf accepted are still read), the signed cookie keeps the
_csrf name (now httpOnly, secure:false to match the session cookie on
plain-HTTP deployments), doubleCsrfProtection attaches req.csrfToken
so app.ts keeps working, and the error code is EBADCSRFTOKEN - already
handled in app.ts. The websocket upgrade check in authCheck.ts now
routes through the shared middleware; upgrade requests are GETs, so
its pass-through semantics are unchanged.
One fix this surfaced: app.ts called req.csrfToken() twice (cookie and
header). Under csurf every token validated against a stable secret;
under csrf-csrf each first-visit call mints a new token, desyncing the
XSRF-TOKEN cookie from the _csrf cookie it must equal. The token is
now generated once per request.
Tokens are session-bound, so a token stolen from one session no longer
validates in another - a check csurf's cookie mode did not perform.
Production npm audit drops from 6 low findings to 4, all in the
crypto-browserify/elliptic chain tracked in #1634.
Verified against the docker regtest fixture: both API suites (43
checks across LND, CLN and Eclair) plus a dedicated CSRF battery -
valid-token auth, missing token 403, garbage token 403, cross-session
replay 403, token stability across requests, the XSRF-TOKEN response
header for Quickpay, and the websocket handshake. Lint and build are
clean.
request has been deprecated since 2020 with an unfixed SSRF advisory and
pins vulnerable copies of form-data (critical), qs, tough-cookie and
uuid - 8 of the 13 remaining production audit findings, none fixable by
version bumps (issue #1634, item 1).
All 36 backend files that imported request-promise now use a small
compatibility wrapper (server/utils/request.ts) backed by axios, which
is already a production dependency. The wrapper accepts the existing
options shape (qs, form - object or pre-encoded string, body,
baseUrl/uri, rejectUnauthorized, json), resolves with the response body
directly, and rejects with a plain object mirroring request-promise's
StatusCodeError/RequestError shape, so CommonService.handleError works
unchanged (ECONNREFUSED -> 503, Eclair StatusCodeError -> 500, nested
error body extraction). Auth headers are excluded from rejected errors
so they cannot leak into logs. Callers without json: true (block
explorer, currency rates) still get raw text bodies, and LND's
line-delimited /v2/router/send stream still surfaces as a string for
the existing parser.
Only behavioral code change: CLN verifyMessage used request-promise's
callback style and was ported to the same promise style as signMessage;
four Eclair handlers gained explicit returns to satisfy
noImplicitReturns once the import became typed.
Production npm audit drops from 13 findings (2 critical) to 6 low, all
in the crypto-browserify/elliptic chain tracked in #1634.
Verified against the docker regtest fixture with 43 API checks across
LND, Core Lightning and Eclair: reads, invoice creation, a routed LND
payment over the streaming endpoint, cross-implementation payments from
CLN and Eclair, message sign/verify, channel backup to disk, and
bad-invoice/node-unreachable error mapping. Lint and both production
builds are clean.
Per LND v0.21.0 release notes, the sat_per_byte option will be removed
in v0.22 across CloseChannel, OpenChannel, SendCoins, SendMany, and
walletrpc.BumpFee. LND already treats sat_per_byte as sat/vbyte
internally, so this is a pure rename with no value conversion. Updates
both the wire-format strings sent to LND and the matching TypeScript
identifiers across the close-channel, open-channel, send-coins, and
bump-fee paths.
The a11y fix in #1609 wrapped the bare Scroll Range mat-select in a
mat-form-field for its label, but the wrapper reserved subscript space
(78.8px vs the date field 56px) and anchored to the row top, leaving
the date picker ~11px lower on every implementation reports screen.
Use subscriptSizing="dynamic" (no hints are used) and center on the
cross axis, restoring the aligned 56px row from v0.15.8 while keeping
the label. Verified headlessly against the regtest fixture: both
fields now render at identical top/height.
Fixes#1635
The fix is within the pinned 0.3.x line but the exact pin kept npm
update from reaching it. Clears the last high-severity production
vulnerability; frontend build and full spec suite verified.
Apply the bumps from all 20 open Dependabot security PRs (#1583-#1617)
in one pass on the release branch: axios 1.16.0, ws 8.21.0, the
socket.io server stack, express path-to-regexp, follow-redirects,
lodash and the remaining flagged transitive deps. Angular framework
packages move in lockstep to 20.3.26 and the CLI/build toolchain to
20.3.32, which drops the vulnerable node-forge from the tree entirely.
Also pick up in-range fixes without open PRs (qs, uuid, tough-cookie,
cookie, ajv, bn.js, elliptic, socket.io-parser).
npm audit: 85 vulnerabilities (23 prod) -> 30 (14 prod). The remainder
(request/request-promise, csurf, pdfmake, crypto-browserify chain)
needs code changes, not bumps, and is tracked separately.
Verified: lint, 199 frontend specs, backend + frontend production
builds, and an end-to-end smoke test against the docker regtest
fixture (LND, CLN and Eclair auth/getinfo/channels + WS upgrade).
Dependabot PRs target master and are resolved in batch PRs against the
release branch, but the process was undocumented. Add a "Handling
Dependabot PRs" section covering target collection, pinned in-range
fixes, Angular lockstep, from-scratch lockfile regeneration, artifact
rebuild, verification, and issue-tracking for deprecated packages.
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.
The #1606 fix added 'channel.connected = !!channel.peer_connected' to
server/controllers/cln/channels.ts but the committed compiled artifact
backend/controllers/cln/channels.js was never regenerated, leaving it stale.
Rebuild it so the committed backend output matches its TypeScript source.
RTL resolves peer aliases by calling listnodes once per peer. A prior fix
(1cec7b1) bounded this to 20 concurrent calls plus a cache for the channel
list, but the peers list and route lookup still used an unbounded Promise.all,
firing one request per peer at once. On nodes with many peers this overwhelms
clnrest and fails with 'Resource temporarily unavailable (os error 11)'
(EAGAIN), so aliases fall back to raw node IDs.
- peers.ts and network.ts getRoute now resolve aliases via
runWithConcurrencyLimit(tasks, 20, ...), matching the channel list.
- Harden runWithConcurrencyLimit to call done() immediately for an empty task
list; otherwise an empty peers/route set would never send a response.
- Give the alias cache a 6h TTL and a max size (evicting oldest) so aliases
refresh without an RTL restart and the cache can't grow unbounded.
Several PRs were merged onto Release-0.15.9 before the per-PR release-notes
process was established, leaving them undocumented. Add entries for:
- #1581 Fix page-load error when a channel alias is undefined (Bug Fixes)
- #1601 Fix stale auth options blocking a not-yet-ready node (Bug Fixes)
- #1582 Add Disable Authentication option (Enhancements)
- #1621 Rebuild the regtest docker fixture (Developer Tooling)
Blocks-till-maturity is critical information for a force-closing channel but
was only visible in the per-channel detail modal. The column and its data
binding already existed in the pending force-closing table (and was selectable
via column settings); it was just missing from the default column selection.
Add blocks_til_maturity to the pending_force_closing default columnSelection
and columnSelectionSM so it is surfaced on the list by default on both desktop
and mobile.
A dependency-update commit in the 0.15.8-beta cycle mechanically renamed
the paginator binding [showFirstLastButtons] to [hidePageSize] on every
mat-paginator while keeping the same 'screenSize === XS ? false : true'
expression. The two properties have opposite polarity, so this inverted
the behavior: on desktop the page-size selector was hidden (locking users
to 10 items per page) and the first/last-page buttons were dropped as
collateral. Revert the ~44 affected paginators back to [showFirstLastButtons]
across the LND, CLN, Eclair and shared tables.
Address review F6 on #1625: the LND channel information modal has the same
unguarded selNode.settings.blockExplorerUrl binding as the CLN one, and it is
opened without selNode from the active-HTLCs and channel-backup tables, so it can
blank out the same way. Guard the explorer link (*ngIf + a no-op click when the
url is absent) so a missing selNode can no longer blank the dialog. Eclair's modal
doesn't use selNode.settings, so it needs no change.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The channel information modal renders a block-explorer link from
selNode.settings.blockExplorerUrl, but the pending/inactive channels table
opened the modal without passing selNode. With it undefined, that binding threw
during change detection and blanked every field below it — State, Connected,
Private and the balances all rendered without a value. A disconnected channel
moves to the pending/inactive table, so this is what surfaced on View Info for a
disconnected channel (the symptom in the original report).
Pass selNode from the pending table (matching the open table), and guard the
modal's explorer link (*ngIf + a no-op click when the url is absent) so a missing
selNode can no longer blank the whole dialog. Add a regression test asserting the
pending table passes selNode when opening the modal.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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>
Introduce a release-notes/ folder to collect the changes for each release, and
add Release-notes-0.15.9.md with the entry for the CLN channel connection status
fix (#1606 / #1625). New process: every PR for a release appends its entry to the
respective release notes document.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>