Bun 1.4.0 shipped on August 20 with its runtime source cut over from Zig to Rust. The release also presents fifteen package-shaped jobs as built-ins: image transforms, browser control, Markdown, PTYs, cron, archives, terminal text, and more. Those changes belong together. Bun is taking responsibility for more application behavior while using Rust, upstream compatibility tests, and production canaries to make that larger boundary safer. Try 1.4 as a pinned, measured migration. Do not swap it blindly for Node.

This is not a story about dependencies disappearing. A line can leave package.json while its implementation, upgrade cadence, platform matrix, and security boundary move into the runtime. Bun 1.4 makes that transfer unusually visible. The release compresses a JavaScript toolchain into one executable, then shows the engineering bill for owning the result.

15 → 1
Package jobs presented as Bun built-ins
+1,517
Node.js test-suite cases added since 1.3
1st
Stable release with Bun's runtime in Rust

Fifteen dependencies did not disappear

Bun's release page covers everything shipped since 1.3.0 rather than only symbols first added by the 1.4.0 tag. Feature headings carry version chips. Bun.Image first shipped in 1.3.14; parts of Bun.Terminal date to 1.3.5; Bun.WebView spans 1.3.12 and 1.4.0. August 20 is still a real boundary: 1.3.14 was the final Zig release, and 1.4.0 is the first stable Rust one.

Read the fifteen-to-one claim as product positioning, not interface parity. These are Bun-native ways to perform common jobs. They do not implement every package API, and adopting them trades portable npm contracts for a tighter runtime contract.

Package-shaped jobBun surfaceWhat remains outside the line count
Sharp image workBun.ImageNative codecs, OS image facilities, platform-specific formats
Puppeteer browser workBun.WebViewSystem WebKit or an installed Chrome-family browser
Marked parsingBun.markdownApplication-owned sanitization for untrusted HTML
node-pty terminalsBun.TerminalPOSIX PTYs or Windows ConPTY and their process semantics
node-cron schedulingBun.cronProcess lifetime or the operating system scheduler

A dependency can leave the lockfile without leaving the system.

Integration can still earn the transfer. It can remove version skew, repeated parsing, process boundaries, install time, and adapter code, while giving one maintainer enough control to optimize an end-to-end path. Independent packages have strengths worth naming: their own release cadence, a public interface shared by several runtimes, and the option to replace one subsystem without replacing the runtime. Bun wins the trade only when its implementation and operating boundary are better for the workload.

A terminal call now ends inside the runtime

Bun.Terminal is the cleanest small example. A TypeScript program supplies dimensions and callbacks, hands the terminal to Bun.spawn(), then reads and writes a pseudo-terminal without importing node-pty. In the tagged 1.4 source, the native object stores the master file descriptors, reader, writer, and event-loop handle. On Windows it also owns the ConPTY handle. The subprocess retains a pointer to the attached terminal and calls back into it on exit. Bun coordinates both objects, so the PTY lifecycle becomes part of the runtime lifecycle.

  1. Create Bun.Terminal
    Columns, rows, data and exit callbacks
    attach
  2. Spawn Child process
    Child process attaches to the PTY
    bridge
  3. Own Native PTY
    POSIX descriptors or Windows ConPTY
    schedule
  4. Deliver Runtime events
    Bun event loop returns data and exit

I downloaded the official macOS arm64 1.4.0 artifact, matched its SHA-256 to GitHub's release metadata, and confirmed revision 34cbb9a40. A small PTY program spawned /bin/sh, received the marker written by the child, and observed exit code zero. That reproduces the narrow path. It does not prove node-pty parity, Windows ConPTY behavior, or resilience under an interactive agent's long session.

One binary still has seams

Built-in means Bun owns the JavaScript API and integration path. It does not mean Bun wrote every layer below it. JavaScriptCore, uSockets, BoringSSL, SQLite, native codecs, browser engines, and operating-system services remain part of the deployed system.

Rust is the payment for the larger boundary

The rewrite targeted cleanup failures rather than missing feature capacity. Jarred Sumner's account describes recurring use-after-free, double-free, and forgotten-cleanup bugs across a large native codebase. The team mechanically ported 535,496 lines of Zig with Claude Code, kept the TypeScript test suite as the behavioral contract, then used Rust ownership and sanitizer builds to expose lifetime problems.

That mechanism is credible; the phrase “Bun is memory-safe now” outruns the evidence. Bun's own post says roughly four percent of the Rust code uses unsafe, and the runtime still crosses large C and C++ boundaries. Rust can make Bun-owned resource cleanup more deterministic and move more lifetime errors to compile time. It cannot make a protocol correct, model every event-loop ordering, or prove an FFI dependency safe.

