Przejdź do głównej zawartości

Guardraile — jedna polityka dla każdej mutującej akcji

Agenty w Control Center mogą usuwać pliki, wypychać gałęzie, publikować pull requesty i wywoływać zewnętrzne API. Każda z tych akcji to inny rodzaj ryzyka, a przez długi czas politykę miała tylko jedna z nich — polecenia powłoki. Guardraile odpowiadają w jednolity sposób na jedno pytanie, dotyczące każdej mutującej akcji, jaką może podjąć agent, niezależnie od tego, który runtime lub które narzędzie ją proponuje: czy jest dozwolona, czy wymaga zatwierdzenia, albo czy jest odrzucona?

Silnik polityki jest dokładnie jeden. Odpytuje się go w każdym punkcie, w którym akcja może opuścić system: w rejestrze narzędzi wbudowanego harnessu, w bramie dyspozytora MCP po stronie serwera i przy mutacjach operacji na repozytoriach. Reguła zapisana raz obowiązuje więc zarówno wbudowany runtime, jak i zewnętrzne CLI agentów — nie ma drugiej, cichszej ścieżki wokół niej. Starsza polityka poleceń tylko dla basha nadal działa: reguła może dopasować prefiks polecenia zamiast klasy akcji, a obie postacie współistnieją w tym samym magazynie.

Silnik nie zastępuje jednak sandboxa systemu operacyjnego i sandbox nie zastępuje jego. Odpowiadają na różne pytania: guardraile decydują, czy wywołanie, które Control Center widzi, może w ogóle się wydarzyć, a sandbox ogranicza, gdzie może dotrzeć powstały proces. cc_server stosuje sandbox tam, gdzie host oferuje backend — ale Windows nie oferuje żadnego, a Linux wymaga bwrap i socat. Na takich hostach guardraile są najzewnętrzniejszą warstwą egzekwowania, a nie polityką posadowioną na podłodze — warto o tym wiedzieć, decydując, jak liberalne ma być ustawienie.

Każde mutujące narzędzie deklaruje, do których ActionClasses należy, ze zamkniętego zestawu trzynastu klas efektów. Pięć z nich domyślnie daje prompt; pozostałe dają allow, a zmienia to dopiero reguła.

Klasa Wbudowana wartość domyślna Co obejmuje
fileDelete prompt Usuwanie pliku (zapis o kształcie usunięcia, rm)
gitPush prompt Wypychanie do zdalnego repozytorium git
prCreate prompt Tworzenie pull requesta
prPublish prompt Publikowanie recenzji, scalanie PR-a
vendorSyncWrite prompt Zapis do zewnętrznego systemu zgłoszeń (synchronizacja Linear, Jira, GitHub)
fileWriteOutsideWorktree allow Zapis pliku poza izolowanym worktree
gitCommit allow Tworzenie commita git
networkEgress allow Ruch wychodzący do sieci (pobieranie z sieci, dowolne HTTP)
secretAccess allow Odczyt sekretu lub danych uwierzytelniających
packageInstall allow Instalowanie pakietu (npm, pip, pub, brew)
processSpawn allow Uruchamianie procesu (powłoka i wszystko, co ją wywołuje)
workspaceMutation allow Zmiana struktury obszaru roboczego (repozytoria, przestrzenie, agenty)
enclosureControl allow Sterowanie rigiem: uruchamianie VM, przesyłanie do niego danych wejściowych

Osiem klas z domyślnym allow wybrano przy założeniu, że pod nimi działa podłoga sandboxa. Działa tam, gdzie host oferuje backend, a nie działa na Windowsie ani na Linuksie bez bwrap i socat — jeśli więc chcesz mieć tam kontrolowane fileWriteOutsideWorktree albo packageInstall, tę regułę musisz napisać sam.

Zestaw jest zamknięty celowo. Nowe mutujące narzędzie, które nie deklaruje żadnej klasy, nie przechodzi testu zapadki, więc taksonomia może rosnąć tylko na podstawie wyraźnej, uzasadnionej decyzji — „rozrost taksonomii to śmierć tej funkcji”. Trzynaście klas wystarcza, by wyrazić polityki, które ludzie naprawdę piszą (nigdy nie wypychaj bez pytania, nigdy nie usuwaj poza worktree, odczyty zawsze dozwolone), a jest ich dość mało, by interfejs ustawień pozostał zrozumiały.

Reguła mapuje (scope, ActionClass | command prefix) → allow | prompt | deny. Zakresy zagnieżdżają się od najbardziej do najmniej szczegółowego:

przestrzeń > agent > obszar roboczy > preset trybu > wbudowana wartość domyślna

Rozstrzyganie idzie najpierw według szczegółowości: pierwszy zakres z pasującą regułą decyduje i na nim rozstrzyganie się kończy. Najbardziej restrykcyjny wariant służy tylko jako rozstrzygnięcie remisu — między dwiema równie szczegółowymi regułami w jednym zakresie i przy łączeniu kilku klas, które deklaruje jedna akcja. W obrębie zakresu reguła polecenia dopasowuje się po najdłuższym prefiksie polecenia. Allow nigdzie nie wygrywa z deny.

