Hoppa till innehåll

Räcken – en policy för varje muterande åtgärd

Agenter i Control Center kan ta bort filer, pusha grenar, publicera pull requests och anropa externa API:er. Var och en av dessa är ett eget slag av risk, och länge hade bara en av dem – skalkommandon – en policy. Räckena finns för att svara på en och samma fråga för varje muterande åtgärd en agent kan utföra, oavsett vilken körare eller vilket verktyg som föreslår den: är det här tillåtet, kräver det ett godkännande, eller nekas det?

Det finns exakt en policymotor. Den tillfrågas vid varje punkt där en åtgärd kan lämna systemet: den inbyggda harnessens verktygsregister, MCP-dispatcherns grind på serversidan och mutationer i repo-operationer. En regel som skrivs en gång gäller därför både den inbyggda körningen och externa agent-CLI:er – det finns ingen andra, tystare väg runt den. Den äldre kommandopolicyn för enbart bash fungerar fortfarande: en regel kan matcha ett kommandoprefix i stället för en åtgärdsklass, och de två formerna samsas i samma regellager.

Motorn är dock ingen ersättning för OS-sandlådan, och sandlådan är ingen ersättning för motorn. De svarar på olika frågor: räckena avgör om ett anrop som Control Center kan se alls får ske, medan sandlådan begränsar var den resulterande processen kan nå. cc_server tillämpar sandlådan där värden erbjuder en backend – men Windows erbjuder ingen och Linux kräver bwrap och socat. På sådana värdar är räckena det yttersta verkställighetslagret i stället för en policy ovanpå ett golv, vilket är värt att veta när du bestämmer hur tillåtande du vill vara.

Varje muterande verktyg deklarerar vilka ActionClasses det tillhör, ur en sluten uppsättning på tretton effektklasser. Fem av dem frågar först som standard; resten tillåter, och det är en regel som ändrar på det.

Klass Inbyggd standard Vad den omfattar
fileDelete Fråga först Ta bort en fil (en raderande skrivning, rm)
gitPush Fråga först Pusha till en git-remote
prCreate Fråga först Skapa en pull request
prPublish Fråga först Publicera en granskning eller slå ihop en PR
vendorSyncWrite Fråga först Skriva till ett externt ärendesystem (Linear, Jira, GitHub-synk)
fileWriteOutsideWorktree tillåt Skriva en fil utanför den isolerade worktreen
gitCommit tillåt Skapa en commit
networkEgress tillåt Nätverksutgång (hämta från webben, godtycklig HTTP)
secretAccess tillåt Läsa en hemlighet eller autentiseringsuppgift
packageInstall tillåt Installera ett paket (npm, pip, pub, brew)
processSpawn tillåt Starta en process (shell och allt som går via shell)
workspaceMutation tillåt Ändra arbetsytans struktur (repos, ytor, agenter)
enclosureControl tillåt Styra en avskild miljö (station): starta en VM, skicka in indata

De åtta klasser som tillåter som standard valdes utifrån antagandet att sandlådegolvet ligger under dem. Det stämmer där värden erbjuder en backend, och det stämmer inte på Windows eller på en Linux-maskin som saknar bwrap och socat – så om du vill att fileWriteOutsideWorktree eller packageInstall ska gå genom en grind där, är det en regel du själv måste skriva.

Uppsättningen är sluten med avsikt. Ett nytt muterande verktyg som inte deklarerar någon klass faller i spärrtestet, så taxonomin kan bara växa genom ett uttryckligt, genomtänkt beslut – “taxonomispridning är döden för den här funktionen”. Tretton klasser räcker för att uttrycka de policyer folk faktiskt skriver (pusha aldrig utan att fråga, radera aldrig utanför en worktree, tillåt alltid läsning) och är få nog för att inställningsgränssnittet ska förbli begripligt.

En regel mappar (scope, ActionClass | command prefix) → allow | prompt | deny. Omfång ligger inuti varandra, från mest till minst specifikt:

yta > agent > arbetsyta > lägesförinställning > inbyggd standard

Uppslagningen sker med specificitet först: det första omfånget med en matchande regel avgör, och uppslagningen stannar där. Mest restriktiv används bara som utslagsregel – mellan två lika specifika regler inom ett omfång och när flera klasser som en enda åtgärd deklarerar kombineras. Inom ett omfång matchar en kommandoregel på det längsta kommandoprefixet. Tillåt vinner aldrig över neka någonstans.

