Pular para o conteúdo

Barreiras — uma política única para toda ação que altera o estado

Os agentes no Control Center podem excluir arquivos, fazer push de branches, publicar pull requests e chamar APIs externas. Cada uma dessas ações é um tipo diferente de risco, e por muito tempo apenas uma delas — os comandos de shell — tinha uma política. As barreiras existem para responder a uma única pergunta de forma uniforme, para toda ação que altera o estado e que um agente pode realizar, independentemente do runtime ou da ferramenta que a propõe: isto é permitido, precisa de uma aprovação ou é negado?

Existe exatamente um motor de políticas. Ele é consultado em todos os pontos em que uma ação pode sair do sistema: o registro de ferramentas do harness integrado, o portão do despachante MCP no servidor e as mutações de operações de repositório. Uma regra escrita uma vez se aplica, portanto, tanto ao runtime integrado quanto às CLIs de agentes externas — não há um segundo caminho, mais silencioso, para contorná-la. A antiga política de comandos restrita ao bash continua funcionando: uma regra pode corresponder a um prefixo de comando em vez de a uma classe de ação, e as duas formas coexistem no mesmo armazenamento.

O motor não substitui, porém, o sandbox do sistema operacional, e o sandbox não o substitui. Eles respondem a perguntas diferentes: as barreiras decidem se uma chamada que o Control Center consegue ver pode acontecer, enquanto o sandbox delimita até onde o processo resultante pode chegar. O cc_server aplica o sandbox quando o host oferece um backend — mas Windows não oferece nenhum e o Linux precisa de bwrap e socat. Nesses hosts, as barreiras são a camada de imposição mais externa, e não uma política sobreposta a um piso, o que vale saber quando você decide o quão permissivo ser.

ActionClass: uma taxonomia deliberadamente fechada

Seção intitulada “ActionClass: uma taxonomia deliberadamente fechada”

Toda ferramenta que altera o estado declara a quais ActionClasses pertence, a partir de um conjunto fechado de treze classes de efeito. Cinco delas perguntam por padrão; as demais permitem, e é uma regra que muda isso.

Classe Padrão integrado O que abrange
fileDelete Perguntar primeiro Excluir um arquivo (uma escrita em forma de exclusão, rm)
gitPush Perguntar primeiro Fazer push para um remoto git
prCreate Perguntar primeiro Criar um pull request
prPublish Perguntar primeiro Publicar uma revisão, fazer merge de uma PR
vendorSyncWrite Perguntar primeiro Escrever em um sistema externo de tickets (Linear, Jira, sincronização do GitHub)
fileWriteOutsideWorktree Permitir Escrever um arquivo fora da árvore de trabalho isolada
gitCommit Permitir Criar um commit git
networkEgress Permitir Saída de rede (busca na web, HTTP arbitrário)
secretAccess Permitir Ler um segredo ou uma credencial
packageInstall Permitir Instalar um pacote (npm, pip, pub, brew)
processSpawn Permitir Gerar um processo (o shell e qualquer coisa que invoque o shell)
workspaceMutation Permitir Alterar a estrutura do espaço de trabalho (repositórios, espaços, agentes)
enclosureControl Permitir Controlar um recinto (rig): inicializar uma VM, enviar entrada para ela

As oito classes permitidas por padrão foram escolhidas com base na suposição de que o piso do sandbox se aplica abaixo delas. Isso acontece onde o host oferece um backend, e não acontece no Windows nem em uma máquina Linux sem bwrap e socat — então, se você quiser que fileWriteOutsideWorktree ou packageInstall sejam controladas ali, essa é uma regra que você mesmo precisa escrever.

O conjunto é fechado de propósito. Uma nova ferramenta que altera o estado e não declara nenhuma classe falha no teste de catraca, de modo que a taxonomia só pode crescer por uma decisão explícita e argumentada — “a proliferação da taxonomia é a morte deste recurso”. Treze classes bastam para expressar as políticas que as pessoas de fato escrevem (nunca fazer push sem perguntar, nunca excluir fora de uma árvore de trabalho, sempre permitir leituras) e são poucas o suficiente para que a interface de configurações continue compreensível.

Uma regra mapeia (scope, ActionClass | command prefix) → allow | prompt | deny. Os escopos se aninham do mais específico ao menos específico:

espaço > agente > espaço de trabalho > predefinição de modo > padrão integrado

A resolução segue a especificidade em primeiro lugar: o primeiro escopo com uma regra correspondente decide, e a resolução para ali. O mais restritivo é apenas um critério de desempate — entre duas regras igualmente específicas dentro de um mesmo escopo e ao combinar as várias classes que uma única ação declara. Dentro de um escopo, uma regra de comando corresponde ao prefixo de comando mais longo. Em nenhum lugar permitir vence negar.

