Sandlåda och säkerhet
Varför isolering spelar roll här
Avsnitt med rubriken “Varför isolering spelar roll här”En agent läser filer, kör skalkommandon och anropar externa API:er å dina vägnar. Felet du bryr dig om är sällan illvilja — det är en agent som missläser en sökväg och skriver om din arbetsutcheckning, eller en som pushar en gren du inte hade hunnit granska klart. Så frågan produkten måste svara på är inte “går agenten att lita på” utan “hur stort är skadeomfånget när den har fel”.
Control Center svarar på det i lager, och lagren är medvetet oberoende. Saknas ett håller de andra fortfarande. Det spelar roll, eftersom det yttersta lagret — OS-sandlådan — är det enda villkorliga: det beror på att värden erbjuder en backend, och på Windows gör ingen värd det.
Vad som faktiskt begränsar en agent
Avsnitt med rubriken “Vad som faktiskt begränsar en agent”Fem saker begränsar en agentkörning på varje värd, med eller utan sandlåda. Ingen av dem är en kärngräns; alla är verkliga. Där värden erbjuder en backend sitter OS-sandlådan ovanpå dem, och “Vad sandlådan tillför och var den slutar” nedan beskriver vad det ändrar.
Sin egen copy-on-write-worktree. Varje samtal skapar sin egen utcheckning av
varje länkat repo, klonad copy-on-write genom den medföljande rift-native-delen
i stället för kopierad byte för byte, så isoleringen är billig nog att vara
standard. Det registrerade repositorium du lade till i arbetsytan är inte
katalogen agenten arbetar i — två samtal som rör samma repo kan inte se
varandras ändringar och ingen av dem redigerar din utcheckning.
Granulariteten är värd att vara precis om: worktreen är per samtal, inte per
agent. Två agenter i samma samtal får distinkta overlay-arbetskataloger som
delar samtalets repos/ — vilket är poängen, eftersom de samarbetar om en och
samma ändring — medan deras privata agentkataloger hålls åtskilda.
Copy-on-write är den enda backenden där rift finns: ett filsystem som inte kan
reflinka, eller en datakatalog på en annan volym än repot, misslyckas med
tilldelningen i stället för att falla tillbaka. Windows är undantaget — där finns
ingen copy-on-write-backend, så git worktree är backenden, inte en tyst
försämring.
Sökvägsbegränsning i den inbyggda harnessens filverktyg. Harnessens verktyg
read, write, edit, apply_patch, search, find och search_files
löser varje sökväg mot körningens arbetskatalog plus delade rötter — samtalets
repos/-katalog, där worktreena ligger, och ytans bilagekatalog när den finns —
och vägrar allt som hamnar utanför. Absoluta sökvägar accepteras bara om de redan
låg innanför. En symlänk inuti worktreen som pekar ut ur den löses också upp och
vägras. Det är därför en agent inte kan write till ~/.ssh ens utan en sandlåda
i bilden.
Lägets verktygsyta. Ett samtals läge avgör
vilka verktyg som materialiseras för körningen över huvud taget. I de
skrivskyddade lägena (plan, review, orchestrate) finns de muterande
verktygen helt enkelt inte i registret, och skalkommandopolicyen för dessa lägen
nekar varje muterande kommando direkt — git commit, git push, npm install,
docker och resten — i stället för att fråga om dem. Ett verktyg som inte finns
kan inte anropas, vilket är en starkare garanti än ett verktyg som finns och är
grindat.
De enhetliga åtgärdsräckena. Varje muterande verktyg deklarerar de
effektklasser det kan producera, och policy-motorn löser var och en till tillåt,
fråga eller neka innan anropet körs. En prompt utan ansluten godkännare löses
till nekat — en fråga som ingen kan svara på är aldrig ett ja. Se
Räcken för lösningsmodellen.
Mäklade inloggningsuppgifter och en sanerad miljö. Inloggningsuppgifter präglas per start och återkallas när körningen avslutas, och en körnings GitHub-token kan läsa men inte pusha — se “Pushar går genom Control Center” nedan. ACP, harnessens bash och Claude Code under den inbyggda inlindningen
får en miljö rensad från dynamiska länkarens och tolkens injektionsvektorer
(LD_PRELOAD, DYLD_INSERT_LIBRARIES, NODE_OPTIONS, GIT_SSH_COMMAND och
deras släktingar) innan processen startar. Claude Code via NoSandboxAdapter
ärver värdmiljön.
Var de gränserna tunnar ut
Avsnitt med rubriken “Var de gränserna tunnar ut”Harnessens filverktyg körs i serverprocessen, inte i en barnprocess, så ingen
sandlådeprofil skulle kunna begränsa dem ens om en sådan kopplades på — bara
bash går genom den sandlådade kommandoköraren. Deras inneslutning är
sökvägskontrollen som beskrivs ovan, och den kontrollen är därför bärande i
stället för en andra försvarslinje.
Verktyget bash är den andra platsen att vara precis på. Dess kommandon
utvärderas mot lägets kommandopolicy och startas sedan med en sanerad miljö och
worktreen som arbetskatalog. Där en backend finns lindas den starten in, så
sandlådan är det som hindrar ett skalkommando från att skriva utanför worktreen;
där ingen finns — Windows, eller Linux utan bwrap och socat — är
kommandopolicyen det enda mellan kommandot och resten av disken. I läget chat
nekar policyn hårt en fast lista (privilegieeskalering, förstörelse av system och
disk, container- och namnrymdsflykt) och frågar vid fjärrtillståndsmutationer
(git push, gh pr create, paketpubliceringar och -installationer). Allt som
står på ingen av listorna körs. Pipelines, kedjor och underskal tolkas och varje
delkommando utvärderas separat, där det mest restriktiva beslutet vinner.
Claude Code, när det används som adapter, är fallet där sandlådan väger tyngst.
Control Center startar det med --dangerously-skip-permissions, eftersom en
icke-interaktiv claude -p annars skulle blockera för evigt på sin egen
godkännandeprompt. Dess läs-, skriv- och redigeringsanrop sker inuti dess egen process och Control Center ser dem aldrig. Dess Bash-anrop skickas, innan de körs, till en hook på servern som klassificerar kommandoraden och frågar den matchande regeln, och de mcp__*-verktyg som Control Center serverar passerar grinden som vanligt. Där en backend
finns sker de osedda anropen fortfarande inuti sandlådeinlindningen, så OS:et är
gränsen runt dem. Där ingen finns är ingenting det. Varje adapter anger detta i
sin verkställighetsmatris — se adapternas ärlighetsmatris.
Pushar går genom Control Center
Avsnitt med rubriken “Pushar går genom Control Center”Det finns inga behörighetsflaggor per agent. Om en agent får skapa en commit, pusha, öppna en pull request, skriva till ett ärendesystem eller nå nätverket avgörs av räckena — tillåt, fråga först eller neka — och samma regler gäller oavsett vilken adapter som kör agenten.
En push är den åtgärd som måste hålla även mot en agent som försöker kringgå en
regel, så den framtvingas genom vad agenten har, inte genom vad den kör. Med en
GitHub-app konfigurerad får en körning i en GitHub-worktree en repo-avgränsad
installationstoken som kan läsa innehåll och arbeta med pull requests men inte
pusha. Körningens git-konfiguration skriver om varje push-URL till GitHub
(https://github.com/, git@github.com:, ssh://git@github.com/) till
agentkörningsgatewayen, en loopback-slutpunkt på cc_server. Gatewayen
läser vilka refs pushen uppdaterar, frågar regeln Pusha till en remote — så
en regel som är begränsad till grenar, till exempel “neka push till main”,
gäller — och skickar först därefter pushen vidare till GitHub med en skrivtoken
som den präglar för ändamålet och aldrig lämnar till agenten. En nekad push syns
i gits egen utdata:
! [remote rejected] main -> main (Control Center policy: …)Gatewayen skickar bara vidare pushar till worktreens eget repo eller till ett GitHub-repo som är länkat till arbetsytan. Fetch går direkt till GitHub med lästoken.
Andra skalkommandon möter en mjukare grind. Harnessens bash-verktyg och, via
en hook, Claude Codes Bash-verktyg klassificerar varje kommandorad och frågar
den regel den hör till: git commit, merge, rebase, cherry-pick,
revert och am är Skapa en commit; gh pr create är Öppna en pull
request; gh pr merge, review, comment och close är Publicera en
granskning eller sammanslagning; gh issue create, edit, comment och
close är Skriv till ett externt ärendesystem; paketinstallationer är
Installera ett paket; curl och wget är Använd nätverket. Det känner
igen det vanliga sättet att skriva en åtgärd, och en agent som är fast besluten
att dölja en (eval, en skriptfil, ett direkt API-anrop) kan göra det. Pushar
är inte beroende av det.
Ingen API-nyckel för ärenden läggs i en sandlåda. Agenter skriver ärenden via Control Centers MCP-verktyg, som frågar Skriv till ett externt ärendesystem.
Regeln Använd nätverket läses en gång, när körningen startar. En sandlådas
nätverk är antingen på eller av, så neka stänger av det medan tillåt och fråga
först lämnar det på; harnessens verktyg web_fetch och web_search frågar
fortfarande vid varje anrop. Inloggningsuppgifterna återkallas när körningen
rivs ner, tillsammans med varje skrivtoken som gatewayen har präglat, så ett
handtag överlever inte körningen.
Vad sandlådan tillför och var den slutar
Avsnitt med rubriken “Vad sandlådan tillför och var den slutar”Enumen SandboxBackend har tre värden — native (Seatbelt på macOS, bubblewrap
på Linux och WSL2), none och microvm. Inlindningen av agentkommandon använder
bara de två första: microvm är backenden med avskild VM för interaktiva
terminaler och stationer, inte en tyst uppgradering av en bash-inlindning. Att
förstå den inbyggda formen förklarar vad du vinner där en sådan kopplas på och vad
den aldrig skulle ge dig.
cc_server sonderar värden en gång vid start och där en backend svarar matar den
båda de skarvar som dispatchvägen har, eftersom transporterna inte delar någon.
Den första (SandboxPort) lindar in Claude Code-CLI:n. Den andra
(SandboxManager) lindar in ACP-adaptrar och den inbyggda harnessens
bash-verktyg. Att bara mata den ena skulle lämna de andra startande utan
inlindning, vilket är vad som brukade hända. På en värd utan backend, eller under
--sandbox off, startar de alla utan inlindning och servern säger vilket fall du
är i på sin startrad. ACP och harnessens bash sanerar fortfarande miljön;
Claude Code via NoSandboxAdapter ärver värdmiljön.
En yta väljer medvetet att stå utanför: den serverdrivna terminalen tar ett
backend-argument per session och cc_server konstruerar den med värdskalet som
standard, så en terminal du öppnar från appen körs utan sandlåda om inte en klient
uttryckligen anger native. Det är ett riktigt skal på servervärden, inte en
agentkörning.
På Linux bygger bubblewrap en monteringsnamnrymd, så agenten ser verkligen bara
det som bands in i den. På macOS tillämpar sandbox-exec en Seatbelt-profil som
är tillåtande som standard — en helt neka-som-standard-profil gör ett
interaktivt skal obrukbart, eftersom antalet dyld-, XPC- och Mach-anrop att räkna
upp är opraktiskt. macOS-profilen utgår därför från (allow default) och skär ut
nekanden: läsningar av hemliga sökvägar, en skrivpolicy återställd till neka med
uttryckliga tillåtanden ovanpå, unlink-blockering på varje nekad sökväg och dess
förfäder så att en namnändring inte kan kringgå ett skrivnekande, exec-nekanden
för alltid farliga binärer och för allt som kopierats in i en skrivbar katalog
samt nätverk begränsat till loopback-proxyportarna plus DNS.
Den praktiska konsekvensen: på Linux är agentens vy av filsystemet snävare; på macOS kan agenten fortfarande läsa det mesta av värden, och dina registrerade utcheckningar skyddas av uttryckliga skriv- och namnändringsnekanden i stället för av osynlighet. Båda är isolering på namnrymdsnivå, inte en säkerhetsgräns i kärnan — backendarna säger det själva i sina egna förmågsanteckningar.
Bind-monteringsuppsättningen är där gränsen mellan agenter dras. Overlay-dispatch
(Claude Code och ACP) monterar upp till fyra sökvägar, lösta per körning: agentens
overlay-arbetskatalog läs-och-skriv, agentens konfigurationskatalog skrivskyddad,
samtalets delade repos/-katalog läs-och-skriv när den finns och ytans
bilagekatalog skrivskyddad när den finns. En systeragents katalog monteras aldrig.
Harnessens bash-inlindning är snävare: den bind-monterar bara körningens
arbetskatalog. Det är samma form som harnessens sökvägskontroll i processen
framtvingar i mjukvara, så de två stämmer överens — men bara sökvägskontrollen
täcker harnessens egna filverktyg, som aldrig når en monteringsnamnrymd över huvud
taget.
Dina registrerade utcheckningar ansluter till skrivförbudsuppsättningen ovanpå
det, i varje läge och på alla tre inlindade vägar (Claude Code, ACP, harnessens
bash), och varje regel skrivs ut i både sin bokstavliga och sin
symlänksupplösta stavning. Den andra stavningen är inte en finess: kärnan matchar
en nekanderegel mot den upplösta sökvägen, och macOS gör upplösning till det
vanliga snarare än det exotiska fallet — /tmp är egentligen /private/tmp,
/var/folders/… är egentligen /private/var/folders/… — så en utcheckning som
nåddes via endera skulle ha varit skrivbar medan profilen såg ut att förbjuda det.
Nätverket, under en sandlåda, är en domän-allowlist per session som framtvingas
av loopback-proxyer i processen. Utgående trafik nekas som standard: med
nätverket av startar proxyna fortfarande men deras allowlist är tom, så varje
utgående anslutning nekas. Med det på bygger uppslagaren allowlisten från en
kurerad baslinje — LLM-API-leverantörer, git-hostning, paketregister,
modellkatalogen — plus GitHubs domäner, och matchar varje anslutnings målvärd
mot den. Jokertecken fungerar (*.example.com matchar underdomäner, inte
toppdomänen). En separat alltid-nekad-lista vinner över allowlisten och täcker
molnmetadatanslutningar och telemetrimottagare. Det som inte är konfigurerbart
är listan själv: specifikationen bär ett egressAllowlist-fält för extra
domäner, men inget i dispatchvägen fyller det, så i praktiken får varje körning
baslinjen plus GitHub och inget mer.
Nekanden, på macOS, är observerbara: en monitor avlyssnar systemloggen efter
Sandbox-nekanden, filtrerar bort känt brus, deduplicerar retry-stormar och lyfter
fram var och en som en SandboxViolation med namn på vad som försöktes och vad som blockerades. På Linux tolkar inlindningsvägen stderr efter
EPERM, Operation not permitted och Permission denied och sänder ut samma
händelsetyp — det finns ingen systemloggavlyssning. De anländer på körningens egen
händelseström, så ett nekat systemanrop syns i transkriptet i stället för bara i
en logg. Telemetrin finns bara medan en inbyggd sandlåda faktiskt lindar in
processen — på en värd utan backend finns inget att observera. Den är inte heller kopplad till räckena: en överträdelse kommer från ett nekat systemanrop, aldrig från en regeluppslagning.
Bekräftelser kommer från räckena, inte sandlådan
Avsnitt med rubriken “Bekräftelser kommer från räckena, inte sandlådan”Sandlådan har ingen godkännandeväg. Den startar, kör, sänder ut händelser och rivs ner; det finns ingenstans i den för en människa att svara på en fråga. När du ser en bekräftelse i en yta — godkänn den här pushen, godkänn den här borttagningen — restes den av räcken-motorn (eller harnessens kommandopolicy) innan åtgärden ens nådde körtiden.
Den här ordningen är avsiktlig. Ett godkännande som skedde efter systemanropet skulle vara en revisionspost, inte en grind. Det är också därför fail-closed-regeln bor i räckena: utan ansluten godkännare fattas beslutet innan något körs, och det är ett nekande.
Diagnostik
Avsnitt med rubriken “Diagnostik”Felsök en agent är den enda platsen som anger vad som kan kontrolleras för hand.
Relaterade koncept
Avsnitt med rubriken “Relaterade koncept”- Stationer och avskilda miljöer: steget ovanför sandlådan — när arbetet behöver en hel engångsmaskin med en kärngräns, inte en inlindad process
- Räcken: policylagret ovanför OS-golvet och var godkännanden faktiskt kommer ifrån
- Livscykel för agentdispatch: var isoleringen passar in i dispatchflödet
- Lägen: hur ett läge snävar in verktygsytan och kommandopolicyen
- Sandlådebackends: backendarna och deras konfiguration
Relaterade guider
Avsnitt med rubriken “Relaterade guider”- Konfigurera sandlådepolicyer: åtgärdsregler, pushverkställighet och filsystemshållning
- Felsök en agent: vad diagnoskontrollerna täcker