Bun lockfile bun.lockb format: Threat model + CI checks [2026]

bun.lockb made Bun installs fast, but it also changed how you audit dependency drift. Here’s what’s inside, what integrity it gives you, and how to enforce it in CI.

Part of theDev Tools & AI Workflow series
Bun boy branding on orange paper and tissue box
Listen to this article
--:--

Bun Lockfile Internals: How `bun.lockb` Changes Your JavaScript Supply Chain Threat Model

Bun lockfile bun.lockb format is Bun’s older, binary representation of your fully-resolved dependency graph. It matters because lockfiles are the line between “we reviewed what we ship” and “we hope the registry gives us the same thing tomorrow.” Bun’s shift in v1.2 to a text lockfile (bun.lock) dragged an old tradeoff back into the open: speed and compactness vs. auditability when something goes sideways.

black flat screen computer monitor

I’m going to be blunt. Binary lockfiles are a security tax you pay later, with interest.

What is `bun.lockb`, and why was it binary?

bun.lockb is Bun’s legacy binary lockfile. Like every lockfile, it freezes the exact versions (including transitive dependencies) that Bun resolved at a point in time so the next install can reproduce that graph.

JavaScript code displayed on a dark screen with colorful syntax highlighting

Why binary? Because it’s fast to parse, smaller on disk, and easier for the package manager to treat as structured data instead of a big text blob. For a tool whose whole identity is “this should be faster,” that choice makes sense.

The catch is that format choices leak into team behavior:

  • A text lockfile is reviewable by default. You can read it, diff it, grep it.
  • A binary lockfile is opaque by default. Review turns into “trust the tool,” not “verify the change.”

So no, bun.lockb wasn’t just a performance detail. It quietly changed your team’s supply chain operating model.

Bun eventually conceded the point. When introducing Bun v1.2’s new lockfile, Ashcon Partovi called out the “Problems with bun.lockb” and moved the default to a text-based bun.lock.

If you can’t review a dependency change in a PR, you don’t have a dependency policy. You have a dependency vibe.

Problems with `bun.lockb` (in real security workflows)

Most write-ups stop at “it’s not diffable.” True. Also incomplete.

Lines of colorful JavaScript code displayed on a dark screen

The real problem is that bun.lockb breaks the stuff you rely on when you’re trying to keep a repo sane over years, across dozens of developers, under the occasional real-world incident.

1) PR review becomes useless for dependency drift

With a text lockfile, a reviewer can catch obvious weirdness in under a minute:

  • a new dependency name that screams typosquat
  • a jump from 1.2.3 to 9.0.0
  • a new Git URL dependency
  • an unexpected registry host

With bun.lockb, the diff is basically “binary changed.” People stop looking. Not because they’re lazy. Because the workflow trains them that looking is pointless.

And when a supply-chain incident happens, the question is never philosophical. It’s practical: what changed, when, and how did it get past review?

2) Incident response gets slower and more annoying

With a text lockfile, you can answer the usual questions quickly:

  • When did left-pad enter our dependency tree?
  • Which PR introduced [email protected]?
  • Did we ever ship the compromised version?

With a binary lockfile, you end up reconstructing history by re-running tooling on old commits. That’s survivable, but it’s slower, more fragile, and it breaks in exactly the situations where you’re already under pressure.

3) “Integrity” turns into a vibes-based claim unless you define it

Engineers love saying “lockfiles give integrity guarantees” and then moving on. Don’t.

A lockfile can help with:

  • Reproducibility: same inputs, same resolved versions.
  • Immutability checks: verifying a downloaded tarball matches an expected hash.

A lockfile does not automatically protect you from:

  • a malicious version that was legitimately published and then pinned
  • compromised maintainer accounts
  • compromised CI runners (your lockfile can be swapped)
  • dependency confusion (you pin the wrong thing faster)

If you want a clean model, tie lockfiles to provenance. The boring but correct direction here is: treat dependency resolution as part of your build provenance story. If your org wants a shared language for that, the SLSA framework is still one of the few places the industry isn’t completely hand-wavy.

Introducing `bun.lock`: what you gain when the lockfile is text

Bun v1.2 changed the default lockfile to a text-based bun.lock (replacing bun.lockb as the default). That’s a developer experience win, sure. But it’s also a security control.