ClaimEvidenceBoundary
The rewrite preserved behaviorMaintainers report six green platforms and the full suite passingIntegration-only states still escape test coverage
The rewrite reduced leak exposureRust cleanup, ASAN wiring, and fixed 1.3.14-repro bugsNative libraries and unsafe code remain
1.4 uses less CPU and memoryPublished Bun and Claude Code measurementsMaintainer-run workloads, not an independent benchmark here

The strongest outside evidence comes from a failure, not a benchmark. Prisma Compute had two Bun 1.3 problems: memory growth while reading S3 objects and a SQL connection pool that did not recover after scale-to-zero. Prisma says the Rust canary passed both tests and launched its public beta on that build. It also explicitly refuses the larger conclusion: two repaired failure modes do not prove every pool or memory problem fixed.


Compatibility percentages cannot model an event loop

Bun reports 1,517 more Node.js suite tests and pass rates of at least 97 percent for several core modules. That is valuable evidence because compatibility is an accumulation of small semantics. Production workloads still add states the suite does not model. A post-release WebSocket report found that delaying authentication before handleUpgrade() could leave an upgrade hanging in 1.4.0. The repair merged two days after the release tag. A broad suite could be green while one ordering between authentication, upgrade, and socket lifecycle was wrong.

The timing matters more than using the bug as a scare story. The issue had a bounded reproduction, maintainers repaired it, and the fix was not in 1.4.0. “Fixed on main” and “safe in the tagged binary” are different states. Pinning a runtime makes that distinction operational instead of rhetorical.

migration gate Make the compatibility claim workload-specific

Run the same service under its current Node version and pinned Bun for seven days—or a predeclared production volume. Promote only after response bytes, status codes, durable writes, queue effects, and error classification agree; no Bun-only hangs appear; diagnostics remain complete; and one declared resource gain survives.

The security obligation moved too

I also passed an image containing a raw onerror attribute through Bun.markdown.html() on the official release binary. The attribute survived, exactly as Bun's documentation warns. That is a parser contract, not a security boundary. The important point is ownership. Deleting a Markdown package does not delete the application's obligation to sanitize untrusted content.

The browser story has the same shape. Bun.WebView can remove Puppeteer from a supported automation path, but macOS uses system WebKit and other platforms use an installed Chrome-family browser. The API is experimental. A deployment may have fewer npm nodes while depending more heavily on OS versions, browser provisioning, profile isolation, and Bun's release cadence.

The package graph measures who you install. It does not measure everything you trust.

Anthropic is both the advantage and the bias

Anthropic acquired Bun in December 2025. Claude Code ships as a Bun executable and exercised the Rust port for months before stable release. That gives Bun something most runtimes lack: funding plus a demanding, long-lived, widely distributed owner workload. Terminal control, browser automation, package review, test selection, self-contained executables, and machine-readable performance reports make sense together when AI coding tools are a strategic user.

The same evidence is correlated. Claude Code proves Bun can carry serious work, but it is no longer an independent customer choosing among neutral vendors. Prisma's beta and Vercel's first-party Bun runtime broaden the case, while Vercel's public-beta label and documented Node-relative gaps keep it open. Bun's reported monthly downloads do not reveal how many teams use the package manager, the runtime, a compiled CLI, or production servers.

Fifty agents make small runtime costs expensive

My laptop is an M5 MacBook Pro with 24 GB of RAM. The CPU, memory, and disk are fast, and I still feel the machine slow down. A normal workday can include Chrome, VS Code, ChatGPT's desktop app or Claude Cowork, several CLIs and MCP servers, and many Claude Code or Codex sessions at once. The metric I care about is how much of the workstation remains after all of them are running.

This is why Bun's resource numbers matter more to me now than they would have a few years ago. Saving a little idle CPU or tens of megabytes in one process can look trivial on modern hardware. Keep 10–15 agent processes and their tools alive, then aim for 50–100, and the same steady-state costs become the limit on useful parallel work. These benchmarks measure the runtime cost beneath every Bun-based agent; total agent capacity remains unmeasured.

24% → 10%
Claude Code p99 CPU
5.8% → 2.5%
Claude Code p50 CPU
5× lower
Idle CPU in a small hello-world app

Bun attributes the CPU drop to better garbage-collector timer requests, a segmented-array layout for visiting JavaScriptCore Strong roots, fewer futex calls, and allocator changes. Claude Code is owner evidence, not an independent benchmark, but it is the right shape of workload: a large application that stays alive while tools, subprocesses, and user sessions keep moving through it.

