LPM-cli

Registries

LPM works with npm, private registries, and lpm.dev. Here is how it routes.

The LPM CLI is registry-agnostic. It is not a client only for lpm.dev — it is a general-purpose package manager that happens to also speak the lpm.dev protocol.

If you have an existing package.json full of react, lodash, typescript, and friends, LPM installs them from registry.npmjs.org exactly like npm, pnpm, yarn, or bun would. Nothing has to change.

Default routing

LPM resolves each package by name and routes accordingly:

PackageGoes to
react, lodash, typescript, …registry.npmjs.org
@scope/anything (any scope except @lpm.dev)registry.npmjs.org, unless .npmrc overrides the scope
@lpm.dev/*https://lpm.dev (auth + monetization layer)
Anything that matches a registry mapping in .npmrcThe registry you mapped

The @lpm.dev/* scope is the only scope that always goes to lpm.dev. Everything else is npm by default.

Pointing at a private registry

LPM reads .npmrc exactly the way npm does — project-local first, then user-level (~/.npmrc).

.npmrc
# A private registry for one scope
@my-company:registry=https://npm.my-company.com
//npm.my-company.com/:_authToken=${NPM_TOKEN}

# Override the default registry for everything else (optional)
registry=https://my-mirror.example.com
//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 LPM-specific config is required.

For per-environment tokens, prefer ${VAR} expansion (above) over hardcoded secrets — LPM and npm both support it.

TLS, custom CAs, and mTLS

LPM 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

.npmrc
# 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.pem

cafile 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)

.npmrc
strict-ssl=false

Disables hostname, expiry, and CA validation process-wide. LPM 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 at a PEM cert and key:

.npmrc
# 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.key

certfile 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:

~/.npmrc
certfile=client.pem      # → ~/client.pem
keyfile=client.key       # → ~/client.key

You can drop secrets next to the .npmrc and reference them with bare names.

Encrypted private keys

LPM accepts encrypted PKCS#8 keys. The passphrase is resolved in this order:

  1. LPM_KEY_PASSPHRASE environment variable (the CI-friendly path).
  2. Interactive TTY prompt — only when both stdin AND stdout are a terminal.
  3. Otherwise, hard error citing the keyfile line.
LPM_KEY_PASSPHRASE='hunter2' lpm install

The 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.pem

PKCS#12 (.p12 / .pfx) is not supported

LPM 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.pem

Then:

.npmrc
certfile=/path/to/cert.pem
keyfile=/path/to/key.pem

If you point certfile= or keyfile= directly at a .p12/.pfx file, LPM emits a cited error with this recipe inline.

When does lpm.dev come in

lpm.dev 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 handles the auth, license, and download.

If you're not publishing, ignore lpm.dev entirely — LPM is a strict superset of npm-the-client and will never send your react install through it.

Private packages on lpm.dev

lpm login
lpm install @lpm.dev/acme.internal

lpm 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 — no other registry sees it.

To generate .npmrc without relying on lpm login in the current shell:

lpm setup ci     # 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, and a private registry simultaneously — LPM resolves each dep against the correct origin and writes the resolution to the lockfile.

package.json
{
  "dependencies": {
    "react": "^19.0.0",
    "@my-company/internal-tool": "^2.1.0",
    "@lpm.dev/acme.private-utils": "^1.0.0"
  }
}
.npmrc
@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