You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Node 20 ("Iron") reached end of life on 2026-04-30 and no longer receives security patches. For a bot that custodies Lightning funds, running on an unsupported runtime is a standing risk on its own.
This became concrete in #917, which upgraded the Lightning dependencies:
lightning11.1.0 → 12.2.4
invoices2.0.6 → 6.0.5
Both new majors declare "engines": { "node": ">=22" }, while the repo still advertises and ships Node 20. There is no .npmrc, so engine-strict is off and npm only emits a warning instead of failing the install.
Current state: it works, but the contract is broken
To be clear about severity — this is not a live production break:
All 298 runtime .js files of lightning and invoices parse cleanly under Node 20.20.2, and both packages require() without error.
Neither package uses a Node 22-only API in shipped code (the only node:test references live in their own test/ directories, which are not used at runtime).
The real problem is forward-looking: upstream has declared Node 20 unsupported. Any future patch release of lightning or invoices may introduce Node 22+ syntax or APIs with no warning, and nothing in our install or CI would catch it before it reached production.
Combined with the EOL status of Node 20, the right fix is to move the runtime forward rather than pin the dependencies back.
Pre-existing inconsistency worth folding in
.github/workflows/code-linter.yaml runs on Node 18.x, which is already below the >=20.0.0 the root package.json declares. Unrelated to #917, but it should be corrected in the same pass.
Add .nvmrc pinning 22, so local development matches CI and the Docker image. The repo currently has neither .nvmrc nor .node-version.
Risks / what to watch
The main risk is native modules and the Node ABI change:
canvas is compiled from source in CI (integrate.yaml installs build-essential, libcairo2-dev, libpango1.0-dev, libjpeg-dev, libgif-dev, librsvg2-dev). This is the most likely place for the bump to break.
@grpc/grpc-js (via lightning) ships prebuilt binaries and should be checked on the new base image.
The Alpine (node:22-alpine) and Debian (node:22-bookworm) images differ in libc, so both the Docker build and the CI container need to be validated independently — a green CI run does not by itself prove the production image builds.
Acceptance criteria
docker build succeeds against node:22-alpine, including the canvas native build
npm start runs successfully inside the built image (per AGENTS.md)
ci_to_main passes on node:22-bookworm
code-linter passes on Node 22
npm ci produces no engine warnings for any dependency
Bot verified against a signet/testnet LND node: sell order, buy order, and cancel flows complete end to end
Context
Node 20 ("Iron") reached end of life on 2026-04-30 and no longer receives security patches. For a bot that custodies Lightning funds, running on an unsupported runtime is a standing risk on its own.
This became concrete in #917, which upgraded the Lightning dependencies:
lightning11.1.0→12.2.4invoices2.0.6→6.0.5Both new majors declare
"engines": { "node": ">=22" }, while the repo still advertises and ships Node 20. There is no.npmrc, soengine-strictis off and npm only emits a warning instead of failing the install.Current state: it works, but the contract is broken
To be clear about severity — this is not a live production break:
ci_to_mainrannpm ci+npm testinsidenode:20-bookwormon the chore: update Lightning Network dependencies to latest #917 head commit and passed..jsfiles oflightningandinvoicesparse cleanly under Node 20.20.2, and both packagesrequire()without error.node:testreferences live in their owntest/directories, which are not used at runtime).The real problem is forward-looking: upstream has declared Node 20 unsupported. Any future patch release of
lightningorinvoicesmay introduce Node 22+ syntax or APIs with no warning, and nothing in our install or CI would catch it before it reached production.Combined with the EOL status of Node 20, the right fix is to move the runtime forward rather than pin the dependencies back.
Pre-existing inconsistency worth folding in
.github/workflows/code-linter.yamlruns on Node 18.x, which is already below the>=20.0.0the rootpackage.jsondeclares. Unrelated to #917, but it should be corrected in the same pass.What needs to change
DockerfileFROM node:20-alpine AS buildernode:22-alpineDockerfileFROM node:20-alpine AS productionnode:22-alpine.github/workflows/integrate.yamlimage: 'node:20-bookworm'node:22-bookworm.github/workflows/integrate.yaml.github/workflows/code-linter.yamlnode-version: 18.x22.xpackage.jsonengines">=20.0.0"">=22.0.0"Optional but recommended:
.npmrcwithengine-strict=trueso an engines mismatch fails the install instead of passing as a warning. This is what would have surfaced the chore: update Lightning Network dependencies to latest #917 problem automatically..nvmrcpinning22, so local development matches CI and the Docker image. The repo currently has neither.nvmrcnor.node-version.Risks / what to watch
The main risk is native modules and the Node ABI change:
canvasis compiled from source in CI (integrate.yamlinstallsbuild-essential,libcairo2-dev,libpango1.0-dev,libjpeg-dev,libgif-dev,librsvg2-dev). This is the most likely place for the bump to break.@grpc/grpc-js(vialightning) ships prebuilt binaries and should be checked on the new base image.The Alpine (
node:22-alpine) and Debian (node:22-bookworm) images differ in libc, so both the Docker build and the CI container need to be validated independently — a green CI run does not by itself prove the production image builds.Acceptance criteria
docker buildsucceeds againstnode:22-alpine, including thecanvasnative buildnpm startruns successfully inside the built image (perAGENTS.md)ci_to_mainpasses onnode:22-bookwormcode-linterpasses on Node 22npm ciproduces no engine warnings for any dependencyReferences