Here’s what becomes possible when the file is readable:

  • Diffs become meaningful. Reviewers can see what packages were added/removed and which versions moved.
  • Policy checks become easy. CI can grep for banned patterns (Git URLs, untrusted registries, link: dependencies, whatever your org doesn’t allow).
  • Forensics get faster. You can use git blame and git log -p like a normal adult without special tooling.

Ashcon also claims Bun got faster after the switch. Good. “Fast” and “auditable” don’t have to be enemies. We just keep shipping tooling that treats auditability as someone else’s problem.

If you want a practitioner breakdown of the performance angle, this video is decent:

Integrity guarantees: what Bun lockfiles provide (and what they don’t)

Bun’s docs mostly describe behavior and flags, not a formal threat model. So here’s the version I’d put in an internal security doc: what you actually get, and what you absolutely do not.

What you get

  1. A pinned resolution of direct and transitive dependencies. If [email protected] depends on [email protected], that transitive pin is part of the lockfile.
  2. Deterministic installs when you enforce it. Using --frozen-lockfile in CI gives you “don’t touch the lockfile” semantics.
  3. A reviewable artifact (with `bun.lock`). This is the big one. Humans notice suspicious stuff scanners routinely miss.

What you do not get

  1. No protection against picking the wrong dependency. If someone adds reaact instead of react, the lockfile will faithfully pin the typo.
  2. No automatic protection against malicious-but-valid upstream publishes. Lockfiles freeze reality, including bad reality.
  3. No provenance guarantee. You still need controls around who can publish, how builds run, and what your CI runner can access.

If you want a complementary control, pair lockfile discipline with SBOMs and dependency graph monitoring. That’s the same reason I like writing down build and artifact hygiene in other ecosystems too, like in Rust reproducible builds + SBOM + signed artifacts.

Generate a lockfile without installing (and why this matters in CI)

Bun supports generating or updating the lockfile without touching node_modules.

Per the official Bun docs, to generate a lockfile without installing dependencies into node_modules, use --lockfile-only:

This sounds like a minor flag. It’s not.

In CI, it lets you run a “resolution check” job that:

  • runs fast
  • avoids executing install scripts
  • doesn’t mutate the workspace with a giant node_modules

There’s also a subtle behavior in the docs that matters for determinism: Bun always saves the lockfile to disk even if it is already up to date, except when --frozen-lockfile (or --production) is set.

That sentence is why I like a two-step posture:

  1. On developer machines: allow lockfile updates.
  2. In CI: prohibit lockfile updates and fail builds when drift exists.

If you care about supply-chain risk, the CI job that fails on lockfile drift is one of the highest-leverage controls you can add.

`--frozen-lockfile` in Bun: what should fail the build?

--frozen-lockfile is the flag you use when you want Bun to treat the lockfile as an input, not an output.

The contract you want in CI is simple:

  • If package.json changed but bun.lock didn’t, fail.
  • If the lockfile would be updated (even if it’s “no functional change”), fail.

That’s how you stop silent dependency drift from sneaking into main.

This maps cleanly to how I think about compliance automation too. When I worked on SOC 2 scaffolding at Rise People, the lesson was painfully consistent: controls that happen after the fact become theatre. Controls baked into scaffolding and CI become defaults people actually follow.

A concrete policy I like

  • PRs can change app code freely.
  • PRs that change dependency inputs (package.json) must change lockfile outputs (bun.lock) in the same PR.
  • CI blocks any “lockfile regenerated by CI” behavior.

If you already have decent review hygiene, this philosophy will feel familiar from my other writing on review automation defaults in 7 safer defaults for code review automation.

How to review dependency changes safely when the lockfile is binary

If you’re stuck on bun.lockb (older Bun, legacy repo, or just inertia), you still need an auditable workflow.

Here are the only approaches I’ve seen that don’t fall apart in practice.

Option A (best): migrate to `bun.lock`

Bun documents the migration path explicitly.

From the Bun docs “Text-based lockfile” section, migrate bun.lockbbun.lock with:

  1. Run bun install --save-text-lockfile --frozen-lockfile --lockfile-only
  2. Delete bun.lockb

Source: Bun docs

That command is doing three important things:

  • --save-text-lockfile: output the new format
  • --frozen-lockfile: don’t allow resolution changes during migration
  • --lockfile-only: avoid side effects in node_modules

Option B: normalize the dependency graph and diff that

