Jeder Agent installierte sich beim Spawn sein eigenes bun
Pro-Agent-Tooling-Isolation klang nach Sicherheit. Auf einem Single-User-Host war sie 100MB und eine Minute Spawn-Zeit, die nichts isolierten. Was wegfiel, als ich die echte Trennlinie ernst nahm.
Ich habe einem Spawn zugeschaut. Vom apes agents spawn bis der Agent tatsächlich auf eine Chat-Nachricht antwortete vergingen rund sechzig Sekunden. Den größten Teil davon verbrachte das Setup-Skript damit, bun herunterzuladen und in das frische Home des Agents zu installieren. Jedes Mal. Pro Agent.
Das war so gebaut, und ich hatte es selbst so gebaut. Hier ist, warum ich es wieder rausgeworfen habe.
Wie es vorher war
Beim Spawn bekommt ein Agent einen eigenen macOS-User und ein eigenes Home-Verzeichnis. Das Setup-Skript zog dann in dieses Home eine eigene bun-Runtime — circa 100MB, eine knappe Minute Arbeit. Danach lief der Agent komplett auf seiner eigenen Toolchain, ohne irgendetwas mit dem Host oder anderen Agents zu teilen.
Die Annahme dahinter: Agents müssen voneinander isoliert sein, und Isolation heißt, dass auch das Tooling nicht geteilt wird. Jeder seine eigene Runtime. Kein gemeinsames Binary, an dem ein Agent etwas verändern könnte, das einen anderen trifft. Isolation by default, runter bis zur Runtime.
Das klang richtig, als ich es schrieb. Es war auch nicht falsch — nur an der falschen Stelle.
Warum ich es rausgenommen habe
Auf einem Single-User-Host sind node, apes und die Chat-Bridge ohnehin da. Sie liegen im System, der User hat sie installiert, sie ändern sich nicht, weil ein Agent sie nicht anfasst. Ein eigenes bun pro Agent dupliziert eine Datei, die längst existiert, in ein Verzeichnis, das niemand sonst liest.
Die eigentliche Trennlinie zwischen zwei Agents auf dieser Maschine ist nicht ihre Runtime. Es ist die macOS-uid und der Privilege-Helper (escapes), der jeden privilegierten Crossing einzeln durchwinkt. Das ist die Isolationsgrenze. Alles davor zu kopieren schützt gegen nichts, das die uid nicht schon abdeckt — es kostet nur Spawn-Zeit und Plattenplatz.
Ich hatte Tooling-Isolation gebaut, als wäre Isolation ein Stapel Kopien. Sie ist aber eine Grenze. Und die Grenze lag woanders.
Wie es jetzt aussieht
Statt pro Agent eine Runtime zu installieren, fängt das Setup ab, wo die Tools auf dem Host liegen, und backt diese Pfade in die launchd-Plist des Agents:
# Capture the host's tool locations once, dedupe, bake into the plist PATH
agent_path=$(
for bin in node openape-chat-bridge apes; do
cmd=$(command -v "$bin" 2>/dev/null) || continue
dirname "$cmd"
done | awk '!seen[$0]++' | paste -sd: -
)
command -v für jedes der drei Tools, dirname auf das Resultat, awk '!seen[$0]++' als Dedupe (zwei Tools liegen oft im selben Verzeichnis), paste zu einem PATH-String. Der landet in den EnvironmentVariables der Plist, und der Agent findet seine Tools dort, wo der Host sie hat.
Der Spawn fällt damit von rund sechzig Sekunden auf rund vier. Die vier Sekunden sind ohnehin nicht das Tooling, das ist der Rest des Setups. Pro Agent fallen die 100MB komplett weg.
Was weggefallen ist
Die per-Agent-bun-Installation. Vollständig.
Der ehrliche Trade-off, den ich nicht verstecken will: alle Agents teilen sich jetzt eine Bridge-Version. Wenn ich die Bridge auf dem Host update, ändert sie sich für alle gleichzeitig. In einem Multi-Tenant-Setup, in dem fremde Agents nebeneinander laufen, wäre das die falsche Entscheidung — dort will man die Tooling-Versionen tatsächlich auseinanderhalten. Auf meinem Single-User-Host ist es genau das, was ich will: ein Update, alle aktuell.
Der client-seitige Reflex wäre gewesen, die Isolation drinzulassen und sie als Feature zu verkaufen. Sie tat nur nichts.
Der Schnitt
isolation by default klingt nach einer sicheren Voreinstellung. Sie war keine — sie war eine Annahme aus der Zeit, als ein Agent auf der Maschine lief und isoliert einfach dupliziert alles hieß. Sobald die Frage konkret wird — Isolation wovon, gegen wen, an welcher Grenze — fällt die Antwort woanders hin: die Grenze ist die uid und der escapes-Helper, nicht das mitkopierte bun. Was davor liegt, ist keine Sicherheit, sondern Reibung mit Sicherheits-Anstrich.
Defaults tragen die Welt mit sich, in der sie geschrieben wurden. Das Gefährliche an by default ist nicht der Default — es ist, dass man aufhört zu fragen, wofür er da war.