
Sandbox para agentes de codificación de IA. Ejecuta Copilot CLI, Claude Code, OpenCode, Gemini CLI, Antigravity, Pi, goose o un shell simple dentro de un sandbox a nivel de kernel, con protecciones de git y gh y una política de sandbox comprometida con el repositorio.
Sandbox aplicado por el kernel para agentes de codificación con IA. cplt envuelve GitHub Copilot CLI, OpenCode, Gemini CLI, Antigravity CLI, Pi, Claude Code, goose, DeepSeek Harness, o cualquier shell, para que el agente pueda escribir código pero no pueda robar credenciales, hacer push a main, fusionar PRs o exfiltrar secretos.
sandbox-exec
Los agentes de IA ejecutan código arbitrario. Un agente comprometido, ya sea mediante inyección de prompts, un ataque a la cadena de suministro o un servidor MCP malicioso, puede leer ~/.ssh, hacer push a main, fusionar PRs o exfiltrar tu código, a menos que el propio sistema operativo lo impida.
cplt te ofrece aplicación a nivel de kernel con políticas configurables por equipo:
.cplt.toml, versionada en el control de código fuente, por lo que es a prueba de manipulaciones y auditableDocumentación detallada: Configuración · Proxy y filtrado de dominios · Guarda del comando gh · Guarda del comando git · Impactos conocidos · Detalles de seguridad · Modelo de seguridad
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
Otros agentes y 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
## Qué bloquea
El sandbox bloquea el acceso a credenciales y secretos en el kernel. Los guardas de comandos bloquean operaciones destructivas. Cada restricción se aplica al agente y a cada proceso que este genera.
| Recurso | Estado | Notas |
| --- | --- | --- |
| Lectura/escritura del directorio del proyecto | ✅ Permitido | |
| Lectura/escritura/borrado de `.env*`, `.pem`, `.key` en el proyecto | 🔒 Bloqueado por el kernel | Evita la exfiltración y destrucción de secretos. `--allow-env-files` lo anula |
| Escritura de `.git/hooks`, `.git/config`, `.gitmodules` | 🔒 Bloqueado por el kernel (macOS), ⚠️ parcial en Linux | Evita la persistencia mediante hooks de git, redirección de hooksPath, secuestro de submódulos. **Linux:** Landlock no puede denegar una subruta dentro de un árbol permitido, por lo que estos permanecen escribibles en la ruta solo-Landlock. `bwrap` vuelve a montar `.git/hooks` como solo lectura, pero deja deliberadamente `.git/config` y `.gitmodules` escribibles, por lo que `core.hooksPath` sigue siendo una vía de persistencia, véase [Limitaciones de Linux](https://github.com/navikt/cplt/blob/main/docs/security.md#linux). Se aplica a **cada** raíz escribible, el proyecto y cada concesión de `allow.write`, incluido un worktree o repo bare concedido cuyos hooks reales viven fuera de `<root>/.git` |
| Ejecución desde `/tmp`, `/var/folders` | 🔒 Bloqueado por el kernel | Evita el write-then-exec. El directorio scratch redirige TMPDIR a una ubicación segura, activado por defecto |
| Escritura en directorios bin/shim resueltos por PATH (`~/.bun/bin`, `~/.deno/bin`, `$PNPM_HOME`, `shims/` de mise y todo `installs/`) | 🔒 Bloqueado por el kernel (macOS), ⚠️ mise parcial en Linux | Evita troyanizar un binario que tu siguiente comando *sin sandbox* resuelva a través del PATH. La misma razón por la que `~/.cargo/bin` y `~/go/bin` siempre han sido de solo lectura. Rompe `bun install -g`, `deno install`, `pnpm add -g`, `mise install`, `mise upgrade`, `mise use -g` dentro de cplt, deliberadamente, y un repo que fija un toolchain no instalado ya no se autoinstala. Las instalaciones locales al proyecto no se ven afectadas. **Linux:** las dos de mise van sobre la capa de solo lectura de `bwrap`; el resto se mantienen de forma nativa. Véase [Instalaciones globales de herramientas](https://github.com/navikt/cplt/blob/main/docs/known-impacts.md#global-tool-installs) |
| Ejecución desde `~/Library/Caches` | 🔒 Bloqueado por el kernel por defecto | Evita el staging de binary-drop. Los módulos nativos de Copilot están exentos mediante una carve-out. Añade exenciones específicas con `--allow-cache-exec <SUBDIR>`, p. ej. `ms-playwright` |
| Modificación de `.vscode/tasks.json`, `launch.json` | ⚠️ Permitido, riesgo conocido | Límite de confianza del IDE. Véase [SECURITY.md](https://github.com/navikt/cplt/blob/main/SECURITY.md) para las mitigaciones |
| Lectura/escritura de `~/.copilot` (auth, settings) | ✅ Permitido | Incluye `file-map-executable` para `keytar.node`, `pty.node`, `computer.node` |
| Escritura de `~/.copilot/pkg` (módulos nativos) | 🔒 Bloqueado por el kernel | Evita la persistencia mediante reemplazo de módulos nativos |
| Variables de entorno | 🔒 Saneadas + endurecidas | Solo pasa una allowlist segura. Scripts de ciclo de vida bloqueados. `--pass-env VAR` añade una de vuelta |
| Lectura de `~/.config/gh/hosts.yml` + `config.yml` | ✅ Permitido (solo lectura) | Solo estos dos archivos. El resto de `.config/gh` está bloqueado |
| Lectura de `~/.config/mise` | ✅ Permitido (solo lectura) | Versiones de herramientas y PATH, sin secretos |
| Lectura de `~/.gitconfig`, `~/.config/git/config` | ✅ Permitido (solo lectura) | Se sigue un symlink de dotfiles hasta su destino, por lo que un `~/.gitconfig` stowed funciona |
| Lectura de `~/.git-credentials` | 🔒 Bloqueado por el kernel | `credential.helper = store` mantiene tokens en texto claro aquí. Ningún `--allow-read` lo reabre, igual que `~/.netrc`. **Linux:** una concesión sobre un *ancestro* (`$HOME` mismo) aún lo expone, porque Landlock no puede denegar una subruta dentro de un árbol permitido |
| Lectura de hooks globales de git (`core.hooksPath`) | ✅ Permitido (solo lectura, escritura denegada) | Autodetectado. Debe estar bajo `$HOME` con profundidad ≥3. Las escrituras se bloquean explícitamente |
| Firma de commit/tag (`commit.gpgsign`, `tag.gpgsign`) | 🔒 Deshabilitado | Las claves privadas en `~/.ssh` y `~/.gnupg` están bloqueadas, por lo que la firma se deshabilita mediante una anulación de variable de entorno |
| Lectura de `~/Library/Application Support/Microsoft` | ✅ Permitido (solo lectura) | Device ID para telemetría |
| Acceso al Keychain de macOS | ⚠️ Permitido (lectura+escritura) para agentes que almacenan auth allí | La concesión no puede limitarse a un solo elemento, por lo que alcanza cada entrada del keychain que el agente pueda desbloquear. Opta por `sandbox.keychain_substitute` (EXPERIMENTAL, desactivado por defecto) para eliminarlo en ejecuciones donde el agente puede autenticarse sin él — `CLAUDE_CODE_OAUTH_TOKEN` para Claude Code, un archivo de token de respaldo existente para Antigravity. Véase [SECURITY.md](https://github.com/navikt/cplt/blob/main/SECURITY.md#keychain-access-is-all-or-nothing) |
| Red saliente (puerto 443) | ✅ Permitido | Todos los demás puertos están bloqueados. Añade extras con `--allow-port` |
| Salida a localhost | 🔒 Bloqueado por el kernel (macOS), ⚠️ basado en puertos en Linux | Evita el acceso a servicios locales. La entrada sigue funcionando para el proxy. **Linux:** las reglas de Landlock son solo números de puerto y no pueden distinguir `localhost:443` de `remote:443`, por lo que un servicio local en un puerto permitido es alcanzable y no hay una denegación específica para localhost. Usa `--with-proxy` para protección SSRF, véase [Limitaciones de Linux](https://github.com/navikt/cplt/blob/main/docs/security.md#linux) |
| Agente SSH (unix socket) | 🔒 Bloqueado por el kernel (macOS), ⚠️ solo env en Linux | Evita firmar operaciones de git o SSH a hosts. **Linux:** el `connect()` de unix socket no está controlado, por lo que el `SSH_AUTH_SOCK` retenido es la única barrera y un agente que lo establezca por sí mismo puede usar las claves cargadas. `bwrap` oculta el socket estándar de OpenSSH bajo `/tmp`, pero no un agente de gnome-keyring/gcr o systemd bajo `$XDG_RUNTIME_DIR`. Véase [Limitaciones de Linux](https://github.com/navikt/cplt/blob/main/docs/security.md#linux) |
| Herramientas de desarrollo (`~/.cargo`, `~/.gradle`, `~/.m2`, `~/.sdkman`, `~/.jenv`, `~/.pyenv`, `~/.konan`, etc.) | ✅ Permitido (lectura+escritura para cachés) | Solo directorios que existen en disco. Ajustado en tiempo de ejecución por lo que detecta `cplt doctor` |
| Archivos de credenciales de registros (`~/.m2/settings.xml`, `~/.gradle/gradle.properties`, `~/.cargo/credentials`) | 🔒 Bloqueado por el kernel en macOS. En Linux el directorio padre de la herramienta permanece legible | Anula con `--allow-read`. Véase [Registros privados](https://github.com/navikt/cplt/blob/main/docs/known-impacts.md#private-registries) |
| Lectura de `~/.npmrc` | 🔒 Bloqueado por el kernel (ambas plataformas) | Anula con `--allow-read`. Rompe yarn 1, véase [yarn 1](https://github.com/navikt/cplt/blob/main/docs/known-impacts.md#yarn-1-and-unreadable-home-rc-files) |
| Código fuente de Go (`~/go/src`) | 🔒 Bloqueado por el kernel | Solo `~/go/bin` y `~/go/pkg` son legibles |
| Lectura de `~/.ssh`, `~/.gnupg`, `~/.aws`, `~/.azure` | 🔒 Bloqueado por el kernel | |
| Lectura de `~/.kube`, `~/.docker`, `~/.nais` | 🔒 Bloqueado por el kernel | |
| Lectura de `~/.password-store`, `~/.terraform.d` | 🔒 Bloqueado por el kernel | |
| Lectura de `~/.config/gcloud`, `~/.config/op` | 🔒 Bloqueado por el kernel | Archivos individuales se pueden anular con `--allow-read`. Véase [Credenciales de la nube](https://github.com/navikt/cplt/blob/main/docs/known-impacts.md#cloud-credential-directories) |
| Lectura o escritura de `~/.config/cplt`, `~/.nav-pilot` | 🔒 Bloqueado por el kernel | Estado de la herramienta que decide qué puede hacer el *siguiente* lanzamiento. `~/.config/cplt` no se puede anular como subárbol completo; dentro de `~/.nav-pilot`, una ruta con nombre sigue siendo concedible para que se pueda leer un payload de agentpakke fijado |
| Lectura de `~/.netrc`, `~/.pypirc`, `~/.vault-token` | 🔒 Bloqueado por el kernel | No anulable en ambas plataformas. Nombrar uno en `allow.read` es un error de arranque |
| Lectura de `~/.gem/credentials` | 🔒 Bloqueado por el kernel | No anulable en ambas plataformas. Nombrar uno en `allow.read` es un error de arranque |
| Operaciones destructivas de la CLI `gh` (merge, delete, release) | 🔒 Controlado por comando (activado por defecto) | Opta por salir con `--no-gh-guard`. Véase [gh guard](https://github.com/navikt/cplt/blob/main/docs/gh-guard.md) |
| `git push` a la rama por defecto | 🔒 Controlado por comando (activado por defecto) | Bloquea pushes a `main`/`master`; los pushes a ramas de características siguen funcionando. `protect_default_branch_only = false` bloquea todos los pushes, `git_guard.mode = "warn"` solo advierte, `--no-git-guard` opta por salir |
| Herencia de procesos hijos | ✅ Todas las restricciones se aplican a los subprocesos | |
Esa tabla es un resumen. El sandbox también permite el acceso a archivos del sistema (certificados SSL, `/etc/hosts`), directorios temporales (lectura y escritura, sin ejecución) y rutas de herramientas del sistema (`/usr/bin`, `/opt/homebrew`). Ejecuta `cplt --print-profile` para ver las reglas SBPL completas.
Para el modelo de seguridad completo, el análisis de amenazas y la estrategia de pruebas, lee [SECURITY.md](https://github.com/navikt/cplt/blob/main/SECURITY.md).
## Cómo se compara cplt
### El sandbox de Codex CLI
| Área | cplt | Sandbox de Codex CLI |
| --- | --- | --- |
| Control de red saliente | Proxy CONNECT con listas de permitidos/bloqueados por dominio | Sin filtrado a nivel de dominio |
| Manejo del entorno | Allowlist más inyección de entorno endurecida | Modelo de paso directo más básico |
| Protección de archivos secretos | Patrones de denegación como `.env*`, `.pem`, `.key` dentro del repo | Acceso principalmente limitado por directorio |
| Política del repo | [`.cplt.toml`](https://github.com/navikt/cplt/blob/main/docs/configuration.md#per-repo-configuration-cplttoml) con un flujo explícito de confianza/aprobación | Sin archivo de política a nivel de repo |
| Soporte de agentes | Copilot, OpenCode, Gemini CLI, Antigravity CLI, Pi, Claude Code, goose, DeepSeek Harness, o shell | Solo Codex |
cplt no es más fuerte en todo. Codex CLI tiene hoy aislamiento de namespaces de Linux, y ya expone modos de sandbox explícitos como read-only y workspace-write. cplt aún no tiene esa matriz de modos.
### Sandboxes basados en Docker
| Área | cplt | Sandbox basado en Docker |
| --- | --- | --- |
| Tiempo de arranque | Prácticamente instantáneo para uso normal de CLI | Arranque de contenedor normalmente más lento |
| Control de red | Filtrado saliente por petición mediante proxy | Acceso de red normalmente todo-o-nada |
| Controles de archivos | Reglas por ruta y por patrón | Controles por montaje |
| Requisitos del host | Un solo binario | Requiere el daemon de Docker |
| Encaje en portátil corporativo | Funciona donde Docker no está disponible o está restringido | A menudo bloqueado por la política local |
Docker sigue dándote un aislamiento más fuerte en algunos entornos, especialmente si quieres un sistema de archivos y un namespace de procesos totalmente separados. cplt cambia eso por una configuración más ligera y una integración más estrecha con la máquina en la que ya desarrollas.
### Permisos del modo agente de VS Code
Herramientas como el modo agente de VS Code se basan principalmente en permisos de la UI. cplt aplica sus restricciones en el kernel, por lo que el agente no puede sortearlas con un prompt o una instrucción modificada. Eso importa sobre todo para agentes de CLI y exposición de credenciales:
- cplt funciona fuera del IDE
- las variables de entorno se filtran antes de que arranque el agente
- los archivos sensibles se pueden bloquear incluso cuando viven dentro del repo
- las mismas restricciones se aplican a los procesos hijos
### El sandbox de Claude Code (Anthropic Sandbox Runtime)
[Anthropic Sandbox Runtime](https://github.com/anthropic-experimental/sandbox-runtime) (`srt`) es la capa de sandbox que usa Claude Code. Mismo enfoque de alto nivel que cplt, Seatbelt de macOS más aplicación a nivel de kernel en Linux más un proxy HTTP, implementación diferente.
| Área | cplt | Anthropic srt |
| --- | --- | --- |
| Lenguaje / entrega | Un solo binario Rust | Node.js + paquete npm + dependencias externas |
| Backend de Linux | Landlock LSM (sin dependencias, sin namespaces) | bubblewrap (contenedor vía namespaces de usuario) |
| Filtrado de entorno | Allowlist estricta + denegación por sufijo (`_TOKEN`, `_SECRET`) | Hereda el entorno completo del padre (los secretos pasan) |
| Protección de directorios de credenciales | 15+ directorios denegados por defecto | El usuario debe configurarlo manualmente |
| Protección contra DNS rebinding | ✅ IP post-DNS comprobada contra rangos privados | ❌ No implementado |
| Proxy de red | HTTP CONNECT + permitir/bloquear dominios | HTTP + SOCKS5 + MITM TLS experimental |
| Git por SSH | Bloqueado en el kernel en macOS (socket del agente denegado); en Linux solo se retiene `SSH_AUTH_SOCK` | Proxy vía SOCKS5 |
| Scripts de gestores de paquetes | Bloqueados por defecto (`npm_config_ignore_scripts`) | No bloqueados |
| Soporte de agentes | Copilot, OpenCode, Gemini, Antigravity, Pi, Claude Code, goose, DSH, Shell | Claude Code |
| Configuración | TOML (global + por repo) | JSON (solo global) + actualizaciones en vivo con `--control-fd` |
| API de librería | ❌ Solo binario | ✅ Librería TypeScript embebible |
cplt es más seguro de fábrica: filtrado de entorno, protección de credenciales, comprobaciones de DNS rebinding, bloqueo de scripts de ciclo de vida. srt es más flexible: SOCKS5, inspección TLS, callbacks por petición, embebido como librería. La elección del backend de Linux importa. bwrap necesita workarounds en Ubuntu 24.04+ por las restricciones de userns de AppArmor, mientras que Landlock requiere kernel 5.13 o superior pero no tiene dependencias externas.
### El propio sandbox de GitHub Copilot CLI
Copilot CLI se distribuye con un sandbox local desde junio de 2026, incluido en el
asiento estándar. Ejecuta comandos de shell a través de Microsoft MXC con acceso
restringido al sistema de archivos, la red y el sistema, en macOS, Linux y Windows.
`/sandbox enable` lo activa.
Si eso te cubre, úsalo. No cuesta nada extra, y funciona en Windows,
donde cplt no lo hace.
Hay dos cosas que no hace.
La política vive con el administrador, no con el repositorio. Las empresas configuran
la política del sandbox a través de Intune u otro MDM. Nada queda junto al código,
así que una regla que importa para un repositorio no puede seguir a un contribuidor,
a CI, o a un portátil que el MDM no gestiona. En cplt la política es
`.cplt.toml` en el repositorio. Los revisores ven los cambios en él en el pull
request, y el archivo puede endurecer la configuración propia de un desarrollador pero nunca
relajarla.
Confina el proceso, no lo que el proceso hace con las credenciales que
posee. Las pestañas de `/sandbox` cubren el sistema de archivos, la red y las
capacidades del sistema, y dentro de un repositorio Git al agente se le concede lectura y escritura
sobre `.git` por defecto. Un agente en sandbox sigue teniendo tu token de `gh` y tu
acceso de push. Hacer push de una rama, fusionar un pull request y eliminar un
repositorio son todas llamadas API bien formadas de un cliente autorizado, y una
regla de sistema de archivos o de red no tiene opinión al respecto. cplt envuelve `git` y
`gh` en su lugar. El agente hace commit, ramas y rebase libremente. `gh pr merge`,
`gh repo delete` y `gh release create` están bloqueados por defecto. También lo está
`git push` a `main`/`master`; los pushes a ramas de características siguen funcionando, porque
`protect_default_branch_only` está activado. Ponlo a `false` para bloquear todos los pushes, o
`git_guard.mode = "warn"` para solo advertir.
Ejecutar ambos es razonable. MXC confina el proceso. Los guardas deciden qué
puede hacer el agente con las credenciales que posee.
### Lagunas honestas
- macOS tiene hoy la aplicación a nivel de archivo más fuerte. La cobertura en Linux está mejorando pero no es idéntica.
- cplt aún no ofrece presets de política simples de solo lectura / workspace-write / acceso completo.
- Si quieres aislamiento completo de contenedor, cplt no intenta reemplazar a Docker.
## Instalación
### Homebrew (recomendado)```bash
brew install navikt/tap/cplt
mise use -g 'github:navikt/cplt@'
mise elige el recurso de la versión correcta para tu plataforma y verifica su
atestación de procedencia de compilación.
Fija la versión. Nuestras cadenas de versión no son semver comparables — llevan
ceros a la izquierda y dos guiones — por lo que `mise latest` puede resolverse a una versión
más antigua que la más reciente ([navikt/copilot#818](https://github.com/navikt/copilot/issues/818)).
### apt (Debian/Ubuntu, recomendado en Linux)
[navikt/apt](https://navikt.github.io/apt/) es un archivo firmado servido a través de
GitHub Pages, que contiene cplt y nav-pilot para amd64 y 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
Es un espejo de repositorio apt simple que replica nuestras versiones, no un paquete de distribución con su propio mantenedor. Su trabajo de publicación se ejecuta cada hora y extrae el .deb más reciente de la última versión de cada herramienta, por lo que una versión publicada hace unos minutos tarda hasta una hora en ser instalable de esa forma.
El paquete coloca el binario en /usr/bin/cplt, y las actualizaciones se realizan mediante sudo apt upgrade a partir de entonces. cplt update se niega a modificar una instalación de apt y en su lugar indica sudo apt upgrade: reemplazar el binario a espaldas de dpkg sería revertido por la siguiente ejecución de apt.
Sin el archivo, el mismo .deb es un recurso de la versión:```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 distribuciones que no son derivadas de Debian, y para CI:```bash
curl -fsSL https://raw.githubusercontent.com/navikt/cplt/main/install.sh | bash
Opciones:```bash
curl -fsSL ... | bash -s -- --version 2026.05.05-174753-75bae5b
curl -fsSL ... | bash -s -- --dir ~/.local/bin
curl -fsSL ... | bash -s -- --no-brew
### Descargar desde releases
Obtén la última compilación para tu plataforma desde [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/
Cada binario de la versión incluye una atestación de procedencia de compilación. Verifícala:```bash gh attestation verify cplt -o navikt
### Compilar desde el código fuente```bash
git clone https://github.com/navikt/cplt.git && cd cplt
cargo build --release
sudo cp target/release/cplt /usr/local/bin/
O con mise:```bash mise run install
`mise run install` y las compilaciones manuales colocan cplt en `/usr/local/bin/cplt`. Si también tienes la compilación de Homebrew en `/opt/homebrew/bin/cplt`, coloca `/usr/local/bin` primero en `PATH` para que tu compilación de desarrollo tenga prioridad:```bash
# Check which cplt is active
which cplt
# If it shows /opt/homebrew/bin/cplt, reorder your PATH:
export PATH="/usr/local/bin:$PATH"
O simplemente ejecuta /usr/local/bin/cplt explícitamente y omite por completo la resolución de PATH.
cplt no tiene backend de sandbox para Windows. El enforcement es Apple Seatbelt en macOS y Landlock LSM en Linux, por lo que no hay nada que ejecutar de forma nativa en Windows. La ruta soportada es WSL2, donde cplt es una instalación Linux ordinaria y el sandbox está aplicado por el kernel. Cada rama del kernel de Microsoft compila CONFIG_SECURITY_LANDLOCK=y y lista landlock primero en CONFIG_LSM (config-wsl), incluido desde el kernel 5.15.57.1, y la línea de comandos del kernel predeterminada de WSL no establece ninguna anulación de lsm=.
En PowerShell, una 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
Todo lo que aparece a continuación se ejecuta **dentro de la distro** (`wsl`, o el perfil de Ubuntu en Windows Terminal), no en 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
No instale Copilot CLI en el lado de Windows. Con la interoperabilidad activada (el valor predeterminado), el PATH de Windows se añade al de la distro, por lo que un npm install -g @github/copilot del lado de Windows aparece dentro de la distro como /mnt/c/Users/<user>/AppData/Roaming/npm/copilot. Esa es una instalación de Windows a la que se llega mediante la interoperabilidad. No puede ejecutarse en el sandbox de Linux, y el shim de npm ejecuta un node que la distro no tendrá a menos que también hayas instalado uno allí. El síntoma solía ser un error de extracción en tiempo de ejecución no relacionado. cplt ahora nombra la causa cuando resuelve un agente bajo /mnt/<drive>/ y se está ejecutando bajo WSL, y cplt doctor lo reporta como una comprobación fallida en lugar de aprobarla (#188). WSL se detecta a partir de estado propiedad del kernel, ya sea /run/WSL o el nombre del kernel en /proc/sys/kernel/osrelease y /proc/version, no a partir de WSL_DISTRO_NAME, que está ausente bajo sudo y en unidades systemd y que cualquier proceso puede establecer. En una máquina Linux normal, /mnt/c se deja en paz. Allí es un punto de montaje ordinario.
Esa comprobación tiene dos límites, ambos deliberados. Se basa en la raíz de automontaje predeterminada, por lo que si la has reubicado ([automount] root en /etc/wsl.conf), la instalación del lado de Windows no se reconoce y obtienes el fallo antiguo, menos útil, con la ruta incluida. Y desactivar la interoperabilidad impide que el PATH de Windows se filtre, pero no desmonta /mnt/c.
Kernel y ABI de Landlock. El WSL actual (2.7.x y posteriores) incluye Linux 6.18, que proporciona Landlock ABI 7 — todo lo que cplt usa excepto el derecho de connect() de socket unix, que necesita ABI 9 (kernel 7.1). Una instalación que siga en la línea del kernel 6.6 obtiene ABI 3: las reglas del sistema de archivos se aplican, pero las reglas de puertos TCP (ABI 4), la restricción de ioctl (ABI 5) y el alcance de señales/sockets abstractos (ABI 6) no están disponibles, y el filtrado de red recurre al proxy CONNECT. wsl --update te hace avanzar. cplt doctor imprime la versión del kernel y el ABI que encontró, que es la comprobación que importa en tu máquina.
No desactive Landlock en
.wslconfig. Un[wsl2] kernelCommandLinecon una listalsm=que omitalandlock, o un[wsl2] kernel=personalizado compilado sinCONFIG_SECURITY_LANDLOCK, elimina la aplicación a nivel de kernel de la que depende cplt, ycplt doctorreportará Landlock como no disponible.
Mantenga el proyecto en el sistema de archivos de Linux. Trabaje en ~/src/... dentro de la distro en lugar de /mnt/c/Users/.... La propia guía de Microsoft indica que el acceso a archivos entre sistemas operativos es notablemente más lento, y /mnt/c se sirve a través de 9p de forma predeterminada a partir de WSL 2.9.x (virtiofs es opcional mediante [wsl2] virtiofs=true). Más al grano, no hemos verificado cómo Landlock aplica las reglas en ese montaje. El kernel no documenta ninguna exclusión para sistemas de archivos respaldados por red o FUSE, solo pipes, sockets y nsfs, y la propia suite de pruebas de Landlock ejercita 9p y FUSE, por lo que esperamos que funcione. Nadie aquí lo ha confirmado. Trate un proyecto bajo /mnt/c como no probado en lugar de compatible.
Bubblewrap. Ubuntu 23.10+ bloquea los espacios de nombres de usuario sin privilegios mediante kernel.apparmor_restrict_unprivileged_userns, lo que rompe bwrap. Ese sysctl proviene de un parche del kernel de Ubuntu que está ausente en el kernel de Microsoft, por lo que se espera que la capa opcional de Bubblewrap funcione en Ubuntu bajo WSL2. Eso es una inferencia a partir del código fuente del kernel, no algo que hayamos ejecutado. Si bwrap falla allí, por favor indíquelo en #189. El propio filtro seccomp de cplt es un programa BPF simple de PR_SET_SECCOMP, que se apila sobre el filtro que WSL instala en cada proceso.
Aún no verificado en una instalación real de WSL2. Verificado a partir del código fuente: Landlock está compilado y es el primero en
CONFIG_LSMen el kernel de Microsoft; la detección de/mnt/<drive>/, las señales de WSL que utiliza y su texto de error; quecplt doctorfalla con un agente de ese tipo e imprime kernel + ABI de Landlock; los requisitos de 5.13+/6.7+; y queinstall.shinstala el binario de la versión de Linux. Aún no verificado por nadie aquí: cómo se comporta Landlock en/mnt/c, si Bubblewrap funciona bajo WSL2, las versiones exactas de los paquetes que incluye tu versión de la distro, y la secuencia anterior de principio a fin. Si lo ejecutas, por favor informa de lo que realmente ocurrió en #189.
De forma predeterminada, obtienes el sandbox escribiendo cplt. Para que copilot a secas también se ejecute en sandbox:```bash
cplt --shell-install
Eso detecta tu shell, añade el alias a tu archivo rc e imprime lo que hizo. Ejecútalo tantas veces como quieras, no añadirá duplicados.
`--agent` elige qué comando recibe el alias, y todos los agentes que cplt puede lanzar están disponibles:```bash
cplt --shell-install --agent opencode # 'opencode' runs sandboxed
cplt --shell-install --agent claude # and 'claude', alongside the others
Cada instalación se añade a tu archivo rc en lugar de reemplazar lo que ya está ahí, así que puedes aislar tantos agentes como uses. Sin --agent obtienes copilot, que es lo que el flag siempre ha instalado.
| Shell | Archivo modificado | Qué se añade (para --agent opencode) |
|---|---|---|
| zsh (predeterminado en 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 alias tanto para antigravity como para agy, ya que cualquiera de los dos nombres inicia el mismo agente.
Reinicia tu shell o haz source del archivo para activarlo.
No hay alias para --agent shell: no existe un binario shell que suplantar. Escribe cplt --agent shell para un shell aislado, o cplt exec -- <command> para un único comando.
Si prefieres no usar --shell-install, añade la línea tú mismo:```bash
eval "$(cplt --shell-setup --agent opencode)"
alias opencode 'cplt --agent opencode'
El mismo patrón que usan mise, direnv y starship.
</details>
**Por qué cada alias nombra a su agente.** `alias opencode=cplt` no haría lo que parece. El simple `cplt` elige su agente desde `--agent`, luego el archivo de configuración, y luego lo que encuentre en PATH — y la detección en PATH prefiere `copilot`. Escribir `opencode` aislaría a Copilot en su lugar, sin nada en pantalla que lo indique. El alias pasa `--agent` para que el comando que escribes sea el agente que obtienes.
**¿Por qué un alias en lugar de un enlace simbólico?** cplt y Copilot CLI se instalan en el mismo directorio bin de Homebrew (`/opt/homebrew/bin/`), y solo un archivo llamado `copilot` puede residir allí, por lo que un enlace simbólico entraría en conflicto. Un alias evita eso. El binario real `copilot` permanece en PATH donde cplt puede encontrarlo y envolverlo, y el alias redirige tu comando.
> **Nota:** cplt se niega a anidarse. Si detecta que ya se está ejecutando dentro de un sandbox (mediante la variable de entorno `__CPLT_WRAPPED`), no se lanzará de nuevo. Los subcomandos de solo lectura como `--print-profile` y `cplt doctor` siguen funcionando dentro de un sandbox existente.
## Uso```
cplt [OPTIONS] [-- <AGENT_ARGS>...]
Todo lo que va después de -- va directamente al proceso del agente (copilot, opencode, gemini, antigravity, pi, claude, goose, dsh o shell).
Un ajuste predefinido establece una base para los cinco interruptores principales del sandbox con una sola bandera en lugar de una lista de ellas. Las banderas individuales siguen prevaleciendo sobre el ajuste predefinido, así que --preset permissive --no-allow-tmp-exec hace lo que dice. También se puede configurar como [sandbox] preset = "..." en la configuración.
Matriz completa de ajustes predefinidos y orden de resolución: docs/configuration.md.
El directorio del proyecto es el espacio de trabajo con permisos de escritura, más una lista de permitidos reducida necesaria para autenticación, runtime y herramientas (véase la tabla anterior). El kernel bloquea todo lo demás, incluidas las claves SSH y las credenciales de la nube.
cplt sanitiza el entorno hijo por defecto. Solo pasan las variables seguras, y se eliminan las credenciales de la nube, las URL de bases de datos y los tokens de paquetes. También inyecta variables de endurecimiento que bloquean los scripts de ciclo de vida de npm/yarn/pnpm (hooks de postinstall, el vector de ataque a la cadena de suministro número uno), desactivan la firma de commits y tags de git (ya que ~/.ssh y ~/.gnupg son inalcanzables dentro del sandbox) y desactivan la telemetría de herramientas de desarrollo (DO_NOT_TRACK=1, NEXT_TELEMETRY_DISABLED=1, TURBO_TELEMETRY_DISABLED=1, CHECKPOINT_DISABLE=1 y otras).
Lo que pasa:
Lista de permitidos por prefijo con protección de sufijos secretos. Una variable que coincida con un prefijo permitido como COPILOT_* o YARN_* aún se descarta si termina en un sufijo portador de secretos: _TOKEN, _AUTH, _SECRET, _SECRET_KEY, _KEY, _PASSWORD o _CREDENTIALS. Así, COPILOT_DEBUG pasa y COPILOT_API_KEY no.
Siempre bloqueadas: AWS_*, AZURE_*, NPM_TOKEN, DATABASE_URL, VAULT_TOKEN, SSH_AUTH_SOCK, variables de Docker, tokens de CI y cualquier cosa que no esté en la lista de permitidos.
| Bandera | Qué hace |
|---|---|
--pass-env <VAR> | Pasar una variable de entorno al agente. Repetible |
--inherit-env | ⚠️ Peligroso. Heredar el entorno padre completo. Solo elimina , , , . Solo para depuración |
cplt autodescubre las herramientas instaladas y escribe reglas de sandbox que coincidan. Por lo general, solo los directorios que existen en disco reciben reglas, así que no hay rutas fantasma. En macOS, los directorios de aplicaciones escribibles se incluyen cuando se descubren aunque aún no existan, para que puedan crearse en el primer uso. Linux no puede permitir una escritura en una ruta inexistente, así que allí la creación tiene que ocurrir fuera del sandbox.
Ejecuta cplt doctor para ver si cplt funcionará aquí para tu agente, y cplt doctor --verbose para todo lo que detectó en tu máquina.
Estas se traducen a las propias banderas de sesión del agente, así que no necesitas un separador --.
--continue y --resume también se mapean para OpenCode, Antigravity y Claude Code:
¹ Ni OpenCode ni Antigravity tienen un selector de sesión interactivo, así que un --resume solo significa "continuar la última sesión". Claude Code sí lo tiene, así que se mapea directamente.
--remote y --name son exclusivas de Copilot. Pi y el modo shell no reciben ninguna traducción, así que las cuatro banderas se descartan para ellos. La autorreanudación es un mecanismo aparte: cuando invocas cplt sin argumentos de paso ni banderas de sesión, añade --resume por ti, y eso se aplica solo a Copilot.
Combínalas con banderas de sandbox y argumentos de paso --:```bash
cplt --resume=my-task # resume by name
cplt --remote --name my-task -- -p "fix tests" # remote + named + prompt
### Agentes
Elige uno con `--agent <name>`, o conviértelo en el predeterminado con `cplt config set sandbox.agent <name>`. Copilot, OpenCode y Antigravity se detectan automáticamente desde `PATH` en ese orden cuando no especificas uno.
| Agente | Valor de `--agent` | Detectado automáticamente | Autenticación |
| --- | --- | --- | --- |
| GitHub Copilot CLI | `copilot` | sí, prioridad 1 | Token de GitHub, desde el Keychain o `gh` |
| [OpenCode](https://opencode.ai/) | `opencode` | sí, prioridad 2 | Suscripción de Copilot mediante `/connect`, o `--pass-env ANTHROPIC_API_KEY` |
| [Antigravity CLI](https://github.com/google-antigravity/antigravity-cli) | `antigravity`, alias `agy` y `agi` | sí, prioridad 3 | Google OAuth en el navegador |
| [Pi](https://github.com/earendil-works/pi) | `pi` | no | `--pass-env ANTHROPIC_API_KEY` y similares |
| [Claude Code](https://docs.anthropic.com/en/docs/claude-code) | `claude`, alias `cc` y `claude-code` | no | OAuth de suscripción en `~/.claude` o el Keychain, `CLAUDE_CODE_OAUTH_TOKEN` (elimina la concesión del Keychain), o `--pass-env ANTHROPIC_API_KEY` |
| [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) | `dsh`, alias `deepseek` y `deepseek-harness` | no | `--pass-env DEEPSEEK_API_KEY`, o `$DSH_HOME/.env` (`~/.dsh/.env`) |
| Tu shell | `shell` | no | ninguna |
- **Pi, Claude Code, goose y DeepSeek Harness nunca se detectan automáticamente.** `pi` y `dsh` son nombres de binarios genéricos que podrían colisionar con otra cosa en tu máquina, y Claude Code debe elegirse a propósito.
- **Las claves de API de terceros son opcionales.** `ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, `GEMINI_API_KEY`, `OPENROUTER_API_KEY`, `ANTHROPIC_AUTH_TOKEN`, `CLAUDE_CODE_OAUTH_TOKEN` y las variables de enrutamiento de Bedrock/Vertex (`CLAUDE_CODE_USE_BEDROCK`, `AWS_BEARER_TOKEN_BEDROCK`, `CLAUDE_CODE_USE_VERTEX`, `ANTHROPIC_VERTEX_PROJECT_ID`, `GOOGLE_CLOUD_PROJECT`) nunca se transfieren a menos que las nombres con `--pass-env`.
- **La autenticación por suscripción no necesita variable de entorno.** El flujo de dispositivo `/connect` de OpenCode almacena su token en `~/.local/share/opencode/auth.json`, y el token OAuth de Claude Code reside en `~/.claude` (`.credentials.json` en Linux) o en el Keychain de macOS. Ambos son accesibles dentro del sandbox, por lo que cplt no insiste con una clave de API faltante para ninguno de los dos.
- **Los flujos OAuth en el navegador necesitan `--allow-browser`** cuando aparece un aviso de inicio de sesión. Eso cubre a Antigravity; todos los demás agentes aquí usan un flujo de dispositivo que imprime un código y una URL y no necesita navegador. El flag permite al agente lanzar cualquier aplicación fuera del sandbox y no puede restringirse a URLs, así que actívalo para el inicio de sesión y desactívalo de nuevo — consulta la [tabla de flags](#sandbox-toggles) y [docs/security.md](https://github.com/navikt/cplt/blob/main/docs/security.md#--allow-browser-is-a-sandbox-escape-and-cannot-be-scoped).
- **La actualización automática de Claude Code está deshabilitada** con `DISABLE_AUTOUPDATER=1`. Claude Code no tiene un flag `--no-auto-update`, la autoactualización dentro del sandbox es un vector de persistencia, y de todos modos fallaría contra rutas de instalación de solo lectura.
- **Se respeta `CLAUDE_CONFIG_DIR`.** Cuando está definida, cplt concede ese directorio en lugar de `~/.claude` y transfiere la variable, de modo que una raíz de configuración reubicada sigue funcionando.
- OpenCode es [un cliente de Copilot oficialmente compatible](https://github.blog/changelog/2026-01-16-github-copilot-now-supports-opencode/), por lo que tu suscripción existente de Copilot funciona con `/connect` dentro de OpenCode.
Los directorios de configuración por agente, el uso del Keychain, los permisos de ejecución y el aislamiento del entorno están en [SECURITY.md](https://github.com/navikt/cplt/blob/main/SECURITY.md#supported-agents).
### Soporte de goose
cplt puede aislar [goose](https://github.com/aaif-goose/goose), el agente de IA de código abierto (binario `goose`). Verificado con 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 seguridad para goose:
--agent goose o establece sandbox.agent = "goose" en la configuraciónANTHROPIC_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) se reconocen como indicios de autenticación y deben pasarse mediante --pass-env. goose lee , no . Cualquier proveedor fuera de este subconjunto sigue funcionando: nombra su variable con cplt puede ejecutar en sandbox DeepSeek Harness (binario dsh), el harness de agentes orientado a plugins de DeepSeek. Upstream lo distribuye como vista previa para desarrolladores y su propio SAFETY.md indica que no se debe confiar en sus controles como única frontera, que es precisamente el caso para el que existe cplt.```bash
cplt --agent dsh
cplt --agent dsh --pass-env DEEPSEEK_API_KEY
cplt config set sandbox.agent dsh
**Notas de seguridad para DSH:**
- **No se detecta automáticamente**: selecciónalo con `--agent dsh` (alias `deepseek`, `deepseek-harness`) o establece `sandbox.agent = "dsh"`. `dsh` es un nombre de comando corto y genérico que podría pertenecer a otra cosa en tu máquina
- **Desactiva el propio sandbox de DSH dentro de cplt**: DSH envuelve cada llamada a herramientas de shell y archivos en su propio sandbox de proceso — Seatbelt en macOS, bwrap o Landlock en Linux. Ninguno se anida dentro de cplt. macOS no admite llamadas anidadas a `sandbox-exec` (la misma limitación que hace que cplt desactive el sandbox interno de Gradle, consulta [Limitaciones](#limitations)), y bwrap construye su namespace con `unshare`, que el filtro seccomp de cplt deniega. cplt es el límite de aplicación en cualquier caso, así que elige el preset de permisos `danger-full-access` que DSH incluye para sesiones en sandbox. Si dejas el runner interno activado, las llamadas a herramientas fallan con un error del runner del sandbox en lugar de un error de tarea
- **Una raíz de home, y cplt sigue la anulación**: DSH mantiene sesiones, ajustes, caché y perfiles bajo `$DSH_HOME` (`~/.dsh` por defecto). `DSH_HOME` está en la lista de permitidos del entorno, así que el hijo resuelve la misma raíz que cplt concede. Un valor que apunte a una raíz del sistema o a tu directorio home se rechaza antes del lanzamiento, el mismo veto por el que pasa `CLAUDE_CONFIG_DIR`
- **Protección de persistencia en el host**: `$DSH_HOME/cordis.patch.yml`, la superposición a nivel de home que el Loader lee al arrancar, tiene denegada la escritura. `$DSH_HOME/profiles/` permanece escribible porque DSH reescribe la raíz de inclusión `cordis.yml` de cada perfil en cada arranque, así que un `cordis.patch.yml` por perfil y los plugins instalados son un residual documentado — haz las ediciones de perfiles y de `dsh plugin` fuera de cplt, y lanza siempre `dsh` a través de cplt para que cualquier cosa plantada siga ejecutándose en sandbox
- **Dominios por defecto**: solo `deepseek.com`. El adaptador `dsh-llm-deepseek` incluido usa por defecto `https://api.deepseek.com`. Si apuntas `DEEPSEEK_BASE_URL` a una pasarela, tienes que añadir el dominio de esa pasarela mediante `allowed_domains`
- **Autenticación**: pasa la clave con `--pass-env DEEPSEEK_API_KEY`, o guárdala en `$DSH_HOME/.env`. Una clave guardada a través de la propia interfaz de modelos de DSH acaba en `$DSH_HOME/.credentials.yaml`, dentro de la misma raíz escribible. El Llavero de macOS está denegado, así que `git push` por HTTPS necesita el token de `gh` en `hosts.yml` o `--pass-env GH_TOKEN`
### Modo shell
Ejecuta un shell simple en sandbox sin agente de IA y con las mismas restricciones. Útil para probar herramientas de compilación, depurar problemas del sandbox o simplemente trabajar con cuidado a mano.```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
Se aplican las mismas reglas de denegación por defecto: aislamiento del sistema de archivos, restricciones de red, saneamiento del entorno. Los directorios de configuración del shell (variables e historial de fish, historial de zsh) permanecen escribibles.
Para un solo comando, cplt exec es más limpio que cplt --agent shell -- -c 'cmd'.
Ejecuta cualquier comando dentro del sandbox sin iniciar un agente. Sin banner de inicio, sin prompt de confirmación, por lo que es adecuado para scripts, pipes y alias 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"
Cada flag de nivel superior de `cplt` se aplica: `--project-dir`, `--allow-read`, `--deny-path`, `--with-proxy`, `--pass-env`, y el resto. Añade `--no-quiet` para ver el resumen completo de la configuración del sandbox antes de que se ejecute el comando.
### Ejemplos```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"
La configuración se realiza en dos niveles: global, para las preferencias del desarrollador, y por repositorio, para las políticas del equipo.```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` es el editor interactivo, con vistas Effective, Global y Repository, búsqueda, cambios por etapas y una confirmación explícita antes de guardar cualquier cosa sensible para la seguridad. `cplt config` sigue siendo la interfaz estable no interactiva para scripts y CI. Las propuestas de repositorio todavía se confirman y aprueban por separado con `cplt trust`. El editor nunca las confirma ni las aprueba automáticamente.
La precedencia se ejecuta con los flags de CLI, luego el archivo de configuración global en `~/.config/cplt/config.toml`, y después los valores predeterminados integrados. La configuración por repositorio en `.cplt.toml` es una capa separada en lugar de un peldaño en esa escalera: `[deny]` se endurece incondicionalmente, y los permisos aprobados son solo aditivos, por lo que un repositorio puede habilitar una función pero nunca puede desactivar algo establecido por un flag de CLI o la configuración global.
Un `.cplt.toml` en la raíz del repositorio contiene la política del equipo:```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"]
cplt lo lee desde git HEAD, por lo que el agente no puede manipular su propia política a mitad de sesión, y las aprobaciones de confianza quedan fijadas al contenido del archivo. Un .cplt.toml sin confirmar no otorga nada hasta que se confirma, aunque sus claves [deny] siguen aplicándose. En CI y scripts, donde nadie puede responder a un prompt, --accept-repo-config aprueba las propuestas del archivo confirmado para esa única ejecución sin persistir ninguna confianza. cplt init escribe uno por ti detectando las herramientas del proyecto:```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
Conoce JVM (Gradle/Maven), Node.js, Docker, Python, Rust, Go, Playwright, Spring Boot, Ktor, TestContainers, Next.js, Vite, Flyway, Cypress y secretos de entorno desde `.env.example`. Los permisos peligrosos salen del generador con una advertencia de riesgo adjunta. `--global` examina en su lugar elementos a nivel de máquina: navegadores de Playwright, firma GPG, credenciales de registro, agentes alternativos.
Algunas claves son solo globales y se rechazan desde `.cplt.toml` porque son específicas de la máquina o una preferencia 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`, y todas las claves de `[gh_guard]` y `[git_guard]`.
Detalles completos, incluido el modelo de confianza, las reglas de expansión de rutas y la referencia completa del archivo de configuración: [docs/configuration.md](https://github.com/navikt/cplt/blob/main/docs/configuration.md).
## Arquitectura```
┌──────────────────────────────────┐
│ 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 │
└──────────────────────────────────┘
El modelo de seguridad es un sistema de archivos de denegación por defecto con aplicación a nivel de kernel. En macOS, y en Linux con kernel 6.7+ (Landlock ABI v4), la red está restringida al puerto 443 por defecto, con --allow-port para puertos adicionales. En kernels de Linux más antiguos, el proxy CONNECT proporciona esa restricción en su lugar, razón por la cual está habilitado por defecto. El acceso al agente SSH y la salida a localhost están bloqueados en el kernel en macOS. En Linux ninguno de los dos lo está: las reglas de Landlock basadas en puertos no pueden distinguir localhost de un host remoto, y connect() de sockets unix no está controlado por Landlock por debajo del kernel 7.1, así que aparte de los sockets que bubblewrap enmascara, el SSH_AUTH_SOCK retenido es lo único que se interpone entre el agente y tus claves cargadas. El generador de perfiles descubre tu entorno (cplt doctor --verbose muestra los mismos resultados de sondeo) y emite reglas solo para los directorios de herramientas que realmente existen en disco. Menos reglas, sandbox más estricto.
sandbox-execpre_exec (kernel 5.13+, filtrado de puertos TCP en 6.7+)Internals y estructura de módulos: docs/architecture.md. Modelo de amenazas, capas de defensa y brechas honestas: SECURITY.md.
Un solo binario, dependencias mínimas, sin servicios en tiempo de ejecución, sin telemetría. Tres capas de defensa, con límites claros entre ellas:
Contra qué protege cplt:
.env): bloqueado por el kernel.git/hooks tiene escritura denegada a nivel de kernel en macOS. En Linux, con Landlock y sin Bubblewrap, permanece escribible, y el propio git del lado padre de cplt se ejecuta entonces con core.hooksPath=/dev/null, por lo que nunca ejecuta un hook plantado, aunque un git que ejecutes tú mismo sí lo haráPNPM_HOME, ~/.deno/bin, ~/.bun/bin): allí se concede escritura para que pnpm add -g y similares funcionen dentro del sandbox, así que un agente puede dejar atrás un binario que un shell posterior recoja de tu PATHContra qué no protege cplt:
sandbox.keychain_substitute puede ceder el permiso cuando un agente tiene otra credencialNuestras prioridades, en orden: correcto (cada afirmación está probada, cada caso límite tiene una CVE o una referencia de investigación), transparente (SECURITY.md no oculta nada), simple (un binario, cero configuración requerida, valores por defecto sensatos) y útil (apartarse del camino y dejar que el agente trabaje, de forma segura).
Más: docs/security.md · SECURITY.md
El proxy está activado por defecto. Todo el tráfico saliente de Copilot CLI, gh y curl pasa por un proxy CONNECT en localhost mediante HTTP_PROXY/HTTPS_PROXY y NODE_USE_ENV_PROXY=1. Escucha en un puerto efímero asignado por el sistema operativo, así que nada colisiona. Obtienes registro de conexiones en tiempo real, bloqueo de dominios, listas de dominios permitidos, un registro de auditoría persistente y la misma política de puertos que aplica el sandbox (443 más cualquier cosa en 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>` escribe el conjunto observado con un dominio por línea, y
`--proxy-upstream-no-proxy <HOST>` lista los hosts a los que llegar directamente en lugar de a través
del 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"
El modo forzado por proxy es opcional. Restringe la salida del kernel al puerto del proxy para que un socket abierto directamente, o un env -u HTTPS_PROXY, no pueda escabullirse. La aplicación es total en macOS, que fija a localhost:<proxy_port>. En Linux bloquea TCP directo :443, y una regla seccomp permite solo SOCK_STREAM con protocolo 0 o IPPROTO_TCP para AF_INET/AF_INET6, por lo que UDP, raw, SCTP y DCCP también quedan cerrados — a costa de cualquier cosa que abra dicho socket, no solo el código que envía UDP. Lo que queda es un residual basado en puerto, evil.com:<proxy_port>, hasta #114.
Fuera del modo forzado por proxy, Linux no restringe UDP. Los derechos de red de Landlock son solo TCP hasta ABI v10, cplt maneja AccessNet::ConnectTcp por sí solo, y la regla seccomp anterior deliberadamente no se aplica — denegar SOCK_DGRAM allí rompería getaddrinfo(3), y por tanto todo el DNS, para cada herramienta no proxificada. El UDP saliente a cualquier host, el bind UDP entrante, el túnel DNS y QUIC/HTTP-3 quedan por tanto sin mediación en el modo predeterminado, y el proxy CONNECT transporta solo TCP, así que nada de ello aparece en el registro del proxy. macOS restringe UDP en el modo predeterminado pero tampoco lo enruta: remote ip "*:443" cubre UDP, así que QUIC/HTTP-3 en 443 sale sin tocar el proxy allí también. Bajo proxy.forced el registro del proxy es un registro completo de la salida en macOS. En Linux es completo excepto por el residual evil.com:<proxy_port> anterior, que no atraviesa el proxy y por tanto no aparece en su registro.
Ambas listas coinciden de la misma manera: example.com cubre el dominio exacto y todos los subdominios, la coincidencia no distingue mayúsculas y minúsculas, y los puntos finales se eliminan. Los archivos de lista de bloqueo y lista de permitidos se releen cada cinco segundos, así que puedes editarlos en vivo. El tráfico a localhost omite el proxy mediante NO_PROXY y nunca aparece en el registro de auditoría. --proxy-timeout <SECONDS> limita las lecturas de solicitud y cabeceras (predeterminado 60) y no derriba los túneles CONNECT establecidos, que pueden permanecer inactivos hasta una hora.
Cada flag del proxy, detalle de filtrado de dominios, encadenamiento a proxy corporativo ascendente y el formato del registro de conexiones: docs/proxy.md.
Actívalas y cplt intercepta gh y git mediante scripts envoltorio en $PATH:
Esta es la Capa 3, una barrera blanda. Impide que un agente conforme haga algo destructivo por accidente. Para un límite duro, apóyate en el sandbox del kernel y la protección de ramas del lado del servidor.
Con la guarda de gh activada, cplt también cachea el token de GitHub al lanzar y lo sirve una vez a través del callback gh auth token, luego elimina la caché. Eso reduce las filtraciones accidentales y basadas en el entorno. No es un límite contra un agente hostil, porque la caché vive en el propio TMPDIR del agente y un agente que la lea antes que el consumidor legítimo igualmente obtiene el token. SECURITY.md tiene la declaración completa sobre block_auth_token.
Comportamiento completo: docs/gh-guard.md · docs/git-guard.md
El sandbox bloquea algunos flujos de trabajo a propósito. Los comunes y sus soluciones:
Playwright Chromium necesita cplt config set sandbox.allow_cache_exec ms-playwright, y Chromium debe ejecutarse sin su propio sandbox anidado. En macOS sus ayudantes no pueden inicializar un segundo sandbox Seatbelt dentro de cplt (forbidden-sandbox-reinit); en Linux el filtro seccomp de cplt bloquea las llamadas al sistema de namespace que ese sandbox necesita. Playwright como biblioteca ya se lanza con --no-sandbox, y esa misma opción establece PLAYWRIGHT_MCP_SANDBOX=false para Playwright MCP, que de lo contrario lo volvería a activar. Cualquier otro lanzador de Chromium necesita --no-sandbox por sí mismo. cplt sigue siendo el límite de kernel que aplica, pero un renderer comprometido recibe entonces el perfil completo de Playwright de cplt en lugar del perfil hijo más restringido de Chromium. Ver Cache exec y SECURITY.md.
Git commit funciona para todos los agentes; si git push funciona sobre HTTPS depende del agente. Tres requisitos previos: usa remotos HTTPS en lugar de SSH (git remote set-url origin https://github.com/org/repo.git, o reescribe globalmente con git config --global url."https://github.com/".insteadOf "[email protected]:"), ejecuta gh auth login una vez fuera del sandbox, y ejecuta gh auth setup-git si el helper de credenciales aún no está configurado. El push entonces ejecuta gh auth git-credential, que necesita un token que gh pueda alcanzar desde dentro del sandbox — eso difiere según el agente, ver Git workflow. Los push a la rama predeterminada y todos los push forzados son rechazados por la guarda de git por defecto; haz push a una rama de característica. El socket del agente SSH está bloqueado porque desbloquea todas las claves cargadas y puede autenticarse ante cualquier host, mientras que el helper de credenciales de gh está limitado a GitHub.
La JVM es consciente del proxy, así que un repositorio Maven interno en una IP privada ahora necesita ser permitido. cplt inyecta http(s).proxyHost/proxyPort en JAVA_TOOL_OPTIONS, así que la resolución de dependencias de Gradle y Maven pasa por el proxy CONNECT y aparece en el registro del proxy en lugar de omitirlo. La guarda SSRF del proxy entonces rechaza un Nexus o Artifactory interno que resuelve a espacio de direcciones privado, exactamente como ya lo hace para curl, npm y pip. Añade su nombre DNS a proxy.allow_private_domains. Una URL de repositorio escrita como IP literal desnuda (https://10.20.30.40/repository/maven-public/) no puede permitirse con ninguna clave — esa comprobación se ejecuta antes de consultar la lista de permitidos — así que tal repositorio necesita un nombre DNS. Los forks de plugin WorkerExecutor, y un daemon de Gradle iniciado fuera de cplt y reutilizado dentro, no están proxificados. Ver Internal Maven/Gradle repositories on private IPs.
Gradle 9+ ejecuta su propio sandbox anidado, y cplt lo desactiva. Desde Gradle 8.8 el daemon se envuelve a sí mismo en sandbox-exec (controlado por GRADLE_MACOS_SANDBOX, anteriormente la propiedad org.gradle.daemon.sandbox). macOS no soporta llamadas anidadas a sandbox-exec, así que el sandbox interno falla con "Operation not permitted" en operaciones de socket. cplt inyecta GRADLE_MACOS_SANDBOX=off, ya que él mismo proporciona sandboxing a nivel de kernel. Este es un problema conocido upstream que afecta a cualquier herramienta que envuelva Gradle en un sandbox externo. Anúlalo con --pass-env GRADLE_MACOS_SANDBOX si realmente quieres el sandbox propio de Gradle.
Copilot CLI 1.0.83 ejecuta su propio sandbox anidado, y cplt lo desactiva. En Linux ese sandbox construye un namespace de red — slirp4netns, iptables, /dev/net/tun — y el filtro seccomp de cplt deniega el unshare que toma. cplt también establece HTTP_PROXY/HTTPS_PROXY, lo que en 1.0.83 pone un sandbox de Linux en la ruta de salida del proxy lo hayas pedido o no, así que los dos colisionan en cada lanzamiento. Síntoma: [cplt] Starting Copilot in sandbox... y luego nada. cplt inyecta la propia opción de exclusión de Copilot, COPILOT_CLI_SANDBOX_SUPPORT_OVERRIDE=unsupported; Copilot se retira para la sesión, lo dice, y deja intacto tu sandbox.enabled guardado. cplt es el límite, como lo es para Gradle y Chromium. Anúlalo con --pass-env COPILOT_CLI_SANDBOX_SUPPORT_OVERRIDE. Una política gestionada empresarial que requiera el sandbox anula todo esto — ver Copilot CLI's own command sandbox.
Cada impacto, con las tablas por herramienta, notas sobre el daemon de JVM y Kotlin, solución de problemas de GPG, y las diferencias de plataforma del registro privado: docs/known-impacts.md.
sandbox-exec está obsoleto. Apple no lo ha eliminado, pero puede hacerlo en una versión futura de macOS.lsopen de SBPL tampoco tiene filtro, así que --allow-browser es todo Launch Services o nada. Con él activado el agente puede lanzar cualquier aplicación fuera del sandbox, y ningún envoltorio puede acotar eso — ver docs/security.md..env dentro del directorio del proyecto no está aplicada por el kernel. Las escrituras en .git/hooks se bloquean cuando Bubblewrap está activo.--deny-path requiere Bubblewrap. Se aplica mediante máscaras de montaje cuando bwrap está activo. Sin él, Landlock es solo lista de permitidos y cplt advierte sobre la denegación en lugar de aplicarla.Más: docs/security.md
Las contribuciones son bienvenidas.```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
Abre un issue antes de comenzar un cambio grande. Cada PR debe pasar CI (fmt, clippy, tests).
## Referencias
- [SECURITY.md](https://github.com/navikt/cplt/blob/main/SECURITY.md), el modelo de seguridad completo, análisis de amenazas, estrategia de pruebas y trabajos previos
- [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)
- [Landlock LSM documentation](https://docs.kernel.org/userspace-api/landlock.html)
- [seccomp-BPF documentation](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)
## Licencia
[MIT](https://github.com/navikt/cplt/blob/main/LICENSE)
| Bandera | Qué hace |
|---|
--preset strict | Bloqueo total de red. Los cinco interruptores desactivados, más gh_guard, git_guard, proxy.forced (egress forzado por proxy) y proxy.default_allowlist (lista de dominios permitidos con fallo cerrado) activados. Vía de escape: --allow-all-domains desactiva solo la lista de permitidos |
--preset standard | Los valores predeterminados actuales. Los cinco desactivados, el directorio scratch permanece activado. Igual que no pasar ningún ajuste predefinido |
--preset permissive | Activa allow_localhost_any, allow_tmp_exec y allow_lifecycle_scripts |
--preset full-trust | ⚠️ Peligroso. Activa los cinco, añadiendo allow_env_files y allow_docker |
| Bandera | Qué hace |
|---|
-d, --project-dir <DIR> | En qué directorio puede trabajar Copilot. Por defecto, la raíz del repositorio git actual |
--allow-read <PATH> | Permitir que Copilot lea archivos fuera del proyecto, solo lectura. Repetible |
--allow-write <PATH> | Permitir que Copilot lea y escriba fuera del proyecto. Úsese con cuidado. Repetible. El árbol es escribible pero no ejecutable — un árbol que sea ambas cosas es una vía de colocación de binarios, así que un allow.write sobre ~/.cargo también impide que se ejecute ~/.cargo/bin. Usa --allow-exec en un árbol separado y no superpuesto cuando necesites ambas cosas |
--allow-exec <PATH> | ⚠️ Peligroso. Permitir que el agente ejecute binarios desde un árbol fuera de los directorios de herramientas predeterminados — por ejemplo, un Homebrew o un prefijo de toolchain reubicado. Concede lectura y ejecución, nunca escritura. Repetible. Se rechaza para una raíz insegura (/, /tmp, $HOME y sus padres, los directorios de sistema de la plataforma) y para cualquier árbol que se superponga con uno escribible — el directorio del proyecto, una concesión de --allow-write, un directorio de herramientas escribible como ~/.cache, un directorio de datos de agente escribible (~/.claude, ~/.local/share/opencode, ~/.pi/agent y similares), el .git real de un worktree o repo bare, o un árbol que los backends hacen escribible sin ninguna concesión (/tmp y /dev/shm en Linux; /private/tmp y /private/var/folders en macOS): escribible más ejecutable es una vía de colocación de binarios, y ningún backend puede restar la concesión de escritura de la concesión de ejecución |
--allow-socket <PATH> | ⚠️ Peligroso. Permitir una ruta de socket de dominio Unix, por ejemplo un daemon LSP personalizado o un socket de base de datos. Repetible. Lo que sea que esté al otro extremo se ejecuta fuera del sandbox, así que apuntar esto a docker.sock o a un socket de agente equivale a --allow-docker, y la única protección es que se rechazan las superposiciones con --deny-path. En Linux no hace nada por debajo del kernel 7.1, ya que las conexiones a sockets unix no están controladas por Landlock antes de ABI v9 (véase Limitaciones de Linux) |
--deny-path <PATH> | Bloquear una ruta que de otro modo estaría permitida. Denegar siempre gana. Repetible |
--allow-port <PORT> | Permitir tráfico saliente en un puerto adicional. Solo 443 por defecto. Repetible. En macOS la regla es (remote ip "*:PORT"), que es agnóstica a la familia y por tanto transporta UDP además de TCP; Landlock controla solo la conexión TCP. Bajo proxy.forced el puerto no abre ningún socket directo — es alcanzable a través del proxy, así que las herramientas conscientes del proxy siguen funcionando |
--allow-localhost <PORT> | Permitir salida a localhost en un puerto. Localhost está bloqueado por defecto. Úsese para servidores MCP o servidores de desarrollo. Repetible |
--allow-localhost-any | Permitir salida a localhost en todos los puertos. Necesario para herramientas de compilación como Turbopack (Next.js) y Vite que usan puertos efímeros aleatorios para IPC |
| Categoría | Ejemplos | Cómo |
|---|
| Sistema central | HOME, USER, PATH, SHELL, TMPDIR, LANG | Lista de permitidos explícita |
| Terminal | TERM, COLORTERM, TERM_PROGRAM | Lista de permitidos explícita |
| Editor | EDITOR, VISUAL, PAGER | Lista de permitidos explícita |
| Tokens de autenticación | GH_TOKEN, GITHUB_TOKEN, COPILOT_GITHUB_TOKEN | Se pasan solo si ya los has establecido. El guard de gh usa en su lugar un archivo de un solo uso |
| Configuración de Copilot | COPILOT_DEBUG, COPILOT_* | Lista de permitidos por prefijo |
| Runtimes de lenguajes | NODE_*, GOPATH, CARGO_HOME, JAVA_HOME, VIRTUAL_ENV, PYTHONPATH | Lista de permitidos explícita |
| Gestores de herramientas | NVM_*, FNM_*, PYENV_*, MISE_*, SDKMAN_*, COREPACK_*, YARN_* | Lista de permitidos por prefijo |
| OpenTelemetry | OTEL_EXPORTER_OTLP_ENDPOINT, OTEL_SERVICE_NAME, OTEL_RESOURCE_ATTRIBUTES, OTEL_* | Lista de permitidos por prefijo (OTEL_EXPORTER_OTLP_HEADERS puede llevar autenticación opcional) |
| Directorios XDG | XDG_CONFIG_HOME, XDG_DATA_HOME, XDG_STATE_HOME, XDG_CACHE_HOME | Lista de permitidos explícita |
NO_COLORFORCE_COLORSSH_AUTH_SOCKSSH_AGENT_PID| Bandera | Qué hace |
|---|
--allow-lifecycle-scripts | Permitir que se ejecuten los scripts de ciclo de vida de npm/yarn/pnpm (hooks de postinstall). Bloqueados por defecto. Úsese cuando npm install los necesite |
--allow-gpg-signing | Permitir la firma de commits y tags con GPG dentro del sandbox. Concede acceso de solo lectura al llavero público y al socket del agente GPG. Las claves privadas permanecen denegadas. Véase Firma GPG |
--allow-jvm-attach | Permitir sockets unix de la API JVM Attach en /tmp. Necesario para el mocking inline de MockK, los agentes inline de Mockito, ByteBuddy. Véase JVM Attach API |
--allow-msbuild | Permitir sockets unix de nodos trabajadores de MSBuild en /tmp. Necesario para dotnet build. No habilita el MSBuild Server persistente. Véase IPC de nodos trabajadores de MSBuild |
--no-scratch-dir | Desactivar el directorio scratch por sesión, que está activado por defecto. TMPDIR no se redirigirá |
--scratch-dir | Activar explícitamente el directorio scratch por sesión. Ya es el valor predeterminado, así que esto sirve para anular scratch_dir = false en la configuración |
--brief | 🧪 Experimental. Escribir el resumen del sandbox orientado al agente en el directorio scratch (CPLT_BRIEF.md). Desactivado por defecto. También sandbox.brief = true en la configuración. Inestable, así que puede cambiar o eliminarse en una versión futura |
--no-brief | Desactivar el resumen del sandbox para esta ejecución, anulando sandbox.brief = true en la configuración. También suprime el bloque AGENTS.md, que está condicionado al resumen |
--agents-md | 🧪 Experimental. Con --brief, escribir también el bloque gestionado de cplt en el AGENTS.md del proyecto. Desactivado por defecto. También sandbox.agents_md = true en la configuración. Sin efecto sin --brief. Inestable, así que puede cambiar o eliminarse en una versión futura |
--no-agents-md | Desactivar el bloque AGENTS.md para esta ejecución, anulando sandbox.agents_md = true en la configuración. Deja intacto el resumen del directorio scratch |
--allow-tmp-exec | ⚠️ Peligroso. Permitir la ejecución desde directorios temporales del sistema (/private/tmp, /private/var/folders). Prefiere el directorio scratch |
--allow-cache-exec <SUBDIR> | Permitir la ejecución desde un ~/Library/Caches/<SUBDIR>. Repetible. Para herramientas que almacenan en caché binarios compilados allí, como Playwright y pnpm dlx |
--allow-cache-exec-any | ⚠️ Peligroso. Permitir la ejecución desde todo ~/Library/Caches. Prefiere --allow-cache-exec <SUBDIR> |
--allow-browser | ⚠️ Peligroso. Con esto activado, el agente puede lanzar cualquier aplicación de tu máquina fuera del sandbox. La concesión es Launch Services, no un navegador: launchd inicia el objetivo fuera del perfil de Seatbelt, así que open -a Terminal /tmp/x.sh se ejecuta sin sandbox. Esto no puede limitarse a URL — el lsopen de SBPL no acepta ningún filtro, y la concesión es alcanzable a través de LSOpenCFURLRef() sin el binario open siquiera, así que ningún wrapper puede restringirla (#251, y docs/security.md). Actívalo solo mientras haya realmente un aviso de inicio de sesión en pantalla (OAuth de servidor MCP, reautenticación), y luego desactívalo de nuevo. Desactivado por defecto |
--deny-clipboard | Bloquear que el agente lea o escriba el portapapeles de macOS (pbpaste/pbcopy) denegando el servicio Mach com.apple.pasteboard. Todos los demás servicios Mach (Keychain, DNS, framework Security) no se ven afectados. Activado por defecto — esta bandera reafirma el valor predeterminado |
--allow-clipboard | Devolver al agente el portapapeles de macOS, que cplt deniega por defecto. Equivalente a sandbox.deny_clipboard = false |
--use-bubblewrap | Solo Linux. Requerir la capa de namespaces de bubblewrap (namespaces de PID, mount, IPC, UTS, cgroup, usuario más un /tmp privado) sobre Landlock y seccomp. Da error si falta bwrap. Se autodetecta cuando no se da ninguna de las dos banderas |
--no-bubblewrap | Solo Linux. No usar nunca bubblewrap, incluso si está instalado. Recurre a Landlock y seccomp. Úsalo cuando bwrap rompa una herramienta específica |
| Runtime | Directorios home | Variables de entorno / prefijos | Descubrimiento |
|---|
| 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 | ninguna | ninguna |
| Python | .pyenv | VIRTUAL_ENV, PYTHONPATH, PYENV_ROOT, PYENV_* | python3 |
| Yarn Berry | .yarn | YARN_* (el endurecimiento anula YARN_ENABLE_SCRIPTS) | yarn |
| pnpm | Library/pnpm, .local/share/pnpm | PNPM_HOME | pnpm |
| Corepack | ninguna | COREPACK_* | ninguna |
| mise | .local/share/mise, .mise | MISE_* | mise |
| Bandera | Qué hace |
|---|
--doctor | Obsoleta. Usa el subcomando cplt doctor en su lugar |
--print-profile | Imprimir el perfil de sandbox generado (SBPL) y salir |
--show-denials | Transmitir en tiempo real los registros de denegación del sandbox de macOS |
--no-validate | Omitir la comprobación de inicio que verifica que las restricciones del sandbox están activas |
-y, --yes | Omitir el aviso de confirmación interactivo. El resumen de configuración aún se imprime, para auditabilidad. Requerido cuando stdin no es un TTY, así que CI y los scripts lo necesitan |
-q, --quiet | Suprimir el banner de inicio y los mensajes no esenciales. Los errores y advertencias aún se imprimen. También sandbox.quiet = true en la configuración |
--no-quiet | Anular sandbox.quiet = true y mostrar el resumen de inicio de todos modos |
--no-audit | Omitir el informe de cambios posterior a la sesión. cplt normalmente compara el árbol de trabajo con un commit de referencia fijado antes de la ejecución y enumera lo que tocó la sesión, marcando rutas sensibles. -q también lo suprime |
--init-config | Crear un archivo de configuración inicial en ~/.config/cplt/config.toml y salir |
| Bandera | Qué hace |
|---|
--resume[=SESSION] | Reanudar una sesión anterior. --resume solo elige interactivamente, --resume=NAME elige por nombre o ID |
--continue | Reanudar la sesión más reciente en el directorio actual |
--remote | Habilitar el control remoto, para que puedas monitorizar y dirigir la sesión desde GitHub.com o el móvil |
--name SESSION | Nombrar la sesión para que --resume=NAME pueda encontrarla después |
| Bandera de 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 | ignorada | ignorada | ignorada |
--name NAME | --name NAME | ignorada | ignorada | ignorada |
GOOGLE_API_KEYGEMINI_API_KEY--pass-env--observe-domains, por lo que su lista de permitidos integrada es solo la base compartida de registros de paquetes. Añade el dominio de tu proveedor mediante allowed_domains antes de habilitar --default-allowlistGOOSE_DISABLE_KEYRING=1 hace que goose use un secrets.yaml en su directorio de configuración en su lugar, y pasar la clave con --pass-env evita por completo los secretos almacenados. En Linux goose usa el D-Bus Secret Service, que no se ve afectado por la concesión del Keychain~/.config/goose/config.yaml declara entradas extensions: cuyo cmd goose ejecuta al inicio de cada sesión, por lo que un directorio de configuración con permisos de escritura es un vector de persistencia en el host. Las sesiones normales no lo escriben; los cambios de /mode y los permisos de herramientas persistidos no sobreviven a una ejecución en sandbox. Reconfigura con goose configure fuera de cplt~/.local/share/goose/) y de estado (~/.local/state/goose/) de goose son escribibles, con ejecución denegada. goose usa estas rutas XDG también en macOS, y respeta allí las anulaciones XDG_*--continue y --resume sin argumentos se asignan a goose session --resume; --resume=ID a goose session --resume --session-id ID; --name X a goose session --name X. Son flags de subcomando, por lo que cplt inyecta el subcomando session con ellos. --remote se ignora (no hay equivalente en goose)| Capa | Aplicación | ¿Eludible? | Qué protege |
|---|
| 1. Sandbox del kernel | macOS Seatbelt / Linux Landlock+seccomp | ❌ No | Acceso a archivos, exec, puertos de red |
| 2. Proxy de red | Proxy CONNECT, filtrado de dominios | ❌ No (dentro del sandbox) | Conexiones salientes, exfiltración |
| 3. Guardia de comandos | Scripts envoltorio basados en PATH | ⚠️ Barrera blanda | Pushes, merges, releases, escrituras de API |
gitbwrapsandbox-execmiseghPATHcplt doctor: sus sondeos de --version ejecutan cada binario de agente que encuentra en tu PATH, en el padre, así que uno plantado se ejecuta allí —la misma exposición de ruta descubierta que en el lanzamiento anterior, razón por la cual doctor es un informe y no un límite. Su comprobación de gh se resuelve desde los directorios de confianza y su lectura de la versión del kernel no lanza ningún proceso| Comando | Acción |
|---|
gh pr merge, gh repo delete, gh release create | 🔒 Bloqueado |
git push origin main, git push --force | 🔒 Bloqueado |
gh api (escritura a otros repos) | 🔒 Verificado por alcance |
gh pr list, gh issue list, git commit | ✅ Permitido |
git push origin feature-branch | ✅ Permitido con protect_default_branch_only |
| Impacto | Solución |
|---|
Archivos .env bloqueados | cplt config set sandbox.allow_env_files true |
| Hooks postinstall de npm bloqueados | cplt config set sandbox.allow_lifecycle_scripts true |
go test / mise run bloqueados (ejecución temporal) | El directorio scratch está activado por defecto. Si aún lo necesitas, cplt config set sandbox.allow_tmp_exec true |
| Conexiones a localhost bloqueadas | cplt config set allow.localhost 3000, o cplt config set sandbox.allow_localhost_any true |
| Docker bloqueado | cplt config set sandbox.allow_docker true ⚠️ |
| SSH bloqueado | Usa remotos HTTPS en su lugar |
| Firma GPG deshabilitada | cplt config set sandbox.allow_gpg_signing true |
| JVM MockK/Mockito falla | cplt config set sandbox.allow_jvm_attach true |
Nodos trabajadores de MSBuild de dotnet build bloqueados | cplt config set sandbox.allow_msbuild true |
| Credenciales de registro privado bloqueadas | cplt config set allow.read "~/.m2/settings.xml" |
| Repositorio interno Maven/Nexus inalcanzable (Gradle/Maven) | cplt config set proxy.allow_private_domains "intern.example.com". Una URL de repositorio con IP literal no puede permitirse — dale un nombre DNS al host; ver abajo |
| Playwright Chromium no arranca | Permite la ejecución de caché, luego deshabilita el sandbox anidado de Chromium; ver abajo |