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.
16 KiB
Release Notes — 0.15.9
This document collects the changes that go into the 0.15.9 release. Each PR merged for this release should add its entry under the appropriate section below.
Bug Fixes
-
All implementations: fix a page-load error when a channel's alias is undefined (#1581). On the home dashboard, channel labels were rendered as
(channel.alias || channel.peer_id).length(and theremote_alias/shortChannelIdvariants). When both the alias and its fallback id were undefined, calling.lengthonundefinedthrew during change detection and errored the page on load. The bindings now fall back to an empty string (|| '', with optional chaining) so a missing alias can no longer break the page. -
Multi-node: fix stale auth options blocking a node whose credentials weren't ready at startup (#1601).
CommonService.setOptionsshort-circuited onthis.nodes[0]regardless of which node was being processed, so once the first node's auth headers loaded, every subsequent call returned early. A second node whose credential load had failed — e.g. a Core Lightning rune file written asynchronously after RTL starts — was never retried and kept failing withmissing rune!until RTL was restarted. The cache check is now per-node, so a node that failed to initialize is retried on the next request once its credential is available, while already-loaded nodes are still skipped. -
Core Lightning: fix contradictory channel connection status between the list and the detail panel (#1625, fixes #1606). CLN's
listpeerchannelsreports connection state aspeer_connected, but the open and pending channel-list columns read the legacyconnectedfield, which the backend never populated. The list therefore always rendered "Disconnected" while the detail panel (which readspeer_connected) showed the true state. The backend now normalizesconnected = peer_connectedin thelistPeerChannelsresponse so legacy consumers stay in sync, and the list columns readpeer_connecteddirectly. Regression tests were added for both channel tables. -
Core Lightning: fix the channel View Info modal rendering blank for disconnected channels (#1625, fixes #1606). The channel information modal renders a block-explorer link from
selNode.settings.blockExplorerUrl, but the pending/inactive channels table opened the modal without passingselNode. With it undefined, that binding threw during change detection and blanked every field below it — State, Connected, Private and the balances all showed no value. Because a disconnected channel moves to the pending/inactive table, this is exactly what was seen on "View Info" for a disconnected channel. The pending table now passesselNode(matching the open table), and the modal guards the explorer link so a missingselNodecan no longer blank the dialog. The LND channel information modal had the same unguardedselNode.settings.blockExplorerUrlbinding (reachable from the active-HTLCs and channel-backup tables, which open it withoutselNode), so the same guard was applied there for parity. Eclair's modal doesn't useselNode.settings, so it is unaffected. -
All implementations: restore the "items per page" dropdown (and first/last-page buttons) on paginated tables (#1626, fixes #1580). A dependency-update commit in the 0.15.8-beta cycle mechanically renamed the paginator binding
[showFirstLastButtons]to[hidePageSize]on everymat-paginatorwhile keeping the samescreenSize === XS ? false : trueexpression. Because the two properties have opposite polarity, this inverted the behavior: on desktop the page-size selector was hidden, so users were locked to 10 items per page with no way to raise it — and the first/last-page buttons were dropped everywhere as collateral. Reverting the ~44 affected paginators back to[showFirstLastButtons]restores both behaviors across the LND, Core Lightning, Eclair and shared tables. -
Accessibility: add missing form-field labels and remove positive tab indexes (#1609, fixes #1566). Several
mat-selectand datepicker controls across the send, invoice, open/close-channel, bump-fee, public-key and settings forms were rendered without amat-label, so screen readers had no way to announce their purpose (WCAG 1.3.1 / 3.3.2). Descriptive labels were added to the affected controls. The forms also relied on positivetabindexvalues, an anti-pattern (WCAG 2.4.3) that produced an inconsistent keyboard order; these were removed so focus follows natural DOM order across the LND, Core Lightning, Eclair and shared modals. -
Bound peer/route alias resolution to stop clnrest "Resource temporarily unavailable" errors (#1629, fixes #1501). RTL resolves peer aliases by calling
listnodes(CLN) /graph/node(LND) once per peer. A prior fix bounded this to 20 concurrent calls (plus a cache) for the CLN channel list, but the Core Lightning peers list and route lookup — and the LND peers list — still fired an unboundedPromise.all, one request per peer at once. On Core Lightning nodes with many peers this overwhelms clnrest and fails withResource temporarily unavailable (os error 11)(EAGAIN), leaving raw node IDs instead of aliases. All of these paths now use the same 20-way concurrency limit (Eclair already resolves aliases inline from a bulk nodes list, so it is unaffected). The CLN alias lookup was also made self-contained so aliases resolve regardless of which screen is opened first; the limiter now resolves immediately for an empty or non-positive input (which would previously never send a response); and the CLN alias cache gained a 6-hour TTL and a max size so aliases refresh without an RTL restart and the cache can't grow unbounded. -
Reports: realign the Scroll Range select with the date picker (#1637, fixes #1635). The a11y fix in #1609 wrapped the Reports page's bare Scroll Range
mat-selectin amat-form-fieldso it could carry a label, but the new wrapper reserved Material's hint/subscript space below the input (78.8px total vs the date field's 56px) and was top-anchored, leaving the Monthly/Yearly Date picker sitting ~11px lower than the select on every implementation's report screens. The field now usessubscriptSizing="dynamic"(no hints are used, so no space is reserved) and centers on the cross axis, restoring the aligned 56px control row from v0.15.8 while keeping the accessibility label. Verified by measuring the rendered layout headlessly against the regtest fixture: both fields now render at identical top/height.
Enhancements
-
Add a Disable Authentication option (#1582). A new
disableAuthconfig flag (orDISABLE_AUTHenvironment variable) lets RTL run without its login screen — intended for node-platform vendors who put their own authentication layer in front of RTL, not for standalone users. When enabled, RTL issues a session token automatically and disables password updates and 2FA; a configuredAPP_PASSWORDis rejected as incompatible. Backend, frontend, and configuration docs were updated. -
LND: show "Blocks till Maturity" by default on the Pending Force Closing list (#1627, fixes #1567). Blocks-till-maturity is critical for a force-closing channel, but it was only visible in the per-channel detail modal. The column and its data binding already existed in the table (and was selectable via column settings); it was simply absent from the default column selection. Added
blocks_til_maturityto thepending_force_closingdefaults for both the desktop and mobile (SM) layouts, so it's surfaced on the list out of the box. Users who have already customized this page keep their saved columns and can add it via the column-settings gear.
Code Health
-
LND: migrate
sat_per_bytetosat_per_vbytein node requests (#1592). LND's v0.21.0 release notes deprecate thesat_per_bytefield, with removal planned in v0.22 acrossCloseChannel,OpenChannel,SendCoins,SendManyandwalletrpc.BumpFee. LND already interprets the old field as sat/vbyte internally, so this is a pure wire-format rename with no value conversion. The close-channel, open-channel, send-coins and bump-fee request paths (and their matching TypeScript identifiers) now sendsat_per_vbyte, keeping RTL compatible ahead of the removal. -
Batch dependency update resolving all 20 open Dependabot security PRs (#1633). Dependabot had 20 open security-alert PRs against
master(#1583–#1617). Rather than merging them piecemeal (they conflict with each other onpackage-lock.jsonand target the wrong branch for the release flow), the same bumps were applied in one pass on the release branch:axios1.16.0 andws8.21.0 (direct), the socket.io server stack (engine.io,engine.io-client,socket.io-adapter,socket.io-parser), express'spath-to-regexp,follow-redirects,lodash, and the rest of the flagged transitive deps; the Angular framework packages moved in lockstep to 20.3.26 and the CLI/build toolchain to 20.3.32 (which drops the vulnerablenode-forgefrom the tree entirely). In-range fixes Dependabot hadn't re-opened PRs for (pdfmake0.3.11 for its SSRF advisory,qs,uuid,tough-cookie,cookie,ajv,bn.js,elliptic) were picked up in the same pass.npm auditgoes from 85 vulnerabilities (23 production) to 29 (13 production, none high or critical besides therequeststack); everything remaining requires code changes, not version bumps — the deprecatedrequest/request-promisestack,csurfand thecrypto-browserifypolyfill chain — and is tracked separately. Verified with a clean lint, the full frontend test suite, both production builds, and an end-to-end smoke test of the docker regtest fixture across LND, Core Lightning and Eclair (auth, getinfo, channel lists, and the WebSocket upgrade path). -
Replace the deprecated
request/request-promiseHTTP stack with axios (#TBD, part of #1634).requesthas been deprecated and unmaintained since 2020 and carries an unfixable SSRF advisory plus vulnerable pinned copies ofform-data(critical),qs,tough-cookieanduuid— 8 of the 13 productionnpm auditfindings left after #1633, none fixable by a version bump. All 36 backend files that importedrequest-promise(the LND, Core Lightning and Eclair controllers, Boltz/Loop/RTLConf shared controllers,common.tsand the LND websocket client) now go through a small compatibility wrapper (server/utils/request.ts) backed byaxios— already a production dependency, so nothing new is added. The wrapper accepts the existing request-promise options (qs,formincluding pre-encoded string bodies,body,baseUrl/uri,rejectUnauthorized,json), resolves with the response body directly, and rejects with a plain object mirroring request-promise'sStatusCodeError/RequestErrorshape, soCommonService.handleError's status-code and message extraction (including theECONNREFUSED→ 503 mapping and Eclair's status-code special case) behaves as before; auth headers are omitted from rejected errors so they cannot leak into logs. Callers withoutjson: truestill receive the raw text body, and LND's line-delimited/v2/router/sendstream still surfaces as a string for the existing parser. Productionnpm auditdrops from 13 findings (2 critical) to 6 low, all in thecrypto-browserify/ellipticpolyfill chain tracked in #1634. Verified end-to-end against the docker regtest fixture: 43 API checks across all three implementations (reads, invoice creation, a routed LND payment over the streaming endpoint, cross-implementation payments from Core Lightning and Eclair, message sign/verify, channel backup to disk, bad-invoice and node-unreachable error mapping) plus a clean lint and both production builds. -
Rebuild the compiled CLN channels controller to match its source (#1631). The #1606 fix updated
server/controllers/cln/channels.tsto mirrorpeer_connectedonto the legacyconnectedfield, but the committed compiled artifactbackend/controllers/cln/channels.jswas never regenerated, so it lagged its source. Rebuilt it so the committed backend output includes the connected-mirror line.
Developer Tooling
-
Documented the Dependabot / dependency-update process in CONTRIBUTING.md (#1636). Dependabot's security PRs target
masterand are never merged individually — they are resolved in batch dependency-update PRs against the current release branch (as done in #1633). That process was previously undocumented. CONTRIBUTING.md now has a "Handling Dependabot PRs" section covering the full flow: collecting targets (including in-range fixes hidden by exact pins), applying bumps with Angular in lockstep, regenerating the lockfile from scratch, rebuilding and committing the compiled artifacts, verification, and tracking deprecated packages that need code-level replacement in dedicated issues. -
Rebuilt the regtest docker fixture (#1621). The
docker/dev setup had been unable to start since February 2021 — a brokenboltzservice (undeclaredBOLTZ_*variables, a non-existent build context, and undeclared volumes) made Compose reject the whole project, so evendocker compose up -d bitcoindfailed. It was replaced with a working regtest network:bitcoind30.0 + three LND 0.20.0-beta nodes (alice → bob → carol, so RTL's routing/forwarding screens have data) + RTL, all using Polar's multi-arch images (nothing built locally; works on arm64), plus a deterministicseed.sh. The Core Lightning node below was later added on top of this fixture. -
Added a Core Lightning node to the regtest docker fixture (#1625). The
docker/fixture now runs aclnnode (officialelementsproject/lightningdimage) alongside the three LND nodes, wired to RTL over clnrest with rune auth, and the seed opens acln→alicechannel. This gives RTL's Core Lightning screens a real backend for local development and testing — it was used to verify the CLN channel-connection fix above end-to-end. Seedocker/README.md. -
Added an Eclair node to the regtest docker fixture (#1632). The
docker/fixture now runs aneclairnode alongside the LND and Core Lightning nodes, completing backend coverage of all three implementations RTL supports. RTL talks to its HTTP API with basic auth (lnApiPassword), and the seed opens aneclair→bobchannel plus payments and an open invoice so RTL's Eclair screens have data. Polar's multi-archpolarlightning/eclairimage is used because the officialacinq/eclairimage is amd64-only and its versioned tags are years stale. Since Eclair drives a bitcoind wallet rather than its own, an init container creates a dedicatedeclairwallet before the node starts — otherwise it would attach to the fixture's mining wallet. Abin/e-clihelper wrapseclair-cli.