Backends de sandbox
Visão geral dos backends
Secção intitulada “Visão geral dos backends”| Backend | Valor | Plataforma | Disponibilidade |
|---|---|---|---|
| VM fechada | microvm |
macOS, Linux, Windows (hipervisor de microVM) | Condicionado pela sonda ao runtime do microVM; aplica-se a terminais interativos — a shell corre dentro de um rig, não no anfitrião |
| Nativo | native |
macOS (Seatbelt), Linux e WSL2 (bubblewrap) | Sondado no arranque; o Linux recusa uma execução que exija negações de escrita por glob de nome de ficheiro, que o bubblewrap não consegue impor |
| Nenhum | none |
Todas | O predefinido de análise, o predefinido do terminal e a alternativa quando não há nenhum backend disponível ou está definido --sandbox off |
SandboxBackend.fromName devolve none para um valor armazenado nulo ou não reconhecido. Um "docker" armazenado é mapeado para native.
O microvm é o único backend com uma fronteira de kernel — a carga de
trabalho não corre genuinamente no anfitrião. Nunca é uma alternativa silenciosa
em nenhum dos sentidos: pedi-lo explicitamente num anfitrião que não consegue
iniciar uma VM falha ruidosamente («pedi uma VM e fiquei com o meu portátil» é a
única degradação que o recinto não se pode permitir), e só um predefinido não
especificado recua para native, com uma linha de registo a dizê-lo. O backend
efetivo da sessão é comunicado de volta ao cliente e assinalado com um
distintivo, pelo que um terminal nunca pode afirmar estar fechado enquanto corre
no anfitrião. O ciclo de vida, as imagens e a saída da própria VM são abordados
na referência de rigs.
O Windows não tem sandbox nativa. NativeSandboxAdapter.probe devolve available: false em todos os anfitriões que não sejam macOS ou Linux/WSL2. O despacho de agentes no Windows usa, por isso, none. O backend microvm é separado: é condicionado pela sonda ao runtime do microVM e aplica-se a terminais interativos e rigs, não ao envolvimento do bash no anfitrião.
Resultados da sonda
Secção intitulada “Resultados da sonda”É a sonda que condiciona a imposição. Corre uma única vez, no arranque do servidor.
| Anfitrião | available |
Nota / dica |
|---|---|---|
| macOS | true |
sandbox-exec (Seatbelt) — isolamento de namespace, sem fronteira de kernel |
Linux ou WSL2 com bwrap e socat |
true |
bubblewrap — isolamento de namespace, sem fronteira de kernel |
| Linux ou WSL2 sem qualquer das ferramentas | false |
requiresInstall: true, com uma dica de instalação que nomeia os pacotes em falta |
| Qualquer outra plataforma | false |
A sandbox nativa não é oferecida nesta plataforma |
O servidor regista o resultado numa única linha de arranque: agent sandbox ON mais a nota, no OS-native agent sandbox on this host mais a dica, ou agent sandbox DISABLED by --sandbox off. O GET /healthz não transporta nenhum campo de sandbox.
--sandbox / CC_SERVER_SANDBOX aceita on (predefinido) ou off. É uma forma de desativar, não de ativar: on significa «envolver quando o anfitrião oferece um backend», nunca «exigir um».
Backend nativo
Secção intitulada “Backend nativo”macOS: Seatbelt
Secção intitulada “macOS: Seatbelt”Utiliza o mecanismo de sandbox Seatbelt da Apple (o mesmo sistema utilizado pela App Sandbox do macOS).
O perfil gerado é permissivo por predefinição ((allow default)) e recorta negações, porque um perfil totalmente deny-by-default torna uma shell interativa inutilizável — há demasiadas chamadas dyld, XPC e Mach para enumerar. A consequência prática é que um agente ainda consegue ler a maior parte do anfitrião; os seus checkouts registados estão protegidos por negações explícitas de escrita e de renomeação, e não por invisibilidade.
- Leituras do sistema de ficheiros: negadas para os caminhos secretos (
~/.ssh,~/.aws,~/.gnupg,~/.config/gh,~/Library/Keychainse o resto do conjunto de negação de leitura) - Escritas no sistema de ficheiros: repostas em negação e depois autorizações explícitas por subcaminho, com os caminhos de negação obrigatória (ficheiros rc da shell,
.git/hooks, configuração do Claude, checkouts protegidos) bloqueados mesmo dentro de uma raiz gravável. Ofile-write-unlinké negado em todos os caminhos negados e nos seus ascendentes, pelo que uma renomeação não consegue contornar uma negação de escrita - Processos: negações de
process-execpara os binários sempre perigosos, mais um bloqueio à execução de qualquer coisa a partir de um diretório gravável - Rede: restrita às portas do proxy em processo mais o DNS — o loopback não é permitido de forma indiscriminada e os sockets Unix não são permitidos. Os proxies impõem a lista de permissões de domínios por sessão (ver Saída de rede)
- Configuração: os perfis são gerados em cada lançamento a partir do
SandboxSpec
Requisitos:
sandbox-exec(/usr/bin/sandbox-exec), parte de qualquer instalação do macOS — não são necessárias ferramentas adicionais
Linux: bubblewrap
Secção intitulada “Linux: bubblewrap”Utiliza o bubblewrap (bwrap) para criar namespaces de montagem e de PID.
- Sistema de ficheiros: cria um namespace de montagem onde só os caminhos associados estão visíveis
- Rede: corre num namespace de rede não partilhado; o tráfego chega aos proxies do anfitrião através de pontes de socket Unix do
socat - Processos: isolamento por namespace de PID
Requisitos:
bubblewrapesocatinstalados (sudo apt-get install bubblewrap socatno Debian/Ubuntu)- Suporte de namespaces no kernel (padrão no Linux moderno)
O Bubblewrap consegue ocultar um caminho existente, mas não consegue proibir um
ficheiro novo .env, *.key ou *.pem dentro de uma associação gravável. A
política de despacho padrão exige estas negações por glob de nome de ficheiro,
pelo que o despacho nativo de agentes atualmente falha de forma fechada no
Linux/WSL2 mesmo quando a sonda encontra o bubblewrap. Não corre com uma
política silenciosamente mais fraca. É necessário um backend com regras de nome
de ficheiro no momento da criação para restaurar o despacho nativo no Linux.
Desativar o isolamento numa sandbox é uma desativação explícita e sem
confinamento, não um contorno equivalente.
Backend nenhum
Secção intitulada “Backend nenhum”Sem isolamento numa sandbox. O processo corre diretamente no anfitrião, com o acesso deste ao sistema de ficheiros, à rede e aos processos. O ACP e a ferramenta bash do harness continuam a ter saneamento do ambiente e a política de comandos; o Claude Code através do NoSandboxAdapter herda o ambiente do anfitrião. É para isto que o despacho de agentes recua num anfitrião sem nenhum backend disponível ou com --sandbox off, e é o predefinido do terminal quando um cliente não indica nenhum.
Terminal alojado no servidor
Secção intitulada “Terminal alojado no servidor”O terminal é isolado numa sandbox segundo regras próprias, e não pela sonda de despacho. O defaultBackend do TerminalSessionService é none e o cc_server não o substitui, pelo que uma sessão de terminal corre a shell do anfitrião a menos que o cliente peça explicitamente native — o painel de terminal do desktop não o faz.
Quando é pedido native, a shell é envolvida através do SandboxManager sob um perfil de terminal fixo: o $HOME e o diretório de trabalho da sessão são graváveis, a rede está aberta e é negada a leitura de ~/.ssh, ~/.aws, ~/.gnupg, ~/.config/gh e ~/Library/Keychains. O token de forge só de leitura e o gateway de push não se aplicam — são questões de despacho. Se o envolvimento não for suportado, a sessão falha em vez de mostrar um distintivo nativo num PTY com a shell do anfitrião.
Saída de rede
Secção intitulada “Saída de rede”Quando uma sandbox é lançada, o acesso de rede de saída é imposto por proxies em processo, e não por regras de firewall do kernel. Num anfitrião sem nenhum backend disponível nada disto se aplica e um processo lançado alcança a rede exatamente como o faria o utilizador do servidor.
- O runtime da sandbox corre um proxy HTTP/HTTPS e um proxy SOCKS5 em
127.0.0.1dentro do processo docc_server, um par por sessão restrita (http_proxy.dart,socks_proxy.dart). As execuções simultâneas não partilham um par. - Os processos em sandbox recebem variáveis de ambiente de proxy (
HTTP_PROXY/HTTPS_PROXY→ o proxy HTTP,ALL_PROXY→ o proxy SOCKS;GIT_SSH_COMMANDencaminha o git por SSH através do proxy SOCKS). No Linux, o namespace de rede não é partilhado, pelo que os proxies são a única saída; no macOS, a sandbox partilha o loopback do anfitrião e fala diretamente com eles. - O anfitrião de destino de cada ligação é comparado com a lista de permissões de domínios da sessão; tudo o resto é negado. Há duas formas de wildcard, cada uma com exatamente um
*: um*.example.comà esquerda corresponde a qualquer subdomínio (incluindoa.b.example.com), mas não ao domínio de topo em si; umbedrock.*.amazonaws.comcom o wildcard no meio corresponde exatamente a um rótulo (bedrock.us-east-1.amazonaws.com, nãobedrock.evil.attacker.amazonaws.com). - A lista de permissões é resolvida por sessão a partir da base curada (
kBaselineAllowedDomains: fornecedores de LLM, alojamento de Git, registos de pacotes), mais os domínios do GitHub (kGithubDomains), mais oSandboxSpec.egressAllowlist. O percurso de despacho nunca passa entradasegressAllowlistadicionais, pelo que, na prática, todas as execuções recebem a base mais o GitHub. - Uma lista fixa de negações (
kBaselineDeniedDomains: pontos finais de metadados da cloud, destinos de telemetria) tem precedência sobre todas as autorizações. networkEnabled: falsecontinua a iniciar os proxies por sessão, mas a lista de permissões fica vazia, pelo que todas as ligações de saída são recusadas. O despacho define-o quando a execução arranca a partir da regra Aceder à rede: negar desliga a rede; permitir e perguntar primeiro mantêm-na ligada.
SandboxSpec
Secção intitulada “SandboxSpec”A especificação passada a SandboxPort.launch:
| Campo | Tipo | Descrição |
|---|---|---|
sessionId |
String |
Identificador estável da sessão (normalmente o id do espaço) |
workspaceId |
String |
Id do espaço de trabalho proprietário |
agentId |
String? |
Agente atualmente associado à sandbox, se existir |
bindMounts |
List<SandboxBindMount> |
Montagens anfitrião→convidado; a sandbox só vê os caminhos do anfitrião aqui listados |
networkEnabled |
bool (predefinido true) |
Definido no despacho a partir da regra de saída de rede (desligado só quando esta resolve para negar). Quando está desligado, os proxies por sessão ainda arrancam, mas a lista de permissões fica vazia, pelo que todas as ligações de saída são recusadas |
egressAllowlist |
List<String> (predefinido []) |
Domínios de saída adicionais permitidos; wildcards suportados (*.example.com, mais uma forma intermédia com um rótulo). O despacho nunca preenche isto, pelo que uma execução envolvida recebe a base curada mais os domínios do GitHub |
guestWorkdir |
String? |
Diretório de trabalho predefinido dentro da sandbox; cada chamada exec pode substituí-lo |
mode |
Mode (predefinido chat) |
Recorta as regras de escrita no sistema de ficheiros: chat mantém as montagens bind graváveis; review, plan e orchestrate são só de leitura |
protectedPaths |
List<String> (predefinido []) |
Caminhos do anfitrião que nunca podem ser graváveis dentro da sandbox, em nenhum modo |
runnerStateDirs |
List<String> (predefinido []) |
Diretórios do anfitrião onde o runner guarda o seu próprio estado, graváveis em todos os modos (o atual é uma CLAUDE_CONFIG_DIR gerida pelo Control Center) |
execGrantRoots |
List<String> (predefinido []) |
Árvores de diretórios aprovadas pelo operador que reabrem o bloqueio de execução em diretórios graváveis no macOS. O Linux ignora-as — o bwrap não tem nenhum bloqueio de execução no $HOME para reabrir |
protectedPaths contém os checkouts originais dos repos registados, resolvidos em cada despacho a partir dos repos do espaço de trabalho. Os agentes trabalham exclusivamente em worktrees copy-on-write por conversa, pelo que os originais nunca podem ser graváveis a partir do interior da sandbox — no macOS, a autorização de escrita indiscriminada do Seatbelt sobre o $HOME deixá-los-ia, de outro modo, graváveis através do bash. Juntam-se ao conjunto de negação de escrita dos segredos em todos os modos, incluindo o chat.
Todos os caminhos de negação de leitura e de negação de escrita são emitidos tanto na sua grafia literal como na grafia resolvida por ligações simbólicas. O kernel compara uma regra de negação com o caminho resolvido, pelo que uma regra escrita contra um caminho com ligação simbólica nunca dispara — e no macOS esse é o caso comum e não o exótico, já que o /tmp resolve para /private/tmp e o /var/folders/… para /private/var/folders/…. As entradas que contêm * ou ? são tratadas como globs e deixadas como estão; um caminho que não resolve é mantido tal como escrito.
SandboxBindMount
Secção intitulada “SandboxBindMount”| Campo | Tipo | Descrição |
|---|---|---|
hostPath |
String |
Caminho absoluto no anfitrião |
guestPath |
String |
Caminho dentro da sandbox (com a sandbox nativa, é igual a hostPath; mantido por simetria entre adaptadores) |
readOnly |
bool (predefinido false) |
Monta só de leitura quando é true |
SandboxHandle
Secção intitulada “SandboxHandle”Devolvido por SandboxPort.launch:
| Campo | Tipo | Descrição |
|---|---|---|
sessionId |
String |
Identificador da sessão |
backend |
SandboxBackend |
Qual o backend em execução |
state |
SandboxState |
Estado do ciclo de vida |
error |
Object? |
Preenchido quando o state é error |
details |
Map<String, Object?> |
Contabilidade privada do adaptador (diretório de trabalho, PID, caminho do perfil, etc.) |
SandboxState
Secção intitulada “SandboxState”| Estado | Descrição |
|---|---|
created |
Especificação criada, mas a VM ainda não arrancou |
warm |
Em execução e pronta para executar comandos |
active |
A executar um comando neste momento |
suspended |
Em pausa / com checkpoint para o disco, para poupar RAM |
destroyed |
Desmontada; o handle deixa de ser válido |
error |
O lançamento falhou — ver SandboxHandle.error |
Eventos da sandbox
Secção intitulada “Eventos da sandbox”| Tipo de evento | Descrição |
|---|---|
stdout |
Saída padrão da sandbox |
stderr |
Erro padrão da sandbox |
exit |
O processo da sandbox terminou (transporta o exitCode) |
starting |
A sandbox está a arrancar |
ready |
A sandbox está pronta para aceitar chamadas exec |
killed |
Processo terminado pelo anfitrião (OOM, tempo limite, paragem manual) |
violation |
Foi violada uma política da sandbox (transporta um SandboxViolation) |
SandboxViolation
Secção intitulada “SandboxViolation”| Campo | Tipo | Descrição |
|---|---|---|
action |
String |
O verbo negado (por exemplo, file-read*, network-outbound) |
target |
String |
Caminho ou anfitrião que foi alvo da operação negada |
raw |
String? |
Linha de registo em bruto, útil para depuração |
Ver também
Secção intitulada “Ver também”- Sandbox e segurança
- Configurar políticas de sandbox
- Barreiras
- CLI do cc_server: o flag
--sandbox