diff --git a/.github/workflows/publish-simplex-desktop.yml b/.github/workflows/publish-simplex-desktop.yml index 48bd03194f..d452af1bbc 100644 --- a/.github/workflows/publish-simplex-desktop.yml +++ b/.github/workflows/publish-simplex-desktop.yml @@ -58,7 +58,7 @@ jobs: - name: Setup Node.js uses: actions/setup-node@v4 with: - node-version: "24.19.0" + node-version: "24.22.1" - name: Setup pnpm uses: pnpm/action-setup@v4 @@ -161,7 +161,7 @@ jobs: - name: Setup Node.js uses: actions/setup-node@v4 with: - node-version: "24.19.0" + node-version: "24.22.1" - name: Setup pnpm uses: pnpm/action-setup@v4 @@ -368,7 +368,7 @@ jobs: - name: Setup Node.js uses: actions/setup-node@v4 with: - node-version: "24.19.0" + node-version: "24.22.1" - name: Setup pnpm uses: pnpm/action-setup@v4 diff --git a/.github/workflows/test-simplex-desktop.yml b/.github/workflows/test-simplex-desktop.yml index f1384e6e34..f1bb93322d 100644 --- a/.github/workflows/test-simplex-desktop.yml +++ b/.github/workflows/test-simplex-desktop.yml @@ -80,14 +80,14 @@ jobs: - name: Execute staged Node on Unix if: runner.os != 'Windows' - run: test "$(packages/simplex-desktop/resources/node/${{ matrix.target }}/node --version)" = "v24.19.0" + run: test "$(packages/simplex-desktop/resources/node/${{ matrix.target }}/node --version)" = "v24.22.1" - name: Execute staged Node on Windows if: runner.os == 'Windows' shell: pwsh run: | $version = & "packages/simplex-desktop/resources/node/win32-x64/node.exe" --version - if ($version -ne "v24.19.0") { throw "Expected v24.19.0, got $version" } + if ($version -ne "v24.22.1") { throw "Expected v24.22.1, got $version" } desktop-e2e-artifacts: name: Build Simplex desktop E2E artifacts diff --git a/docs/content/developers/evm/simplex/installation.mdx b/docs/content/developers/evm/simplex/installation.mdx index ae9ca64c4f..c1591c18a0 100644 --- a/docs/content/developers/evm/simplex/installation.mdx +++ b/docs/content/developers/evm/simplex/installation.mdx @@ -93,7 +93,7 @@ simplex ``` The scope matters: the unscoped `simplex` package on npm is unrelated. The installed command is still -`simplex`. A global install needs Node.js 22.16 or newer; the desktop and Docker distributions bundle +`simplex`. A global install needs Node.js 22.23.4+, 24.22.1+ or 26.12.1+; the desktop and Docker distributions bundle their own runtime. ## Building a config file diff --git a/docs/content/developers/sdk/api/simplex.mdx b/docs/content/developers/sdk/api/simplex.mdx index b64039c37c..cd24e483a9 100644 --- a/docs/content/developers/sdk/api/simplex.mdx +++ b/docs/content/developers/sdk/api/simplex.mdx @@ -878,8 +878,8 @@ it is. directory written by the binary is picked up unchanged. Limit orders and operator state ride in `bids.db` beside the bids, and implements `patch`; a `runtime-state.json` from an earlier version is imported once and then removed. It is built on Node's -built-in `node:sqlite`, so there is no native module to install or compile — which is why the -package requires Node 22.16 or newer. +built-in `node:sqlite`, so there is no native module to install or compile. The package requires +Node 22.23.4+, 24.22.1+ or 26.12.1+. **Example — a custom backend:** diff --git a/docs/content/developers/sdk/simplex.mdx b/docs/content/developers/sdk/simplex.mdx index 3115e68823..4df7db1630 100644 --- a/docs/content/developers/sdk/simplex.mdx +++ b/docs/content/developers/sdk/simplex.mdx @@ -19,7 +19,7 @@ less work. npm install @hyperbridge/simplex ``` -Node.js 22.16 or later. The package ships ESM and CJS builds with bundled types. +Node.js 22.23.4+, 24.22.1+ or 26.12.1+. The package ships ESM and CJS builds with bundled types. ## Starting a solver @@ -254,7 +254,7 @@ that is only reclaimable by retracting it, and the retraction sweep finds what t querying the bid store. Lose those records and you leak deposits. The bundled SQLite store is a separate entry point. It runs on Node's built-in `node:sqlite`, so -there is no native module to install or compile — the package just needs Node 22.16 or newer: +there is no native module to install or compile — the package just needs Node 22.23.4+, 24.22.1+ or 26.12.1+: ```ts lineNumbers import { SqliteDataStore } from "@hyperbridge/simplex/sqlite" diff --git a/sdk/packages/simplex-desktop/README.md b/sdk/packages/simplex-desktop/README.md index 1fada40825..d00d35bcf0 100644 --- a/sdk/packages/simplex-desktop/README.md +++ b/sdk/packages/simplex-desktop/README.md @@ -238,7 +238,7 @@ without a separate trusted network boundary. ## Runtime resources The staging script follows Node's binary-verification procedure: it verifies the signed -`SHASUMS256.txt.asc` with the pinned release key, verifies the selected Node 24.19.0 archive checksum, +`SHASUMS256.txt.asc` with the pinned release key, verifies the selected Node 24.22.1 archive checksum, and installs only `node` or `node.exe`. `darwin-universal` verifies both macOS slices independently before merging them with `lipo`. diff --git a/sdk/packages/simplex-desktop/package.json b/sdk/packages/simplex-desktop/package.json index 360ea19a13..39cebc4973 100644 --- a/sdk/packages/simplex-desktop/package.json +++ b/sdk/packages/simplex-desktop/package.json @@ -1,7 +1,7 @@ { "name": "@hyperbridge/simplex-desktop", "productName": "Simplex", - "version": "0.18.3", + "version": "0.18.4", "private": true, "description": "Native desktop shell for the Simplex intent solver", "author": "Polytope Labs ", diff --git a/sdk/packages/simplex-desktop/scripts/stage-node.mjs b/sdk/packages/simplex-desktop/scripts/stage-node.mjs index 2cf4740c99..890aaeec36 100644 --- a/sdk/packages/simplex-desktop/scripts/stage-node.mjs +++ b/sdk/packages/simplex-desktop/scripts/stage-node.mjs @@ -9,7 +9,7 @@ import { pipeline } from "node:stream/promises" import * as openpgp from "openpgp" import * as tar from "tar" -export const NODE_VERSION = "24.19.0" +export const NODE_VERSION = "24.22.1" export const NODE_BASE_URL = `https://nodejs.org/dist/v${NODE_VERSION}` export const NODE_RELEASE_KEY_FINGERPRINT = "5BE8A3F6C8A5C01D106C0AD820B1A390B168D356" export const TARGETS = ["darwin-arm64", "darwin-x64", "darwin-universal", "linux-arm64", "linux-x64", "win32-x64"] diff --git a/sdk/packages/simplex/README.md b/sdk/packages/simplex/README.md index d30e785b2f..859b10f42a 100644 --- a/sdk/packages/simplex/README.md +++ b/sdk/packages/simplex/README.md @@ -22,7 +22,7 @@ Embed it in your own Node service, or run it as a ready-made app: npm install @hyperbridge/simplex ``` -Requires Node.js 22.16 or later. +Requires Node.js 22.23.4+, 24.22.1+ or 26.12.1+. ## Quick start diff --git a/sdk/packages/simplex/docs/ai/changelog/2026-10-23-node-runtime-node2shell-fix.md b/sdk/packages/simplex/docs/ai/changelog/2026-10-23-node-runtime-node2shell-fix.md new file mode 100644 index 0000000000..98577ea619 --- /dev/null +++ b/sdk/packages/simplex/docs/ai/changelog/2026-10-23-node-runtime-node2shell-fix.md @@ -0,0 +1,27 @@ +# 2026-10-23 — Simplex runs on Node 24.22.1, and requires a Node with the Node2Shell fix + +CVE-2026-111497 (Node2Shell) is an out-of-bounds write in the Brotli decoder bundled with Node. Any +Brotli data the process decompresses can trigger it, and that includes `fetch()` responses with +`Content-Encoding: br`. Simplex fetches from RPCs, the orderbook, the indexer and bundlers. A malicious +or compromised endpoint could send a crafted response and run code in the solver. + +Remote access does not reach the decoder. The embedded SSH server only negotiates `zlib@openssh.com` +compression, and `UiServer` reads request bodies as raw bytes without decoding them. The exposure is +the runtime. + +## Runtimes + +- The Docker image's builder and runtime stages are on `node:24.22.1` and `node:24.22.1-slim`. +- The desktop app stages Node 24.22.1 (`NODE_VERSION` in `simplex-desktop/scripts/stage-node.mjs`). + The release is signed by the same key the script already pins. +- The desktop release workflow packages on Node 24.22.1, and the staging check expects `v24.22.1`. + +## `engines.node` + +`engines.node` is `^22.23.4 || ^24.22.1 || >=26.12.1`: the first patched release on each supported +line. Node 23 and 25 are end of life and never got the fix, so they are no longer in range. The +`node:sqlite` floor (22.16.0) is still the oldest version the store can run on. `engines` is only +advisory, so npm and pnpm warn on an older Node rather than refuse to install. + +Electron has no release with the fix yet. The packaged solver runs on the staged Node, not +Electron's, so this does not change what the solver is exposed to. diff --git a/sdk/packages/simplex/docs/ai/decisions/2026-09-09-engines-node-floor-is-22-16-0-not-22-13-0-or-24-1236.md b/sdk/packages/simplex/docs/ai/decisions/2026-09-09-engines-node-floor-is-22-16-0-not-22-13-0-or-24-1236.md index be60cc4cf7..589f2b9aca 100644 --- a/sdk/packages/simplex/docs/ai/decisions/2026-09-09-engines-node-floor-is-22-16-0-not-22-13-0-or-24-1236.md +++ b/sdk/packages/simplex/docs/ai/decisions/2026-09-09-engines-node-floor-is-22-16-0-not-22-13-0-or-24-1236.md @@ -3,6 +3,11 @@ Decided: `>=22.16.0`. That is the first release carrying *every* `node:sqlite` API this store calls, which is a stricter bar than the first release where the module is unflagged. +Since 2026-10-23 the declared range is higher: `^22.23.4 || ^24.22.1 || >=26.12.1`, the first +release on each line with the Node2Shell fix (see +`changelog/2026-10-23-node-runtime-node2shell-fix.md`). The `node:sqlite` reasoning below still +sets the oldest version the store itself can run on. + The two dates differ. `node:sqlite` stopped needing `--experimental-sqlite` in v23.4.0 and v22.13.0. But `database.isTransaction`, which `attachOrder`'s rollback branch reads, was added in v24.0.0 and v22.16.0; `database.isOpen`, which `close()` reads, in v23.11.0 and v22.15.0. So diff --git a/sdk/packages/simplex/package.json b/sdk/packages/simplex/package.json index 6814a0e2e9..3f1f0786a3 100644 --- a/sdk/packages/simplex/package.json +++ b/sdk/packages/simplex/package.json @@ -1,6 +1,6 @@ { "name": "@hyperbridge/simplex", - "version": "0.18.3", + "version": "0.18.4", "license": "Apache-2.0", "description": "Automated intent filler for the Hyperbridge IntentGateway — run it as a binary or embed it as a library", "main": "dist/index.js", @@ -114,7 +114,7 @@ "access": "public" }, "engines": { - "node": ">=22.16.0" + "node": "^22.23.4 || ^24.22.1 || >=26.12.1" }, "dependencies": { "@base-ui/react": "^1.8.0", diff --git a/sdk/packages/simplex/scripts/Dockerfile b/sdk/packages/simplex/scripts/Dockerfile index 32ddf72515..21cb61843e 100644 --- a/sdk/packages/simplex/scripts/Dockerfile +++ b/sdk/packages/simplex/scripts/Dockerfile @@ -16,7 +16,7 @@ # --------------------------------------------------------------------------- # Builder — installs the toolchain, compiles protos, bundles the CLI and web UI. # --------------------------------------------------------------------------- -FROM node:24.19.0 AS builder +FROM node:24.22.1 AS builder # protoc is needed by packages/simplex/scripts/build.sh. There is deliberately no python3, # make or g++ here: nothing in the tree compiles any more, so node-gyp has nothing to fall @@ -52,7 +52,7 @@ RUN cd packages/simplex && pnpm build # Runtime — slim base, no compiler toolchain. Keeps the pull small on Docker Desktop # hosts, where every layer lands inside the Linux VM's disk image. # --------------------------------------------------------------------------- -FROM node:24.19.0-slim AS runtime +FROM node:24.22.1-slim AS runtime # Create non-root user RUN groupadd -g 1001 simplex && \ diff --git a/sdk/packages/simplex/src/data/sqlite/index.ts b/sdk/packages/simplex/src/data/sqlite/index.ts index 5a876ccfc3..d175a81695 100644 --- a/sdk/packages/simplex/src/data/sqlite/index.ts +++ b/sdk/packages/simplex/src/data/sqlite/index.ts @@ -31,8 +31,9 @@ export { SqliteStateStore } from "./state" * Set through the PRAGMA rather than `DatabaseSync`'s `timeout` option on purpose. * That option only exists in Node >=22.18 and >=24 (`@types/node` says 22.16, which * is wrong — the version table on nodejs.org is authoritative), so it is silently - * ignored on 22.16, 22.17 and the whole 23 line, all of which `engines.node` - * admits. The PRAGMA is plain SQLite and works wherever `node:sqlite` does. + * ignored on 22.16, 22.17 and the whole 23 line. `engines.node` no longer admits + * those, but it is only advisory. The PRAGMA is plain SQLite and works wherever + * `node:sqlite` does. */ const BUSY_TIMEOUT_MS = 5_000