LPM-cli

lpm doctor / lpm health

Project + environment health checks, with auto-fix.

lpm doctor [--all] [--fix] [-y]
lpm health

Two complementary diagnostic commands:

  • lpm doctor — checks the project + environment: managed runtime detection, lpm install state, global store accessibility, vault-storage and Sigstore posture, plus the broader --all sweep for registry/auth/tunnel/tooling/sandbox state. The big "is everything OK here?" command.
  • lpm health — checks the registry: connectivity, latency, the registry's own health endpoint. The "is the service up?" command.

Fast vs full

lpm doctor runs a fast local-only preset by default: no registry probe, no whoami, no tunnel lookup, no lint / fmt subprocess, no plugin reachability fetch, no sandbox probe, no manifest-compat sweep. It's the "why is this project broken right now?" pass — milliseconds.

Pass --all to run every catalog row, including the network-touching and subprocess-spawning ones. Use --all in CI gates and when you actually want the broad sweep; stick with the default for quick local checks.

Examples

lpm doctor                       # fast preset — local-only checks
lpm doctor --all                 # full sweep — every catalog row
lpm doctor --fix                 # auto-fix what it can, prompt where unclear
lpm doctor --fix -y              # auto-fix, no prompts (CI-friendly)
lpm doctor --all --json          # full sweep, structured for tooling

lpm health                       # registry connectivity check
lpm health --json

--fix only dispatches remediations for rows that were actually checked. Some of those remediations, especially lpm install, can incidentally repair adjacent state as a side effect, so treat fixes_applied as the authoritative list of what doctor intentionally ran. Pair --all --fix for the full repair pass.

What lpm doctor checks

The check set is intentionally broad — every signal that "this project is in a known-good state to develop in." For the canonical inventory, run:

lpm doctor list                          # full catalog (human)
lpm doctor list --json                   # full catalog (machine-readable)
lpm doctor list --code <code>            # one entry
lpm doctor list --category <substring>   # filter by group

The catalog drives the runtime: lpm doctor's Check constructors take a &'static CheckEntry reference, so a runtime row cannot carry a code that isn't a registered static — that part is by construction. Catalog completeness (every static reaches lpm doctor list) is enforced by a unit-test drift guard rather than the type system, so adding a new static without registering it fails CI rather than the build. The Catalog section below mirrors what lpm doctor list prints today.

--json returns the full per-check result set; --verbose adds remediation hints inline.

Machine-readable output (--json)

lpm doctor --json is the structured surface every CI pipeline, MCP server, or scripting tool should consume — it's a contract, not a debug aid. The shape is stable; consumers should match on code, never on the human-readable check or detail strings.

{
  "success": true,
  "mode": "all",
  "no_failures": false,
  "clean": false,
  "has_warnings": true,
  "passed": 12,
  "failed": 1,
  "warnings": 2,
  "fixes_applied": [],
  "checks": [
    {
      "code": "registry_reachable",
      "check": "Registry",
      "passed": true,
      "severity": "pass",
      "detail": "https://lpm.dev"
    },
    {
      "code": "auth_invalid",
      "check": "Authentication",
      "passed": false,
      "severity": "fail",
      "detail": "token exists but invalid — run: lpm login"
    }
  ]
}

Top-level fields:

FieldTypeNotes
modestring"fast" (default preset) or "all" (under --all). Agents need this to distinguish what was actually checked vs skipped.
no_failuresbooltrue when no severity = "fail" checks fired (warnings tolerated).
cleanbooltrue when no failures AND no warnings.
has_warningsbooltrue when at least one severity = "warn" check fired.
passed / failed / warningsnumberCounts.
fixes_appliedstring[]Remediations --fix ran. Empty in read-only mode.
checks[]arrayPer-check rows — see below.

Per-check fields:

FieldTypeNotes
codestringStable snake_case identifier. Examples: registry_unreachable, node_modules_missing, pnpm_overrides_drift. Match on this.
checkstringHuman-readable label. Wording can change.
passedbooltrue for pass and warn; false for fail.
severitystringOne of pass | fail | warn.
detailstringHuman-readable detail with remediation. May be empty.

severity = "warn" carries passed = true so that warning-only runs don't trip strict CI gates that key off no_failures. Use has_warnings (top-level) or filter checks[].severity == "warn" if you want to surface warnings explicitly.

Exit code: 0 when no severity = "fail" checks fired (warnings are tolerated), 1 otherwise.

Auto-fix mode

lpm doctor --fix

