Ele lê as suas regras
O ai-jail confia no seu arquivo ~/.ai-jail. Um arquivo .ai-jail dentro do projeto é tratado como não confiável: ele pode apertar o sandbox, nunca abrir. As flags na linha de comando têm prioridade sobre os dois.
O ai-jail monta uma cópia pequena e fechada do seu sistema, coloca o seu projeto nela e inicia o agente lá dentro. Os muros são recursos do próprio sistema operacional, então o agente roda em velocidade total e não consegue desligá-los. A cada inicialização, a rede fica fechada exceto para o host da API do próprio agente, aberta para alguns hosts nomeados ou totalmente aberta.
Para o engenheiro que quer saber o que é aplicado de fato. O primeiro parágrafo e a primeira imagem bastam para todos os outros.
Leva um instante, toda vez, e depois nada fica rodando em segundo plano.
↔ Deslize para o lado para ver o diagrama inteiro
O ai-jail confia no seu arquivo ~/.ai-jail. Um arquivo .ai-jail dentro do projeto é tratado como não confiável: ele pode apertar o sandbox, nunca abrir. As flags na linha de comando têm prioridade sobre os dois.
O ai-jail prepara cada uma das camadas abaixo antes de o agente existir e mapeia as toolchains que você tem (mise, cargo, caches do npm e outros) somente leitura ou num cache da própria jaula. Se um arquivo de configuração estiver ilegível ou inválido, o ai-jail para, em vez de iniciar com regras mais fracas.
O agente herda os mesmos muros, assim como cada script, pacote e servidor MCP que ele inicia. Quando o agente termina, o ai-jail termina junto.
Cada inicialização escolhe um de três modos de rede. O padrão é o mais restrito: nenhuma rota para fora, exceto o que o proxy filtrado deixa passar, que é o host da API de um agente conhecido nas duas plataformas e, no Linux com as toolchains ligadas (o padrão), uma lista curta de registries de pacotes. Os outros dois são flags que você passa de propósito.
↔ Deslize para o lado para ver o diagrama inteiro
O padrão. O sandbox ganha um network namespace privado sem rota para fora, exceto pelo proxy filtrado. Para um agente conhecido o proxy deixa passar o host da API dele, no Linux e no macOS, então um ai-jail claude puro funciona. No Linux, com as toolchains ligadas (o padrão), ele também deixa passar uma lista fixa de registries de pacotes, então npm install e cargo build funcionam; no macOS os registries ficam fechados. --no-network deixa tudo offline.
--allow-host HOST, pode ser repetida. O namespace continua fechado; uma ponte pequena lá dentro encaminha o tráfego para um proxy CONNECT do lado de fora, que disca só para os hosts listados e seus subdomínios. O proxy resolve cada nome uma vez, do lado de fora, e disca o endereço que conferiu. O ai-jail define http_proxy lá dentro, e um programa que ignora isso não alcança nada.
--network. O muro some do lado da rede, e o agente pode enviar para fora o que conseguir ler.
| Linux | macOS | |
|---|---|---|
| A cerca no modo filtrado | O próprio network namespace, mais a ponte dentro dele | Uma regra do sandbox-exec que permite conexões de saída só para a porta do proxy |
| DNS dentro do sandbox | Falha. O proxy resolve os nomes do lado de fora. | O resolver do sistema não fica cercado, então sobra um canal de DNS de baixa largura de banda |
O ai-jail não confia em nenhum muro para segurar tudo sozinho. No Linux o agente fica atrás de cinco deles, cada um imposto pelo kernel, mais dois filtros sobre o que entra e o que sai.
↔ Deslize para o lado para ver o diagrama inteiro
ai-sandbox), nenhum canal de comunicação com os programas do host e um network namespace sem rota para fora, a menos que você abra uma com --allow-host ou --network. Se o ai-jail morrer, tudo lá dentro morre junto./usr ficam somente leitura, e o diretório home e o /tmp são novos e ficam na memória. As toolchains que você tem (mise, cargo, caches do npm) são mapeadas nesse home novo, somente leitura ou como um cache da própria jaula.~/.ssh, ~/.aws, ~/.gnupg, ~/.docker e os perfis de navegador estão numa lista embutida que nunca é montada. O diretório de estado do próprio harness, por exemplo o ~/.claude do Claude, é a exceção: o ai-jail monta com leitura e escrita por padrão, para o agente começar logado, e --no-agent-state deixa de fora. Tudo o que for escrito fora do projeto some quando a sessão termina.ptrace, mount, bpf, módulos do kernel, io_uring, o keyring do kernel, raw sockets e TIOCSTI, uma chamada que consegue digitar teclas no seu terminal. É uma blocklist de propósito, para que compiladores e runtimes continuem funcionando. O único processo iniciado antes de o Landlock e o seccomp valerem é a pequena ponte que leva o tráfego de rede filtrado para fora, então só ela fica sem restrição.PATH, HOME, TERM, locale e configurações de proxy e de certificados.ANTHROPIC_API_KEY para o Claude, copiada por padrão junto com o login dele; --no-agent-state impede isso. Quando uma tarefa precisa de outra variável, você a repassa pelo nome com --env.O macOS não tem namespaces, Landlock nem seccomp. O ai-jail transforma a mesma política num perfil para o sandbox-exec, o sandbox embutido da Apple. A documentação do ai-jail diz com todas as letras que isso não equivale ao isolamento do Linux.
↔ Deslize para o lado para ver o diagrama inteiro
| Linux | macOS | |
|---|---|---|
| Backend | bubblewrap, depois Landlock, seccomp e limites de recursos | sandbox-exec com um perfil gerado a cada inicialização. A Apple marcou a ferramenta como obsoleta, e não existe nada melhor sem extensões de kernel. |
| Diretório home | Um home novo, na memória. O seu home real não é montado. | O perfil começa sem nenhuma leitura global e depois libera só os caminhos de que o agente precisa, então o seu home real continua fechado. |
/tmp | Privado, descartado na saída | O /tmp do host, porque não existe mount namespace. TMPDIR aponta para uma pasta privada criada para aquela inicialização, e esse é o único caminho temporário liberado. |
| Mapeamentos copy-on-write | --overlay-map deixa o agente escrever numa cópia | Tratado como um mapeamento somente leitura |
| Rede | Fechada por padrão: sem rota para fora, exceto o host da API de um agente conhecido e os registries de pacotes liberados quando as toolchains estão ligadas. --allow-host ou --network abre mais, --forward-port repassa uma porta do loopback do host, --no-network fecha tudo. No --lockdown a rede aberta fica sempre bloqueada, mesmo com --network. | O mesmo padrão e as mesmas flags, incluindo o host da API do agente conhecido, mas sem --forward-port, os registries e os mapeamentos de cache das toolchains, que só existem no Linux. O lockdown bloqueia a rede aberta aqui também. |
| Outros programas | Invisíveis: lista de processos própria, sem IPC do host | Mach, IOKit e IPC do host são negados, a menos que você passe --macos-host-ipc |
O ai-jail escreve o mínimo possível de código de segurança próprio. Ele comanda o bubblewrap, que é pequeno, vem empacotado em todas as grandes distribuições e é exercitado todo dia pelo Flatpak, e acrescenta recursos do kernel por cima.
| Opção | Por que não |
|---|---|
| Firejail | Dá conta do trabalho, mas é um binário setuid root, e várias CVEs vêm desse design. O bubblewrap funciona sem root. |
| Reescrever o bubblewrap em Rust | Mais de 500 linhas de código sensível de segurança para manter e corrigir sozinho, tudo para evitar uma dependência de uns 50 KB que recebe correções do upstream. |
| nsjail | Capaz, mas feito para servidores, configurado por arquivos protobuf e empacotado em menos distribuições. |
| systemd-nspawn | Exige o systemd, então falha dentro de containers e em sistemas sem ele. |
A ferramenta inteira é um único programa síncrono em Rust, sem runtime assíncrono, daemon ou imagem de container.
Dependências contadas no repositório do ai-jail na versão 2.9.0; testes na versão 2.9.0.
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