Skip to content

Move the client build to Node 26 - #328

Merged
Serraniel merged 1 commit into
mainfrom
build/node-26
Oct 21, 2026
Merged

Serraniel merged 1 commit into
mainfrom
build/node-26

Conversation

@Serraniel

Copy link
Copy Markdown
Owner

Closes #325. Replaces #323, which proposed Node 25 — a line that has never been
LTS and reached end of life on 2026-06-01.

Node became LTS maintenance end of life
24 2025-10-28 2026-10-20 2028-04-30
25 never 2026-04-01 2026-06-01
26 2026-10-28 2027-10-20 2029-04-30

Node 24 entered maintenance on 2026-10-20, so it receives security fixes only.
Nothing is urgent — it is supported until 2028 — but the window is open, and 26
is the line that will carry the next three years.

Six references, because the gate requires it

scripts/check-toolchains.sh fails the build if any one of these disagrees:

  • the FROM in the Dockerfile, tag and digest together
  • NODE_VERSION in release.yml, test.yml, security-scan.yml and
    tend-dependency-branches.yml
  • the Dockerfile comment that uses the pinned image as its own example

The digest is the OCI image index covering linux/amd64 and linux/arm64/v8,
the same form as the pin it replaces, and it resolves to v26.12.1.

toolchains: Go 1.27.2 and Node 26 everywhere.

A measurement that contradicts this project's own comment

#325 asked for the content-hashed filenames to be compared between the two build
paths, because that equality is the entire point of pinning the major. I did
that, and the result is not what check-toolchains.sh and the Dockerfile both
claim.

Built from one tree in node:24-alpine (v24.22.1) and node:26-alpine
(v26.12.1), with SENDAN_COMMIT fixed so the version name was not a variable,
then sha256sum over all of internal/webui/dist rather than a spot check:

node 24: 33 files
node 26: 33 files
contents, not just names:
  every one of the 33 files is byte-identical across both majors

The Dockerfile's own web stage, built with the same COMMIT, produced the
same 33 files a third time.

So on this toolchain the Node major is not currently a source of divergence,
and the v0.2.0 failure is fully explained by the other cause already recorded in
vite.config.ts: the version name was Date.now(), so two builds of one source
disagreed by construction. 2e038e1 fixed that by naming the build after the
commit. The Node major looks like a second hypothesis that was never separated
from the first.

⚠️ I have not loosened the gate on the strength of one measurement. It costs
nothing, it would catch a future bundler that does key its output to the
runtime, and an equality that holds today on one commit is not a guarantee.
What is wrong is the wording — two comments and a CONTRIBUTING.md paragraph
state it as established fact. #327 carries that correction, and notes that the
comparison is worth re-running when Vite or Rolldown takes a major, since that
is where the assumption could quietly become true again.

On the timing

The rule in CONTRIBUTING.md, which #326 wrote down, is that the line must be
LTS on the day it is adopted. Node 26 reaches that on 2026-10-28, seven days
out, so merging before then is seven days on a Current line.

That is your call and the PR is ready either way. My only suggestion is to
release #322 (0.3.3) first, so that release is built on a line that is LTS
today, and merge this after.

Verified

check-toolchains.sh · check-workflows.py · the client builds in the pinned
image · docker build --target web succeeds · output byte-identical to the
Node 24 build and to the direct build.

🤖 Generated with Claude Code

Node 26 becomes Active LTS on 2026-10-28. Node 24 entered maintenance on
2026-10-20 and is supported until 2028-04-30, so this is not urgent, but the
window is open and 24 now receives security fixes only.

The move skips 25 rather than passing through it. Odd-numbered majors never
become LTS: 25 was Current from 2025-10-15 and reached end of life on
2026-06-01, which is why #323 was closed rather than fixed - it proposed a tag
newer than the pinned one with a support window four months shorter.

Six references move together because check-toolchains.sh requires it: the FROM
in the Dockerfile with its digest, NODE_VERSION in release.yml, test.yml,
security-scan.yml and tend-dependency-branches.yml, and the Dockerfile comment
that used the pinned image as its own example. The digest is the OCI image
index covering linux/amd64 and linux/arm64/v8, as the previous pin was, and it
resolves to v26.12.1.

One measured result belongs on the record, because it does not match what this
project has written down twice. check-toolchains.sh and the Dockerfile both
say a bundler on two different Node majors emits two different sets of
content-hashed filenames. Building the client in node:24-alpine and
node:26-alpine with one SENDAN_COMMIT produced 33 files whose names and bytes
were identical - sha256 over the whole tree, not a spot check. The Dockerfile's
own web stage, built with the same COMMIT, produced the same 33 files again.

So on this toolchain the Node major is not currently a source of divergence,
and the v0.2.0 failure is fully explained by the other cause the configuration
records: the version name was Date.now(), so two builds disagreed by
construction, which 2e038e1 fixed by naming the build after the commit.

The gate is not loosened on the strength of one measurement. It costs nothing,
it would catch a future bundler that does diverge, and an equality that holds
today is not a guarantee. #327 carries the correction to the two comments that
overstate it.

Verified: check-toolchains.sh, check-workflows.py, the client builds in the
pinned image, and `docker build --target web` succeeds.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Signed-off-by: Serraniel <mail@serraniel.dev>
@Serraniel
Serraniel merged commit 6937052 into main Oct 21, 2026
15 checks passed
@Serraniel
Serraniel deleted the build/node-26 branch October 21, 2026 20:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Move the client build to Node 26 once it is LTS

1 participant