Walks the failing checks and asks before each remediation. Common fixes include:

  • Install the missing managed runtime
  • Run lpm install to reconcile node_modules/
  • Run lpm fmt to reconcile formatting
  • Regenerate lpm.lockb from lpm.lock
  • Update .gitattributes so lpm.lockb is marked as binary

Pass -y (which implies --fix) to skip every prompt — useful in CI bootstrap scripts.

What lpm health checks

lpm health
lpm health --json

Hits the registry's health endpoint with a single round-trip, reports up/down and the response time in milliseconds. Exits non-zero when the registry is unreachable, so the command works as a self-gating CI smoke test on its own — no need to wrap it.

{
  "success": true,
  "healthy": true,
  "registry_url": "https://lpm.dev",
  "response_time_ms": 87
}

lpm health does not aggregate percentiles or sample repeatedly — response_time_ms is one measurement. For load-testing or latency distribution, drive it from your own benchmarking harness.

lpm health does not check your project — only the registry. For project + environment checks, use lpm doctor.

Flags

lpm doctor

FlagEffect
--allRun every catalog row (including registry / auth / tunnel / lint / fmt / plugin / sandbox / manifest-compat). Default is the fast local-only preset
--fixAuto-fix issues with prompts. Only repairs what was actually checked — pair with --all for the full repair pass
-y, --yesSkip prompts (implies --fix)

lpm doctor list

FlagEffect
--code <code>Filter to a single catalog entry by exact code match
--category <substring>Case-insensitive substring filter against the category label

--json (the global flag) toggles structured output for both lpm doctor and lpm doctor list.

lpm health

No specific flags besides the global flags--json and --verbose are useful.

Catalog

Every check lpm doctor can emit, grouped by category. The code column is the load-bearing identifier — match on it in automation. Wording in the description and remediation columns may evolve, but codes never change once shipped.

This table is hand-maintained alongside the catalog source in crates/lpm-cli/src/doctor_catalog.rs. For the live, machine-readable inventory, run lpm doctor list --json — that's the authoritative surface. Drift between this table and the runtime catalog is a docs bug; please file an issue if you spot it.

Infrastructure

CodeSeverityDescription
registry_reachablepassThe configured registry responds to its health endpoint.
registry_unreachablefailThe configured registry could not be reached. Fix: Check network connectivity, firewall rules, or the registry status page.
global_store_accessiblepassThe shared content-addressable store at ~/.lpm/store/ resolves.
global_store_inaccessiblefailThe shared content-addressable store could not be located or read. Fix: Verify $HOME is set and that ~/.lpm/store/ is writable. Re-run lpm install to recreate.

Auth

CodeSeverityDescription
auth_validpassA registry auth token is present and whoami succeeds.
auth_invalidfailA token is stored but the registry rejected it. Fix: Re-authenticate with lpm login.
auth_missingfailNo registry auth token is configured. Fix: Run lpm login.
vault_storage_keychainpassOn macOS, vault secrets are unlocked through the OS Keychain.
vault_storage_nativepassOn Linux/Windows, vault blobs are encrypted on disk and the local data key is protected by Secret Service-compatible storage or Credential Manager.
vault_storage_fallbackwarnLinux/Windows vault secrets are using the encrypted-file fallback key at ~/.lpm/.vault-fallback-key; any same-UID process can read that key file. Fix: unlock or repair the OS secure store so LPM can promote the data key.
vault_storage_unavailablefailEncrypted vault files exist locally, but LPM cannot access the OS-protected data key or the legacy fallback key. Fix: unlock Secret Service/Credential Manager, repair the secure store, or restore the fallback key from backup if this machine was not promoted yet.

Project state

