Skip to content

Enforce the node:sqlite runtime floor at startup and before slot activation #1985

Description

@Jurij89

Deferred from #1984 (10.0.10 release prep) by decision: 10.0.10 ships the declaration plus a documented Known-issue; enforcement lands in 10.0.11.

Background

10.0.10 makes node:sqlite a hard startup dependency. prepareRfc64PersistenceV1() is awaited in DKGAgent.start() outside the surrounding try/catch, and the CLI daemon always passes a dataDir, so on an unsupported runtime the node does not boot.

Supported range (declared in 10.0.10 on @origintrail-official/dkg and @origintrail-official/dkg-agent):

>=22.13.0 <23.0.0 || >=23.4.0

node:sqlite ships from Node 22.5.0 but stays behind --experimental-sqlite until 22.13.0 / 23.4.0, and the daemon builds its command from process.execArgv and never passes that flag.

Why engines is not enough (verified, not assumed)

  • pnpm install --frozen-lockfile with an impossible engines.node: WARN Unsupported engine, install completes, exit 0. With --config.engine-strict=true: ERR_PNPM_UNSUPPORTED_ENGINE, exit 1.
  • npm install --production --no-audit --no-fund (the exact form the npm-slot updater runs): npm warn EBADENGINE, exit 0. With --engine-strict: exit 1.
  • pnpm config get engine-strict in this repo → unset (default false). The repo .npmrc does not set it.

The gap that actually strands a fleet

Blue-green auto-update gates activation on existsSync of build output only — nothing launches the new slot to prove it boots:

  • git path: build-output checks at packages/cli/src/daemon/auto-update.ts:1407-1418, then swapSlot(target) at :1496
  • npm-slot path: checks at :578-598, then swapSlot(target) at :656
  • swapSlot itself: packages/cli/src/config.ts:1812-1830

So a node auto-updating onto 10.0.10 on Node 22.12 installs cleanly, passes every existing check, activates the slot, and then restart-loops — with a message the operator never sees, because dkg start detaches the daemon with stdio: ['ignore','ignore','ignore'] (packages/cli/src/commands/lifecycle.ts:194-203).

dkg update does run a preflight (UPDATE_PREFLIGHT_CHECKS = ['install-layout','version-skew'], packages/cli/src/doctor/index.ts:46), but the unattended auto-update-runner.ts never imports doctor, so scheduled updates have no preflight at all.

Proposed work

Probe capability, not the version string. Use process.getBuiltinModule('node:sqlite') (guard typeof process.getBuiltinModule === 'function'; added v22.3.0) rather than comparing process.version. A capability probe also catches a --without-sqlite custom build and correctly passes someone running 22.10 with --experimental-sqlite. The error message should name the supported range and the current process.version.

  1. New minimal module (no heavy imports), e.g. packages/cli/src/node-runtime-preflight.ts.
  2. Wire before the existing configExists() guard at packages/cli/src/commands/lifecycle.ts:136 — the only surface where the message reaches an operator's terminal.
  3. Wire in the daemon near exitOnStoreConfigErrors (packages/cli/src/daemon/lifecycle.ts:~1400), well before DKGAgent.create at :1699, so --foreground and supervisor respawns log a readable reason.
  4. Extend the shared throw at packages/agent/src/sqlite/module-loader-v1.ts:8 to name the version floor — it is the common site for all consumers (rfc64/inventory-v1/open.ts:603, finalization-recovery-sqlite-schema.ts:258, generic-sql-source.ts:698), so fixing it there sweeps the class.
  5. Add a node-runtime doctor check (packages/cli/src/doctor/), include it in ALL_CHECK_IDS and UPDATE_PREFLIGHT_CHECKS, and add a runtime block to StateSummary.
  6. Gate before swapSlot on both auto-update paths (auto-update.ts:1496 and :656) so unattended updates cannot activate an unbootable slot.
  7. Release-metadata guard: assert in scripts/release-packages.mjs that every publishable package importing node:sqlite declares the supported range, so the contract cannot drift as new packages adopt it. (Raised as a 🟡 on chore(release): bump version set to 10.0.10 #1984.)

Optional hardening: --engine-strict on the npm-slot install (auto-update.ts:558) and engine-strict=true in .npmrc — the latter does bite the git-slot path, since auto-update.ts:1306 runs pnpm with cwd set to a checkout containing .npmrc.

Do not hang the check on the packages/cli postinstall: it is skipped by --ignore-scripts, its best-effort contract forbids failing an install, and it never runs on the git-slot path.

Docs to update alongside

README.md:70 and docs/getting-started/quickstart.md:24 both say "Node.js 22+", which is now under-specified.

Acceptance

  • Node 22.12 (and 23.2) cannot be promoted or started as 10.0.10+; the failure is loud, early, and names the required range.
  • Node 22.13+ / 23.4+ / 24+ installs and starts unchanged.
  • A node auto-updating on an unsupported runtime refuses to activate the slot instead of activating and restart-looping.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions