Sandbox e segurança
Por que o isolamento importa aqui
Seção intitulada “Por que o isolamento importa aqui”Um agente lê arquivos, executa comandos de shell e chama APIs externas em seu nome. A falha com que você se importa raramente é malícia — é um agente que lê errado um caminho e reescreve o seu checkout de trabalho, ou um que faz push de um branch que você não tinha terminado de revisar. Então a pergunta que o produto precisa responder não é “o agente é confiável”, e sim “qual é o raio de impacto quando ele erra”.
O Control Center responde a isso em camadas, e as camadas são independentes de propósito. Se uma delas faltar, as outras continuam valendo. Isso importa, porque a camada mais externa — o sandbox do sistema operacional — é a única condicional: ela depende de o host oferecer um backend, e no Windows nenhum host oferece.
O que de fato restringe um agente
Seção intitulada “O que de fato restringe um agente”Cinco coisas limitam uma execução de agente em todo host, com ou sem sandbox. Nenhuma delas é uma fronteira de kernel; todas são reais. Onde o host oferece um backend, o sandbox do sistema operacional fica acima delas, e “O que o sandbox acrescenta e onde ele para”, abaixo, cobre o que isso muda.
A própria worktree copy-on-write. Cada conversa provisiona o próprio checkout
de cada repositório vinculado, clonado via copy-on-write pelo nativo rift que
acompanha o produto, em vez de copiado byte a byte, de modo que o isolamento é
barato o suficiente para ser o padrão. O repositório registrado que você
adicionou ao espaço de trabalho não é o diretório em que o agente trabalha — duas
conversas que tocam o mesmo repositório não veem as edições uma da outra, e
nenhuma delas está editando o seu checkout.
Vale precisar a granularidade: a worktree é por conversa, não por agente. Dois
agentes na mesma conversa recebem diretórios de trabalho overlay distintos que
compartilham o repos/ daquela conversa — o que é justamente o ponto, já que
estão colaborando em uma única mudança —, enquanto seus diretórios privados de
agente permanecem separados. O copy-on-write é o único backend onde quer que o
rift seja distribuído: um sistema de arquivos que não faz reflink, ou um
diretório de dados em um volume diferente do repositório, faz a provisão falhar
em vez de recorrer a uma alternativa. O Windows é a exceção — lá não existe
backend copy-on-write, então o git worktree é o backend, e não uma degradação
silenciosa.
Confinamento de caminhos nas ferramentas de arquivo do harness embutido. As
ferramentas read, write, edit, apply_patch, search, find e
search_files do harness resolvem todo caminho em relação ao diretório de
trabalho da execução mais as raízes compartilhadas — o diretório repos/ da
conversa, onde as worktrees ficam, e o diretório de anexos do espaço, quando
existe — e recusam qualquer coisa que caia fora. Caminhos absolutos só são aceitos
se já estavam dentro. Um symlink dentro da worktree apontando para fora dela
também é resolvido e recusado. É por isso que um agente não consegue write em
~/.ssh mesmo sem nenhum sandbox em cena.
A superfície de ferramentas do modo. O
modo de uma conversa decide quais ferramentas
são materializadas para a execução. Nos modos somente leitura (plan, review,
orchestrate), as ferramentas de mutação simplesmente não estão no registro, e a
política de comandos de shell desses modos nega de saída todo comando de mutação
— git commit, git push, npm install, docker e o resto — em vez de pedir
confirmação para eles. Uma ferramenta ausente não pode ser chamada, o que é uma
garantia mais forte do que uma ferramenta presente e condicionada.
As barreiras de ação unificadas. Toda ferramenta de mutação declara as classes
de efeito que pode produzir, e o motor de políticas resolve cada uma como
permitir, pedir confirmação ou negar antes de a chamada rodar. Um prompt sem
nenhum aprovador conectado resolve como negado — uma pergunta que ninguém
consegue responder nunca é um sim. Consulte
Barreiras para conhecer o modelo de
resolução.
Credenciais intermediadas pelo broker e um ambiente higienizado. As credenciais são emitidas a cada inicialização e revogadas quando a execução termina, e o token do GitHub de uma execução pode ler, mas não fazer push — veja “Os pushes passam pelo Control Center” mais abaixo. ACP, o bash do harness e o
Claude Code sob o envelope nativo recebem um ambiente sem os vetores de injeção do
vinculador dinâmico e do interpretador (LD_PRELOAD, DYLD_INSERT_LIBRARIES,
NODE_OPTIONS, GIT_SSH_COMMAND e afins) antes de o processo começar. O Claude
Code via NoSandboxAdapter herda o ambiente do host.
Onde essas fronteiras ficam mais finas
Seção intitulada “Onde essas fronteiras ficam mais finas”As ferramentas de arquivo do harness rodam dentro do processo do servidor, não
em um processo filho, então nenhum perfil de sandbox poderia restringi-las mesmo
que houvesse um anexado — só o bash passa pelo executor de comandos com
sandbox. O confinamento delas é a verificação de caminho descrita acima, e essa
verificação é, portanto, estrutural, e não uma segunda linha de defesa.
A ferramenta bash é o outro ponto que exige precisão. Seus comandos são
avaliados em relação à política de comandos do modo e então iniciados com um
ambiente higienizado e a worktree como diretório de trabalho. Onde existe um
backend, esse início é envolvido, então é o sandbox que impede um comando de shell
de gravar fora da worktree; onde não existe — Windows, ou Linux sem bwrap e
socat —, a política de comandos é a única coisa entre o comando e o resto do
disco. No modo chat, a política nega sumariamente uma lista fixa (escalada de
privilégios, destruição do sistema e do disco, escape de contêiner e de
namespace) e pede confirmação em mutações de estado remoto (git push,
gh pr create, publicações e instalações de pacotes). Qualquer coisa que não
esteja em nenhuma das duas listas roda. Pipelines, encadeamentos e subshells são
analisados, e cada subcomando é avaliado separadamente, prevalecendo a decisão
mais restritiva.
O Claude Code, quando usado como adaptador, é o caso em que o sandbox carrega o
maior peso. O Control Center o inicia com --dangerously-skip-permissions,
porque um claude -p não interativo bloquearia para sempre no próprio prompt de
aprovação. Suas chamadas de leitura, escrita e edição acontecem dentro do próprio processo, e o Control Center nunca as vê. Suas chamadas Bash são enviadas, antes de rodarem, a um hook no servidor que classifica a linha de comando e pergunta à regra correspondente, e as ferramentas mcp__* que o Control Center serve passam pelo portão como sempre. Onde existe um backend, essas chamadas invisíveis ainda acontecem dentro
do envelope do sandbox, então o sistema operacional é a fronteira em volta delas.
Onde não existe, nada é. Cada adaptador declara isso na própria matriz de
aplicação — consulte a matriz de honestidade dos
adaptadores.
Os pushes passam pelo Control Center
Seção intitulada “Os pushes passam pelo Control Center”Não há flags de permissão por agente. Se um agente pode criar um commit, fazer push, abrir um pull request, escrever em um rastreador ou alcançar a rede é decidido pelas barreiras — permitir, perguntar primeiro ou negar —, e as mesmas regras valem qualquer que seja o adaptador que executa o agente.
O push é a ação que precisa resistir até a um agente que tenta contornar uma
regra, então ele é aplicado pelo que o agente tem, e não pelo que ele executa.
Com um GitHub App configurado, uma execução em uma worktree do GitHub recebe um
token de instalação com escopo de repositório que pode ler conteúdo e trabalhar
com pull requests, mas não fazer push. A configuração do git da execução
reescreve toda URL de push do GitHub (https://github.com/, git@github.com:,
ssh://git@github.com/) para o gateway de execuções de agentes, um endpoint
de loopback no cc_server. O gateway lê quais refs o push atualiza, pergunta à
regra Fazer push para um remoto — então uma regra limitada a branches, como
“negar push para main”, se aplica — e só então encaminha o push ao GitHub com
um token de escrita que ele emite para esse fim e nunca entrega ao agente. Um
push recusado aparece na própria saída do git:
! [remote rejected] main -> main (Control Center policy: …)O gateway só encaminha pushes para o repositório da própria worktree ou para um repositório do GitHub vinculado ao espaço de trabalho. Os fetches vão direto ao GitHub com o token de leitura.
Os demais comandos de shell encontram um portão mais brando. A ferramenta bash
do harness e, por meio de um hook, a ferramenta Bash do Claude Code
classificam cada linha de comando e perguntam à regra em que ela se encaixa:
git commit, merge, rebase, cherry-pick, revert e am são Criar um
commit; gh pr create é Abrir um pull request; gh pr merge, review,
comment e close são Publicar uma revisão ou fazer merge;
gh issue create, edit, comment e close são Escrever em um rastreador
externo; instalações de pacotes são Instalar um pacote; curl e wget
são Acessar a rede. Isso reconhece a grafia convencional de uma ação, e um
agente determinado a escondê-la (eval, um arquivo de script, uma chamada
direta à API) consegue. Os pushes não dependem disso.
Nenhuma chave de API de tickets é colocada em um sandbox. Os agentes escrevem tickets pelas ferramentas MCP do Control Center, que perguntam a Escrever em um rastreador externo.
A regra Acessar a rede é lida uma única vez, quando a execução começa. A
rede de um sandbox está ligada ou desligada, então negar a desliga, enquanto
permitir e perguntar primeiro a mantêm ligada; as ferramentas web_fetch e
web_search do harness continuam perguntando a cada chamada. As credenciais são
revogadas quando a execução é desmontada, junto com qualquer token de escrita
que o gateway tenha emitido, então um identificador não sobrevive à execução.
O que o sandbox acrescenta e onde ele para
Seção intitulada “O que o sandbox acrescenta e onde ele para”A enumeração SandboxBackend tem três valores — native (Seatbelt no macOS,
bubblewrap no Linux e no WSL2), none e microvm. O envelopamento de comandos
de agentes usa apenas os dois primeiros: microvm é o backend de VM fechada para
terminais interativos e rigs, não uma promoção silenciosa de um envelope de
bash. Entender a forma nativa explica o que você ganha onde ela está anexada e
o que ela nunca ia lhe dar.
O cc_server sonda o host uma vez na inicialização e, onde um backend responde,
alimenta as duas costuras do caminho de despacho, porque os transportes não
compartilham uma só. A primeira (SandboxPort) envolve a CLI do Claude Code. A
segunda (SandboxManager) envolve adaptadores ACP e a ferramenta bash do
harness embutido. Alimentar só uma deixaria as outras iniciando sem envelope, que
é o que costumava acontecer. Em um host sem backend, ou sob --sandbox off, todas
iniciam sem envelope, e o servidor diz em qual caso você está na sua linha de
inicialização. ACP e o bash do harness ainda higienizam o ambiente; o Claude
Code via NoSandboxAdapter herda o ambiente do host.
Uma superfície fica deliberadamente de fora: o terminal hospedado no servidor
aceita um argumento de backend por sessão, e o cc_server o constrói com o padrão
do shell do host, então um terminal que você abre no app roda sem sandbox, a menos
que um cliente nomeie native explicitamente. Esse é um shell de verdade no host
do servidor, não uma execução de agente.
No Linux, o bubblewrap monta um namespace de montagem, então o agente de fato vê
apenas o que foi vinculado nele. No macOS, o sandbox-exec aplica um perfil
Seatbelt que é permissivo por padrão — um perfil totalmente de negação por
padrão torna um shell interativo inutilizável, porque o número de chamadas de
dyld, XPC e Mach a enumerar é impraticável. O perfil do macOS, portanto, parte de
(allow default) e recorta negações: leitura de caminhos secretos, uma política
de escrita redefinida para negar com permissões explícitas por cima, bloqueio de
unlink em todo caminho negado e em seus ancestrais, para que um rename não
contorne uma negação de escrita, negações de exec para binários sempre perigosos
e para qualquer coisa copiada para um diretório gravável, e rede restrita às
portas do proxy de loopback mais o DNS.
A consequência prática: no Linux, a visão do agente sobre o sistema de arquivos é estreitada; no macOS, o agente ainda consegue ler a maior parte do host, e seus checkouts registrados são protegidos por negações explícitas de escrita e rename, e não por invisibilidade. Ambos são isolamento em nível de namespace, não uma fronteira de segurança do kernel — os próprios backends dizem isso nas suas notas de capacidade.
O conjunto de bind mounts é onde a fronteira entre agentes é traçada. O despacho
por overlay (Claude Code e ACP) monta até quatro caminhos, resolvidos por
execução: o diretório de trabalho overlay do agente em leitura e escrita, o
diretório de configuração do agente em somente leitura, o diretório repos/
compartilhado da conversa em leitura e escrita quando existe, e o diretório de
anexos do espaço em somente leitura quando existe. O diretório de um agente irmão
nunca é montado. O envelope de bash do harness é mais estreito: ele faz bind
mount somente do diretório de trabalho da execução. Essa é a mesma forma que a
verificação de caminho em processo do harness aplica em software, então as duas
concordam — mas só a verificação de caminho cobre as próprias ferramentas de
arquivo do harness, que nunca chegam a um namespace de montagem.
Seus checkouts registrados entram no conjunto de negação de escrita por cima
disso, em todo modo e nos três caminhos envolvidos (Claude Code, ACP, bash do
harness), e cada regra é emitida tanto na sua forma literal quanto na sua forma
resolvida por symlink. A segunda forma não é um capricho: o kernel casa uma regra
de negação com o caminho resolvido, e o macOS faz da resolução o caso comum, e não
o exótico — /tmp na verdade é /private/tmp, /var/folders/… na verdade é
/private/var/folders/… —, então um checkout alcançado por qualquer uma das duas
seria gravável enquanto o perfil parecia proibi-lo.
A rede, sob um sandbox, é uma lista de permissões de domínio por sessão,
aplicada por proxies de loopback em processo. A saída é negada por padrão: com a
rede desligada, os proxies ainda sobem, mas a lista de permissões deles está
vazia, então toda conexão de saída é recusada. Com ela ligada, o resolvedor
monta a lista de permissões a partir de uma base curada — provedores de API de
LLM, hospedagem de git, registros de pacotes, o catálogo de modelos —, mais os
domínios do GitHub, e compara com ela o host de destino de cada conexão.
Curingas funcionam (*.example.com casa subdomínios, não o domínio raiz). Uma
lista separada, sempre negada, prevalece sobre a lista de permissões e cobre
endpoints de metadados de nuvem e destinos de telemetria. O que não é
configurável é a própria lista: a especificação carrega um campo
egressAllowlist para domínios extras, mas nada no caminho de despacho o
preenche, então na prática toda execução recebe a base mais o GitHub, e nada
mais.
As negações, no macOS, são observáveis: um monitor lê o log do sistema em busca de
negações do Sandbox, filtra o ruído conhecido, deduplica tempestades de tentativas
repetidas e expõe cada uma como um SandboxViolation, nomeando o que foi tentado e o que foi bloqueado. No Linux, o caminho do envelope analisa o
stderr em busca de EPERM, Operation not permitted e Permission denied e
emite o mesmo tipo de evento — não há leitura do log do sistema. Eles chegam no
próprio fluxo de eventos da execução, então uma syscall negada aparece na
transcrição, e não apenas em um log. A telemetria só existe enquanto um sandbox
nativo está de fato envolvendo o processo — em um host sem backend, não há nada a
observar. Ela também não tem relação com as barreiras: uma violação vem de uma syscall negada, nunca de uma consulta de regra.
As confirmações vêm das barreiras, não do sandbox
Seção intitulada “As confirmações vêm das barreiras, não do sandbox”O sandbox não tem caminho de aprovação. Ele inicia, executa, emite eventos e se desmonta; não há nele nenhum lugar para uma pessoa responder a uma pergunta. Quando você vê uma confirmação em um espaço — aprove este push, aprove esta exclusão —, ela foi levantada pelo motor de barreiras (ou pela política de comandos do harness) antes de a ação sequer chegar ao runtime.
Essa ordem é deliberada. Uma aprovação que acontecesse depois da syscall seria um registro de auditoria, não um portão. É também por isso que a regra de falha fechada mora nas barreiras: sem nenhum aprovador conectado, a decisão é tomada antes de qualquer coisa rodar, e ela é uma negação.
Diagnóstico
Seção intitulada “Diagnóstico”Diagnosticar um agente é o único lugar que enuncia o que pode ser verificado à mão.
Conceitos relacionados
Seção intitulada “Conceitos relacionados”- Rigs e recintos: o degrau acima do sandbox — quando o trabalho precisa de uma máquina descartável inteira com uma fronteira de kernel, não de um processo envolvido
- Barreiras: a camada de políticas acima do piso do sistema operacional e de onde as aprovações de fato vêm
- Ciclo de vida do despacho de agentes: onde o isolamento se encaixa no fluxo de despacho
- Modos: como um modo estreita a superfície de ferramentas e a política de comandos
- Backends de sandbox: os backends e sua configuração
Guias relacionados
Seção intitulada “Guias relacionados”- Configurar políticas de sandbox: regras de ação, aplicação de push e postura do sistema de arquivos
- Diagnosticar um agente: o que as verificações de diagnóstico cobrem