Dwie decyzje niosą twarde gwarancje:

  • Domyślne odrzucanie zapytań. prompt bez podłączonego zatwierdzającego rozstrzyga się jako odmowa. Brak odpowiedzi nigdy nie jest zgodą.
  • Najbardziej restrykcyjne łączenie. Akcja obejmująca kilka klas łączy je najbardziej restrykcyjnie; gdy o zgodę pyta kilka klas, jedno potwierdzenie wymienia je wszystkie.

Celowo nie ma tu zapadki na jedną turę. Każde wywołanie narzędzia rozstrzyga politykę od zera, więc powtarzanie pytania o tę samą akcję ogranicza wyłącznie własna decyzja operatora „zapamiętaj ten wybór”, a nie cokolwiek związanego z turą.

Reguła może ograniczać ARGUMENTY akcji, nie tylko sam jej czasownik. „Może wypychać” i „może wypychać do feature/*” to różne twierdzenia i tylko drugie jest kontrolą — bramka możliwości, która autoryzuje sam czasownik, to kształt stojący za większością opisanych incydentów z agentami.

Ograniczenie może wskazywać:

  • ścieżki — wzorce glob (repos/**, !**/.env)
  • refy — refy git, z negacją ! (['**', '!main'] czyta się jako „dowolna gałąź oprócz main”)
  • hosty — cele sieciowe, z symbolami wieloznacznymi subdomen (*.internal)
  • polecenia — prefiksy poleceń, dopasowywane na granicy słowa
  • pułapy — maxCount / maxCents, dzięki czemu „usuń maksymalnie 50 plików” da się wyrazić

Z utrzymania gramatyki zamkniętej i bez pętli wynikają trzy właściwości i wszystkie trzy są tu istotą: każda reguła daje się zapisać jako zdanie, które można odczytać, każde odrzucenie umie wskazać pasujące ograniczenie, a całą politykę można wyliczyć — widzisz, co agent może zrobić, zanim ruszy.

Trzy reguły, które mają znaczenie:

  • Ograniczona reguła jest bardziej szczegółowa niż nieograniczona w tym samym zakresie, więc „odmów wypychania do main” wygrywa z „zezwól na wypychanie”, bez zastanawiania się nad kolejnością.
  • Reguła restrykcyjna (deny / prompt) działa, gdy żądanie dotyka którejkolwiek wskazanej wartości; permisywna (allow) działa tylko wtedy, gdy żądanie mieści się w całości w niej. Czytanie obu tak samo pozwoliłoby agentowi wyprać zakazaną ścieżkę, dorzucając obok niej niewinną.
  • Reguła wskazująca aspekt, o którym żądanie nic nie mówi, nie jest ani stosowana, ani pomijana: eskaluje do zatwierdzenia. Ciche pominięcie uczyniłoby każdą regułę chronionej gałęzi bezużyteczną wobec narzędzia, które nie raportuje swojego refu; ciche odrzucenie odmawiałoby każdego wypchnięcia, którego ekstraktor nie umie opisać. Bez podłączonego zatwierdzającego istniejąca reguła domyślnego odrzucania zamienia tę eskalację w odmowę.

Reguła commandPrefix dopasowuje na granicy słowa: git push obejmuje git push origin main, a nie git pushx. Nie normalizuje powłoki. git push (dwie spacje), cd sub && git push i GIT_DIR=… git push to różne łańcuchy i żaden z nich nie pasuje do reguły o git push.

To realne ograniczenie i mówimy o nim wprost, zamiast je zakamuflować: reguły poleceń to udogodnienie dla operatora do kształtowania zwykłego zachowania agenta, a nie granica przeciwko agentowi wrogiego. Granicami są klasy efektów (które narzędzie deklaruje i nie może zapisać inaczej), siatka poleceń trybu pod magazynem polityki oraz sandbox systemu operacyjnego pod obiema. Reguła polecenia rozstrzygająca się jako allow nigdy nie poluzowuje żadnej z nich — siatka trybu działa później, a jej deny jest ostateczne.

