.node-version file. It is
the 2026-10-21 security release of the Node 26 Current line, which is not LTS.
Treat .node-version as the source of the requested version. Installing that
version and activating it are separate steps. Version managers need the setup
below; a plain node command can still resolve an older installation.
The workflow migration and its guard are pending in
PR #6496. Until that layer lands,
this pin alone does not change CI runtime selection. The CI behavior and
receipts described below are the intended acceptance criteria for that layer.
The engines.node ranges are support floors, not runtime pins. The root
>=22.13.0 floor stays because Vercel maps >=22 to its newest supported
major. The >=18.0.0 ranges in published packages are consumer contracts.
Raising either range to 26 would break Vercel deployments and package
consumers without changing the CI runtime.
Local setup
On a personal machine, install the pinned version side by side, activate it, and verify the version in the same shell that will run your commands. For fnm, initialize the shell and select the version explicitly:--use-on-cd hook switches to .node-version when entering the repository.
Installing a version without selecting it or enabling that hook is insufficient.
mise version-file support
is opt-in. On your personal machine, enable Node version files with
mise settings add idiomatic_version_file_enable_tools node (this changes
mise’s global configuration). With mise’s shell integration active, it then
selects the repository’s .node-version. Explicit execution works without
relying on shell activation:
.node-version. On a personal machine, run
nvm install "$(cat .node-version)", then nvm use "$(cat .node-version)", and
check node --version; automatic directory switching requires a separate hook.
Volta project pins
use the volta field in package.json, which this repository does not supply.
Do not assume Volta reads .node-version. Use an explicit one-command override,
volta run --node "$(cat .node-version)" node --version, or one of the managers
above instead of adding a second repository pin.
The devbox bootstrap remains distribution/LTS-based; it does not activate
.node-version. Select the private toolchain explicitly when validating on a
devbox, just as on other shared hosts.
Shared agent hosts
Several projects share agent hosts, so do not usenvm install there. Placing
a build under ~/.nvm/versions/node changes what nvm’s major alias (for
example 26) resolves to for every new shell, even when the alias file stays
the same. Instead:
- Download
node-v<version>-<platform>.tar.xz,SHASUMS256.txtandSHASUMS256.txt.sigfromhttps://nodejs.org/dist/v<version>/into a new empty directory. - Check the SHA-256 of the tarball against
SHASUMS256.txt. Verify the signature with the Node.js release keys in a temporaryGNUPGHOME. - Extract the tarball to
~/.local/share/tuturuuu/toolchains/node-v<version>-<platform>/. - Call that
bin/nodeby absolute path, or prepend itsbintoPATHfor your own commands only. For queued work, usettr resources run -- <toolchain>/bin/node ….
- nvm’s aliases or
~/.nvm; - the
ttr resourcesshims; - Homebrew or
/usr/localNode; - runner tool caches;
- shared setup scripts or shell profiles.
bun doctor still enforces the Node 22 floor rather than the exact pin.
Where 26.12.1 applies after #6496
A job reads
node-version-file only when its checkout is the workflow’s own
commit (no ref, or ${{ github.sha }}) and is not sparse. The file then comes
from the same tree as the workflow. Other jobs pin the literal 26.12.1:
- jobs that set up Node before any checkout, such as package publish and dispatch;
- sparse duplicate-validation checkouts;
- jobs that check out a PR head, the
productionbranch, a workflow-run head or an input SHA, because that tree may predate.node-version.
scripts/ci/node-version-pin.test.js) requires every literal to equal
.node-version and every exception to be listed. It also requires each
test "$(node --version)" = v… workflow assertion to name the pin.
Each setup step logs the resolved version. Two receipts bind node --version to
the exact source SHA:
- the SDK qualification receipt (
runtimeVersions.nodewithsourceSha); - the Calendar native artifact receipt (
nodewithsha).
Intentional exceptions
A floating
node-version: 24 does not guarantee the newest patch:
setup-node defaults check-latest to false
and first accepts a matching cached release. An exact patched version gives a
reproducible CI selection; check-latest: true instead requests the newest
matching release. Neither proves which patch a deployed function runs.
Native addons
Node 26 usesNODE_MODULE_VERSION 147 and N-API 10. sharp, bcrypt,
better-sqlite3, esbuild, lightningcss, @swc/core, @napi-rs/canvas,
@napi-rs/keyring and node:sqlite all load and run on 26.12.1.
better-sqlite3 is the only one tied to a V8 ABI, so reinstall it after a major
Node change.
Updating the pin
- Read the target from
https://nodejs.org/dist/index.json, including itssecurityandltsfields. - Change
.node-version, plus any pre-checkoutnode-versionliterals the workflow contract tests list. - Run the root script tests and the affected package tests with that exact
binary through
ttr resources run. - Check the CI setup logs and the SDK qualification receipt for the new
node --versionon the exact commit. - Re-check every exception above against its upstream documentation.