Uma camada útil, com limites conhecidos
O ai-jail é feito para conter os erros de um agente confiável e manter o estrago dentro do projeto: o diretório do projeto é o raio de impacto, e o seu home, as suas chaves e a rede ficam fora dele. É um sandbox de processo que compartilha o seu kernel, então não é uma fronteira contra código hostil nem contra um atacante motivado, e não substitui uma máquina virtual descartável.
Para quem assina embaixo, e depois para o engenheiro de segurança.
O que ele foi feito para barrar
Um agente confiável ainda erra um comando, roda um script que sobe acima da raiz do projeto, ou é enganado por uma página web ou um pacote. É isto que ele vai procurar em seguida, e não vai encontrar.
↔ Deslize para o lado para ver o diagrama inteiro
Fora de alcance
- Seu diretório home e as credenciais que estão nele. O agente recebe um home novo e vazio, então
~/.sshe~/.awsnão existem lá dentro. - Segredos no seu shell. O ai-jail reduz as variáveis de ambiente a uma allowlist pequena, então os tokens exportados ficam de fora. A única exceção é a chave de API do próprio harness iniciado, por exemplo
ANTHROPIC_API_KEYpara o Claude, copiada por padrão junto com o login dele;--no-agent-stateimpede isso. - A rede. Ela fica fechada, com duas exceções que passam pelo proxy filtrado: uma lista curta de registries de pacotes quando as toolchains estão ligadas, o padrão no Linux, e, para um agente conhecido, o host da API dele, nas duas plataformas. Os hosts de telemetria continuam fechados.
--no-networkfecha as duas. - A memória compartilhada do host, o seu servidor gráfico e o seu clipboard. A saída do agente passa por um filtro que descarta sequências de terminal de clipboard e de consulta.
- Digitar no seu terminal. O Linux bloqueia a chamada
TIOCSTI, que simula teclas, e o macOS restringe o controle de terminal ao único terminal que o ai-jail criou para aquela execução. - As próprias regras. Um arquivo
.ai-jaildentro de um repositório não é confiável: ele pode apertar o sandbox, nunca abrir.
Onde o muro acaba
A jaula e o seu sistema se apoiam no mesmo kernel. Um bug nesse kernel, num driver ou no backend do sandbox é um caminho por fora do muro, e o ai-jail não tem o que fazer a respeito. Uma máquina virtual traz o próprio kernel, e por isso é a fronteira mais forte.
↔ Deslize para o lado para ver o diagrama inteiro
O que isso significa para os seus dados
- Segredos dentro do projeto. Um arquivo
.envno repositório pode ser lido, a menos que você o mascare. Veja como mascarar arquivos. - As máscaras cobrem só os caminhos que existem quando o sandbox inicia. Um arquivo de segredos criado depois, durante a sessão, não fica escondido.
- Um host permitido é confiável por inteiro. Liberar
github.comcom--allow-hostdeixa o agente fazer push em qualquer repositório de lá, não só nos seus. --networkabre a rede inteira, então tudo o que o agente consegue ler, o seu projeto incluído, ele consegue enviar para fora.- O ai-jail monta o login do próprio agente por padrão, com leitura e escrita, então um agente enganado pode usá-lo enquanto a execução durar. O projeto documenta isso como uma troca por conveniência;
--no-agent-statedá uma execução sem login. - O git do lado do host confia no
.git/do projeto, que é gravável. Um programa nomeado em.git/config, comocore.fsmonitoroucore.pager, ou colocado em.git/hooks/roda como você, fora do sandbox, na próxima vez que um comando git correspondente rodar no host; paracore.fsmonitorbasta umgit status, mesmo um que o seu editor roda em segundo plano, e nada aparece nogit diff.--lockdownfecha isso deixando o projeto somente leitura, e--map .gitmantém o repositório somente leitura numa execução normal, ao custo de não poder commitar de dentro. Depois de uma sessão que te surpreendeu, leia o.git/confige liste o.git/hookscom ferramentas de arquivo comuns antes de rodar git no host, porque o própriogit config --listabre o pager configurado. --maske--deny-pathmudam só o que o sandbox vê. Qualquer coisa fora do sandbox que age em nome do agente continua lendo os arquivos reais: o daemon do Docker com--docker, um servidor MCP alcançado por um socket mapeado ou um serviço atrás de--forward-port.
Para o engenheiro de segurança
- Vulnerabilidades de kernel e de drivers. Todo processo em sandbox fala com o mesmo kernel que o resto da sua máquina.
- Bugs no seu emulador de terminal, principalmente com
--terminal-passthrough, que desliga o filtro de saída. - Defeitos no próprio backend do sandbox: bubblewrap, Landlock e seccomp no Linux,
sandbox-execno macOS. - O Landlock, as regras do próprio kernel, só cerca sockets Unix abstratos e sinais em kernels 6.12 ou mais novos, em modo best-effort. Kernels mais antigos ficam só com as regras de sistema de arquivos e de rede, então com
--x11,--networkou--systemd-useros sockets abstratos e os sinais do host não ficam cercados. - Side channels e algumas classes de comunicação entre processos.
- A saída filtrada é só TCP. Não há UDP, então um programa que precisa dele não funciona; por outro lado, também não vaza por ali.
- DNS com
--allow-host. No Linux, as consultas de nome dentro do sandbox falham, então não existe canal de DNS para fora. No macOS o resolver do sistema não fica cercado, então sobra um canal de DNS de baixa largura de banda, a mesma brecha que a Anthropic documenta para o sandbox-runtime dela. - O proxy vê o host, a porta e o horário de cada conexão. O que passa lá dentro fica entre as duas pontas. Como a saída filtrada é construída.
- As credenciais fantasma transferem a confiança para o próprio ai-jail. Com
--secret, as requisições para o host vinculado passam pelo ai-jail em claro, para que ele insira a chave real, e ele as criptografa de novo na saída. Os túneis para todos os outros hosts continuam opacos. Só cobre clientes que conseguem falar HTTP puro com o proxy para esse host. - Credenciais que você passa com
--github,--aws,--kube,--gcloudou--docker-config. Somente leitura protege o arquivo contra alterações, não a credencial contra o uso: qualquer coisa dentro do sandbox pode agir como você naquele serviço enquanto ela for válida. - No macOS, o
sandbox-execfoi marcado como obsoleto pela Apple e não equivale ao isolamento do Linux. Veja as diferenças no macOS.
Opções que abrem o muro
Tudo o que está abaixo fica desligado até você ligar, exceto duas opções que vêm ligadas por padrão: --toolchains no Linux e --agent-state em todo lugar. A maioria dá acesso real ao agente. Como definir cada um está na página Configurar.
Mostrar todas as opções e consequências
| Opção | O que qualquer coisa no sandbox passa a poder fazer |
|---|---|
--network | Enviar para qualquer lugar tudo o que consegue ler, sem nenhuma restrição de tráfego. O padrão libera só o host da API de um agente conhecido e, no Linux, os registries de pacotes; --allow-host é a alternativa mais apertada. |
--allow-host | Alcançar exatamente os hosts que você lista, e seus subdomínios, por TCP, através de um proxy embutido. Cada host listado é confiável por inteiro. Sua lista soma ao host da API do próprio agente; --no-network com uma lista deixa só essa lista. |
--forward-port | Usar por inteiro um serviço no loopback do seu host. O que o servidor MCP, o socket do editor ou o banco de dados nessa porta aceitar, o agente pode pedir, sem allowlist, sem inspeção e sem registro no log de auditoria. Todas as outras portas do host continuam fechadas. Só no Linux, e recusado com --network. |
--toolchains | Ligado por padrão no Linux. Rodar as suas toolchains: ~/.cargo/bin e ~/.rustup somente leitura, um cache de dependências próprio da jaula em ~/.local/share/ai-jail/cache/ que sobrevive entre execuções, e saída filtrada para os registries de pacotes. Os caches reais do host nunca são montados. Desligado sob --lockdown e com --no-toolchains. |
--secret | Nada de novo. O valor real dessa chave nunca entra no sandbox: o agente vê um placeholder, e o proxy troca pelo valor real só nas requisições em HTTP puro para o único host ao qual você a vinculou. O próprio processo do ai-jail vê essa requisição em claro. |
--audit-log | Nada de novo. Esta opção não enfraquece nada: ela grava uma linha por inicialização num arquivo com modo 0600 fora do sandbox, então o agente nunca o vê. |
--docker | Agir como root na sua máquina por meio do daemon do Docker. |
--kvm | Abrir /dev/kvm, a interface de virtualização do kernel, para que QEMU, Firecracker ou o emulador do Android rodem na velocidade do hardware. Só no Linux. Os guests só têm a rede do sandbox, e nenhum /dev/net/tun é passado. |
--x11 | Capturar teclas e tirar screenshots de outras janelas X11. |
--systemd-user | Pedir ao seu gerenciador de usuário do systemd para rodar serviços no host. |
--inherit-env | Ler o ambiente inteiro do seu shell, com os segredos. Para chaves de API, --env-from-file passa um arquivo no lugar. |
--agent-state | Ligado por padrão. Usa o login salvo do próprio agente, por exemplo o ~/.claude do Claude ou o ~/.local/share/devin e o ~/.config/devin do Devin, montado com leitura e escrita, mais a chave de API dele copiada do seu shell quando estiver definida, então ele começa logado. Tudo o que está no sandbox pode usar essas credenciais enquanto a execução durar. --no-agent-state dá uma execução isolada, sem login; desligado no --lockdown. |
--github | Agir como você no GitHub. O diretório de configuração do gh é montado somente leitura, e o token ativo do github.com é lido do keyring do sistema no host e repassado como GH_TOKEN para essa execução, nunca na linha de comando nem em disco. Somente leitura protege o arquivo; o token continua funcionando enquanto for válido. |
--aws | Agir como você na AWS com ~/.aws, montado somente leitura. |
--kube | Agir como você em todos os clusters de ~/.kube, montado somente leitura. |
--gcloud | Agir como você no Google Cloud com ~/.config/gcloud, montado somente leitura. |
--docker-config | Usar seus logins de registries de containers em ~/.docker/config.json, montado somente leitura. |
--audio | Gravar e reproduzir áudio enquanto estiver ligada. |
--no-private-home | Ver o seu diretório home real. É uma exceção ampla; prefira mapear só o caminho de que você precisa. |
Na dúvida, ele se recusa a iniciar
Um sandbox que inicia, sem avisar, mais fraco do que você pediu é pior do que um que para com um erro. Nestes casos o ai-jail para.
- Um arquivo de configuração que existe e está inválido ou ilegível.
- Um
.ai-jailde projeto que é um symlink. Um~/.ai-jailglobal que é symlink só é seguido até um arquivo regular que pertence a você, em que ninguém mais pode escrever, fora do projeto. --lockdownquando o Landlock, as regras de sistema de arquivos do próprio kernel, está ausente, parcial ou falha ao ser aplicado.--lockdown --no-landlocktambém é rejeitado.--allow-tcp-port. Essa interface não consegue restringir UDP, então a flag é aceita por compatibilidade, a inicialização falha e o erro aponta para--allow-host.--networkjunto com--allow-host, ou--browsercom--allow-host. As duas se contradizem, então o ai-jail se recusa a iniciar.--forward-portjunto com--network, quando o loopback do host já está alcançável; fora do Linux; e para a porta 0 ou uma porta abaixo de 1024, que o sandbox não consegue abrir. Uma entradaforward_portsna tabela base do arquivo global esbarra nos dois primeiros casos em toda inicialização com navegador e toda inicialização no macOS.- Um arquivo de
--env-from-fileque é um symlink, que o grupo ou todo mundo consegue ler, que pertence a outra pessoa ou que está dentro do projeto. - Um
BWRAP_BINque aponta para um binário do bubblewrap que alguém além do root poderia ter trocado. Ele é ignorado com um aviso, e sem um bubblewrap confiável a inicialização falha. - Um overlay que não consegue ser montado: origem ausente, destino que se sobrepõe a outro mapeamento ou falha no armazenamento. Desde a 2.4 a inicialização aborta em vez de cair para um bind simples de leitura e escrita dos arquivos reais.
Como o projeto é verificado
Um único mantenedor toca o projeto, tudo em aberto. Abaixo está o que é verificado e o que não é.
- 23
- testes de integração de fuga do sandbox, que tentam escrever em
/usr, ler~/.ssh, chamarptraceebpf, e esperam falhar - 959
- testes unitários na árvore de código-fonte na versão 2.9.0
- 0
- requisições de rede na inicialização. A checagem de atualização é opt-in, com
--update-check
Correções de segurança recentes
- 2.9.0:
--x11sozinho passa o X11 para dentro. Antes, ele não fazia nada sem--display, em silêncio. - 2.9.0: O auto-save não grava mais no arquivo de projeto opt-ins que a carga não confiável depois recusa, então um arquivo salvo nunca avisa na execução seguinte.
- 2.9.0: macOS: o Codex inicia de novo sob o Seatbelt com um
psshim privado no lugar dopssetuid do host. - 2.8.0: macOS: o perfil do seatbelt mantém o login do Claude Code no Keychain e libera o socket de controle do Codex, então os dois iniciam logados.
- 2.8.0: Pedidos de posição do cursor vindos de um launcher são respondidos pelo terminal virtual, então os launchers não travam mais.
- 2.6.2:
--lockdownsempre isola o network namespace, mesmo com--network. Antes, esse par contraditório deixava o namespace compartilhado. - 2.6.2: Os valores de
--enve--env-from-filenão ficam mais na linha de comando do launcher, onde qualquer processo rodando como você podia lê-los. Eles passam por um arquivo em memória. - 2.4.0: Os metadados de worktree de um repositório não conseguem mais montar um diretório qualquer do host em leitura e escrita.
- 2.4.0: Uma entrada duplicada no ambiente não deixa mais o valor real de uma chave
--secretescapar para dentro do sandbox. - 2.4.0: Um comando de navegador num
.ai-jailde projeto não confiável não desliga mais o lockdown. - 2.4.0:
TIOCSTIé comparado como o kernel lê, em 32 bits, então uma variante com bit alto não passa mais pelo seccomp. - 2.4.0: O Landlock cerca sockets Unix abstratos e sinais em kernels 6.12 ou mais novos.
- 2.4.0: macOS: o acesso ao terminal fica restrito aos terminais da própria execução, em vez de todos os
/dev/ttys*. - 2.4.0: macOS: caminhos com
--masksão negados para escrita, além de leitura. - 2.4.0: O filtro de saída não desmonta mais com um ESC duplicado, que podia contrabandear uma escrita no clipboard.
- 2.4.0: As saídas de emergência de teste do proxy ficam fora dos builds de release.
Como uma release é feita
- As tags de release são assinadas, e o CI verifica cada uma contra um fingerprint de chave fixado no repositório.
- Cada GitHub Action é fixada num SHA de commit, e as actions são restritas a uma allowlist.
- Os builds usam uma toolchain de Rust fixada e dependências com
--locked. - Os binários de macOS são assinados e notarizados pela Apple.
- Cada arquivo compactado vem com um checksum SHA-256, e o job de publicação confere todos de novo.
O que o documento de release diz que ainda falta
- Os segredos de assinatura e de publicação ainda ficam guardados no nível do repositório e deveriam ir para os ambientes de release.
- Ainda não há um ruleset que obrigue toda tag
v*a ser assinada. - Releases imutáveis não estão habilitadas.
- A publicação no crates.io ainda usa um token. O trusted publishing está planejado.
- Com um único mantenedor, não há um segundo revisor para as mudanças nem para as releases.
Reportar uma vulnerabilidade
Não abra uma issue pública. Use o relato privado de vulnerabilidades do GitHub, inclua os passos para reproduzir e as versões afetadas, e dê tempo para coordenar uma correção.
Perguntas comuns
O agente consegue escapar?
É seguro rodar com a rede ligada?
--network, tudo o que o agente consegue ler, ele consegue enviar, e o ai-jail não é feito para impedir um atacante determinado de fazer isso. O meio-termo é --allow-host: o agente alcança só os hosts que você lista, e cada um é confiável por inteiro. Mascare antes os segredos dentro do projeto. Veja como mascarar.Ele já foi auditado?
Coloque seu agente atrás das grades
O ai-jail é um binário único que não precisa de daemon nem de root. Você acrescenta uma palavra na frente do comando que já usa.
brew tap akitaonrails/tap && brew install ai-jailai-jail claude