LPM CLI

lpm policy

Inspect, diagnose, and test local programs that allow, warn about, or block packages during install.

Use lpm policy to inspect and test local policy extensions for package installs.

lpm policy list [--json]
lpm policy status [--json]
lpm policy doctor [<EXTENSION>] [--json]
lpm policy test <EXTENSION> --package <NAME@VERSION> [--json]

A policy extension is a local program that reviews resolved package candidates. It returns an allow, warn, or block decision for each candidate.

For example, an organization can connect LPM CLI to a local deny list or an internal package-approval service. The extension runs before LPM CLI downloads registry tarballs or links packages.

Quickstart

Inspect the policy extensions that are active on this computer:

lpm policy status

The output shows the number of active extensions, their modes, and any command-availability errors.

Choose a command

CommandPurposeUse it when
lpm policy listShow active extension configurationYou want names, commands, modes, error behavior, and timeouts
lpm policy statusShow a policy overview and diagnosticsYou want a read-only summary that always exits successfully after valid configuration loads
lpm policy doctor [EXTENSION]Diagnose all extensions or one named extensionYou need a CI gate or an exit code for failed diagnostics
lpm policy test <EXTENSION> --package <NAME@VERSION>Run one extension with a synthetic package candidateYou want to see its decision before an install

list, status, and doctor do not run extension programs. test runs the selected program.

When to use a policy extension

Use a policy extension for a package rule that depends on your own system. Common uses include:

  • A company package deny list
  • An internal approval or compliance service
  • A local license or package-source rule
  • A custom warning feed during package evaluation

Policy extensions are separate from LPM Firewall and the lpm security floor.

The current policy-extension configuration is local user configuration. It is not part of the signed security floor or an OS-managed policy.

Recipes

Review all active extensions

List the resolved configuration first:

lpm policy list

Then review the policy posture and command availability:

lpm policy status

Disabled extensions do not appear in either command.

Diagnose one extension

Run the diagnostic for one named extension:

lpm policy doctor local-feed

If the configured program is missing, is not a file, or is not executable, the command fails.

Use the unscoped command to diagnose every active extension:

lpm policy doctor

doctor exits with code 1 for a failed diagnostic. Report-only mode produces a warning and keeps exit code 0.

Test one package candidate

Send a synthetic package candidate to the extension:

lpm policy test local-feed --package react@19.0.0

For a scoped package, LPM CLI uses the final @ as the version separator:

lpm policy test local-feed --package @scope/pkg@1.2.3

Use one npm-compatible concrete version. LPM converts accepted loose forms to a canonical version before it starts the extension. For example, v19.0.0 and 19.0 become 19.0.0.

Ranges such as ^19.0.0 and ~19.0.0 are invalid. Wildcards and dist-tags such as latest are also invalid.

The test does not contact a registry or download the package.

The synthetic candidate represents a direct public npm dependency. It has no integrity value or registry publication time.

The command reports allow, warn, and block decisions. A block result does not make this test command fail.

Start in report mode

Use report mode while you evaluate a new extension:

~/.lpm/config.toml
[policy.extensions.local-feed]
command = ["/usr/local/bin/lpm-policy-feed", "--deny-list", "/etc/lpm/deny.json"]
mode = "report"
on-error = "warn"

Run the diagnostic and a normal install:

lpm policy doctor local-feed
lpm install

Report mode prints warning and block decisions but does not stop the install.

After you review the results, change the extension to enforcement mode:

~/.lpm/config.toml
[policy.extensions.local-feed]
command = ["/usr/local/bin/lpm-policy-feed", "--deny-list", "/etc/lpm/deny.json"]
mode = "enforce"
on-error = "block"

Enforcement mode stops the install for a block decision.

Configuration

Configure policy extensions in ~/.lpm/config.toml:

~/.lpm/config.toml
[policy.extensions.local-feed]
command = ["/usr/local/bin/lpm-policy-feed", "--deny-list", "/etc/lpm/deny.json"]
enabled = true
mode = "enforce"
on-error = "block"
timeout-ms = 5000
events = ["package.candidate"]
KeyAccepted valueDefaultPurpose
commandNon-empty string arrayRequired for an active extensionSelect the program and its arguments
enabledBooleantrueInclude or disable the extension
modereport or enforcereportControl the effect of package decisions
on-errorwarn or blockwarn in report mode, block in enforcement modeControl runner and protocol errors
timeout-msInteger from 1 through 1200005000Limit the complete extension exchange
events["package.candidate"]["package.candidate"]Select the supported policy event

These are the only accepted keys. This rule also applies to disabled extensions.

Command rules

The first command entry must use one of these forms:

  • An absolute executable path
  • A program name found in an absolute PATH directory

LPM CLI rejects relative executable paths such as ./policy-extension. It also ignores relative and empty PATH entries.

Later command entries are program arguments. LPM CLI starts the program directly and does not use a shell.

The program runs in the current project directory during install. lpm policy test uses the directory where you run the command.

LPM CLI provides a small process environment. It sends one JSON request on standard input and reads one JSON response from standard output.

Read the policy-extension protocol before you write an extension.

