Zum Inhalt springen

Sandbox-Backends

Backend Wert Plattform Verfügbarkeit
Gekapselte VM microvm macOS, Linux, Windows (microVM-Hypervisor) An die Prüfung der microVM-Laufzeit gebunden; gilt für interaktive Terminals — die Shell läuft in einem Rig, nicht auf dem Host
Native sandbox native macOS (Seatbelt), Linux und WSL2 (bubblewrap) Beim Start geprüft; Linux verweigert einen Lauf, der Schreibverbote per Dateinamen-Glob verlangt, die bubblewrap nicht durchsetzen kann
No isolation none Alle Der Standard beim Parsen, der Standard des Terminals und der Rückfall, wenn kein Backend verfügbar ist oder --sandbox off gesetzt ist

SandboxBackend.fromName liefert none für einen Nullwert oder einen unbekannten gespeicherten Wert. Ein gespeichertes "docker" wird auf native abgebildet.

microvm ist das einzige Backend mit einer Kernel-Grenze — die Arbeit läuft wirklich nicht auf dem Host. Es ist in keine Richtung ein stiller Rückfall: Wer es auf einem Host ausdrücklich verlangt, der keine VM starten kann, scheitert laut („Ich habe eine VM verlangt und meinen Laptop bekommen“ ist die eine Verschlechterung, die sich die Kapsel nicht leisten kann), und nur ein nicht festgelegter Standard wird auf native herabgestuft, mit einer Logzeile, die das sagt. Das tatsächliche Backend der Sitzung wird an den Client gemeldet und mit einem Badge versehen, ein Terminal kann also nie als gekapselt gelten, während es auf dem Host läuft. Lebenszyklus, Images und der Ausgang der VM selbst stehen in der Rigs-Referenz.

Windows hat keine native Sandbox. NativeSandboxAdapter.probe liefert available: false auf jedem Host, der nicht macOS oder Linux/WSL2 ist. Der Agenten-Dispatch unter Windows nutzt deshalb none. Das Backend microvm ist davon getrennt: Es hängt an der microVM-Laufzeit und gilt für interaktive Terminals und Rigs, nicht für die Umhüllung von bash auf dem Host.

Die Prüfung entscheidet, ob durchgesetzt wird. Sie läuft einmal, beim Start des Servers.

Host available Notiz / Hinweis
macOS true sandbox-exec (Seatbelt) — Namespace-Isolation, keine Kernel-Grenze
Linux oder WSL2 mit bwrap und socat true bubblewrap — Namespace-Isolation, keine Kernel-Grenze
Linux oder WSL2, eines der Werkzeuge fehlt false requiresInstall: true, mit einem Installationshinweis, der die fehlenden Pakete nennt
Jede andere Plattform false Eine native Sandbox wird auf dieser Plattform nicht angeboten

Der Server schreibt das Ergebnis in eine Startzeile: agent sandbox ON plus die Notiz, no OS-native agent sandbox on this host plus den Hinweis, oder agent sandbox DISABLED by --sandbox off. GET /healthz trägt kein Sandbox-Feld.

--sandbox / CC_SERVER_SANDBOX nimmt on (Standard) oder off an. Es ist ein Abwählen, kein Einschalten: on heißt „umhüllen, wenn der Host ein Backend anbietet“, nie „eines verlangen“.

Nutzt Apples Seatbelt-Sandbox (dasselbe System wie die macOS App Sandbox).

