Repository navigation
With Bun's globalStore (Bun ≥ 1.3.14), agent mode patches and rolls back every other project sharing the store, and vex attests unpatched transitive copies as not_affected #635
Description
[agent] Found by the scheduled Bun bug-hunt routine (ledger #306).
Summary
Bun 1.3.14 added a machine-wide store for the isolated linker: [install] globalStore = true in bunfig.toml, or BUN_INSTALL_GLOBAL_STORE=1. With it on, each node_modules/.bun/<name>@<version> entry is a symlink into <BUN_INSTALL_CACHE_DIR>/links/<name>@<version>-<hash>/, and every project on the machine links to the same directory. socket-patch doesn't recognize this store, and that has two effects:
- Agent mode writes into the shared store.
scan --mode agent/applyfollowsnode_modules/<dep>into<cache>/links/…and rewrites the file there. Every other project using that store is then patched, including ones that never ran socket-patch.rollbackin one project silently unpatches all of them, including projects that recorded the patch in their own.socket/manifest.json. That project'svexthen fails withnot_applied. Fix agent apply writing into shared package stores (#332, #361) #486 (Agent mode writes the patch into PDM's shared install cache when PDM 2.0–2.12 installs packages as directory symlinks (install.cache + symlink) #332, Agent-mode apply and rollback on a pnpm project with enableGlobalVirtualStore patch (and unpatch) every other project that shares the store #361) added exactly this guard for pnpm's global virtual store and PDM's cache, butpatch/shared_store.rsonly knows those two layouts. - Transitive packages are invisible. The
.bunstore walk keeps only real directories, so it skips every symlinked entry. Agent mode reports a transitive dependency aspackage_not_installed, leaves it unpatched, and still exits 0. Worse, hostedvexnever looks at the installed copy: right afterscan --mode hosted(before reinstalling), it attests an unpatched transitiveis-numberasverified/not_affected. WithglobalStore = falsethe same tree correctly givesnot_applied. This is the Bun isolated linker: transitive packages under node_modules/.bun are "not installed" in agent mode, and scan --mode agent exits 0 with them unpatched #366 / With Bun's isolated linker,vexattests a hosted patch as not_affected (verified) while the installed copy under node_modules/.bun is still unpatched (v5 regression) #405 failure, brought back by the global store.
Impact
- Patching one project silently changes the code other projects run, and rolling it back silently removes patches other projects depend on. Nothing in the envelope mentions it (
status: success). - False VEX: a hosted project attests
not_affectedfor a transitive package that is installed unpatched. - Agent mode silently skips every transitive dependency of a globalStore project.
Repro (Linux, real Bun, local patch-API mock)
The mock serves the public-proxy routes (/patch/batch, by-package, view with blob contents, blob, /patch/package, the tarball route). Each patch prepends /* SOCKET-PATCHED */ to index.js. SOCKET_PROXY_URL and SOCKET_PATCH_SERVER_URL point at it.
# usage: repro-gs.sh <bun> <dir> <bunfig|env>
B=$1; D=$2; CFG=$3
rm -rf "$D"; mkdir -p "$D/p1" "$D/p2"; export BUN_INSTALL_CACHE_DIR="$D/cache"
for p in p1 p2; do
cd "$D/$p"
[ $p = p1 ] && deps='"left-pad":"1.3.0","is-odd":"3.0.1"' || deps='"left-pad":"1.3.0"'
printf '{"name":"%s","version":"1.0.0","dependencies":{%s}}' $p "$deps" > package.json
if [ $CFG = bunfig ]; then printf '[install]\nlinker = "isolated"\nglobalStore = true\n' > bunfig.toml
else printf '[install]\nlinker = "isolated"\n' > bunfig.toml; export BUN_INSTALL_GLOBAL_STORE=1; fi
"$B" install
done
readlink "$D/p1/node_modules/.bun/left-pad@1.3.0" # -> $D/cache/links/left-pad@1.3.0-6a490709ba3c5c8f
cd "$D/p2" && socket-patch scan --mode agent --json --yes # p2 records + applies left-pad
cd "$D/p1" && socket-patch scan --mode agent --json --yes # left-pad added; is-number skipped package_not_installed
node -e "const fs=require('fs');console.log(require.resolve('is-number',{paths:[fs.realpathSync(require.resolve('is-odd'))]}))" # unpatched copy
cd "$D/p1" && socket-patch rollback --json --yes
head -1 "$D/p2/node_modules/left-pad/index.js" # original bytes: p2's patch is gone
cd "$D/p2" && socket-patch vex --product pkg:npm/p2@1.0.0 --output v.json --json # exit 1, not_appliedOutput on main 045d7ec, Bun 1.4.2 (bunfig):
p2 agent scan: success [('pkg:npm/left-pad@1.3.0', 'added', None)]
p2 left-pad: /* SOCKET-PATCHED */
p1 agent scan: success [('pkg:npm/left-pad@1.3.0', 'added', None), ('pkg:npm/is-number@6.0.0', 'skipped', 'package_not_installed')]
p1 transitive is-number (loaded via is-odd): /*! <- unpatched
p1 rollback: success
p1 left-pad: /* This program is fre
p2 left-pad: /* This program is fre <- p2 never rolled back
p2 vex: error [('pkg:npm/left-pad@1.3.0', 'not_applied')] exit=1
Hosted false attestation (one project, linker = "isolated", globalStore = true, deps left-pad@1.3.0 + is-odd@3.0.1):
bun install
socket-patch scan --mode hosted --json --yes # bun.lock rewired, success, 2 redirected
socket-patch vex --product pkg:npm/p@1.0.0 --output v.json --json # before reinstallingglobalStore=true : partialFailure [('pkg:npm/is-number@6.0.0', 'verified'), ('pkg:npm/left-pad@1.3.0', 'not_applied')] <- is-number is still unpatched
globalStore=false: error [('pkg:npm/is-number@6.0.0', 'not_applied'), ('pkg:npm/left-pad@1.3.0', 'not_applied')]
After a fresh frozen install, overwriting the store copy of the patched is-number with the original bytes still gives verified for it, so vex never reads those copies.
Expected vs actual
- Expected: CLI_CONTRACT.md and
patch/shared_store.rssay agent apply/rollback refuse a package whose real location is a store shared by other projects, with an error naming the store and how to get a private copy. That's the Agent-mode apply and rollback on a pnpm project with enableGlobalVirtualStore patch (and unpatch) every other project that shares the store #361 behaviour for pnpm'senableGlobalVirtualStore.vexshould only attest a patch when every installed copy is patched (With Bun's isolated linker,vexattests a hosted patch as not_affected (verified) while the installed copy under node_modules/.bun is still unpatched (v5 regression) #405, Fix agent vex checking only one installed copy (#516) #517). - Actual: Bun's global store is patched and unpatched in place with
success. Transitive entries arepackage_not_installed, and hostedvexattests an unpatched copy asnot_affected.
OS × version (Linux sandbox; 2+ runs each)
| OS | Bun | config | agent: cross-project apply/rollback | agent: transitive skipped | hosted vex on unpatched transitive |
|---|---|---|---|---|---|
| Linux | 1.4.2 | bunfig globalStore = true |
fail | fail | fail (verified) |
| Linux | 1.4.2 | BUN_INSTALL_GLOBAL_STORE=1 |
fail | fail | untested |
| Linux | 1.3.14 | bunfig / env | fail / fail | fail | fail (verified) |
| Linux | 1.3.13 | bunfig globalStore = true (key not supported, so ignored) |
pass | pass | n/a |
| Linux | 1.4.2 / 1.3.14 | globalStore = false |
pass | pass | pass (not_applied) |
| macOS / Windows | — | — | untested (probe branches currently blocked) |
First bad Bun release: 1.3.14, the first build with globalStore / BUN_INSTALL_GLOBAL_STORE (absent from 1.3.13 and earlier). This isn't a socket-patch regression. It's a Bun layout socket-patch has never handled.
Suspect code
crates/socket-patch-core/src/patch/shared_store.rs:122(shared_store_of_blocking) recognizes only<store>/v<N>/links(pnpm) and PDM'spackages/<stem>/lib. Bun's<cache>/links/<name>@<ver>-<hash>/node_modules/<name>(with the cache's<name>@<ver>@@@1/*.npmentries besidelinks) has noSharedStoreKind.crates/socket-patch-core/src/crawlers/npm_crawler.rs:2143(list_pnpm_shaped_store_entries_sync) filters.bunentries byfile_type().is_dir(), which is false for a symlink. So every globalStore entry is dropped from the crawl, andvex/verify.rsnever sees those copies.
Activity
bun: run 10 ledger (#635)
mikolalysenko commentedon Oct 3, 2026
[agent] Triaged:
priority:p1(Bun). Not a duplicate. It's related to #599 (Bun isolated-linker vex checking orphaned.bunentries) and to the #361 pnpm global-virtual-store guard, but the cause here is separate:patch/shared_store.rsdefines onlyPdmPackageCacheandPnpmGlobalVirtualStore(line 35), so Bun's<cache>/links/<name>@<ver>-<hash>store has noSharedStoreKind, and the.bunstore walk incrawlers/npm_crawler.rskeeps only entries whose own file type is a directory, so symlinked globalStore entries are dropped. No open or merged PR addresses it. The fix needs both aBunGlobalStorekind (refuse agent apply/rollback, like #361) and a crawler that follows.bunentry symlinks for discovery and vex.Generated by Claude Code
mikolalysenko commentedon Oct 7, 2026
Claim-ID: agent/fix-bun-open-issues. Draft fix PR: #1009 (it covers all open
pm:bunissues).Refuse Bun global store copies and walk its links (#635)
Reword apply's Bun note for a global store project (#635)
Reconcile #599 with #635's Bun global store links
Judge orphaned Bun global store links by reachability (#599)
Merge origin/main into agent/fix-bun-open-issues