Två beslut har hårda garantier:

  • Frågor stänger vid fel. En prompt utan ansluten godkännare löses till nekad. En obesvarad fråga är aldrig ett ja.
  • Mest restriktiv kombination. En åtgärd som spänner över flera klasser kombinerar dem så restriktivt som möjligt; när flera klasser frågar först listar en enda bekräftelse dem alla.

Det finns medvetet ingen spärr per tur. Varje verktygsanrop löser upp policyn på nytt från grunden, så upprepade frågor för samma åtgärd begränsas av operatörens eget “kom ihåg det här beslutet” i stället för av något som gäller per tur.

En regel kan begränsa ARGUMENTEN i en åtgärd, inte bara verbet. “Får pusha” och “får pusha till feature/*” är olika påståenden, och bara det andra är en kontroll – en behörighetsgrind som bara auktoriserar verbet ligger bakom de flesta publicerade agentincidenter.

En begränsning kan namnge:

  • sökvägar – globmönster (repos/**, !**/.env)
  • refs – git-refar, med ! som negering (['**', '!main'] läses som “vilken gren som helst utom main”)
  • värdar – nätverksdestinationer, med jokertecken för underdomäner (*.internal)
  • kommandon – kommandoprefix, matchade på en ordgräns
  • tak – maxCount / maxCents, så att “radera upp till 50 filer” går att uttrycka

Tre egenskaper följer av att grammatiken hålls sluten och utan loopar, och alla tre är själva poängen: varje regel kan skrivas ut som en mening du kan läsa tillbaka, varje avslag kan namnge den begränsning som matchade, och hela policyn kan räknas upp – du kan se vad en agent får göra innan den kör.

Tre regler som spelar roll:

  • En begränsad regel är mer specifik än en obegränsad regel i samma omfång, så “neka push till main” slår “tillåt push” utan att någon behöver tänka på ordningen.
  • En restriktiv regel (neka / fråga först) gäller när begäran rör något värde som regeln namnger; en tillåtande regel (tillåt) gäller bara när begäran ligger helt och hållet inom den. Om båda lästes på samma sätt skulle en agent kunna tvätta en förbjuden sökväg genom att lägga en oskyldig bredvid.
  • En regel som namnger en aspekt som begäran inte säger något om tillämpas varken eller hoppas över: den eskalerar till ett godkännande. Att tyst hoppa över skulle göra varje regel för skyddade grenar verkningslös mot ett verktyg som inte rapporterar sin ref; att tyst neka skulle avvisa varje push som extraheraren inte kan beskriva. Utan ansluten godkännare omvandlar den befintliga regeln om stängning vid fel eskaleringen till ett avslag.

En commandPrefix-regel matchar på en ordgräns: git push täcker git push origin main och inte git pushx. Den normaliserar inte skalet. git push (två mellanslag), cd sub && git push och GIT_DIR=… git push är alla olika strängar, och ingen av dem matchar en regel om git push.

Det är en verklig gräns, och den sägs rakt ut i stället för att slätas över: kommandoregler är en bekvämlighet för operatören för att forma en agents vanliga beteende, inte en gräns mot en fientlig agent. Gränserna är effektklasserna (som ett verktyg deklarerar och inte kan skriva om), lägets kommandonät under regellagret och OS-sandlådan under båda. En kommandoregel som löses till allow luckrar aldrig upp någon av dem – kommandonätet körs efteråt och dess deny är slutgiltigt.

