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 statusThe output shows the number of active extensions, their modes, and any command-availability errors.
Choose a command
| Command | Purpose | Use it when |
|---|---|---|
lpm policy list | Show active extension configuration | You want names, commands, modes, error behavior, and timeouts |
lpm policy status | Show a policy overview and diagnostics | You want a read-only summary that always exits successfully after valid configuration loads |
lpm policy doctor [EXTENSION] | Diagnose all extensions or one named extension | You 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 candidate | You 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 listThen review the policy posture and command availability:
lpm policy statusDisabled extensions do not appear in either command.
Diagnose one extension
Run the diagnostic for one named extension:
lpm policy doctor local-feedIf 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 doctordoctor 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.0For a scoped package, LPM CLI uses the final @ as the version separator:
lpm policy test local-feed --package @scope/pkg@1.2.3Use 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:
[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 installReport mode prints warning and block decisions but does not stop the install.
After you review the results, change the extension to enforcement mode:
[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:
[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"]| Key | Accepted value | Default | Purpose |
|---|---|---|---|
command | Non-empty string array | Required for an active extension | Select the program and its arguments |
enabled | Boolean | true | Include or disable the extension |
mode | report or enforce | report | Control the effect of package decisions |
on-error | warn or block | warn in report mode, block in enforcement mode | Control runner and protocol errors |
timeout-ms | Integer from 1 through 120000 | 5000 | Limit 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
PATHdirectory
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 result | Report mode | Enforcement mode |
|---|---|---|
allow | Continue | Continue |
warn | Print the decision and continue | Print the decision and continue |
block | Print the decision and continue | Stop the install |
on-error controls program and protocol failures. It does not change package decisions.
on-error value | Result |
|---|---|
warn | Print the error and continue the install |
block | Stop 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:
| Code | Severity | Meaning |
|---|---|---|
policy_extensions_not_configured | pass | No active extension exists |
policy_extensions_configured | pass | One or more active extensions exist |
policy_extension_report_mode | warn | An active extension cannot stop an install |
policy_extension_command_unavailable | fail | The configured program is missing or not executable |
policy_extension_config_invalid | fail | LPM 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 doctorRemove 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 listUse 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.0Make 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 --jsonList 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
| Command | Argument or flag | Purpose |
|---|---|---|
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 | --json | Print structured JSON |
list and status have no command-specific flags. See the other global flags.
See also
lpm install- learn where extensions run during install~/.lpm/config.toml- read the configuration and protocol referencelpm doctor- include policy diagnostics in a broader environment checklpm security- manage the separate signed security floor