Sandbox und Sicherheit
Warum Isolation hier zählt
Abschnitt betitelt „Warum Isolation hier zählt“Ein Agent liest Dateien, führt Shell-Befehle aus und ruft in deinem Namen externe APIs auf. Der Fehler, um den es geht, ist selten Böswilligkeit. Es ist ein Agent, der einen Pfad falsch liest und deinen Arbeitscheckout umschreibt, oder einer, der einen Branch pusht, den du noch nicht fertig geprüft hattest. Die Frage, die das Produkt beantworten muss, heißt also: Wie groß ist der Wirkungsradius, wenn der Agent sich irrt.
Control Center beantwortet das in Schichten, und die Schichten sind absichtlich unabhängig. Fehlt eine, halten die anderen. Das zählt, denn die äußerste Schicht, die Betriebssystem-Sandbox, ist die einzige bedingte: Sie hängt daran, dass der Host ein Backend anbietet, und unter Windows tut das kein Host.
Was einen Agenten tatsächlich begrenzt
Abschnitt betitelt „Was einen Agenten tatsächlich begrenzt“Fünf Dinge begrenzen einen Agentenlauf auf jedem Host, mit Sandbox und ohne. Keines davon ist eine Kernel-Grenze, und alle sind real. Wo der Host ein Backend anbietet, sitzt die Betriebssystem-Sandbox darüber, und „Was die Sandbox hinzufügt und wo sie aufhört“ weiter unten sagt, was sich dadurch ändert.
Ein eigener Copy-on-Write-Worktree. Jede Unterhaltung stellt einen eigenen
Checkout jedes verknüpften Repos bereit, per Copy-on-Write über das
mitgelieferte rift-Native geklont und nicht Byte für Byte kopiert. Isolation
ist dadurch billig genug, um Standard zu sein. Das registrierte Repository, das
du dem Arbeitsbereich hinzugefügt hast, ist nicht das Verzeichnis, in dem der
Agent arbeitet. Zwei Unterhaltungen, die dasselbe Repo anfassen, sehen die
Änderungen der anderen nicht, und keine von beiden bearbeitet deinen Checkout.
Die Körnung lohnt eine genaue Formulierung: Der Worktree gehört zur
Unterhaltung, nicht zum Agenten. Zwei Agenten in derselben Unterhaltung
bekommen getrennte Overlay-Arbeitsverzeichnisse, die sich das repos/ dieser
Unterhaltung teilen. Darum geht es, denn sie arbeiten gemeinsam an einer
Änderung, während ihre privaten Agentenverzeichnisse getrennt bleiben.
Copy-on-Write ist das einzige Backend, wo rift ausgeliefert wird. Ein
Dateisystem ohne Reflink, oder ein Datenverzeichnis auf einem anderen Volume
als das Repo, lässt die Bereitstellung scheitern, statt auf etwas anderes
zurückzufallen. Windows ist die Ausnahme. Dort gibt es kein
Copy-on-Write-Backend, also ist git worktree das Backend, kein stilles
Herabstufen.
Pfadbeschränkung in den Dateiwerkzeugen des eingebauten Harness. Die
Werkzeuge read, write, edit, apply_patch, search, find und
search_files des Harness lösen jeden Pfad gegen das Arbeitsverzeichnis des
Laufs plus gemeinsame Wurzeln auf, das Verzeichnis repos/ der Unterhaltung,
in dem die Worktrees liegen, und das Anhängeverzeichnis des Bereichs, wenn es
existiert, und lehnen alles ab, was außerhalb landet. Absolute Pfade werden nur
akzeptiert, wenn sie bereits innerhalb lagen. Ein Symlink im Worktree, der
hinauszeigt, wird aufgelöst und ebenfalls abgelehnt. Deshalb kann ein Agent
auch ganz ohne Sandbox nicht mit write nach ~/.ssh schreiben.
Die Werkzeugfläche des Modus. Der Modus
einer Unterhaltung entscheidet, welche Werkzeuge für den Lauf überhaupt
materialisiert werden. In den Nur-Lese-Modi (plan, review, orchestrate)
stehen die verändernden Werkzeuge schlicht nicht in der Registry, und die
Befehlsrichtlinie dieser Modi lehnt jeden verändernden Befehl sofort ab,
git commit, git push, npm install, docker und den Rest, statt
nachzufragen. Ein Werkzeug, das fehlt, lässt sich nicht aufrufen. Das
garantiert mehr als ein Werkzeug, das vorhanden ist und hinter einem Tor liegt.
Die einheitlichen Aktions-Schutzregeln. Jedes verändernde Werkzeug
deklariert die Effektklassen, die es erzeugen kann, und die Policy-Engine löst
jede davon vor dem Aufruf auf allow, prompt oder deny auf. Ein prompt ohne
verbundene zuständige Person wird abgelehnt. Eine Frage, die niemand
beantworten kann, ist nie ein Ja. Das Auflösungsmodell steht unter
Schutzregeln.
Vermittelte Zugangsdaten und eine bereinigte Umgebung. Zugangsdaten werden bei jedem Start ausgegeben und widerrufen, wenn der Lauf endet, und das GitHub-Token eines Laufs kann lesen, aber nicht pushen — siehe „Pushes laufen durch Control Center“ weiter unten. ACP, das bash des Harness und
Claude Code unter der nativen Umhüllung bekommen eine Umgebung, aus der die
Vektoren für Dynamic-Linker- und Interpreter-Injektion (LD_PRELOAD,
DYLD_INSERT_LIBRARIES, NODE_OPTIONS, GIT_SSH_COMMAND und ihresgleichen)
entfernt sind, bevor der Prozess startet. Claude Code über NoSandboxAdapter
erbt die Umgebung des Hosts.
Wo diese Grenzen dünner werden
Abschnitt betitelt „Wo diese Grenzen dünner werden“Die Dateiwerkzeuge des Harness laufen im Serverprozess, nicht in einem
Kindprozess. Kein Sandbox-Profil könnte sie begrenzen, auch wenn eines hinge.
Nur bash geht durch den gekapselten Befehlsrunner. Ihre Beschränkung ist die
Pfadprüfung von oben. Diese Prüfung trägt deshalb. Sie ist keine zweite
Verteidigungslinie.
Beim Werkzeug bash kommt es auf dieselbe Genauigkeit an. Seine Befehle werden
gegen die Befehlsrichtlinie des Modus geprüft und dann mit bereinigter Umgebung
und dem Worktree als Arbeitsverzeichnis gestartet. Wo ein Backend existiert,
wird dieser Start umhüllt, und die Sandbox hindert einen Shell-Befehl daran,
außerhalb des Worktrees zu schreiben. Wo keines existiert, also unter Windows
oder unter Linux ohne bwrap und socat, steht nur die Befehlsrichtlinie
zwischen dem Befehl und dem Rest der Platte. Im Modus chat lehnt die
Richtlinie eine feste Liste hart ab (Rechteausweitung, Zerstörung von System
und Datenträger, Ausbruch aus Container und Namespace) und fragt bei Änderungen
am entfernten Zustand nach (git push, gh pr create, Paketveröffentlichungen
und -installationen). Was auf keiner der beiden Listen steht, läuft. Pipelines,
Ketten und Subshells werden geparst, jeder Teilbefehl einzeln bewertet, und die
strengste Entscheidung gewinnt.
Claude Code als Adapter ist der Fall, in dem die Sandbox am meisten trägt.
Control Center startet ihn mit --dangerously-skip-permissions, weil ein nicht
interaktives claude -p sonst endlos an seiner eigenen Rückfrage hängen
bliebe. Seine Aufrufe zum Lesen, Schreiben und Bearbeiten passieren in seinem eigenen Prozess, und Control Center sieht sie nie. Seine Bash-Aufrufe gehen vor der Ausführung an einen Hook auf dem Server, der die Befehlszeile einordnet und die passende Regel fragt, und die mcp__*-Werkzeuge, die Control Center bereitstellt, laufen wie gewohnt durch das Tor. Wo ein Backend existiert, passieren auch
die ungesehenen Aufrufe innerhalb der Sandbox-Umhüllung, die Grenze darum ist
also das Betriebssystem. Wo keines existiert, ist da nichts. Jeder Adapter hält
das in seiner Durchsetzungsmatrix fest. Siehe die Ehrlichkeitsmatrix der
Adapter.
Pushes laufen durch Control Center
Abschnitt betitelt „Pushes laufen durch Control Center“Es gibt keine Berechtigungs-Flags je Agent. Ob ein Agent committen, pushen, einen Pull Request öffnen, in einen Tracker schreiben oder das Netzwerk erreichen darf, entscheiden die Schutzregeln — zulassen, zuerst fragen oder verweigern —, und dieselben Regeln gelten, egal welcher Adapter den Agenten ausführt.
Ein Push ist die Aktion, die auch gegen einen Agenten halten muss, der eine
Regel umgehen will. Deshalb wird er über das durchgesetzt, was der Agent
besitzt, nicht über das, was er ausführt. Ist eine GitHub App konfiguriert,
bekommt ein Lauf in einem GitHub-Worktree ein auf das Repo begrenztes
Installationstoken, das Inhalte lesen und mit Pull Requests arbeiten, aber
nicht pushen kann. Die Git-Konfiguration des Laufs schreibt jede GitHub-Push-URL
(https://github.com/, git@github.com:, ssh://git@github.com/) auf das
Gateway für Agentenläufe um, einen Loopback-Endpunkt auf cc_server. Das
Gateway liest, welche Refs der Push aktualisiert, fragt die Regel Zu einem
Remote pushen — eine auf Branches beschränkte Regel wie „Pushes auf main
verweigern“ greift also — und leitet den Push erst dann an GitHub weiter, mit
einem Schreib-Token, das es eigens dafür ausstellt und nie an den Agenten
weitergibt. Ein abgelehnter Push erscheint in der Ausgabe von git selbst:
! [remote rejected] main -> main (Control Center policy: …)Das Gateway leitet Pushes nur an das eigene Repo des Worktrees oder an ein GitHub-Repo weiter, das mit dem Arbeitsbereich verknüpft ist. Fetches gehen mit dem Lese-Token direkt an GitHub.
Andere Shell-Befehle treffen auf ein weicheres Tor. Das Werkzeug bash des
Harness und, über einen Hook, das Werkzeug Bash von Claude Code ordnen jede
Befehlszeile ein und fragen die Regel, unter die sie fällt: git commit,
merge, rebase, cherry-pick, revert und am sind Einen Commit
erstellen; gh pr create ist Einen Pull Request öffnen; gh pr merge,
review, comment und close sind Eine Review veröffentlichen oder
mergen; gh issue create, edit, comment und close sind In einen
externen Tracker schreiben; Paketinstallationen sind Ein Paket
installieren; curl und wget sind Auf das Netzwerk zugreifen. Das
erkennt die übliche Schreibweise einer Aktion, und ein Agent, der eine
verstecken will (eval, eine Skriptdatei, ein direkter API-Aufruf), schafft
das. Pushes hängen nicht davon ab.
In eine Sandbox wird kein Ticketing-API-Schlüssel gelegt. Agenten schreiben Tickets über die MCP-Werkzeuge von Control Center, die In einen externen Tracker schreiben fragen.
Die Regel Auf das Netzwerk zugreifen wird einmal gelesen, wenn der Lauf
startet. Das Netzwerk einer Sandbox ist entweder an oder aus, also schaltet
Verweigern es ab, während Zulassen und Zuerst fragen es anlassen; web_fetch
und web_search des Harness fragen weiterhin bei jedem Aufruf. Zugangsdaten
werden widerrufen, wenn der Lauf abgebaut wird, zusammen mit jedem
Schreib-Token, das das Gateway ausgestellt hat. Ein Handle überdauert also den
Lauf nicht.
Was die Sandbox hinzufügt und wo sie aufhört
Abschnitt betitelt „Was die Sandbox hinzufügt und wo sie aufhört“Das Enum SandboxBackend hat drei Werte: native (Seatbelt unter macOS,
bubblewrap unter Linux und WSL2), none und microvm. Die Umhüllung von
Agentenbefehlen nutzt nur die ersten beiden. microvm ist das Backend der
gekapselten VM für interaktive Terminals und Rigs, kein stilles Hochstufen
einer bash-Umhüllung. Die native Form erklärt, was du dort gewinnst, wo eine
anliegt, und was sie dir nie geben sollte.
cc_server prüft den Host einmal beim Start, und wo ein Backend antwortet,
speist es beide Nahtstellen des Ausführungspfads, weil die Transporte sich
keine teilen. Die erste (SandboxPort) umhüllt die Claude-Code-CLI. Die zweite
(SandboxManager) umhüllt ACP-Adapter und das Werkzeug bash des eingebauten
Harness. Nur eine zu speisen ließe die anderen unumhüllt starten. Genau das ist
früher passiert. Auf einem Host ohne Backend, oder unter --sandbox off,
starten alle unumhüllt, und der Server sagt in seiner Startzeile, welcher Fall
vorliegt. ACP und das bash des Harness bereinigen die Umgebung weiterhin.
Claude Code über NoSandboxAdapter erbt die Umgebung des Hosts.
Eine Oberfläche bleibt bewusst außen vor. Das servergehostete Terminal nimmt
je Sitzung ein Backend-Argument entgegen, und cc_server erzeugt es mit der
Host-Shell als Vorgabe. Ein Terminal, das du aus der App öffnest, läuft also
ohne Sandbox, außer ein Client nennt ausdrücklich native. Das ist eine echte
Shell auf dem Server-Host, kein Agentenlauf.
Unter Linux baut bubblewrap einen Mount-Namespace, der Agent sieht also
wirklich nur, was hineingebunden wurde. Unter macOS legt sandbox-exec ein
Seatbelt-Profil an, das standardmäßig permissiv ist. Ein Profil, das
standardmäßig alles verweigert, macht eine interaktive Shell unbenutzbar, weil
die Zahl der dyld-, XPC- und Mach-Aufrufe, die man aufzählen müsste, nicht
praktikabel ist. Das macOS-Profil beginnt deshalb bei (allow default) und
schneidet Verbote aus: Lesen geheimer Pfade, eine Schreibrichtlinie, die auf
Verweigern zurückgesetzt wird und darüber ausdrückliche Erlaubnisse legt,
Unlink-Sperren auf jedem verweigerten Pfad und seinen Vorfahren, damit ein
Umbenennen ein Schreibverbot nicht umgeht, Exec-Verbote für dauerhaft
gefährliche Binaries und für alles, was in ein beschreibbares Verzeichnis
kopiert wurde, und das Netzwerk beschränkt auf die Loopback-Ports der Proxys
plus DNS.
Die praktische Folge: Unter Linux ist die Sicht des Agenten auf das Dateisystem verengt. Unter macOS kann der Agent den größten Teil des Hosts weiterhin lesen, und deine registrierten Checkouts sind durch ausdrückliche Verbote für Schreiben und Umbenennen geschützt, nicht durch Unsichtbarkeit. Beides ist Isolation auf Namespace-Ebene, keine Sicherheitsgrenze des Kernels. Die Backends sagen das selbst in ihren Hinweisen zu den Fähigkeiten.
Die Menge der Bind-Mounts zieht die Grenze zwischen den Agenten.
Overlay-Dispatch (Claude Code und ACP) mountet bis zu vier Pfade, je Lauf
aufgelöst: das Overlay-Arbeitsverzeichnis des Agenten lesend und schreibend,
das Konfigurationsverzeichnis des Agenten nur lesend, das gemeinsame
Verzeichnis repos/ der Unterhaltung lesend und schreibend, wenn es existiert,
und das Anhängeverzeichnis des Bereichs nur lesend, wenn es existiert. Das
Verzeichnis eines benachbarten Agenten wird nie eingehängt. Die
bash-Umhüllung des Harness ist enger: Sie bind-mountet nur das
Arbeitsverzeichnis des Laufs. Dieselbe Form erzwingt die prozessinterne
Pfadprüfung des Harness in Software, die beiden decken sich also. Nur die
Pfadprüfung erfasst aber die eigenen Dateiwerkzeuge des Harness, die einen
Mount-Namespace überhaupt nie erreichen.
Deine registrierten Checkouts kommen zusätzlich in die Menge der
Schreibverbote, in jedem Modus und auf allen drei umhüllten Pfaden (Claude
Code, ACP, bash des Harness), und jede Regel wird in beiden Schreibweisen
ausgegeben, der wörtlichen und der über den Symlink aufgelösten. Die zweite
Schreibweise ist keine Kür: Der Kernel prüft eine Verbotsregel gegen den
aufgelösten Pfad, und unter macOS ist die Auflösung der Normalfall, nicht die
Ausnahme. /tmp ist in Wahrheit /private/tmp, /var/folders/… ist in
Wahrheit /private/var/folders/…. Ein Checkout, der über eine der beiden
Formen erreicht wird, wäre sonst beschreibbar gewesen, während das Profil ihn
zu verbieten schien.
Netzwerk ist in einer Sandbox eine Domain-Zulassungsliste je Sitzung,
durchgesetzt von prozessinternen Loopback-Proxys. Der Ausgang verweigert
standardmäßig: Ist das Netzwerk aus, starten die Proxys trotzdem, ihre
Zulassungsliste bleibt aber leer, also wird jede ausgehende Verbindung
abgelehnt. Ist es an, baut der Resolver die Zulassungsliste aus einer
kuratierten Basis, Anbieter von LLM-APIs, Git-Hosting, Paketregistries, der
Modellkatalog, plus die Domains von GitHub, und prüft den Zielhost jeder
Verbindung dagegen. Platzhalter funktionieren (*.example.com trifft
Subdomains, nicht die Apex-Domain). Eine eigene Liste, die immer verweigert,
gewinnt gegen die Zulassungsliste und umfasst Endpunkte für Cloud-Metadaten
sowie Senken für Telemetrie. Was sich nicht einstellen lässt, ist die Liste
selbst: Die Spezifikation trägt ein Feld egressAllowlist für zusätzliche
Domains, aber nichts im Ausführungspfad füllt es. In der Praxis bekommt also
jeder Lauf die Basis plus GitHub, und nichts darüber hinaus.
Verweigerungen sind unter macOS beobachtbar. Ein Monitor zapft das
Systemprotokoll nach Sandbox-Verweigerungen an, filtert bekanntes Rauschen,
dedupliziert Wiederholungsstürme und meldet jede als SandboxViolation, mit dem Versuch und dem, was blockiert wurde. Unter Linux parst
der Umhüllungspfad stderr nach EPERM, Operation not permitted und
Permission denied und gibt denselben Ereignistyp aus. Einen Abgriff am
Systemprotokoll gibt es dort nicht. Sie kommen auf dem eigenen Ereignisstrom
des Laufs an, ein abgelehnter Syscall erscheint also im Transkript, nicht nur
in einem Log. Die Telemetrie gibt es nur, solange eine native Sandbox den
Prozess wirklich umhüllt. Auf einem Host ohne Backend gibt es nichts zu
beobachten. Mit den Schutzregeln hat sie außerdem nichts zu tun: Eine Verletzung kommt von einem abgelehnten Syscall, nie von einer Regelabfrage.
Bestätigungen kommen von den Schutzregeln, nicht von der Sandbox
Abschnitt betitelt „Bestätigungen kommen von den Schutzregeln, nicht von der Sandbox“Die Sandbox hat keinen Freigabepfad. Sie startet, führt aus, sendet Ereignisse und baut ab. Nirgends darin kann ein Mensch eine Frage beantworten. Siehst du in einem Bereich eine Bestätigung, diesen Push freizugeben oder dieses Löschen freizugeben, dann hat sie die Engine der Schutzregeln ausgelöst, oder die Befehlsrichtlinie des Harness, und zwar bevor die Aktion die Laufzeit überhaupt erreicht hat.
Diese Reihenfolge ist Absicht. Eine Freigabe nach dem Syscall wäre ein Audit-Eintrag, kein Tor. Genau deshalb sitzt die Regel fail-closed in den Schutzregeln: Ohne verbundene zuständige Person fällt die Entscheidung, bevor irgendetwas läuft, und sie lautet Ablehnung.
Diagnostik
Abschnitt betitelt „Diagnostik“Einen Agenten diagnostizieren ist die eine Stelle, an der steht, was sich von Hand prüfen lässt.
Verwandte Konzepte
Abschnitt betitelt „Verwandte Konzepte“- Rigs und Kapseln: die Stufe über der Sandbox, wenn die Arbeit eine ganze Wegwerfmaschine mit einer Kernel-Grenze braucht, nicht einen umhüllten Prozess
- Schutzregeln: die Richtlinienschicht über der Untergrenze des Betriebssystems, und woher die Freigaben tatsächlich kommen
- Lebenszyklus der Agenten-Ausführung: wo die Isolation im Ablauf der Ausführung sitzt
- Modi: wie ein Modus Werkzeugfläche und Befehlsrichtlinie einschränkt
- Sandbox-Backends: die Backends und ihre Konfiguration
Verwandte Anleitungen
Abschnitt betitelt „Verwandte Anleitungen“- Sandbox-Richtlinien konfigurieren: Aktionsregeln, Push-Durchsetzung und die Dateisystemhaltung
- Einen Agenten diagnostizieren: was die Prüfungen der Diagnose abdecken