Duas decisões trazem garantias rígidas:

  • Prompts com fechamento seguro. Um prompt sem aprovador conectado é resolvido como negado. Uma pergunta sem resposta nunca é um sim.
  • Combinação mais restritiva. Uma ação que abrange várias classes as combina da forma mais restritiva; quando várias classes perguntam, uma única confirmação lista todas.

Não existe, de propósito, nenhuma trava por turno. Cada chamada de ferramenta resolve a política do zero, de modo que a repetição de perguntas para a mesma ação é limitada pelo próprio “lembrar desta decisão” do operador, e não por qualquer coisa ligada ao turno.

Uma regra pode restringir os ARGUMENTOS de uma ação, não apenas o seu verbo. “Pode fazer push” e “pode fazer push para feature/*” são afirmações diferentes, e só a segunda é um controle — um portão de capacidade que autoriza apenas o verbo é a forma por trás da maioria dos incidentes publicados com agentes.

Uma restrição pode nomear:

  • caminhos — padrões glob (repos/**, !**/.env)
  • referências — referências do git, com negação por ! (['**', '!main'] lê-se “qualquer branch exceto main”)
  • hosts — destinos de rede, com curingas de subdomínio (*.internal)
  • comandos — prefixos de comando, correspondidos em um limite de palavra
  • tetos — maxCount / maxCents, de modo que “excluir até 50 arquivos” é expressável

Três propriedades decorrem de manter a gramática fechada e sem laços, e todas as três são o objetivo: toda regra se expressa como uma frase que você consegue ler de volta, toda negação pode nomear a restrição que correspondeu, e a política inteira pode ser enumerada — você consegue ver o que um agente pode fazer antes de ele ser executado.

Três regras que importam:

  • Uma regra restringida é mais específica do que uma regra não restringida no mesmo escopo, de modo que “negar push para main” vence “permitir push” sem que ninguém precise pensar na ordenação.
  • Uma regra restritiva (negar / perguntar) se aplica quando a requisição toca qualquer valor que ela nomeia; uma regra permissiva (permitir) só se aplica quando a requisição está inteiramente dentro dela. Ler as duas da mesma forma permitiria que um agente mascarasse um caminho proibido colocando um caminho inocente ao lado dele.
  • Uma regra que nomeia uma faceta sobre a qual a requisição nada diz não é aplicada nem ignorada: ela escala para uma aprovação. Ignorá-la em silêncio tornaria toda regra de branch protegida inerte diante de uma ferramenta que não informa sua referência; negá-la em silêncio recusaria todo push que o extrator não consegue descrever. Sem aprovador conectado, a regra de fechamento seguro já existente transforma essa escalação em negação.

O que uma regra de comando consegue e não consegue capturar

Seção intitulada “O que uma regra de comando consegue e não consegue capturar”

Uma regra commandPrefix corresponde em um limite de palavra: git push cobre git push origin main e não git pushx. Ela não normaliza o shell. git push (dois espaços), cd sub && git push e GIT_DIR=… git push são todas strings diferentes, e nenhuma delas corresponde a uma regra sobre git push.

Esse é um limite real, e ele é declarado em vez de disfarçado: regras de comando são uma conveniência para o operador moldar o comportamento comum dos agentes, não uma fronteira contra um agente adversário. As fronteiras são as classes de efeito (que uma ferramenta declara e não pode reescrever), a malha de comandos do modo sob o armazenamento de políticas e o sandbox do sistema operacional abaixo de ambos. Uma regra de comando que resolve para allow nunca afrouxa nenhuma das duas — a malha do modo é executada depois, e seu deny é definitivo.