Memory decides how many workers stay resident

Bun measured peak memory under one million requests with 64 connections, except the Next.js and Vite runs, which used 100,000 requests. Every tested Bun server used less peak memory than it did on 1.3:

ServerBun 1.4Bun 1.3Node.js 26Change vs 1.3
Fastify120 MB233 MB156 MB−48%
Express92 MB169 MB145 MB−46%
node:http81 MB135 MB107 MB−40%
Elysia55 MB91 MBn/a−40%
Next.js285 MB397 MB342 MB−28%
Bun.serve36 MB45 MBn/a−20%
Vite dev server233 MB268 MB214 MB−13%

The long-running cases are even more relevant to agent software. In Bun's Next.js App Router test, a dynamic route using React.cache and a no-store fetch settled at 238 MB after 4,000 pages on Bun 1.4. Node settled at 410 MB; Bun 1.3 kept growing. A 100-deep Response.clone() chain around a 10 MB stream fell from about 1,050 MB to 20 MB. Those are narrow tests, but both remove the kind of retained memory that makes a long session decay while the developer is still using it.

Startup and binary size constrain fan-out

Hello-world processBun 1.4Bun 1.3Node.js 26
Windows startup15.5 ms39.0 ms40.1 ms
Windows peak memory16.8 MB46.5 MB32.5 MB
Linux startup5.1 ms10.9 ms27.2 ms
Linux peak memory14.6 MB33.0 MB44.5 MB

Short-lived workers pay startup cost on every invocation. Resident workers pay the memory cost for their whole lifetime. Bun 1.4 cuts both in these Linux and Windows tests, which makes process-level isolation less expensive for schedulers that start tools on demand instead of keeping every tool inside one host.

Runtime binaryBun 1.4Bun 1.3.14Result
Linux x6477.0 MB88.5 MB11.5 MB smaller
Linux arm6476.8 MB87.6 MB10.8 MB smaller
Windows x6484.8 MB93.9 MB9.1 MB smaller
Windows arm6475.1 MB90.2 MB15.1 MB smaller
macOS arm6461.2 MB60.2 MB1.0 MB larger
macOS x6466.6 MB66.0 MB0.6 MB larger

Bun can also compile TypeScript or JavaScript, its imports, and a copy of the runtime into one executable for a supported operating-system and architecture target. That does not turn JavaScript into a tiny native program; each executable still embeds Bun. It does give agent tools one deployable file and one runtime boundary instead of a separate Node installation and package tree.

I want 10–15 parallel agent sessions to feel ordinary, then 50–100 terminal panes without saturating the machine. A runtime cannot erase the cost of models, browsers, editors, MCP servers, or the work each agent performs. Bun 1.4 still moves in the direction I care about: more work per machine, a smaller steady-state cost, and one fast toolchain with which to build the next layer of agent software. To me, these improvements buy capacity.

What engineers get from Bun 1.4 in practice

Bun's original moat was the integrated JavaScript toolchain. Version 1.4 pushes that integration upward into application work. Node compatibility is becoming the entry contract beneath a Bun-specific toolkit, not the final destination. That is a stronger product and a larger bet.

The architecture matters only if it removes work or makes a production failure less likely. The practical list below uses the full 1.3-to-1.4 release train and keeps the final tag boundary visible. These are the changes that make me want to test Bun again on real projects.

1

The test runner and tracer can follow the application

Bun reports that Playwright, Vitest with coverage and worker pools, OpenTelemetry HTTP and file instrumentation, dd-trace, and @datadog/pprof now run on 1.4.0. Those are migration blockers, not demo features. Verify the exact plugins and transports you use. Addons bound directly to Node, V8, libuv, or the C++ ABI need matching prebuilds for Bun's Node 26.3.0 target; Node-API addons retain their cross-version ABI contract.

2

Large test suites get scheduling primitives

--parallel, --shard, --changed, and isolation now compose with --timings=<path> and --update-timings, so workers and CI runners can start with slow tests instead of dividing files evenly. Keep a no-output watchdog and a serial fallback: a large-suite deadlock report remained open at the cutoff.

3

Performance evidence becomes plain Markdown

--cpu-prof-md, --heap-prof-md, and --metafile-md emit call trees, retaining chains, and bundle dependency paths that work over SSH, in an issue, or inside an agent's context. Most of this arrived during 1.3.x. The 1.4 value is the coherent workflow: measure, inspect, change, and compare without converting a GUI profile or a large JSON file first.

4

Agent tools gain native terminal, browser, and image surfaces

