lpm.lockb format
Binary lockfile companion to lpm.lock — mmap-friendly, zero-parse reads.
lpm.lockb is a binary mirror of lpm.lock optimized for reads. The install pipeline mmaps it, binary-searches the entry table by package name, and reads UTF-8 strings out of a packed string table — no parser, no allocation per package. On a typical project this is ~0.1 ms vs ~10 ms to parse the TOML.
The binary lockfile is always written alongside the TOML file. On read, the binary is preferred when it exists and is at least as new as the TOML.
Both files should be committed. Mark lpm.lockb as binary in .gitattributes — lpm init does this automatically:
lpm.lockb binaryFile location
<project-root>/lpm.lockb
Layout (v2)
[Header: 16 bytes]
magic [u8; 4] = b"LPMB"
version u32 LE = 2
package_count u32 LE
string_table_off u32 LE — byte offset where the string table starts
[PackageEntry × N: 36 bytes each, sorted by name]
name_off u32 LE — offset into the string table
name_len u16 LE
version_off u32 LE
version_len u16 LE
source_off u32 LE — 0 = None
source_len u16 LE
integrity_off u32 LE — 0 = None
integrity_len u16 LE
deps_off u32 LE — offset into the deps table
deps_count u16 LE
tarball_off u32 LE — 0 = None (v2+)
tarball_len u16 LE (v2+)
[DepsEntry × total_deps: 6 bytes each]
str_off u32 LE — offset into the string table
str_len u16 LE
[String table]
packed UTF-8, no NUL terminatorsAll numeric fields are little-endian. Packages are sorted by name so reads can binary-search the entry table.
Wire-format version
Current: v2 (BINARY_VERSION). The 1 → 2 bump appended (tarball_off, tarball_len) to every PackageEntry.
The reader rejects any file whose header version is not exactly BINARY_VERSION — strict by design. Per-entry layout differs across versions, so a v2 reader interpreting v1 30-byte entries as 36-byte entries would slip a half-package every time and produce garbage. On rejection, read_fast falls back to parsing the TOML lockfile and the next write_all rewrites lpm.lockb as the current version, completing the migration transparently.
Sentinel for optional fields
Optional string fields (source, integrity, tarball) use (off=0, len=0) to mean None.
To keep this sentinel unambiguous, the writer rejects empty strings at insert time: an empty source URL, integrity hash, or tarball URL is nonsensical input regardless. Failing loud is correct.
What's NOT stored in the binary lockfile
The binary format is intentionally smaller than the TOML format. A few rare fields are not represented:
alias-dependencies— npm-alias edgesroot-aliases— root-level npm-alias map
Lockfiles that use any alias edges are written as TOML-only — the binary file is skipped entirely. Reads fall through to the TOML lockfile in that case. This is an explicit safety check (binary_format_supports) — silently dropping alias info would produce a binary lockfile that disagrees with the TOML lockfile and a warm install that materializes node_modules/<target>/ instead of node_modules/<local>/.
If your project uses npm:<target>@<range> aliases, you'll see only lpm.lock (no lpm.lockb) — that's correct.
Reading the file
The reader BinaryLockfileReader mmaps the file once, parses the header, validates the magic + version, and exposes by-name lookup via binary search over the entry table. No allocation per entry; string slices borrow directly from the mmap.
For projects with thousands of packages, this is the difference between a ~0 and a ~10–50 ms warm-start cost.
See also
lpm.lockformat — the canonical TOML lockfile- Lockfile concept — design overview
lpm install— what reads each file