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.

FeatureStatusNotes
package-lock.jsonSupportedv2 / v3, read + write; v1 errors
project + user .npmrcSupported
global / builtin .npmrcSupported
overridesSupported
resolutionsNot applicablenpm ignores it — use overrides
workspacesSupported
engines / os / cpu / libcSupported

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_SCRIPTS until nub 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 / libc are 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 install once to upgrade first.
  • A v2 lock becomes v3 on the first mutating install (preserved on no-op installs only).
  • No drop-in npm CLI grammar. npm-specific subcommands/flags are not emulated; the CLI is pnpm-shaped. (npm-shrinkwrap.json is read and preserved as-is.)