A TypeScript process can drive a real PTY with Bun.Terminal, navigate and screenshot with Bun.WebView, and resize or encode an image with Bun.Image. That covers much of a coding agent's observe-and-act loop inside one runtime. It does not supply approvals, secret policy, session recovery, or cross-platform parity; WebView is experimental and still relies on OS or installed browsers.

5

Package maintenance becomes one reviewable workflow

bun pm diff, bun audit fix --dry-run, bun dedupe, bun prune --production, and bun pm licenses --prod --json cover upgrade review, advisory repair, lockfile cleanup, deployment pruning, and license inventory. Audit sends installed package names and versions to the configured registries; it is not an offline scan. Keep repair changes on a branch: an open report shows audit fix --latest selecting an older, more vulnerable major.

6

React Compiler can become a Bun build option

bun build --react-compiler runs React's auto-memoization transform inside Bun's parser instead of adding another Babel or SWC parse-and-print pass. Bun reports 71 ms of added work across roughly 860 components versus 9.15 seconds for the Babel plugin on the same input. Treat that as one measured codebase, then compare your plugins, output, and runtime behavior before removing the old compiler path.

7

Monorepo fan-out can lose two coordinator packages

bun run --parallel --filter prefixes output by package and script, preserves pre/post hooks, and can continue after failures. That absorbs common concurrently and npm-run-all jobs. The related test flags shorten the edit-test loop further, but Bun still does not provide a distributed task graph, remote cache, or isolation policy for shared ports and databases.

Two smaller fixes with a large practical effect
  • bun --hot no longer loses track of macOS files after an editor's atomic rename.
  • Bun.Glob and recursive fs.readdir no longer skip files on bind mounts, FUSE, or NFS.

This is the part that genuinely excites me. Bun says the 1.3-to-1.4 train fixed more than 2,900 issues, while the Rust port fixed 128 bugs that still reproduced on 1.3.14. The one-time cutover now sits behind a stable tag. In the first week after release, smaller fixes, ownership cleanup, and performance work were already landing in an official 1.4.1 canary. That proves fast delivery from main to canary, not a faster stable cadence; at the August 26 cutoff, 1.4.0 was still the only stable 1.4 release. What changed is that the next fixes no longer have to share a release milestone with the mechanical port. Whether the 1.4.x line turns that into smaller, quicker stable releases is the next result I want to see.

Adopt by canary, not by ambient upgrade

try now Use a pinned runtime where you can measure the whole path

Bun 1.4 is a credible candidate for TypeScript CLIs, coding tools, greenfield services, and incremental package, test, or bundling adoption. Record the exact revision, keep rollback cheap, and test the Bun-native API on every operating system you ship.

wait or dual-run Do not treat Node compatibility as an in-place guarantee

Gate services with delayed WebSocket authentication, browser automation, Bun-native operating-system APIs, and workloads that depend on exact Node behavior. Wait when the migration needs mature host observability or cross-runtime behavior that your application cannot independently verify.

Bun 1.4 preserved enough behavior through a source-language migration to keep widening the runtime boundary. That combination deserves a serious production trial because the integration is real, the test investment is large, and the canary evidence reaches beyond microbenchmarks. A blind replacement is still unjustified because every deleted package line concentrates more behavior behind one upgrade.

The package graph shrank. The dependency became Bun.

