Upgrade runtime from Node 20 to Node 22
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 292
- Forks
- 136
- Avg merge
- 19m
- Merged PRs (30d)
- 1
Description
## 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:
- `lightning` `11.1.0` → `12.2.4`
- `invoices` `2.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:
- `ci_to_main` ran `npm ci` + `npm test` inside `node:20-bookworm` on the #917 head commit and passed.
- 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.
## What needs to change
| File | Line | Current | Target |
| --- | --- | --- | --- |
| `Dockerfile` | 4 | `FROM node:20-alpine AS builder` | `node:22-alpine` |
| `Dockerfile` | 48 | `FROM node:20-alpine AS production` | `node:22-alpine` |
| `.github/workflows/integrate.yaml` | 28 | `image: 'node:20-bookworm'` | `node:22-bookworm` |
| `.github/workflows/integrate.yaml` | 24-26 | comment referencing Node 20 | update wording |
| `.github/workflows/code-linter.yaml` | 21 | `node-version: 18.x` | `22.x` |
| `package.json` | `engines` | `">=20.0.0"` | `">=22.0.0"` |
Optional but recommended:
- Add `.npmrc` with `engine-strict=true` so an engines mismatch fails the install instead of passing as a warning. This is what would have surfaced the #917 problem automatically.
- 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
## References
- #917 — the dependency upgrade that surfaced this
- Codex review comment: https://github.com/lnp2pBot/bot/pull/917#discussion_r3766395184
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with Dockerfile, package.json, .github/workflows/integrate.yaml, and .github/workflows/code-linter.yaml, then read AGENTS.md for the expected container checks. Run the Docker build, npm start, and both workflows while checking the canvas and gRPC dependencies on Node 22. Done means the builds and tests pass without engine warnings and the listed signet/testnet order flows complete.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- docker, github-actions, node.js
- Domain
- build-system, ci-cd, devops
- Issue type
- Refactor
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 48/100