Sandbox i bezpieczeństwo
Dlaczego izolacja ma tu znaczenie
Dział zatytułowany „Dlaczego izolacja ma tu znaczenie”Agent czyta pliki, uruchamia polecenia powłoki i wywołuje zewnętrzne API w Twoim imieniu. Awaria, która Cię interesuje, rzadko wynika ze złej woli — to raczej agent, który błędnie odczyta ścieżkę i nadpisze Twoją kopię roboczą, albo taki, który wypchnie gałąź, której nie zdążyłeś przejrzeć. Pytanie, na które produkt musi odpowiedzieć, nie brzmi więc „czy agentowi można zaufać”, lecz „jaki jest promień rażenia, gdy się myli”.
Control Center odpowiada na to warstwami, które celowo są niezależne. Jeśli jednej brakuje, pozostałe nadal działają. Ma to znaczenie, bo najbardziej zewnętrzna warstwa — sandbox systemowy — jest jedyną warunkową: zależy od tego, czy host oferuje backend, a w Windows żaden host go nie oferuje.
Co faktycznie ogranicza agenta
Dział zatytułowany „Co faktycznie ogranicza agenta”Na każdym hoście — z sandboxem czy bez — uruchomienie agenta ogranicza pięć rzeczy. Żadna z nich nie jest granicą jądra; wszystkie są realne. Tam, gdzie host oferuje backend, sandbox systemowy sytuuje się ponad nimi, a sekcja „Co dodaje sandbox i gdzie się zatrzymuje” poniżej opisuje, co to zmienia.
Własny worktree copy-on-write. Każda rozmowa przygotowuje własną kopię
roboczą każdego powiązanego repozytorium, klonowaną copy-on-write przez
dołączony natywny komponent rift, a nie kopiowaną bajt po bajcie, więc
izolacja jest wystarczająco tania, by była ustawieniem domyślnym.
Zarejestrowane repozytorium, które dodałeś do obszaru roboczego, to nie
katalog, w którym pracuje agent — dwie rozmowy sięgające do tego samego
repozytorium nie widzą swoich zmian i żadna z nich nie edytuje Twojej kopii
roboczej.
Warto doprecyzować ziarnistość: worktree przypada na rozmowę, a nie na
agenta. Dwaj agenci w tej samej rozmowie dostają odrębne katalogi robocze
overlay, które dzielą katalog repos/ tej rozmowy — i o to właśnie chodzi,
skoro współpracują nad jedną zmianą — a ich prywatne katalogi agentów
pozostają osobne. Copy-on-write jest jedynym backendem wszędzie tam, gdzie
trafia rift: system plików, który nie potrafi reflinkować, albo katalog
danych na innym wolumenie niż repozytorium, powoduje niepowodzenie aprowizacji,
a nie przejście do trybu zapasowego. Wyjątkiem jest Windows — nie ma tam
backendu copy-on-write, więc backendem jest git worktree, a nie cicha
degradacja.
Izolacja ścieżek w narzędziach plikowych wbudowanego harnessa. Narzędzia
read, write, edit, apply_patch, search, find i search_files
harnessa rozwiązują każdą ścieżkę względem katalogu roboczego uruchomienia
oraz współdzielonych katalogów głównych — katalogu repos/ rozmowy, w którym
żyją worktree, i katalogu załączników przestrzeni, jeśli istnieje — i odmawiają
dostępu do wszystkiego, co wypada poza nie. Ścieżki bezwzględne są akceptowane
tylko wtedy, gdy już znajdowały się w środku. Dowiązanie symboliczne wewnątrz
worktree wskazujące na zewnątrz również jest rozwiązywane i odrzucane. Dlatego
agent nie może zapisać przez write do ~/.ssh, nawet gdy w ogóle nie ma
sandboxa.
Powierzchnia narzędzi trybu. Tryb
rozmowy decyduje o tym, które narzędzia w ogóle zostaną zmaterializowane dla
danego uruchomienia. W trybach tylko do odczytu (plan, review,
orchestrate) narzędzia modyfikujące po prostu nie trafiają do rejestru, a
polityka poleceń powłoki dla tych trybów wprost odmawia każdego polecenia
modyfikującego — git commit, git push, npm install, docker i reszty —
zamiast o nie pytać. Narzędzia, którego nie ma, nie da się wywołać, co jest
silniejszą gwarancją niż narzędzie obecne i objęte bramką.
Ujednolicone guardraile działań. Każde narzędzie modyfikujące deklaruje
klasy efektów, które może wywołać, a silnik polityk rozstrzyga każdą z nich na
zezwolenie, pytanie albo odmowę, zanim wywołanie się wykona. prompt bez
podłączonego zatwierdzającego rozstrzyga się na odmowę — pytanie, na które
nikt nie może odpowiedzieć, nigdy nie jest zgodą. Model rozstrzygania opisują
guardraile.
Poświadczenia z brokera i oczyszczone środowisko. Poświadczenia są tworzone przy każdym uruchomieniu i unieważniane, gdy uruchomienie się kończy, a token GitHub, który ma uruchomienie, pozwala czytać, ale nie wykonać push — zobacz „Push przechodzi przez Control Center” poniżej. ACP, bash
harnessa i Claude Code w natywnym opakowaniu dostają środowisko pozbawione
wektorów wstrzykiwania przez konsolidator dynamiczny i interpreter
(LD_PRELOAD, DYLD_INSERT_LIBRARIES, NODE_OPTIONS, GIT_SSH_COMMAND i im
podobnych), zanim proces wystartuje. Claude Code przez NoSandboxAdapter
dziedziczy środowisko hosta.
Gdzie te granice słabną
Dział zatytułowany „Gdzie te granice słabną”Narzędzia plikowe harnessa działają w procesie serwera, a nie w procesie
potomnym, więc żaden profil sandboxa nie mógłby ich ograniczyć, nawet gdyby był
podłączony — przez sandboxowany runner poleceń przechodzi tylko bash. Ich
izolację zapewnia opisane wyżej sprawdzanie ścieżek, które jest więc elementem
nośnym, a nie drugą linią obrony.
bash to drugie miejsce, które warto doprecyzować. Jego polecenia są oceniane
względem polityki poleceń danego trybu, a następnie uruchamiane z oczyszczonym
środowiskiem i worktree jako katalogiem roboczym. Tam, gdzie istnieje backend,
to uruchomienie jest opakowane, więc to sandbox powstrzymuje polecenie powłoki
przed zapisem poza worktree; tam, gdzie go nie ma — Windows albo Linux bez
bwrap i socat — polityka poleceń jest jedyną rzeczą między poleceniem a
resztą dysku. W trybie chat polityka twardo odmawia w przypadku stałej listy
(podnoszenie uprawnień, niszczenie systemu i dysku, ucieczka z kontenera i
przestrzeni nazw) i pyta o mutacje stanu zdalnego (git push, gh pr create,
publikowanie i instalowanie pakietów). Wszystko, czego nie ma na żadnej z list,
zostaje uruchomione. Potoki, łańcuchy i podpowłoki są parsowane, a każde
podpolecenie oceniane osobno, przy czym wygrywa najbardziej restrykcyjna
decyzja.
Claude Code użyty jako adapter to przypadek, w którym sandbox ma największe
znaczenie. Control Center uruchamia go z --dangerously-skip-permissions,
ponieważ nieinteraktywne claude -p w przeciwnym razie zablokowałoby się na
zawsze na własnym pytaniu o zatwierdzenie. Jego wywołania odczytu, zapisu i edycji odbywają się wewnątrz własnego procesu i Control Center ich nie widzi. Jego wywołania Bash trafiają przed wykonaniem do hooka na serwerze, który klasyfikuje wiersz poleceń i pyta odpowiednią regułę, a narzędzia mcp__* udostępniane przez Control Center przechodzą przez bramkę jak zwykle. Tam, gdzie istnieje backend, te
niewidoczne wywołania i tak odbywają się w opakowaniu sandboxa, więc granicą
wokół nich jest system operacyjny. Tam, gdzie go nie ma, nie ma żadnej. Każdy
adapter stwierdza to w swojej macierzy egzekwowania — zobacz macierz
uczciwości adapterów.
Push przechodzi przez Control Center
Dział zatytułowany „Push przechodzi przez Control Center”Nie ma flag uprawnień dla poszczególnych agentów. O tym, czy agent może utworzyć commit, wykonać push, otworzyć pull request, zapisywać do trackera albo sięgać do sieci, decydują guardraile — zezwól, zapytaj najpierw lub odmów — i te same reguły obowiązują niezależnie od adaptera, który uruchamia agenta.
Push to akcja, która musi się utrzymać nawet wobec agenta próbującego obejść
regułę, więc egzekwuje się ją przez to, co agent posiada, a nie przez to, co
uruchamia. Przy skonfigurowanej GitHub App uruchomienie w worktree GitHuba
dostaje token instalacji o zakresie repozytorium, który może czytać zawartość i
pracować z pull requestami, ale nie może wykonać push. Konfiguracja gita
uruchomienia przepisuje każdy adres URL push do GitHuba (https://github.com/,
git@github.com:, ssh://git@github.com/) na bramę uruchomień agentów,
punkt końcowy na pętli zwrotnej w cc_server. Brama odczytuje, które refy push
aktualizuje, pyta regułę Push do zdalnego repozytorium — więc obowiązuje
też reguła ograniczona do gałęzi, na przykład „odmów push do main” — i dopiero
wtedy przekazuje push do GitHuba z tokenem zapisu, który tworzy w tym celu i
nigdy nie przekazuje agentowi. Odrzucony push widać w wyjściu samego gita:
! [remote rejected] main -> main (Control Center policy: …)Brama przekazuje push tylko do własnego repozytorium worktree albo do repozytorium GitHuba połączonego z obszarem roboczym. Fetch trafia bezpośrednio do GitHuba z tokenem odczytu.
Inne polecenia powłoki napotykają łagodniejszą bramkę. Narzędzie bash
harnessa oraz, przez hook, narzędzie Bash Claude Code klasyfikują każdy wiersz
poleceń i pytają regułę, pod którą podpada: git commit, merge, rebase,
cherry-pick, revert i am to Utworzenie commita; gh pr create to
Otwarcie pull requesta; gh pr merge, review, comment i close to
Opublikowanie recenzji lub scalenie; gh issue create, edit, comment
i close to Zapis do zewnętrznego trackera; instalacje pakietów to
Instalacja pakietu; curl i wget to Dostęp do sieci. To rozpoznaje
typowy zapis akcji, a agent zdecydowany ją ukryć (eval, plik skryptu,
bezpośrednie wywołanie API) jest w stanie to zrobić. Push od tego nie zależy.
Do sandboxa nie trafia żaden klucz API zgłoszeń. Agenci zapisują zgłoszenia przez narzędzia MCP Control Center, które pytają regułę Zapis do zewnętrznego trackera.
Reguła Dostęp do sieci jest odczytywana raz, na starcie uruchomienia. Sieć
sandboxa jest albo włączona, albo wyłączona, więc odmowa ją wyłącza, a zezwól i
zapytaj najpierw zostawiają ją włączoną; narzędzia web_fetch i web_search
harnessa nadal pytają przy każdym wywołaniu. Poświadczenia są unieważniane, gdy
uruchomienie się kończy, razem z każdym tokenem zapisu utworzonym przez bramę,
więc uchwyt nie przeżywa uruchomienia.
Co dodaje sandbox i gdzie się zatrzymuje
Dział zatytułowany „Co dodaje sandbox i gdzie się zatrzymuje”Wyliczenie SandboxBackend ma trzy wartości — native (Seatbelt w macOS,
bubblewrap w Linuksie i WSL2), none i microvm. Opakowywanie poleceń agenta
korzysta tylko z dwóch pierwszych: microvm to backend zamkniętej maszyny
wirtualnej dla interaktywnych terminali i rigów, a nie ciche ulepszenie
opakowania bash. Zrozumienie kształtu natywnego wyjaśnia, co zyskujesz, gdy
jest podłączony, i czego nigdy nie miał ci dać.
cc_server sonduje host raz przy starcie i jeśli backend odpowiada, zasila
oba punkty podłączenia, które ma ścieżka wysyłania, bo transporty nie dzielą
jednego. Pierwszy (SandboxPort) opakowuje CLI Claude Code. Drugi
(SandboxManager) opakowuje adaptery ACP i narzędzie bash wbudowanego
harnessa. Zasilenie tylko jednego pozostawiłoby pozostałe uruchamiane bez
opakowania — i tak właśnie kiedyś było. Na hoście bez backendu albo przy
--sandbox off wszystkie uruchamiają się bez opakowania, a serwer w wierszu
startowym mówi, który to przypadek. ACP i bash harnessa nadal oczyszczają
środowisko; Claude Code przez NoSandboxAdapter dziedziczy środowisko hosta.
Jedna powierzchnia celowo się wycofuje: terminal hostowany na serwerze
przyjmuje argument backendu dla sesji, a cc_server konstruuje go tak, że
domyślnie wskazuje powłokę hosta, więc terminal otwarty z aplikacji działa bez
sandboxa, chyba że klient wprost wskaże native. To prawdziwa powłoka na
hoście serwera, a nie uruchomienie agenta.
W Linuksie bubblewrap buduje przestrzeń nazw montowania, więc agent naprawdę
widzi tylko to, co zostało do niej podmontowane. W macOS sandbox-exec
stosuje profil Seatbelt, który jest domyślnie permisywny — w pełni
odmawiający domyślnie profil czyni interaktywną powłokę bezużyteczną, bo
liczba wywołań dyld, XPC i Mach do wyliczenia jest niepraktyczna. Profil macOS
wychodzi więc od (allow default) i wycina z niego odmowy: odczyty ścieżek z
sekretami, politykę zapisu zresetowaną do odmowy z jawnymi zezwoleniami na
wierzchu, blokowanie odłączania (unlink) na każdej zabronionej ścieżce i jej
przodkach, tak aby zmiana nazwy nie ominęła zakazu zapisu, odmowy wykonania
(exec) dla zawsze niebezpiecznych plików binarnych i dla wszystkiego, co
skopiowano do katalogu z prawem zapisu, oraz sieć ograniczoną do portów proxy
na pętli zwrotnej plus DNS.
Praktyczna konsekwencja: w Linuksie widok systemu plików dostępny dla agenta jest zawężony; w macOS agent nadal może czytać większość hosta, a Twoje zarejestrowane kopie robocze chronią jawne odmowy zapisu i zmiany nazwy, a nie niewidzialność. Oba przypadki to izolacja na poziomie przestrzeni nazw, a nie granica bezpieczeństwa jądra — same backendy tak o tym mówią w swoich notatkach o możliwościach.
Zbiór podmontowań jest miejscem, w którym wyznacza się granicę między agentami.
Wysyłanie przez overlay (Claude Code i ACP) montuje do czterech ścieżek,
ustalanych dla każdego uruchomienia: katalog roboczy overlay agenta w trybie
odczytu i zapisu, katalog konfiguracji agenta tylko do odczytu, współdzielony
katalog repos/ rozmowy w trybie odczytu i zapisu, jeśli istnieje, oraz
katalog załączników przestrzeni tylko do odczytu, jeśli istnieje. Katalog
bratniego agenta nigdy nie jest montowany. Opakowanie bash harnessa jest
węższe: montuje wyłącznie katalog roboczy uruchomienia. To ten sam kształt,
który sprawdzanie ścieżek w procesie harnessa wymusza programowo, więc oba są
spójne — ale tylko sprawdzanie ścieżek obejmuje własne narzędzia plikowe
harnessa, które nigdy nie trafiają do przestrzeni nazw montowania.
Twoje zarejestrowane kopie robocze dołączają ponadto do zbioru objętego
zakazem zapisu, w każdym trybie i na wszystkich trzech opakowanych ścieżkach
(Claude Code, ACP, bash harnessa), a każda reguła jest emitowana zarówno w
zapisie dosłownym, jak i w zapisie po rozwiązaniu dowiązań symbolicznych. Drugi
zapis nie jest ozdobnikiem: jądro dopasowuje regułę odmowy do ścieżki po
rozwiązaniu, a macOS czyni rozwiązywanie przypadkiem powszechnym, a nie
egzotycznym — /tmp to w rzeczywistości /private/tmp, /var/folders/… to w
rzeczywistości /private/var/folders/… — więc kopia robocza osiągnięta przez
którykolwiek z nich byłaby zapisywalna, choć profil pozornie jej tego
zabraniał.
Sieć w sandboxie to lista dozwolonych domen dla sesji, egzekwowana przez
działające w procesie proxy na pętli zwrotnej. Ruch wychodzący jest domyślnie
blokowany: przy wyłączonej sieci proxy wciąż startują, ale ich lista dozwolonych
jest pusta, więc każde połączenie wychodzące jest odrzucane. Przy włączonej
resolver buduje listę dozwolonych z wyselekcjonowanej bazy — dostawcy API LLM,
hosting gita, rejestry pakietów, katalog modeli — plus domeny GitHuba i
dopasowuje do niej host docelowy każdego połączenia. Wieloznaczniki działają
(*.example.com dopasowuje subdomeny, a nie samą domenę). Osobna, zawsze
zabroniona lista wygrywa z listą dozwolonych i obejmuje punkty końcowe
metadanych chmury oraz odbiorniki telemetrii. To, co nie jest
konfigurowalne, to sama lista: specyfikacja niesie pole egressAllowlist na
dodatkowe domeny, ale nic w ścieżce wysyłania go nie wypełnia, więc w praktyce
każde uruchomienie dostaje bazę plus GitHuba i nic więcej.
Odmowy w macOS są obserwowalne: monitor podczepia się do logu systemowego pod
kątem odmów Sandbox, filtruje znany szum, deduplikuje burze ponowień i pokazuje
każdą jako SandboxViolation, nazywając to, czego próbowano, i to, co zostało zablokowane. W Linuksie ścieżka opakowania parsuje stderr pod kątem
EPERM, Operation not permitted i Permission denied i emituje ten sam typ
zdarzenia — nie ma podczepienia do logu systemowego. Trafiają one do własnego
strumienia zdarzeń uruchomienia, więc odrzucone wywołanie systemowe pojawia się
w transkrypcie, a nie tylko w logu. Telemetria istnieje tylko wtedy, gdy
natywny sandbox faktycznie opakowuje proces — na hoście bez backendu nie ma
czego obserwować. Nie ma też związku z guardrailami: naruszenie pochodzi z odrzuconego wywołania systemowego, nigdy ze sprawdzenia reguły.
Potwierdzenia pochodzą od guardraili, a nie od sandboxa
Dział zatytułowany „Potwierdzenia pochodzą od guardraili, a nie od sandboxa”Sandbox nie ma ścieżki zatwierdzania. Uruchamia, wykonuje, emituje zdarzenia i się kończy; nie ma w nim miejsca, w którym człowiek mógłby odpowiedzieć na pytanie. Gdy widzisz w przestrzeni potwierdzenie — zatwierdź to wypchnięcie, zatwierdź to usunięcie — zostało ono zgłoszone przez silnik guardraili (albo politykę poleceń harnessa) zanim działanie w ogóle dotarło do środowiska wykonawczego.
Ta kolejność jest celowa. Zgoda wydana po wywołaniu systemowym byłaby zapisem audytowym, a nie bramką. To także dlatego reguła domyślnej odmowy (fail-closed) mieszka w guardrailach: gdy nie ma podłączonego zatwierdzającego, decyzja zapada, zanim cokolwiek się uruchomi, i jest to odmowa.
Diagnostyka
Dział zatytułowany „Diagnostyka”Uruchom diagnostykę agenta to jedyne miejsce, które opisuje, co można sprawdzić ręcznie.
Powiązane koncepcje
Dział zatytułowany „Powiązane koncepcje”- Rigi i izolacje: krok ponad sandboxem — gdy praca potrzebuje całej jednorazowej maszyny z granicą jądra, a nie opakowanego procesu
- Guardraile: warstwa polityk ponad systemowym poziomem bazowym i prawdziwe źródło zatwierdzeń
- Cykl życia wysyłania agentów: gdzie w przepływie wysyłania mieści się izolacja
- Tryby: jak tryb zawęża powierzchnię narzędzi i politykę poleceń
- Backendy sandboxa — dokumentacja referencyjna: backendy i ich konfiguracja
Powiązane poradniki
Dział zatytułowany „Powiązane poradniki”- Skonfiguruj polityki sandboxa: reguły akcji, egzekwowanie push i ustawienia systemu plików
- Uruchom diagnostykę agenta: co obejmują sprawdzenia diagnostyczne