Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
354 commits
Select commit Hold shift + click to select a range
d328f72
bun build --compile --compile-image: single file — the raw page-align…
Jarred-Sumner Aug 6, 2026
e366bdb
Bun.unsafe.embedImage(exe, img, out?): embed a heap image into an exi…
Jarred-Sumner Aug 6, 2026
0e16126
heap-image: CLAUDE_CODE_SNAPSHOT_OUT also marks the process as buildi…
Jarred-Sumner Aug 6, 2026
6f97ce6
heap-image: dedicated post-restore arena is macOS-default / Linux opt…
Jarred-Sumner Aug 6, 2026
2a280cc
SNAPSHOT.md: single-file CC reaches the prompt in ~45ms warm; restore…
Jarred-Sumner Aug 6, 2026
4721c89
SNAPSHOT.md: raw vs zstd embed numbers for CC
Jarred-Sumner Aug 6, 2026
4ae3c5d
heap-image: re-apply the mimalloc hint floor after the overlay (the b…
Jarred-Sumner Aug 6, 2026
7c31f2c
heap-image: apply the allocator hint floor immediately after the data…
Jarred-Sumner Aug 6, 2026
cacbf92
heap-image: the deferred (lib-fixup) data-segment overlay must run ri…
Jarred-Sumner Aug 6, 2026
0a3439e
SNAPSHOT.md: Linux regression root cause (deferred overlay ordering)
Jarred-Sumner Aug 6, 2026
a37fa3f
mimalloc: persistent hint floor — repairs a wrapped/invalid inherited…
Jarred-Sumner Aug 6, 2026
91aeb42
heap-image: hint-floor 'top of image' considers only mimalloc's hint …
Jarred-Sumner Aug 6, 2026
a489119
fix: stray comment inside one-line statements
Jarred-Sumner Aug 6, 2026
6368cdf
heap-image: Linux embedded-image source (PT_LOAD pointer -> file offset)
Jarred-Sumner Aug 6, 2026
9a62872
SNAPSHOT.md: first-exec signature assessment cost vs file size on macOS
Jarred-Sumner Aug 6, 2026
9149642
heap-image: refresh the VM's stack bounds before taking the JSLock at…
Jarred-Sumner Aug 6, 2026
195d5fc
heap-image: stock JIT tiers everywhere (no baseline/FTL overrides in …
Jarred-Sumner Aug 6, 2026
5064dc9
heap-image: Linux include order (link.h/elf.h at top)
Jarred-Sumner Aug 6, 2026
37722c7
heap-image: _GNU_SOURCE for dl_iterate_phdr
Jarred-Sumner Aug 6, 2026
8b11be4
heap-image: refresh the VM's stack bounds inside adopt, before the en…
Jarred-Sumner Aug 6, 2026
8412809
heap-image: verbose around the VM stack-bounds refresh
Jarred-Sumner Aug 6, 2026
8277edb
heap-image (Linux): build identity hashes code around main(), not the…
Jarred-Sumner Aug 6, 2026
96762de
heap-image (Linux): build identity = NT_GNU_BUILD_ID note
Jarred-Sumner Aug 6, 2026
418a168
heap-image (Linux): the ELF section reader reads /proc/self/exe with …
Jarred-Sumner Aug 6, 2026
304f787
SNAPSHOT.md: Linux 3/3 incl. single-file; the five restore-time bugs
Jarred-Sumner Aug 6, 2026
20feae9
heap-image (Linux): compiled executables use the BUN_COMPILED pre-mai…
Jarred-Sumner Aug 6, 2026
cb7383f
heap-image: one process-wide restore epoch (exported bun_image_epoch,…
Jarred-Sumner Aug 6, 2026
ec87f16
SNAPSHOT.md: epoch/once rule + CPU dispatch handling
Jarred-Sumner Aug 6, 2026
8d051e8
misctools/run-jetstream.js: JetStream2 shell-runner shim for bun (per…
Jarred-Sumner Aug 6, 2026
3d2568e
heap-image: JSGlobalObject::throw_disabled_in_snapshot_error_if_neede…
Jarred-Sumner Aug 6, 2026
86a033a
heap-image: BUN_IMAGE_IO_WARN=1 logs disabled-in-snapshot I/O with a …
Jarred-Sumner Aug 6, 2026
25d233a
heap-image: BUN_IMAGE_NO_CPU_REPROBE kill-switch
Jarred-Sumner Aug 7, 2026
e724f8f
SNAPSHOT.md: perf-when-unused findings, harness, merge bar + ship order
Jarred-Sumner Aug 7, 2026
208d354
env_var: cached values are keyed on the heap-image restore epoch (os.…
Jarred-Sumner Aug 7, 2026
29b994b
heap-image: run one unconditional tick after emitting 'restore' so co…
Jarred-Sumner Aug 7, 2026
239b5bc
heap-image: process.cwd() and the resolver/node:fs top-level dir re-r…
Jarred-Sumner Aug 7, 2026
771e74c
SNAPSHOT.md: immortal-mark branches measured at ~1.5% on JetStream is…
Jarred-Sumner Aug 7, 2026
1e6e317
SNAPSHOT.md: why the immortal mark branches exist and the exact zero-…
Jarred-Sumner Aug 7, 2026
2aa44b6
heap-image: re-exec hard cap via argv[0] tag (independent of environm…
Jarred-Sumner Aug 7, 2026
ff5f533
WIP heap-image: ProcessDerived<T> (launch-context values recompute pe…
Jarred-Sumner Aug 7, 2026
a0209f7
heap-image: launch context is one mechanism — LaunchContext capture/r…
Jarred-Sumner Aug 7, 2026
3aa9425
node_process: keep #[export_name] on create_argv0 (helper had been in…
Jarred-Sumner Aug 7, 2026
ea128bf
SNAPSHOT.md: open item — restored CC segfault in fetch-abort on SIGTE…
Jarred-Sumner Aug 7, 2026
33e7357
misctools: bun.report trace decoder; safe-run watchdog for CLI launches
Jarred-Sumner Aug 7, 2026
e0ba4b1
ByteStream::on_data: take the pending read action before signal_drain…
Jarred-Sumner Aug 7, 2026
b349b2d
post-rebase: drop unused VM::collect_async_full (main's GC controller…
Jarred-Sumner Aug 7, 2026
9311c35
SNAPSHOT.md: fetch-abort crash resolved by rebase; rebase notes
Jarred-Sumner Aug 7, 2026
d80bee1
mimalloc fork: carry the upstream 1803341d header comment change (aud…
Jarred-Sumner Aug 7, 2026
15d1611
test(image): full GC right after restore completes promptly (regressi…
Jarred-Sumner Aug 7, 2026
b8b3d76
SNAPSHOT.md: ParkingLot restore fix + current CC pre-dispatch numbers
Jarred-Sumner Aug 7, 2026
2187cd6
SNAPSHOT.md: CC cut-point comparison (plain 262 / pre-dispatch 265 / …
Jarred-Sumner Aug 8, 2026
ee847e4
SNAPSHOT.md: late cut + hydrate v0 = 56 MB idle
Jarred-Sumner Aug 8, 2026
f712c14
image: keepTimers — armed timers survive the snapshot and are re-base…
Jarred-Sumner Aug 8, 2026
5cd5f5b
image: rebuild the isolated spawnSync event loop after restore (it wr…
Jarred-Sumner Aug 8, 2026
d87ed58
SNAPSHOT.md: late cut + hydrate v1 runs a real turn at 76 MB idle; ru…
Jarred-Sumner Aug 8, 2026
2df7597
image restore: fresh randomness and time bases per process — reseed M…
Jarred-Sumner Aug 8, 2026
95a6fad
image restore prelude: drop the tty _refreshSize call (materializing …
Jarred-Sumner Aug 8, 2026
ec4b89c
restore prelude: process.chdir('.') through any installed wrapper (no…
Jarred-Sumner Aug 8, 2026
b752ce9
image restore: flush the getaddrinfo cache (answers came from the bui…
Jarred-Sumner Aug 8, 2026
d6d98c5
misctools/cc-image-demo.sh: plain vs image side-by-side with a real t…
Jarred-Sumner Aug 8, 2026
28c888b
image: extern-library fixups on Darwin (per-segment table, compacted;…
Jarred-Sumner Aug 8, 2026
896d5a7
image restore: deliver hangups for dead fds from the event loop after…
Jarred-Sumner Aug 8, 2026
d39e971
image: --compile-image embeds the zstd frame and inflates it into the…
Jarred-Sumner Aug 8, 2026
605cac6
image: pages are frozen in the builder (MI_THREADID_FROZEN, frees dro…
Jarred-Sumner Aug 8, 2026
5e5d945
image: envGate snapshot option — the image records the build-time sta…
Jarred-Sumner Aug 8, 2026
a7a0d50
mimalloc: hole-sweep state moves from __thread variables onto the swe…
Jarred-Sumner Aug 8, 2026
07677b3
mimalloc: frozen image pages are skipped by collect/hole sweeps and r…
Jarred-Sumner Aug 8, 2026
cabe630
SNAPSHOT.md: end-of-day state, honest numbers, the day's bug list
Jarred-Sumner Aug 8, 2026
2578fef
heap image: tests move to test/js/bun/heap-image (test/js/bun/image b…
Jarred-Sumner Aug 8, 2026
285cb0d
Revert "WIP: bundler prototype: lazy module init (BUN_BUNDLER_LAZY_IN…
Jarred-Sumner Aug 8, 2026
0165ce3
docs: --compile-image
Jarred-Sumner Aug 8, 2026
9f98e6c
SNAPSHOT.md: fair merge-bar numbers
Jarred-Sumner Aug 8, 2026
98a97f1
heap image: BunMemDebug.cpp becomes HeapImage.{h,cpp} (build + restor…
Jarred-Sumner Aug 8, 2026
433ae98
heap image: plain bun only takes part when asked to via the environme…
Jarred-Sumner Aug 8, 2026
c45f713
heap image tooling: enable allocation sampling after restore under BU…
Jarred-Sumner Aug 8, 2026
b8c9b35
node:tls: bundled root certificates are built once as static strings …
Jarred-Sumner Aug 8, 2026
a64ab65
SNAPSHOT.md: the bimodal idle is the claude.ai MCP connectors
Jarred-Sumner Aug 8, 2026
57bbb5d
run_command: comment no longer refers to a removed image mode
Jarred-Sumner Aug 8, 2026
9710be4
mimalloc: pin to oven-sh/mimalloc#13 and stop vendoring the fork's fi…
Jarred-Sumner Aug 8, 2026
33cdd1d
Merge origin/main into claude/lowmem-cc
Jarred-Sumner Aug 8, 2026
80703f9
Revert "node:tls: bundled root certificates are built once as static …
Jarred-Sumner Aug 8, 2026
09db11f
SNAPSHOT.md: note the reverted root-cert experiment
Jarred-Sumner Aug 8, 2026
a73d4c5
WebKit: pin to oven-sh/WebKit#397's preview artifact so the branch bu…
Jarred-Sumner Aug 8, 2026
e7733ce
heap image: remove the experiment knobs from the product path (immort…
Jarred-Sumner Aug 8, 2026
816229b
Remove working notes and an abandoned experiment patch from the branch
Jarred-Sumner Aug 8, 2026
15b60d1
mimalloc: pin to the reworded head of oven-sh/mimalloc#13
Jarred-Sumner Aug 9, 2026
34f7031
[autofix.ci] apply automated fixes
autofix-ci[bot] Aug 9, 2026
48b17f5
heap image: satisfy the source lints — the restore-time poll loops go…
Jarred-Sumner Aug 9, 2026
b5cafd5
Drop this branch's earlier node:https change; the reviewed version is…
Jarred-Sumner Aug 9, 2026
07e72bb
heap image restore: the resuming thread adopts the image's main threa…
Jarred-Sumner Aug 9, 2026
146f4f7
heap image restore: one full collection before the 2 s reclean — the …
Jarred-Sumner Aug 9, 2026
c2b8fec
Portability: heap images are implemented on macOS and Linux; other ta…
Jarred-Sumner Aug 9, 2026
632c5db
clippy clean: safety comments, unsafe extern signatures, bun_sys/bun_…
Jarred-Sumner Aug 9, 2026
64bf05c
tooling: metadata census uses sizeInBytesForGC; StrongRef.cpp back to…
Jarred-Sumner Aug 9, 2026
8f55088
Bun.unsafe.snapshot: one `timers: "keep" | "cancel"` option replaces …
Jarred-Sumner Aug 9, 2026
1fab523
[autofix.ci] apply automated fixes
autofix-ci[bot] Aug 9, 2026
652e1d1
Heap images: process.env is a store-backed view in image builds — a r…
Jarred-Sumner Aug 9, 2026
eef647e
[autofix.ci] apply automated fixes
autofix-ci[bot] Aug 9, 2026
f7fa45d
snapshot report: attribute each copy of process.env to its call site
Jarred-Sumner Aug 9, 2026
f88a614
Merge remote-tracking branch 'origin/main' into claude/lowmem-cc
Jarred-Sumner Aug 9, 2026
b31867c
mimalloc: pin the squashed heap-image branch
Jarred-Sumner Aug 9, 2026
71f1565
Snapshots: the feature is called a snapshot everywhere (files, identi…
Jarred-Sumner Aug 9, 2026
279ba76
[autofix.ci] apply automated fixes
autofix-ci[bot] Aug 9, 2026
c1939c8
Bun.startupSnapshot { take, isBuildingSnapshot, epoch, reclean } repl…
Jarred-Sumner Aug 9, 2026
946c58c
[autofix.ci] apply automated fixes
autofix-ci[bot] Aug 9, 2026
b5ba628
Snapshots are embedded raw and mapped in place (no compressed form, c…
Jarred-Sumner Aug 9, 2026
874c227
[autofix.ci] apply automated fixes
autofix-ci[bot] Aug 9, 2026
23968dc
deps: WebKit preview build for oven-sh/WebKit#397 at 8e803318
Jarred-Sumner Aug 9, 2026
3338a7d
Snapshot.cpp: dump-time diagnostics are verbose-only; snapshotVerbose…
Jarred-Sumner Aug 9, 2026
7168b33
Snapshots: support is a runtime property of the build (off under ASAN…
Jarred-Sumner Aug 9, 2026
f5cbf94
[autofix.ci] apply automated fixes
autofix-ci[bot] Aug 9, 2026
5988bfa
Snapshot restore re-derives stdio: tty/null flags and saved termios, …
Jarred-Sumner Aug 9, 2026
9d44940
[autofix.ci] apply automated fixes
autofix-ci[bot] Aug 9, 2026
23863f5
Snapshot restore re-installs the signal handlers behind process.on(si…
Jarred-Sumner Aug 9, 2026
3c5832e
[autofix.ci] apply automated fixes
autofix-ci[bot] Aug 9, 2026
72eea79
Snapshots on macOS are compiled in only when mimalloc is the process'…
Jarred-Sumner Aug 9, 2026
92b48ad
CI: Windows needs bun_is_compiled_executable and the signal re-instal…
Jarred-Sumner Aug 9, 2026
2127eca
ELF injector: rewriting an executable that already carries a payload …
Jarred-Sumner Aug 9, 2026
6aeb614
mimalloc's snapshot hook is only built where snapshots exist; on Wind…
Jarred-Sumner Aug 9, 2026
a77adc9
Snapshots: terminal std streams survive a restore in place (size refr…
Jarred-Sumner Aug 9, 2026
263c895
[autofix.ci] apply automated fixes
autofix-ci[bot] Aug 9, 2026
1061f65
Startup snapshots: identifiers say StartupSnapshot (files, module, ex…
Jarred-Sumner Aug 9, 2026
c51b557
[autofix.ci] apply automated fixes
autofix-ci[bot] Aug 9, 2026
4adfeae
ELF rewrites overwrite the previous block when the new payload fits; …
Jarred-Sumner Aug 9, 2026
9b745af
[autofix.ci] apply automated fixes
autofix-ci[bot] Aug 9, 2026
a5df1e2
A build without startup-snapshot support refuses to take one on the p…
Jarred-Sumner Aug 9, 2026
1b03b85
Linux: the data segment captured at snapshot time ends at _end, so pa…
Jarred-Sumner Aug 9, 2026
2dc76ca
fs.watch's manager is per process (a restored process makes a new one…
Jarred-Sumner Aug 9, 2026
a5018b5
Regression test: a restored process whose stack lands where the build…
Jarred-Sumner Aug 9, 2026
819e4f6
ELF: a rewrite that outgrows the previous block replaces it instead o…
Jarred-Sumner Aug 9, 2026
fe68dc0
WebKit pin: per-process stack-bounds environ cache
Jarred-Sumner Aug 9, 2026
5369203
[autofix.ci] apply automated fixes
autofix-ci[bot] Aug 9, 2026
fbd2236
Poll re-arm after restore registers afresh: on epoll a poll that stil…
Jarred-Sumner Aug 9, 2026
9fb628f
Restore: build the frozen-range tables after the fresh heap is bound;…
Jarred-Sumner Aug 9, 2026
3c8cc45
Restore re-runs BoringSSL fork detection's per-process setup: its sta…
Jarred-Sumner Aug 9, 2026
9bf6eae
Restore keeps process.stdin like stdout/stderr when fd 0 is a termina…
Jarred-Sumner Aug 9, 2026
69a5c8f
Drop the stdin raw-mode re-apply (a no-op: the stream's mode state al…
Jarred-Sumner Aug 9, 2026
aee486f
Restore recreated the builder's dup'd tty reader fd at the wrong numb…
Jarred-Sumner Aug 9, 2026
3f47faf
Review fixes: reclean reads pagemap directly (ipread adds the embedde…
Jarred-Sumner Aug 9, 2026
457d585
WebKit pin: review fixes
Jarred-Sumner Aug 9, 2026
3d2a92d
Review fixes: a snapshot dump that declines makes the process exit 1 …
Jarred-Sumner Aug 9, 2026
8c6ff08
[autofix.ci] apply automated fixes
autofix-ci[bot] Aug 9, 2026
0523a4a
WebKit pin: review batch 2 (guards, pacing accounting, side-table loc…
Jarred-Sumner Aug 9, 2026
3790c2a
Review fixes: the x86-64 arch tag no longer shares a bit with AVX512V…
Jarred-Sumner Aug 9, 2026
255c5f2
[autofix.ci] apply automated fixes
autofix-ci[bot] Aug 9, 2026
97c13ab
Review fixes: the snapshot build step removes any earlier sidecar bef…
Jarred-Sumner Aug 9, 2026
483a7e0
Review fixes: env-read recording ends when the dump reports it, so re…
Jarred-Sumner Aug 9, 2026
b7781d3
[autofix.ci] apply automated fixes
autofix-ci[bot] Aug 9, 2026
6da9699
Review fixes: the parent-death watchdog is re-installed for the resto…
Jarred-Sumner Aug 9, 2026
0cae3ad
ParentDeathWatchdog: the event-loop-installed flag only exists on macOS
Jarred-Sumner Aug 9, 2026
a928c12
[autofix.ci] apply automated fixes
autofix-ci[bot] Aug 9, 2026
c0cf359
Review fixes: the HTTP client thread is stopped before the freeze and…
Jarred-Sumner Aug 9, 2026
3b18d6a
Review fixes: TLS verification and S3 credentials derived from the bu…
Jarred-Sumner Aug 10, 2026
98d5d0a
[autofix.ci] apply automated fixes
autofix-ci[bot] Aug 10, 2026
0a73f78
Review fixes: the Linux fs.watch reader is stopped before the freeze …
Jarred-Sumner Aug 10, 2026
bea7083
The placement predicate reads the payload trailer itself: a weak refe…
Jarred-Sumner Aug 10, 2026
3df566b
Review fixes: WebKit's mimalloc routing is off under ASAN like every …
Jarred-Sumner Aug 10, 2026
01164bb
[autofix.ci] apply automated fixes
autofix-ci[bot] Aug 10, 2026
6b8b084
Merge origin/main into claude/lowmem-cc
Jarred-Sumner Aug 10, 2026
50f429b
Review fixes: Bun.cwd and Bun.enableANSIColors reified during the bui…
Jarred-Sumner Aug 10, 2026
bd8fec0
Merge remote-tracking branch 'origin/claude/lowmem-cc' into claude/lo…
Jarred-Sumner Aug 10, 2026
df08073
The allocator gets its own placement predicate; bun_is_compiled_execu…
Jarred-Sumner Aug 10, 2026
78fe79e
WebKit pin: review batches 3-11 (guards, pacing accounting, side-tabl…
Jarred-Sumner Aug 10, 2026
84590a5
Review fixes: a strict snapshot build refuses servers and UDP sockets…
Jarred-Sumner Aug 10, 2026
d9397bf
[autofix.ci] apply automated fixes
autofix-ci[bot] Aug 10, 2026
bcef003
WebKit pin: snapshot butterflies grow by copying, weak-set reset with…
Jarred-Sumner Aug 10, 2026
77213c0
Merge remote-tracking branch 'origin/claude/lowmem-cc' into claude/lo…
Jarred-Sumner Aug 10, 2026
0eea1d6
Review fixes: the ELF payload header is read as bytes (it is only pag…
Jarred-Sumner Aug 10, 2026
9e16744
Review fixes: the executable's own path is derived per process, so a …
Jarred-Sumner Aug 10, 2026
d8adb52
test: a restored process holds far less private memory than one build…
Jarred-Sumner Aug 10, 2026
56d6624
[autofix.ci] apply automated fixes
autofix-ci[bot] Aug 10, 2026
c716a93
test: build and restore under JSC's GC verifier; docs: engine options…
Jarred-Sumner Aug 10, 2026
5b55d95
Merge remote-tracking branch 'origin/claude/lowmem-cc' into claude/lo…
Jarred-Sumner Aug 10, 2026
9f25e16
[autofix.ci] apply automated fixes
autofix-ci[bot] Aug 10, 2026
3d37dcc
test: every Intl object created before the freeze is used after resto…
Jarred-Sumner Aug 10, 2026
100f1c7
Merge remote-tracking branch 'origin/claude/lowmem-cc' into claude/lo…
Jarred-Sumner Aug 10, 2026
863356b
[autofix.ci] apply automated fixes
autofix-ci[bot] Aug 10, 2026
4b60129
Review fixes: only the main thread's loop acts on a snapshot request,…
Jarred-Sumner Aug 10, 2026
1d0be5d
Merge remote-tracking branch 'origin/claude/lowmem-cc' into claude/lo…
Jarred-Sumner Aug 10, 2026
2146cc0
[autofix.ci] apply automated fixes
autofix-ci[bot] Aug 10, 2026
6a5592c
Review fixes: FreeBSD keeps bun_is_compiled_executable; the snapshot …
Jarred-Sumner Aug 10, 2026
e7d8d6b
Merge remote-tracking branch 'origin/claude/lowmem-cc' into claude/lo…
Jarred-Sumner Aug 10, 2026
91c5965
[autofix.ci] apply automated fixes
autofix-ci[bot] Aug 10, 2026
b0f9f74
test: build-flow snapshot tests move to their own file, next to the c…
Jarred-Sumner Aug 10, 2026
2efc31f
Merge remote-tracking branch 'origin/claude/lowmem-cc' into claude/lo…
Jarred-Sumner Aug 10, 2026
036cbfd
WebKit pin: Intl.Collator, DateTimeFormat and RelativeTimeFormat reop…
Jarred-Sumner Aug 10, 2026
8f0cd34
test: the exited-early assertion follows the reworded manual-mode mes…
Jarred-Sumner Aug 10, 2026
77406a5
Review fixes: the hard-link test copies when tmp is on another filesy…
Jarred-Sumner Aug 10, 2026
e6047ee
[autofix.ci] apply automated fixes
autofix-ci[bot] Aug 10, 2026
da8b913
Review fixes: the hand-written node:fs bindings are gated like the ge…
Jarred-Sumner Aug 10, 2026
d64d50b
Merge remote-tracking branch 'origin/claude/lowmem-cc' into claude/lo…
Jarred-Sumner Aug 10, 2026
679dfad
[autofix.ci] apply automated fixes
autofix-ci[bot] Aug 10, 2026
0ba22b8
test: SharedArrayBuffers created before the freeze — contents, aliasi…
Jarred-Sumner Aug 10, 2026
9b1c5c0
Merge remote-tracking branch 'origin/claude/lowmem-cc' into claude/lo…
Jarred-Sumner Aug 10, 2026
19790e9
[autofix.ci] apply automated fixes
autofix-ci[bot] Aug 10, 2026
617f19e
test: stdin's tty reader on a high descriptor number survives restore…
Jarred-Sumner Aug 10, 2026
598b653
Merge remote-tracking branch 'origin/claude/lowmem-cc' into claude/lo…
Jarred-Sumner Aug 10, 2026
de191db
[autofix.ci] apply automated fixes
autofix-ci[bot] Aug 10, 2026
4e17311
WebKit pin: every Intl object reopens its ICU state in a restored pro…
Jarred-Sumner Aug 10, 2026
19aaeb8
Merge remote-tracking branch 'origin/claude/lowmem-cc' into claude/lo…
Jarred-Sumner Aug 10, 2026
fe50430
Review fixes: Bun.write and file-backed Bun.file reads are gated duri…
Jarred-Sumner Aug 10, 2026
5c37d1a
[autofix.ci] apply automated fixes
autofix-ci[bot] Aug 10, 2026
b45c24e
Review fixes: the two snapshot test files share one harness (support …
Jarred-Sumner Aug 10, 2026
46afcc2
Merge remote-tracking branch 'origin/claude/lowmem-cc' into claude/lo…
Jarred-Sumner Aug 10, 2026
48408cf
[autofix.ci] apply automated fixes
autofix-ci[bot] Aug 10, 2026
dc1f185
Bun.build: snapshot options apply when compilation comes from the bun…
Jarred-Sumner Aug 10, 2026
f201d80
Merge remote-tracking branch 'origin/claude/lowmem-cc' into claude/lo…
Jarred-Sumner Aug 10, 2026
0da8a1d
A restored process ends its run through the same tail as a normal boo…
Jarred-Sumner Aug 10, 2026
d0e7633
[autofix.ci] apply automated fixes
autofix-ci[bot] Aug 10, 2026
3ce420b
A restored process adopts the IPC channel of the launch that spawned …
Jarred-Sumner Aug 10, 2026
ee0ab2f
Merge remote-tracking branch 'origin/claude/lowmem-cc' into claude/lo…
Jarred-Sumner Aug 10, 2026
02c5914
[autofix.ci] apply automated fixes
autofix-ci[bot] Aug 10, 2026
80090d8
Review fixes: a worker counts as live from before its thread is spawn…
Jarred-Sumner Aug 10, 2026
27da7aa
Merge the automated formatting commit
Jarred-Sumner Aug 10, 2026
c4239f5
test: the waiter fixture spawns sh rather than bun, which would inher…
Jarred-Sumner Aug 10, 2026
9146936
Census: the indexed window of array-storage butterflies starts after …
Jarred-Sumner Aug 10, 2026
f77d5af
Review fixes: restore-time hangups are delivered with no borrow of th…
Jarred-Sumner Aug 10, 2026
e71ad26
Docs sit on the functions they describe; the store's restore-time rea…
Jarred-Sumner Aug 10, 2026
48f0243
process.send/disconnect/channel reified during the build are refreshe…
Jarred-Sumner Aug 10, 2026
435a4fb
Review fixes: the IPC adoption takes its borrow of the env map after …
Jarred-Sumner Aug 10, 2026
5a6e515
A restored process arms the runtime's own recursion guard (a thread-l…
Jarred-Sumner Aug 10, 2026
0e58635
Bun.build: snapshot is rejected for standalone HTML output too, match…
Jarred-Sumner Aug 10, 2026
d9d2c5f
A restored process takes its time zone from the launch (TZ, or the ma…
Jarred-Sumner Aug 10, 2026
9eb83f7
docs: the time zone is among what a launch re-derives
Jarred-Sumner Aug 10, 2026
414c4c5
Bun.s3 and the stdio blobs reified during the build are remade for th…
Jarred-Sumner Aug 10, 2026
2e32463
WebKit pin: the series rebased onto current WebKit main (same content…
Jarred-Sumner Aug 10, 2026
18cccab
WebKit pin line restored (a comment cleanup removed the whole line)
Jarred-Sumner Aug 10, 2026
8d1d4a6
Bun.redis reified during the build is remade for the launch like s3 (…
Jarred-Sumner Aug 10, 2026
c8a9003
Review fixes: the restore-time zone refresh clears before it sets, so…
Jarred-Sumner Aug 10, 2026
565974d
The -Wformat push/pop pairs sit outside every #if, so they balance in…
Jarred-Sumner Aug 10, 2026
df2aa00
[autofix.ci] apply automated fixes
autofix-ci[bot] Aug 10, 2026
3b743fc
Review fixes: S3-backed blobs are network I/O for the snapshot gate (…
Jarred-Sumner Aug 10, 2026
32622ca
Merge remote-tracking branch 'origin/claude/lowmem-cc' into claude/lo…
Jarred-Sumner Aug 10, 2026
fffdeb8
fork-detection re-init: drop the generation reset that the unconditio…
Jarred-Sumner Aug 10, 2026
3398aef
Snapshot writer: every owned mapping is recorded even if nothing in i…
Jarred-Sumner Aug 11, 2026
caacb7b
[autofix.ci] apply automated fixes
autofix-ci[bot] Aug 11, 2026
a1caca4
Merge branch 'main' into claude/lowmem-cc
alii Aug 11, 2026
e50905a
Snapshot: gate the S3 client entries and Blob delete()/stat(); no-orp…
Jarred-Sumner Aug 11, 2026
b98655b
Merge remote-tracking branch 'origin/claude/lowmem-cc' into claude/lo…
Jarred-Sumner Aug 11, 2026
403c52b
Tooling: the new-cells census iterates blocks inside the HeapIteratio…
Jarred-Sumner Aug 11, 2026
dfc2ab0
Snapshot: shadowed .env values are expanded like their siblings; tool…
Jarred-Sumner Aug 11, 2026
534e364
Snapshot: heap-resident thread ids (timers, VmHandle debug check, sta…
Jarred-Sumner Aug 11, 2026
dc8de9d
VmHandle: update the remaining debug-only reader of the js-thread field
Jarred-Sumner Aug 11, 2026
92748c7
Snapshot: repeated .env keys keep the last stash; remake diagnostic c…
Jarred-Sumner Aug 11, 2026
4142b2c
Snapshot: refuse while the file I/O watcher thread exists; a launch w…
Jarred-Sumner Aug 11, 2026
a19d2a9
[autofix.ci] apply automated fixes
autofix-ci[bot] Aug 11, 2026
27bca85
Tooling: keep the profiler enable inside the BUN_MEMDEBUG guard
Jarred-Sumner Aug 11, 2026
47e8cf0
Merge origin/claude/lowmem-cc
Jarred-Sumner Aug 11, 2026
ac9ab52
Snapshot: envGate names are limited to 4096 bytes with an error at ta…
Jarred-Sumner Aug 11, 2026
572d2a8
Snapshot: time zone names of any length are honored at restore, as at…
Jarred-Sumner Aug 11, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions docs/bundler/executables.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,14 @@ Bytecode compilation moves parsing overhead for large input files from runtime t

<Note>Bytecode compilation supports both `cjs` and `esm` formats when used with `--compile`.</Note>

### Startup snapshots (experimental)

`--snapshot` goes one step further than bytecode: after producing the executable, `bun build` runs it once and embeds a snapshot of its started-up state — modules evaluated, startup objects built, JIT code included — so later launches resume from that point instead of booting. See [Startup Snapshots](/bundler/startup-snapshots).

```bash icon="terminal" terminal
bun build --compile --bytecode --snapshot ./app.ts --outfile myapp
```

### What do these flags do?

The `--minify` argument reduces the size of the transpiled output code. For a large application, this can save megabytes of space. For smaller applications, it might still improve start time a little.
Expand Down
159 changes: 159 additions & 0 deletions docs/bundler/startup-snapshots.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
---
title: Startup Snapshots
description: Embed a snapshot of your program's started-up state in a compiled executable, so every launch resumes instead of booting
---

A startup snapshot is a build-time optimization for [single-file executables](/bundler/executables). `bun build` runs your executable once, and when it has finished starting up, the runtime writes out the state of the whole process — every module loaded and evaluated, every object your startup code built, JIT-compiled code included — and embeds it in the executable. Every later launch maps that snapshot back into memory and continues from there. The work your program does while starting up happens once, on the build machine, instead of on every launch.

```bash icon="terminal" terminal
bun build --compile --snapshot ./app.ts --outfile myapp
```

This is experimental. It is available on macOS and Linux (glibc).

## What it saves

[Bytecode caching](/bundler/bytecode) removes parsing and compiling from startup; the modules still have to be evaluated on every launch, and everything they build — parsers, tables, plugin registries, the object graph a framework assembles before it does anything — is built again each time. A snapshot removes that too: a launch costs what it takes to map a file, and the snapshotted memory is shared between concurrent processes and stays untouched on disk until the program writes to it.

Whether it is worth it depends on how much your program does before it becomes useful. A tool that imports a large dependency graph, or a long-running program that spends its first second building state, benefits. A small script does not.

## Command-line tools

For a program that starts, does one job and exits, the requirement is that the job runs _after_ the snapshot is restored, not while the modules are being loaded. Import everything at the top level and put the program itself in `Bun.startupSnapshot.main()`:

```ts cli.ts icon="/icons/typescript.svg"
import { format } from "./formatter"; // the expensive part: this, and everything it imports, ends up in the snapshot

Bun.startupSnapshot.main(async () => {
// Runs in every launch — after the snapshot has been restored, when there is one — with the
// launch's own argv, cwd, environment and stdio.
const [file] = process.argv.slice(2);
process.stdout.write(await format(file));
});
```

`main()` calls the function immediately in a launch that has no snapshot, stores it in the launch that takes the snapshot, and calls it after the restore in every launch that resumes from one. Because nothing but imports runs before the snapshot is taken, there is nothing machine-specific in it, and the default build settings need no adjustment:

```bash icon="terminal" terminal
bun build --compile --bytecode --snapshot ./cli.ts --outfile fmt
```

### WebAssembly

A WebAssembly module compiled and instantiated before the snapshot is taken is in it, compiled code and linear memory included, so a tool built around a large wasm module (a compiler or formatter compiled to wasm) starts without compiling it. Code that JavaScriptCore compiles in the background while the program runs is included too, so exercising the module's main entry point once before the snapshot is taken (with representative input) puts the tiered-up code in the snapshot rather than leaving each launch to produce it again.

## Long-running programs

A server or an interactive program keeps running after it starts, so its snapshot is taken at the point where startup is over. By default (`--snapshot`, or `--snapshot=auto`) the runtime decides: it evaluates the entry point, lets whatever that started run to completion, and takes the snapshot once nothing is pending. Timers that are still armed at that point survive, with their remaining time preserved across the restore. Nothing in the program has to change.

A program that wants to choose the moment itself — for example because it wants to release things right before, or because its startup involves timers — builds with `--snapshot=manual` and calls `take()` when it is ready:

```ts app.ts icon="/icons/typescript.svg"
import { boot } from "./app";

const app = await boot();

process.on("restore", () => {
// Runs first thing in every launch that resumes from the snapshot, before the first event-loop
// turn: re-read whatever depends on the machine, re-open what has to be open.
app.reload();
});

// Returns immediately in every process except the one the build runs to take the snapshot.
Bun.startupSnapshot.take({
timers: "keep", // or "cancel"; by default a manual snapshot refuses while timers are armed
envGate: ["APP_MODE"], // launches whose values for these differ from the build's boot normally
});
```

`process.on("restore")` is available in both modes. Its listeners run before anything else in a restored launch, so state that must be recomputed per machine can be recomputed there; `main()` functions run after them.

## What a restored launch gets from its environment

The runtime refreshes what it owns before your code runs again: `process.argv`, `process.env`, the working directory, `process.pid`, the clocks (`performance.now()`, `process.uptime()`), the time zone, every source of randomness, timers, the DNS cache, its own threads, standard input and output, which are the launcher's, the IPC channel to a parent that spawned the launch with one (`process.send`), and the defaults it derives from the environment, such as the default S3 client and TLS verification. Files and sockets that were open when the snapshot was taken are gone; the program receives hangups for them after `"restore"`.

What it cannot refresh is anything your code derived from those before the snapshot was taken:

- A value read from `process.env`, `os.homedir()`, `process.cwd()` and the like and kept in a variable is the build machine's value in every launch. Holding a reference to `process.env` itself is fine — it is updated in place — but a copy of it (`{ ...process.env }`, `dotenv`-style merging into another object) is not.
- Random values and timestamps produced before the snapshot (an id generated at startup, `Date.now()` stored in a variable) are identical in every launch.
- Anything computed from files or the network before the snapshot is whatever the build machine had.
- `.env` files are read when the snapshot is built, not at launch: a launch gets the build's `.env` values (the launch's own environment still takes precedence), and any `${VAR}` references in them were expanded against the build's environment.
- The JavaScript engine's own options (`BUN_JSC_*` variables) are read once when the engine starts, so a snapshot carries the build's; setting them when launching a snapshot has no effect.

When the snapshot is written, the build prints which environment variables were read by name before it, and every place `process.env` was copied, with the code that did it. Each entry is either something to move into `main()` or a `"restore"` listener, or a variable that genuinely changes what the program is — those go in `envGate`, and a launch whose value differs boots normally.

## What the build may do

While the snapshot is being taken, operations whose result would be frozen into every launch are refused: `fetch()` and other network use, `node:fs`, subprocesses, DNS and sockets throw an error saying so. If the program fails because of that, or never becomes quiet, no snapshot is taken and the build fails, saying what happened; the executable it built is left in place, so it can still be used or the step re-run with different settings.

Programs that legitimately read their own files or run helpers while starting up can be allowed to:

| Setting | Allowed while the snapshot is taken |
| -------------------------------- | ---------------------------------------------------------------- |
| `--snapshot-io=strict` (default) | Nothing that touches the machine. |
| `--snapshot-io=local` | Files, subprocesses, local sockets, name resolution. |
| `--snapshot-io=network` | The above, and the network. What it answered is in the snapshot. |

With either of the last two, the build prints every operation it allowed, attributed to the code that performed it, so what went into the snapshot can be reviewed.

## Building in CI

The snapshot has to be taken on the platform the executable is for, because taking it means running the executable. The snapshot step therefore also works by itself, on an executable built earlier, and modifies it in place; running it again replaces the previous snapshot.

```bash icon="terminal" terminal
# On any machine: build for each target
bun build --compile --target=bun-linux-x64 ./app.ts --outfile dist/app-linux-x64
bun build --compile --target=bun-darwin-arm64 ./app.ts --outfile dist/app-darwin-arm64

# On a Linux x64 machine, and on an Apple silicon machine respectively:
bun build --snapshot --outfile dist/app-linux-x64
bun build --snapshot=manual --snapshot-io=local --outfile dist/app-darwin-arm64
```

Take snapshots in an environment that holds nothing you would not ship: the snapshot is a copy of the process's memory, and whatever the build environment exposed to the program — variables, files, credentials — is in it.

## `Bun.build()`

The programmatic API takes the same thing as a top-level option. It requires `compile`, and the executable is built for the machine running the build.

```ts build.ts icon="/icons/typescript.svg"
await Bun.build({
entrypoints: ["./app.ts"],
compile: { outfile: "./myapp" },
snapshot: true, // or { mode: "auto" | "manual", io: "strict" | "local" | "network" }
});
```

## When a launch does not use its snapshot

A launch uses its snapshot only when it can do so exactly: the same executable, the same operating system build and system libraries, a CPU with the features the snapshot was taken on, and agreement on every `envGate` variable. Otherwise it boots normally — the program behaves the same either way, it just starts up the slow way. `Bun.startupSnapshot.epoch()` returns `0` in a launch that booted normally and a positive number in one that resumed.

The snapshot is embedded in the executable as it is, and a launch maps the executable's own pages: nothing is unpacked, and nothing is written anywhere. It makes the executable larger by the size of the snapshotted state — typically tens of megabytes for a tool that loads a large dependency graph — and the build prints the size. For debugging, a launch prefers a `<name>.snapshot` file placed next to the executable over the embedded one, so an alternative snapshot can be tried without re-embedding it.

## Limitations

- A launch that resumes from a snapshot runs with address-space layout randomization disabled, because the snapshot has to be mapped at the addresses it was taken at.
- Native addons and `bun:ffi` libraries that keep state of their own, and worker threads that are alive when the snapshot is taken, are not supported.
- macOS and glibc Linux only; on other platforms and builds `--snapshot` reports that it is unavailable.
- Programs that were not written with the snapshot in mind may hold state of the kinds described above; the reports the build prints are the way to find it.

## Reference

**`bun build`**

| Flag | Meaning |
| -------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `--snapshot`, `--snapshot=auto` | With `--compile`: run the executable once and embed its snapshot, taken when startup has drained. Without entrypoints: do this to the existing `--outfile`. |
| `--snapshot=manual` | The same, but the program calls `Bun.startupSnapshot.take()` to say when. |
| `--snapshot-io=strict\|local\|network` | What the program may do while the snapshot is taken (default `strict`). |

**`Bun.startupSnapshot`**

| | |
| --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `main(fn)` | The program. Called now in an ordinary launch, after the restore in a launch that resumes from a snapshot, and not at all in the run that takes the snapshot. |
| `take(options?)` | Manual mode: take the snapshot now. `options.timers` is `"keep"` or `"cancel"`; `options.envGate` is a list of variable names. Returns immediately in every other process. |
| `isBuildingSnapshot()` | `true` only in the run that takes the snapshot. |
| `epoch()` | `0` in a launch that booted normally; otherwise the number of times this process has been resumed. |
| `reclean()` | In a resumed launch, hand pages the program wrote and then restored to their original contents back to the shared snapshot. Optional; useful for programs that idle for a long time. |
| `process.on("restore", fn)` | Runs first in every launch that resumes from a snapshot. |
2 changes: 1 addition & 1 deletion docs/docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,7 @@
{
"group": "Single File Executable",
"icon": "binary",
"pages": ["/bundler/executables"]
"pages": ["/bundler/executables", "/bundler/startup-snapshots"]
},
{
"group": "Extensions",
Expand Down
59 changes: 59 additions & 0 deletions packages/bun-types/bun.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3222,6 +3222,15 @@ declare module "bun" {
* ```
*/
compile?: boolean | Bun.Build.CompileTarget | CompileBuildOptions;
/**
* Snapshots (experimental; requires `compile`): after producing the executable, run it once and
* embed a snapshot of its started-up state, so later launches resume instead of booting. `true`
* takes the snapshot once startup work drains (`bun build --snapshot`); use `mode: "manual"` when
* the app calls `Bun.startupSnapshot.take()` itself, and `io` to let the build touch this machine
* (`"local"`: files, subprocesses, local sockets; `"network"`: the network too) — every use is
* reported when the snapshot is written. Default `io` is `"strict"`.
*/
snapshot?: boolean | { mode?: "auto" | "manual"; io?: "strict" | "local" | "network" };
}

interface CompileBuildOptions {
Expand Down Expand Up @@ -4943,6 +4952,56 @@ declare module "bun" {
function memoryFootprint(): number | undefined;
}

/**
* Startup snapshots (experimental) — see `bun build --snapshot`. A snapshot of the started-up
* process is embedded in a compiled executable, and later launches resume from it instead of
* booting; `process.on("restore")` runs first thing in such a launch.
*/
namespace startupSnapshot {
/**
* The program itself, for tools that start, do a job and exit. Called immediately in a launch
* that has no snapshot; stored — not called — in the run that takes the snapshot, so the snapshot
* holds the loaded program; called after `"restore"` in a launch that resumes from the snapshot,
* with that launch's argv, cwd, environment and stdio. A snapshot taken with a `main()` registered
* is used for every invocation, whatever the arguments.
*/
function main(program: () => unknown): void;

/**
* With `--snapshot=manual`, the point in startup at which the snapshot is taken. In the run
* `bun build` makes for that purpose this never returns: the process exits once the snapshot is
* written (or with a message naming what kept it busy). In every other process it returns at
* once, so it can be called unconditionally. With `--snapshot` (auto) the runtime picks the
* moment itself and a call only contributes the options.
*/
function take(options?: {
/**
* Timers still armed when the process goes quiet: `"keep"` lets them survive with their
* remaining time preserved across the restore; `"cancel"` drops them. By default armed
* timers keep a manual snapshot from being taken; auto mode keeps them.
*/
timers?: "keep" | "cancel";
/**
* Environment variables the snapshotted startup depended on. A launch whose values for these
* differ from the build's boots normally instead of resuming from the snapshot.
*/
envGate?: string[];
}): void;

/** True only in the run `bun build --snapshot` makes to take the snapshot. */
function isBuildingSnapshot(): boolean;

/** 0 in a process that booted normally; otherwise how many times this process has been resumed from a snapshot. */
function epoch(): number;

/**
* In a process resumed from a snapshot: hand back to the shared snapshot any page this process
* wrote and then restored to its original contents. Cheap; call it once startup work has
* settled. A no-op elsewhere.
*/
function reclean(): void;
}

type DigestEncoding = "utf8" | "ucs2" | "utf16le" | "latin1" | "ascii" | "base64" | "base64url" | "hex";

/**
Expand Down
Loading
Loading