Registries
LPM CLI works with npm, private registries, and LPM.dev Registry. Here is how it routes.
LPM CLI is registry-agnostic. It is not a client only for LPM.dev Registry — it is a general-purpose package manager with LPM.dev Registry support.
If you have an existing package.json full of react, lodash, typescript, and friends, LPM CLI installs them from registry.npmjs.org exactly like npm, pnpm, yarn, or bun would. Nothing has to change.
Default routing
LPM CLI resolves each package by name and routes accordingly:
| Package | Goes to |
|---|---|
react, lodash, typescript, … | registry.npmjs.org |
@scope/anything (except built-in scopes below) | registry.npmjs.org, unless .npmrc overrides the scope |
jsr:@std/path manifest specs (@jsr/* internally) | https://npm.jsr.io, unless .npmrc maps @jsr |
@lpm.dev/* | LPM.dev Registry (https://lpm.dev; auth + monetization) |
Anything that matches a registry mapping in .npmrc | The registry you mapped |
The @lpm.dev/* scope is the only scope that always goes to LPM.dev Registry. JSR packages use JSR's npm-compatible registry. Everything else is npm by default.
One dependency, one source
LPM CLI chooses a package's registry route before resolving it. A dependency does not fall through from a configured private index to npm, or from npm to LPM.dev Registry, when lookup fails. Missing, unauthorized, or invalid metadata from the selected registry is a hard error. This prevents dependency-confusion behavior where the same name silently resolves from a lower-priority index.
The selected logical source is recorded on the resolved package in lpm.lock, for example registry+https://npm.my-company.com. Scoped .npmrc mappings, the configured npm registry, and the active LPM.dev Registry origin are preserved exactly. The package identity used by lockfile evidence includes this source, so evidence from one registry cannot be replayed for a same-name package from another.
LPM CLI may use the LPM Worker as a transport proxy for public npm metadata and fall back to a direct npm request when that transport is unavailable. Both paths represent the same configured logical npm origin, so this is transport failover—not index fallback—and the lockfile records the npm origin rather than the Worker URL.
JSR packages
LPM CLI accepts jsr: dependency specs in package.json and resolves them through JSR's npm-compatible registry. The JSR package name stays as the local import name, while the metadata fetch uses JSR's folded npm package name under @jsr.
{
"dependencies": {
"@std/path": "jsr:@std/path@^1.1.0",
"@std/fs": "jsr:^1.0.0"
}
}"@std/path": "jsr:@std/path@^1.1.0" fetches @jsr/std__path from https://npm.jsr.io and links it at node_modules/@std/path. Version-only specs such as "@std/fs": "jsr:^1.0.0" borrow the package name from the dependency key.
To mirror or intercept JSR traffic, map the @jsr scope explicitly:
@jsr:registry=https://npm-jsr-mirror.example.comPointing at a private registry
LPM CLI reads .npmrc project-local first, then user-level (~/.npmrc). Keep committed project .npmrc files to literal routing data, and put env-backed secrets in user or CI-owned config.
# A private registry for one scope
@my-company:registry=https://npm.my-company.com
# Override the default registry for everything else (optional)
registry=https://my-mirror.example.com//npm.my-company.com/:_authToken=${NPM_TOKEN}
//my-mirror.example.com/:_authToken=${MIRROR_TOKEN}@my-company/internal-tool will be fetched from npm.my-company.com with the configured auth token. Public packages still flow through the default registry. No CLI-specific config is required.
For per-environment tokens, prefer ${VAR} expansion in trusted config over hardcoded secrets. LPM CLI refuses ${VAR} expansion for registry URLs, auth values, proxy URLs, and TLS paths in project-local .npmrc because those files can be controlled by the repo you cloned. Move those entries to ~/.npmrc, have CI generate literal-token config at runtime, or use the matching login command.
Registry configuration size limits
Each .npmrc layer has a 1 MiB limit, and each file referenced by cafile, certfile, or keyfile has a 1 MiB limit. LPM CLI enforces these limits before parsing or building an HTTP client. Missing optional layers are still skipped, but an oversized or invalid UTF-8 .npmrc is a fatal configuration error: it is not ignored in favor of a lower-precedence registry. An oversized TLS file likewise fails with its path and byte limit, without logging tokens, certificates, keys, or file contents.
For installs, these errors surface before dependency resolution or any registry request. Inline ca / cert / key values are part of the containing .npmrc and therefore covered by that file's 1 MiB layer limit. See local configuration size limits for non-registry config and explicit exclusions.
Redirect transport policy
LPM CLI checks every automatically followed redirect before sending the next request. Once a request chain has used HTTPS, a redirect to HTTP is refused — including redirects to localhost, 127.0.0.1, or [::1]. The command fails with a network or registry error containing refused HTTPS-to-HTTP redirect.
HTTPS-to-HTTPS redirects and HTTP-to-HTTPS upgrades remain allowed. HTTP-to-HTTP development flows can still begin from a directly configured loopback URL. For registry operations, --insecure can admit an explicitly configured HTTP endpoint outside loopback, but it never permits an HTTPS request to downgrade through a redirect.
strict-ssl=false changes certificate verification only; it does not weaken the redirect rule. This policy covers outbound HTTP clients used by LPM CLI itself. It does not make claims about arbitrary application URLs opened by project code or installed packages.
TLS, custom CAs, and mTLS
LPM CLI honors the same .npmrc TLS keys npm does. All settings can be set globally or per-origin (//host/:key=value); per-origin scoping wins for the matching host.
Trusting a corporate CA
# Add a CA to the trust store (file path or inline PEM).
cafile=/etc/ssl/corp-ca.pem
ca="-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----"
# Per-origin form: only this host trusts the extra CA.
//npm.internal/:cafile=/etc/ssl/corp-ca.pemcafile and ca are additive — they extend the system trust store, never replace it. A globally-trusted CA still validates registry.npmjs.org normally.
Disabling cert verification (escape hatch)
strict-ssl=falseDisables hostname, expiry, and CA validation process-wide. It does not allow an HTTPS redirect to downgrade to HTTP. LPM CLI logs a loud warning at install start whenever strict-ssl=false is in effect — this is the kind of setting that should never accidentally land in CI.
Per-origin //host/:strict-ssl=false is not supported in v1; the parser warns and ignores it. Use cafile to scope a custom-CA trust addition instead.
Mutual TLS (client certificates)
For private registries that require client-cert authentication, point LPM CLI at a PEM cert and key:
# Global mTLS identity — applies to every request from the default client.
certfile=/etc/ssl/client.pem
keyfile=/etc/ssl/client.key
# Per-origin mTLS — only this host gets the client cert.
//npm.internal/:certfile=/etc/ssl/client.pem
//npm.internal/:keyfile=/etc/ssl/client.keycertfile and keyfile must always be set together — half-configured global identities abort the install with a cited error. Per-origin half-configurations only fail when that origin is actually used.
Path resolution
Relative paths in certfile= / keyfile= / cafile= resolve against the .npmrc file that contained them, not against the working directory you ran lpm install from. So in ~/.npmrc:
certfile=client.pem # → ~/client.pem
keyfile=client.key # → ~/client.keyYou can drop secrets next to the .npmrc and reference them with bare names.
Encrypted private keys
LPM CLI accepts encrypted PKCS#8 keys. The passphrase is resolved in this order:
LPM_KEY_PASSPHRASEenvironment variable (the CI-friendly path).- Interactive TTY prompt — only when both stdin AND stdout are a terminal.
- Otherwise, hard error citing the keyfile line.
LPM_KEY_PASSPHRASE='hunter2' lpm installThe passphrase is cached in memory for the lifetime of the install — multiple per-origin clients sharing the same key file prompt at most once.
If your key uses the legacy PKCS#1 Proc-Type: 4,ENCRYPTED format, convert it first:
openssl pkcs8 -topk8 -in legacy.key -out key.pemPKCS#12 (.p12 / .pfx) is not supported
LPM CLI uses rustls, which doesn't ingest PKCS#12 archives directly. Convert with openssl and point certfile= / keyfile= at the resulting PEMs:
openssl pkcs12 -in identity.pfx -clcerts -nokeys -out cert.pem
openssl pkcs12 -in identity.pfx -nocerts -nodes -out key.pemThen:
certfile=/path/to/cert.pem
keyfile=/path/to/key.pemIf you point certfile= or keyfile= directly at a .p12/.pfx file, LPM CLI emits a cited error with this recipe inline.
When does LPM.dev Registry come in
LPM.dev Registry is a registry plus an auth and monetization layer. Use it when:
- You want private packages without standing up your own registry. Every published package starts private — only the publisher can install it. (Same shape as npm private packages.)
- You publish under your scope and want to flip a package to pool distribution. Metadata becomes public, but installs are gated to pool subscribers — you earn a share of the pool's revenue.
- You publish a paid package via marketplace distribution. Installs require a license purchase.
Setting distribution is a per-package opt-in on the publisher's side. As an installer, you simply lpm install @lpm.dev/owner.package — LPM CLI handles the auth, license, and download through LPM.dev Registry.
If you're not publishing, ignore LPM.dev Registry entirely — LPM CLI is a strict superset of npm-the-client and will never send your react install through it.
Private packages on LPM.dev Registry
lpm login
lpm install @lpm.dev/acme.internallpm login stores a token in your OS keychain (Keychain on macOS, libsecret on Linux, Credential Manager on Windows). The token is scoped to LPM.dev Registry — no other registry sees it.
To generate .npmrc without relying on lpm login in the current shell:
lpm setup ci npmrc # generates an .npmrc for CI/CD use
lpm setup local # mints a read-only .npmrc token for local development (auto-gitignored)Mixing registries in one project
A package.json can pull from npm, LPM.dev Registry, and a private registry simultaneously — LPM CLI resolves each dep against the correct origin and writes the resolution to the lockfile.
{
"dependencies": {
"react": "^19.0.0",
"@my-company/internal-tool": "^2.1.0",
"@lpm.dev/acme.private-utils": "^1.0.0"
}
}@my-company:registry=https://npm.my-company.com//npm.my-company.com/:_authToken=${NPM_TOKEN}lpm install
# react → registry.npmjs.org
# @my-company/internal-tool → npm.my-company.com (via .npmrc)
# @lpm.dev/acme.private-utils → lpm.dev (via stored token)See also
- Installation — get the CLI on your machine
- Authentication — token storage, scopes, rotation
lpm install— the install commandlpm publish— publishing to LPM.dev Registry or npm