#1120 · Fresh install silently breaks native modules under npm 11's default allow-scripts policy
TL;DR
Plain-language framing. bb ships as an npm package (bb-app). Three of its dependencies are native add-ons: better-sqlite3 (the database), node-pty (terminals) and @parcel/watcher (file watching). Their compiled .node binaries are not in the package tarball; a small install script that npm runs during npm install downloads (or compiles) them. If npm does not run those scripts, the files are missing and bb dies at startup with Could not locate the bindings file (server) or Failed to load native module: pty.node (host daemon). npm recently introduced an allowScripts policy (RFC npm/rfcs#868) that decides which dependencies may run install scripts.
The reporter is right that this policy breaks bb, but wrong about which npm. On every npm 11.x (11.16–11.19, the versions bundled with Node 24.18+ / 26.3+) the policy is advisory: npm prints npm warn allow-scripts N packages have install scripts not yet covered by allowScripts and then runs the scripts anyway. I installed bb-app@latest (0.38.0) with a clean HOME under npm 11.16.0 and 11.17.0 (the reporter's version): the warning appears, both .node files are present, and bb-server serves GET /api/v1/hosts with 200. The maintainer's own attempt to reproduce on 11.17.0 found the same. So the mechanism the reporter describes ("silently no-ops install scripts") does not exist in npm 11, and whatever broke their install remains unexplained (they never posted the actual failure).
On npm 12 (12.0.0 released 2026-07-08; npm@latest on the registry is 12.0.2 as of today, and every npm 11 prints a "New major version of npm available! → 12.0.2" nag) the policy is enforced: dependency install scripts are blocked unless allowed. I reproduced the exact reported outcome with npm 12.0.2 on all three of bb's install paths: npm install -g bb-app, npx bb-app@latest, and the "Add machine" curl …/install.sh | sh installer served by a dev server at the base commit. npm exits 0, prints only a warning, the two .node files are missing, and bb crashes as described. Crucially, for global/npx installs npm 12 consults only the --allow-scripts flag, the npm_config_allow_scripts env var, or the user/global .npmrc; the installed package's own package.json#allowScripts is not read, so bb-app cannot fix this from inside its package. bb's installer, the daemon self-updater and the docs currently pass nothing. Adding --allow-scripts=better-sqlite3,node-pty,@parcel/watcher to the installer made the npm 12 machine join succeed; npm 10 silently ignores the flag and npm 11 accepts it, so it is safe on every supported Node.
The second sub-claim ("npm installed bb-app, but its global bin directory is not on PATH" on macOS) refers to code that no longer exists: #1419 (merged 2026-08-12, five days after the issue) installs bb-app into a private prefix under the enrollment data dir and runs $prefix/bin/bb-app directly, so PATH no longer matters for the installer. That part is ALREADY FIXED at the base commit.
Claims vs findings
| Claim | Status | Evidence |
|---|---|---|
npm 11.17.0 (Node 24) prints npm warn allow-scripts 4 packages have install scripts not yet covered by allowScripts for @parcel/watcher, better-sqlite3, esbuild, node-pty | Verified | Same warning on 11.16.0 and 11.17.0 for bb-app 0.38.0 (esbuild is no longer a dependency; @google/genai and protobufjs are listed instead). npm11.out, npm11.17.out. |
| npm 11 "silently no-ops install scripts for packages not on the list"; the native bindings are never built | Refuted for npm 11 | npm 11.16.0 and 11.17.0 built both better_sqlite3.node and pty.node and bb-server answered HTTP 200 (npm11.out, npm11.17.out). Arborist's gate in npm 11.16.0 and 11.19.0 only skips packages whose verdict is false (explicit deny); null (unreviewed) still runs (11.16.0 gate, 11.19.0 gate). The maintainer's 11.17.0 debug log in the issue thread shows info run better-sqlite3@12.10.0 install { code: 0 }. |
| The install produced a broken bb ("exact failure mode … Could not locate the bindings file") | Unverified for the reporter's setup; Verified for npm 12 | The reporter never posted the failure. With npm 12.0.2 the outcome is exactly this: scripts blocked, files missing, Could not locate the bindings file / Failed to load native module: pty.node (npm12.out, npx12.out, installer-npm12.out). |
Default is allow-scripts = [""] (empty) and that empty list is what blocks | Refuted (mechanism), Verified (default is empty) | Default is '', parsed to an empty list, which yields no policy (null). In npm 11 no policy means "run and warn"; in npm 12 no policy means "block and warn" (12.0.2 gate). |
| "Will affect essentially every fresh install on a machine with npm 11+" | Refuted for npm 11; true for npm 12 | No released Node bundles npm 12 yet (Node 24.19.0 → npm 11.17.0, Node 26.7.0 → npm 11.19.0, per nodejs.org/dist/index.json), but npm install -g npm@latest gives 12.0.2 today and Node 27 (Oct 2026) will likely bundle it. |
macOS "Add machine" installer failed with npm installed bb-app, but its global bin directory is not on PATH | Verified as a real message in bb-app ≤ 0.35.x; already fixed | The check existed at 65bab835f^:apps/server/src/assets/install-machine.sh L309-L316. #1419 (2026-08-12) replaced npm install -g + command -v bb-app with npm install -g --prefix "$data_dir/npm" + "$prefix/bin/bb-app" (install-machine.sh#L386-L427). Not present at base. |
| Expected: installer should pass an allow-list for its known native deps, or fail loudly | Agree; neither is done at base | No allow-scripts anywhere in the repo (git grep); the installer prints ✓ Installed the server's bb-app build after npm 12 blocked the scripts and only fails 30 s later at the join step with a raw stack trace (installer-npm12.out). |
Environment
- bb
16ceb3a54(main, 2026-08-18). Worktree/home/sawyer/projects/bb/.claude/worktrees/wf_242c3e11-a10-27; dev instance app:16167, server:24167, daemon:32167, data dir/home/sawyer/.bb-dev/projects-bb-.claude-worktrees-wf_242c3e11-a10-27-86a7e16fa77a(used only to serve/install.sh,/install/bb-app.tgzand to accept machine joins). - Linux 7.0.0-29-generic x86_64, node v24.18.0. System npm 11.16.0. Extra npm versions installed privately under
/tmp/1120/npm*/node_modules/.bin/npm: 10.9.9, 11.17.0, 11.19.0, 12.0.2 (registrylateston 2026-08-18). - Package under test:
bb-app@latestfrom the npm registry = 0.38.0 (deps better-sqlite3 12.10.0, node-pty 1.1.0, @parcel/watcher 2.5.6; same versions the issue lists) and, for the installer runs, the dev server's own tarball built from the base commit. - Every run used a fresh
HOMEunder/tmp/1120/(no~/.npmrc), a private npm cache, and private ports (39871–39873, installer-assigned 38888). Nothing under~/.bbor the real instance on :38886 was touched.
Minimal reproduction
All scripts are in 1120/repro/. They only need node, curl, and an npm binary; the only setup step is putting npm 12 somewhere: npm install --prefix /tmp/1120/npm12 npm@12.0.2 (gives /tmp/1120/npm12/node_modules/.bin/npm).
1. npm install -g bb-app: npm 11 works, npm 12 breaks
Script 1120-install-matrix.sh (usage 1120-install-matrix.sh <label> <npm-binary> [extra npm args]): clean HOME, npm install -g --prefix /tmp/1120/g-<label> bb-app@latest, then checks the two .node files and starts bb-server for 6 s and curls it. Before running: set ROOT=/tmp/<yours> and BB_PORT=<free port> (defaults /tmp/1120, 39871; the script exits 2 if the port is busy); each run downloads ~1 GB into $ROOT/cache and unpacks ~300 packages (tens of thousands of inodes on tmpfs), so rm -rf $ROOT afterwards. The npx and installer scripts take the same ROOT variable.
Control, npm 11.17.0 (the reporter's version; 11.16.0 gives the same, npm11.out): warning printed, files present, server answers. (Re-run on a fresh port after the verifier caught that the first paste's HTTP 200 came from a stale server left behind by an earlier run, see Verification below; the script now refuses a busy port and kills the whole process group.)
== npm: 11.17.0 node: v24.18.0 HOME=/tmp/1120/home-npm11.17 prefix=/tmp/1120/g-npm11.17
== ~/.npmrc present? no
== $ /tmp/1120/npm1117/node_modules/.bin/npm install -g --prefix /tmp/1120/g-npm11.17 bb-app@latest
npm warn deprecated prebuild-install@7.1.3: No longer maintained. Please contact the author of the relevant native addon; alternatives are available.
npm warn deprecated node-domexception@1.0.0: Use your platform's native DOMException instead
npm warn deprecated node-domexception@1.0.0: Use your platform's native DOMException instead
added 316 packages in 9s
26 packages are looking for funding
run `npm fund` for details
npm warn allow-scripts 7 packages have install scripts not yet covered by allowScripts:
npm warn allow-scripts @parcel/watcher@2.5.6 (install: node scripts/build-from-source.js)
npm warn allow-scripts better-sqlite3@12.10.0 (install: prebuild-install || node-gyp rebuild --release)
npm warn allow-scripts node-pty@1.1.0 (install: node scripts/prebuild.js || node-gyp rebuild; postinstall: node scripts/post-install.js)
npm warn allow-scripts @google/genai@1.52.0 (preinstall: echo 'preinstall: no-op')
npm warn allow-scripts protobufjs@7.6.5 (postinstall: node scripts/postinstall)
npm warn allow-scripts @google/genai@1.52.0 (preinstall: echo 'preinstall: no-op')
npm warn allow-scripts protobufjs@7.6.5 (postinstall: node scripts/postinstall)
npm warn allow-scripts
npm warn allow-scripts Run `npm install -g --allow-scripts=@parcel/watcher,better-sqlite3,node-pty,@google/genai,protobufjs,@google/genai,protobufjs` to allow these scripts once, or `npm config set allow-scripts=@parcel/watcher,better-sqlite3,node-pty,@google/genai,protobufjs,@google/genai,protobufjs --location=user` to allow them for all global installs.
== npm exit code: 0
== installed bb-app version: 0.38.0
== PRESENT better-sqlite3/build/Release/better_sqlite3.node
== PRESENT node-pty/build/Release/pty.node
== @parcel/watcher-* prebuilt platform packages: 1
== $ BB_DATA_DIR=/tmp/1120/data-npm11.17 BB_SERVER_PORT=39901 /tmp/1120/g-npm11.17/bin/bb-server (background, then curl /api/v1/hosts after 6s)
== curl http://127.0.0.1:39901/api/v1/hosts -> HTTP 200
== bb-server exit: 143
== still listening on :39901 after kill? no
== first 20 lines of bb-server stdout/stderr:
[14:54:59] INFO: [server] Server listening {"bindHost":"127.0.0.1","port":39901,"dataDir":"/tmp/1120/data-npm11.17"}
[14:54:59] INFO: [server] plugin automations@0.1.0 loaded
[14:54:59] INFO: [server] plugin connect@0.1.0 loaded
[14:54:59] INFO: [server] plugin custom-instructions@0.1.0 loaded
[14:54:59] INFO: [server] plugin inline-vis@0.1.0 loaded
[14:54:59] INFO: [server] plugin secrets@0.1.0 loaded
[14:54:59] INFO: [server] plugin side-chat@0.1.0 loaded
npm 12.0.2: the wording changes from "not yet covered" to "blocked", npm still exits 0, both files are missing, and bb-server dies with the README's "bindings file" error. This is the issue's reported failure mode.
== npm: 12.0.2 node: v24.18.0 HOME=/tmp/1120/home-npm12 prefix=/tmp/1120/g-npm12
== ~/.npmrc present? no
== $ /tmp/1120/npm12/node_modules/.bin/npm install -g --prefix /tmp/1120/g-npm12 bb-app@latest
added 200 packages in 4s
26 packages are looking for funding
run `npm fund` for details
npm warn install-scripts 5 packages had install scripts blocked because they are not covered by allowScripts:
npm warn install-scripts @parcel/watcher@2.5.6 (install: node scripts/build-from-source.js)
npm warn install-scripts better-sqlite3@12.10.0 (install: prebuild-install || node-gyp rebuild --release)
npm warn install-scripts node-pty@1.1.0 (install: node scripts/prebuild.js || node-gyp rebuild; postinstall: node scripts/post-install.js)
npm warn install-scripts @google/genai@1.52.0 (preinstall: echo 'preinstall: no-op')
npm warn install-scripts protobufjs@7.6.5 (postinstall: node scripts/postinstall)
npm warn install-scripts
npm warn install-scripts Run `npm install -g --allow-scripts=@parcel/watcher,better-sqlite3,node-pty,@google/genai,protobufjs` to allow these scripts once, or `npm config set allow-scripts=@parcel/watcher,better-sqlite3,node-pty,@google/genai,protobufjs --location=user` to allow them for all global installs.
== npm exit code: 0
== installed bb-app version: 0.38.0
== MISSING better-sqlite3/build/Release/better_sqlite3.node
== MISSING node-pty/build/Release/pty.node
== @parcel/watcher-* prebuilt platform packages: 1
== $ BB_DATA_DIR=/tmp/1120/data-npm12 BB_SERVER_PORT=39871 /tmp/1120/g-npm12/bin/bb-server (background, then curl /api/v1/hosts after 6s)
== curl http://127.0.0.1:39871/api/v1/hosts -> HTTP 000
== bb-server exit: 1
== first 20 lines of bb-server stdout/stderr:
Error: Could not locate the bindings file. Tried:
→ /tmp/1120/g-npm12/lib/node_modules/bb-app/node_modules/better-sqlite3/build/better_sqlite3.node
→ /tmp/1120/g-npm12/lib/node_modules/bb-app/node_modules/better-sqlite3/build/Debug/better_sqlite3.node
→ /tmp/1120/g-npm12/lib/node_modules/bb-app/node_modules/better-sqlite3/build/Release/better_sqlite3.node
→ /tmp/1120/g-npm12/lib/node_modules/bb-app/node_modules/better-sqlite3/out/Debug/better_sqlite3.node
→ /tmp/1120/g-npm12/lib/node_modules/bb-app/node_modules/better-sqlite3/Debug/better_sqlite3.node
→ /tmp/1120/g-npm12/lib/node_modules/bb-app/node_modules/better-sqlite3/out/Release/better_sqlite3.node
→ /tmp/1120/g-npm12/lib/node_modules/bb-app/node_modules/better-sqlite3/Release/better_sqlite3.node
→ /tmp/1120/g-npm12/lib/node_modules/bb-app/node_modules/better-sqlite3/build/default/better_sqlite3.node
→ /tmp/1120/g-npm12/lib/node_modules/bb-app/node_modules/better-sqlite3/compiled/24.18.0/linux/x64/better_sqlite3.node
→ /tmp/1120/g-npm12/lib/node_modules/bb-app/node_modules/better-sqlite3/addon-build/release/install-root/better_sqlite3.node
→ /tmp/1120/g-npm12/lib/node_modules/bb-app/node_modules/better-sqlite3/addon-build/debug/install-root/better_sqlite3.node
→ /tmp/1120/g-npm12/lib/node_modules/bb-app/node_modules/better-sqlite3/addon-build/default/install-root/better_sqlite3.node
→ /tmp/1120/g-npm12/lib/node_modules/bb-app/node_modules/better-sqlite3/lib/binding/node-v137-linux-x64/better_sqlite3.node
at bindings (/tmp/1120/g-npm12/lib/node_modules/bb-app/node_modules/bindings/bindings.js:126:9)
at new Database (/tmp/1120/g-npm12/lib/node_modules/bb-app/node_modules/better-sqlite3/lib/database.js:48:64)
at createConnection (file:///tmp/1120/g-npm12/lib/node_modules/bb-app/server/dist/start-server.js:28217:18)
at initDb (file:///tmp/1120/g-npm12/lib/node_modules/bb-app/server/dist/start-server.js:284058:14)
at Module.runServer (file:///tmp/1120/g-npm12/lib/node_modules/bb-app/server/dist/start-server.js:324008:14)
at main (file:///tmp/1120/g-npm12/lib/node_modules/bb-app/server/dist/index.js:18499:22)
npm 12.0.2 with the allow-list (--allow-scripts=better-sqlite3,node-pty,@parcel/watcher): files present, HTTP 200. Note npm still blocks @google/genai's no-op preinstall and protobufjs's postinstall; bb starts fine without them.
== npm: 12.0.2 node: v24.18.0 HOME=/tmp/1120/home-npm12-allow prefix=/tmp/1120/g-npm12-allow
== ~/.npmrc present? no
== $ /tmp/1120/npm12/node_modules/.bin/npm install -g --prefix /tmp/1120/g-npm12-allow bb-app@latest --allow-scripts=better-sqlite3,node-pty,@parcel/watcher
added 200 packages in 7s
26 packages are looking for funding
run `npm fund` for details
npm warn install-scripts 2 packages had install scripts blocked because they are not covered by allowScripts:
npm warn install-scripts @google/genai@1.52.0 (preinstall: echo 'preinstall: no-op')
npm warn install-scripts protobufjs@7.6.5 (postinstall: node scripts/postinstall)
npm warn install-scripts
npm warn install-scripts Run `npm install -g --allow-scripts=@google/genai,protobufjs` to allow these scripts once, or `npm config set allow-scripts=@google/genai,protobufjs --location=user` to allow them for all global installs.
== npm exit code: 0
== installed bb-app version: 0.38.0
== PRESENT better-sqlite3/build/Release/better_sqlite3.node
== PRESENT node-pty/build/Release/pty.node
== @parcel/watcher-* prebuilt platform packages: 1
== $ BB_DATA_DIR=/tmp/1120/data-npm12-allow BB_SERVER_PORT=39871 /tmp/1120/g-npm12-allow/bin/bb-server (background, then curl /api/v1/hosts after 6s)
== curl http://127.0.0.1:39871/api/v1/hosts -> HTTP 200
== bb-server exit: 143
== first 20 lines of bb-server stdout/stderr:
[07:45:37] INFO: [server] Server listening {"bindHost":"127.0.0.1","port":39871,"dataDir":"/tmp/1120/data-npm12-allow"}
[07:45:39] INFO: [server] plugin automations@0.1.0 loaded
[07:45:40] INFO: [server] plugin connect@0.1.0 loaded
[07:45:41] INFO: [server] plugin custom-instructions@0.1.0 loaded
[07:45:41] INFO: [server] plugin inline-vis@0.1.0 loaded
[07:45:41] WARN: [server] marketplace bb-community entry "cascade" icon https://getbb.app/marketplace/v1/icons/cascade-7b651515.svg was rejected:
[07:45:41] WARN: [server] marketplace bb-community entry "ntfy" icon https://getbb.app/marketplace/v1/icons/ntfy-2ef77317.svg was rejected:
[07:45:41] WARN: [server] marketplace bb-community entry "slopcop" icon https://getbb.app/marketplace/v1/icons/slopcop-35963518.svg was rejected:
[07:45:41] WARN: [server] marketplace bb-community entry "sticky-notes" icon https://getbb.app/marketplace/v1/icons/sticky-notes-5dc1948a.svg was rejected:
[07:45:41] INFO: [server] plugin secrets@0.1.0 loaded
[07:45:42] INFO: [server] plugin side-chat@0.1.0 loaded
[07:45:42] INFO: [server] Event loop stalled {"intervalMs":5000,"maxDelayMs":1373.6,"meanDelayMs":206.6,"p99DelayMs":1373.6,"resolutionMs":20,"thresholdMs":500,"currentWork":null,"lastWork":"GET /api/v1/hosts","lastWorkMs":4.5,"slowestWork":"GET /api/v1/hosts","slowestWorkMs":4.5}
2. npx bb-app@latest under npm 12
Script 1120-npx.sh (usage 1120-npx.sh <label> <npx-binary>). Same result on the quick-start path; the launcher's health check times out because the server child crashed:
== npx: 12.0.2 node: v24.18.0 HOME=/tmp/1120/home-npx12
== $ BB_DATA_DIR=/tmp/1120/data-npx12 BB_SERVER_PORT=39872 BB_HOST_DAEMON_PORT=39873 /tmp/1120/npm12/node_modules/.bin/npx --yes bb-app@latest (30s timeout)
npm notice run npx
npm notice run 'bb-app'
bb
○ Starting server
Error: Could not locate the bindings file. Tried:
→ /tmp/1120/cache-npx12/_npx/614ebd23ff24de90/node_modules/better-sqlite3/build/better_sqlite3.node
→ /tmp/1120/cache-npx12/_npx/614ebd23ff24de90/node_modules/better-sqlite3/build/Debug/better_sqlite3.node
→ /tmp/1120/cache-npx12/_npx/614ebd23ff24de90/node_modules/better-sqlite3/build/Release/better_sqlite3.node
→ /tmp/1120/cache-npx12/_npx/614ebd23ff24de90/node_modules/better-sqlite3/out/Debug/better_sqlite3.node
→ /tmp/1120/cache-npx12/_npx/614ebd23ff24de90/node_modules/better-sqlite3/Debug/better_sqlite3.node
→ /tmp/1120/cache-npx12/_npx/614ebd23ff24de90/node_modules/better-sqlite3/out/Release/better_sqlite3.node
→ /tmp/1120/cache-npx12/_npx/614ebd23ff24de90/node_modules/better-sqlite3/Release/better_sqlite3.node
→ /tmp/1120/cache-npx12/_npx/614ebd23ff24de90/node_modules/better-sqlite3/build/default/better_sqlite3.node
→ /tmp/1120/cache-npx12/_npx/614ebd23ff24de90/node_modules/better-sqlite3/compiled/24.18.0/linux/x64/better_sqlite3.node
→ /tmp/1120/cache-npx12/_npx/614ebd23ff24de90/node_modules/better-sqlite3/addon-build/release/install-root/better_sqlite3.node
→ /tmp/1120/cache-npx12/_npx/614ebd23ff24de90/node_modules/better-sqlite3/addon-build/debug/install-root/better_sqlite3.node
→ /tmp/1120/cache-npx12/_npx/614ebd23ff24de90/node_modules/better-sqlite3/addon-build/default/install-root/better_sqlite3.node
→ /tmp/1120/cache-npx12/_npx/614ebd23ff24de90/node_modules/better-sqlite3/lib/binding/node-v137-linux-x64/better_sqlite3.node
at bindings (/tmp/1120/cache-npx12/_npx/614ebd23ff24de90/node_modules/bindings/bindings.js:126:9)
at new Database (/tmp/1120/cache-npx12/_npx/614ebd23ff24de90/node_modules/better-sqlite3/lib/database.js:48:64)
at createConnection (file:///tmp/1120/cache-npx12/_npx/614ebd23ff24de90/node_modules/bb-app/server/dist/start-server.js:28217:18)
at initDb (file:///tmp/1120/cache-npx12/_npx/614ebd23ff24de90/node_modules/bb-app/server/dist/start-server.js:284058:14)
at Module.runServer (file:///tmp/1120/cache-npx12/_npx/614ebd23ff24de90/node_modules/bb-app/server/dist/start-server.js:324008:14)
at main (file:///tmp/1120/cache-npx12/_npx/614ebd23ff24de90/node_modules/bb-app/server/dist/index.js:18499:22)
✗ Server failed to start (health check timed out)
Check logs: /tmp/1120/data-npx12/logs/
● Shutting down
== exit: 0
With npx --allow-scripts=better-sqlite3,node-pty,@parcel/watcher bb-app@latest (1120-npx-allow.sh) the full stack comes up ("bb is ready", daemon session opened): npx12-allow.out.
3. The "Add machine" installer under npm 12 (end to end against a bb server)
Script 1120-installer.sh (usage BB_SERVER_URL=http://localhost:24167 1120-installer.sh <label> <dir containing npm>). It asks the server for a join code (POST /api/v1/hosts/join-codes), then runs the exact command the Add-machine dialog shows (curl …/install.sh | sh -s -- --join-code … --host-id … --server …) with BB_DATA_DIR under /tmp, BB_INSTALL_SKIP_SERVICE=1 (no launchd/systemd unit), and the chosen npm first on PATH. The server offered its own tarball, so this is the npm install -g --prefix … bb-app.tgz branch at install-machine.sh#L391.
npm 12.0.2: npm blocks the scripts, the installer still prints ✓ Installed the server's bb-app build, and the join fails because the temporary daemon crashes loading pty.node:
== npm on PATH: /tmp/1120/npm12/node_modules/.bin/npm -> 12.0.2; node v24.18.0; HOME=/tmp/1120/home-inst-npm12; BB_DATA_DIR=/tmp/1120/machine-inst-npm12
== join code response: {"joinCode":"bbde_REDACTED_JOIN_CODE","hostId":"host_j2j33czvkt","expiresAt":1787040325304}
== $ curl -fsSL http://localhost:24167/install.sh | sh -s -- --join-code <code> --host-id host_j2j33czvkt --server http://localhost:24167
bb machine setup
○ Setting up this machine as host_j2j33czvkt for http://localhost:24167
✓ Using local host-daemon port 38888
○ Downloading the server's bb-app package (timeout: 5 minutes)
✓ Downloaded the server's bb-app package
○ Installing the server's bb-app build
added 200 packages in 8s
26 packages are looking for funding
run `npm fund` for details
npm warn install-scripts 5 packages had install scripts blocked because they are not covered by allowScripts:
npm warn install-scripts @parcel/watcher@2.5.6 (install: node scripts/build-from-source.js)
npm warn install-scripts better-sqlite3@12.10.0 (install: prebuild-install || node-gyp rebuild --release)
npm warn install-scripts node-pty@1.1.0 (install: node scripts/prebuild.js || node-gyp rebuild; postinstall: node scripts/post-install.js)
npm warn install-scripts @google/genai@1.52.0 (preinstall: echo 'preinstall: no-op')
npm warn install-scripts protobufjs@7.6.5 (postinstall: node scripts/postinstall)
npm warn install-scripts
npm warn install-scripts Run `npm install -g --allow-scripts=@parcel/watcher,better-sqlite3,node-pty,@google/genai,protobufjs` to allow these scripts once, or `npm config set allow-scripts=@parcel/watcher,better-sqlite3,node-pty,@google/genai,protobufjs --location=user` to allow them for all global installs.
✓ Installed the server's bb-app build
○ Joining http://localhost:24167 as host_j2j33czvkt
Join progress is logged to /tmp/1120/machine-inst-npm12/install-join.log
○ Waiting for the temporary host daemon to connect (up to about 2 minutes)
✗ bb host daemon exited before it connected to http://localhost:24167.
See /tmp/1120/machine-inst-npm12/install-join.log
== installer exit: 1
== MISSING better-sqlite3/build/Release/better_sqlite3.node
== MISSING node-pty/build/Release/pty.node
== install-join.log:
bb host-daemon
○ Enrolling and starting daemon
/tmp/1120/machine-inst-npm12/npm/lib/node_modules/bb-app/node_modules/node-pty/lib/utils.js:36
throw new Error("Failed to load native module: " + name + ".node, checked: " + dirs.join(', ') + ": " + lastError);
^
Error: Failed to load native module: pty.node, checked: build/Release, build/Debug, prebuilds/linux-x64: Error: Cannot find module './prebuilds/linux-x64//pty.node'
Require stack:
- /tmp/1120/machine-inst-npm12/npm/lib/node_modules/bb-app/node_modules/node-pty/lib/utils.js
- /tmp/1120/machine-inst-npm12/npm/lib/node_modules/bb-app/node_modules/node-pty/lib/index.js
at Object.loadNativeModule (/tmp/1120/machine-inst-npm12/npm/lib/node_modules/bb-app/node_modules/node-pty/lib/utils.js:36:11)
== killed temporary daemon
== host status on server:
{"code":"host_not_found","message":"Host not found"}
Control, system npm 11.16.0 (installer-npm11.out): ✓ Joined successfully, host connected on the server. So the installer itself is fine at base as long as npm runs the scripts.
== npm on PATH: /home/sawyer/.nvm/versions/node/v24.18.0/bin/npm -> 11.16.0; node v24.18.0; HOME=/tmp/1120/home-inst-npm11; BB_DATA_DIR=/tmp/1120/machine-inst-npm11
== join code response: {"joinCode":"bbde_REDACTED_JOIN_CODE","hostId":"host_44k34yk6av","expiresAt":1787040358033}
== $ curl -fsSL http://localhost:24167/install.sh | sh -s -- --join-code <code> --host-id host_44k34yk6av --server http://localhost:24167
bb machine setup
○ Setting up this machine as host_44k34yk6av for http://localhost:24167
✓ Using local host-daemon port 38888
○ Downloading the server's bb-app package (timeout: 5 minutes)
✓ Downloaded the server's bb-app package
○ Installing the server's bb-app build
added 316 packages in 11s
26 packages are looking for funding
run `npm fund` for details
npm warn allow-scripts 7 packages have install scripts not yet covered by allowScripts:
npm warn allow-scripts
npm warn allow-scripts Run `npm approve-scripts --allow-scripts-pending` to review, or `npm approve-scripts <pkg>` to allow.
✓ Installed the server's bb-app build
○ Joining http://localhost:24167 as host_44k34yk6av
Join progress is logged to /tmp/1120/machine-inst-npm11/install-join.log
○ Waiting for the temporary host daemon to connect (up to about 2 minutes)
✓ Joined successfully
! Service installation skipped; daemon PID 2584093 is still running.
== installer exit: 0
== PRESENT better-sqlite3/build/Release/better_sqlite3.node
== PRESENT node-pty/build/Release/pty.node
== install-join.log:
bb host-daemon
○ Enrolling and starting daemon
✓ Host daemon running
● bb host-daemon is ready
server http://localhost:24167
daemon 38888
data /tmp/1120/machine-inst-npm11
logs /tmp/1120/machine-inst-npm11/logs/
lock /tmp/1120/machine-inst-npm11/daemon.lock
== killed temporary daemon
== host status on server:
{"id":"host_44k34yk6av","name":"bee","type":"persistent","status":"disconnected","maxPermissionMode":"full","lastSeenAt":1787039470609,"lastRejectedProtocolVersion":null,"createdAt":1787039470190,"updatedAt":1787039470609}
4. Candidate fix, verified
Diff 1120-installer-fix.diff (applied to my worktree only while running these two tests, then reverted): add --allow-scripts=better-sqlite3,node-pty,@parcel/watcher to both npm install -g lines and verify the two add-ons load before continuing. Same installer run, npm 12.0.2:
== npm on PATH: /tmp/1120/npm12/node_modules/.bin/npm -> 12.0.2; node v24.18.0; HOME=/tmp/1120/home-inst-npm12-fixed; BB_DATA_DIR=/tmp/1120/machine-inst-npm12-fixed
== join code response: {"joinCode":"bbde_REDACTED_JOIN_CODE","hostId":"host_yfnrjxiaff","expiresAt":1787040498554}
== $ curl -fsSL http://localhost:24167/install.sh | sh -s -- --join-code <code> --host-id host_yfnrjxiaff --server http://localhost:24167
bb machine setup
○ Setting up this machine as host_yfnrjxiaff for http://localhost:24167
✓ Using local host-daemon port 38888
○ Downloading the server's bb-app package (timeout: 5 minutes)
✓ Downloaded the server's bb-app package
○ Installing the server's bb-app build
added 200 packages in 10s
26 packages are looking for funding
run `npm fund` for details
npm warn install-scripts 2 packages had install scripts blocked because they are not covered by allowScripts:
npm warn install-scripts
npm warn install-scripts Run `npm install -g --allow-scripts=@google/genai,protobufjs` to allow these scripts once, or `npm config set allow-scripts=@google/genai,protobufjs --location=user` to allow them for all global installs.
✓ Installed the server's bb-app build
○ Joining http://localhost:24167 as host_yfnrjxiaff
Join progress is logged to /tmp/1120/machine-inst-npm12-fixed/install-join.log
○ Waiting for the temporary host daemon to connect (up to about 2 minutes)
✓ Joined successfully
! Service installation skipped; daemon PID 2610364 is still running.
== installer exit: 0
== PRESENT better-sqlite3/build/Release/better_sqlite3.node
== PRESENT node-pty/build/Release/pty.node
== install-join.log:
bb host-daemon
○ Enrolling and starting daemon
✓ Host daemon running
● bb host-daemon is ready
server http://localhost:24167
daemon 38888
data /tmp/1120/machine-inst-npm12-fixed
logs /tmp/1120/machine-inst-npm12-fixed/logs/
lock /tmp/1120/machine-inst-npm12-fixed/daemon.lock
== killed temporary daemon
== host status on server:
{"id":"host_yfnrjxiaff","name":"bee","type":"persistent","status":"connected","maxPermissionMode":"full","lastSeenAt":1787039609684,"lastRejectedProtocolVersion":null,"createdAt":1787039609224,"updatedAt":1787039609684}
And the loud-failure path (system npm 11.16.0 with npm_config_ignore_scripts=true, i.e. the #1057 case), instead of the 30 s wait and stack trace:
== npm on PATH: /home/sawyer/.nvm/versions/node/v24.18.0/bin/npm -> 11.16.0; node v24.18.0; HOME=/tmp/1120/home-inst-npm11-ignorescripts-fixed; BB_DATA_DIR=/tmp/1120/machine-inst-npm11-ignorescripts-fixed
== join code response: {"joinCode":"bbde_REDACTED_JOIN_CODE","hostId":"host_a4jq9ahrhx","expiresAt":1787040517034}
== $ curl -fsSL http://localhost:24167/install.sh | sh -s -- --join-code <code> --host-id host_a4jq9ahrhx --server http://localhost:24167
bb machine setup
○ Setting up this machine as host_a4jq9ahrhx for http://localhost:24167
✓ Using local host-daemon port 38888
○ Downloading the server's bb-app package (timeout: 5 minutes)
✓ Downloaded the server's bb-app package
○ Installing the server's bb-app build
added 316 packages in 10s
26 packages are looking for funding
run `npm fund` for details
✓ Installed the server's bb-app build
✗ npm installed bb-app, but its native add-ons (better-sqlite3, node-pty) were not built.
npm did not run their install scripts. Check the npm warnings above; if it mentions allowScripts or ignore-scripts, rerun with: npm_config_allow_scripts=better-sqlite3,node-pty,@parcel/watcher npm_config_ignore_scripts=false <this command>
== installer exit: 1
== MISSING better-sqlite3/build/Release/better_sqlite3.node
== MISSING node-pty/build/Release/pty.node
== install-join.log:
== host status on server:
{"code":"host_not_found","message":"Host not found"}
Compatibility of the flag: npm 10.9.9 (Node 22.19–22.23) neither errors nor warns on --allow-scripts / npm_config_allow_scripts, and still builds better-sqlite3, in the -g --prefix form the installer uses (flag and env-var variants) as well as in a project-scoped install (script 1120-npm10-allow-scripts.sh, output npm10-allow-scripts-flag.out, commands echoed inline). Note that in a project-scoped install npm 12 rejects --allow-scripts with EALLOWSCRIPTS; bb's installs are all -g/npx, where the flag is the sanctioned mechanism.
Root cause
1. bb needs npm to run dependency install scripts. bb-app depends on better-sqlite3, node-pty and @parcel/watcher (packages/bb-app/package.json#L68-L77). better-sqlite3's install script (prebuild-install || node-gyp rebuild) and node-pty's (node scripts/prebuild.js || node-gyp rebuild) are the only things that put build/Release/*.node on disk. @parcel/watcher is fine without its script because it also ships prebuilt platform packages as optionalDependencies (my npm 12 run shows one @parcel/watcher-* platform package installed). The server opens SQLite at startup (createConnection in packages/db) and the host daemon loads node-pty at startup, so a missing binary is fatal for both processes.
2. npm's allowScripts policy (RFC 868) went from advisory (npm 11) to enforced (npm 12). The default allow-scripts value is empty, which produces no policy; the gate then behaves differently per major:
npm 11.16.0 (Phase 1; 11.19.0 is equivalent) — only an explicit deny skips a package:
// Phase 1 allowScripts gate: a `false` verdict from the policy matcher
// means the user explicitly denied install scripts for this node, so skip
// it. `true` and `null` (unreviewed) both fall through to the existing
// detection logic — unreviewed nodes still run their scripts in Phase 1
// and are surfaced via the post-reify advisory warning. The global
// --ignore-scripts kill switch in #build() still takes precedence, and
// --dangerously-allow-all-scripts bypasses this gate entirely.
if (!this.options.dangerouslyAllowAllScripts &&
isScriptAllowed(node, this.options.allowScripts) === false) {
return
}
npm 12.0.2 — anything not explicitly allowed is skipped (bins are still linked, so bb-app looks installed):
const { preinstall, install, postinstall, prepare } = scripts
const tests = { bin, preinstall, install, postinstall, prepare }
// allowScripts gate (RFC npm/rfcs#868): `true` runs lifecycle
// scripts; `false` and `null` (unreviewed) block. Bypassed by
// --dangerously-allow-all-scripts and workspaces (owner-managed).
// --ignore-scripts still wins (in #build); bins are never gated.
//
// Checked on node.target, not the Link: a Link's `resolved` is
// node_modules-relative (`file:../../dep`) so it can't match a
// project-root-relative policy key; the target carries the realpath
// and link specs that script-allowed.js matches on (npm/cli#9498).
// For non-links node.target === node, so registry deps are unaffected.
const scriptsAllowed =
this.options.dangerouslyAllowAllScripts ||
node.isWorkspace ||
isScriptAllowed(node.target, this.options.allowScripts) === true
for (const [key, has] of Object.entries(tests)) {
if (!has) {
continue
}
if (key !== 'bin' && !scriptsAllowed) {
continue
}
this.#queues[key].push(node)
}
}
The 12.0.0 release notes state it directly: "Dependency lifecycle scripts are now blocked by default unless allowed by the root package's allowScripts policy" (npm-12.0.0-notes.md).
3. For -g/npx the only inputs are the CLI flag, env, or .npmrc. npm 12's resolver skips the package.json layer whenever npm.global is set or for npm exec, so bb-app cannot declare its own allow-list in package.json#allowScripts:
// Resolve the effective allowScripts policy from the layered sources.
// Returns `{ policy, source }` where:
// - `policy` is an object map of `package-spec` -> boolean, or `null` if
// no layer has any configuration
// - `source` is one of `'cli'`, `'package.json'`, `'.npmrc'`, or `null`
//
// Precedence order (highest to lowest), per RFC npm/rfcs#868:
// 1. CLI flags (--allow-scripts) and env vars
// 2. Root `package.json#allowScripts`
// 3. `.npmrc` cascade (project, user, global)
//
// The project `package.json` layer is skipped when:
// - `npm.global` is true (no project context exists for global installs)
// - `skipProjectConfig` is true (e.g. npm exec / npx, which per the RFC
// consult only user/global .npmrc)
//
// In both skipped cases, the CLI and .npmrc layers are still consulted;
// only the project package.json layer is skipped.
//
// The first source with any configuration wins for the entire install;
// lower layers are ignored. A `log.warn` is emitted whenever a setting is
// being suppressed by a higher-priority source.
//
// Reads `package.json` from `npm.prefix` (not `npm.localPrefix`) so an
// install run from a workspace sub-directory still picks up the project
// root's policy.
const resolveAllowScripts = async (npm, { skipProjectConfig = false } = {}) => {
// Independently probe each RFC layer.
const cliPolicy = policyFromSources(npm, ['cli', 'env'])
const npmrcPolicy = policyFromSources(npm, ['project', 'user', 'global', 'builtin'])
// The --allow-scripts CLI flag is intended for one-off and global
// contexts (npm exec, npx, npm install -g). In a project-scoped install,
// team policy belongs in package.json or .npmrc, so reject the flag
// outright to avoid the "works on my machine" footgun.
if (cliPolicy && !npm.global && !skipProjectConfig) {
throw Object.assign(
new Error(
'--allow-scripts is not allowed in project-scoped installs. ' +
'Add the entries to the "allowScripts" field in package.json, ' +
'or to .npmrc, instead.'
),
{ code: 'EALLOWSCRIPTS' }
)
}
// Project package.json is consulted only when the caller is operating
// inside a real project (not -g, not npx).
let pkgPolicy = null
if (!npm.global && !skipProjectConfig) {
try {
const { content } = await pkgJson.normalize(npm.prefix)
if (content?.allowScripts && typeof content.allowScripts === 'object') {
const entries = Object.entries(content.allowScripts)
if (entries.length > 0) {
pkgPolicy = Object.fromEntries(entries)
}
}
} catch (err) {
log.silly('install-scripts', 'no package.json at prefix', err.message)
4. bb passes nothing and never checks the result. The installer's two npm calls (L391, L409), the daemon self-updater (protocol-self-update.ts#L149-L151: npm install -g [--prefix …] tarball) and every documented npx bb-app@latest / npm install -g bb-app command run without --allow-scripts. After the install the installer only checks that $prefix/bin/bb-app is executable (L423-L428), which npm 12 still creates ("bins are never gated"), so the script reports success and the failure surfaces later as an opaque native-module stack trace. The README's troubleshooting entry (README.md#L190-L222) covers only the ignore-scripts=true cause from #1057.
Why the symptom follows. npm 12 → no policy → arborist queues no install scripts for better-sqlite3/node-pty → no .node files → npm exits 0 with a warning → bb-server throws Could not locate the bindings file at createConnection, bb host-daemon throws Failed to load native module: pty.node. Under npm 11 the identical warning is printed but the scripts run, which is why the reporter's log alone does not prove their install was broken by this policy; their npm 11.17.0 breakage (if real) had another cause they did not report.
Deeper issue. Every bb install path relies on npm running third-party install scripts, and the trend in npm is to stop doing that by default. Anything that reduces the number of script-dependent native add-ons (the maintainer's node:sqlite idea removes better-sqlite3; node-pty is the other hard requirement) shrinks the problem, but as long as any remain, the install surfaces must opt them in explicitly and verify the result.
Proposed fix (first principles)
- Opt in explicitly wherever bb runs npm. Add
--allow-scripts=better-sqlite3,node-pty,@parcel/watcherto bothnpm install -glines inapps/server/src/assets/install-machine.shand to the self-updater's argv inapps/host-daemon/src/protocol-self-update.ts(defaultInstallTarball). Bare names are the RFC's sanctioned form; exact pins (better-sqlite3@12.10.0) would have to be kept in sync withpackages/bb-app/package.jsonon every bump, so bare names are the lower-maintenance choice. Verified above on npm 10 (ignored), 11 (accepted), 12 (required). Update the fakenpminapps/server/test/app/install-machine-script.test.ts(regexes at L371/L392/L434 assert the exact argv) in the same change. No wire shape changes, so noHOST_DAEMON_PROTOCOL_VERSIONbump. - Fail loudly at install time. After
npm install, the installer shouldnode -e 'require(root+"/node_modules/better-sqlite3"); require(root+"/node_modules/node-pty")'and exit non-zero with a message that names--allow-scripts/ignore-scripts(see the diff; the test's fake npm then needs to drop stub modules, or the check can be gated the wayBB_INSTALL_SKIP_SERVICEis). Thebb-applauncher could do the same preflight and print the README remedy instead of a stack trace; that also improves the #1057 experience. - Document the npm 12 path. README quick start / troubleshooting and
packages/bb-app/README.md:npx --allow-scripts=better-sqlite3,node-pty,@parcel/watcher bb-app@latest(ornpm config set allow-scripts=better-sqlite3,node-pty,@parcel/watcher --location=useronce), alongside the existingnpm_config_ignore_scripts=falseparagraph. Per AGENTS.md this is a user-facing install knob, so update the discoverable surfaces together. - Optional / longer term: replace better-sqlite3 with
node:sqlite(Node ≥ 22.13 has it; bb's engines floor is 22.19), leaving node-pty as the only script-dependent add-on.
What could go wrong: (a) once a CLI policy exists npm 12 skips every other unlisted script instead of running-and-warning; today that only affects @google/genai's no-op preinstall and protobufjs's postinstall (bb ran fine without them above), but a future dependency with a load-bearing install script would need adding to the list; (b) the flag must not leak into project-scoped installs (npm 12 errors with EALLOWSCRIPTS there); (c) users who already have allow-scripts in ~/.npmrc get a harmless "npmrc setting is being ignored because --allow-scripts was passed" warning.
PR review
No open PRs are linked to this issue.
Related issues
- #1057 (closed): the same
Could not locate the bindings filefromnpx bb-app@latest, caused byignore-scripts=truein~/.npmrc; produced the README troubleshooting section. npm 12 is a second, default-on cause of the identical failure. - #1419 (merged 2026-08-12): private npm prefix per enrollment; removes the "global bin directory is not on PATH" failure this issue also reports.
- #1131: synchronous SQLite on the event loop; relevant if better-sqlite3 is replaced.
- Upstream: npm/rfcs#868 (allowScripts), npm v12.0.0 release notes.
Appendix
Side observation
In my first npx run I forgot to set BB_SERVER_PORT, so the launcher used the default 38886, where an unrelated bb server was already listening. The child server crashed with the bindings error, but waitForHealth (launcher.ts#L2164-L2185) got a 200 from the foreign server first and the launcher printed ✓ Server listening on http://127.0.0.1:38886 before the daemon step failed. Not this issue, but the health check does not verify that the responder is its own child.
Commands run
# worktree at 16ceb3a54 pnpm install --frozen-lockfile --prefer-offline && pnpm exec turbo run build git fetch origin main; git log 16ceb3a54..origin/main -- apps/server/src/assets/install-machine.sh apps/host-daemon/src/protocol-self-update.ts README.md packages/bb-app # nothing git log -S"global bin directory is not on PATH" -- apps/server/src/assets/install-machine.sh # 85fbd4f79 added, 65bab835f (#1419) removed # npm versions npm --version # 11.16.0 (Node 24.18.0) npm install --prefix /tmp/1120/npm12 npm@12.0.2 npm install --prefix /tmp/1120/npm1117 npm@11.17.0 npm install --prefix /tmp/1120/npm1119 npm@11.19.0 npm install --prefix /tmp/1120/npm10 npm@10.9.9 npm view npm dist-tags --json; npm view npm time --json | tail # latest = 12.0.2 (2026-07-29) curl -s https://nodejs.org/dist/index.json # which npm each Node bundles gh api repos/npm/cli/releases/tags/v12.0.0 --jq .body # "blocked by default" # repro matrix (scripts in 1120/repro/) cd /tmp/1120 1120-install-matrix.sh npm11 npm > npm11.out BB_PORT=39901 1120-install-matrix.sh npm11.17 /tmp/1120/npm1117/node_modules/.bin/npm > npm11.17.out # re-run on a fresh port (verifier finding) 1120-install-matrix.sh npm12 /tmp/1120/npm12/node_modules/.bin/npm > npm12.out 1120-install-matrix.sh npm12-allow /tmp/1120/npm12/node_modules/.bin/npm --allow-scripts=better-sqlite3,node-pty,@parcel/watcher > npm12-allow.out 1120-npx.sh npx12 /tmp/1120/npm12/node_modules/.bin/npx > npx12.out 1120-npx-allow.sh > npx12-allow.out 1120-npm10-allow-scripts.sh > npm10-allow-scripts-flag.out # installer end to end (dev instance: scripts/bb-dev-app current -> server http://localhost:24167) BB_SERVER_URL=http://localhost:24167 1120-installer.sh inst-npm12 /tmp/1120/npm12/node_modules/.bin > installer-npm12.out BB_SERVER_URL=http://localhost:24167 1120-installer.sh inst-npm11 /home/sawyer/.nvm/versions/node/v24.18.0/bin > installer-npm11.out # apply 1120-installer-fix.diff to apps/server/src/assets/install-machine.sh (the dev server serves it from src, so it takes effect immediately) BB_SERVER_URL=http://localhost:24167 1120-installer.sh inst-npm12-fixed /tmp/1120/npm12/node_modules/.bin > installer-npm12-fixed.out npm_config_ignore_scripts=true BB_SERVER_URL=http://localhost:24167 1120-installer.sh inst-npm11-ignorescripts-fixed /home/sawyer/.nvm/versions/node/v24.18.0/bin > installer-npm11-ignore-scripts-fixed.out cd apps/server && pnpm exec vitest run test/app/install-machine-script.test.ts # 6/16 fail with the diff applied: the fake npm creates no node_modules and the argv regexes lack the flag # revert the diff (git checkout -- apps/server/src/assets/install-machine.sh) pnpm dev:stop
Repro scripts (inline)
#!/bin/sh
# Issue #1120 repro: install bb-app from the npm registry with different npm
# versions, using a clean HOME (no ~/.npmrc) and a private cache, then check
# whether the native add-ons were built.
#
# Env: ROOT (default /tmp/1120; each run downloads ~1 GB into ROOT/cache and
# unpacks ~300 packages into ROOT/g-<label>; rm -rf ROOT when done),
# BB_PORT (default 39871; must be free), BB_APP_VERSION (default latest).
#
# usage: 1120-install-matrix.sh <label> <npm-binary> [extra npm args...]
# e.g. 1120-install-matrix.sh npm11 npm
# 1120-install-matrix.sh npm12 /tmp/1120/npm12/node_modules/.bin/npm
# 1120-install-matrix.sh npm12-allow /tmp/1120/npm12/node_modules/.bin/npm --allow-scripts=better-sqlite3,node-pty,@parcel/watcher,esbuild
set -u
label=$1; shift
npmbin=$1; shift
root=${ROOT:-/tmp/1120}
export HOME="$root/home-$label"
export npm_config_cache="$root/cache"
prefix="$root/g-$label"
rm -rf "$HOME" "$prefix"; mkdir -p "$HOME" "$prefix"
echo "== npm: $($npmbin --version) node: $(node --version) HOME=$HOME prefix=$prefix"
echo "== ~/.npmrc present? $(test -e "$HOME/.npmrc" && echo yes || echo no)"
echo "== \$ $npmbin install -g --prefix $prefix bb-app@${BB_APP_VERSION:-latest} $*"
$npmbin install -g --prefix "$prefix" "bb-app@${BB_APP_VERSION:-latest}" "$@"
echo "== npm exit code: $?"
nm="$prefix/lib/node_modules/bb-app/node_modules"
echo "== installed bb-app version: $(node -p "require('$prefix/lib/node_modules/bb-app/package.json').version")"
for f in better-sqlite3/build/Release/better_sqlite3.node node-pty/build/Release/pty.node; do
if [ -e "$nm/$f" ]; then echo "== PRESENT $f"; else echo "== MISSING $f"; fi
done
ls "$nm/@parcel/" 2>/dev/null | grep -c "^watcher-" | sed 's/^/== @parcel\/watcher-* prebuilt platform packages: /'
port=${BB_PORT:-39871}
# Refuse to run against a port that is already taken: otherwise curl would hit
# a stale server and the "HTTP 200" below would prove nothing.
if ss -ltn 2>/dev/null | grep -q ":$port "; then
echo "== ERROR: port $port is already in use; set BB_PORT to a free port"; exit 2
fi
echo "== \$ BB_DATA_DIR=$root/data-$label BB_SERVER_PORT=$port $prefix/bin/bb-server (background, then curl /api/v1/hosts after 6s)"
mkdir -p "$root/data-$label"
# setsid: bb-server (dist/bb-server.js) forks server/dist/index.js; killing
# only $! would leave the child listening. Kill the whole process group.
setsid env BB_DATA_DIR="$root/data-$label" BB_SERVER_PORT=$port "$prefix/bin/bb-server" >"$root/server-$label.log" 2>&1 &
spid=$!
sleep 6
echo "== curl http://127.0.0.1:$port/api/v1/hosts -> HTTP $(curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:$port/api/v1/hosts)"
pkill -TERM -g $spid 2>/dev/null; wait $spid 2>/dev/null
echo "== bb-server exit: $?"
pkill -TERM -f "$prefix/lib/node_modules/bb-app/" 2>/dev/null
sleep 1
echo "== still listening on :$port after kill? $(ss -ltn 2>/dev/null | grep -q ":$port " && echo yes || echo no)"
echo "== first 20 lines of bb-server stdout/stderr:"
head -20 "$root/server-$label.log"
#!/bin/sh
# Issue #1120: run the real "Add machine" installer (GET <server>/install.sh)
# against a bb server, with a chosen npm first on PATH, clean HOME, and no
# service installation (BB_INSTALL_SKIP_SERVICE=1). The daemon that the
# installer starts is killed at the end.
#
# usage: BB_SERVER_URL=http://localhost:24167 1120-installer.sh <label> <dir-containing-npm-binary>
# e.g. ... 1120-installer.sh inst-npm12 /tmp/1120/npm12/node_modules/.bin
# ... 1120-installer.sh inst-npm11 "$(dirname "$(command -v npm)")"
set -u
label=$1; npmdir=$2
root=${ROOT:-/tmp/1120}
server=${BB_SERVER_URL:?set BB_SERVER_URL}
export HOME="$root/home-$label"; export npm_config_cache="$root/cache-$label"
export BB_DATA_DIR="$root/machine-$label"; export BB_INSTALL_SKIP_SERVICE=1
export PATH="$npmdir:$PATH"; export NO_COLOR=1
rm -rf "$HOME" "$BB_DATA_DIR" "$npm_config_cache"; mkdir -p "$HOME" "$BB_DATA_DIR"
echo "== npm on PATH: $(command -v npm) -> $(npm --version); node $(node --version); HOME=$HOME; BB_DATA_DIR=$BB_DATA_DIR"
jc=$(curl -s -X POST "$server/api/v1/hosts/join-codes" -H 'content-type: application/json' -d '{}')
echo "== join code response: $jc"
join_code=$(printf '%s' "$jc" | node -pe 'JSON.parse(require("fs").readFileSync(0,"utf8")).joinCode')
host_id=$(printf '%s' "$jc" | node -pe 'JSON.parse(require("fs").readFileSync(0,"utf8")).hostId')
echo "== \$ curl -fsSL $server/install.sh | sh -s -- --join-code <code> --host-id $host_id --server $server"
curl -fsSL "$server/install.sh" | sh -s -- --join-code "$join_code" --host-id "$host_id" --server "$server"
echo "== installer exit: $?"
nm="$BB_DATA_DIR/npm/lib/node_modules/bb-app/node_modules"
for f in better-sqlite3/build/Release/better_sqlite3.node node-pty/build/Release/pty.node; do
if [ -e "$nm/$f" ]; then echo "== PRESENT $f"; else echo "== MISSING $f"; fi
done
echo "== install-join.log:"; sed -n 1,12p "$BB_DATA_DIR/install-join.log" 2>/dev/null
if [ -f "$BB_DATA_DIR/install-daemon.pid" ]; then kill "$(cat "$BB_DATA_DIR/install-daemon.pid")" 2>/dev/null; echo "== killed temporary daemon"; fi
echo "== host status on server:"; curl -s "$server/api/v1/hosts/$host_id" | head -c 300; echo
#!/bin/sh
# Issue #1120: the `npx bb-app@latest` path under a given npm, clean HOME.
# usage: 1120-npx.sh <label> <npx-binary>
set -u
label=$1; npxbin=$2
root=${ROOT:-/tmp/1120}
export HOME="$root/home-$label"; export npm_config_cache="$root/cache-$label"
rm -rf "$HOME" "$npm_config_cache" "$root/data-$label"; mkdir -p "$HOME" "$root/data-$label"
echo "== npx: $($npxbin --version) node: $(node --version) HOME=$HOME"
echo "== \$ BB_DATA_DIR=$root/data-$label BB_SERVER_PORT=39872 BB_HOST_DAEMON_PORT=39873 $npxbin --yes bb-app@latest (30s timeout)"
BB_DATA_DIR="$root/data-$label" BB_SERVER_PORT=39872 BB_HOST_DAEMON_PORT=39873 timeout 30 $npxbin --yes bb-app@latest 2>&1 | grep -v deprecated | head -30
echo "== exit: $?"
1120-installer-fix.diff (candidate fix; not committed)
diff --git a/apps/server/src/assets/install-machine.sh b/apps/server/src/assets/install-machine.sh
index 006542ab2..a9c9db8ab 100755
--- a/apps/server/src/assets/install-machine.sh
+++ b/apps/server/src/assets/install-machine.sh
@@ -181,6 +181,12 @@ canonical_data_dir=$(node -e '
# Keep the package private to this enrollment. Besides avoiding system-prefix
# permissions, this lets one machine follow servers running different builds.
machine_npm_prefix="$canonical_data_dir/npm"
+# bb-app depends on native add-ons whose binaries are fetched or built by npm
+# lifecycle scripts. npm >= 12 blocks dependency install scripts by default
+# for global installs unless they are named in --allow-scripts (the installed
+# package's own package.json#allowScripts is not consulted for -g / npx).
+# npm 10 ignores the unknown flag; npm 11 accepts it.
+bb_app_allow_scripts="--allow-scripts=better-sqlite3,node-pty,@parcel/watcher"
port_registry_dir="$HOME/.bb-machines/host-daemon-ports"
mkdir -p "$port_registry_dir"
@@ -388,7 +394,7 @@ if [ "$package_status" -ge 200 ] && [ "$package_status" -lt 300 ]; then
require_npm
complete_step "Downloaded the server's bb-app package"
active_step "Installing the server's bb-app build"
- if ! npm install -g --prefix "$machine_npm_prefix" "$package_file"; then
+ if ! npm install -g "$bb_app_allow_scripts" --prefix "$machine_npm_prefix" "$package_file"; then
rm -rf "$package_dir"
fail_step "Could not install bb-app for this machine. Check the npm error above, then rerun this command."
exit 1
@@ -406,7 +412,7 @@ elif [ "$package_status" = 404 ]; then
require_npm
warning_step "The server does not provide its bb-app package"
active_step "Installing bb-app from the npm registry"
- if ! npm install -g --prefix "$machine_npm_prefix" bb-app; then
+ if ! npm install -g "$bb_app_allow_scripts" --prefix "$machine_npm_prefix" bb-app; then
rm -rf "$package_dir"
fail_step "Could not install bb-app for this machine. Check the npm error above, then rerun this command."
exit 1
@@ -426,6 +432,14 @@ if [ -n "$bb_app_npm_prefix" ]; then
fail_step "npm installed bb-app, but did not create the expected executable at $bb_app."
exit 1
fi
+ # Fail loudly if npm skipped the native add-on install scripts (npm >= 12
+ # allowScripts policy, or ignore-scripts=true in an npmrc).
+ bb_app_root="$bb_app_npm_prefix/lib/node_modules/bb-app"
+ if ! node -e 'require(process.argv[1] + "/node_modules/better-sqlite3"); require(process.argv[1] + "/node_modules/node-pty")' "$bb_app_root" >/dev/null 2>&1; then
+ fail_step "npm installed bb-app, but its native add-ons (better-sqlite3, node-pty) were not built."
+ detail "npm did not run their install scripts. Check the npm warnings above; if it mentions allowScripts or ignore-scripts, rerun with: npm_config_allow_scripts=better-sqlite3,node-pty,@parcel/watcher npm_config_ignore_scripts=false <this command>" >&2
+ exit 1
+ fi
fi
if [ -n "$machine_code" ]; then
#!/bin/sh
# Issue #1120: does npm 10.9.9 (Node 22.x) choke on --allow-scripts /
# npm_config_allow_scripts, which it does not know? Tests the -g --prefix
# form bb's installer uses, plus a project-scoped install for completeness.
# usage: 1120-npm10-allow-scripts.sh (env: ROOT, default /tmp/1120)
set -u
root=${ROOT:-/tmp/1120}
export HOME="$root/home-npm10"; export npm_config_cache="$root/cache"
mkdir -p "$HOME"
if [ ! -x "$root/npm10/node_modules/.bin/npm" ]; then
echo "== \$ npm install --prefix $root/npm10 npm@10.9.9"
npm install --prefix "$root/npm10" npm@10.9.9 2>&1 | tail -1
fi
npm10="$root/npm10/node_modules/.bin/npm"
echo "== npm10 version: $($npm10 --version) node: $(node --version) HOME=$HOME"
run() { echo "== \$ $*"; "$@" >"$root/.npm10-run.out" 2>&1; rc=$?; grep -v -e '^$' -e 'npm fund' -e 'looking for funding' -e 'npm notice' "$root/.npm10-run.out"; echo "== npm exit: $rc"; }
echo "--- global form (what install-machine.sh uses):"
rm -rf "$root/p10g"; mkdir -p "$root/p10g"
run "$npm10" install -g --prefix "$root/p10g" --allow-scripts=better-sqlite3,node-pty,@parcel/watcher better-sqlite3@12.10.0
ls "$root/p10g/lib/node_modules/better-sqlite3/build/Release/" 2>&1 | grep -c better_sqlite3.node | sed 's/^/== better_sqlite3.node present (1=yes): /'
echo "--- env form, global:"
rm -rf "$root/p10g"; mkdir -p "$root/p10g"
run env npm_config_allow_scripts=better-sqlite3,node-pty,@parcel/watcher "$npm10" install -g --prefix "$root/p10g" better-sqlite3@12.10.0
ls "$root/p10g/lib/node_modules/better-sqlite3/build/Release/" 2>&1 | grep -c better_sqlite3.node | sed 's/^/== better_sqlite3.node present (1=yes): /'
echo "--- project-scoped form (not what bb uses; for completeness):"
rm -rf "$root/p10"; mkdir -p "$root/p10"
run "$npm10" install --prefix "$root/p10" --allow-scripts=better-sqlite3 better-sqlite3@12.10.0
ls "$root/p10/node_modules/better-sqlite3/build/Release/" 2>&1 | grep -c better_sqlite3.node | sed 's/^/== better_sqlite3.node present (1=yes): /'
Other artifacts
- npm11.out, npm11.17.out, npm12.out, npm12-allow.out, npx12.out, npx12-allow.out, npm10-allow-scripts-flag.out (from 1120-npm10-allow-scripts.sh)
- installer-npm12.out, installer-npm11.out, installer-npm12-fixed.out, installer-npm11-ignore-scripts-fixed.out
- npm source excerpts: 11.16.0 gate, 11.19.0 gate, 12.0.2 gate, 12.0.2 resolve-allow-scripts
- npm release notes: 11.16.0, 11.17.0, 11.18.0, 11.19.0, 12.0.0; nodejs.org dist index
Verification
An independent verifier followed the "Minimal reproduction" literally in a separate worktree (own dev instance :20338, ROOT=/tmp/1120w, ports 41871-41873; outputs in 1120/verify/): npm 11.16.0 -> both .node files present, fresh bb-server HTTP 200; npm 12.0.2 -> "install scripts blocked", both missing, "Could not locate the bindings file", HTTP 000; npm 12 + --allow-scripts=better-sqlite3,node-pty,@parcel/watcher -> present + 200; npx 12.0.2 -> bindings error; install.sh under npm 12 -> "Installed the server's bb-app build" then daemon dies with "Failed to load native module: pty.node"; with 1120-installer-fix.diff applied the npm 12 machine join succeeds. All root-cause code claims (install-machine.sh lines, protocol-self-update.ts, README, launcher waitForHealth, #1419 PATH-check removal, npm 12.0.2 arborist gate / resolve-allow-scripts.js, npm 11.16 "Phase 1" gate, npm v12.0.0 release notes) were confirmed against the code and the installed npm versions.
Findings and what changed in this revision: (major) the original npm 11.17.0 control paste showed EADDRINUSE because 1120-install-matrix.sh killed only the dist/bb-server.js wrapper and the child server/dist/index.js kept listening, so its "HTTP 200" came from a stale server. The script now refuses a busy port, runs bb-server under setsid, kills the process group and reports whether the port is still listening; the 11.17.0 control was re-run on a fresh port (39901: bb-server exit 143, "still listening after kill? no", HTTP 200 from that install's own server) and npm11.17.out plus the paste above were replaced. (minor) The package.json permalink now points at the dependencies block (L68-L77). (minor) The npm 10 flag-compatibility check is now the saved script 1120-npm10-allow-scripts.sh, which echoes its commands and tests the -g --prefix form (flag and env var) as well as the project-scoped form. (minor) The repro section now documents ROOT/BB_PORT overrides, the ~1 GB / inode cost per run and the cleanup step; the author's /tmp/1120 scratch was deleted after this revision.