LPM-cli

lpm publish

Publish a package to lpm.dev, npm, GitHub Packages, or a custom registry.

lpm publish   # alias: lpm p

Packs the current directory and uploads it to a registry. By default, the target is lpm.dev (where every package is private until you flip distribution to pool or marketplace). Publish elsewhere with --npm, --github, --gitlab, or --publish-registry.

lpm publish runs the same quality and security checks regardless of target — it scans for hardcoded secrets, computes a quality score, and refuses to publish if --min-score is not met.

Examples

lpm publish                                  # publish to lpm.dev
lpm publish --dry-run                        # pack + check, don't upload
lpm publish --check                          # quality report only
lpm publish --npm                            # publish to registry.npmjs.org
lpm publish --github                         # publish to GitHub Packages
lpm publish --publish-registry https://r.example.com   # custom npm-compatible
lpm publish --provenance                     # require Sigstore provenance (CI)
lpm publish --min-score 80                   # gate on quality score
lpm stage publish --tag next                 # stage a version on npm
lpm stage list @scope/pkg                    # list staged npm versions
lpm stage approve <stage-id> --otp 123456    # promote the staged version

What runs before upload

In order:

  1. Pack — assemble the tarball from files, main, bin, etc. Tarballs over 500 MB are rejected outright.
  2. Workspace protocol rewrite — in monorepos, workspace:* and catalog: specifiers in dependencies get replaced with their resolved versions before hashing. Registries can't resolve those protocols; the rewrite happens on the in-memory tarball so the on-disk package.json stays untouched.
  3. Secret scan — refuse to publish if known secret patterns are found in the pack contents (skip with --allow-secrets, not recommended).
  4. Quality score — compute readme/license/types/tests/maintenance signals. The publish path prints one compact score line and lists only failing checks. With --min-score N, abort if the score is below the threshold.
  5. Skills validation (LPM target only) — when the project ships .lpm/skills/, validate every skill file and run a security pattern check on the content; either failing aborts the publish.
  6. Confirm — interactive confirmation in a TTY. Skip with -y.
  7. Upload — push the tarball to each target registry with the appropriate auth.

Successful human output is a compact status stream:

✓ Secret scan passed
✓ Quality score: 91/100
› Uploading tarball to lpm.dev
    target     @lpm.dev/owner.pkg@1.2.3
    visibility private
    dist-tag   latest
✓ Done · published @lpm.dev/owner.pkg@1.2.3 in 2.41s

If quality checks fail or are missing, only those rows are shown beneath the score. The full per-category breakdown lives in lpm quality.

--check is the local-validation surface — it runs the local pack + secret scan + quality scoring and stops. It never contacts a registry: no OIDC exchange, no skills-staleness lookup, no auth handshake. Use it as a fast pre-commit gate.

--dry-run runs everything --check does, plus an LPM-side preflight: OIDC exchange (when LPM is a target) and a skills-metadata lookup. It stops there. It does not prompt for confirmation, does not verify the resolved token can actually publish this package (no whoami / permission check), and does not contact --npm / --github / --gitlab targets. Use it to catch local issues plus LPM-trust misconfiguration — it won't tell you whether the upload itself would land.

npm Trusted Publishing

lpm publish --npm and lpm stage publish automatically use npm Trusted Publishing when a supported CI OIDC token is available. LPM exchanges the CI identity token with npm for a short-lived registry token, then uses that token only for the publish or stage-publish upload.

Supported inputs:

CI sourceConfigure
GitHub Actionspermissions: id-token: write; LPM fetches the runtime token with audience npm:registry.npmjs.org
GitLab CI / CircleCI / other supported providersSet NPM_ID_TOKEN to an OIDC ID token minted with audience npm:registry.npmjs.org

Trusted Publishing is attempted only for npm's public registry (https://registry.npmjs.org; HTTP loopback is accepted for local test registries). Custom npm-compatible registries, GitHub Packages, GitLab Packages, and --publish-registry targets keep using token auth. If npm's OIDC exchange fails and a normal npm token is available, LPM falls back to the token path; otherwise the OIDC error is surfaced.

OIDC is publish-only. lpm stage list, view, download, approve, and reject still require normal npm auth because npm limits Trusted Publishing authentication to publish operations.

npm staged publish

lpm stage publish [--tag next] [--access public] [-y]
lpm stage list [package]
lpm stage view <stage-id>
lpm stage download <stage-id>
lpm stage approve <stage-id> [--otp 123456]
lpm stage reject <stage-id> [--otp 123456]

lpm stage wraps npm staged publishing for the current project. It is npm-only in this release: there is no lpm.dev, GitHub Packages, GitLab Packages, custom publish registry, workspace-recursive, or tarball/package-spec staging mode yet.

Stage publish reuses the normal npm publish preparation path: pack, workspace dependency rewrite, secret scan, quality gate, npm name/access/tag config, optional npm-name tarball rewrite, and optional --provenance over the final tarball. The upload goes to npm's staging endpoint instead of the live publish endpoint, so the version is not installable until it is approved.

