LPM-cli

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, and SRI integrity hash, so the next install — on any machine, with any cache state — produces a byte-identical node_modules/.

The companion lpm.lockb binary lockfile holds the same data in a mmap-friendly format for zero-parse warm starts. Both files should be committed.

File location

<project-root>/lpm.lock

Top-level shape

lpm.lock
[metadata]
lockfile-version = 2
resolved-with = "greedy-fusion"
# Only present when the default linker auto-switched to isolated
# after detecting incompatible peer requirements.
auto-isolated-peer-conflicts = true

[[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"

[[packages]]
name = "scheduler"
version = "0.25.0"
source = "registry+https://registry.npmjs.org"
integrity = "sha512-..."

# 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]

FieldTypeNotes
lockfile-versionu32Schema version. Current: 2 (bumped from 1 when the per-entry tarball hint + peers array landed). Schema-versioned (not tool-versioned) so older clients can refuse cleanly when the schema bumps.
resolved-withstringWhich resolver produced this file (e.g. "greedy-fusion", "pubgrub"). Informational.
auto-isolated-peer-conflictsboolPresent 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.

[[packages]]

One entry per resolved package. Sorted by name for deterministic diffs and minimal merge conflicts.

FieldTypeNotes
namestringPackage name (e.g. react, @lpm.dev/owner.pkg)
versionstringExact resolved version
sourcestring | absentSource registry, e.g. registry+https://registry.npmjs.org. Absent for packages without a known source.
integritystring | absentSRI integrity hash (sha512-…). Populated when the registry provides it. Always trusted on subsequent installs.
dependenciesstring[]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.
peersstring[]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).
tarballstring | absentTarball 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.

[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 name
  • dependencies arrays inside each package are sorted
  • Optional fields are omitted when empty / None rather 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