Even with a binary lockfile, you can create a text artifact for review by snapshotting the resolved graph.

My rule is: diff the graph, not the file.

Practical ways to do this:

  • generate an SBOM and diff it across commits
  • dump a dependency list (direct + transitive) and diff it
  • store the resolved graph as a CI artifact for every merge to main

This is the same “make it observable” move I use when I build lightweight tooling on this site. Maintaining 25+ free browser tools at /tools taught me that the thing that gets adopted is the thing people can actually see and grep.

Option C: treat `bun.lockb` as an artifact, not a review surface

If you refuse to migrate, you can still improve your risk posture by:

  • requiring two-person review for any bun.lockb change
  • blocking lockfile changes from automation bots
  • requiring a separate “dependency snapshot” file in PRs

Is it clunky? Yes.

Is it better than “binary changed”? Also yes.

Automatic lockfile migration: supply-chain risk you didn’t ask for

Bun can automatically migrate other lockfiles when bun.lock is missing. Per the Bun docs, if there’s no bun.lock, Bun will migrate:

  • yarn.lock (v1)
  • package-lock.json (npm lockfileVersion 2, 3, or 4)
  • pnpm-lock.yaml

…and it preserves the original lockfile.

Source: Bun lockfile docs

This is great for developer experience. It can be terrible for security if you don’t control it.

Here’s the failure mode:

  1. A repo historically used npm/yarn/pnpm.
  2. Someone runs bun install without noticing.
  3. Bun migrates the lockfile.
  4. The lockfile format changes, and reviewers miss the graph changes.

Even if Bun’s migration is correct, you’re now reviewing a massive rewrite of your dependency manifest. That’s the exact moment typosquats and supply-chain surprises slip in.

My recommendation:

  • Make lockfile migration an explicit PR with a dedicated reviewer.
  • Generate a dependency graph snapshot before and after migration.
  • Block registry host changes unless explicitly approved.

If you want to go deeper on supply-chain attack patterns (dependency confusion, typosquatting), I’ve already written about the JavaScript ecosystem’s unique pain here: NPM supply chain attacks in 2026.

bun.lock vs bun.lockb vs npm/yarn/pnpm: what to commit, and why

You should commit Bun’s lockfile to git. Bun’s docs say this bluntly: “Should it be committed to git? Yes.” (Bun docs)

The more useful question is which lockfile you want to be the source of truth.

Here’s a comparison table I’d actually use in a team decision:

LockfileFormatDiff-friendly in PRsBest use caseSupply-chain review posture
`bun.lockb`BinaryNoLegacy Bun installs, speed-first workflowsWeak unless you add separate graph snapshots
`bun.lock`TextYesModern Bun (v1.2+)Strong. Easy to review and enforce in CI
`package-lock.json`JSON textSort of (noisy)npm-native reposOK. Reviewable but can be verbose
`yarn.lock` (v1)TextYesYarn classic reposStrong diff story; mature workflow
`pnpm-lock.yaml`YAML textYespnpm reposStrong diff story; strict by design

Two concrete numbers that matter here:

  • Bun’s text lockfile became the default starting in Bun v1.2.
  • Bun’s docs explicitly call out it currently migrates npm lockfileVersion 2/3/4, but not npm 6’s lockfileVersion 1.

Those little version footnotes are exactly the kind of thing that turns into a bad day if you miss it during a migration.

CI policy checks I’d add for Bun (today)

Actionable CI patterns, ordered by impact. No ceremony.

  1. Fail builds on lockfile drift
    • Run bun install --frozen-lockfile in CI.
    • If Bun tries to change the lockfile, the job fails.
  2. Resolve without installing (faster, safer)
    • Run bun install --lockfile-only in a separate job to validate resolution.
    • Pair with --frozen-lockfile when you want strictness.
  3. Block unreviewed lockfile changes
    • Require CODEOWNERS approval when bun.lock changes.
    • If you’re still on bun.lockb, require a dependency snapshot artifact in the PR.
  4. Enforce registry and scope policies
    • Maintain an allowlist of registries.
    • Reject dependencies that resolve from unexpected hosts.
  5. Pin or ban Git dependencies
    • If you allow git+https://..., require commit SHAs, not branches.
  6. Monitor dependency graph deltas over time
    • Store a normalized dependency list artifact for every merge to main.
    • Alert on additions of new top-level packages or large transitive expansions.

