Summary
On npm >= 12, npm install -g @railway/cli produces a package with no railway binary: the published tarball does not contain one, and the postinstall script that downloads it is blocked by npm's default script policy.
Because the CLI's own auto-updater shells out to npm install -g @railway/cli, the CLI effectively uninstalls itself whenever an update runs.
Evidence
The published @railway/cli@5.45.7 tarball is 4643 bytes:
package/LICENSE
package/README.md
package/package.json
package/bin/railway.js
package/npm-install/config.js
package/npm-install/postinstall.js
There is no native binary in it. The 17.8 MB bin/railway comes exclusively from npm-install/postinstall.js, which fetches it from GitHub Releases.
npm 12.0.2 blocks install scripts by default — npm config ls -l reports:
allow-scripts = [""]
strict-allow-scripts = false
ignore-scripts = false
With an empty allowlist and strict-allow-scripts = false, npm warns and continues, leaving a broken package. ~/.railway/auto-update.log recorded exactly that after an auto-update:
changed 1 package in 755ms
npm warn install-scripts 1 package had install scripts blocked because they are not covered by allowScripts:
npm warn install-scripts @railway/cli@5.45.7 (postinstall: node ./npm-install/postinstall.js)
npm warn install-scripts Run `npm install -g --allow-scripts=@railway/cli` to allow these scripts once, or
npm warn install-scripts `npm config set allow-scripts=@railway/cli --location=user` to allow them for all global installs.
Two of my core dumps were recorded running from node_modules/@railway/.cli-MaiaxxV0/bin/railway — npm's temporary rename of the old package during the swap — so a running CLI process was executing out of the directory being replaced.
Reproduction
On a machine with npm >= 12 and no allow-scripts entry:
npm install -g @railway/cli
railway --version
bin/railway is absent and the wrapper exits 127 with could not find the CLI binary at <path>.
Workaround
npm config set allow-scripts=@railway/cli --location=user
npm install -g @railway/cli
This restores the binary and survives subsequent auto-updates.
Suggested fixes
- Ship the per-platform binaries as
optionalDependencies packages (the esbuild / swc / rollup pattern). This removes the need for any install script and is immune to npm's script policy.
- Or have
bin/railway.js download the binary lazily on first run when it is missing, instead of relying on postinstall.
- Or, at minimum, detect this specific case in
bin/railway.js and print the allow-scripts hint rather than a bare ENOENT message.
Environment
- Arch Linux, kernel 7.1.9
- node 26.7.0 (installed via mise)
- npm 12.0.2
- @railway/cli 5.45.7
Summary
On npm >= 12,
npm install -g @railway/cliproduces a package with norailwaybinary: the published tarball does not contain one, and thepostinstallscript that downloads it is blocked by npm's default script policy.Because the CLI's own auto-updater shells out to
npm install -g @railway/cli, the CLI effectively uninstalls itself whenever an update runs.Evidence
The published
@railway/cli@5.45.7tarball is 4643 bytes:There is no native binary in it. The 17.8 MB
bin/railwaycomes exclusively fromnpm-install/postinstall.js, which fetches it from GitHub Releases.npm 12.0.2 blocks install scripts by default —
npm config ls -lreports:With an empty allowlist and
strict-allow-scripts = false, npm warns and continues, leaving a broken package.~/.railway/auto-update.logrecorded exactly that after an auto-update:Two of my core dumps were recorded running from
node_modules/@railway/.cli-MaiaxxV0/bin/railway— npm's temporary rename of the old package during the swap — so a running CLI process was executing out of the directory being replaced.Reproduction
On a machine with npm >= 12 and no
allow-scriptsentry:bin/railwayis absent and the wrapper exits 127 withcould not find the CLI binary at <path>.Workaround
This restores the binary and survives subsequent auto-updates.
Suggested fixes
optionalDependenciespackages (the esbuild / swc / rollup pattern). This removes the need for any install script and is immune to npm's script policy.bin/railway.jsdownload the binary lazily on first run when it is missing, instead of relying onpostinstall.bin/railway.jsand print theallow-scriptshint rather than a bare ENOENT message.Environment