Odpowiedź na zapytanie przez „zapamiętaj to” zapisuje prawdziwą regułę polityki: z zakresem (przestrzeń / agent / obszar roboczy), ograniczoną do argumentów (zatwierdzenie wypchnięcia do feature/login przyznaje feature/**, a nie main) i samoodwołującą się po upływie wyraźnego TTL. Zgoda na najbliższe kilka godzin to nie to samo, co ciche przepisanie trwałej polityki; zapisane reguły trafiają do tego samego magazynu z pochodzeniem remembered i terminem wygaśnięcia, więc można je odwołać wcześniej.

Każda reguła ma jeden:

  • advisory — zezwól, ale zapisz, że reguła zadziałała. To ścieżka wdrożenia: wdrażasz rygorystyczną regułę w trybie advisory, przez tydzień czytasz dziennik audytu i widzisz dokładnie, co byłaby zablokowała, a potem ją awansujesz. Bez tego poziomu rygorystyczne polityki nigdy się nie włączają.
  • soft — odmów, z możliwością nadpisania przez osobę mającą uprawnienie do nadpisania, z zapisanym uzasadnieniem. Samo nadpisanie jest zdarzeniem objętym audytem.
  • hard (domyślny) — odmów. Nic tego nie nadpisuje.

Operator instalacji może przypiąć politykę, której żaden administrator obszaru roboczego nie poluzuje. Reguły zarządzane żyją po stronie serwera i są łączone najbardziej restrykcyjnie z własnym łańcuchem każdego obszaru roboczego — nie są zakresem na jego czele, bo zarządzane allow na czele łańcucha pozwoliłoby instalacji nadpisać odmowę obszaru roboczego, czyli odwrotnie, niż ma działać klamra. Reguła zarządzana może więc wyłącznie zaostrzać.

Ustawienie CC_SERVER_MANAGED_POLICY na plik JSON ma całkowite pierwszeństwo przed zapisanymi regułami, dzięki czemu operator przypina postawę, której żaden interfejs administracyjny nie przełączy — odpowiedź na pytanie „czy mogę powstrzymać moich programistów przed wyłączaniem kontroli bezpieczeństwa?”.

Suwak autonomii dla przestrzeni to nazwany profil nad tym samym magazynem polityki, a nie drugi mechanizm. Jego trzy ustawienia są zapisywane i wysyłane jako proposeOnly, actWithApproval i actFreely (w interfejsie to tylko teksty „Tylko propozycje”, „Działa po zatwierdzeniu” i „Działa swobodnie”, a autonomy.setForSpace odrzuca wszystko inne). Stan nieustawiony zachowuje się jak actWithApproval.

Co każde z nich robi z rozstrzygniętą decyzją:

  • proposeOnly odrzuca z góry każde narzędzie objęte bramką. Agent nadal może rozumować i odpowiadać; po prostu nie może działać, a jego komunikat mówi dlaczego.
  • actWithApproval (i stan nieustawiony) to opisana wyżej brama zatwierdzeń z domyślnym odrzucaniem.
  • actFreely zatwierdza z góry wszystko, co nie rozstrzygnęło się jako twarde deny.

To ostatnie zasługuje na powiedzenie wprost, bez łagodzenia: w trybie actFreely decyzja prompt nie jest eskalowana do zatwierdzającego — zostaje dozwolona i nikt nie jest pytany. Suwak przepuszcza tylko wyraźną regułę deny. actFreely to więc świadome przyznanie autonomii, a nie ustawienie dla wygody, a reguła domyślnego odrzucania chroni dokładnie ten przypadek, w którym ma to znaczenie (actWithApproval, domyślny).

Delegowanie między agentami jest chronione w punkcie kontrolnym delegate_task przez wszystkie cztery zabezpieczenia: limit głębokości (domyślnie 3), wykrywanie cykli, pułap autonomii (delegat nigdy nie działa z większą autonomią niż jego delegujący — uprawnień nie da się wyprać, przekazując pracę swobodniejszemu agentowi) i koperta budżetowa (delegowanie obciąża pozostały budżet delegującego i nie może wykreować kolejnego). Każde odmawia głośno, a powód zabezpieczenia wraca do agenta dosłownie.

Nie każdy runtime pozwala Control Center przechwycić każdą akcję. Zewnętrzne CLI agentów uruchamiają część narzędzi natywnie, w swoim procesie, gdzie żadna bramka nie może stanąć. Zamiast udawać inaczej, każdy adapter niesie matrycę uczciwości: dla każdego adaptera mówi, czy Control Center filtruje powierzchnię narzędzi, przechwytuje wywołania narzędzi, obserwuje kontrakt zakończenia, widzi natywne narzędzia runnera i czy narzędzia działające w procesie obejmuje profil sandboxa.

Dwa wpisy warto przeczytać, zanim zaufasz konfiguracji. Wbudowany harness deklaruje, że jego narzędzia plikowe działające w procesie nie są w sandboxie — powierzchnia narzędzi i ten silnik polityki to jedyna granica systemu plików, jaką ma. Claude Code deklaruje, że jego własne wywołania narzędzi nie dają się przechwycić, bo uruchamia się z --dangerously-skip-permissions; Control Center widzi jego wywołania mcp__* oraz, przez hook, jego wiersze poleceń Bash, a jego własne narzędzia odczytu, zapisu i edycji działają niezauważone.

Ekran ustawień pokazuje tę matrycę, bo jedyna niewybaczalna porażka systemu guardrailów to twierdzenie o pokryciu, które nie istnieje.

Guardraile znajdują się w Ustawienia → Obszar roboczy → Uprawnienia agentów (/workspaces/<id>/settings/workspace/permissions). Na ekranie jest matryca polityki, w której zapisuje się reguły, szablony polityki, sonda „A co jeśli?”, dziennik audytu autoryzacji i matryca uczciwości adapterów.

Sonda przyjmuje klasę akcji (albo polecenie) i zakres, a następnie pokazuje, która reguła zdecydowała i dlaczego. Rozstrzyganie jest czyste i deterministyczne — ta sama akcja i ten sam zakres dają tę samą decyzję — więc odpowiedź sondy to odpowiedź, którą dostanie agent.