Skip to content

Migrating from npm to vlt

Quick Start

Terminal
# Install vlt globally
$ npm install -g vlt
# In your existing project, run:
$ vlt install $ vlt build

That’s it for basic usage. vlt reads your existing package.json and resolves dependencies. Below are the details on what’s different and how to handle specific scenarios.


Command Mapping

npmvltNotes
npm installvlt installDoes not run lifecycle scripts
npm install <pkg>vlt install <pkg>Same behavior
npm install -D <pkg>vlt install -D <pkg>Same flags
npm uninstall <pkg>vlt uninstall <pkg>Aliases: rm, u
npm run <script>vlt run <script>Aliases: r, run-script
npm testvlt run testNo vlt test shorthand
npm startvlt run startNo vlt start shorthand
npx <pkg>vlx <pkg>Run remote packages
npm exec <cmd>vlt exec <cmd>Local bins on PATH
npm initvlt initCreates package.json
npm packvlt pack
npm publishvlt publish
npm loginvlt login
npm whoamivlt whoami
npm lsvlt listAlias: ls
npm queryvlt queryUses DSS, not CSS selectors
npm civlt install --expect-lockfileStrict lockfile mode
npm install --ignore-scriptsvlt installDefault behavior
npm auditvlt query ':malware'More powerful; see Security
npm config set <key>=<val>vlt config set <key>=<val>Writes to vlt.json
npm overridesGraph ModifiersDSS-based, more precise

Configuration

.npmrc → vlt.json

npm reads configuration from .npmrc files (INI format, multiple locations). vlt uses a single vlt.json file in your project root, plus an optional user-level vlt.json in the XDG config directory.

.npmrc
# .npmrc (npm)
registry=https://registry.internal.company.com/
@mycompany:registry=https://npm.mycompany.com/
save-exact=true
vlt.json
{
"registry": "https://registry.internal.company.com/",
"scope-registries": {
"@mycompany": "https://npm.mycompany.com/"
}
}

To create this file interactively:

Terminal
$ vlt config set registry=https://registry.internal.company.com/
$ vlt config set scope-registries=@mycompany=https://npm.mycompany.com/

User vs Project Config

npmvlt
~/.npmrc (user)XDG config dir vlt/vlt.json
.npmrc (project)vlt.json (project root)
$PREFIX/etc/npmrc (global)No global config file

View config locations:

Terminal
$ vlt config location --config=user
$ vlt config location --config=project

Registry Configuration

vlt handles registries similarly to npm, with some additions.

Scoped Registries

npm’s scoped registries map directly:

.npmrc
# npm (.npmrc)
@mycompany:registry=https://npm.mycompany.com/
vlt.json
{
"scope-registries": {
"@mycompany": "https://npm.mycompany.com/"
}
}

Named Registry Aliases

vlt also supports named registry aliases, which are more explicit than scope-based mapping. This is a vlt-specific feature:

vlt.json
{
"registries": {
"internal": "https://npm.mycompany.com/"
}
}

Then reference packages explicitly:

Terminal
$ vlt install internal:@mycompany/utils@^2.0

Authentication

Tokens

npm stores auth tokens in .npmrc. vlt stores them in an XDG-compliant keychain file, which is less likely to be accidentally committed to source control.

Terminal
# npm: tokens live in .npmrc (risky if committed)
//registry.npmjs.org/:_authToken=npm_abc123
# vlt: log in interactively
$ vlt login
# vlt: or for custom registries
$ vlt login --registry=https://npm.mycompany.com/

CI Environments

Terminal
# npm
NPM_TOKEN=abc123 npm install
# vlt
VLT_TOKEN=abc123 vlt install

For non-default registries, replace non-alphanumeric characters in the URL with _:

Terminal
VLT_TOKEN_https_npm_mycompany_com=abc123 vlt install

See Authentication for full details.


Lockfile

npm uses package-lock.json. vlt uses vlt-lock.json.

When you first run vlt install, vlt resolves from your package.json and creates vlt-lock.json. Your existing package-lock.json is not read or migrated — vlt performs a fresh resolution.

What to do:

  1. Run vlt install to generate vlt-lock.json
  2. Commit vlt-lock.json to source control
  3. Optionally remove package-lock.json (or keep it if you’re running both tools during a transition period)

CI / Frozen Lockfile

Terminal
# npm
$ npm ci
# vlt
$ vlt install --expect-lockfile

Install Script Protection

This is the biggest behavioral difference from npm.

npm runs all lifecycle scripts (preinstall, install, postinstall) automatically during npm install. This is a known supply chain attack vector.

vlt separates installation into two phases:

  1. vlt install — Downloads and extracts packages. No scripts run.
  2. vlt build — Runs lifecycle scripts selectively.
Terminal
# Install packages (safe — no code executes)
$ vlt install
# Run build scripts, automatically excluding packages flagged as malware
$ vlt build

By default, vlt build uses the target :scripts:not(:built):not(:malware), which skips packages with known malware alerts.

You can be more selective:

Terminal
# Only allow specific packages to run scripts
$ vlt build --target="#esbuild, #node-gyp"
# Or allow scripts for direct dependencies only
$ vlt build --target=":root > :scripts"

If you need legacy npm behavior (not recommended):

Terminal
$ vlt install --allow-scripts='*'

See vlt build for full details.


Workspaces

npm workspaces are defined in the root package.json:

package.json (npm)
{
"workspaces": [
"packages/*"
]
}

vlt workspaces are defined in vlt.json:

vlt.json
{
"workspaces": [
"packages/*"
]
}

Key Differences

  • Definition locationvlt.json instead of package.json
  • Named groups — vlt supports grouping workspaces for targeted operations:
vlt.json
{
"workspaces": {
"apps": "apps/*",
"libs": "packages/*"
}
}

Workspace Commands

npmvlt
npm run test -w packages/avlt run test -w packages/a
npm run test --workspacesvlt run test --recursive
npm install <pkg> -w packages/avlt install <pkg> -w packages/a

See Workspaces for full details.


Overrides → Graph Modifiers

npm uses overrides in package.json to force dependency versions. vlt uses Graph Modifiers in vlt.json, which are powered by DSS selectors and offer more precise targeting.

package.json (npm)
{
"overrides": {
"lodash": "^4.17.21",
"express>qs": "6.10.0"
}
}
vlt.json
{
"modifiers": {
"#lodash": "^4.17.21",
":root > #express > #qs": "=6.10.0"
}
}

Graph Modifiers use CSS-like specificity rules when multiple selectors match the same dependency.


Migration Checklist

  1. Install vlt: npm install -g vlt
  2. In your project root, create vlt.json with your registry and workspace config (or use vlt config set)
  3. Move any scoped registry config from .npmrc to vlt.json
  4. Move workspace definitions from package.json to vlt.json
  5. Move overrides from package.json to modifiers in vlt.json
  6. Run vlt install then vlt build
  7. Commit vlt-lock.json
  8. Update CI scripts: replace npm ci with vlt install --expect-lockfile && vlt build
  9. Update CI auth: replace NPM_TOKEN with VLT_TOKEN
  10. Update any npx usage to vlx