If you want inspiration for “policy as code,” I’d steal from how I think about AI security and making unsafe states hard to enter. Same idea. Different threat model.

How to monitor unexpected dependency graph changes over time

Most teams do dependency review at PR time and then never look back. That’s not enough.

The workflow I like:

  • On every merge to main, generate a normalized dependency graph snapshot.
  • Commit it or store it as a build artifact.
  • Diff it nightly or weekly and alert on anomalies.

What counts as an anomaly depends on your org, but these are good starters:

  • A new dependency that appears across many services in the same day.
  • A sudden jump in transitive dependency count.
  • A new dependency pulled from a Git URL.

If you’re already producing SBOMs in other languages, treat JavaScript the same way. You don’t need to boil the ocean. You just need one diffable artifact that doesn’t lie.

The practical recommendation (and my prediction)

If you’re on Bun today, move to bun.lock unless you have a very specific reason not to. The moment you care about audits, incident response, or compliance, a binary lockfile is self-inflicted pain.

My prediction: by 2027, “we can’t diff our lockfile” will sound as ridiculous as “we don’t run tests in CI.” Not because everyone suddenly became security saints, but because the first team that gets burned will turn it into policy everywhere else.

Photo by Adhitya Sibikumar on Unsplash.

Continue reading

a key chain with a key

Rust Reproducible Builds + SBOM + Signed Artifacts [2026]

A 2026-ready Rust release pipeline: pin toolchains, make builds reproducible, gate deps with cargo-vet, generate SBOM + SLSA provenance, and sign everything with keyless cosign.

Bun vs Deno in 2026: Which Next-Gen JS Runtime Actually Wins?

Bun vs Deno in 2026: Which Next-Gen JS Runtime Actually Wins?

Bun wins for raw speed and Node.js drop-in replacement; Deno wins for security-first architectures and standards compliance. Here's exactly when to pick each.

a white dice with a black github logo on it

How to Build a Rust Version Bump Tool [2026 Tutorial]

Build a CI-safe Rust release toolchain: semver bump rules, workspace updates, changelog generation, annotated/signed git tags, and reproducible benchmarks.

Laptop displaying code next to a lucky cat statue

7 Safer Defaults for Code Review Automation (No AI) [2026]

Stop chasing AI review bots. Use formatting, linting, hooks, CODEOWNERS, and branch protections to cut review noise without lowering the bar.

Cite this article
Kunal Ganglani (2026, September 10). Bun lockfile bun.lockb format: Threat model + CI checks [2026]. Kunal Ganglani. Retrieved September 10, 2026, from https://www.kunalganglani.com/blog/bun-lockfile-lockb-format

Frequently Asked Questions

What is bun.lockb?

bun.lockb is Bun’s legacy lockfile format that stores the resolved dependency graph in a binary file. It pins direct and transitive dependency versions so installs can be reproducible. The main downside is that it’s hard to review in code review because diffs aren’t human-readable.

Is bun.lockb a binary lockfile?

Yes. The “b” in bun.lockb refers to Bun’s older binary lockfile format. Bun v1.2 switched the default to a text lockfile named bun.lock to improve reviewability and tooling support.

Should I commit bun.lock or bun.lockb to git?

You should commit your lockfile to git so installs are reproducible across machines and CI. If you’re on a modern Bun version, prefer committing bun.lock because it’s text and reviewable. Only keep bun.lockb if you’re stuck on an older workflow and have compensating controls like dependency graph snapshots.

How do I migrate bun.lockb to bun.lock?

Run `bun install --save-text-lockfile --frozen-lockfile --lockfile-only`, then delete bun.lockb. The `--frozen-lockfile` part helps ensure the migration doesn’t change your resolved versions unexpectedly. After migrating, review the new bun.lock diff in a PR like any other dependency change.

What does --frozen-lockfile do in Bun?

It tells Bun to treat the lockfile as an input, not something it’s allowed to rewrite. In CI, this should fail the build if `package.json` and the lockfile don’t match. It’s one of the simplest ways to prevent silent dependency drift from landing on main.

How can I generate a lockfile without installing dependencies in Bun?

Use `bun install --lockfile-only`. This updates or generates the lockfile without writing dependencies into `node_modules`. It’s useful in CI for fast validation jobs that avoid running install scripts or mutating the workspace.