Das erzeugte Profil ist standardmäßig permissiv ((allow default)) und schneidet Verbote aus, weil ein Profil, das standardmäßig alles verweigert, eine interaktive Shell unbenutzbar macht — es gibt zu viele dyld-, XPC- und Mach-Aufrufe, als dass man sie aufzählen könnte. Die praktische Folge: Ein Agent kann den größten Teil des Hosts weiterhin lesen; deine registrierten Checkouts sind durch ausdrückliche Verbote für Schreiben und Umbenennen geschützt, nicht durch Unsichtbarkeit.

  • Dateisystem, Lesen: verweigert für die geheimen Pfade (~/.ssh, ~/.aws, ~/.gnupg, ~/.config/gh, ~/Library/Keychains und der Rest der Leseverbotsmenge)
  • Dateisystem, Schreiben: zuerst auf Verweigern zurückgesetzt, dann ausdrückliche Erlaubnisse für Teilpfade; die Pfade mit zwingendem Verbot (Shell-rc-Dateien, .git/hooks, Claude-Konfiguration, geschützte Checkouts) bleiben auch innerhalb einer beschreibbaren Wurzel gesperrt. file-write-unlink ist auf jedem verweigerten Pfad und seinen Vorfahren verboten, ein Umbenennen kann ein Schreibverbot also nicht umgehen
  • Prozess: process-exec verweigert die dauerhaft gefährlichen Binaries, dazu eine Sperre für alles, was aus einem beschreibbaren Verzeichnis ausgeführt wird
  • Netzwerk: beschränkt auf die Ports der prozessinternen Proxys plus DNS — Loopback ist nicht pauschal erlaubt, und Unix-Sockets sind nicht erlaubt. Die Proxys setzen die Domain-Zulassungsliste je Sitzung durch (siehe Netzwerkausgang)
  • Konfiguration: Profile werden je Start aus der SandboxSpec erzeugt

Voraussetzungen:

  • sandbox-exec (/usr/bin/sandbox-exec), Teil jeder macOS-Installation — kein zusätzliches Werkzeug nötig

Nutzt bubblewrap (bwrap), um Mount- und PID-Namespaces anzulegen.

  • Dateisystem: legt einen Mount-Namespace an, in dem nur die gebundenen Pfade sichtbar sind
  • Netzwerk: läuft in einem nicht geteilten Netzwerk-Namespace; der Verkehr erreicht die Host-Proxys über Unix-Socket-Brücken von socat
  • Prozess: Isolation im PID-Namespace

Voraussetzungen:

  • bubblewrap und socat installiert (sudo apt-get install bubblewrap socat unter Debian/Ubuntu)
  • Unterstützung für Kernel-Namespaces (Standard auf modernem Linux)

Bubblewrap kann einen bestehenden Pfad überlagern, aber keine neue Datei .env, *.key oder *.pem in einem beschreibbaren Bind verbieten. Die Standardrichtlinie des Dispatch verlangt genau diese Schreibverbote per Dateinamen-Glob, deshalb schlägt der native Agenten-Dispatch unter Linux/WSL2 derzeit fail-closed fehl, auch wenn die Prüfung bubblewrap findet. Er läuft nicht mit einer stillschweigend schwächeren Richtlinie. Um den nativen Dispatch unter Linux wiederherzustellen, braucht es ein Backend mit Dateinamenregeln zum Erzeugungszeitpunkt. Das Sandboxing abzuschalten ist ein ausdrückliches, unbeschränktes Abwählen, kein gleichwertiger Ersatz.

Keine Sandbox. Der Prozess läuft direkt auf dem Host, mit Dateisystem, Netzwerk und Prozesszugriff des Hosts. ACP und das Werkzeug bash des Harness bekommen weiterhin die Bereinigung der Umgebung und die Befehlsrichtlinie; Claude Code über NoSandboxAdapter erbt die Umgebung des Hosts. Darauf fällt der Agenten-Dispatch zurück, wenn kein Backend verfügbar ist oder --sandbox off gesetzt ist, und das ist der Standard des Terminals, wenn ein Client none nennt.

Das Terminal wird nach eigenen Regeln in die Sandbox gelegt, nicht durch die Prüfung des Dispatch. defaultBackend von TerminalSessionService ist none, und cc_server überschreibt das nicht. Eine Terminalsitzung führt also die Host-Shell aus, außer der Client verlangt ausdrücklich native — das Terminal der Desktop-App tut das nicht.

Wird native verlangt, umhüllt SandboxManager die Shell mit einem festen Terminalprofil: $HOME und das Arbeitsverzeichnis der Sitzung sind beschreibbar, das Netzwerk ist offen, und ~/.ssh, ~/.aws, ~/.gnupg, ~/.config/gh und ~/Library/Keychains sind leseverboten. Das Forge-Token, das nur lesen darf, und das Push-Gateway gelten nicht — das sind Sachen des Dispatch. Ist die Umhüllung nicht unterstützt, schlägt die Sitzung fehl, statt ein natives Badge auf einem PTY der Host-Shell zu zeigen.