CodeSeverityDescription
package_json_presentpassA readable package.json exists in the project directory.
package_json_missingfailNo package.json exists in the project directory. Fix: Run lpm init, or cd into your project root before running doctor.
linker_mode_resolvedpassReports the linker mode the install pipeline would resolve to plus which surface produced it (CLI flag / ~/.lpm/config.toml / LPM_LINKER / package.json > lpm > linker / workspace auto-detect / default). Informational.
node_modules_isolated_healthypassnode_modules/ exists and is backed by an isolated .lpm/wrappers/ store.
node_modules_hoisted_healthypassnode_modules/ exists and uses the hoisted layout.
node_modules_virtual_healthypassnode_modules/ symlinks point into the virtual store at ~/.lpm/store/v2/links/.
v2_store_orphanswarnThe v2 store at ~/.lpm/store/v2/ has link entries or objects no longer reachable from any registered project. Fix: lpm cache prune (dry-run) then lpm cache prune --apply.
node_modules_mixed_layoutwarnBoth isolated and hoisted layout state are present in node_modules/. Fix: Re-run lpm install to converge on the configured linker layout.
node_modules_no_storewarnnode_modules/ exists but no LPM-owned store is present. Fix: Run lpm install to rebuild the layout under LPM ownership.
node_modules_legacy_layoutwarnAn older LPM layout is on disk and a one-time migration is pending. Fix: Run lpm install to migrate to the current layout.
node_modules_missingfailnode_modules/ is missing — dependencies have not been installed. Fix: Run lpm install.
lockfile_presentpasslpm.lock is present at the project root.
lockfile_missingwarnNo lpm.lock was found at the project root. Fix: Run lpm install — it generates the lockfile alongside node_modules/.
lockfile_binary_validpasslpm.lockb matches lpm.lock and parses cleanly.
lockfile_binary_missingwarnlpm.lockb is missing while lpm.lock is present. Fix: Run lpm doctor --fix to regenerate, or run lpm install.
lockfile_binary_stalewarnlpm.lockb does not match the contents of lpm.lock. Fix: Run lpm doctor --fix to regenerate, or run lpm install.
lockfile_binary_corruptwarnlpm.lockb does not parse as a valid binary lockfile. Fix: Run lpm doctor --fix to regenerate from lpm.lock.
gitattributes_lockb_markedpass.gitattributes marks lpm.lockb as binary.
gitattributes_lockb_unmarkedwarn.gitattributes exists but does not mark lpm.lockb as binary. Fix: Add lpm.lockb binary to .gitattributes so git diffs treat the file correctly.
gitattributes_missingwarnNo .gitattributes found — lpm.lockb may be diffed as text. Fix: Run lpm init or add lpm.lockb binary to a new .gitattributes.
deps_sync_cleanpasslpm.lock and package.json agree on the declared dependency set.
deps_sync_driftwarnlpm.lock and package.json disagree — manifest changes have not been resolved. Fix: Run lpm install to reconcile.
local_source_dir_okpassA file: / link: dependency points at a directory with a readable package.json.
local_source_tarball_okpassA file: dependency points at a readable tarball.
local_source_dir_no_pkgfailA file: / link: dependency points at a directory with no package.json. Fix: Add package.json to the local path or update the dep target.
local_source_invalid_typefailA file: / link: dependency points at an unexpected file type. Fix: Re-target the dependency at a directory or a tarball file.
local_source_link_to_filefailA link: dependency points at a regular file rather than a directory. Fix: link: must reference a project directory; switch to file: for tarballs.
local_source_unreadablefailA file: / link: dependency target could not be read. Fix: Restore the target path or fix permissions; rerun lpm install.

lpm.json

CodeSeverityDescription
lpm_json_validpasslpm.json exists and parses against the strict schema.
lpm_json_schema_warningswarnlpm.json parsed but contained unknown or off-schema fields. Fix: Inspect the listed fields and align with the documented schema.
lpm_json_invalid_syntaxfaillpm.json is not valid JSON. Fix: Fix the JSON syntax error reported in detail.
lpm_json_not_objectfaillpm.json's top-level value is not an object. Fix: Replace with a JSON object literal { ... }.
lpm_json_unreadablefaillpm.json could not be read from disk. Fix: Fix file permissions; rerun doctor.

Runtime

CodeSeverityDescription
node_managed_matchpassA managed Node install matches the pinned spec.
node_pinned_unmetwarnProject pins a Node version but only a system Node satisfies (or none does). Fix: Run lpm use node@<version> to install and pin the managed version.
node_missing_pinnedfailProject pins a Node version and no Node is reachable. Fix: Run lpm use node@<version> to install the pinned version.
node_system_unpinnedpassNo Node version is pinned; a system Node was found. Fix: Optional — pin a Node version via lpm.json > runtime.node for reproducibility.
node_missing_unpinnedfailNo Node version is pinned and no Node is reachable. Fix: Install Node via lpm use node@22 (or your preferred version).
bun_managed_matchpassA managed Bun install matches lpm.json > runtime.bun.
bun_pinned_unmetwarnProject pins a Bun version but no managed Bun install matches. Fix: Run lpm use bun@<version> to install and pin the managed version.
bun_missing_pinnedfailProject pins a Bun version and no Bun is reachable. Fix: Run lpm use bun@<version> to install the pinned version.

Tunnel

