A lockfile changed. See which dependency trees changed with it.
Try the live demo — no login required · Real upgrade examples · Download v0.2.0 · GitHub
简体中文 · Model and boundaries · Product and launch plan · Contributing
Lockray compares two npm package-lock.json files and turns a wall of JSON into a review workspace. Group changes by the direct dependencies that can reach them, select a package, and inspect its shortest dependency path before and after the change.
It runs locally in your browser or terminal. No account, API key, registry lookup, or package installation is needed to analyze a lockfile. Lockray does not execute the packages described by your input.
New in v0.2.0: load four real upstream dependency updates from Axios, Marked, and PDF.js, with pinned file hashes and reproducible offline checks. See what changed.
The screenshot uses Axios’s public historical dependency refresh: 187 changed installed locations, filtered to the 48 changes reachable from @commitlint/cli. Counts describe installation locations, including nested copies of a package.
A direct dependency bump can touch many transitive entries. A plain diff tells you the fields changed; Lockray helps you navigate their recorded relationships:
Before: project → [email protected] → [email protected]
After: project → [email protected] → [email protected]
- Find your starting point. Group changed installed locations under the direct dependencies that can reach them in either lockfile.
- Follow the evidence. Inspect a shortest path from the root, the package's installation location, and its recorded before/after fields.
- Separate version changes from metadata. Review additions, removals, version changes, and changes to source, integrity, install-script flags, or dependency metadata.
- Keep declarations distinct. Range and dependency-category changes at the root are reported separately, even when installed versions stay the same.
- Notice review signals. Source-host or protocol changes, newly recorded install scripts, and integrity changes deserve context. Signals are prompts for human review; they do not establish a vulnerability or malicious package.
- Take the review to a PR. Export a Markdown report or structured JSON using the browser or CLI.
- Keep inputs local. Analysis uses no upload endpoint, analytics, remote fonts, or registry service. Browser inputs live in tab memory.
This is a locked-tree reachability tool. A dependency path explains how entries are connected in the supplied snapshots. It does not prove why npm's resolver selected a version, that code uses a dependency, or that a change caused a regression.
The current source includes a Real upgrade cases picker. Select a case to load both complete historical lockfiles from the same static host. The browser checks their bytes against the pinned SHA-256 values before analyzing them. Nothing is installed and no upstream dependency code runs.
| Historical update | Installed locations | Changes |
|---|---|---|
| Axios development dependency refresh | 687 → 687 | 29 version changes |
| Axios dependency-tree refresh | 715 → 707 | 187: 29 added, 37 removed, 116 version, 5 metadata |
| Marked npm upgrade | 660 → 660 | 24 version changes |
| PDF.js eslint-plugin-jsdoc major upgrade | 965 → 965 | 8 version changes |
These are unchanged snapshots from actual upstream commits and their first parents, rather than locally generated updates. Each pair has source links, byte lengths, hashes, and full upstream license notices. Explore the evidence and offline checks, or inspect the fixture manifest.
The Marked case exposed a noisy review signal: nine bundled packages already lacked individual integrity values. v0.2 reports that limitation once as a coverage note instead of nine missing-integrity signals. A previously recorded checksum that disappears still generates a signal. This does not certify the packages or their parent artifact as safe.
The versioned release page includes a source ZIP for local development and the CLI, a static-site ZIP for HTTP hosting, and SHA256SUMS.txt. Download both ZIPs and the checksum file into the same directory, then verify before extracting:
# macOS
shasum -a 256 -c SHA256SUMS.txt
# Linux
sha256sum -c SHA256SUMS.txtBoth archives should report OK. Extract the source ZIP and open lockray-0.2.0/ to run the commands below. The site ZIP contains the prebuilt app and must be served over HTTP or HTTPS; opening it directly with file:// is not supported.
Requires Node.js 22 or newer. From this repository's directory:
npm startOpen the loopback URL printed in your terminal. No npm install step is required. Load the synthetic example, choose a real historical case, or load a before and after package-lock.json.
- Compare the two snapshots and inspect the overall change counts.
- Choose a direct dependency to narrow the view to changes reachable from it.
- Select a changed package to compare its fields and shortest before/after paths.
- Review any signals in context, then download a Markdown or JSON report.
The browser app is static. It can be built for any static HTTP host:
npm test
npm run test:real
npm run build
npm run package:releasenpm run test:real checks the bundled real snapshots, their exact change sets, native Node lookup paths, and root ownership without contacting a registry. The static build is written to dist/. Release packaging requires Git and an existing commit: it builds reproducible source/site ZIPs from committed HEAD, excludes uncommitted changes, and writes them with SHA256SUMS.txt to releases/v<version>/. Use npm run package:release -- --force to replace existing release files. Module loading requires HTTP or HTTPS; opening index.html directly through file:// is not supported.
Keep both lockfile revisions as local files, then compare them:
# Try the included synthetic pair.
node bin/lockray.js samples/before.package-lock.json samples/after.package-lock.json
# Review a real historical dependency-tree change in the v0.2 source.
node bin/lockray.js samples/real/axios-tree/before.package-lock.json \
samples/real/axios-tree/after.package-lock.json
node bin/lockray.js before-package-lock.json after-package-lock.json
node bin/lockray.js before-package-lock.json after-package-lock.json \
--format markdown --output dependency-review.md
node bin/lockray.js before-package-lock.json after-package-lock.json \
--format json --output dependency-review.json
node bin/lockray.js --help| Option | Purpose |
|---|---|
--format markdown|json |
Choose the report format; the default is Markdown |
--output <path> |
Write a report instead of printing it to stdout |
--force |
Permit replacing an existing report file |
--fail-on-signals |
Return a nonzero exit code when the comparison contains review signals |
--help |
Show the available options |
--version |
Print the version |
The CLI reads the two files and never fetches tarballs or invokes npm. Existing output files require --force; source inputs cannot be used as output destinations, including symlink and hardlink aliases. Exit codes are 0 for success, 1 for review signals when --fail-on-signals is supplied, and 2 for input or I/O errors. Review the complete output before using signals as a CI policy: legitimate dependency updates can produce them.
Lockray is not currently distributed through npm. Use the repository commands above.
The public source is on GitHub. The initial release passed the core/CLI suite and static build on Node.js 22 and 24 in GitHub Actions. Current validation evidence and its limits are recorded in the validation record.
The live browser app is deployed with GitHub Pages. The v0.2.0 source passed 48 local tests and the real examples were exercised in Chromium. Versioned source/site archives are built from the committed release tag. Current remote CI, deployment, and release evidence is maintained in the validation record.
| Input or question | Current scope |
|---|---|
| npm lockfile versions | lockfileVersion: 2 or 3, with a packages map; a root entry enables direct-dependency explanations |
| Package identity for comparison | Exact installed location, such as node_modules/a/node_modules/b |
| Dependency attribution | Recorded dependency reachability from root declarations |
| Path explanation | A shortest supported dependency path in each snapshot |
| Source and script review | Lockfile metadata only; no downloaded artifact or script inspection |
| Other lockfile formats | npm v1, Yarn, pnpm, Bun, and other ecosystems are not supported |
Several direct dependencies can reach the same changed location. Their groups overlap, so group counts must not be summed as a project-wide total. A path is one shortest explanation from any root, not every possible route; it may start at a different root than the selected group. Package locations that move are reported as an addition and a removal; Lockray does not infer a relocation.
Each input is limited to 10 MiB, with up to 20,000 non-root package entries and 100,000 declared dependency edges. All-root attribution has a 100,000-relationship work budget per snapshot; if reached, root groups may be incomplete. Path output has a separate 50,000-segment or 4,000,000-character budget across the report; paths beyond it are explicitly marked omitted, which does not mean the package is unreachable. Missing root metadata permits a location diff but produces a warning and cannot provide root paths. Coverage notes identify these limits.
The analysis is a bounded model of lockfile metadata, not npm Arborist or a full package installation. Workspaces, links, aliases, optional dependencies, and peer relationships have additional interpretation limits. An absent path can mean a relationship is outside the supported model. See the model document for the supported edges, limits, and unresolved cases.
Lockray does not scan advisories, inspect tarball contents, verify a registry's ownership, estimate bundle size, or determine runtime reachability. A hasInstallScript flag records script presence, not what that script does. An integrity field records a declared digest; Lockray does not fetch bytes to validate it. The npm registry hostname can be a placeholder for the configured registry.
Inputs and derived graphs remain in browser memory until you clear or reload the page. The app has no analysis backend or telemetry. Serving the page still makes ordinary requests for app files and built-in examples; your hosting provider and browser extensions have their own behavior.
Reports are not anonymized. Package names, locations, version strings, registry URLs, and other recorded metadata can identify a private project. Exports can include source URLs containing credentials if those were present in an input. Check a report before sharing it, and use synthetic fixtures in public issues. See SECURITY.md.
| Tool | Focus | Lockray's focus alongside it |
|---|---|---|
| LockSignal | Local npm lockfile comparison with review signals, evidence, browser and CLI reports | Reachability groups rooted at direct dependencies and shortest before/after paths |
| lockdiff | CLI comparison of npm installed locations and review metadata | An interactive browser workspace for exploring the relationships behind the diff |
| npm-diff | Formatted lockfile summaries in GitHub Actions | A local two-file review without connecting a repository or running an Action |
npm explain |
Explain packages in a project's installed dependency tree | Compare two saved lockfile snapshots without installing either tree |
These projects are independent. Lockray does not bundle their implementations and does not claim to replace a vulnerability scanner or a package manager.
Useful contributions include minimal lockfile fixtures, corrections to path resolution backed by npm behavior, clearer review explanations, accessibility improvements, and reports from real dependency updates with private data removed. Start with CONTRIBUTING.md.
MIT © 2026 Songxin Qu. See LICENSE. Third-party historical snapshots retain their full upstream licenses and attribution.