Wird eine Sandbox gestartet, setzen prozessinterne Proxys den ausgehenden Netzwerkzugriff durch, nicht Regeln einer Kernel-Firewall. Auf einem Host ohne verfügbares Backend gilt davon nichts, und ein gestarteter Prozess erreicht das Netzwerk genau so, wie es der Server-Benutzer täte.

  • Die Sandbox-Laufzeit betreibt einen HTTP/HTTPS-Proxy und einen SOCKS5-Proxy auf 127.0.0.1 im Prozess von cc_server, ein Paar je eingeschränkter Sitzung (http_proxy.dart, socks_proxy.dart). Gleichzeitige Läufe teilen sich kein Paar.
  • Sandbox-Prozesse bekommen Proxy-Umgebungsvariablen (HTTP_PROXY/HTTPS_PROXY → der HTTP-Proxy, ALL_PROXY → der SOCKS-Proxy; GIT_SSH_COMMAND leitet Git über SSH durch den SOCKS-Proxy). Unter Linux ist der Netzwerk-Namespace nicht geteilt, die Proxys sind also der einzige Weg nach draußen; unter macOS teilt die Sandbox das Loopback des Hosts und spricht die Proxys direkt an.
  • Der Zielhost jeder Verbindung wird gegen die Domain-Zulassungsliste der Sitzung geprüft; alles andere wird abgelehnt. Zwei Platzhalterformen, jede mit genau einem *: ein führendes *.example.com trifft jede Subdomain (einschließlich a.b.example.com), nicht aber die Apex selbst; ein mittleres Label bedrock.*.amazonaws.com trifft genau ein Label (bedrock.us-east-1.amazonaws.com, nicht bedrock.evil.attacker.amazonaws.com).
  • Die Zulassungsliste wird je Sitzung aus der kuratierten Basis aufgelöst (kBaselineAllowedDomains: LLM-Anbieter, Git-Hosting, Paketregistries), plus die GitHub-Domains (kGithubDomains), plus SandboxSpec.egressAllowlist. Der Ausführungspfad übergibt nie zusätzliche Einträge in egressAllowlist, in der Praxis bekommt also jeder Lauf die Basis plus GitHub.
  • Eine feste Verbotsliste (kBaselineDeniedDomains: Endpunkte für Cloud-Metadaten, Senken für Telemetrie) hat Vorrang vor allen Erlaubnissen.
  • networkEnabled: false startet die Proxys je Sitzung trotzdem, die Zulassungsliste ist aber leer, also wird jede ausgehende Verbindung abgelehnt. Der Dispatch setzt den Wert beim Start des Laufs aus der Regel Auf das Netzwerk zugreifen: Verweigern schaltet das Netzwerk ab, Zulassen und Zuerst fragen lassen es an.

Die Spezifikation, die an SandboxPort.launch geht:

Feld Typ Beschreibung
sessionId String Stabile Sitzungskennung (typischerweise die Bereichs-ID)
workspaceId String ID des zugehörigen Arbeitsbereichs
agentId String? Agent, der gerade an die Sandbox gebunden ist, falls vorhanden
bindMounts List<SandboxBindMount> Mounts Host→Gast; die Sandbox sieht nur die hier aufgeführten Host-Pfade
networkEnabled bool (Standard true) Beim Dispatch aus der Regel für ausgehenden Netzwerkverkehr gesetzt (nur aus, wenn sie zu Verweigern auflöst). Ist es aus, starten die Proxys je Sitzung trotzdem, die Zulassungsliste bleibt aber leer, also wird jede ausgehende Verbindung abgelehnt
egressAllowlist List<String> (Standard []) Zusätzliche erlaubte Ausgangsdomains; Platzhalter sind möglich (*.example.com, dazu eine Form mit genau einem Label in der Mitte). Der Dispatch füllt das nie, ein umhüllter Lauf bekommt also die kuratierte Basis plus die GitHub-Domains
guestWorkdir String? Standard-Arbeitsverzeichnis in der Sandbox; einzelne exec-Aufrufe können es überschreiben
mode Mode (Standard chat) Schneidet die Schreibregeln des Dateisystems: chat lässt Bind-Mounts beschreibbar; review, plan und orchestrate sind nur lesend
protectedPaths List<String> (Standard []) Host-Pfade, die in der Sandbox in keinem Modus beschreibbar sein dürfen
runnerStateDirs List<String> (Standard []) Host-Verzeichnisse, in denen der Runner seinen eigenen Zustand hält, in jedem Modus beschreibbar (ein von Control Center verwaltetes CLAUDE_CONFIG_DIR ist derzeit das eine)
execGrantRoots List<String> (Standard []) Von dir freigegebene Verzeichnisbäume, die unter macOS die Exec-Sperre für beschreibbare Verzeichnisse wieder öffnen. Linux ignoriert sie — bwrap hat keine $HOME-Exec-Sperre, die sich wieder öffnen ließe

protectedPaths enthält die ursprünglich registrierten Repo-Checkouts, je Dispatch aus den Repos des Arbeitsbereichs aufgelöst. Agenten arbeiten ausschließlich in Copy-on-Write-Worktrees je Unterhaltung, die Originale dürfen aus der Sandbox heraus also nie beschreibbar sein — unter macOS ließe die pauschale Schreibfreigabe von Seatbelt für $HOME sie sonst über bash beschreibbar. Sie gehören in jedem Modus zur Menge der Schreibverbote für Geheimnisse, chat eingeschlossen.

Jeder Leseverbots- und Schreibverbotspfad wird in beiden Schreibweisen ausgegeben, der wörtlichen und der über den Symlink aufgelösten. Der Kernel prüft eine Verbotsregel gegen den aufgelösten Pfad, eine Regel gegen einen Symlink-Pfad greift also nie — und unter macOS ist das der Normalfall, nicht die Ausnahme, denn /tmp löst sich zu /private/tmp auf und /var/folders/… zu /private/var/folders/…. Einträge mit * oder ? gelten als Globs und bleiben unverändert; ein Pfad, der sich nicht auflöst, bleibt, wie er geschrieben wurde.

Feld Typ Beschreibung
hostPath String Absoluter Pfad auf dem Host
guestPath String Pfad in der Sandbox (bei der nativen Sandbox gleich hostPath; bleibt wegen der Symmetrie der Adapter)
readOnly bool (Standard false) Nur lesend einhängen, wenn wahr

Zurückgegeben von SandboxPort.launch:

Feld Typ Beschreibung
sessionId String Sitzungskennung
backend SandboxBackend Welches Backend gerade läuft
state SandboxState Zustand im Lebenszyklus
error Object? Gesetzt, wenn state den Wert error hat
details Map<String, Object?> Interne Verwaltungsdaten des Adapters (Arbeitsverzeichnis, PID, Profilpfad usw.)
Zustand Beschreibung
created Spezifikation angelegt, die VM ist noch nicht gestartet
warm Läuft und bereit für exec-Befehle
active Führt gerade einen Befehl aus
suspended Pausiert und als Checkpoint auf die Platte geschrieben, um RAM zu sparen
destroyed Abgebaut; das Handle ist nicht mehr gültig
error Start fehlgeschlagen — siehe SandboxHandle.error
Ereignistyp Beschreibung
stdout Standardausgabe der Sandbox
stderr Standardfehler der Sandbox
exit Der Sandbox-Prozess ist beendet (trägt exitCode)
starting Die Sandbox startet
ready Die Sandbox nimmt exec-Aufrufe an
killed Vom Host beendet (OOM, Zeitüberschreitung, manueller Stopp)
violation Eine Sandbox-Richtlinie wurde verletzt (trägt eine SandboxViolation)
Feld Typ Beschreibung
action String Das verweigerte Verb (z. B. file-read*, network-outbound)
target String Pfad oder Host, der Ziel der verweigerten Operation war
raw String? Rohe Logzeile, nützlich zur Fehlersuche