Sources & Evidence
Bun v1.4.0 was published on August 20, 2026 at commit 34cbb9a40 The release record fixes the version, date, source baseline, and official binary artifacts used by this review.
Bun v1.4.0 release
The Bun 1.4 post covers the 1.3-to-1.4 train, reports its performance and compatibility results, and labels feature chronology It supports the CPU, memory, startup, binary-size, and practical-impact claims while keeping release-train work separate from the final tag.
Bun 1.4 release post
Bun can bundle JavaScript or TypeScript, imports, and the Bun runtime into a standalone executable for supported cross-compilation targets The pinned documentation supports the deployment model while making clear that the result still embeds a runtime and targets a defined platform matrix.
Bun standalone executable documentation
The official 1.4.0 macOS arm64 binary matched its published digest and passed the PTY, Markdown, JSON5, and string-width checks used here The public record preserves the exact script, binary identity, outputs, platform, and limits behind the first-person reproduction claims.
Local reproduction record
Bun 1.4 is the first Rust release, fixes 128 bugs reproducible in 1.3.14, and leaves idiomatic Rust refactoring for later releases The maintainer account supplies the rewrite mechanism, operational examples, future work, and the boundary between shipped fixes and enabled improvements.
Bun is rewriting itself in Rust
The tagged Bun.Terminal object owns PTY descriptors, I/O, event-loop state, and a Windows ConPTY handle Pinned source shows where the JavaScript call becomes runtime-owned native lifecycle instead of an external package wrapper.
Bun.Terminal implementation
The tagged Subprocess object retains the pointer to its attached terminal The ownership direction explains how Bun coordinates process exit with the runtime-owned PTY without putting a child-process reference on Terminal.
Bun Subprocess implementation
Bun.Image routes image work through native codecs and operating-system image facilities The implementation supports the table's narrower point: an npm entry can disappear while codec and platform dependencies remain.
Bun.Image pipeline
Bun.markdown allows raw HTML and does not sanitize untrusted output The documented boundary proves that a built-in parser does not absorb the application security job.
Bun.markdown release notes
Bun.WebView uses system WebKit or an installed Chrome-family browser and remains experimental The outside browser and platform contract bounds the “no Puppeteer” product claim.
Pinned Bun.WebView documentation
A delayed-authentication WebSocket upgrade path could hang in Bun 1.4.0 The bounded production report shows why broad compatibility rates and a specific event-loop ordering must be evaluated separately.
Bun issue #39766
The delayed WebSocket upgrade repair merged on August 22, after the 1.4.0 release commit The merged test and implementation change proves that “fixed on main” was not the behavior of the pinned release binary.
Bun pull request #39642
A large-suite bun test --parallel deadlock remained reported against stable 1.4.0 at the cutoff The open report keeps the new CI scheduling workflow behind a soak test, watchdog, and serial fallback.
Bun issue #39987
bun audit fix --latest had an open report of selecting an older, more vulnerable package major The report makes dry-run, branch isolation, and manifest-plus-lockfile review part of the package-repair workflow.
Bun issue #39309
Bun audit serializes installed package names and versions and sends the request to configured registries Pinned implementation code establishes the network and inventory-disclosure boundary behind the new repair workflow.
Bun audit implementation
Node-API carries cross-version ABI stability while direct V8, libuv, and Node C++ bindings do not The Node contract bounds which native addons should survive Bun's Node 26.3.0 target without a matching rebuild.
Node-API ABI stability
An official canary identified itself as 1.4.1 and contained post-tag fixes, Rust cleanup, and performance work This first-hand check supports fast main-to-canary delivery while preserving that no faster stable point-release cadence had yet been proved.
Post-1.4 cadence check
Prisma Compute launched its public beta on Bun's Rust canary after two specific failure tests improved This is independent first-hand runtime evidence with explicit workloads and limits, not a Bun microbenchmark.
Prisma Compute production report
Vercel offers a first-party Bun Functions runtime in public beta with documented gaps beside Node Hosting support broadens adoption evidence while preserving the difference between availability and operational parity.
Vercel Bun runtime
Anthropic acquired Bun and funds it as infrastructure for Claude Code, the Agent SDK, and future AI coding products Ownership explains both Bun's production feedback loop and the strategic user behind the expanding application toolkit.
Bun joins Anthropic
Anthropic describes Claude Code as Bun infrastructure while committing to Bun's open-source development The buyer's announcement confirms the acquisition and also shows why Claude Code remains correlated owner evidence.
Anthropic acquisition announcement

Research footprint

Open the evidence artifacts ↗
292 minwall-clock window
4linked agent sessions
13committed Markdown artifacts
114.3Mtokens processed

The planning envelope was 90–120 minutes and 250,000–400,000 aggregate tokens. The measured freeze arrived after 292 minutes, from to . The token estimate was badly wrong because cumulative accounting counts the full context presented on every response, including cache hits.

The 114,317,897 total equals 113,943,245 input plus 374,652 output tokens. Of the input, 110,553,216 came from cache and 3,390,029 did not. Reasoning output (131,130) is a subset of output, not an extra addition. This OSS Radar issue has no public raw-prompt transcript; its prompt count is zero by publication rule.

The frozen manifest records the recursive session closure, selected cumulative epoch ends, log-prefix hashes, artifact counts, and timing. The root counter reset once after compaction, so rules version 2 counts its two monotonic epochs; the other three sessions have one. Cached input is part of input, and reasoning output is part of output.

The counter-reset audit also records four automatic guardian sessions totaling 956,584 tokens. They have no parent-thread link and remain outside the headline total. This number describes the article's linked research-session tree, not every internal token the Codex product may have processed around it.

This is provenance, not a quality score, bill, or environmental estimate. Wall-clock time is not hands-on time. The private session logs are not published; the prefix commitments make the record auditable by the author but do not let a reader independently reconstruct the totals.