lpm.lock format
TOML lockfile schema — what every field means and why it's there.
lpm.lock is the human-readable, git-diffable lockfile written next to package.json after every lpm install. It pins every transitive dependency by exact version, source registry, SRI integrity hash, platform metadata, registry-signature evidence, and any cryptographically verified provenance evidence, so the next install can reproduce the same graph without trusting ambient registry state.
The companion lpm.lockb binary lockfile is generated when the current binary wire format can represent the graph. lpm.lock is always authoritative; commit lpm.lockb when LPM CLI writes it.
File location
<project-root>/lpm.lock
Top-level shape
[metadata]
lockfile-version = 7
resolved-with = "greedy-fusion"
# Only present when the default linker auto-switched to isolated
# after detecting incompatible peer requirements.
auto-isolated-peer-conflicts = true
[importers.".".dependencies]
react = "^19.0.0"
[importers.".".dev-dependencies]
vite = "^6.0.0"
[patches."left-pad@1.3.0"]
path = "patches/left-pad@1.3.0.patch"
sha256 = "sha256-..."
original-integrity = "sha512-..."
[provenance."react@19.0.0#npm-..."]
subject-name = "pkg:npm/react@19.0.0"
subject-sha512 = "..."
integrated-time-secs = 1740000000
log-id = "..."
log-index = 123456
bundle-sha256 = "sha256-..."
[provenance."react@19.0.0#npm-...".snapshot]
present = true
publisher = "github:facebook/react"
workflowPath = ".github/workflows/publish.yml"
workflowRef = "refs/tags/v19.0.0"
attestation_cert_sha256 = "sha256-..."
[[packages]]
name = "react"
version = "19.0.0"
source = "registry+https://registry.npmjs.org"
integrity = "sha512-..."
dependencies = ["scheduler@0.25.0"]
tarball = "https://registry.npmjs.org/react/-/react-19.0.0.tgz"
registry-published-at = "2025-01-01T00:00:00.000Z"
node-engine = ">=18"
[[packages.registry-signatures]]
keyid = "SHA256:..."
sig = "MEUCIQ..."
[[packages]]
name = "scheduler"
version = "0.25.0"
source = "registry+https://registry.npmjs.org"
integrity = "sha512-..."
os = ["darwin", "linux"]
cpu = ["arm64", "x64"]
libc = ["glibc"]
optional = true
# Only present when at least one root dep uses npm:<target>@<range>
[root-aliases]
my-react-alias = "react"
# Only present when the eager peer-drain pass synthesized installs to
# satisfy unmet peerDependencies (default-on under autoInstallPeers).
ambient-peer-installs = ["loose-envify"][metadata]
| Field | Type | Notes |
|---|---|---|
lockfile-version | u32 | Schema version. Current: 7. Schema-versioned (not tool-versioned) so older clients can refuse cleanly when the schema bumps. Version 2 made peer state authoritative; version 3 added platform + optional metadata; version 4 added registry signature metadata; version 5 added importer snapshots and patch checksum records; version 6 added per-package engines.node; version 7 added artifact-bound Sigstore provenance evidence. |
resolved-with | string | Which resolver produced this file (e.g. "greedy-fusion", "pubgrub"). Informational. |
auto-isolated-peer-conflicts | bool | Present only when a default-hoisted install detected incompatible peer requirements and auto-switched the project to isolated layout. Warm installs read this before the resolver runs so the install hash and linker stay on the peer-preserving layout. Explicit linker config ignores this flag. |
[importers]
[importers.".".dependencies]
react = "^19.0.0"
[importers.".".dev-dependencies]
vite = "^6.0.0"
[importers.".".lpm-overrides]
"left-pad" = "1.3.0"
[importers.".".catalogs.default]
react = "^19.0.0"
[importers."."]
patches-fingerprint = "sha256-..."
peer-dependency-rules-fingerprint = "sha256-..."
auto-install-peers = trueImporter snapshots record the manifest inputs that must match before a frozen install can replay the lockfile. LPM CLI currently writes the root importer as ".".
| Field | Type | Notes |
|---|---|---|
dependencies | table | package.json > dependencies exactly as declared. |
dev-dependencies | table | package.json > devDependencies exactly as declared. |
optional-dependencies | table | package.json > optionalDependencies exactly as declared. |
peer-dependencies | table | package.json > peerDependencies exactly as declared. |
lpm-overrides | table | package.json > lpm.overrides after catalog references are resolved. |
overrides | table | npm-style package.json > overrides after catalog references are resolved. |
resolutions | table | yarn-style package.json > resolutions after catalog references are resolved. |
catalogs | nested table | Catalog ranges visible to this importer. |
patches-fingerprint | string | Hash of lpm.patchedDependencies; absent when no patches are declared. |
peer-dependency-rules-fingerprint | string | Hash of lpm.peerDependencyRules; absent when default/empty. |
auto-install-peers | bool | Effective autoInstallPeers value captured for frozen replay. |
[patches]
[patches."left-pad@1.3.0"]
path = "patches/left-pad@1.3.0.patch"
sha256 = "sha256-..."
original-integrity = "sha512-..."Patch records bind package.json > lpm.patchedDependencies to the exact patch file bytes LPM CLI is allowed to replay. The table key is the exact patched package selector (<name>@<version>).
| Field | Type | Notes |
|---|---|---|
path | string | Patch file path relative to the project root. |
sha256 | string | SHA-256 digest of the patch file contents. Install hard-errors if the current file hash differs from this value. |
original-integrity | string | SRI integrity of the pristine package bytes the patch was authored against. Install also verifies this before applying the patch. |
[patches] is TOML-only metadata. Projects with patch records skip lpm.lockb and remove any stale binary companion.
[[packages]]
One entry per resolved package. Sorted by name for deterministic diffs and minimal merge conflicts.
| Field | Type | Notes |
|---|---|---|
name | string | Package name (e.g. react, @lpm.dev/owner.pkg) |
version | string | Exact resolved version |
source | string | absent | Source registry, e.g. registry+https://registry.npmjs.org. Absent for packages without a known source. |
integrity | string | absent | SRI integrity hash (sha512-…). Populated when the registry provides it. Always trusted on subsequent installs. |
registry-signatures | table[] | npm-compatible dist.signatures entries persisted as nested [[packages.registry-signatures]] tables. Skipped when absent. |
registry-published-at | string | absent | Publish timestamp used to verify npm registry signing-key expiry without rehydrating package metadata. |
os | string[] | package.json > os restrictions for this version. Skipped when empty. |
cpu | string[] | package.json > cpu restrictions for this version. Skipped when empty. |
libc | string[] | package.json > libc restrictions for this version. Skipped when empty. |
node-engine | string | absent | The package's package.json > engines.node constraint. Warm and frozen installs revalidate it against the effective Node.js version and engine-strict setting. |
optional | bool | Present as true when the package is reachable only through optional dependency edges. Used with platform filtering so warm installs can skip incompatible optional packages and fail incompatible required packages. |
dependencies | string[] | Direct deps as <local_name>@<version> entries. Skipped from output when empty. |
alias-dependencies | [string, string][] | npm-alias edges as [local_name, target_canonical_name] pairs. Only present when the package uses npm:<target>@<range> aliases. |
peers | string[] | Resolved peer dependencies as <peer_name>@<resolved_version> entries (same format as dependencies). Each entry is one of the package's declared peerDependencies intersected with the install set's resolved versions. Load-bearing for warm-install correctness — the v2 store's link-entry identity hashes peer pinning, so dropping these would let the warm install compute a different graph key than the cold install. Sorted by peer name; skipped from output when empty (the common case). |
tarball | string | absent | Tarball URL hint cached from resolve time. Speeds up the warm-install fast path by letting it skip the per-package metadata round-trip. Only valid for Source::Registry packages — pairing this hint with a non-Registry source is rejected at parse time. Absent on lockfiles produced before this hint shipped. |
dependencies format
dependencies = ["scheduler@0.25.0", "loose-envify@1.4.0"]<local_name>@<version> — the local-name is what the depending package writes in its own dependencies map. For non-aliased deps the local name equals the canonical registry name. For npm-alias deps (npm:react@^19.0.0 masquerading as react-canary), the local name diverges from the target — see alias-dependencies below.
alias-dependencies format
alias-dependencies = [["my-react", "react"]]Each [local_name, target_canonical_name] records that the package depends on my-react@<version> (in dependencies) but the actual target is react. Used to compute the right .lpm/<target>@<version>/ store path on link.
tarball field
tarball = "https://registry.npmjs.org/react/-/react-19.0.0.tgz"A dist-URL hint cache for Source::Registry packages. Lets the warm-install fast path skip the per-package metadata round-trip. For non-Registry sources (Source::Tarball, Source::Git), the URL is part of source identity (lives inside the source variant) — pairing them with this field is rejected at parse time.
Platform fields
os = ["darwin", "linux"]
cpu = ["arm64", "x64"]
libc = ["glibc"]
optional = trueLPM CLI records os, cpu, and libc restrictions from the package version's manifest. Empty arrays are omitted. Warm installs replay the same host filtering as fresh installs: incompatible optional packages are skipped; incompatible required packages fail loudly.
The optional = true bit means this package is only reachable through optional dependency edges. It is what lets the lockfile fast path distinguish "safe to skip on this host" from "required package cannot run here."
Registry signatures
registry-published-at = "2025-01-01T00:00:00.000Z"
[[packages.registry-signatures]]
keyid = "SHA256:..."
sig = "MEUCIQ..."When npm metadata includes dist.signatures, LPM CLI persists the signature payloads and the version publish timestamp. Install-time signature verification can then run from the lockfile fast path without fetching the full package metadata again. The verifier still checks the package integrity hash and registry signing keys; the lockfile stores evidence, not a cached pass/fail verdict.
[provenance]
[provenance."axios@1.14.0#npm-036b43b16792c643"]
subject-name = "pkg:npm/axios@1.14.0"
subject-sha512 = "dd8f32ae..."
integrated-time-secs = 1774638099
log-id = "wNI9atQG..."
log-index = 1189268120
bundle-sha256 = "sha256-75564031..."
[provenance."axios@1.14.0#npm-036b43b16792c643".snapshot]
present = true
publisher = "github:axios/axios"
workflowPath = ".github/workflows/publish.yml"
workflowRef = "refs/tags/v1.14.0"
attestation_cert_sha256 = "sha256-9a9a6a..."The table key is <name>@<version>#<source-id>, so evidence is attached to one exact package version from one exact source. Only successfully verified evidence is written.
| Field | Type | Notes |
|---|---|---|
subject-name | string | Exact npm package URL signed by the attestation, pkg:npm/<name>@<version>. |
subject-sha512 | string | Lowercase hexadecimal SHA-512 digest of the signed tarball subject. It must match the package entry's SHA-512 SRI integrity. |
integrated-time-secs | u64 | Rekor integrated timestamp from the verified transparency-log entry. |
log-id | string | Identity of the transparency log that recorded the attestation. |
log-index | i64 | Verified transparency-log index. |
bundle-sha256 | string | SHA-256 digest of the original Sigstore bundle bytes. |
snapshot.present | bool | Must be true; absent or unverified provenance is never locked as evidence. |
snapshot.publisher | string | absent | Publisher identity extracted from the verified certificate, such as github:axios/axios. |
snapshot.workflowPath | string | absent | Trusted publishing workflow path. |
snapshot.workflowRef | string | absent | Git ref used for this publication. |
snapshot.attestation_cert_sha256 | string | absent | SHA-256 digest of the verified leaf certificate. |
The reader rejects orphaned evidence, empty required fields, malformed digests, non-npm subjects, subject name/version mismatches, source mismatches, and subject digests that do not match the locked package integrity. A frozen install can therefore replay internally consistent evidence without fetching the attestation again.
lpm.lock is trusted project input. It stores derived evidence, not the original Sigstore bundle, so frozen replay does not rerun the certificate-chain, transparency-log, or signed-statement verification and cannot independently detect a sophisticated edit that changes all related derived fields consistently. Protect and review lockfile changes like source code. Online attestation-cache hits do rerun verification because that cache stores the original bundle bytes.
[root-aliases]
[root-aliases]
my-alias = "actual-package"Maps root-level npm-alias edges so warm installs reproduce the original node_modules/<local>/ layout without re-resolving. Empty for projects without root-level aliases — and omitted entirely from output in that case (backwards-compatible with older lockfiles that pre-date alias support).
ambient-peer-installs
ambient-peer-installs = ["loose-envify", "scheduler"]Canonical names the resolver auto-installed at root scope to satisfy unmet peerDependencies (the eager peer-drain pass, on by default via autoInstallPeers = true). These packages are already in [[packages]] (they were resolved and extracted like any other dep) — this list carries the orthogonal signal "surface them at node_modules/<peer>/ even though they aren't in pkg.dependencies." Symmetric with [root-aliases] — both are project-side install-orchestration metadata, not per-package state.
Empty and omitted from output on the common no-auto-install path (project's dependencies block already covers every declared peer). See Resolver for the auto-install contract + toggle.
Determinism
The on-disk file is deterministic by construction:
[[packages]]entries are sorted by namedependenciesarrays inside each package are sorted[provenance]entries are sorted by their full package/source key- Optional fields are omitted when empty /
Nonerather than written as null
This makes git diff lpm.lock actionable: a new dep adds entries, a version bump rewrites a single package's fields, and you can spot supply-chain surprises without parsing.
Schema versioning
lockfile-version is schema-versioned, not tool-versioned. A version bump only happens when the on-disk shape changes. Older clients reading a lockfile with a higher lockfile-version should refuse with a clear error rather than silently misinterpreting fields.
See also
lpm.lockbformat — companion binary lockfile- Lockfile concept — design overview, when each file is read
lpm install --offline— installs entirely from the lockfilelpm install --strict-integrity— disallow trust-on-first-use for tarball-URL deps