Responder a um prompt com “lembrar desta” grava uma regra de política de verdade: com escopo (espaço / agente / espaço de trabalho), restrita por argumentos (aprovar um push para feature/login concede feature/**, e não main) e autorrevogável com base em um TTL explícito. Dizer sim pelas próximas horas não é o mesmo que reescrever em silêncio a sua política permanente, e as regras gravadas assim caem no mesmo armazenamento, com procedência remembered e uma expiração, de modo que podem ser revogadas antes do prazo.

Cada regra carrega um:

  • advisory — permite, mas registra que a regra correspondeu. Este é o caminho de adoção: implante uma regra rígida no modo advisory, leia o registro de auditoria por uma semana para ver exatamente o que ela teria bloqueado e então promova-a. Sem esse nível, políticas rígidas nunca são ativadas.
  • soft — nega, com possibilidade de exceção por quem detém a permissão de exceção, com justificativa registrada. A exceção é, ela própria, um evento auditado.
  • hard (o padrão) — nega. Nada a sobrepõe.

Um operador da instalação pode fixar uma política que nenhum administrador de espaço de trabalho consegue afrouxar. As regras gerenciadas ficam no servidor e são mescladas da forma mais restritiva com a cadeia de cada espaço de trabalho — elas não são um escopo na cabeça dessa cadeia, porque um allow gerenciado no topo da cadeia permitiria que a instalação se sobrepusesse a um deny de um espaço de trabalho, o oposto do que um limite deve fazer. Uma regra gerenciada, portanto, só pode apertar.

Definir CC_SERVER_MANAGED_POLICY como um arquivo JSON tem precedência sobre as regras armazenadas, o que é o que permite a um operador fixar uma postura que nenhuma interface de administração consegue inverter — a resposta para “posso impedir que meus desenvolvedores desativem os controles de segurança?”.

O dial de autonomia é um perfil, não um sistema paralelo

Seção intitulada “O dial de autonomia é um perfil, não um sistema paralelo”

O dial de autonomia de cada espaço é um perfil nomeado sobre esse mesmo armazenamento de políticas, e não um segundo mecanismo. Suas três configurações são armazenadas e enviadas como proposeOnly, actWithApproval e actFreely (as formas com hífen são apenas texto de interface, e autonomy.setForSpace rejeita qualquer outra coisa). Deixá-lo sem definição se comporta como actWithApproval.

O que cada uma faz com uma decisão resolvida:

  • proposeOnly nega de imediato toda ferramenta controlada. O agente ainda pode raciocinar e responder; ele simplesmente não pode agir, e a mensagem dele explica o motivo.
  • actWithApproval (e sem definição) é o portão de aprovação com fechamento seguro descrito acima.
  • actFreely pré-aprova tudo o que não foi resolvido como um deny rígido.

Esse último merece ser dito com todas as letras, em vez de suavizado: sob actFreely, uma decisão prompt não é escalada para um aprovador — ela é permitida e ninguém é consultado. Só uma regra deny explícita sobrevive ao dial. Portanto, actFreely é uma concessão deliberada de autonomia, e não um ajuste de conveniência, e a regra de fechamento seguro protege exatamente o caso em que isso importa (actWithApproval, o padrão).

A delegação entre agentes é protegida no ponto de estrangulamento delegate_task pelos quatro mecanismos de proteção: um limite de profundidade (padrão 3), detecção de ciclos, um teto de autonomia (um delegado nunca pode agir com mais autonomia do que quem o delegou — privilégio não pode ser lavado entregando o trabalho a um agente mais livre) e um envelope de orçamento (a delegação debita o orçamento restante do delegador e não pode criar mais). Cada um deles recusa de forma explícita, com o motivo do mecanismo devolvido ao agente palavra por palavra.

Nem todo runtime permite que o Control Center intercepte todas as ações. As CLIs de agentes externas executam algumas ferramentas nativamente, dentro do próprio processo, onde nenhum portão pode se posicionar. Em vez de fingir o contrário, cada adaptador carrega uma matriz de honestidade: por adaptador, se o Control Center filtra a superfície de ferramentas, intercepta chamadas de ferramentas, observa o contrato de conclusão, consegue ver as ferramentas nativas do executor e se as ferramentas executadas no processo são cobertas por um perfil de sandbox.

Duas entradas merecem ser lidas antes de você confiar em uma configuração. O harness integrado declara que suas ferramentas de arquivo executadas no próprio processo não são isoladas em sandbox — a superfície de ferramentas e este motor de políticas são a única fronteira de sistema de arquivos que ele tem. O Claude Code declara que suas próprias chamadas de ferramentas não são interceptáveis, porque ele é iniciado com --dangerously-skip-permissions; o Control Center vê suas chamadas mcp__* e, por meio de um hook, suas linhas de comando Bash, enquanto suas próprias ferramentas de leitura, escrita e edição são executadas sem serem vistas.

A tela de configurações mostra essa matriz, porque a única falha imperdoável de um sistema de barreiras é alegar uma cobertura que não existe.

As barreiras ficam em Configurações → Espaço de trabalho → Permissões dos agentes (/workspaces/<id>/settings/workspace/permissions). A tela reúne a matriz de políticas onde as regras são escritas, modelos de política, uma sonda hipotética, o registro de auditoria de autorização e a matriz de honestidade dos adaptadores.

A sonda recebe uma classe de ação (ou um comando) e um escopo e mostra qual regra decidiu e por quê. Como a resolução é pura e determinística — mesma ação, mesmo escopo, mesma decisão —, a resposta da sonda é a resposta que o agente vai receber.