CodeSeverityDescription
tunnel_activepassA tunnel domain is configured and the registry confirms ownership.
tunnel_idlepassNo tunnel domain is configured for this project.
tunnel_unauthenticatedpassA tunnel domain is configured but the user is not authenticated to verify ownership. Fix: Run lpm login to verify ownership.
tunnel_unverifiedpassOwnership of the tunnel domain could not be verified due to a transient registry error. Fix: Retry; check lpm health.
tunnel_not_claimedwarnThe configured tunnel domain is not claimed by this account. Fix: Run lpm tunnel claim <domain> (Pro/Org) or change the domain.
tunnel_owned_by_otherwarnThe configured tunnel domain is claimed by a different account. Fix: Choose a different domain; the current claim is held elsewhere.
tunnel_unreachablewarnThe registry could not be reached to verify the tunnel claim. Fix: Check network; retry lpm doctor.
tunnel_unknown_basewarnConfigured tunnel domain uses a base domain LPM does not recognize. Fix: Use one of the supported base domains, or contact the LPM team to add a new one.
tunnel_domain_no_dotwarnConfigured tunnel domain has no dot separating subdomain from base. Fix: Set the full domain: <subdomain>.lpm.fyi or <subdomain>.lpm.llc.
tunnel_domain_empty_labelwarnConfigured tunnel domain has an empty label (e.g., ..lpm.fyi). Fix: Remove the empty label.
tunnel_domain_label_too_longwarnA label in the configured tunnel domain exceeds 63 characters (DNS limit). Fix: Shorten the offending label.
tunnel_domain_too_longwarnTotal tunnel domain length exceeds the DNS limit. Fix: Shorten the domain.
tunnel_subdomain_lengthwarnTunnel subdomain length is outside the allowed range. Fix: Pick a subdomain between the documented bounds.
tunnel_subdomain_charswarnTunnel subdomain contains characters outside the allowed alphabet. Fix: Use only lowercase letters, digits, and hyphens.
tunnel_subdomain_hyphenwarnTunnel subdomain starts or ends with a hyphen. Fix: Subdomains must start and end with an alphanumeric character.

Code quality

CodeSeverityDescription
lint_cleanpassOxlint reports no issues for the project.
lint_warningswarnOxlint reported warnings for the project. Fix: Run lpm lint to inspect, then address.
lint_errorsfailOxlint reported errors for the project. Fix: Run lpm lint and fix the reported errors.
lint_unparseablewarnOxlint output could not be parsed by doctor. Fix: Run lpm lint directly to see the raw output.
fmt_cleanpassBiome reports the project formatting is clean.
fmt_unformattedwarnBiome found files that need reformatting. Fix: Run lpm fmt to apply formatting.
fmt_other_issuewarnBiome reported a non-format issue (parse error, config error, etc.). Fix: Run lpm fmt --check to see the raw biome output.

TypeScript

CodeSeverityDescription
typescript_healthypassProject-local tsc resolves through the node_modules/.bin chain.
typescript_missing_for_tsconfigwarntsc is reachable only via the system PATH; no project-local install. Fix: Run lpm install -D typescript so editor + CI use the same version.
typescript_unavailablefailtsc is not reachable for a tsconfig.json-owning directory. Fix: Run lpm install -D typescript (or lpm install if typescript is already declared).

Plugin

CodeSeverityDescription
plugin_up_to_datepassAn installed plugin is at the latest known version.
plugin_update_availablewarnA newer version of an installed plugin is available upstream. Fix: Run lpm plugin update <name> to update.

Workspace

CodeSeverityDescription
workspace_acyclicpassNo dependency cycles among workspace members.
workspace_cyclefailA dependency cycle exists among workspace members. Fix: Break the cycle by removing or restructuring the offending workspace dep.

Global installs

CodeSeverityDescription
global_manifest_validpass~/.lpm/global/manifest.json parses and is structurally valid.
global_manifest_absentpassNo global install manifest is present (no global installs yet).
global_manifest_corruptfail~/.lpm/global/manifest.json is unreadable or malformed. Fix: Inspect and repair, or reinstall affected globals.
global_bin_on_pathpass~/.lpm/bin is on the user's PATH.
global_bin_off_pathwarn~/.lpm/bin is not on the user's PATH. Fix: Add ~/.lpm/bin to your shell PATH (see lpm install --global notes).
global_shims_cleanpassEvery shim in ~/.lpm/bin belongs to a recorded global install.
global_shims_no_dirpassGlobal bin directory does not yet exist.
global_shims_orphanswarnFiles exist in ~/.lpm/bin without a matching manifest entry. Fix: Remove the orphan files, or run lpm install --global to re-register.
global_shims_unreadablewarnGlobal bin directory could not be enumerated. Fix: Fix permissions; rerun doctor.
global_install_roots_emptypassNo global installs are recorded.
global_install_roots_healthypassEvery global install root exists and carries a ready marker.
global_install_roots_unhealthyfailOne or more global install roots are missing or incomplete. Fix: Reinstall the affected globals (lpm install --global <pkg>).
global_trusted_deps_validpass~/.lpm/global/trusted-dependencies.json parses cleanly.
global_trusted_deps_absentpassNo host-global trusted-dependencies file is present yet.
global_trusted_deps_corruptfail~/.lpm/global/trusted-dependencies.json is unreadable, malformed, or uses a newer schema. Fix: Repair or delete it, then re-approve globals as needed.

