
Sandbox para agentes de codificação de IA. Executa Copilot CLI, Claude Code, OpenCode, Gemini CLI, Antigravity, Pi, goose ou um shell simples dentro de um sandbox em nível de kernel, com proteções de git e gh e política de sandbox versionada no repositório.
Sandbox imposto pelo kernel para agentes de codificação de IA. O cplt envolve o GitHub Copilot CLI, OpenCode, Gemini CLI, Antigravity CLI, Pi, Claude Code, goose, DeepSeek Harness, ou qualquer shell, para que o agente possa escrever código mas não possa roubar credenciais, fazer push para a main, fazer merge de PRs ou exfiltrar segredos.
sandbox-exec
Agentes de IA executam código arbitrário. Um agente comprometido, seja por injeção de prompt, ataque à cadeia de suprimentos ou um servidor MCP malicioso, pode ler ~/.ssh, fazer push para a main, fazer merge de PRs ou exfiltrar seu código, a menos que o próprio sistema operacional diga não.
O cplt oferece imposição em nível de kernel com política configurável pela equipe:
.cplt.toml, versionada no controle de versão, portanto à prova de adulteração e auditávelDocumentação detalhada: Configuração · Proxy e filtragem de domínio · guarda de comando gh · guarda de comando git · Impactos conhecidos · Detalhes de segurança · Modelo de segurança
brew install navikt/tap/cplt # macOS. On Debian or Ubuntu, see apt below cplt --shell-install # make 'copilot' run sandboxed (persistent) # --agent opencode for any other agent cplt doctor # check your environment cplt -- -p "fix the tests" # run Copilot in sandbox
Outros agentes e comandos de sandbox:```bash
cplt --agent opencode # OpenCode (Copilot subscription)
cplt --agent opencode --pass-env ANTHROPIC_API_KEY # third-party provider
cplt --agent shell # interactive sandboxed shell (no AI)
cplt exec -- npm install # sandbox any command directly
cplt exec -c "npm install && npm test" # compound commands in sandbox
alias npm="cplt exec -- npm" # sandboxed npm for every invocation
cplt init --write
cplt trust accept --all
cplt config set git_guard.protect_default_branch_only false # block every push, not just main cplt config set git_guard.mode warn # observe instead of blocking
## O que ele bloqueia
O sandbox bloqueia o acesso a credenciais e segredos no kernel. Guardas de comando bloqueiam operações destrutivas. Toda restrição se aplica ao agente e a todo processo que ele gera.
| Recurso | Status | Notas |
| --- | --- | --- |
| Ler/escrever diretório do projeto | ✅ Permitido | |
| Ler/escrever/apagar `.env*`, `.pem`, `.key` no projeto | 🔒 Bloqueado pelo kernel | Impede exfiltração e destruição de segredos. `--allow-env-files` sobrepõe |
| Escrever `.git/hooks`, `.git/config`, `.gitmodules` | 🔒 Bloqueado pelo kernel (macOS), ⚠️ parcial no Linux | Impede persistência via git hooks, redirecionamento de hooksPath, sequestro de submódulo. **Linux:** Landlock não consegue negar um subcaminho dentro de uma árvore permitida, então estes permanecem graváveis no caminho somente-Landlock. `bwrap` re-vincula `.git/hooks` como somente leitura mas deliberadamente deixa `.git/config` e `.gitmodules` graváveis, então `core.hooksPath` permanece uma rota de persistência, veja [Limitações do Linux](https://github.com/navikt/cplt/blob/main/docs/security.md#linux). Aplica-se a **toda** raiz gravável, o projeto e cada concessão de `allow.write`, incluindo um worktree concedido ou repositório bare cujos hooks reais vivem fora de `<root>/.git` |
| Executar de `/tmp`, `/var/folders` | 🔒 Bloqueado pelo kernel | Impede escrever-depois-executar. O diretório de rascunho redireciona TMPDIR para um local seguro, ativado por padrão |
| Escrever diretórios bin/shim resolvidos via PATH (`~/.bun/bin`, `~/.deno/bin`, `$PNPM_HOME`, `shims/` do mise e todo o `installs/`) | 🔒 Bloqueado pelo kernel (macOS), ⚠️ mise parcial no Linux | Impede trojanizar um binário que seu próximo comando *não-sandboxed* resolve via PATH. Mesmo motivo pelo qual `~/.cargo/bin` e `~/go/bin` sempre foram somente leitura. Quebra `bun install -g`, `deno install`, `pnpm add -g`, `mise install`, `mise upgrade`, `mise use -g` dentro do cplt, deliberadamente, e um repositório fixando uma toolchain não instalada não faz mais bootstrap. Instalações locais ao projeto não são afetadas. **Linux:** os dois do mise usam a sobreposição somente-leitura do `bwrap`; o resto se mantém nativamente. Veja [Instalações globais de ferramentas](https://github.com/navikt/cplt/blob/main/docs/known-impacts.md#global-tool-installs) |
| Executar de `~/Library/Caches` | 🔒 Bloqueado pelo kernel por padrão | Impede preparação de entrega de binários. Módulos nativos do Copilot são isentos via uma exceção. Adicione isenções direcionadas com `--allow-cache-exec <SUBDIR>`, ex. `ms-playwright` |
| Modificar `.vscode/tasks.json`, `launch.json` | ⚠️ Permitido, risco conhecido | Fronteira de confiança da IDE. Veja [SECURITY.md](https://github.com/navikt/cplt/blob/main/SECURITY.md) para mitigações |
| Ler/escrever `~/.copilot` (auth, configurações) | ✅ Permitido | Inclui `file-map-executable` para `keytar.node`, `pty.node`, `computer.node` |
| Escrever `~/.copilot/pkg` (módulos nativos) | 🔒 Bloqueado pelo kernel | Impede persistência via substituição de módulo nativo |
| Variáveis de ambiente | 🔒 Sanitizadas + endurecidas | Apenas uma allowlist segura passa. Scripts de ciclo de vida bloqueados. `--pass-env VAR` adiciona uma de volta |
| Ler `~/.config/gh/hosts.yml` + `config.yml` | ✅ Permitido (somente leitura) | Apenas estes dois arquivos. O resto de `.config/gh` é bloqueado |
| Ler `~/.config/mise` | ✅ Permitido (somente leitura) | Versões de ferramentas e PATH, sem segredos |
| Ler `~/.gitconfig`, `~/.config/git/config` | ✅ Permitido (somente leitura) | Um symlink de dotfiles é seguido até seu alvo, então um `~/.gitconfig` stowed funciona |
| Ler `~/.git-credentials` | 🔒 Bloqueado pelo kernel | `credential.helper = store` mantém tokens em texto claro aqui. Nenhum `--allow-read` reabre, como `~/.netrc`. **Linux:** uma concessão em um *ancestral* (`$HOME` em si) ainda o expõe, porque Landlock não consegue negar um subcaminho dentro de uma árvore permitida |
| Ler hooks globais do git (`core.hooksPath`) | ✅ Permitido (somente leitura, escrita negada) | Auto-detectado. Deve estar sob `$HOME` com profundidade ≥3. Escritas são explicitamente bloqueadas |
| Assinatura de commit/tag (`commit.gpgsign`, `tag.gpgsign`) | 🔒 Desabilitado | Chaves privadas em `~/.ssh` e `~/.gnupg` são bloqueadas, então a assinatura é desabilitada via uma sobreposição de variável de ambiente |
| Ler `~/Library/Application Support/Microsoft` | ✅ Permitido (somente leitura) | Device ID para telemetria |
| Acessar macOS Keychain | ⚠️ Permitido (leitura+escrita) para agentes que armazenam auth lá | A concessão não pode ser limitada a um item, então alcança toda entrada do keychain que o agente consegue desbloquear. Opte por `sandbox.keychain_substitute` (EXPERIMENTAL, padrão desligado) para descartá-lo em execuções onde o agente consegue autenticar sem ele — `CLAUDE_CODE_OAUTH_TOKEN` para Claude Code, um arquivo de token de fallback existente para Antigravity. Veja [SECURITY.md](https://github.com/navikt/cplt/blob/main/SECURITY.md#keychain-access-is-all-or-nothing) |
| Rede de saída (porta 443) | ✅ Permitido | Toda outra porta é bloqueada. Adicione extras com `--allow-port` |
| Saída para localhost | 🔒 Bloqueado pelo kernel (macOS), ⚠️ baseado em porta no Linux | Impede acesso a serviço local. Entrada ainda funciona para o proxy. **Linux:** regras do Landlock são apenas números de porta e não conseguem distinguir `localhost:443` de `remote:443`, então um serviço local em uma porta permitida é alcançável e não há negação específica para localhost. Use `--with-proxy` para proteção SSRF, veja [Limitações do Linux](https://github.com/navikt/cplt/blob/main/docs/security.md#linux) |
| SSH agent (unix socket) | 🔒 Bloqueado pelo kernel (macOS), ⚠️ apenas env no Linux | Impede assinatura de operações git ou SSH para hosts. **Linux:** `connect()` de unix socket não é controlado, então o `SSH_AUTH_SOCK` retido é a única barreira e um agente que o define por conta própria pode usar as chaves carregadas. `bwrap` esconde o socket OpenSSH padrão sob `/tmp`, mas não um agente gnome-keyring/gcr ou systemd sob `$XDG_RUNTIME_DIR`. Veja [Limitações do Linux](https://github.com/navikt/cplt/blob/main/docs/security.md#linux) |
| Ferramentas de desenvolvimento (`~/.cargo`, `~/.gradle`, `~/.m2`, `~/.sdkman`, `~/.jenv`, `~/.pyenv`, `~/.konan`, etc.) | ✅ Permitido (leitura+escrita para caches) | Apenas diretórios que existem no disco. Ajustado em tempo de execução pelo que `cplt doctor` detecta |
| Arquivos de credencial de registro (`~/.m2/settings.xml`, `~/.gradle/gradle.properties`, `~/.cargo/credentials`) | 🔒 Bloqueado pelo kernel no macOS. No Linux o diretório pai da ferramenta permanece legível | Sobrepõe com `--allow-read`. Veja [Registros privados](https://github.com/navikt/cplt/blob/main/docs/known-impacts.md#private-registries) |
| Ler `~/.npmrc` | 🔒 Bloqueado pelo kernel (ambas plataformas) | Sobrepõe com `--allow-read`. Quebra yarn 1, veja [yarn 1](https://github.com/navikt/cplt/blob/main/docs/known-impacts.md#yarn-1-and-unreadable-home-rc-files) |
| Código-fonte Go (`~/go/src`) | 🔒 Bloqueado pelo kernel | Apenas `~/go/bin` e `~/go/pkg` são legíveis |
| Ler `~/.ssh`, `~/.gnupg`, `~/.aws`, `~/.azure` | 🔒 Bloqueado pelo kernel | |
| Ler `~/.kube`, `~/.docker`, `~/.nais` | 🔒 Bloqueado pelo kernel | |
| Ler `~/.password-store`, `~/.terraform.d` | 🔒 Bloqueado pelo kernel | |
| Ler `~/.config/gcloud`, `~/.config/op` | 🔒 Bloqueado pelo kernel | Arquivos individuais são sobreponíveis com `--allow-read`. Veja [Credenciais de nuvem](https://github.com/navikt/cplt/blob/main/docs/known-impacts.md#cloud-credential-directories) |
| Ler ou escrever `~/.config/cplt`, `~/.nav-pilot` | 🔒 Bloqueado pelo kernel | Estado da ferramenta que decide o que o *próximo* lançamento pode fazer. `~/.config/cplt` é não-sobreponível como uma subárvore inteira; dentro de `~/.nav-pilot`, um caminho nomeado permanece concedível para que um payload de agentpakke fixado possa ser lido |
| Ler `~/.netrc`, `~/.pypirc`, `~/.vault-token` | 🔒 Bloqueado pelo kernel | Não-sobreponível em ambas plataformas. Nomear um em `allow.read` é um erro de inicialização |
| Ler `~/.gem/credentials` | 🔒 Bloqueado pelo kernel | Não-sobreponível em ambas plataformas. Nomear um em `allow.read` é um erro de inicialização |
| Operações destrutivas do `gh` CLI (merge, delete, release) | 🔒 Controlado por comando (ativado por padrão) | Opte por sair com `--no-gh-guard`. Veja [gh guard](https://github.com/navikt/cplt/blob/main/docs/gh-guard.md) |
| `git push` para o branch padrão | 🔒 Controlado por comando (ativado por padrão) | Bloqueia pushes para `main`/`master`; pushes de feature-branch ainda funcionam. `protect_default_branch_only = false` bloqueia todo push, `git_guard.mode = "warn"` apenas avisa, `--no-git-guard` opta por sair |
| Herança de processo filho | ✅ Todas as restrições se aplicam a subprocessos | |
Essa tabela é um resumo. O sandbox também permite acesso a arquivos de sistema (certificados SSL, `/etc/hosts`), diretórios temporários (leitura e escrita, sem exec) e caminhos de ferramentas de sistema (`/usr/bin`, `/opt/homebrew`). Execute `cplt --print-profile` para as regras SBPL completas.
Para o modelo de segurança completo, análise de ameaças e estratégia de testes, leia [SECURITY.md](https://github.com/navikt/cplt/blob/main/SECURITY.md).
## Como o cplt se compara
### Sandbox do Codex CLI
| Área | cplt | Sandbox do Codex CLI |
| --- | --- | --- |
| Controle de rede de saída | Proxy CONNECT com listas de permitir/bloquear domínios | Sem filtragem em nível de domínio |
| Tratamento de ambiente | Allowlist mais injeção de env endurecida | Modelo de pass-through mais básico |
| Proteção de arquivos de segredo | Padrões de negação como `.env*`, `.pem`, `.key` dentro do repositório | Acesso primariamente com escopo de diretório |
| Política de repositório | [`.cplt.toml`](https://github.com/navikt/cplt/blob/main/docs/configuration.md#per-repo-configuration-cplttoml) com um fluxo explícito de confiança/aprovação | Sem arquivo de política em nível de repositório |
| Suporte a agentes | Copilot, OpenCode, Gemini CLI, Antigravity CLI, Pi, Claude Code, goose, DeepSeek Harness, ou shell | Apenas Codex |
O cplt não é mais forte em tudo. O Codex CLI tem isolamento de namespace Linux hoje, e já expõe modos de sandbox explícitos como somente leitura e workspace-write. O cplt ainda não tem essa matriz de modos.
### Sandboxes baseados em Docker
| Área | cplt | Sandbox baseado em Docker |
| --- | --- | --- |
| Tempo de inicialização | Praticamente instantâneo para uso normal de CLI | Inicialização de contêiner geralmente mais lenta |
| Controle de rede | Filtragem de saída por requisição via proxy | Acesso à rede geralmente tudo-ou-nada |
| Controles de arquivo | Regras por caminho e por padrão | Controles por montagem |
| Requisitos de host | Binário único | Daemon Docker necessário |
| Adequação a laptop corporativo | Funciona onde Docker é indisponível ou restrito | Frequentemente bloqueado por política local |
Docker ainda oferece isolamento mais forte em alguns ambientes, especialmente se você quer um sistema de arquivos e namespace de processo totalmente separados. O cplt troca isso por configuração mais leve e integração mais estreita com a máquina em que você já desenvolve.
### Permissões do modo agente do VS Code
Ferramentas como o modo agente do VS Code dependem principalmente de permissões de UI. O cplt impõe suas restrições no kernel, então o agente não pode contorná-las com um prompt ou uma instrução modificada. Isso importa mais para agentes de CLI e exposição de credenciais:
- o cplt funciona fora da IDE
- variáveis de ambiente são filtradas antes do agente iniciar
- arquivos sensíveis podem ser bloqueados mesmo quando vivem dentro do repositório
- as mesmas restrições se aplicam a processos filhos
### Sandbox do Claude Code (Anthropic Sandbox Runtime)
O [Anthropic Sandbox Runtime](https://github.com/anthropic-experimental/sandbox-runtime) (`srt`) é a camada de sandbox usada pelo Claude Code. Mesma abordagem de alto nível que o cplt, macOS Seatbelt mais imposição em nível de kernel no Linux mais um proxy HTTP, implementação diferente.
| Área | cplt | Anthropic srt |
| --- | --- | --- |
| Linguagem / entrega | Binário Rust único | Pacote Node.js + npm + deps externas |
| Backend Linux | Landlock LSM (sem deps, sem namespaces) | bubblewrap (contêiner via user namespaces) |
| Filtragem de ambiente | Allowlist estrita + negação por sufixo (`_TOKEN`, `_SECRET`) | Herda env pai completo (segredos passam) |
| Proteção de diretório de credenciais | 15+ diretórios negados por padrão | Usuário deve configurar manualmente |
| Proteção contra DNS rebinding | ✅ IP pós-DNS verificado contra faixas privadas | ❌ Não implementado |
| Proxy de rede | HTTP CONNECT + permitir/bloquear domínio | HTTP + SOCKS5 + TLS MITM experimental |
| SSH git | Bloqueado no kernel no macOS (socket do agente negado); no Linux apenas `SSH_AUTH_SOCK` é retido | Proxied via SOCKS5 |
| Scripts de gerenciador de pacotes | Bloqueados por padrão (`npm_config_ignore_scripts`) | Não bloqueados |
| Suporte a agentes | Copilot, OpenCode, Gemini, Antigravity, Pi, Claude Code, goose, DSH, Shell | Claude Code |
| Configuração | TOML (global + por repositório) | JSON (apenas global) + atualizações ao vivo via `--control-fd` |
| API de biblioteca | ❌ Apenas binário | ✅ Biblioteca TypeScript embutível |
O cplt é mais seguro pronto para uso: filtragem de env, proteção de credenciais, verificações de DNS rebinding, bloqueio de scripts de ciclo de vida. O srt é mais flexível: SOCKS5, inspeção TLS, callbacks por requisição, embutimento de biblioteca. A escolha do backend Linux importa. bwrap precisa de contornos no Ubuntu 24.04+ por causa das restrições de userns do AppArmor, enquanto Landlock requer kernel 5.13 ou mais novo mas tem zero dependências externas.
### O próprio sandbox do GitHub Copilot CLI
O Copilot CLI vem com um sandbox local desde junho de 2026, incluído na
assento padrão. Ele executa comandos de shell através do Microsoft MXC com
acesso restrito a sistema de arquivos, rede e sistema, no macOS, Linux e Windows.
`/sandbox enable` o ativa.
Se isso cobre você, use. Não custa nada extra, e roda no Windows,
que o cplt não roda.
Duas coisas que ele não faz.
A política vive com o administrador, não com o repositório. Empresas definem
política de sandbox através do Intune ou outro MDM. Nada fica ao lado do código,
então uma regra que importa para um repositório não pode segui-lo até um contribuidor,
até o CI, ou até um laptop que o MDM não gerencia. No cplt a política é
`.cplt.toml` no repositório. Revisores veem mudanças nele no pull
request, e o arquivo pode apertar a configuração do próprio desenvolvedor mas nunca
afrouxá-la.
Ele confina o processo, não o que o processo faz com credenciais que ele
detém. As abas `/sandbox` cobrem o sistema de arquivos, a rede e capacidades
de sistema, e dentro de um repositório Git o agente recebe leitura e escrita
em `.git` por padrão. Um agente em sandbox ainda tem seu token `gh` e seu
acesso de push. Fazer push de um branch, fazer merge de um pull request e apagar um
repositório são todas chamadas de API bem formadas de um cliente autorizado, e uma
regra de sistema de arquivos ou rede não tem opinião sobre elas. O cplt envolve `git` e
`gh` em vez disso. O agente faz commit, branch e rebase livremente. `gh pr merge`,
`gh repo delete` e `gh release create` são bloqueados por padrão. Também é
`git push` para `main`/`master`; pushes de feature-branch ainda funcionam, porque
`protect_default_branch_only` está ativado. Defina como `false` para bloquear todo push, ou
`git_guard.mode = "warn"` para apenas avisar.
Rodar ambos é razoável. O MXC confina o processo. As guardas decidem o que
o agente pode fazer com as credenciais que ele detém.
### Lacunas honestas
- macOS tem a imposição em nível de arquivo mais forte hoje. A cobertura Linux está melhorando mas não é idêntica.
- O cplt ainda não oferece presets simples de política somente leitura / workspace-write / acesso total.
- Se você quer isolamento total de contêiner, o cplt não está tentando substituir o Docker.
## Instalação
### Homebrew (recomendado)```bash
brew install navikt/tap/cplt
mise use -g 'github:navikt/cplt@'
mise escolhe o asset de release correto para a sua plataforma e verifica a
atestação de proveniência de build.
Fixe a versão. Nossas strings de versão não são semver comparáveis — elas carregam
zeros à esquerda e dois hífens — então `mise latest` pode resolver para uma
release mais antiga do que a mais recente ([navikt/copilot#818](https://github.com/navikt/copilot/issues/818)).
### apt (Debian/Ubuntu, recomendado no Linux)
[navikt/apt](https://navikt.github.io/apt/) é um arquivo assinado servido via
GitHub Pages, contendo cplt e nav-pilot para amd64 e arm64:```bash
curl -fsSL https://navikt.github.io/apt/keyring/navikt-archive-keyring.gpg \
| sudo tee /usr/share/keyrings/navikt-archive-keyring.gpg >/dev/null
echo "deb [signed-by=/usr/share/keyrings/navikt-archive-keyring.gpg] https://navikt.github.io/apt stable main" \
| sudo tee /etc/apt/sources.list.d/navikt.list
sudo apt update && sudo apt install cplt
É um espelho simples de repositório apt que espelha as nossas releases, não um pacote de distribuição com o seu próprio mantenedor. O seu job de publicação corre de hora a hora e puxa o .deb mais recente da última release de cada ferramenta, pelo que uma release cortada há minutos demora até uma hora a tornar-se instalável dessa forma.
O pacote coloca o binário em /usr/bin/cplt, e as atualizações seguem via sudo apt upgrade a partir daí. O cplt update recusa-se a tocar numa instalação apt e aponta para sudo apt upgrade em vez disso: substituir o binário pelas costas do dpkg seria desfeito na próxima execução do apt.
Sem o arquivo, o mesmo .deb é um recurso de release:```bash
arch=$(dpkg --print-architecture) # amd64 or arm64
gh release download --repo navikt/cplt --pattern "${arch}.deb"
sudo apt install ./cplt_"${arch}".deb
### curl | bash
Para distribuições que não são derivadas do Debian, e para CI:```bash
curl -fsSL https://raw.githubusercontent.com/navikt/cplt/main/install.sh | bash
Opções:```bash
curl -fsSL ... | bash -s -- --version 2026.05.05-174753-75bae5b
curl -fsSL ... | bash -s -- --dir ~/.local/bin
curl -fsSL ... | bash -s -- --no-brew
### Baixar das releases
Pegue a build mais recente para a sua plataforma em [GitHub Releases](https://github.com/navikt/cplt/releases/latest):```bash
# macOS, Apple Silicon (M1/M2/M3/M4)
curl -fsSL https://github.com/navikt/cplt/releases/latest/download/cplt-aarch64-apple-darwin.tar.gz | tar xz
sudo mv cplt /usr/local/bin/
# macOS, Intel
curl -fsSL https://github.com/navikt/cplt/releases/latest/download/cplt-x86_64-apple-darwin.tar.gz | tar xz
sudo mv cplt /usr/local/bin/
# Linux, x86_64
curl -fsSL https://github.com/navikt/cplt/releases/latest/download/cplt-x86_64-unknown-linux-gnu.tar.gz | tar xz
sudo mv cplt /usr/local/bin/
# Linux, ARM64
curl -fsSL https://github.com/navikt/cplt/releases/latest/download/cplt-aarch64-unknown-linux-gnu.tar.gz | tar xz
sudo mv cplt /usr/local/bin/
Todo binário de release carrega uma atestação de proveniência de build. Verifique-a:```bash gh attestation verify cplt -o navikt
### Compilar a partir do código-fonte```bash
git clone https://github.com/navikt/cplt.git && cd cplt
cargo build --release
sudo cp target/release/cplt /usr/local/bin/
Ou com mise:```bash mise run install
`mise run install` e builds manuais colocam o cplt em `/usr/local/bin/cplt`. Se você também tem o build do Homebrew em `/opt/homebrew/bin/cplt`, coloque `/usr/local/bin` primeiro no `PATH` para que seu build de desenvolvimento tenha prioridade:```bash
# Check which cplt is active
which cplt
# If it shows /opt/homebrew/bin/cplt, reorder your PATH:
export PATH="/usr/local/bin:$PATH"
Ou apenas execute /usr/local/bin/cplt explicitamente e ignore completamente a resolução do PATH.
O cplt não tem backend de sandbox para Windows. A aplicação é feita via Apple Seatbelt no macOS e Landlock LSM no Linux, portanto não há nada para executar nativamente no Windows. A rota suportada é o WSL2, onde o cplt é uma instalação Linux comum e a sandbox é aplicada pelo kernel. Todos os ramos do kernel da Microsoft compilam com CONFIG_SECURITY_LANDLOCK=y e listam landlock em primeiro lugar em CONFIG_LSM (config-wsl), disponível desde o kernel 5.15.57.1, e a linha de comando padrão do kernel do WSL não define nenhuma substituição lsm=.
No PowerShell, uma vez:```powershell wsl --install # WSL2 + the default distro (now Ubuntu 26.04 LTS), then reboot wsl --install -d Ubuntu-24.04 # ...or pin an older release wsl --update # keep the Microsoft kernel current, see the ABI note below
Tudo abaixo é executado **dentro da distro** (`wsl`, ou o perfil Ubuntu no Windows Terminal), não no PowerShell:```bash
# 1. Node. Copilot CLI requires Node 22+
# Ubuntu 26.04 ships 22.x, so apt is enough:
sudo apt update && sudo apt install -y nodejs npm
# Ubuntu 24.04 ships Node 18, too old. Use nvm, fnm, or NodeSource there instead.
# 2. GitHub CLI, and log in. Ubuntu's universe package works but lags
# (2.45 on 24.04); add GitHub's apt repo if you want a current gh:
# https://github.com/cli/cli/blob/trunk/docs/install_linux.md
sudo apt install -y gh
gh auth login
# 3. The agent, installed in the distro, never on the Windows side
npm install -g @github/copilot
# 4. cplt, from the apt archive. The default distro is Ubuntu, so this is
# the same route as on any other Debian derivative.
curl -fsSL https://navikt.github.io/apt/keyring/navikt-archive-keyring.gpg \
| sudo tee /usr/share/keyrings/navikt-archive-keyring.gpg >/dev/null
echo "deb [signed-by=/usr/share/keyrings/navikt-archive-keyring.gpg] https://navikt.github.io/apt stable main" \
| sudo tee /etc/apt/sources.list.d/navikt.list
sudo apt update && sudo apt install cplt
# 5. Check the result
cplt doctor
Não instale o Copilot CLI no lado do Windows. Com o interop ativado (o padrão), o PATH do Windows é anexado ao da distro, então um npm install -g @github/copilot do lado do Windows aparece dentro da distro como /mnt/c/Users/<user>/AppData/Roaming/npm/copilot. Essa é uma instalação do Windows acessada via interop. Ela não pode ser executada no sandbox do Linux, e o shim do npm executa um node que a distro não terá, a menos que você tenha instalado um lá também. O sintoma costumava ser um erro de extração de runtime não relacionado. O cplt agora nomeia a causa quando resolve um agente sob /mnt/<drive>/ e está sendo executado sob WSL, e cplt doctor o reporta como uma verificação com falha em vez de passar (#188). O WSL é detectado a partir de estado pertencente ao kernel, seja /run/WSL ou o nome do kernel em /proc/sys/kernel/osrelease e /proc/version, não a partir de WSL_DISTRO_NAME, que está ausente sob sudo e em unidades systemd e que qualquer processo pode definir. Em uma máquina Linux comum, /mnt/c é deixado em paz. É um ponto de montagem comum lá.
Essa verificação tem dois limites, ambos deliberados. Ela se baseia no default automount root, então se você o realocou ([automount] root em /etc/wsl.conf), a instalação do lado do Windows não é reconhecida e você obtém a falha antiga, menos útil, com o caminho nela. E desativar o interop impede que o PATH do Windows vaze, mas não desmonta /mnt/c.
Kernel e Landlock ABI. O WSL atual (2.7.x e posterior) traz o Linux 6.18, que fornece Landlock ABI 7 — tudo o que o cplt usa, exceto o direito de connect() de unix-socket, que precisa do ABI 9 (kernel 7.1). Uma instalação ainda na linha de kernel 6.6 obtém o ABI 3: as regras de filesystem são aplicadas, mas as regras de porta TCP (ABI 4), a restrição de ioctl (ABI 5) e o escopo de signal/abstract-socket (ABI 6) não estão disponíveis, e a filtragem de rede recorre ao proxy CONNECT. wsl --update avança você. cplt doctor imprime a versão do kernel e o ABI que encontrou, que é a verificação que importa na sua máquina.
Não desative o Landlock em
.wslconfig. Um[wsl2] kernelCommandLinecom uma listalsm=que omitelandlock, ou um[wsl2] kernel=personalizado compilado semCONFIG_SECURITY_LANDLOCK, remove a aplicação do kernel da qual o cplt depende, ecplt doctorreportará o Landlock como indisponível.
Mantenha o projeto no filesystem do Linux. Trabalhe em ~/src/... dentro da distro em vez de /mnt/c/Users/.... A própria orientação da Microsoft é que o acesso a arquivos entre sistemas operacionais é marcadamente mais lento, e /mnt/c é servido via 9p por padrão a partir do WSL 2.9.x (virtiofs é opt-in via [wsl2] virtiofs=true). Mais ao ponto, não verificamos como o Landlock aplica regras nessa montagem. O kernel não documenta nenhuma exclusão para filesystems com suporte de rede ou FUSE, apenas pipes, sockets e nsfs, e a própria suíte de testes do Landlock exercita 9p e FUSE, então esperamos que funcione. Ninguém aqui confirmou isso. Trate um projeto sob /mnt/c como não comprovado em vez de suportado.
Bubblewrap. O Ubuntu 23.10+ bloqueia namespaces de usuário não privilegiados através de kernel.apparmor_restrict_unprivileged_userns, o que quebra o bwrap. Esse sysctl vem de um patch do kernel do Ubuntu que está ausente do kernel da Microsoft, então a camada opcional do Bubblewrap deve funcionar no Ubuntu-sob-WSL2. Isso é inferência a partir do código-fonte do kernel, não algo que executamos. Se o bwrap falhar lá, por favor diga isso em #189. O próprio filtro seccomp do cplt é um programa BPF simples de PR_SET_SECCOMP, que se empilha sobre o filtro que o WSL instala em cada processo.
Ainda não verificado em uma instalação real do WSL2. Verificado a partir do código-fonte: o Landlock está compilado e é o primeiro em
CONFIG_LSMno kernel da Microsoft; a detecção de/mnt/<drive>/, os sinais de WSL que ele usa e seu texto de erro; quecplt doctorfalha em tal agente e imprime kernel + Landlock ABI; os requisitos de 5.13+/6.7+; e queinstall.shinstala o binário de release do Linux. Ainda não verificado por ninguém aqui: como o Landlock se comporta em/mnt/c, se o Bubblewrap funciona sob WSL2, as versões exatas de pacotes que sua release de distro fornece, e a sequência acima de ponta a ponta. Se você executá-lo, por favor relate o que realmente aconteceu em #189.
Por padrão, você obtém o sandbox digitando cplt. Para fazer o copilot simples também ser executado em sandbox:```bash
cplt --shell-install
Isso detecta seu shell, anexa o alias ao seu arquivo rc e imprime o que fez. Execute quantas vezes quiser, ele não adicionará duplicatas.
`--agent` escolhe qual comando recebe o alias, e todo agente que o cplt pode iniciar está disponível:```bash
cplt --shell-install --agent opencode # 'opencode' runs sandboxed
cplt --shell-install --agent claude # and 'claude', alongside the others
Cada instalação adiciona ao seu arquivo rc em vez de substituir o que já está lá, então você pode isolar quantos agentes usar. Sem --agent você obtém copilot, que é o que a flag sempre instalou.
| Shell | Arquivo modificado | O que é adicionado (para --agent opencode) |
|---|---|---|
| zsh (padrão do macOS) | ~/.zshrc | eval "$(cplt --shell-setup --agent opencode)" |
| bash | ~/.bashrc | eval "$(cplt --shell-setup --agent opencode)" |
| fish | ~/.config/fish/conf.d/cplt.fish | alias opencode 'cplt --agent opencode' |
--agent antigravity instala aliases tanto para antigravity quanto para agy, já que qualquer um dos nomes inicia o mesmo agente.
Reinicie seu shell ou dê source no arquivo para ativar.
Não há alias para --agent shell: não existe um binário shell para sobrescrever. Digite cplt --agent shell para um shell isolado, ou cplt exec -- <command> para um único comando.
Se você preferir não usar --shell-install, adicione a linha você mesmo:```bash
eval "$(cplt --shell-setup --agent opencode)"
alias opencode 'cplt --agent opencode'
O mesmo padrão que mise, direnv e starship usam.
</details>
**Por que cada alias nomeia seu agente.** `alias opencode=cplt` não faria o que parece. O `cplt` simplesmente escolhe seu agente a partir de `--agent`, depois do arquivo de configuração, e então do que encontrar no PATH — e a detecção do PATH prefere `copilot`. Digitar `opencode` acabaria isolando o Copilot, sem nada na tela para avisar. O alias passa `--agent` para que o comando que você digita seja o agente que você obtém.
**Por que um alias em vez de um symlink?** O cplt e o Copilot CLI instalam no mesmo diretório bin do Homebrew (`/opt/homebrew/bin/`), e apenas um arquivo chamado `copilot` pode existir lá, então um symlink entraria em conflito. Um alias contorna isso. O binário real `copilot` permanece no PATH onde o cplt pode encontrá-lo e envolvê-lo, e o alias redireciona seu comando.
> **Nota:** o cplt se recusa a aninhar. Se ele detectar que já está sendo executado dentro de um sandbox (através da variável de ambiente `__CPLT_WRAPPED`), ele não iniciará novamente. Subcomandos somente leitura, como `--print-profile` e `cplt doctor`, ainda funcionam dentro de um sandbox existente.
## Uso```
cplt [OPTIONS] [-- <AGENT_ARGS>...]
Tudo depois de -- vai direto para o processo do agente (copilot, opencode, gemini, antigravity, pi, claude, goose, dsh ou shell).
Um preset define uma linha de base para os cinco principais toggles de sandbox com uma flag em vez de uma lista delas. Flags individuais ainda prevalecem sobre o preset, então --preset permissive --no-allow-tmp-exec faz o que diz. Também configurável como [sandbox] preset = "..." no config.
Matriz completa de presets e ordem de resolução: docs/configuration.md.
O diretório do projeto é o workspace gravável, mais uma allowlist estreita necessária para autenticação, runtime e ferramentas (veja a tabela acima). O kernel bloqueia todo o resto, incluindo chaves SSH e credenciais de nuvem.
cplt sanitiza o ambiente filho por padrão. Apenas variáveis seguras passam, e credenciais de nuvem, URLs de banco de dados e tokens de pacotes são removidos. Também injeta variáveis de hardening que bloqueiam scripts de ciclo de vida do npm/yarn/pnpm (hooks postinstall, o vetor de ataque de cadeia de suprimentos número um), desabilitam assinatura de commit e tag do git (já que ~/.ssh e ~/.gnupg são inacessíveis dentro do sandbox), e optam por não participar de telemetria de ferramentas de desenvolvimento (DO_NOT_TRACK=1, NEXT_TELEMETRY_DISABLED=1, TURBO_TELEMETRY_DISABLED=1, CHECKPOINT_DISABLE=1, e outros).
O que passa:
Allowlist de prefixo com proteção de sufixo secreto. Uma variável que corresponde a um prefixo permitido como COPILOT_* ou YARN_* ainda é descartada se terminar em um sufixo portador de segredo: _TOKEN, _AUTH, _SECRET, _SECRET_KEY, _KEY, _PASSWORD ou _CREDENTIALS. Então COPILOT_DEBUG passa e COPILOT_API_KEY não.
Sempre bloqueados: AWS_*, AZURE_*, NPM_TOKEN, DATABASE_URL, VAULT_TOKEN, SSH_AUTH_SOCK, variáveis do Docker, tokens de CI, e qualquer coisa que não esteja na allowlist.
| Flag | O que faz |
|---|---|
--pass-env <VAR> | Passa uma variável de ambiente para o agente. Repetível |
--inherit-env | ⚠️ Perigoso. Herda o ambiente pai completo. Remove apenas , , , . Apenas para depuração |
cplt auto-descobre ferramentas instaladas e escreve regras de sandbox para corresponder. Geralmente apenas diretórios que existem no disco recebem regras, então não há caminhos fantasma. No macOS, diretórios de aplicativos graváveis são incluídos quando descobertos mesmo se ainda não existirem, para que possam ser criados no primeiro uso. O Linux não pode permitir escrita em um caminho inexistente, então a criação precisa acontecer fora do sandbox lá.
Execute cplt doctor para ver se o cplt funcionará aqui para o seu agente, e cplt doctor --verbose para tudo que ele detectou na sua máquina.
Estas se traduzem nas próprias flags de sessão do agente, então você não precisa de um separador --.
--continue e --resume também são mapeados para OpenCode, Antigravity e Claude Code:
¹ Nem OpenCode nem Antigravity têm um seletor de sessão interativo, então um --resume sozinho significa "continuar a última sessão". Claude Code tem um, então mapeia diretamente.
--remote e --name são exclusivos do Copilot. Pi e modo shell não recebem tradução alguma, então todas as quatro flags são descartadas para eles. Auto-resume é um mecanismo separado: quando você invoca cplt sem args de pass-through e sem flags de sessão, ele anexa --resume para você, e isso se aplica apenas ao Copilot.
Combine-os com flags de sandbox e args de pass-through --:```bash
cplt --resume=my-task # resume by name
cplt --remote --name my-task -- -p "fix tests" # remote + named + prompt
### Agentes
Escolha um com `--agent <name>`, ou torne-o o padrão com `cplt config set sandbox.agent <name>`. Copilot, OpenCode e Antigravity são detectados automaticamente a partir do `PATH` nessa ordem quando você não nomeia um.
| Agente | Valor de `--agent` | Detectado automaticamente | Autenticação |
| --- | --- | --- | --- |
| GitHub Copilot CLI | `copilot` | sim, prioridade 1 | Token do GitHub, do Keychain ou do `gh` |
| [OpenCode](https://opencode.ai/) | `opencode` | sim, prioridade 2 | Assinatura do Copilot via `/connect`, ou `--pass-env ANTHROPIC_API_KEY` |
| [Antigravity CLI](https://github.com/google-antigravity/antigravity-cli) | `antigravity`, aliases `agy` e `agi` | sim, prioridade 3 | Google OAuth no navegador |
| [Pi](https://github.com/earendil-works/pi) | `pi` | não | `--pass-env ANTHROPIC_API_KEY` e similares |
| [Claude Code](https://docs.anthropic.com/en/docs/claude-code) | `claude`, aliases `cc` e `claude-code` | não | OAuth de assinatura em `~/.claude` ou no Keychain, `CLAUDE_CODE_OAUTH_TOKEN` (descarta a concessão do Keychain), ou `--pass-env ANTHROPIC_API_KEY` |
| [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) | `dsh`, aliases `deepseek` e `deepseek-harness` | não | `--pass-env DEEPSEEK_API_KEY`, ou `$DSH_HOME/.env` (`~/.dsh/.env`) |
| Seu shell | `shell` | não | nenhuma |
- **Pi, Claude Code, goose e DeepSeek Harness nunca são detectados automaticamente.** `pi` e `dsh` são nomes de binários genéricos que podem colidir com outra coisa na sua máquina, e o Claude Code precisa ser escolhido de propósito.
- **Chaves de API de terceiros são opt-in.** `ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, `GEMINI_API_KEY`, `OPENROUTER_API_KEY`, `ANTHROPIC_AUTH_TOKEN`, `CLAUDE_CODE_OAUTH_TOKEN` e as variáveis de roteamento Bedrock/Vertex (`CLAUDE_CODE_USE_BEDROCK`, `AWS_BEARER_TOKEN_BEDROCK`, `CLAUDE_CODE_USE_VERTEX`, `ANTHROPIC_VERTEX_PROJECT_ID`, `GOOGLE_CLOUD_PROJECT`) nunca passam, a menos que você as nomeie com `--pass-env`.
- **Autenticação por assinatura não precisa de variável de ambiente.** O fluxo de dispositivo `/connect` do OpenCode armazena seu token em `~/.local/share/opencode/auth.json`, e o token OAuth do Claude Code fica em `~/.claude` (`.credentials.json` no Linux) ou no Keychain do macOS. Ambos são acessíveis dentro do sandbox, então o cplt não fica insistindo sobre uma chave de API ausente para nenhum dos dois.
- **Fluxos OAuth no navegador precisam de `--allow-browser`** quando aparece um prompt de login. Isso cobre o Antigravity; todos os outros agentes aqui usam um fluxo de dispositivo que imprime um código e uma URL e não precisa de navegador. A flag permite que o agente inicie qualquer aplicação fora do sandbox e não pode ser restringida a URLs, então ative-a para o login e desative-a novamente — veja a [tabela de flags](#sandbox-toggles) e [docs/security.md](https://github.com/navikt/cplt/blob/main/docs/security.md#--allow-browser-is-a-sandbox-escape-and-cannot-be-scoped).
- **A atualização automática do Claude Code está desabilitada** com `DISABLE_AUTOUPDATER=1`. O Claude Code não tem uma flag `--no-auto-update`, a auto-atualização dentro do sandbox é um vetor de persistência, e falharia de qualquer forma contra caminhos de instalação somente leitura.
- **`CLAUDE_CONFIG_DIR` é respeitado.** Quando definido, o cplt concede esse diretório em vez de `~/.claude` e repassa a variável, então uma raiz de configuração realocada continua funcionando.
- O OpenCode é [um cliente Copilot oficialmente suportado](https://github.blog/changelog/2026-01-16-github-copilot-now-supports-opencode/), então sua assinatura existente do Copilot funciona com `/connect` dentro do OpenCode.
Diretórios de configuração por agente, uso do Keychain, permissões de execução e isolamento de ambiente estão em [SECURITY.md](https://github.com/navikt/cplt/blob/main/SECURITY.md#supported-agents).
### Suporte ao goose
O cplt pode colocar em sandbox o [goose](https://github.com/aaif-goose/goose), o agente de IA de código aberto (binário `goose`). Verificado com o goose 1.48.0.```bash
# Run goose (must be explicit — not auto-detected)
cplt --agent goose
# goose is provider-agnostic — pass your provider's API key
cplt --agent goose --pass-env ANTHROPIC_API_KEY
cplt --agent goose --pass-env OPENAI_API_KEY
# Skip the keyring entirely: keep the key in the environment
GOOSE_DISABLE_KEYRING=1 cplt --agent goose --pass-env OPENAI_API_KEY --pass-env GOOSE_DISABLE_KEYRING
# Set goose as your default agent
cplt config set sandbox.agent goose
Notas de segurança para goose:
--agent goose ou defina sandbox.agent = "goose" na configANTHROPIC_API_KEY, OPENAI_API_KEY, AZURE_OPENAI_API_KEY, GOOGLE_API_KEY, DATABRICKS_HOST/DATABRICKS_TOKEN, GROQ_API_KEY, OPENROUTER_API_KEY, XAI_API_KEY, AWS_BEARER_TOKEN_BEDROCK) são reconhecidas como dicas de autenticação e devem ser passadas via --pass-env. O goose lê , não . Qualquer provedor fora desse subconjunto ainda funciona: nomeie sua variável com O cplt pode colocar em sandbox o DeepSeek Harness (binário dsh), o harness de agente orientado a plugins da DeepSeek. O upstream o disponibiliza como developer preview e seu próprio SAFETY.md diz para não confiar em seus controles como a única fronteira, que é exatamente o caso para o qual o cplt existe.```bash
cplt --agent dsh
cplt --agent dsh --pass-env DEEPSEEK_API_KEY
cplt config set sandbox.agent dsh
**Notas de segurança para DSH:**
- **Não é detetado automaticamente**: selecione-o com `--agent dsh` (aliases `deepseek`, `deepseek-harness`) ou defina `sandbox.agent = "dsh"`. `dsh` é um nome de comando curto e genérico que pode pertencer a outra coisa na sua máquina
- **Desative o sandbox do próprio DSH dentro do cplt**: o DSH envolve cada chamada de shell e de ferramenta de ficheiro no seu próprio sandbox de processo — Seatbelt no macOS, bwrap ou Landlock no Linux. Nenhum deles se aninha dentro do cplt. O macOS não suporta chamadas `sandbox-exec` aninhadas (a mesma limitação que faz o cplt desativar o sandbox interno do Gradle, ver [Limitations](#limitations)), e o bwrap constrói o seu namespace com `unshare`, que o filtro seccomp do cplt nega. O cplt é a fronteira de imposição de qualquer forma, por isso escolha o preset de permissões `danger-full-access` fornecido pelo DSH para sessões em sandbox. Deixe o runner interno ativo e as chamadas de ferramentas falham com um erro do sandbox runner em vez de um erro de tarefa
- **Uma raiz home, e o cplt segue a substituição**: o DSH mantém sessões, definições, cache e perfis sob `$DSH_HOME` (`~/.dsh` por predefinição). `DSH_HOME` está na lista de permissões de env, por isso o processo filho resolve a mesma raiz que o cplt concede. Um valor que aponte para uma raiz de sistema ou para o seu diretório home é recusado antes do lançamento, o mesmo veto pelo qual `CLAUDE_CONFIG_DIR` passa
- **Guarda de persistência no host**: `$DSH_HOME/cordis.patch.yml`, o overlay ao nível do home que o Loader lê no arranque, tem escrita negada. `$DSH_HOME/profiles/` permanece gravável porque o DSH reescreve a raiz de inclusão `cordis.yml` de cada perfil a cada arranque, por isso um `cordis.patch.yml` por perfil e plugins instalados são um residual documentado — faça edições de perfil e de `dsh plugin` fora do cplt, e lance sempre `dsh` através do cplt para que qualquer coisa plantada continue a correr em sandbox
- **Domínios predefinidos**: apenas `deepseek.com`. O adaptador `dsh-llm-deepseek` fornecido usa por predefinição `https://api.deepseek.com`. Aponte `DEEPSEEK_BASE_URL` para um gateway e terá de adicionar o domínio desse gateway via `allowed_domains`
- **Autenticação**: passe a chave com `--pass-env DEEPSEEK_API_KEY`, ou mantenha-a em `$DSH_HOME/.env`. Uma chave guardada através da própria UI de modelos do DSH fica em `$DSH_HOME/.credentials.yaml`, dentro da mesma raiz gravável. O Keychain do macOS é negado, por isso `git push` sobre HTTPS precisa do token do `gh` em `hosts.yml` ou de `--pass-env GH_TOKEN`
### Modo shell
Execute uma shell simples em sandbox sem agente de IA e com as mesmas restrições. Útil para testar ferramentas de build, depurar problemas de sandbox, ou simplesmente trabalhar com cuidado à mão.```bash
# Interactive sandboxed shell (uses $SHELL: fish, zsh, bash)
cplt --agent shell
# Inspect what's allowed without entering the shell
cplt --agent shell --print-profile
As mesmas regras de negação por padrão se aplicam: isolamento do sistema de arquivos, restrições de rede, sanitização de env. Os diretórios de configuração do shell (variáveis e histórico do fish, histórico do zsh) permanecem graváveis.
Para um único comando, cplt exec é mais limpo do que cplt --agent shell -- -c 'cmd'.
Execute qualquer comando dentro do sandbox sem iniciar um agente. Sem banner de inicialização, sem prompt de confirmação, portanto é adequado para scripts, pipes e aliases de shell.```bash
cplt exec -- npm install cplt exec -- make build cplt exec -- go test ./...
cplt exec -c "npm install && npm test"
cplt exec --allow-lifecycle-scripts -- npm install cplt exec --project-dir /path/to/repo -- make build cplt exec --with-proxy -- curl https://example.com
alias npm="cplt exec -- npm" alias node="cplt exec -- node" alias python="cplt exec -- python"
Todas as flags de nível superior do `cplt` se aplicam: `--project-dir`, `--allow-read`, `--deny-path`, `--with-proxy`, `--pass-env` e o restante. Adicione `--no-quiet` para ver o resumo completo da configuração do sandbox antes da execução do comando.
### Exemplos```bash
# The common case: Copilot in the sandbox
cplt -- -p "fix the tests"
# Sessions
cplt --resume # pick one interactively
cplt --resume=my-refactor # by name
cplt --continue # most recent in this directory
cplt --remote --name my-task -- -p "fix tests" # named remote session
# Check the environment before the first run
cplt doctor
# Let Copilot read a shared library directory
cplt --allow-read ~/shared-libs -- -p "use shared-libs"
# Block a path you don't want Copilot to see
cplt --deny-path ~/.config/gh -- -p "refactor auth"
# Extra outbound port, e.g. an external API
cplt --allow-port 8443 -- -p "test the API"
# Localhost for MCP servers or dev servers
cplt --allow-localhost 3000 --allow-localhost 8080 -- -p "use the MCP server"
# All of localhost, needed by Next.js/Turbopack and Vite builds
cplt --allow-localhost-any -- -p "fix the build"
# Pass specific env vars through
cplt --pass-env MY_CUSTOM_VAR --pass-env ANOTHER_VAR -- -p "run with custom config"
# Inherit the full environment (dangerous, debugging only)
cplt --inherit-env -- -p "debug the build"
# Network
cplt --no-proxy -- -p "fix the tests" # proxy is on by default
cplt --blocked-domains ./blocked-domains.txt -- -p "refactor"
cplt --allow-private-domain intern.nav.no -- -p "use mcp-onboarding"
# Non-interactive / CI (skip the confirmation prompt)
cplt --yes -- -p "fix the tests"
# Inspect and debug the sandbox itself
cplt --print-profile
cplt --show-denials -- -p "fix the tests"
A configuração acontece em dois níveis: global, para preferências do desenvolvedor, e por repositório, para políticas da equipe.```bash
cplt settings
cplt config set sandbox.quiet true cplt config set proxy.blocked_domains "~/.config/cplt/blocked-domains.txt" cplt config set git_guard.mode warn # observe pushes instead of blocking them cplt config set gh_guard.enabled false # opt out of the gh guard entirely
cplt config set --repo sandbox.allow_jvm_attach true cplt config set --repo deny.paths "~/secrets"
cplt config show # effective config (file + defaults) cplt config explain # every key with its description
`cplt settings` é o editor interativo, com visualizações Effective, Global e Repository, pesquisa, alterações preparadas e uma confirmação explícita antes de guardar qualquer coisa sensível à segurança. `cplt config` mantém-se a interface não interativa estável para scripts e CI. As propostas de repositório continuam a ser submetidas e aprovadas separadamente com `cplt trust`. O editor nunca as submete nem aprova automaticamente.
A precedência segue as flags da CLI, depois o ficheiro de configuração global em `~/.config/cplt/config.toml`, e depois os valores predefinidos incorporados. A configuração por repositório em `.cplt.toml` é uma camada separada em vez de um degrau nessa escada: `[deny]` restringe incondicionalmente, e as permissões aprovadas são apenas aditivas, pelo que um repositório pode ativar uma funcionalidade mas nunca pode desativar algo definido por uma flag da CLI ou pela configuração global.
Um `.cplt.toml` na raiz do repositório contém a política da equipa:```toml
[deny] # Applied automatically, no opt-in needed
paths = ["~/secrets", "~/.vault-token"]
env = ["VAULT_TOKEN", "DATABASE_URL"]
[propose] # Requires developer approval (cplt trust accept)
gh_guard = true
git_push_prevention = true
allow_jvm_attach = true
allow_docker = true
[propose.allow]
ports = [5432]
localhost = [3000]
socket = ["/var/run/docker.sock"]
O cplt lê-o a partir do git HEAD, pelo que o agente não pode adulterar a sua própria política a meio da sessão, e as aprovações de confiança ficam fixadas ao conteúdo do ficheiro. Um .cplt.toml não commitado não concede nada até ser commitado, embora as suas chaves [deny] continuem a aplicar-se. Em CI e scripts, onde ninguém pode responder a um prompt, --accept-repo-config aprova as propostas do ficheiro commitado para essa única execução sem persistir qualquer confiança. O cplt init escreve um por ti ao detetar as ferramentas do projeto:```bash
cplt init # preview detected permissions
cplt init --write # write .cplt.toml to disk
cplt init --quiet # output only TOML (pipe-friendly)
cplt init --global # generate a personal ~/.config/cplt/config.toml
Ele conhece JVM (Gradle/Maven), Node.js, Docker, Python, Rust, Go, Playwright, Spring Boot, Ktor, TestContainers, Next.js, Vite, Flyway, Cypress, e segredos de ambiente a partir de `.env.example`. Permissões perigosas saem do gerador com um aviso de risco anexado. `--global` olha para coisas ao nível da máquina: navegadores Playwright, assinatura GPG, credenciais de registry, agentes alternativos.
Algumas chaves são apenas globais e rejeitadas de `.cplt.toml` porque são específicas da máquina ou uma preferência local: `sandbox.agent`, `sandbox.quiet`, `sandbox.yes`, `sandbox.validate`, `sandbox.scratch_dir`, `sandbox.pass_env`, `sandbox.inherit_env`, `sandbox.allow_cache_exec`, `sandbox.allow_cache_exec_any`, `proxy.enabled`, `proxy.port`, `proxy.log_file`, `proxy.log_level`, `proxy.blocked_domains`, `proxy.allowed_domains`, e todas as chaves `[gh_guard]` e `[git_guard]`.
Detalhes completos, incluindo o modelo de confiança, regras de expansão de caminhos, e a referência completa do ficheiro de configuração: [docs/configuration.md](https://github.com/navikt/cplt/blob/main/docs/configuration.md).
## Arquitetura```
┌──────────────────────────────────┐
│ cplt (Rust binary) │
│ ┌───────────┐ ┌─────────────┐ │
│ │ Policy │ │ CONNECT │ │
│ │ Generator │ │ Proxy │ │
│ └─────┬─────┘ │ (optional) │ │
│ │ └─────────────┘ │
│ ▼ │
│ ┌─────────────┬────────────┐ │
│ │ macOS │ Linux │ │
│ │ Seatbelt │ Landlock │ │
│ │ sandbox- │ + seccomp │ │
│ │ exec │ pre_exec │ │
│ └─────────────┴────────────┘ │
│ │ │
│ ▼ │
│ copilot (sandboxed) │
│ ├── All child processes │
│ ├── Cannot read ~/.ssh │
│ ├── Network port-restricted │
│ ├── SSH agent blocked │
│ └── Filesystem = primary ctrl │
└──────────────────────────────────┘
O modelo de segurança é um sistema de arquivos deny-by-default com imposição pelo kernel. No macOS, e no Linux com kernel 6.7+ (Landlock ABI v4), a rede é restrita à porta 443 por padrão, com --allow-port para extras. Em kernels Linux mais antigos, o proxy CONNECT fornece essa restrição, razão pela qual está habilitado por padrão. O acesso ao agente SSH e a saída para localhost são bloqueados no kernel no macOS. No Linux, nenhum dos dois é: regras Landlock baseadas em porta não conseguem distinguir localhost de um host remoto, e o connect() de socket unix não é controlado pelo Landlock abaixo do kernel 7.1, então, além dos sockets que o bubblewrap mascara, o SSH_AUTH_SOCK retido é a única coisa entre o agente e suas chaves carregadas. O gerador de perfil descobre seu ambiente (cplt doctor --verbose mostra os mesmos resultados de sondagem) e emite regras apenas para diretórios de ferramentas que realmente existem no disco. Menos regras, sandbox mais restrita.
sandbox-execpre_exec (kernel 5.13+, filtragem de porta TCP no 6.7+)Internos e layout de módulos: docs/architecture.md. Modelo de ameaças, camadas de defesa e lacunas honestas: SECURITY.md.
Um binário, dependências mínimas, sem serviços em tempo de execução, sem telemetria. Três camadas de defesa, com limites claros entre elas:
Contra o que o cplt protege:
.env): bloqueado pelo kernel.git/hooks tem escrita negada no kernel no macOS. No Linux, com Landlock e sem Bubblewrap, permanece gravável, e o próprio git do lado pai do cplt então é executado com core.hooksPath=/dev/null, de modo que nunca executa um hook plantado, embora um git que você mesmo execute ainda o faráPNPM_HOME, ~/.deno/bin, ~/.bun/bin): a escrita é concedida ali para que pnpm add -g e similares funcionem dentro da sandbox, então um agente pode deixar um binário para trás que um shell posterior pegará do seu PATHContra o que o cplt não protege:
sandbox.keychain_substitute pode trocar a concessão onde um agente tem outra credencialNossas prioridades, em ordem: correto (toda afirmação é testada, todo caso extremo tem uma CVE ou referência de pesquisa), transparente (SECURITY.md não esconde nada), simples (um binário, zero configuração necessária, padrões sensatos) e útil (sair do caminho e deixar o agente trabalhar, com segurança).
Mais: docs/security.md · SECURITY.md
O proxy está ativado por padrão. Todo o tráfego de saída do Copilot CLI, gh e curl passa por um proxy CONNECT localhost via HTTP_PROXY/HTTPS_PROXY e NODE_USE_ENV_PROXY=1. Ele escuta em uma porta efêmera atribuída pelo SO, então nada colide. Você obtém registro de conexões em tempo real, bloqueio de domínios, lista de permissões de domínios, um log de auditoria persistente e a mesma política de portas que a sandbox impõe (443 mais qualquer coisa em allow.ports).```bash
cplt --proxy-forced -- -p "fix tests" # force all egress through the proxy
cplt --no-proxy -- -p "fix tests" # disable for one run
cplt --blocked-domains blocked-domains.txt -- -p "x" # block known-bad domains
cplt --allowed-domains allowed-domains.txt -- -p "x" # allowlist mode
cplt --default-allowlist -- -p "x" # fail-closed: only the agent's own domains
cplt --observe-domains -- -p "x" # record what the agent contacts, block nothing
cplt --proxy-upstream http://proxy.corp:8080 -- -p "x" # chain through a corporate proxy
`--observe-domains-out <FILE>` grava o conjunto observado, um domínio por linha, e
`--proxy-upstream-no-proxy <HOST>` lista os hosts a serem acessados diretamente em vez de através
do upstream.```bash
cplt config set proxy.enabled false
cplt config set proxy.blocked_domains "~/.config/cplt/blocked-domains.txt"
cplt config set proxy.allowed_domains "~/.config/cplt/allowed-domains.txt"
cplt config set proxy.log_file "~/.config/cplt/proxy.log"
O modo forçado por proxy é opt-in. Ele restringe a saída do kernel à porta do proxy, de modo que um socket aberto diretamente, ou um env -u HTTPS_PROXY, não consiga escapar. A aplicação é total no macOS, que fixa em localhost:<proxy_port>. No Linux, bloqueia TCP direto para :443, e uma regra seccomp permite apenas SOCK_STREAM com protocolo 0 ou IPPROTO_TCP para AF_INET/AF_INET6, portanto UDP, raw, SCTP e DCCP também ficam fechados — ao custo de qualquer coisa que abra tal socket, não apenas código que envia UDP. O que resta é um resíduo baseado em porta, evil.com:<proxy_port>, até #114.
Fora do modo forçado por proxy, o Linux não restringe UDP. Os direitos de rede do Landlock são apenas TCP até a ABI v10, o cplt trata apenas AccessNet::ConnectTcp, e a regra seccomp acima é deliberadamente não aplicada — negar SOCK_DGRAM ali quebraria getaddrinfo(3), e portanto todo o DNS, para toda ferramenta não-proxied. UDP de saída para qualquer host, bind UDP de entrada, tunelamento DNS e QUIC/HTTP-3 ficam, portanto, sem mediação no modo padrão, e o proxy CONNECT transporta apenas TCP, então nada disso aparece no log do proxy. O macOS restringe UDP no modo padrão, mas também não o roteia: remote ip "*:443" cobre UDP, então QUIC/HTTP-3 na 443 sai sem tocar no proxy lá também. Sob proxy.forced, o log do proxy é um registro completo da saída no macOS. No Linux, é completo exceto pelo resíduo evil.com:<proxy_port> acima, que não atravessa o proxy e portanto não aparece em seu log.
Ambas as listas correspondem da mesma forma: example.com cobre o domínio exato e todos os subdomínios, a correspondência não diferencia maiúsculas de minúsculas, e pontos finais são removidos. Os arquivos de blocklist e allowlist são relidos a cada cinco segundos, então você pode editá-los ao vivo. O tráfego de localhost contorna o proxy via NO_PROXY e nunca aparece no log de auditoria. --proxy-timeout <SECONDS> limita a leitura de requisições e cabeçalhos (padrão 60) e não derruba túneis CONNECT estabelecidos, que podem ficar ociosos por até uma hora.
Cada flag de proxy, detalhe de filtragem de domínio, encadeamento de proxy corporativo upstream, e o formato do log de conexões: docs/proxy.md.
Habilite-as e o cplt intercepta gh e git através de scripts wrapper no $PATH:
Esta é a Camada 3, uma barreira suave. Ela impede que um agente complacente faça algo destrutivo por acidente. Para um limite rígido, apoie-se no sandbox do kernel e na proteção de branch do lado do servidor.
Com a guarda do gh ativada, o cplt também armazena em cache o token do GitHub na inicialização e o serve uma vez através do callback gh auth token, depois exclui o cache. Isso reduz vazamentos acidentais e baseados em ambiente. Não é um limite contra um agente hostil, porque o cache vive no próprio TMPDIR do agente e um agente que o leia antes do consumidor legítimo ainda obtém o token. SECURITY.md tem a declaração completa sobre block_auth_token.
Comportamento completo: docs/gh-guard.md · docs/git-guard.md
O sandbox bloqueia alguns fluxos de trabalho de propósito. Os mais comuns e suas correções:
O Playwright Chromium precisa de cplt config set sandbox.allow_cache_exec ms-playwright, e o Chromium deve rodar sem seu próprio sandbox aninhado. No macOS, seus helpers não conseguem inicializar um segundo sandbox Seatbelt dentro do cplt (forbidden-sandbox-reinit); no Linux, o filtro seccomp do cplt bloqueia as syscalls de namespace que o sandbox precisa. O Playwright como biblioteca já inicia com --no-sandbox, e esse mesmo opt-in define PLAYWRIGHT_MCP_SANDBOX=false para o Playwright MCP, que de outra forma o reativaria. Qualquer outro lançador de Chromium precisa de --no-sandbox por conta própria. O cplt continua sendo o limite de kernel aplicador, mas um renderer comprometido então recebe o perfil completo do Playwright do cplt em vez do perfil filho mais restrito do Chromium. Veja Cache exec e SECURITY.md.
O commit do Git funciona para todo agente; se o git push funciona via HTTPS depende do agente. Três pré-requisitos: use remotes HTTPS em vez de SSH (git remote set-url origin https://github.com/org/repo.git, ou reescreva globalmente com git config --global url."https://github.com/".insteadOf "[email protected]:"), execute gh auth login uma vez fora do sandbox, e execute gh auth setup-git se o credential helper ainda não estiver configurado. O push então executa gh auth git-credential, que precisa de um token que o gh consiga alcançar de dentro do sandbox — isso difere por agente, veja Git workflow. Pushes para o branch padrão e todos os force pushes são recusados pela guarda do git por padrão; faça push de um feature branch. O agent socket do SSH é bloqueado porque ele desbloqueia todas as chaves carregadas e pode autenticar em qualquer host, enquanto o credential helper do gh é restrito ao GitHub.
A JVM é proxy-aware, então um repositório Maven interno em um IP privado agora precisa ser permitido. O cplt injeta http(s).proxyHost/proxyPort em JAVA_TOOL_OPTIONS, então a resolução de dependências do Gradle e Maven passa pelo proxy CONNECT e aparece no log do proxy em vez de contorná-lo. A guarda SSRF do proxy então recusa um Nexus ou Artifactory interno que resolva para espaço de endereço privado, exatamente como já faz para curl, npm e pip. Adicione seu nome DNS a proxy.allow_private_domains. Uma URL de repositório escrita como IP literal puro (https://10.20.30.40/repository/maven-public/) não pode ser permitida por nenhuma chave — essa verificação roda antes da allow list ser consultada — então tal repositório precisa de um nome DNS. Forks de plugin WorkerExecutor, e um daemon Gradle iniciado fora do cplt e reutilizado dentro, não são proxied. Veja Internal Maven/Gradle repositories on private IPs.
O Gradle 9+ executa seu próprio sandbox aninhado, e o cplt o desativa. Desde o Gradle 8.8 o daemon se envolve em sandbox-exec (controlado por GRADLE_MACOS_SANDBOX, anteriormente a propriedade org.gradle.daemon.sandbox). O macOS não suporta chamadas sandbox-exec aninhadas, então o sandbox interno falha com "Operation not permitted" em operações de socket. O cplt injeta GRADLE_MACOS_SANDBOX=off, já que ele próprio fornece sandboxing em nível de kernel. Este é um problema upstream conhecido que afeta qualquer ferramenta que envolva o Gradle em um sandbox externo. Sobrescreva com --pass-env GRADLE_MACOS_SANDBOX se você realmente quiser o sandbox próprio do Gradle.
O Copilot CLI 1.0.83 executa seu próprio sandbox aninhado, e o cplt o desativa. No Linux, esse sandbox constrói um network namespace — slirp4netns, iptables, /dev/net/tun — e o filtro seccomp do cplt nega o unshare que ele usa. O cplt também define HTTP_PROXY/HTTPS_PROXY, o que na 1.0.83 coloca um sandbox Linux no caminho de saída do proxy, quer você tenha pedido ou não, então os dois colidem em toda inicialização. Sintoma: [cplt] Starting Copilot in sandbox... e depois nada. O cplt injeta o próprio opt-out do Copilot, COPILOT_CLI_SANDBOX_SUPPORT_OVERRIDE=unsupported; o Copilot se retira para a sessão, avisa isso, e deixa seu sandbox.enabled salvo intacto. O cplt é o limite, como é para Gradle e Chromium. Sobrescreva com --pass-env COPILOT_CLI_SANDBOX_SUPPORT_OVERRIDE. Uma política gerenciada empresarial que exija o sandbox sobrescreve tudo isso — veja Copilot CLI's own command sandbox.
Cada impacto, com as tabelas por ferramenta, notas sobre daemon JVM e Kotlin, solução de problemas de GPG, e as diferenças de plataforma de registro privado: docs/known-impacts.md.
sandbox-exec está obsoleto. A Apple não o removeu, mas pode removê-lo em uma versão futura do macOS.lsopen do SBPL também não tem filtro, então --allow-browser é todo o Launch Services ou nada dele. Com ele ativado, o agente pode lançar qualquer aplicação fora do sandbox, e nenhum wrapper pode restringir isso — veja docs/security.md..env dentro do diretório do projeto não é aplicada pelo kernel. Escritas em .git/hooks são bloqueadas quando o Bubblewrap está ativo.--deny-path requer Bubblewrap. É aplicado através de máscaras de mount quando o bwrap está ativo. Sem ele, o Landlock é apenas allowlist e o cplt avisa sobre o deny em vez de aplicá-lo.Mais: docs/security.md
Contribuições são bem-vindas.```bash git clone https://github.com/navikt/cplt.git && cd cplt git config core.hooksPath hack # enables pre-commit fmt + clippy checks mise run check # runs fmt, clippy, and tests
Abra uma issue antes de iniciar uma grande alteração. Todo PR precisa passar pelo CI (fmt, clippy, testes).
## Referências
- [SECURITY.md](https://github.com/navikt/cplt/blob/main/SECURITY.md), o modelo de segurança completo, análise de ameaças, estratégia de testes e trabalhos anteriores
- [Apple sandbox-exec(1)](https://keith.github.io/xcode-man-pages/sandbox-exec.1.html)
- [Chromium Seatbelt V2 Design](https://chromium.googlesource.com/chromium/src/sandbox/+show/refs/heads/main/mac/seatbelt_sandbox_design.md)
- [Documentação do Landlock LSM](https://docs.kernel.org/userspace-api/landlock.html)
- [Documentação do seccomp-BPF](https://www.kernel.org/doc/html/latest/userspace-api/seccomp_filter.html)
- [OWASP SSRF Prevention Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Server_Side_Request_Forgery_Prevention_Cheat_Sheet.html)
- [michaelneale/agent-seatbelt-sandbox](https://github.com/michaelneale/agent-seatbelt-sandbox)
## Licença
[MIT](https://github.com/navikt/cplt/blob/main/LICENSE)
| Flag | O que faz |
|---|
--preset strict | Bloqueio total de rede. Todos os cinco toggles desligados, mais gh_guard, git_guard, proxy.forced (egress via proxy forçado) e proxy.default_allowlist (allowlist de domínios fail-closed) ligados. Válvula de escape: --allow-all-domains desabilita apenas a allowlist |
--preset standard | Os padrões atuais. Todos os cinco desligados, o diretório scratch permanece ligado. O mesmo que não passar preset |
--preset permissive | Liga allow_localhost_any, allow_tmp_exec e allow_lifecycle_scripts |
--preset full-trust | ⚠️ Perigoso. Liga todos os cinco, adicionando allow_env_files e allow_docker |
| Flag | O que faz |
|---|
-d, --project-dir <DIR> | Em qual diretório o Copilot pode trabalhar. Padrão é a raiz do repositório git atual |
--allow-read <PATH> | Permite ao Copilot ler arquivos fora do projeto, somente leitura. Repetível |
--allow-write <PATH> | Permite ao Copilot ler e escrever fora do projeto. Use com cuidado. Repetível. A árvore é gravável mas não executável — uma árvore que é ambas é um caminho de drop de binários, então um allow.write sobre ~/.cargo também impede ~/.cargo/bin de executar. Use --allow-exec em uma árvore separada e não sobreposta quando precisar de ambos |
--allow-exec <PATH> | ⚠️ Perigoso. Permite ao agente executar binários de uma árvore fora dos diretórios de ferramentas padrão — um Homebrew ou prefixo de toolchain realocado, por exemplo. Concede leitura e execução, nunca escrita. Repetível. Recusado para uma raiz insegura (/, /tmp, $HOME e seus pais, os diretórios de sistema da plataforma) e para qualquer árvore que se sobreponha a uma gravável — o diretório do projeto, uma concessão --allow-write, um diretório de ferramentas gravável como ~/.cache, um diretório de dados de agente gravável (~/.claude, ~/.local/share/opencode, ~/.pi/agent e similares), o .git real de um worktree ou repo bare, ou uma árvore que os backends tornam gravável sem nenhuma concessão (/tmp e /dev/shm no Linux; /private/tmp e /private/var/folders no macOS): gravável mais executável é um caminho de drop de binários, e nenhum backend consegue subtrair a concessão de escrita da concessão de execução |
--allow-socket <PATH> | ⚠️ Perigoso. Permite um caminho de socket de domínio Unix, por exemplo um daemon LSP customizado ou um socket de banco de dados. Repetível. O que estiver do outro lado roda fora do sandbox, então apontar isso para docker.sock ou um socket de agente é equivalente a --allow-docker, e a única proteção é que sobreposições com --deny-path são rejeitadas. No Linux não faz nada abaixo do kernel 7.1, já que conexões de socket unix não são controladas pelo Landlock antes do ABI v9 (veja Limitações do Linux) |
--deny-path <PATH> | Bloqueia um caminho que de outra forma seria permitido. Deny sempre vence. Repetível |
--allow-port <PORT> | Permite tráfego de saída em uma porta extra. Apenas 443 por padrão. Repetível. No macOS a regra é (remote ip "*:PORT"), que é agnóstica de família e portanto carrega UDP além de TCP; o Landlock controla apenas conexões TCP. Sob proxy.forced a porta não abre nenhum socket direto — é alcançável através do proxy, então ferramentas cientes de proxy continuam funcionando |
--allow-localhost <PORT> | Permite saída para localhost em uma porta. Localhost é bloqueado por padrão. Use para servidores MCP ou servidores de desenvolvimento. Repetível |
--allow-localhost-any | Permite saída para localhost em todas as portas. Necessário para ferramentas de build como Turbopack (Next.js) e Vite que usam portas efêmeras aleatórias para IPC |
| Categoria | Exemplos | Como |
|---|
| Sistema central | HOME, USER, PATH, SHELL, TMPDIR, LANG | Allowlist explícita |
| Terminal | TERM, COLORTERM, TERM_PROGRAM | Allowlist explícita |
| Editor | EDITOR, VISUAL, PAGER | Allowlist explícita |
| Tokens de autenticação | GH_TOKEN, GITHUB_TOKEN, COPILOT_GITHUB_TOKEN | Passados apenas se você já os definiu. O gh guard usa um arquivo de uso único em vez disso |
| Config do Copilot | COPILOT_DEBUG, COPILOT_* | Allowlist de prefixo |
| Runtimes de linguagem | NODE_*, GOPATH, CARGO_HOME, JAVA_HOME, VIRTUAL_ENV, PYTHONPATH | Allowlist explícita |
| Gerenciadores de ferramentas | NVM_*, FNM_*, PYENV_*, MISE_*, SDKMAN_*, COREPACK_*, YARN_* | Allowlist de prefixo |
| OpenTelemetry | OTEL_EXPORTER_OTLP_ENDPOINT, OTEL_SERVICE_NAME, OTEL_RESOURCE_ATTRIBUTES, OTEL_* | Allowlist de prefixo (OTEL_EXPORTER_OTLP_HEADERS pode carregar autenticação opt-in) |
| Diretórios XDG | XDG_CONFIG_HOME, XDG_DATA_HOME, XDG_STATE_HOME, XDG_CACHE_HOME | Allowlist explícita |
NO_COLORFORCE_COLORSSH_AUTH_SOCKSSH_AGENT_PID| Flag | O que faz |
|---|
--allow-lifecycle-scripts | Permite que scripts de ciclo de vida do npm/yarn/pnpm (hooks postinstall) executem. Bloqueado por padrão. Use quando npm install precisar deles |
--allow-gpg-signing | Permite assinatura de commit e tag GPG dentro do sandbox. Concede acesso somente leitura ao chaveiro público e ao socket do agente GPG. Chaves privadas permanecem negadas. Veja Assinatura GPG |
--allow-jvm-attach | Permite sockets unix da JVM Attach API em /tmp. Necessário para MockK inline mocking, agentes inline do Mockito, ByteBuddy. Veja JVM Attach API |
--allow-msbuild | Permite sockets unix de worker-node do MSBuild em /tmp. Necessário para dotnet build. Não habilita o MSBuild Server persistente. Veja IPC de worker-node do MSBuild |
--no-scratch-dir | Desabilita o diretório scratch por sessão, que é ligado por padrão. TMPDIR não será redirecionado |
--scratch-dir | Habilita explicitamente o diretório scratch por sessão. Já é o padrão, então isso serve para sobrescrever scratch_dir = false no config |
--brief | 🧪 Experimental. Escreve o brief de sandbox voltado ao agente no diretório scratch (CPLT_BRIEF.md). Desligado por padrão. Também sandbox.brief = true no config. Instável, então pode mudar ou ser removido em uma versão futura |
--no-brief | Desliga o brief de sandbox para esta execução, sobrescrevendo sandbox.brief = true no config. Também suprime o bloco AGENTS.md, que é condicionado ao brief |
--agents-md | 🧪 Experimental. Com --brief, também escreve o bloco gerenciado do cplt no AGENTS.md do projeto. Desligado por padrão. Também sandbox.agents_md = true no config. Sem efeito sem --brief. Instável, então pode mudar ou ser removido em uma versão futura |
--no-agents-md | Desliga o bloco AGENTS.md para esta execução, sobrescrevendo sandbox.agents_md = true no config. Deixa o brief do diretório scratch intacto |
--allow-tmp-exec | ⚠️ Perigoso. Permite execução a partir de diretórios temporários do sistema (/private/tmp, /private/var/folders). Prefira o diretório scratch |
--allow-cache-exec <SUBDIR> | Permite execução a partir de um ~/Library/Caches/<SUBDIR>. Repetível. Para ferramentas que armazenam binários compilados ali, como Playwright e pnpm dlx |
--allow-cache-exec-any | ⚠️ Perigoso. Permite execução a partir de todo o ~/Library/Caches. Prefira --allow-cache-exec <SUBDIR> |
--allow-browser | ⚠️ Perigoso. Com isso ligado, o agente pode iniciar qualquer aplicação na sua máquina fora do sandbox. A concessão é o Launch Services, não um navegador: o launchd inicia o alvo fora do perfil Seatbelt, então open -a Terminal /tmp/x.sh roda sem sandbox. Isso não pode ser restrito a URLs — o lsopen do SBPL não aceita filtro, e a concessão é alcançável através de LSOpenCFURLRef() sem o binário open de forma alguma, então nenhum wrapper pode restringi-la (#251, e docs/security.md). Só ligue enquanto um prompt de login estiver realmente na tela (OAuth de servidor MCP, re-autenticação), depois desligue novamente. Desligado por padrão |
--deny-clipboard | Bloqueia o agente de ler ou escrever a área de transferência do macOS (pbpaste/pbcopy) negando o serviço Mach com.apple.pasteboard. Todos os outros serviços Mach (Keychain, DNS, Security framework) não são afetados. Ligado por padrão — esta flag reafirma o padrão |
--allow-clipboard | Devolve ao agente a área de transferência do macOS, que o cplt nega por padrão. Equivalente a sandbox.deny_clipboard = false |
--use-bubblewrap | Apenas Linux. Exige a camada de namespace do bubblewrap (namespaces PID, mount, IPC, UTS, cgroup, user mais um /tmp privado) sobre o Landlock e seccomp. Erra se bwrap estiver ausente. Auto-detectado quando nenhuma das flags é fornecida |
--no-bubblewrap | Apenas Linux. Nunca usa bubblewrap, mesmo quando instalado. Recorre ao Landlock e seccomp. Use quando o bwrap quebra uma ferramenta específica |
| Runtime | Diretórios home | Variáveis de ambiente / prefixos | Descoberta |
|---|
| Node.js | .nvm, .local/share/fnm, .local/bin | NODE_*, NPM_*, NVM_*, FNM_* | node |
| Rust | .cargo, .rustup | CARGO_HOME, RUSTUP_HOME | cargo |
| Go | go/bin, go/pkg | GOPATH, GOROOT, GOCACHE, etc. | go |
| Java/Kotlin (JVM) | .sdkman, .jenv, .gradle, .m2 | JAVA_HOME, JAVA_TOOL_OPTIONS, GRADLE_*, MAVEN_*, SDKMAN_*, JENV_* | java, gradle |
| Kotlin Native | .konan | nenhum | nenhum |
| Python | .pyenv | VIRTUAL_ENV, PYTHONPATH, PYENV_ROOT, PYENV_* | python3 |
| Yarn Berry | .yarn | YARN_* (hardening sobrescreve YARN_ENABLE_SCRIPTS) | yarn |
| pnpm | Library/pnpm, .local/share/pnpm | PNPM_HOME | pnpm |
| Corepack | nenhum | COREPACK_* | nenhum |
| mise | .local/share/mise, .mise | MISE_* | mise |
| Flag | O que faz |
|---|
--doctor | Obsoleto. Use o subcomando cplt doctor em vez disso |
--print-profile | Imprime o perfil de sandbox gerado (SBPL) e sai |
--show-denials | Transmite logs de negação do sandbox do macOS em tempo real |
--no-validate | Pula a verificação de inicialização que confirma que as restrições de sandbox estão ativas |
-y, --yes | Pula o prompt de confirmação interativo. O resumo da configuração ainda é impresso, para auditabilidade. Necessário quando stdin não é um TTY, então CI e scripts precisam dele |
-q, --quiet | Suprime o banner de inicialização e mensagens não essenciais. Erros e avisos ainda são impressos. Também sandbox.quiet = true no config |
--no-quiet | Sobrescreve sandbox.quiet = true e mostra o resumo de inicialização de qualquer forma |
--no-audit | Pula o relatório de mudanças pós-sessão. cplt normalmente compara a árvore de trabalho com um commit de linha de base fixado antes da execução e lista o que a sessão tocou, sinalizando caminhos sensíveis. -q também o suprime |
--init-config | Cria um arquivo de config inicial em ~/.config/cplt/config.toml e sai |
| Flag | O que faz |
|---|
--resume[=SESSION] | Retoma uma sessão anterior. --resume sozinho escolhe interativamente, --resume=NAME escolhe por nome ou ID |
--continue | Retoma a sessão mais recente no diretório atual |
--remote | Habilita controle remoto, para que você possa monitorar e conduzir a sessão do GitHub.com ou mobile |
--name SESSION | Nomeia a sessão para que --resume=NAME possa encontrá-la depois |
| Flag cplt | Copilot | OpenCode | Antigravity (agy) | Claude Code |
|---|
--continue | --continue | --continue | --continue | --continue |
--resume | --resume | --continue¹ | --continue¹ | --resume |
--resume=ID | --resume=ID | --session ID | --conversation ID | --resume ID |
--remote | --remote | ignorado | ignorado | ignorado |
--name NAME | --name NAME | ignorado | ignorado | ignorado |
GOOGLE_API_KEYGEMINI_API_KEY--pass-env--observe-domains, então sua allowlist embutida é apenas a base compartilhada de registros de pacotes. Adicione o domínio do seu provedor via allowed_domains antes de habilitar --default-allowlistGOOSE_DISABLE_KEYRING=1 faz o goose usar um secrets.yaml em seu diretório de config, e passar a chave com --pass-env evita segredos armazenados por completo. No Linux o goose usa o D-Bus Secret Service, que a concessão do Keychain não afeta~/.config/goose/config.yaml declara entradas extensions: cujo cmd o goose executa a cada início de sessão, então um diretório de config gravável é um vetor de persistência no host. Sessões normais não o gravam; mudanças de /mode e permissões de ferramentas persistidas não sobrevivem a uma execução em sandbox. Reconfigure com goose configure fora do cplt~/.local/share/goose/) e de estado (~/.local/state/goose/) do goose são graváveis, com exec negado. O goose usa esses caminhos XDG no macOS também, e honra as sobrescritas XDG_* lá--continue e --resume sem argumento mapeiam para goose session --resume; --resume=ID para goose session --resume --session-id ID; --name X para goose session --name X. Essas são flags de subcomando, então o cplt injeta o subcomando session com elas. --remote é ignorado (sem equivalente no goose)| Camada | Imposição | Contornável? | O que protege |
|---|
| 1. Sandbox do kernel | macOS Seatbelt / Linux Landlock+seccomp | ❌ Não | Acesso a arquivos, exec, portas de rede |
| 2. Proxy de rede | Proxy CONNECT, filtragem de domínio | ❌ Não (dentro da sandbox) | Conexões de saída, exfiltração |
| 3. Guarda de comandos | Scripts wrapper baseados em PATH | ⚠️ Barreira suave | Pushes, merges, releases, escritas de API |
gitbwrapsandbox-execmiseghPATHcplt doctor: suas sondagens --version executam cada binário de agente que encontra no seu PATH, no processo pai, então um binário plantado é executado ali — a mesma exposição de caminho descoberto do lançamento acima, razão pela qual o doctor é um relatório e não um limite. Sua verificação de gh é resolvida a partir dos diretórios confiáveis e sua leitura da versão do kernel não gera nenhum processo| Comando | Ação |
|---|
gh pr merge, gh repo delete, gh release create | 🔒 Bloqueado |
git push origin main, git push --force | 🔒 Bloqueado |
gh api (escrita em outros repositórios) | 🔒 Verificado por escopo |
gh pr list, gh issue list, git commit | ✅ Permitido |
git push origin feature-branch | ✅ Permitido com protect_default_branch_only |
| Impacto | Correção |
|---|
Arquivos .env bloqueados | cplt config set sandbox.allow_env_files true |
| Hooks de postinstall do npm bloqueados | cplt config set sandbox.allow_lifecycle_scripts true |
go test / mise run bloqueados (execução temporária) | O diretório scratch está ativado por padrão. Se você ainda precisar, cplt config set sandbox.allow_tmp_exec true |
| Conexões de localhost bloqueadas | cplt config set allow.localhost 3000, ou cplt config set sandbox.allow_localhost_any true |
| Docker bloqueado | cplt config set sandbox.allow_docker true ⚠️ |
| SSH bloqueado | Use remotes HTTPS em vez disso |
| Assinatura GPG desabilitada | cplt config set sandbox.allow_gpg_signing true |
| JVM MockK/Mockito falha | cplt config set sandbox.allow_jvm_attach true |
Nós worker do MSBuild do dotnet build bloqueados | cplt config set sandbox.allow_msbuild true |
| Credenciais de registro privado bloqueadas | cplt config set allow.read "~/.m2/settings.xml" |
| Repositório Maven/Nexus interno inacessível (Gradle/Maven) | cplt config set proxy.allow_private_domains "intern.example.com". Uma URL de repositório com IP literal não pode ser permitida — dê um nome DNS ao host; veja abaixo |
| Playwright Chromium não inicia | Permita a execução do cache, depois desabilite o sandbox aninhado do Chromium; veja abaixo |