[{"data":1,"prerenderedAt":342},["ShallowReactive",2],{"blog-en-isolation-by-default-single-agent-default":3,"header-blog-translations-/en/blog/isolation-by-default-single-agent-default":339},{"id":4,"title":5,"author":6,"body":7,"date":321,"description":322,"draft":323,"extension":324,"image":325,"meta":326,"navigation":327,"path":328,"seo":329,"stem":330,"tags":331,"translationKey":337,"__hash__":338},"blog_en/blog/en/isolation-by-default-single-agent-default.md","Every Agent Installed Its Own bun at Spawn","Patrick Hofmann",{"type":8,"value":9,"toc":314},"minimark",[10,23,26,31,37,40,43,47,61,73,76,80,83,234,260,263,267,273,276,279,283,303,310],[11,12,13,14,18,19,22],"p",{},"I watched a spawn. From ",[15,16,17],"code",{},"apes agents spawn"," until the agent actually answered a chat message, about sixty seconds passed. Most of that the setup script spent downloading ",[15,20,21],{},"bun"," and installing it into the agent's fresh home. Every time. Per agent.",[11,24,25],{},"That was built that way, and I had built it that way myself. Here's why I took it out again.",[27,28,30],"h2",{"id":29},"how-it-was-before","How it was before",[11,32,33,34,36],{},"At spawn an agent gets its own macOS user and its own home directory. The setup script then pulled its own ",[15,35,21],{}," runtime into that home — about 100MB, a good minute of work. After that the agent ran entirely on its own toolchain, without sharing anything with the host or other agents.",[11,38,39],{},"The assumption behind it: agents have to be isolated from each other, and isolation means the tooling isn't shared either. Each its own runtime. No shared binary an agent could change in a way that hits another. Isolation by default, down to the runtime.",[11,41,42],{},"That sounded right when I wrote it. It also wasn't wrong — just in the wrong place.",[27,44,46],{"id":45},"why-i-took-it-out","Why I took it out",[11,48,49,50,53,54,57,58,60],{},"On a single-user host, ",[15,51,52],{},"node",", ",[15,55,56],{},"apes"," and the chat bridge are there anyway. They sit in the system, the user installed them, they don't change because an agent doesn't touch them. An own ",[15,59,21],{}," per agent duplicates a file that already exists, into a directory nobody else reads.",[11,62,63,64,67,68,72],{},"The actual dividing line between two agents on this machine isn't their runtime. It's the macOS uid and the privilege helper (",[15,65,66],{},"escapes","), which waves every privileged crossing through individually. ",[69,70,71],"em",{},"That"," is the isolation boundary. Copying everything in front of it protects against nothing the uid doesn't already cover — it only costs spawn time and disk space.",[11,74,75],{},"I had built tooling isolation as if isolation were a stack of copies. But it's a boundary. And the boundary was elsewhere.",[27,77,79],{"id":78},"how-it-looks-now","How it looks now",[11,81,82],{},"Instead of installing a runtime per agent, the setup captures where the tools sit on the host and bakes those paths into the agent's launchd plist:",[84,85,90],"pre",{"className":86,"code":87,"language":88,"meta":89,"style":89},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","# Capture the host's tool locations once, dedupe, bake into the plist PATH\nagent_path=$(\n  for bin in node openape-chat-bridge apes; do\n    cmd=$(command -v \"$bin\" 2>/dev/null) || continue\n    dirname \"$cmd\"\n  done | awk '!seen[$0]++' | paste -sd: -\n)\n","bash","",[15,91,92,101,112,141,181,196,228],{"__ignoreMap":89},[93,94,97],"span",{"class":95,"line":96},"line",1,[93,98,100],{"class":99},"sHwdD","# Capture the host's tool locations once, dedupe, bake into the plist PATH\n",[93,102,104,108],{"class":95,"line":103},2,[93,105,107],{"class":106},"sTEyZ","agent_path",[93,109,111],{"class":110},"sMK4o","=$(\n",[93,113,115,119,122,125,129,132,135,138],{"class":95,"line":114},3,[93,116,118],{"class":117},"s7zQu","  for",[93,120,121],{"class":106}," bin ",[93,123,124],{"class":117},"in",[93,126,128],{"class":127},"sfazB"," node",[93,130,131],{"class":127}," openape-chat-bridge",[93,133,134],{"class":127}," apes",[93,136,137],{"class":110},";",[93,139,140],{"class":117}," do\n",[93,142,144,147,150,154,157,160,163,166,169,172,175,178],{"class":95,"line":143},4,[93,145,146],{"class":106},"    cmd",[93,148,149],{"class":110},"=$(",[93,151,153],{"class":152},"s2Zo4","command",[93,155,156],{"class":127}," -v",[93,158,159],{"class":110}," \"",[93,161,162],{"class":106},"$bin",[93,164,165],{"class":110},"\"",[93,167,168],{"class":110}," 2>",[93,170,171],{"class":127},"/dev/null",[93,173,174],{"class":110},")",[93,176,177],{"class":110}," ||",[93,179,180],{"class":117}," continue\n",[93,182,184,188,190,193],{"class":95,"line":183},5,[93,185,187],{"class":186},"sBMFI","    dirname",[93,189,159],{"class":110},[93,191,192],{"class":106},"$cmd",[93,194,195],{"class":110},"\"\n",[93,197,199,202,205,208,211,214,217,219,222,225],{"class":95,"line":198},6,[93,200,201],{"class":117},"  done",[93,203,204],{"class":110}," |",[93,206,207],{"class":186}," awk",[93,209,210],{"class":110}," '",[93,212,213],{"class":127},"!seen[$0]++",[93,215,216],{"class":110},"'",[93,218,204],{"class":110},[93,220,221],{"class":186}," paste",[93,223,224],{"class":127}," -sd:",[93,226,227],{"class":127}," -\n",[93,229,231],{"class":95,"line":230},7,[93,232,233],{"class":110},")\n",[11,235,236,239,240,243,244,247,248,251,252,255,256,259],{},[15,237,238],{},"command -v"," for each of the three tools, ",[15,241,242],{},"dirname"," on the result, ",[15,245,246],{},"awk '!seen[$0]++'"," as dedupe (two tools often sit in the same directory), ",[15,249,250],{},"paste"," into one ",[15,253,254],{},"PATH"," string. That lands in the ",[15,257,258],{},"EnvironmentVariables"," of the plist, and the agent finds its tools where the host has them.",[11,261,262],{},"The spawn falls from about sixty seconds to about four. The four seconds aren't the tooling anyway, that's the rest of the setup. Per agent the 100MB fall away entirely.",[27,264,266],{"id":265},"what-fell-away","What fell away",[11,268,269,270,272],{},"The per-agent ",[15,271,21],{}," installation. Completely.",[11,274,275],{},"The honest trade-off I don't want to hide: all agents now share one bridge version. If I update the bridge on the host, it changes for all of them at once. In a multi-tenant setup, where foreign agents run side by side, that would be the wrong decision — there you do want to keep the tooling versions apart. On my single-user host it's exactly what I want: one update, all current.",[11,277,278],{},"The client-side reflex would have been to leave the isolation in and sell it as a feature. It just did nothing.",[27,280,282],{"id":281},"the-cut","The cut",[11,284,285,288,289,292,293,296,297,299,300,302],{},[15,286,287],{},"isolation by default"," sounds like a safe default. It was none — it was an assumption from the time when one agent ran on the machine and ",[69,290,291],{},"isolated"," simply meant ",[69,294,295],{},"duplicates everything",". As soon as the question gets concrete — isolation from what, against whom, at which boundary — the answer falls elsewhere: the boundary is the uid and the ",[15,298,66],{}," helper, not the copied-in ",[15,301,21],{},". What lies in front of that isn't security, but friction with a security coat of paint.",[11,304,305,306,309],{},"Defaults carry the world they were written in with them. The dangerous thing about ",[15,307,308],{},"by default"," isn't the default — it's that you stop asking what it was there for.",[311,312,313],"style",{},"html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":89,"searchDepth":103,"depth":103,"links":315},[316,317,318,319,320],{"id":29,"depth":103,"text":30},{"id":45,"depth":103,"text":46},{"id":78,"depth":103,"text":79},{"id":265,"depth":103,"text":266},{"id":281,"depth":103,"text":282},"2026-05-10","Per-agent tooling isolation sounded like security. On a single-user host it was 100MB and a minute of spawn time that isolated nothing. What fell away when I took the real dividing line seriously.",false,"md",null,{},true,"/blog/en/isolation-by-default-single-agent-default",{"title":5,"description":322},"blog/en/isolation-by-default-single-agent-default",[332,333,334,335,336],"OpenApe","AI Agents","Infrastructure","Tooling","Building in Public","isolation-by-default-single-agent-default","_UJzsQzuxdlULX59MA0Vaonw_9QyVJo0yQtyCNgL1os",{"en":340,"de":341},"/en/blog/isolation-by-default-single-agent-default","/de/blog/jeder-agent-installierte-sich-beim-spawn-sein-eigenes-bun",1779001886870]