Auth for lpm stage publish uses npm Trusted Publishing first when available, then the normal npm token sources:

Source order
npm Trusted Publishing (NPM_ID_TOKEN or GitHub Actions OIDC, audience npm:registry.npmjs.org)
NPM_TOKEN
token stored by lpm login --npm
locked .npmrc token

Publishing to staging does not prompt for OTP. npm defers proof-of-presence to lpm stage approve and lpm stage reject, where --otp or the usual npm web-auth retry flow is used. Those approval commands are token-authenticated; NPM_ID_TOKEN alone is not enough.

lpm stage publish validates npm's version/tag safety rules before the real upload: the package must already exist on npm, an already-published version is blocked, prerelease versions require an explicit tag, and implicit latest is blocked when npm already has a higher stable version. --dry-run stays local-only and does not contact npm, so those remote version checks run only for a real stage publish.

Use --npm-registry <URL> to target a compatible npm staging registry. The global --registry flag still means the lpm.dev registry and is rejected on lpm stage commands with a message to use --npm-registry.

Every lpm stage subcommand supports --json. JSON output uses LPM envelopes with success, target: "npm", registry, auth on stage publish ("oidc" or "token"), stageId when a single staged package is involved, and data for the registry response.

Provenance

lpm publish --provenance

Generates a Sigstore-signed attestation that proves which CI workflow built the tarball and which commit it came from. Requires an audience-sigstore OIDC token from a supported CI environment:

  • GitHub Actions — enable permissions: id-token: write on the job; the runtime mints the JWT.
  • GitLab CI — mint SIGSTORE_ID_TOKEN via the id_tokens block with aud: sigstore (the legacy LPM_GITLAB_OIDC_TOKEN env var is also accepted).

Other platforms aren't supported — Sigstore needs the OIDC issuer to sign. The flag fails loud (non-zero exit) if no usable token is found or if Sigstore signing/Rekor recording fails; it never silently falls back to publishing without provenance.

Consumers can later verify provenance via the audit pipeline or registry UI.

Targets

lpm publish --lpm --npm                 # publish to both in one run
lpm publish --github --gitlab           # cross-publish to both git-host registries

Multiple target flags compose — lpm publish runs through them in order and reports per-target success / failure. Up to 5 target registries per invocation.

FlagRegistryRequirements
(default) / --lpmlpm.devPackage name must be @lpm.dev/owner.pkg. Override with publish.lpm.name in lpm.json
--npmregistry.npmjs.org (or custom via publish.npm.registry)Override name with publish.npm.name; tune access / tag / otpRequired under publish.npm.*
--githubGitHub PackagesPackage name must be scoped (@owner/pkg). Falls back to publish.github.namepublish.npm.namepackage.json#name — any of them being a scoped name is enough
--gitlabGitLab Packagespublish.gitlab.projectId is required. Optional publish.gitlab.registry overrides the default https://gitlab.com instance
--publish-registry <URL>Any npm-compatible registryURL must be https://...

When no target flag is set on the CLI, the active set comes from lpm.json > publish.registries (default: ["lpm"]).

You must have auth for the chosen target — see Authentication for lpm login --npm, --github, --gitlab, or lpm login --login-registry <URL> for custom.

Custom publish registry URLs are HTTPS-only whether they come from --publish-registry or lpm.json > publish.registries. LPM rejects http:// before dry-run planning or upload.

Token resolution for npm-compatible targets:

TargetResolution order
npmnpm Trusted Publishing for publish --npm / stage publishNPM_TOKEN → token stored by lpm login --npm → locked .npmrc token
GitHub PackagesGITHUB_TOKEN → valid gh auth for github.com → LPM-stored fallback token
GitLab.com PackagesGITLAB_TOKEN / CI_JOB_TOKEN → valid glab auth → LPM-stored fallback token
Self-managed GitLabGITLAB_TOKEN / CI_JOB_TOKEN → LPM-stored fallback token
Custom registriesLPM-stored custom-registry token

LPM advertises npm web auth on npm-compatible publish requests (npm-auth-type: web, npm-command: publish). If npm returns a browser-based OTP challenge (authUrl + doneUrl), LPM opens the browser flow, polls for completion, and retries with the returned npm-otp. Classic www-authenticate: OTP prompts still work in an interactive terminal; non-interactive publish should use an automation token.

Flags

FlagEffect
--dry-runPack and check, don't upload
--checkRun quality report only
-y, --yesSkip interactive confirmation
--provenanceRequire Sigstore provenance (CI with OIDC only)
--min-score <N>Minimum quality score required (0–100)
--allow-secretsSkip pre-publish secret scanning (not recommended)
--npm / --lpm / --github / --gitlabTarget a specific registry
--publish-registry <URL>Custom npm-compatible registry (https:// only)
stage --npm-registry <URL>npm staging registry for lpm stage subcommands
stage --otp <CODE>One-time password for lpm stage approve / reject

Plus the global flags.

See also

  • Authentication — login, tokens, CI setup
  • Save policy — how versions get saved on the consumer side
  • lpm quality — preview the quality report
  • Pool — flipping a published package to pool distribution