Att svara på en fråga med “kom ihåg det här” skriver en riktig policyregel: avgränsad till ett omfång (yta / agent / arbetsyta), argumentbegränsad (att godkänna en push till feature/login ger feature/**, inte main) och självåterkallande efter en uttrycklig TTL. Att säga ja de närmaste timmarna är inte samma sak som att tyst skriva om din permanenta policy, och reglerna den skriver hamnar i samma regellager med ursprunget remembered och ett utgångsdatum, så de kan återkallas i förtid.

Varje regel har en:

  • rådgivande – tillåt, men registrera att regeln matchade. Det här är införandevägen: rulla ut en strikt regel i rådgivande läge, läs granskningskedjan i en vecka för att se exakt vad den skulle ha blockerat och skärp den sedan. Utan den här nivån slås strikta policyer aldrig på.
  • mjuk – neka, men kan åsidosättas av någon med behörigheten att åsidosätta, med en registrerad motivering. Själva åsidosättandet är en granskningsbar händelse.
  • hård (standard) – neka. Inget åsidosätter den.

En installationsoperatör kan låsa policy som ingen arbetsytas administratör kan lätta på. Hanterade regler ligger på serversidan och slås samman så restriktivt som möjligt med varje arbetsytas egen kedja – de är inte ett omfång i toppen av kedjan, för en hanterad allow i toppen skulle låta installationen åsidosätta en arbetsytas neka, vilket är motsatsen till vad en åtstramning är till för. En hanterad regel kan därför bara strama åt.

Att sätta CC_SERVER_MANAGED_POLICY till en JSON-fil går helt före de lagrade reglerna, vilket är det som låter en operatör låsa en hållning som inget admin-gränssnitt kan ändra – svaret på “kan jag hindra mina utvecklare från att stänga av säkerhetskontrollerna?”.

Autonomireglaget är en profil, inte ett parallellt system

Avsnitt med rubriken “Autonomireglaget är en profil, inte ett parallellt system”

Autonomireglaget per yta är en namngiven profil ovanpå samma regellager, inte en andra mekanism. Dess tre inställningar lagras och skickas som proposeOnly, actWithApproval och actFreely (formerna med bindestreck är bara UI-text, och autonomy.setForSpace avvisar allt annat). Lämnas det oanvänt beter det sig som actWithApproval.

Vad var och en gör med ett upplöst beslut:

  • proposeOnly nekar varje grindat verktyg direkt. Agenten kan fortfarande resonera och svara; den kan bara inte agera, och dess meddelande säger varför.
  • actWithApproval (och oanvänt) är godkännandegrinden som stänger vid fel, som beskrivs ovan.
  • actFreely förhandsgodkänner allt som inte löstes till ett hårt deny.

Den sista förtjänar att sägas rakt ut i stället för att lindas in: under actFreely eskaleras inte ett prompt-beslut till en godkännare – det tillåts och ingen tillfrågas. Bara en uttrycklig deny-regel överlever reglaget. Så actFreely är en medveten gåva av autonomi, inte en bekvämlighetsinställning, och regeln om stängning vid fel skyddar precis det fall där den spelar roll (actWithApproval, standard).

Delegering mellan agenter bevakas vid kontrollpunkten delegate_task av alla fyra vakter: en djupgräns (standard 3), cykeldetektering, ett autonomitak (en delegerad kan aldrig agera med mer autonomi än sin delegerare – privilegier kan inte tvättas genom att skicka arbete till en friare agent) och en budgetram (delegering debiterar delegerarens återstående budget och kan inte skapa mer). Var och en avvisas högt och tydligt, med vaktens skäl återgivet ordagrant till agenten.

Det är inte alla körare som låter Control Center fånga upp varje åtgärd. Externa agent-CLI:er kör vissa verktyg internt i den egna processen, där ingen grind kan sitta. I stället för att låtsas något annat bär varje adapter en ärlighetsmatris: per adapter, huruvida Control Center filtrerar verktygsytan, fångar upp verktygsanrop, observerar slutförandekontraktet, kan se körarens inbyggda verktyg och huruvida verktyg som körs i processen omfattas av en sandlådeprofil.

Två poster är värda att läsa innan du litar på en konfiguration. Den inbyggda harnessen deklarerar att dess filverktyg i processen inte omfattas av sandlådan – verktygsytan och den här policymotorn är den enda filsystemgräns den har. Claude Code deklarerar att dess egna verktygsanrop inte går att fånga upp, eftersom den startas med --dangerously-skip-permissions; Control Center ser dess mcp__*-anrop och, via en hook, dess Bash-kommandorader, medan dess egna läs-, skriv- och redigeringsverktyg körs osedda.

Inställningsskärmen visar den här matrisen, för det enda oförlåtliga misslyckandet för ett räckessystem är att påstå en täckning som inte finns.

Räckena finns under Inställningar → Arbetsyta → Agentbehörigheter (/workspaces/<id>/settings/workspace/permissions). Skärmen har policymatrisen där regler skrivs, policymallar, en tänk-om-probe, auktoriseringsgranskningskedjan och adapternas ärlighetsmatris.

Proben tar en åtgärdsklass (eller ett kommando) och ett omfång och visar vilken regel som avgjorde och varför. Eftersom uppslagningen är ren och deterministisk – samma åtgärd, samma omfång, samma beslut – är probens svar det svar agenten kommer att få.