npm
Nub speaks npm's on-disk formats — the version-2 and version-3 lockfile round-trips byte-for-byte, and npm config is read across npm's builtin, global, user, and project scopes. The CLI is pnpm-shaped; the files are npm's.
The CLI you type is pnpm-shaped; the files Nub reads and writes are npm's. A project already on npm keeps working unchanged — no pnpm-lock.yaml is introduced.
| Feature | Status | Notes |
|---|---|---|
package-lock.json | Supported | v2 / v3, read + write; v1 errors |
project + user .npmrc | Supported | |
global / builtin .npmrc | Supported | |
overrides | Supported | |
resolutions | Not applicable | npm ignores it — use overrides |
workspaces | Supported | |
engines / os / cpu / libc | Supported |
package-lock.json
Nub reads lockfileVersion 2 and 3, resolves against it, and writes it back in npm's exact format. On a no-op install the writer reproduces npm's output byte-for-byte — key ordering, the devOptional collapse, peerDependenciesMeta.optional, and platform fields all preserved — so the file doesn't churn and npm ci accepts it unchanged.
// package-lock.json written by `nub install` — accepted by `npm ci` byte-for-byte
{
"name": "app",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": { "name": "app", "version": "1.0.0", "dependencies": { "is-odd": "3.0.1" } },
"node_modules/is-odd": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/is-odd/-/is-odd-3.0.1.tgz",
"integrity": "sha512-CQpnWPrDwmP1+SMHXZhtLtJv90yiyVfluGsX5iNCVkrhQtU3TQHsUWPG9wkdk9Lgd5yNpAg9jQEo90CBaXgWMA==",
"license": "MIT",
"dependencies": { "is-number": "^6.0.0" },
"engines": { "node": ">=4" }
}
}
}A no-op rewrite keeps the incoming lockfileVersion — a v2 lock stays v2. The first mutating operation (nub add, nub remove) rewrites it as v3. The result is still valid and npm ci-accepted (v3 is npm's default since npm 9), but the version field is an expected diff.
lockfileVersion 1 is refused
A lockfileVersion: 1 lock (npm 5–6) is rejected before install, not silently misread:
# captured: nub 0.0.44 on a lockfileVersion 1 package-lock.json
$ nub install
ERR_NUB_LOCKFILE_PARSE
× failed to parse lockfile # ❌ v1 is unsupported — install refused
╰─▶ failed to parse lockfile /path/to/package-lock.json:
package-lock.json lockfileVersion 1 is not supported (need v2 or v3)```
Run `npm install` once under npm 7+ to upgrade the lock to v2/v3, then Nub reads it.
## `.npmrc`
When npm is the incumbent, Nub reads npm's `.npmrc` cascade:
builtin npm's builtin npmrc global $PREFIX/etc/npmrc, or NPM_CONFIG_GLOBALCONFIG user ~/.npmrc, or NPM_CONFIG_USERCONFIG project ./.npmrc
Everything at those scopes works — default and scoped registries, auth tokens, TLS, proxies, and install-shaping keys like `engine-strict` and `save-exact`. The `npm_config_*` env vars drive install behavior too. Precedence matches npm, highest first: CLI flags, then `npm_config_*`, then project, user, global, and builtin `.npmrc`.
### Custom CAs
Corporate or self-signed certificate authorities are configured through the standard npm TLS keys, top-level or scoped to one registry:
```ini
# .npmrc
cafile=./corp-ca.pem # PEM bundle, all registries
ca="-----BEGIN CERTIFICATE-----..." # inline PEM (repeat ca[]= to stack)
//registry.example.com/:cafile=./corp-ca.pem # per-registry override
strict-ssl=false # disable verification (user/global scope only)The certificates are added to the trust store of the install client. A committed project .npmrc cannot turn off strict-ssl — only the user or global scope can.
Install behavior
- Build trust. Lifecycle scripts run under Nub's deny-by-default posture plus the gated default-trust floor (see Security), not npm's run-everything default. Curated, age-vetted packages build automatically; everything else is skipped with
WARN_NUB_IGNORED_BUILD_SCRIPTSuntilnub approve-builds. - overrides. The
package.json"overrides"field is applied;"resolutions"is dropped with a warning, matching npm's own behavior. - Platform fields.
engines/os/cpu/libcare enforced, and platform-gated optionals (e.g.fsevents) resolve and round-trip into the lock. - workspaces. The neutral
"workspaces"field is honored.
Gaps
- lockfileVersion 1 (npm ≤ 6) errors, not migrated — run
npm installonce to upgrade first. - A v2 lock becomes v3 on the first mutating install (preserved on no-op installs only).
- No drop-in
npmCLI grammar. npm-specific subcommands/flags are not emulated; the CLI is pnpm-shaped. (npm-shrinkwrap.jsonis read and preserved as-is.)
pnpm
pnpm is the package manager Nub mirrors most closely — native version-9 lockfile read and write, an isolated dependency tree, and pnpm's hook file, all gated on pnpm being the incumbent.
Bun
When Bun is the incumbent, Nub installs against it — Bun's text lockfile round-trips byte-for-byte, its trusted-dependencies list gates build scripts, and overrides, resolutions, patches, and catalogs all resolve Bun's way.