Project configuration

Policy extensions use only ~/.lpm/config.toml.

You cannot configure them in lpm.json, package.json, or lpm.toml. A cloned repository cannot activate a local program.

This configuration is not part of the signed security floor yet. Protect write access to ~/.lpm/config.toml with your operating-system permissions.

How installs use extensions

LPM CLI runs active extensions after dependency resolution and package filtering. It runs them before registry tarballs are fetched or packages are linked.

Each extension receives package metadata, including the name, version, source, integrity, direct status, and optional status. It does not receive package contents.

The extension can omit candidates that need no decision. Every returned decision must match a candidate by its exact name and version.

Extension resultReport modeEnforcement mode
allowContinueContinue
warnPrint the decision and continuePrint the decision and continue
blockPrint the decision and continueStop the install

on-error controls program and protocol failures. It does not change package decisions.

on-error valueResult
warnPrint the error and continue the install
blockStop the install

The timeout includes the standard-input write, process exit, output reads, and response validation.

Warm lockfile and offline installs run the same policy extensions before linking. A warm install cannot bypass a local policy extension.

Active extensions delay registry tarball prefetch until their decisions pass. Direct remote tarball URL dependencies are not supported while an extension is active.

Diagnostics

lpm policy status and lpm policy doctor report these diagnostic codes:

CodeSeverityMeaning
policy_extensions_not_configuredpassNo active extension exists
policy_extensions_configuredpassOne or more active extensions exist
policy_extension_report_modewarnAn active extension cannot stop an install
policy_extension_command_unavailablefailThe configured program is missing or not executable
policy_extension_config_invalidfailLPM CLI cannot load the extension configuration

Only doctor converts a configuration-load error into policy_extension_config_invalid. status stops and returns the configuration error directly.

status reports failed diagnostics but returns exit code 0. If automation must fail for these diagnostics, use doctor.

Recovery

The configuration does not load

Run the doctor command to get the configuration error:

lpm policy doctor

Remove unknown keys and correct invalid value types in ~/.lpm/config.toml. Then run the command again.

The command is unavailable

Inspect the resolved command:

lpm policy list

Use an absolute executable path, or add the program to an absolute PATH directory. On Unix, make sure that the file is executable.

The extension returns invalid JSON

Run the extension with one synthetic candidate:

lpm policy test local-feed --package react@19.0.0

Make sure that standard output contains only one valid response document. Send logs to standard error.

The response must use schema version 1 and contain only known fields. Read the complete response schema.

A remote tarball dependency is rejected

Replace the remote tarball URL with a supported package source. Active extensions cannot evaluate that URL before download.

JSON output

Use JSON output for automation:

lpm policy list --json
lpm policy status --json
lpm policy doctor --json
lpm policy test local-feed --package react@19.0.0 --json

List output

{
  "success": true,
  "enabled_count": 1,
  "extensions": [
    {
      "name": "local-feed",
      "command": ["/usr/local/bin/lpm-policy-feed", "--deny-list", "/etc/lpm/deny.json"],
      "mode": "enforce",
      "on_error": "block",
      "timeout_ms": 5000,
      "events": ["package.candidate"]
    }
  ]
}

Status and doctor output

status --json and doctor --json use the same response shape:

{
  "success": true,
  "enabled": true,
  "enabled_count": 1,
  "enforce_count": 1,
  "report_count": 0,
  "no_failures": true,
  "has_warnings": false,
  "diagnostics": [
    {
      "code": "policy_extensions_configured",
      "severity": "pass",
      "detail": "1 enabled (1 enforce, 0 report)"
    }
  ]
}

For doctor, success: true means that the command produced a diagnostic response. Use no_failures and the process exit code for policy health.

Test output

{
  "success": true,
  "extension": "local-feed",
  "event": "package.candidate",
  "package": {
    "name": "react",
    "version": "19.0.0"
  },
  "duration_ms": 12,
  "allow_count": 0,
  "warn_count": 1,
  "block_count": 0,
  "decisions": [
    {
      "name": "react",
      "version": "19.0.0",
      "action": "warn",
      "code": "local-feed",
      "reason": "review required"
    }
  ]
}

Install JSON

Successful lpm install --json output includes counters at security.policy_extensions.

Add --timing to include the same counters at timing.policy_extensions:

lpm install --json --timing
{
  "enabled": true,
  "configured_count": 1,
  "ran_count": 1,
  "candidate_count": 42,
  "duration_ms": 24,
  "allow_count": 40,
  "warn_count": 2,
  "block_count": 0,
  "error_count": 0,
  "extensions": [
    {
      "name": "local-feed",
      "mode": "enforce",
      "on_error": "block",
      "duration_ms": 24,
      "allow_count": 40,
      "warn_count": 2,
      "block_count": 0
    }
  ]
}

Flags

CommandArgument or flagPurpose
doctor[EXTENSION]Diagnose only one named active extension
test<EXTENSION>Select one named active extension
test--package <NAME@VERSION>Send one exact package candidate to the extension
All--jsonPrint structured JSON

list and status have no command-specific flags. See the other global flags.

See also