Skip to content

npm tarball ships no binary + npm >=12 blocks postinstall, so auto-update silently uninstalls the CLI #1148

Description

@Alex-CODAURA

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

  1. 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.
  2. Or have bin/railway.js download the binary lazily on first run when it is missing, instead of relying on postinstall.
  3. 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

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