A lockfile is a generated file that records the exact resolved version, source, and integrity hash of every dependency in a project, including transitive ones, so that repeated installs produce an identical dependency tree. While a manifest like package.json or pyproject.toml declares flexible version ranges (for example, "any 4.x release"), a lockfile captures the specific versions your package manager actually chose the last time it resolved those ranges. Common examples include package-lock.json and yarn.lock for JavaScript, poetry.lock and Pipfile.lock for Python, Cargo.lock for Rust, Gemfile.lock for Ruby, and composer.lock for PHP. The lockfile is what turns "install the newest compatible versions" into "install exactly these versions, verified by hash," which is the foundation of reproducible and auditable builds.
Why lockfiles matter for supply chain security
Without a lockfile, two installs of the same project on different days can produce different code, because a flexible range like "^1.2.0" will happily pick up a newer patch or minor release the moment one is published. That non-determinism, sometimes called dependency drift, is a security problem: a build you tested and approved may not be the build that ships, and a freshly published version could introduce a regression or malicious change you never reviewed.
A lockfile closes that gap by freezing resolution to specific versions and recording an integrity hash for each artifact. On the next install, the package manager verifies that what it downloads matches the recorded hash, so tampering in transit or a surprise version substitution is detected rather than silently accepted. This is why committing the lockfile to source control and enforcing it in continuous integration is a baseline control in software supply chain security: it ensures the dependency tree you audited is the exact tree your build produces.
How a lockfile works
When you first install dependencies, the package manager reads the manifest's version ranges, resolves them against the registry, walks the full transitive graph, and picks a concrete version for every package. It then writes those decisions into the lockfile: the resolved version, the registry URL or source, and a cryptographic integrity hash for each package.
On subsequent installs, the behavior depends on the command. A normal install typically respects the lockfile and only re-resolves when the manifest changes. A strict, CI-oriented command (such as npm ci, yarn install --frozen-lockfile, or poetry install against an existing lock) refuses to modify the lockfile at all and fails if the manifest and lockfile disagree, guaranteeing the build uses exactly the recorded versions. Updating a dependency is a deliberate act: you change the manifest or run an update command, the package manager re-resolves, and the lockfile changes, which shows up as a reviewable diff in your pull request.
Key points at a glance
| Aspect | Manifest | Lockfile |
|---|---|---|
| Version format | Flexible ranges | Exact pinned versions |
| Scope | Direct dependencies | Direct plus all transitive |
| Written by | Humans | The package manager |
| Integrity hashes | No | Yes, per artifact |
| Purpose | Declare intent | Reproduce the exact tree |
| In source control | Always commit | Always commit for apps |
How Safeguard uses lockfiles
Lockfiles are one of the highest-fidelity inputs Safeguard consumes, because they name the exact versions your build actually uses rather than the loose ranges a manifest declares. Our software composition analysis parses the lockfile to build a precise, complete dependency graph, including deeply nested transitive packages, and matches each pinned version against known vulnerabilities so a finding reflects what you truly ship.
SBOM Studio uses that resolved tree to generate a Software Bill of Materials tied to the exact hashes in your lockfile, giving you an auditable record for compliance and incident response. When Safeguard identifies a vulnerable pinned version, Griffin AI proposes the minimal lockfile change needed to reach a safe, compatible version and opens it as a pull request, so the fix arrives as a reviewable diff with its blast radius already assessed rather than an open-ended "run update and hope."
Frequently Asked Questions
Should I commit my lockfile to version control? Yes, for applications. Committing the lockfile ensures every developer and every CI run installs the identical dependency tree, which makes builds reproducible and auditable. Libraries are a nuanced case, since a published library resolves against the consumer's own tree, but for deployable applications a committed lockfile is a baseline supply chain control.
What is the difference between a lockfile and a manifest? A manifest states intent using flexible version ranges and is written by developers. A lockfile records the exact resolved outcome, including every transitive dependency and its integrity hash, and is generated by the package manager. The manifest says "roughly this," while the lockfile says "precisely these, verified."
Does a lockfile prevent malicious packages? Not by itself. A lockfile guarantees you install the exact version you pinned, which stops surprise substitutions and drift, but it cannot tell you whether that pinned version is safe. If a malicious version is what you pinned, the lockfile faithfully reproduces it. Lockfiles must be paired with vulnerability scanning and provenance checks.
Why does my lockfile change when I did not touch dependencies? Common causes include a different package manager version normalizing the file, a transitive dependency being re-resolved, or a teammate running an update. Unexpected lockfile churn is worth reviewing, because a diff you did not intend can indicate an accidental upgrade or, rarely, tampering. Treat lockfile diffs as security-relevant in code review.
Ready to turn your lockfiles into a precise map of real risk? Create a free account at app.safeguard.sh/register and start scanning, then keep learning with the free Safeguard Academy.