Sandbox + scripts

CodeSeverityDescription
sandbox_availablepassThe OS sandbox backend used by lifecycle scripts is available.
sandbox_helper_missingwarn(Windows) lpm-sandbox-helper.exe is not located next to lpm.exe, so strict-mode AppContainer containment falls back to the Low IL backend (no outbound-network denial). Fix: Reinstall @lpm-registry/cli, or set LPM_SANDBOX_HELPER=<path> if the helper lives elsewhere.
sandbox_degradedwarnStrict mode is engaged but the host kernel forced the V1 fallback (filesystem containment only — no outbound network denial). Fix: Upgrade to kernel 6.7+ and unset [sandbox] allow-degraded, or drop back to default via lpm config sandbox --set default.
sandbox_disabled_by_userwarnSandbox is persistently disabled via [sandbox] mode = "none". Lifecycle scripts run with no containment, including credential env vars. Fix: Restore via lpm config sandbox --set default (or --set strict).
sandbox_kernel_too_oldwarnLinux kernel is too old to support Landlock at the required ABI. Fix: Upgrade the kernel, or accept that lifecycle scripts run unsandboxed.
sandbox_unsupported_platformwarnNo supported sandbox backend exists for this platform. Fix: Lifecycle scripts will not be sandboxed on this platform — review with lpm approve-scripts.
sandbox_probe_failedfailThe sandbox probe errored unexpectedly. Fix: File a bug with the detail text.
policy_force_security_floorwarnAn override is in effect that lowers the default script-policy floor. Fix: Review the override and confirm it matches the project's threat model.

Sigstore provenance

CodeSeverityDescription
sigstore_verify_enforcedpassSigstore provenance verification is fail-closed. Rejected attestations refuse the install or approval.
sigstore_verify_warn_modewarnSigstore verification only warns and does not block installs. Fix: Unset LPM_PROVENANCE_ENFORCE (or set it to deny), or run lpm config sigstore --set deny.
sigstore_verify_disabledwarnSigstore verification is disabled entirely. Fix: Re-enable deny mode via LPM_PROVENANCE_ENFORCE=deny or lpm config sigstore --set deny.

Manifest compat

CodeSeverityDescription
pnpm_overrides_driftwarnEntries in pnpm.overrides that LPM is not honoring through lpm.overrides, top-level overrides, or resolutions. Fix: Run lpm migrate to translate, or mirror the entries verbatim in lpm.overrides.
pnpm_patches_driftwarnEntries in pnpm.patchedDependencies whose patch files LPM cannot bind to via lpm.patchedDependencies. Fix: Re-author with lpm patch or mirror the entries into lpm.patchedDependencies after verifying the patch applies cleanly under LPM's stricter integrity binding.
pnpm_peer_rules_driftwarnSub-key entries (ignoreMissing, allowedVersions, allowAny) that LPM is not honoring via lpm.peerDependencyRules. Fix: Run lpm migrate (the planner translates selector keys 1:1) or mirror the rules under lpm.peerDependencyRules.
engines_npm_ignoredwarnengines.npm is declared but LPM is not the npm CLI and does not enforce its constraint. Fix: Remove the field, or accept that LPM ignores it. engines.node and engines.lpm are enforced.
engines_pnpm_ignoredwarnengines.pnpm is declared but LPM does not enforce pnpm's version constraint. Fix: Remove the field, or accept that LPM ignores it. engines.node and engines.lpm are enforced.
engines_yarn_ignoredwarnengines.yarn is declared but LPM does not enforce yarn's version constraint. Fix: Remove the field, or accept that LPM ignores it. engines.node and engines.lpm are enforced.
engines_bun_ignoredwarnengines.bun is declared but LPM does not enforce bun's version constraint. Fix: Use lpm.json > runtime.bun when the project needs managed Bun on PATH; engines.node and engines.lpm are enforced.

See also