
Sandbox per agenti di coding AI. Esegue Copilot CLI, Claude Code, OpenCode, Gemini CLI, Antigravity, Pi, goose o una shell semplice all'interno di un sandbox a livello kernel, con guardie git e gh e policy del sandbox salvata nel repository.
Sandbox applicata dal kernel per agenti di coding AI. cplt avvolge GitHub Copilot CLI, OpenCode, Gemini CLI, Antigravity CLI, Pi, Claude Code, goose, DeepSeek Harness o qualsiasi shell, così l'agente può scrivere codice ma non può rubare credenziali, fare push su main, fare merge di PR o esfiltrare segreti.
sandbox-exec
Gli agenti AI eseguono codice arbitrario. Un agente compromesso, che sia tramite prompt injection, un attacco alla supply chain o un server MCP malevolo, può leggere ~/.ssh, fare push su main, fare merge di PR o esfiltrare il tuo codice, a meno che il sistema operativo stesso non dica di no.
cplt ti offre un'enforcement a livello di kernel con policy configurabili dal team:
.cplt.toml, committata nel controllo di versione, quindi a prova di manomissione e verificabileDocumentazione dettagliata: Configurazione · Proxy e filtraggio dei domini · Guardia del comando gh · Guardia del comando git · Impatti noti · Dettagli sulla sicurezza · Modello di sicurezza
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
Altri agenti e comandi 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
## Cosa blocca
La sandbox blocca l'accesso a credenziali e segreti nel kernel. Le guardie sui comandi bloccano le operazioni distruttive. Ogni restrizione si applica all'agente e a ogni processo che genera.
| Risorsa | Stato | Note |
| --- | --- | --- |
| Lettura/scrittura della directory del progetto | ✅ Consentito | |
| Lettura/scrittura/cancellazione di `.env*`, `.pem`, `.key` nel progetto | 🔒 Bloccato dal kernel | Impedisce l'esfiltrazione e la distruzione di segreti. `--allow-env-files` esegue l'override |
| Scrittura di `.git/hooks`, `.git/config`, `.gitmodules` | 🔒 Bloccato dal kernel (macOS), ⚠️ parziale su Linux | Impedisce la persistenza tramite git hooks, redirect di hooksPath, hijacking dei submodule. **Linux:** Landlock non può negare un sottopercorso all'interno di un albero consentito, quindi questi restano scrivibili sul percorso solo-Landlock. `bwrap` ri-monta `.git/hooks` in sola lettura ma lascia deliberatamente `.git/config` e `.gitmodules` scrivibili, quindi `core.hooksPath` rimane una via di persistenza, vedi [Limitazioni Linux](https://github.com/navikt/cplt/blob/main/docs/security.md#linux). Si applica a **ogni** root scrivibile, al progetto e a ogni concessione `allow.write`, incluso un worktree concesso o un bare repo i cui hook reali risiedono fuori da `<root>/.git` |
| Esecuzione da `/tmp`, `/var/folders` | 🔒 Bloccato dal kernel | Impedisce write-then-exec. La directory scratch reindirizza TMPDIR verso una posizione sicura, attiva per impostazione predefinita |
| Scrittura nelle directory bin/shim risolte tramite PATH (`~/.bun/bin`, `~/.deno/bin`, `$PNPM_HOME`, `shims/` di mise e tutto `installs/`) | 🔒 Bloccato dal kernel (macOS), ⚠️ mise parziale su Linux | Impedisce il trojan di un binario che il tuo prossimo comando *non sandboxed* risolve tramite PATH. Stesso motivo per cui `~/.cargo/bin` e `~/go/bin` sono sempre stati in sola lettura. Rompe `bun install -g`, `deno install`, `pnpm add -g`, `mise install`, `mise upgrade`, `mise use -g` dentro cplt, deliberatamente, e un repo che fissa una toolchain non installata non esegue più il bootstrap. Le installazioni locali al progetto non sono interessate. **Linux:** i due di mise viaggiano sull'overlay in sola lettura di `bwrap`; gli altri tengono nativamente. Vedi [Installazioni globali di tool](https://github.com/navikt/cplt/blob/main/docs/known-impacts.md#global-tool-installs) |
| Esecuzione da `~/Library/Caches` | 🔒 Bloccato dal kernel per impostazione predefinita | Impedisce lo staging di binary-drop. I moduli nativi di Copilot sono esentati tramite un carve-out. Aggiungi esenzioni mirate con `--allow-cache-exec <SUBDIR>`, ad es. `ms-playwright` |
| Modifica di `.vscode/tasks.json`, `launch.json` | ⚠️ Consentito, rischio noto | Confine di fiducia dell'IDE. Vedi [SECURITY.md](https://github.com/navikt/cplt/blob/main/SECURITY.md) per le mitigazioni |
| Lettura/scrittura di `~/.copilot` (auth, impostazioni) | ✅ Consentito | Include `file-map-executable` per `keytar.node`, `pty.node`, `computer.node` |
| Scrittura di `~/.copilot/pkg` (moduli nativi) | 🔒 Bloccato dal kernel | Impedisce la persistenza tramite sostituzione di moduli nativi |
| Variabili d'ambiente | 🔒 Sanitizzate + rafforzate | Passa solo una allowlist sicura. Script del ciclo di vita bloccati. `--pass-env VAR` ne ripristina una |
| Lettura di `~/.config/gh/hosts.yml` + `config.yml` | ✅ Consentito (sola lettura) | Solo questi due file. Il resto di `.config/gh` è bloccato |
| Lettura di `~/.config/mise` | ✅ Consentito (sola lettura) | Versioni dei tool e PATH, nessun segreto |
| Lettura di `~/.gitconfig`, `~/.config/git/config` | ✅ Consentito (sola lettura) | Un symlink dotfiles viene seguito fino al suo target, quindi un `~/.gitconfig` stowed funziona |
| Lettura di `~/.git-credentials` | 🔒 Bloccato dal kernel | `credential.helper = store` conserva qui i token in chiaro. Nessun `--allow-read` lo riapre, come `~/.netrc`. **Linux:** una concessione su un *antenato* (`$HOME` stesso) lo espone comunque, perché Landlock non può negare un sottopercorso all'interno di un albero consentito |
| Lettura degli hook git globali (`core.hooksPath`) | ✅ Consentito (sola lettura, scrittura negata) | Rilevato automaticamente. Deve trovarsi sotto `$HOME` con profondità ≥3. Le scritture sono esplicitamente bloccate |
| Firma di commit/tag (`commit.gpgsign`, `tag.gpgsign`) | 🔒 Disabilitata | Le chiavi private in `~/.ssh` e `~/.gnupg` sono bloccate, quindi la firma è disabilitata tramite un override di variabile d'ambiente |
| Lettura di `~/Library/Application Support/Microsoft` | ✅ Consentito (sola lettura) | Device ID per la telemetria |
| Accesso al Portachiavi macOS | ⚠️ Consentito (lettura+scrittura) per gli agenti che vi memorizzano l'auth | La concessione non può essere limitata a una singola voce, quindi raggiunge ogni voce del portachiavi che l'agente può sbloccare. Attiva `sandbox.keychain_substitute` (SPERIMENTALE, disattivato per impostazione predefinita) per eliminarlo nelle esecuzioni in cui l'agente può autenticarsi senza — `CLAUDE_CODE_OAUTH_TOKEN` per Claude Code, un file token di fallback esistente per Antigravity. Vedi [SECURITY.md](https://github.com/navikt/cplt/blob/main/SECURITY.md#keychain-access-is-all-or-nothing) |
| Rete in uscita (porta 443) | ✅ Consentito | Ogni altra porta è bloccata. Aggiungi extra con `--allow-port` |
| Uscita verso localhost | 🔒 Bloccato dal kernel (macOS), ⚠️ basato su porta su Linux | Impedisce l'accesso ai servizi locali. L'ingresso funziona ancora per il proxy. **Linux:** le regole di Landlock sono solo numeri di porta e non distinguono `localhost:443` da `remote:443`, quindi un servizio locale su una porta consentita è raggiungibile e non esiste un deny specifico per localhost. Usa `--with-proxy` per la protezione SSRF, vedi [Limitazioni Linux](https://github.com/navikt/cplt/blob/main/docs/security.md#linux) |
| SSH agent (unix socket) | 🔒 Bloccato dal kernel (macOS), ⚠️ solo-env su Linux | Impedisce la firma di operazioni git o SSH verso host. **Linux:** la `connect()` su unix socket non è controllata, quindi l'unica barriera è il `SSH_AUTH_SOCK` trattenuto e un agente che lo imposta da sé può usare le chiavi caricate. `bwrap` nasconde il socket OpenSSH standard sotto `/tmp`, ma non un agente gnome-keyring/gcr o systemd sotto `$XDG_RUNTIME_DIR`. Vedi [Limitazioni Linux](https://github.com/navikt/cplt/blob/main/docs/security.md#linux) |
| Strumenti di sviluppo (`~/.cargo`, `~/.gradle`, `~/.m2`, `~/.sdkman`, `~/.jenv`, `~/.pyenv`, `~/.konan`, ecc.) | ✅ Consentito (lettura+scrittura per le cache) | Solo le directory che esistono su disco. Ristrette a runtime da ciò che rileva `cplt doctor` |
| File di credenziali dei registry (`~/.m2/settings.xml`, `~/.gradle/gradle.properties`, `~/.cargo/credentials`) | 🔒 Bloccato dal kernel su macOS. Su Linux la directory padre del tool resta leggibile | Esegui l'override con `--allow-read`. Vedi [Registry privati](https://github.com/navikt/cplt/blob/main/docs/known-impacts.md#private-registries) |
| Lettura di `~/.npmrc` | 🔒 Bloccato dal kernel (entrambe le piattaforme) | Esegui l'override con `--allow-read`. Rompe yarn 1, vedi [yarn 1](https://github.com/navikt/cplt/blob/main/docs/known-impacts.md#yarn-1-and-unreadable-home-rc-files) |
| Codice sorgente Go (`~/go/src`) | 🔒 Bloccato dal kernel | Solo `~/go/bin` e `~/go/pkg` sono leggibili |
| Lettura di `~/.ssh`, `~/.gnupg`, `~/.aws`, `~/.azure` | 🔒 Bloccato dal kernel | |
| Lettura di `~/.kube`, `~/.docker`, `~/.nais` | 🔒 Bloccato dal kernel | |
| Lettura di `~/.password-store`, `~/.terraform.d` | 🔒 Bloccato dal kernel | |
| Lettura di `~/.config/gcloud`, `~/.config/op` | 🔒 Bloccato dal kernel | I singoli file sono sovrascrivibili con `--allow-read`. Vedi [Credenziali cloud](https://github.com/navikt/cplt/blob/main/docs/known-impacts.md#cloud-credential-directories) |
| Lettura o scrittura di `~/.config/cplt`, `~/.nav-pilot` | 🔒 Bloccato dal kernel | Stato del tool che decide cosa può fare il *prossimo* avvio. `~/.config/cplt` non è sovrascrivibile come intero sottoalbero; dentro `~/.nav-pilot`, un percorso nominato resta concedibile così un payload agentpakke fissato può essere letto |
| Lettura di `~/.netrc`, `~/.pypirc`, `~/.vault-token` | 🔒 Bloccato dal kernel | Non sovrascrivibile su entrambe le piattaforme. Nominarne uno in `allow.read` è un errore all'avvio |
| Lettura di `~/.gem/credentials` | 🔒 Bloccato dal kernel | Non sovrascrivibile su entrambe le piattaforme. Nominarne uno in `allow.read` è un errore all'avvio |
| Operazioni distruttive della CLI `gh` (merge, delete, release) | 🔒 Controllato dai comandi (attivo per impostazione predefinita) | Disattiva con `--no-gh-guard`. Vedi [gh guard](https://github.com/navikt/cplt/blob/main/docs/gh-guard.md) |
| `git push` verso il branch predefinito | 🔒 Controllato dai comandi (attivo per impostazione predefinita) | Blocca i push verso `main`/`master`; i push su feature-branch funzionano ancora. `protect_default_branch_only = false` blocca ogni push, `git_guard.mode = "warn"` avvisa soltanto, `--no-git-guard` disattiva |
| Ereditarietà dei processi figli | ✅ Tutte le restrizioni si applicano ai sottoprocessi | |
Quella tabella è un riepilogo. La sandbox consente anche l'accesso ai file di sistema (certificati SSL, `/etc/hosts`), alle directory temporanee (lettura e scrittura, nessuna esecuzione) e ai percorsi degli strumenti di sistema (`/usr/bin`, `/opt/homebrew`). Esegui `cplt --print-profile` per le regole SBPL complete.
Per il modello di sicurezza completo, l'analisi delle minacce e la strategia di test, leggi [SECURITY.md](https://github.com/navikt/cplt/blob/main/SECURITY.md).
## Come si confronta cplt
### La sandbox di Codex CLI
| Area | cplt | Sandbox di Codex CLI |
| --- | --- | --- |
| Controllo della rete in uscita | Proxy CONNECT con liste di domini consentiti/bloccati | Nessun filtraggio a livello di dominio |
| Gestione dell'ambiente | Allowlist più iniezione di env rafforzata | Modello di pass-through più basilare |
| Protezione dei file segreti | Pattern di deny come `.env*`, `.pem`, `.key` dentro il repo | Accesso principalmente limitato alla directory |
| Policy del repo | [`.cplt.toml`](https://github.com/navikt/cplt/blob/main/docs/configuration.md#per-repo-configuration-cplttoml) con un flusso esplicito di fiducia/approvazione | Nessun file di policy a livello di repo |
| Supporto agenti | Copilot, OpenCode, Gemini CLI, Antigravity CLI, Pi, Claude Code, goose, DeepSeek Harness, o shell | Solo Codex |
cplt non è più forte ovunque. Codex CLI ha oggi l'isolamento dei namespace Linux, ed espone già modalità sandbox esplicite come read-only e workspace-write. cplt non ha ancora quella matrice di modalità.
### Sandbox basate su Docker
| Area | cplt | Sandbox basata su Docker |
| --- | --- | --- |
| Tempo di avvio | Quasi istantaneo per l'uso normale della CLI | Avvio del container di solito più lento |
| Controllo della rete | Filtraggio in uscita per richiesta tramite proxy | Di solito accesso alla rete tutto-o-niente |
| Controlli sui file | Regole per percorso e per pattern | Controlli per mount |
| Requisiti dell'host | Singolo binario | Richiede il daemon Docker |
| Adattabilità su laptop aziendale | Funziona dove Docker non è disponibile o è limitato | Spesso bloccato dalla policy locale |
Docker offre comunque un isolamento più forte in alcuni ambienti, specialmente se vuoi un filesystem e un namespace di processi completamente separati. cplt scambia questo con una configurazione più leggera e un'integrazione più stretta con la macchina su cui già sviluppi.
### Permessi della modalità agente di VS Code
Strumenti come la modalità agente di VS Code si affidano principalmente ai permessi dell'interfaccia utente. cplt applica le sue restrizioni nel kernel, quindi l'agente non può aggirarle con un prompt o un'istruzione modificata. Questo conta soprattutto per gli agenti CLI e l'esposizione delle credenziali:
- cplt funziona fuori dall'IDE
- le variabili d'ambiente sono filtrate prima che l'agente si avvii
- i file sensibili possono essere bloccati anche quando risiedono dentro il repo
- le stesse restrizioni si applicano ai processi figli
### La sandbox di Claude Code (Anthropic Sandbox Runtime)
[Anthropic Sandbox Runtime](https://github.com/anthropic-experimental/sandbox-runtime) (`srt`) è il livello di sandboxing usato da Claude Code. Stesso approccio di alto livello di cplt, Seatbelt di macOS più applicazione a livello kernel su Linux più un proxy HTTP, implementazione diversa.
| Area | cplt | Anthropic srt |
| --- | --- | --- |
| Linguaggio / distribuzione | Singolo binario Rust | Pacchetto Node.js + npm + dipendenze esterne |
| Backend Linux | Landlock LSM (nessuna dipendenza, nessun namespace) | bubblewrap (container tramite user namespace) |
| Filtraggio dell'ambiente | Allowlist rigorosa + deny per suffisso (`_TOKEN`, `_SECRET`) | Eredita l'intero env del padre (i segreti passano) |
| Protezione delle directory di credenziali | Oltre 15 directory negate per impostazione predefinita | L'utente deve configurare manualmente |
| Protezione dal DNS rebinding | ✅ IP post-DNS verificato rispetto agli intervalli privati | ❌ Non implementata |
| Proxy di rete | HTTP CONNECT + allow/block di domini | HTTP + SOCKS5 + MITM TLS sperimentale |
| Git via SSH | Bloccato a livello kernel su macOS (socket dell'agente negato); su Linux viene trattenuto solo `SSH_AUTH_SOCK` | Inoltrato tramite SOCKS5 |
| Script dei package manager | Bloccati per impostazione predefinita (`npm_config_ignore_scripts`) | Non bloccati |
| Supporto agenti | Copilot, OpenCode, Gemini, Antigravity, Pi, Claude Code, goose, DSH, Shell | Claude Code |
| Configurazione | TOML (globale + per-repo) | JSON (solo globale) + aggiornamenti live con `--control-fd` |
| API di libreria | ❌ Solo binario | ✅ Libreria TypeScript incorporabile |
cplt è più sicuro out of the box: filtraggio dell'env, protezione delle credenziali, controlli sul DNS rebinding, blocco degli script del ciclo di vita. srt è più flessibile: SOCKS5, ispezione TLS, callback per richiesta, incorporamento come libreria. La scelta del backend Linux conta. bwrap necessita di workaround su Ubuntu 24.04+ a causa delle restrizioni AppArmor sugli userns, mentre Landlock richiede kernel 5.13 o successivo ma non ha dipendenze esterne.
### La sandbox propria di GitHub Copilot CLI
Copilot CLI è dotato di una sandbox locale da giugno 2026, inclusa nella
licenza standard. Esegue i comandi shell tramite Microsoft MXC con accesso
ristretto a filesystem, rete e sistema, su macOS, Linux e Windows.
`/sandbox enable` la attiva.
Se questo ti basta, usala. Non costa nulla in più e funziona su Windows,
cosa che cplt non fa.
Due cose che non fa.
La policy risiede con l'amministratore, non con il repository. Le aziende impostano
la policy della sandbox tramite Intune o un altro MDM. Nulla sta accanto al codice,
quindi una regola che conta per un repository non può seguirlo fino a un contributore,
alla CI, o a un laptop che l'MDM non gestisce. In cplt la policy è
`.cplt.toml` nel repository. I revisori vedono le modifiche nella pull
request, e il file può stringere la configurazione di uno sviluppatore ma mai
allentarla.
Confina il processo, non ciò che il processo fa con le credenziali che
possiede. Le schede `/sandbox` coprono il filesystem, la rete e le capacità
di sistema, e dentro un repository Git all'agente è concesso in lettura e scrittura
su `.git` per impostazione predefinita. Un agente in sandbox ha comunque il tuo token `gh` e il tuo
accesso in push. Fare il push di un branch, fare il merge di una pull request e cancellare un
repository sono tutte chiamate API ben formate da un client autorizzato, e una
regola di filesystem o di rete non ha opinioni al riguardo. cplt avvolge invece `git` e
`gh`. L'agente esegue commit, branch e rebase liberamente. `gh pr merge`,
`gh repo delete` e `gh release create` sono bloccati per impostazione predefinita. Lo è anche
`git push` verso `main`/`master`; i push su feature-branch funzionano ancora, perché
`protect_default_branch_only` è attivo. Impostalo su `false` per bloccare ogni push, o
`git_guard.mode = "warn"` per avvisare soltanto.
Eseguire entrambi è ragionevole. MXC confina il processo. Le guardie decidono cosa
l'agente può fare con le credenziali che possiede.
### Lacune oneste
- macOS ha oggi l'applicazione a livello di file più forte. La copertura Linux sta migliorando ma non è identica.
- cplt non offre ancora semplici preset di policy read-only / workspace-write / full-access.
- Se vuoi un isolamento completo tramite container, cplt non cerca di sostituire Docker.
## Installazione
### Homebrew (consigliato)```bash
brew install navikt/tap/cplt
mise use -g 'github:navikt/cplt@'
mise seleziona l'asset di rilascio corretto per la tua piattaforma e verifica
la sua attestazione di provenienza della build.
Fissa la versione. Le nostre stringhe di versione non sono semver comparabili — contengono
zeri iniziali e due trattini — quindi `mise latest` può risolvere a una release
più vecchia rispetto a quella più recente ([navikt/copilot#818](https://github.com/navikt/copilot/issues/818)).
### apt (Debian/Ubuntu, consigliato su Linux)
[navikt/apt](https://navikt.github.io/apt/) è un archivio firmato servito tramite
GitHub Pages, che contiene cplt e nav-pilot per 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
È un semplice mirror del repository apt che rispecchia le nostre release, non un pacchetto di distribuzione con un proprio maintainer. Il suo job di pubblicazione viene eseguito ogni ora e preleva il .deb più recente dalla release più recente di ogni tool, quindi una release creata pochi minuti fa impiega fino a un'ora per diventare installabile in questo modo.
Il pacchetto posiziona il binario in /usr/bin/cplt, e da quel momento gli aggiornamenti passano da sudo apt upgrade. cplt update rifiuta di toccare un'installazione apt e rimanda invece a sudo apt upgrade: sostituire il binario alle spalle di dpkg verrebbe annullato dalla successiva esecuzione di apt.
Senza l'archivio, lo stesso .deb è un asset di 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
Per distribuzioni che non sono derivate da Debian, e per la CI:```bash
curl -fsSL https://raw.githubusercontent.com/navikt/cplt/main/install.sh | bash
Opzioni:```bash
curl -fsSL ... | bash -s -- --version 2026.05.05-174753-75bae5b
curl -fsSL ... | bash -s -- --dir ~/.local/bin
curl -fsSL ... | bash -s -- --no-brew
### Download dalle release
Scarica l'ultima build per la tua piattaforma da [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/
Ogni binario di release porta una attestazione di provenienza della build. Verificala:```bash gh attestation verify cplt -o navikt
### Compilazione dal sorgente```bash
git clone https://github.com/navikt/cplt.git && cd cplt
cargo build --release
sudo cp target/release/cplt /usr/local/bin/
Oppure con mise:```bash mise run install
`mise run install` e le build manuali mettono cplt in `/usr/local/bin/cplt`. Se hai anche il build Homebrew in `/opt/homebrew/bin/cplt`, metti `/usr/local/bin` per primo nel `PATH` così il tuo build di sviluppo ha la precedenza:```bash
# Check which cplt is active
which cplt
# If it shows /opt/homebrew/bin/cplt, reorder your PATH:
export PATH="/usr/local/bin:$PATH"
Oppure esegui esplicitamente /usr/local/bin/cplt e salta del tutto la risoluzione del PATH.
cplt non ha un backend sandbox per Windows. L'enforcement è Apple Seatbelt su macOS e Landlock LSM su Linux, quindi non c'è nulla da eseguire nativamente su Windows. La via supportata è WSL2, dove cplt è una normale installazione Linux e la sandbox è applicata a livello di kernel. Ogni branch del kernel Microsoft compila CONFIG_SECURITY_LANDLOCK=y e elenca landlock per primo in CONFIG_LSM (config-wsl), distribuito dal kernel 5.15.57.1, e la riga di comando predefinita del kernel WSL non imposta alcun override lsm=.
In PowerShell, una volta:```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
Tutto ciò che segue viene eseguito **all'interno della distro** (`wsl`, o il profilo Ubuntu in Windows Terminal), non in 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
Non installare Copilot CLI sul lato Windows. Con l'interop attivo (impostazione predefinita), il PATH di Windows viene aggiunto a quello della distro, quindi un npm install -g @github/copilot eseguito sul lato Windows compare all'interno della distro come /mnt/c/Users/<user>/AppData/Roaming/npm/copilot. Si tratta di un'installazione Windows raggiunta tramite interop. Non può essere eseguita nella sandbox Linux, e lo shim npm esegue un node che la distro non avrà a meno che tu non ne abbia installato uno anche lì. Il sintomo era un errore di estrazione del runtime non correlato. Ora cplt identifica la causa quando risolve un agente sotto /mnt/<drive>/ e è in esecuzione sotto WSL, e cplt doctor lo segnala come controllo fallito invece che superato (#188). WSL viene rilevato dallo stato di proprietà del kernel, ovvero /run/WSL o il nome del kernel in /proc/sys/kernel/osrelease e /proc/version, non da WSL_DISTRO_NAME, che è assente sotto sudo e nelle unit systemd e che qualsiasi processo può impostare. Su una macchina Linux normale /mnt/c viene lasciato in pace. Lì è un normale punto di mount.
Quel controllo ha due limiti, entrambi deliberati. Si basa sulla radice di automount predefinita, quindi se l'hai spostata ([automount] root in /etc/wsl.conf) l'installazione lato Windows non viene riconosciuta e ottieni il vecchio errore, meno utile, con il percorso al suo interno. E disattivare l'interop impedisce al PATH di Windows di filtrare ma non smonta /mnt/c.
Kernel e ABI di Landlock. L'attuale WSL (2.7.x e successive) include Linux 6.18, che fornisce Landlock ABI 7 — tutto ciò che cplt usa tranne il diritto connect() sulle unix-socket, che richiede ABI 9 (kernel 7.1). Un'installazione ancora sulla linea kernel 6.6 ottiene ABI 3: le regole del filesystem vengono applicate, ma le regole sulle porte TCP (ABI 4), la restrizione ioctl (ABI 5) e lo scoping di signal/abstract-socket (ABI 6) non sono disponibili, e il filtraggio di rete ricade sul proxy CONNECT. wsl --update ti fa avanzare. cplt doctor stampa la versione del kernel e l'ABI che ha trovato, che è il controllo che conta sulla tua macchina.
Non disabilitare Landlock in
.wslconfig. Un[wsl2] kernelCommandLinecon una listalsm=che omettelandlock, o un[wsl2] kernel=personalizzato compilato senzaCONFIG_SECURITY_LANDLOCK, rimuove l'enforcement del kernel da cui dipende cplt, ecplt doctorsegnalerà Landlock come non disponibile.
Mantieni il progetto nel filesystem Linux. Lavora in ~/src/... all'interno della distro invece che in /mnt/c/Users/.... La guida di Microsoft stessa afferma che l'accesso ai file cross-OS è marcatamente più lento, e /mnt/c è servito tramite 9p per impostazione predefinita a partire da WSL 2.9.x (virtiofs è opt-in tramite [wsl2] virtiofs=true). Ancora più importante, non abbiamo verificato come Landlock applichi le regole su quel mount. Il kernel non documenta alcuna esclusione per filesystem supportati da rete o FUSE, solo pipe, socket e nsfs, e la suite di test di Landlock stessa esercita 9p e FUSE, quindi ci aspettiamo che funzioni. Nessuno qui l'ha confermato. Considera un progetto sotto /mnt/c come non provato anziché supportato.
Bubblewrap. Ubuntu 23.10+ blocca gli user namespace non privilegiati tramite kernel.apparmor_restrict_unprivileged_userns, il che rompe bwrap. Quel sysctl proviene da una patch del kernel Ubuntu che è assente dal kernel Microsoft, quindi il layer opzionale Bubblewrap dovrebbe funzionare su Ubuntu-sotto-WSL2. Questa è un'inferenza dal sorgente del kernel, non qualcosa che abbiamo eseguito. Se bwrap fallisce lì, segnalalo in #189. Il filtro seccomp di cplt stesso è un semplice programma BPF PR_SET_SECCOMP, che si sovrappone al filtro che WSL installa in ogni processo.
Non ancora verificato su una vera installazione WSL2. Verificato dal sorgente: Landlock è compilato e primo in
CONFIG_LSMsul kernel Microsoft; il rilevamento di/mnt/<drive>/, i segnali WSL che usa e il loro testo di errore; checplt doctorfallisce su un tale agente e stampa kernel + Landlock ABI; i requisiti 5.13+/6.7+; e cheinstall.shinstalla il binario di release Linux. Ancora non verificato da nessuno qui: come si comporta Landlock su/mnt/c, se Bubblewrap funziona sotto WSL2, le versioni esatte dei pacchetti fornite dalla tua release della distro, e la sequenza sopra dall'inizio alla fine. Se lo esegui, segnala cosa è successo realmente in #189.
Per impostazione predefinita ottieni la sandbox digitando cplt. Per far eseguire in sandbox anche il semplice copilot:```bash
cplt --shell-install
Rileva la tua shell, aggiunge l'alias al tuo file rc e stampa ciò che ha fatto. Eseguilo tutte le volte che vuoi, non aggiungerà duplicati.
`--agent` sceglie quale comando riceve l'alias, e ogni agente che cplt può avviare è disponibile:```bash
cplt --shell-install --agent opencode # 'opencode' runs sandboxed
cplt --shell-install --agent claude # and 'claude', alongside the others
Ogni installazione aggiunge al tuo file rc invece di sostituire ciò che è già presente, così puoi isolare in sandbox tutti gli agenti che utilizzi. Senza --agent ottieni copilot, che è ciò che il flag ha sempre installato.
| Shell | File modificato | Cosa viene aggiunto (per --agent opencode) |
|---|---|---|
| zsh (predefinita su 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 installa alias sia per antigravity che per agy, poiché entrambi i nomi avviano lo stesso agente.
Riavvia la shell o esegui il source del file per attivare.
Non esiste un alias per --agent shell: non c'è un binario shell da oscurare. Digita cplt --agent shell per una shell in sandbox, oppure cplt exec -- <command> per un singolo comando.
Se preferisci non usare --shell-install, aggiungi tu stesso la riga:```bash
eval "$(cplt --shell-setup --agent opencode)"
alias opencode 'cplt --agent opencode'
Lo stesso schema usato da mise, direnv e starship.
</details>
**Perché ogni alias nomina il suo agente.** `alias opencode=cplt` non farebbe ciò che sembra. Il semplice `cplt` sceglie il suo agente da `--agent`, poi dal file di configurazione, poi da qualunque cosa trovi nel PATH — e il rilevamento del PATH preferisce `copilot`. Digitare `opencode` metterebbe invece in sandbox Copilot, senza nulla sullo schermo che lo segnali. L'alias passa `--agent` in modo che il comando che digiti sia l'agente che ottieni.
**Perché un alias invece di un symlink?** cplt e Copilot CLI si installano nella stessa directory bin di Homebrew (`/opt/homebrew/bin/`), e solo un file chiamato `copilot` può risiedervi, quindi un symlink entrerebbe in conflitto. Un alias aggira il problema. Il vero binario `copilot` resta nel PATH dove cplt può trovarlo e incapsularlo, e l'alias reindirizza il tuo comando.
> **Nota:** cplt rifiuta di annidarsi. Se rileva di essere già in esecuzione all'interno di una sandbox (tramite la variabile d'ambiente `__CPLT_WRAPPED`), non si avvierà di nuovo. I sottocomandi di sola lettura come `--print-profile` e `cplt doctor` funzionano comunque all'interno di una sandbox esistente.
## Utilizzo```
cplt [OPTIONS] [-- <AGENT_ARGS>...]
Tutto ciò che segue -- viene passato direttamente al processo dell'agente (copilot, opencode, gemini, antigravity, pi, claude, goose, dsh o shell).
Un preset imposta una base per i cinque toggle principali della sandbox con un solo flag invece di un elenco. I flag individuali hanno comunque la precedenza sul preset, quindi --preset permissive --no-allow-tmp-exec fa esattamente ciò che dice. Impostabile anche come [sandbox] preset = "..." nella configurazione.
Matrice completa dei preset e ordine di risoluzione: docs/configuration.md.
La directory del progetto è lo spazio di lavoro scrivibile, più una ristretta allowlist necessaria per auth, runtime e tooling (vedi la tabella sopra). Il kernel blocca tutto il resto, chiavi SSH e credenziali cloud incluse.
cplt sanifica l'ambiente figlio per default. Solo le variabili sicure passano, e credenziali cloud, URL di database e token dei package vengono rimossi. Inietta anche variabili di hardening che bloccano gli script lifecycle di npm/yarn/pnpm (hook postinstall, il vettore di attacco della supply chain numero uno), disabilitano la firma di commit e tag git (poiché ~/.ssh e ~/.gnupg sono irraggiungibili dentro la sandbox), e disattivano la telemetria del tooling di sviluppo (DO_NOT_TRACK=1, NEXT_TELEMETRY_DISABLED=1, TURBO_TELEMETRY_DISABLED=1, CHECKPOINT_DISABLE=1 e altri).
Cosa passa:
Allowlist per prefisso con protezione dal suffisso segreto. Una variabile che corrisponde a un prefisso consentito come COPILOT_* o YARN_* viene comunque scartata se termina con un suffisso che porta segreti: _TOKEN, _AUTH, _SECRET, _SECRET_KEY, _KEY, _PASSWORD o _CREDENTIALS. Quindi COPILOT_DEBUG passa e COPILOT_API_KEY no.
Sempre bloccate: AWS_*, AZURE_*, NPM_TOKEN, DATABASE_URL, VAULT_TOKEN, SSH_AUTH_SOCK, variabili Docker, token CI e qualsiasi cosa non sia nell'allowlist.
| Flag | Cosa fa |
|---|---|
--pass-env <VAR> | Passa una variabile d'ambiente all'agente. Ripetibile |
--inherit-env | ⚠️ Pericoloso. Eredita l'intero ambiente del processo padre. Rimuove solo , , , . Solo per debug |
cplt rileva automaticamente gli strumenti installati e scrive regole di sandbox corrispondenti. In genere solo le directory che esistono su disco ricevono regole, quindi non ci sono percorsi fantasma. Su macOS, le directory delle app scrivibili vengono incluse quando rilevate anche se non esistono ancora, così possono essere create al primo utilizzo. Linux non può consentire una scrittura su un percorso inesistente, quindi lì la creazione deve avvenire fuori dalla sandbox.
Esegui cplt doctor per vedere se cplt funzionerà qui per il tuo agente, e cplt doctor --verbose per tutto ciò che ha rilevato sulla tua macchina.
Questi si traducono nei flag di sessione dell'agente stesso, quindi non serve un separatore --.
--continue e --resume sono mappati anche per OpenCode, Antigravity e Claude Code:
¹ Né OpenCode né Antigravity hanno un selettore interattivo di sessione, quindi un --resume da solo significa "continua l'ultima sessione". Claude Code ne ha uno, quindi viene mappato direttamente.
--remote e --name sono solo per Copilot. Pi e la modalità shell non ricevono alcuna traduzione, quindi tutti e quattro i flag vengono scartati per loro. L'auto-resume è un meccanismo separato: quando invochi cplt senza argomenti pass-through e senza flag di sessione, aggiunge --resume per te, e questo vale solo per Copilot.
Combinabili con i flag della sandbox e gli argomenti pass-through dopo --:```bash
cplt --resume=my-task # resume by name
cplt --remote --name my-task -- -p "fix tests" # remote + named + prompt
### Agenti
Scegline uno con `--agent <name>`, oppure rendilo predefinito con `cplt config set sandbox.agent <name>`. Copilot, OpenCode e Antigravity vengono rilevati automaticamente da `PATH` in quest'ordine quando non ne specifichi uno.
| Agente | Valore `--agent` | Rilevato automaticamente | Autenticazione |
| --- | --- | --- | --- |
| GitHub Copilot CLI | `copilot` | sì, priorità 1 | Token GitHub, dal Keychain o da `gh` |
| [OpenCode](https://opencode.ai/) | `opencode` | sì, priorità 2 | Abbonamento Copilot tramite `/connect`, oppure `--pass-env ANTHROPIC_API_KEY` |
| [Antigravity CLI](https://github.com/google-antigravity/antigravity-cli) | `antigravity`, alias `agy` e `agi` | sì, priorità 3 | Google OAuth nel browser |
| [Pi](https://github.com/earendil-works/pi) | `pi` | no | `--pass-env ANTHROPIC_API_KEY` e simili |
| [Claude Code](https://docs.anthropic.com/en/docs/claude-code) | `claude`, alias `cc` e `claude-code` | no | OAuth dell'abbonamento in `~/.claude` o nel Keychain, `CLAUDE_CODE_OAUTH_TOKEN` (rimuove la concessione del Keychain), oppure `--pass-env ANTHROPIC_API_KEY` |
| [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) | `dsh`, alias `deepseek` e `deepseek-harness` | no | `--pass-env DEEPSEEK_API_KEY`, oppure `$DSH_HOME/.env` (`~/.dsh/.env`) |
| La tua shell | `shell` | no | nessuna |
- **Pi, Claude Code, goose e DeepSeek Harness non vengono mai rilevati automaticamente.** `pi` e `dsh` sono nomi di binari generici che potrebbero entrare in conflitto con qualcos'altro sulla tua macchina, e Claude Code deve essere scelto intenzionalmente.
- **Le chiavi API di terze parti sono opt-in.** `ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, `GEMINI_API_KEY`, `OPENROUTER_API_KEY`, `ANTHROPIC_AUTH_TOKEN`, `CLAUDE_CODE_OAUTH_TOKEN` e le variabili di routing Bedrock/Vertex (`CLAUDE_CODE_USE_BEDROCK`, `AWS_BEARER_TOKEN_BEDROCK`, `CLAUDE_CODE_USE_VERTEX`, `ANTHROPIC_VERTEX_PROJECT_ID`, `GOOGLE_CLOUD_PROJECT`) non passano mai a meno che tu non le specifichi con `--pass-env`.
- **L'autenticazione tramite abbonamento non richiede variabili d'ambiente.** Il flusso device di `/connect` di OpenCode memorizza il suo token in `~/.local/share/opencode/auth.json`, e il token OAuth di Claude Code risiede in `~/.claude` (`.credentials.json` su Linux) o nel Keychain di macOS. Entrambi sono raggiungibili all'interno della sandbox, quindi cplt non ti avvisa di una chiave API mancante per nessuno dei due.
- **I flussi OAuth nel browser richiedono `--allow-browser`** quando appare una richiesta di accesso. Questo vale per Antigravity; ogni altro agente qui utilizza un flusso device che stampa un codice e un URL e non richiede un browser. Il flag consente all'agente di avviare qualsiasi applicazione al di fuori della sandbox e non può essere limitato a URL specifici, quindi attivalo per l'accesso e disattivalo subito dopo — vedi la [tabella dei flag](#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).
- **L'aggiornamento automatico di Claude Code è disabilitato** con `DISABLE_AUTOUPDATER=1`. Claude Code non ha un flag `--no-auto-update`, l'auto-aggiornamento all'interno della sandbox è un vettore di persistenza, e fallirebbe comunque contro percorsi di installazione in sola lettura.
- **`CLAUDE_CONFIG_DIR` è rispettato.** Quando è impostato, cplt concede quella directory invece di `~/.claude` e passa la variabile, così una radice di configurazione spostata continua a funzionare.
- OpenCode è [un client Copilot ufficialmente supportato](https://github.blog/changelog/2026-01-16-github-copilot-now-supports-opencode/), quindi il tuo abbonamento Copilot esistente funziona con `/connect` all'interno di OpenCode.
Le directory di configurazione per agente, l'uso del Keychain, i permessi di esecuzione e l'isolamento delle variabili d'ambiente sono in [SECURITY.md](https://github.com/navikt/cplt/blob/main/SECURITY.md#supported-agents).
### Supporto goose
cplt può eseguire in sandbox [goose](https://github.com/aaif-goose/goose), l'agente AI open-source (binario `goose`). Verificato 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
Note di sicurezza per goose:
--agent goose o imposta sandbox.agent = "goose" nella configurazioneANTHROPIC_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) sono riconosciute come suggerimenti di autenticazione e devono essere passate tramite --pass-env. goose legge , non . Qualsiasi provider al di fuori di questo sottoinsieme funziona comunque: assegna un nome alla sua variabile con cplt può eseguire in sandbox DeepSeek Harness (binario dsh), l'agent harness orientato ai plugin di DeepSeek. A monte viene distribuito come anteprima per sviluppatori e il suo stesso SAFETY.md afferma di non fare affidamento sui suoi controlli come unico confine, che è il caso per cui esiste cplt.```bash
cplt --agent dsh
cplt --agent dsh --pass-env DEEPSEEK_API_KEY
cplt config set sandbox.agent dsh
**Note di sicurezza per DSH:**
- **Non rilevato automaticamente**: selezionalo con `--agent dsh` (alias `deepseek`, `deepseek-harness`) oppure imposta `sandbox.agent = "dsh"`. `dsh` è un nome di comando breve e generico che potrebbe appartenere a qualcos'altro sulla tua macchina
- **Disattiva la sandbox interna di DSH dentro cplt**: DSH avvolge ogni chiamata agli strumenti shell e file nel proprio sandbox di processo — Seatbelt su macOS, bwrap o Landlock su Linux. Nessuno dei due si annida dentro cplt. macOS non supporta chiamate `sandbox-exec` annidate (la stessa limitazione che fa disattivare a cplt la sandbox interna di Gradle, vedi [Limitazioni](#limitations)), e bwrap costruisce il suo namespace con `unshare`, che il filtro seccomp di cplt nega. In ogni caso cplt è il confine che applica le restrizioni, quindi scegli il preset di permessi `danger-full-access` fornito da DSH per le sessioni in sandbox. Lascia attivo il runner interno e le chiamate agli strumenti falliranno con un errore del sandbox runner anziché un errore del task
- **Una sola radice home, e cplt segue l'override**: DSH mantiene sessioni, impostazioni, cache e profili sotto `$DSH_HOME` (`~/.dsh` per impostazione predefinita). `DSH_HOME` è nella allowlist delle variabili d'ambiente, quindi il processo figlio risolve la stessa radice che cplt concede. Un valore che punta a una radice di sistema o alla tua directory home viene rifiutato prima dell'avvio, lo stesso veto a cui va incontro `CLAUDE_CONFIG_DIR`
- **Protezione della persistenza sull'host**: `$DSH_HOME/cordis.patch.yml`, l'overlay a livello di home che il Loader legge all'avvio, è protetto da scrittura. `$DSH_HOME/profiles/` rimane scrivibile perché DSH riscrive la radice di inclusione `cordis.yml` di ogni profilo a ogni avvio, quindi un `cordis.patch.yml` per profilo e i plugin installati sono un residuo documentato — fai le modifiche ai profili e a `dsh plugin` fuori da cplt, e avvia sempre `dsh` tramite cplt così qualunque cosa impiantata viene comunque eseguita in sandbox
- **Domini predefiniti**: solo `deepseek.com`. L'adattatore `dsh-llm-deepseek` fornito punta per impostazione predefinita a `https://api.deepseek.com`. Punta `DEEPSEEK_BASE_URL` a un gateway e dovrai aggiungere il dominio di quel gateway tramite `allowed_domains`
- **Autenticazione**: passa la chiave con `--pass-env DEEPSEEK_API_KEY`, oppure conservala in `$DSH_HOME/.env`. Una chiave salvata tramite l'interfaccia dei modelli di DSH finisce in `$DSH_HOME/.credentials.yaml`, dentro la stessa radice scrivibile. Il Keychain di macOS è negato, quindi `git push` su HTTPS richiede il token di `gh` in `hosts.yml` oppure `--pass-env GH_TOKEN`
### Modalità shell
Esegui una semplice shell in sandbox senza alcun agente AI e con le stesse restrizioni. Utile per testare strumenti di build, fare debug di problemi della sandbox, o semplicemente lavorare con attenzione manualmente.```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
Le stesse regole di negazione predefinita si applicano: isolamento del filesystem, restrizioni di rete, sanificazione delle variabili d'ambiente. Le directory di configurazione della shell (variabili e cronologia di fish, cronologia di zsh) rimangono scrivibili.
Per un singolo comando, cplt exec è più pulito di cplt --agent shell -- -c 'cmd'.
Esegui qualsiasi comando all'interno della sandbox senza avviare un agente. Nessun banner di avvio, nessun prompt di conferma, quindi è adatto a script, pipe e alias della 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"
Ogni flag di primo livello di `cplt` si applica: `--project-dir`, `--allow-read`, `--deny-path`, `--with-proxy`, `--pass-env` e gli altri. Aggiungi `--no-quiet` per vedere il riepilogo completo della configurazione della sandbox prima che il comando venga eseguito.
### Esempi```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 configurazione avviene a due livelli: globale, per le preferenze dello sviluppatore, e per-repo, per le policy del team.```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` è l'editor interattivo, con viste Effective, Global e Repository, ricerca, modifiche in staging e una conferma esplicita prima di salvare qualsiasi cosa relativa alla sicurezza. `cplt config` rimane l'interfaccia stabile non interattiva per script e CI. Le proposte del repository vengono comunque committate e approvate separatamente con `cplt trust`. L'editor non le committa né le approva automaticamente.
La precedenza segue i flag CLI, poi il file di configurazione globale in `~/.config/cplt/config.toml`, poi i valori predefiniti integrati. La configurazione per-repo in `.cplt.toml` è un livello separato anziché un gradino di quella scala: `[deny]` restringe incondizionatamente, e i permessi approvati sono solo additivi, quindi un repo può abilitare una funzionalità ma non può mai disattivare qualcosa impostato da un flag CLI o dalla configurazione globale.
Un `.cplt.toml` nella radice del repository contiene la policy del team:```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 legge da git HEAD, quindi l'agente non può manomettere la propria policy a metà sessione, e le approvazioni di fiducia sono vincolate al contenuto del file. Un .cplt.toml non committato non concede nulla finché non viene committato, sebbene le sue chiavi [deny] si applichino comunque. In CI e negli script, dove nessuno può rispondere a un prompt, --accept-repo-config approva le proposte del file committato per quella singola esecuzione senza persistere alcuna fiducia. cplt init ne scrive uno per te rilevando il tooling del progetto:```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
Conosce JVM (Gradle/Maven), Node.js, Docker, Python, Rust, Go, Playwright, Spring Boot, Ktor, TestContainers, Next.js, Vite, Flyway, Cypress e i segreti d'ambiente da `.env.example`. Le autorizzazioni pericolose escono dal generatore con un avviso di rischio allegato. `--global` esamina invece le cose a livello di macchina: browser Playwright, firma GPG, credenziali del registry, agenti alternativi.
Alcune chiavi sono solo globali e vengono rifiutate da `.cplt.toml` perché sono specifiche della macchina o una preferenza locale: `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 ogni chiave `[gh_guard]` e `[git_guard]`.
Dettagli completi, incluso il modello di fiducia, le regole di espansione dei percorsi e il riferimento completo del file di configurazione: [docs/configuration.md](https://github.com/navikt/cplt/blob/main/docs/configuration.md).
## Architettura```
┌──────────────────────────────────┐
│ 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 │
└──────────────────────────────────┘
Il modello di sicurezza è un filesystem deny-by-default con enforcement a livello di kernel. Su macOS, e su Linux con kernel 6.7+ (Landlock ABI v4), la rete è limitata alla porta 443 per impostazione predefinita, con --allow-port per le eccezioni. Su kernel Linux più vecchi il proxy CONNECT fornisce invece quella restrizione, ed è per questo che è abilitato per impostazione predefinita. L'accesso all'agente SSH e l'outbound verso localhost sono bloccati nel kernel su macOS. Su Linux nessuno dei due lo è: le regole Landlock basate sulle porte non riescono a distinguere localhost da un host remoto, e la connect() su unix socket non è gestita da Landlock sotto il kernel 7.1, quindi a parte i socket che bubblewrap maschera, il SSH_AUTH_SOCK trattenuto è l'unica cosa che si frappone tra l'agente e le tue chiavi caricate. Il generatore di profili rileva il tuo ambiente (cplt doctor --verbose mostra gli stessi risultati della sonda) ed emette regole solo per le directory degli strumenti che esistono effettivamente su disco. Meno regole, sandbox più stretta.
sandbox-execpre_exec (kernel 5.13+, filtraggio delle porte TCP su 6.7+)Internals e struttura dei moduli: docs/architecture.md. Modello di minaccia, livelli di difesa e lacune dichiarate: SECURITY.md.
Un solo binario, dipendenze minime, nessun servizio runtime, nessuna telemetria. Tre livelli di difesa, con confini chiari tra loro:
Da cosa protegge cplt:
.env): bloccata dal kernel.git/hooks è in scrittura negata a livello di kernel su macOS. Su Linux, con Landlock e senza Bubblewrap, rimane scrivibile, e il git lato genitore di cplt viene poi eseguito con core.hooksPath=/dev/null così non esegue mai un hook impiantato, anche se un git che esegui tu stesso lo farà comunquePNPM_HOME, ~/.deno/bin, ~/.bun/bin): la scrittura è concessa lì così pnpm add -g e simili funzionano nella sandbox, quindi un agente può lasciare dietro un binario che una successiva shell raccoglierà dal tuo PATHDa cosa cplt non protegge:
sandbox.keychain_substitute può scambiare la concessione dove un agente ha un'altra credenzialeLe nostre priorità, in ordine: corretto (ogni affermazione è testata, ogni caso limite ha un CVE o un riferimento di ricerca), trasparente (SECURITY.md non nasconde nulla), semplice (un binario, zero configurazione richiesta, default sensati) e utile (togliersi di mezzo e lasciare che l'agente lavori, in sicurezza).
Altro: docs/security.md · SECURITY.md
Il proxy è attivo per impostazione predefinita. Tutto il traffico in uscita da Copilot CLI, gh e curl passa attraverso un proxy CONNECT su localhost tramite HTTP_PROXY/HTTPS_PROXY e NODE_USE_ENV_PROXY=1. Ascolta su una porta effimera assegnata dal sistema operativo, quindi nulla va in conflitto. Ottieni logging delle connessioni in tempo reale, blocco dei domini, allowlisting dei domini, un log di audit persistente e la stessa politica sulle porte applicata dalla sandbox (443 più qualsiasi cosa in 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>` scrive l'insieme osservato con un dominio per riga, e
`--proxy-upstream-no-proxy <HOST>` elenca gli host da raggiungere direttamente invece che attraverso
l'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"
La modalità forzata tramite proxy è opt-in. Limita l'egress del kernel alla porta del proxy, così un socket aperto direttamente, o un env -u HTTPS_PROXY, non può scivolare oltre. L'applicazione è completa su macOS, che si fissa a localhost:<proxy_port>. Su Linux blocca il TCP diretto su :443, e una regola seccomp consente solo SOCK_STREAM con protocollo 0 o IPPROTO_TCP per AF_INET/AF_INET6, quindi anche UDP, raw, SCTP e DCCP sono chiusi — al costo di qualsiasi cosa apra un socket di quel tipo, non solo il codice che invia UDP. Ciò che rimane è un residuo basato sulla porta, evil.com:<proxy_port>, fino a #114.
Al di fuori della modalità forzata tramite proxy, Linux non limita UDP. I diritti di rete di Landlock sono solo TCP fino all'ABI v10, cplt gestisce solo AccessNet::ConnectTcp, e la regola seccomp sopra non viene deliberatamente applicata — negare SOCK_DGRAM lì romperebbe getaddrinfo(3), e quindi tutto il DNS, per ogni strumento non proxato. L'UDP in uscita verso qualsiasi host, il bind UDP in entrata, il DNS tunnelling e QUIC/HTTP-3 sono quindi non mediati in modalità predefinita, e il proxy CONNECT trasporta solo TCP, quindi nulla di tutto ciò appare nel log del proxy. macOS limita UDP in modalità predefinita ma non lo instrada nemmeno: remote ip "*:443" copre UDP, quindi QUIC/HTTP-3 sulla 443 esce senza toccare il proxy anche lì. Sotto proxy.forced il log del proxy è un record completo dell'egress su macOS. Su Linux è completo tranne il residuo evil.com:<proxy_port> sopra, che non attraversa il proxy e quindi non appare nel suo log.
Entrambe le liste corrispondono allo stesso modo: example.com copre il dominio esatto e ogni sottodominio, la corrispondenza è case-insensitive e i punti finali vengono rimossi. I file blocklist e allowlist vengono riletti ogni cinque secondi, quindi puoi modificarli a caldo. Il traffico localhost bypassa il proxy tramite NO_PROXY e non appare mai nel log di audit. --proxy-timeout <SECONDS> limita le letture di richieste e header (predefinito 60) e non chiude i tunnel CONNECT stabiliti, che possono restare inattivi fino a un'ora.
Ogni flag del proxy, dettaglio del filtraggio dei domini, concatenamento con proxy aziendali upstream e il formato del log delle connessioni: docs/proxy.md.
Abilitale e cplt intercetta gh e git tramite script wrapper in $PATH:
Questo è il Livello 3, una barriera soft. Impedisce a un agente compiacente di fare qualcosa di distruttivo per errore. Per un confine rigido, affidati al sandbox del kernel e alla protezione dei branch lato server.
Con la guardia gh attiva, cplt mette anche in cache il token GitHub all'avvio e lo serve una volta tramite il callback gh auth token, poi elimina la cache. Questo riduce le fughe accidentali e basate sull'ambiente. Non è un confine contro un agente ostile, perché la cache vive nel TMPDIR dell'agente stesso e un agente che la legge prima del consumatore legittimo ottiene comunque il token. SECURITY.md contiene la dichiarazione completa su block_auth_token.
Comportamento completo: docs/gh-guard.md · docs/git-guard.md
Il sandbox blocca alcuni flussi di lavoro di proposito. Quelli comuni e le relative soluzioni:
Playwright Chromium richiede cplt config set sandbox.allow_cache_exec ms-playwright, e Chromium deve essere eseguito senza il proprio sandbox annidato. Su macOS i suoi helper non possono inizializzare un secondo sandbox Seatbelt dentro cplt (forbidden-sandbox-reinit); su Linux il filtro seccomp di cplt blocca le syscall dei namespace di cui quel sandbox ha bisogno. Playwright come libreria si avvia già con --no-sandbox, e quello stesso opt-in imposta PLAYWRIGHT_MCP_SANDBOX=false per Playwright MCP, che altrimenti lo riattiverebbe. Qualsiasi altro launcher Chromium necessita di --no-sandbox esso stesso. cplt rimane il confine di applicazione a livello kernel, ma un renderer compromesso riceve quindi il profilo Playwright completo di cplt invece del profilo figlio più ristretto di Chromium. Vedi Cache exec e SECURITY.md.
Il commit Git funziona per ogni agente; se git push funziona su HTTPS dipende dall'agente. Tre prerequisiti: usa remote HTTPS invece di SSH (git remote set-url origin https://github.com/org/repo.git, oppure riscrivi globalmente con git config --global url."https://github.com/".insteadOf "[email protected]:"), esegui gh auth login una volta fuori dal sandbox, ed esegui gh auth setup-git se l'helper delle credenziali non è ancora configurato. Il push esegue quindi gh auth git-credential, che necessita di un token che gh possa raggiungere dall'interno del sandbox — questo varia per agente, vedi Git workflow. I push verso il branch predefinito e tutti i force push vengono rifiutati dalla guardia git per impostazione predefinita; fai push di un feature branch. Il socket dell'agente SSH è bloccato perché sblocca ogni chiave caricata e può autenticarsi verso qualsiasi host, mentre l'helper delle credenziali gh è limitato a GitHub.
La JVM è proxy-aware, quindi un repository Maven interno su un IP privato ora necessita di essere consentito. cplt inietta http(s).proxyHost/proxyPort in JAVA_TOOL_OPTIONS, quindi la risoluzione delle dipendenze di Gradle e Maven passa attraverso il proxy CONNECT e appare nel log del proxy invece di bypassarlo. La guardia SSRF del proxy rifiuta quindi un Nexus o Artifactory interno che si risolve in spazio di indirizzi privato, esattamente come già fa per curl, npm e pip. Aggiungi il suo nome DNS a proxy.allow_private_domains. Un URL di repository scritto come IP letterale nudo (https://10.20.30.40/repository/maven-public/) non può essere consentito da nessuna chiave — quel controllo viene eseguito prima che la allow list venga consultata — quindi un tale repository necessita di un nome DNS. I fork dei plugin WorkerExecutor, e un daemon Gradle avviato fuori da cplt e riutilizzato all'interno, non sono proxati. Vedi Repository Maven/Gradle interni su IP privati.
Gradle 9+ esegue il proprio sandbox annidato, e cplt lo disattiva. Da Gradle 8.8 il daemon si avvolge in sandbox-exec (controllato da GRADLE_MACOS_SANDBOX, in precedenza la proprietà org.gradle.daemon.sandbox). macOS non supporta chiamate sandbox-exec annidate, quindi il sandbox interno fallisce con "Operation not permitted" sulle operazioni sui socket. cplt inietta GRADLE_MACOS_SANDBOX=off, poiché fornisce già sandboxing a livello kernel. Questo è un problema upstream noto che colpisce qualsiasi strumento che avvolge Gradle in un sandbox esterno. Sovrascrivi con --pass-env GRADLE_MACOS_SANDBOX se vuoi davvero il sandbox di Gradle stesso.
Copilot CLI 1.0.83 esegue il proprio sandbox annidato, e cplt lo disattiva. Su Linux quel sandbox costruisce un network namespace — slirp4netns, iptables, /dev/net/tun — e il filtro seccomp di cplt nega la unshare che richiede. cplt imposta anche HTTP_PROXY/HTTPS_PROXY, il che nella 1.0.83 mette un sandbox Linux sul percorso di egress del proxy che tu l'abbia richiesto o meno, quindi i due collidono a ogni avvio. Sintomo: [cplt] Starting Copilot in sandbox... e poi nulla. cplt inietta l'opt-out di Copilot stesso, COPILOT_CLI_SANDBOX_SUPPORT_OVERRIDE=unsupported; Copilot si fa da parte per la sessione, lo dichiara, e lascia intatto il tuo sandbox.enabled salvato. cplt è il confine, come lo è per Gradle e Chromium. Sovrascrivi con --pass-env COPILOT_CLI_SANDBOX_SUPPORT_OVERRIDE. Una policy gestita aziendale che richiede il sandbox sovrascrive tutto questo — vedi Il sandbox dei comandi di Copilot CLI.
Ogni impatto, con le tabelle per strumento, le note sui daemon JVM e Kotlin, la risoluzione dei problemi GPG e le differenze di piattaforma per i registry privati: docs/known-impacts.md.
sandbox-exec è deprecato. Apple non l'ha rimosso, ma potrebbe farlo in una versione futura di macOS.lsopen di SBPL non ha filtri, quindi --allow-browser è tutto Launch Services o niente. Con esso attivo l'agente può avviare qualsiasi applicazione fuori dal sandbox, e nessun wrapper può restringere ciò — vedi docs/security.md..env dentro la directory del progetto non è applicata dal kernel. Le scritture in .git/hooks sono bloccate quando Bubblewrap è attivo.--deny-path richiede Bubblewrap. Viene applicato tramite maschere di mount quando bwrap è attivo. Senza di esso, Landlock è solo allowlist e cplt avvisa del deny invece di applicarlo.Altro: docs/security.md
I contributi sono benvenuti.```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
Apri una issue prima di iniziare una modifica importante. Ogni PR deve superare la CI (fmt, clippy, test).
## Riferimenti
- [SECURITY.md](https://github.com/navikt/cplt/blob/main/SECURITY.md), il modello di sicurezza completo, l'analisi delle minacce, la strategia di test e lo stato dell'arte
- [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)
- [Documentazione Landlock LSM](https://docs.kernel.org/userspace-api/landlock.html)
- [Documentazione 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)
## Licenza
[MIT](https://github.com/navikt/cplt/blob/main/LICENSE)
| Flag | Cosa fa |
|---|
--preset strict | Blocco completo della rete. Tutti e cinque i toggle disattivati, più gh_guard, git_guard, proxy.forced (egress forzato via proxy) e proxy.default_allowlist (allowlist di domini fail-closed) attivi. Via di fuga: --allow-all-domains disabilita solo l'allowlist |
--preset standard | I default attuali. Tutti e cinque disattivati, la scratch dir resta attiva. Come non passare alcun preset |
--preset permissive | Attiva allow_localhost_any, allow_tmp_exec e allow_lifecycle_scripts |
--preset full-trust | ⚠️ Pericoloso. Attiva tutti e cinque, aggiungendo allow_env_files e allow_docker |
| Flag | Cosa fa |
|---|
-d, --project-dir <DIR> | In quale directory Copilot può lavorare. Default alla root del repo git corrente |
--allow-read <PATH> | Consente a Copilot di leggere file fuori dal progetto, in sola lettura. Ripetibile |
--allow-write <PATH> | Consente a Copilot di leggere e scrivere fuori dal progetto. Usare con cautela. Ripetibile. L'albero è scrivibile ma non eseguibile — un albero che è entrambe le cose è un percorso di binary-drop, quindi un allow.write su ~/.cargo impedisce anche l'esecuzione di ~/.cargo/bin. Usa --allow-exec su un albero separato e non sovrapposto quando ti servono entrambi |
--allow-exec <PATH> | ⚠️ Pericoloso. Consente all'agente di eseguire binari da un albero fuori dalle directory di tool predefinite — un Homebrew o un prefisso di toolchain rilocato, per esempio. Concede lettura ed esecuzione, mai scrittura. Ripetibile. Rifiutato per una root non sicura (/, /tmp, $HOME e i suoi parent, le directory di sistema della piattaforma) e per qualsiasi albero che si sovrapponga a uno scrivibile — la directory del progetto, una concessione --allow-write, una directory di tool scrivibile come ~/.cache, una directory dati dell'agente scrivibile (~/.claude, ~/.local/share/opencode, ~/.pi/agent e simili), il .git reale di un worktree o bare repo, o un albero che i backend rendono scrivibile senza alcuna concessione (/tmp e /dev/shm su Linux; /private/tmp e /private/var/folders su macOS): scrivibile più eseguibile è un percorso di binary-drop, e nessuno dei due backend può sottrarre la concessione di scrittura da quella di esecuzione |
--allow-socket <PATH> | ⚠️ Pericoloso. Consente un percorso di socket di dominio Unix, per esempio un daemon LSP personalizzato o un socket di database. Ripetibile. Qualsiasi cosa ci sia dall'altra parte viene eseguita fuori dalla sandbox, quindi puntarlo a docker.sock o a un socket dell'agente equivale a --allow-docker, e l'unica protezione è che le sovrapposizioni con --deny-path vengono rifiutate. Su Linux non fa nulla sotto il kernel 7.1, poiché le connessioni ai socket unix non sono controllate da Landlock prima dell'ABI v9 (vedi Limitazioni Linux) |
--deny-path <PATH> | Blocca un percorso che altrimenti sarebbe consentito. Il deny vince sempre. Ripetibile |
--allow-port <PORT> | Consente traffico in uscita su una porta aggiuntiva. Solo 443 per default. Ripetibile. Su macOS la regola è (remote ip "*:PORT"), che è family-agnostic e quindi trasporta sia UDP che TCP; Landlock controlla solo il connect TCP. Sotto proxy.forced la porta non apre alcun socket diretto — è raggiungibile attraverso il proxy, quindi gli strumenti proxy-aware continuano a funzionare |
--allow-localhost <PORT> | Consente l'uscita verso localhost su una porta. Localhost è bloccato per default. Usare per server MCP o server di sviluppo. Ripetibile |
--allow-localhost-any | Consente l'uscita verso localhost su tutte le porte. Necessario per build tool come Turbopack (Next.js) e Vite che usano porte effimere casuali per l'IPC |
| Categoria | Esempi | Come |
|---|
| Sistema core | HOME, USER, PATH, SHELL, TMPDIR, LANG | Allowlist esplicita |
| Terminale | TERM, COLORTERM, TERM_PROGRAM | Allowlist esplicita |
| Editor | EDITOR, VISUAL, PAGER | Allowlist esplicita |
| Token di auth | GH_TOKEN, GITHUB_TOKEN, COPILOT_GITHUB_TOKEN | Passati solo se li hai già impostati. La gh guard usa invece un file monouso |
| Config Copilot | COPILOT_DEBUG, COPILOT_* | Allowlist per prefisso |
| Runtime dei linguaggi | NODE_*, GOPATH, CARGO_HOME, JAVA_HOME, VIRTUAL_ENV, PYTHONPATH | Allowlist esplicita |
| Tool manager | NVM_*, FNM_*, PYENV_*, MISE_*, SDKMAN_*, COREPACK_*, YARN_* | Allowlist per prefisso |
| OpenTelemetry | OTEL_EXPORTER_OTLP_ENDPOINT, OTEL_SERVICE_NAME, OTEL_RESOURCE_ATTRIBUTES, OTEL_* | Allowlist per prefisso (OTEL_EXPORTER_OTLP_HEADERS può trasportare auth opt-in) |
| Directory XDG | XDG_CONFIG_HOME, XDG_DATA_HOME, XDG_STATE_HOME, XDG_CACHE_HOME | Allowlist esplicita |
NO_COLORFORCE_COLORSSH_AUTH_SOCKSSH_AGENT_PID| Flag | Cosa fa |
|---|
--allow-lifecycle-scripts | Consente l'esecuzione degli script lifecycle di npm/yarn/pnpm (hook postinstall). Bloccati per default. Usare quando npm install ne ha bisogno |
--allow-gpg-signing | Consente la firma GPG di commit e tag dentro la sandbox. Concede accesso in sola lettura al portachiavi pubblico e al socket dell'agente GPG. Le chiavi private restano negate. Vedi Firma GPG |
--allow-jvm-attach | Consente i socket unix della JVM Attach API in /tmp. Necessario per il mocking inline di MockK, gli agent inline di Mockito, ByteBuddy. Vedi JVM Attach API |
--allow-msbuild | Consente i socket unix dei worker-node di MSBuild in /tmp. Necessario per dotnet build. Non abilita il MSBuild Server persistente. Vedi IPC dei worker-node di MSBuild |
--no-scratch-dir | Disabilita la directory scratch per sessione, che è attiva per default. TMPDIR non verrà reindirizzata |
--scratch-dir | Abilita esplicitamente la directory scratch per sessione. È già il default, quindi serve a sovrascrivere scratch_dir = false nella configurazione |
--brief | 🧪 Sperimentale. Scrive il brief della sandbox rivolto all'agente nella scratch dir (CPLT_BRIEF.md). Disattivato per default. Anche sandbox.brief = true nella configurazione. Instabile, quindi potrebbe cambiare o essere rimosso in una release futura |
--no-brief | Disattiva il brief della sandbox per questa esecuzione, sovrascrivendo sandbox.brief = true nella configurazione. Sopprime anche il blocco AGENTS.md, che è vincolato al brief |
--agents-md | 🧪 Sperimentale. Con --brief, scrive anche il blocco cplt gestito nel file AGENTS.md del progetto. Disattivato per default. Anche sandbox.agents_md = true nella configurazione. Nessun effetto senza --brief. Instabile, quindi potrebbe cambiare o essere rimosso in una release futura |
--no-agents-md | Disattiva il blocco AGENTS.md per questa esecuzione, sovrascrivendo sandbox.agents_md = true nella configurazione. Lascia intatto il brief nella scratch dir |
--allow-tmp-exec | ⚠️ Pericoloso. Consente l'esecuzione dalle directory temporanee di sistema (/private/tmp, /private/var/folders). Preferisci la scratch dir |
--allow-cache-exec <SUBDIR> | Consente l'esecuzione da una ~/Library/Caches/<SUBDIR>. Ripetibile. Per strumenti che vi mettono in cache binari compilati, come Playwright e pnpm dlx |
--allow-cache-exec-any | ⚠️ Pericoloso. Consente l'esecuzione da tutta ~/Library/Caches. Preferisci --allow-cache-exec <SUBDIR> |
--allow-browser | ⚠️ Pericoloso. Con questo attivo, l'agente può avviare qualsiasi applicazione sulla tua macchina fuori dalla sandbox. La concessione è Launch Services, non un browser: launchd avvia il target fuori dal profilo Seatbelt, quindi open -a Terminal /tmp/x.sh viene eseguito senza sandbox. Questo non può essere limitato agli URL — lsopen di SBPL non accetta filtri, e la concessione è raggiungibile tramite LSOpenCFURLRef() anche senza il binario open, quindi nessun wrapper può restringerla (#251, e docs/security.md). Attivalo solo mentre un prompt di accesso è effettivamente sullo schermo (OAuth di server MCP, ri-autenticazione), poi disattivalo di nuovo. Disattivato per default |
--deny-clipboard | Blocca l'agente dal leggere o scrivere gli appunti di macOS (pbpaste/pbcopy) negando il servizio Mach com.apple.pasteboard. Ogni altro servizio Mach (Keychain, DNS, Security framework) non è interessato. Attivo per default — questo flag ribadisce il default |
--allow-clipboard | Restituisce all'agente gli appunti di macOS, che cplt nega per default. Equivalente a sandbox.deny_clipboard = false |
--use-bubblewrap | Solo Linux. Richiede il layer di namespace bubblewrap (namespace PID, mount, IPC, UTS, cgroup, user più una /tmp privata) sopra Landlock e seccomp. Va in errore se bwrap manca. Rilevato automaticamente quando non viene passato nessuno dei due flag |
--no-bubblewrap | Solo Linux. Non usare mai bubblewrap, anche se installato. Ripiega su Landlock e seccomp. Usalo quando bwrap rompe uno strumento specifico |
| Runtime | Directory home | Variabili d'ambiente / prefissi | Rilevamento |
|---|
| 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, ecc. | go |
| Java/Kotlin (JVM) | .sdkman, .jenv, .gradle, .m2 | JAVA_HOME, JAVA_TOOL_OPTIONS, GRADLE_*, MAVEN_*, SDKMAN_*, JENV_* | java, gradle |
| Kotlin Native | .konan | nessuna | nessuno |
| Python | .pyenv | VIRTUAL_ENV, PYTHONPATH, PYENV_ROOT, PYENV_* | python3 |
| Yarn Berry | .yarn | YARN_* (l'hardening sovrascrive YARN_ENABLE_SCRIPTS) | yarn |
| pnpm | Library/pnpm, .local/share/pnpm | PNPM_HOME | pnpm |
| Corepack | nessuna | COREPACK_* | nessuno |
| mise | .local/share/mise, .mise | MISE_* | mise |
| Flag | Cosa fa |
|---|
--doctor | Deprecato. Usa invece il sottocomando cplt doctor |
--print-profile | Stampa il profilo di sandbox generato (SBPL) ed esce |
--show-denials | Trasmette in tempo reale i log di negazione della sandbox di macOS |
--no-validate | Salta il controllo all'avvio che verifica che le restrizioni della sandbox siano attive |
-y, --yes | Salta il prompt di conferma interattivo. Il riepilogo della configurazione viene comunque stampato, per auditabilità. Richiesto quando stdin non è un TTY, quindi CI e script ne hanno bisogno |
-q, --quiet | Sopprime il banner di avvio e i messaggi non essenziali. Errori e avvisi vengono comunque stampati. Anche sandbox.quiet = true nella configurazione |
--no-quiet | Sovrascrive sandbox.quiet = true e mostra comunque il riepilogo di avvio |
--no-audit | Salta il report delle modifiche post-sessione. cplt normalmente confronta l'albero di lavoro con un commit di baseline fissato prima dell'esecuzione ed elenca ciò che la sessione ha toccato, segnalando i percorsi sensibili. Anche -q lo sopprime |
--init-config | Crea un file di configurazione iniziale in ~/.config/cplt/config.toml ed esce |
| Flag | Cosa fa |
|---|
--resume[=SESSION] | Riprende una sessione precedente. --resume da solo sceglie in modo interattivo, --resume=NAME sceglie per nome o ID |
--continue | Riprende la sessione più recente nella directory corrente |
--remote | Abilita il controllo remoto, così puoi monitorare e guidare la sessione da GitHub.com o da mobile |
--name SESSION | Assegna un nome alla sessione così --resume=NAME può trovarla dopo |
| 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 | ignorato | ignorato | ignorato |
--name NAME | --name NAME | ignorato | ignorato | ignorato |
GOOGLE_API_KEYGEMINI_API_KEY--pass-env--observe-domains, quindi la sua allowlist integrata è solo la base condivisa dei package-registry. Aggiungi il dominio del tuo provider tramite allowed_domains prima di abilitare --default-allowlistGOOSE_DISABLE_KEYRING=1 fa sì che goose usi un secrets.yaml nella sua directory di configurazione, e passare la chiave con --pass-env evita del tutto i segreti memorizzati. Su Linux goose usa il D-Bus Secret Service, che la concessione del Keychain non influenza~/.config/goose/config.yaml dichiara voci extensions: il cui cmd viene avviato da goose a ogni inizio di sessione, quindi una directory di configurazione scrivibile è un vettore di persistenza sull'host. Le sessioni normali non vi scrivono; le modifiche a /mode e i permessi degli strumenti persistiti non sopravvivono a un'esecuzione in sandbox. Riconfigura con goose configure al di fuori di cplt~/.local/share/goose/) e dello stato (~/.local/state/goose/) di goose sono scrivibili, con exec negato. goose usa questi percorsi XDG anche su macOS, e rispetta le sostituzioni XDG_* in tale contesto--continue e --resume da solo corrispondono a goose session --resume; --resume=ID a goose session --resume --session-id ID; --name X a goose session --name X. Questi sono flag di sottocomando, quindi cplt inietta il sottocomando session insieme a essi. --remote viene ignorato (nessun equivalente in goose)| Livello | Enforcement | Aggirabile? | Cosa protegge |
|---|
| 1. Sandbox del kernel | macOS Seatbelt / Linux Landlock+seccomp | ❌ No | Accesso ai file, exec, porte di rete |
| 2. Proxy di rete | Proxy CONNECT, filtraggio dei domini | ❌ No (all'interno della sandbox) | Connessioni in uscita, esfiltrazione |
| 3. Guardia dei comandi | Script wrapper basati su PATH | ⚠️ Barriera soft | Push, merge, release, scritture API |
gitbwrapsandbox-execmiseghPATHcplt doctor: le sue sonde --version eseguono ogni binario di agente che trova sul tuo PATH, nel genitore, quindi uno impiantato viene eseguito lì — la stessa esposizione del percorso scoperto dell'avvio di cui sopra, ed è per questo che doctor è un report e non un confine. Il suo controllo di gh viene risolto dalle directory fidate e la sua lettura della release del kernel non avvia nulla| Comando | Azione |
|---|
gh pr merge, gh repo delete, gh release create | 🔒 Bloccato |
git push origin main, git push --force | 🔒 Bloccato |
gh api (scrittura su altri repo) | 🔒 Con controllo di scope |
gh pr list, gh issue list, git commit | ✅ Consentito |
git push origin feature-branch | ✅ Consentito con protect_default_branch_only |
| Impatto | Soluzione |
|---|
File .env bloccati | cplt config set sandbox.allow_env_files true |
| Hook postinstall di npm bloccati | cplt config set sandbox.allow_lifecycle_scripts true |
go test / mise run bloccati (exec temporaneo) | La scratch dir è attiva per impostazione predefinita. Se ti serve ancora, cplt config set sandbox.allow_tmp_exec true |
| Connessioni localhost bloccate | cplt config set allow.localhost 3000, oppure cplt config set sandbox.allow_localhost_any true |
| Docker bloccato | cplt config set sandbox.allow_docker true ⚠️ |
| SSH bloccato | Usa remote HTTPS |
| Firma GPG disabilitata | cplt config set sandbox.allow_gpg_signing true |
| JVM MockK/Mockito fallisce | cplt config set sandbox.allow_jvm_attach true |
Nodi worker MSBuild di dotnet build bloccati | cplt config set sandbox.allow_msbuild true |
| Credenziali di registry privati bloccate | cplt config set allow.read "~/.m2/settings.xml" |
| Repo Maven/Nexus interno irraggiungibile (Gradle/Maven) | cplt config set proxy.allow_private_domains "intern.example.com". Un URL di repository con IP letterale non può essere consentito — assegna un nome DNS all'host; vedi sotto |
| Playwright Chromium non si avvia | Consenti l'exec della cache, poi disabilita il sandbox annidato di Chromium; vedi sotto |