Bac à sable pour agents de codage IA. Exécute Copilot CLI, Claude Code, OpenCode, Gemini CLI, Antigravity, Pi, goose ou un shell simple dans un bac à sable au niveau du noyau, avec des garde-fous git et gh et une politique de bac à sable versionnée dans le dépôt.
Bac à sable appliqué par le noyau pour les agents de codage IA. cplt encapsule GitHub Copilot CLI, OpenCode, Gemini CLI, Antigravity CLI, Pi, Claude Code, goose, DeepSeek Harness, ou n'importe quel shell, afin que l'agent puisse écrire du code mais ne puisse pas voler des identifiants, pousser sur main, fusionner des PR, ou exfiltrer des secrets.
sandbox-exec
Les agents IA exécutent du code arbitraire. Un agent compromis, que ce soit par injection de prompt, attaque de chaîne d'approvisionnement, ou serveur MCP malveillant, peut lire ~/.ssh, pousser sur main, fusionner des PR, ou exfiltrer votre code, à moins que l'OS lui-même ne dise non.
cplt vous offre une application au niveau du noyau avec une politique configurable par équipe :
.cplt.toml, versionnée dans le contrôle de version, donc infalsifiable et auditableDocumentation détaillée : Configuration · Proxy & filtrage de domaines · Garde de commande gh · Garde de commande git · Impacts connus · Détails de sécurité · Modèle de sécurité
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
Autres agents et commandes 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
## Ce qu'il bloque
Le sandbox bloque l'accès aux identifiants et secrets dans le noyau. Les gardes de commandes bloquent les opérations destructrices. Chaque restriction s'applique à l'agent et à chaque processus qu'il lance.
| Ressource | Statut | Notes |
| --- | --- | --- |
| Lecture/écriture du répertoire du projet | ✅ Autorisé | |
| Lecture/écriture/suppression de `.env*`, `.pem`, `.key` dans le projet | 🔒 Bloqué par le noyau | Empêche l'exfiltration et la destruction de secrets. `--allow-env-files` outrepasse |
| Écriture de `.git/hooks`, `.git/config`, `.gitmodules` | 🔒 Bloqué par le noyau (macOS), ⚠️ partiel sur Linux | Empêche la persistance via les hooks git, la redirection hooksPath, le détournement de sous-modules. **Linux :** Landlock ne peut pas refuser un sous-chemin à l'intérieur d'une arborescence autorisée, donc ceux-ci restent inscriptibles sur le chemin Landlock uniquement. `bwrap` relie `.git/hooks` en lecture seule mais laisse délibérément `.git/config` et `.gitmodules` inscriptibles, donc `core.hooksPath` reste une voie de persistance, voir [Limitations Linux](https://github.com/navikt/cplt/blob/main/docs/security.md#linux). S'applique à **chaque** racine inscriptible, le projet et chaque autorisation `allow.write`, y compris un worktree accordé ou un dépôt nu dont les vrais hooks vivent en dehors de `<root>/.git` |
| Exécution depuis `/tmp`, `/var/folders` | 🔒 Bloqué par le noyau | Empêche l'écriture-puis-exécution. Le répertoire scratch redirige TMPDIR vers un emplacement sûr, activé par défaut |
| Écriture dans les répertoires bin/shim résolus via PATH (`~/.bun/bin`, `~/.deno/bin`, `$PNPM_HOME`, les `shims/` de mise et tout `installs/`) | 🔒 Bloqué par le noyau (macOS), ⚠️ mise partiel sur Linux | Empêche de transformer en cheval de Troie un binaire que votre prochaine commande *non sandboxée* résout via PATH. Même raison pour laquelle `~/.cargo/bin` et `~/go/bin` ont toujours été en lecture seule. Casse `bun install -g`, `deno install`, `pnpm add -g`, `mise install`, `mise upgrade`, `mise use -g` dans cplt, délibérément, et un dépôt épinglant une chaîne d'outils non installée ne s'amorce plus. Les installations locales au projet ne sont pas affectées. **Linux :** les deux de mise reposent sur la surcouche en lecture seule de `bwrap` ; le reste tient nativement. Voir [Installations globales d'outils](https://github.com/navikt/cplt/blob/main/docs/known-impacts.md#global-tool-installs) |
| Exécution depuis `~/Library/Caches` | 🔒 Bloqué par le noyau par défaut | Empêche la mise en place de dépôts de binaires. Les modules natifs de Copilot sont exemptés via une dérogation. Ajoutez des exemptions ciblées avec `--allow-cache-exec <SUBDIR>`, par ex. `ms-playwright` |
| Modification de `.vscode/tasks.json`, `launch.json` | ⚠️ Autorisé, risque connu | Frontière de confiance de l'IDE. Voir [SECURITY.md](https://github.com/navikt/cplt/blob/main/SECURITY.md) pour les atténuations |
| Lecture/écriture de `~/.copilot` (auth, paramètres) | ✅ Autorisé | Inclut `file-map-executable` pour `keytar.node`, `pty.node`, `computer.node` |
| Écriture de `~/.copilot/pkg` (modules natifs) | 🔒 Bloqué par le noyau | Empêche la persistance via le remplacement de modules natifs |
| Variables d'environnement | 🔒 Assainies + durcies | Seule une liste blanche sûre passe. Scripts de cycle de vie bloqués. `--pass-env VAR` en rajoute une |
| Lecture de `~/.config/gh/hosts.yml` + `config.yml` | ✅ Autorisé (lecture seule) | Uniquement ces deux fichiers. Le reste de `.config/gh` est bloqué |
| Lecture de `~/.config/mise` | ✅ Autorisé (lecture seule) | Versions d'outils et PATH, aucun secret |
| Lecture de `~/.gitconfig`, `~/.config/git/config` | ✅ Autorisé (lecture seule) | Un lien symbolique dotfiles est suivi jusqu'à sa cible, donc un `~/.gitconfig` stowé fonctionne |
| Lecture de `~/.git-credentials` | 🔒 Bloqué par le noyau | `credential.helper = store` conserve les jetons en clair ici. Aucun `--allow-read` ne le rouvre, comme `~/.netrc`. **Linux :** une autorisation sur un *ancêtre* (`$HOME` lui-même) l'expose toujours, car Landlock ne peut pas refuser un sous-chemin à l'intérieur d'une arborescence autorisée |
| Lecture des hooks git globaux (`core.hooksPath`) | ✅ Autorisé (lecture seule, écriture refusée) | Auto-détecté. Doit être sous `$HOME` avec une profondeur ≥3. Les écritures sont explicitement bloquées |
| Signature de commit/tag (`commit.gpgsign`, `tag.gpgsign`) | 🔒 Désactivé | Les clés privées dans `~/.ssh` et `~/.gnupg` sont bloquées, donc la signature est désactivée via une surcharge de variable d'environnement |
| Lecture de `~/Library/Application Support/Microsoft` | ✅ Autorisé (lecture seule) | ID d'appareil pour la télémétrie |
| Accès au trousseau macOS | ⚠️ Autorisé (lecture+écriture) pour les agents qui y stockent l'auth | L'autorisation ne peut pas être limitée à un seul élément, donc elle atteint chaque entrée du trousseau que l'agent peut déverrouiller. Activez `sandbox.keychain_substitute` (EXPÉRIMENTAL, désactivé par défaut) pour le supprimer lors des exécutions où l'agent peut s'authentifier sans lui — `CLAUDE_CODE_OAUTH_TOKEN` pour Claude Code, un fichier de jeton de repli existant pour Antigravity. Voir [SECURITY.md](https://github.com/navikt/cplt/blob/main/SECURITY.md#keychain-access-is-all-or-nothing) |
| Réseau sortant (port 443) | ✅ Autorisé | Tous les autres ports sont bloqués. Ajoutez-en avec `--allow-port` |
| Sortie vers localhost | 🔒 Bloqué par le noyau (macOS), ⚠️ basé sur le port sur Linux | Empêche l'accès aux services locaux. L'entrée fonctionne toujours pour le proxy. **Linux :** les règles Landlock ne sont que des numéros de port et ne peuvent pas distinguer `localhost:443` de `remote:443`, donc un service local sur un port autorisé est joignable et il n'y a pas de refus spécifique à localhost. Utilisez `--with-proxy` pour la protection SSRF, voir [Limitations Linux](https://github.com/navikt/cplt/blob/main/docs/security.md#linux) |
| Agent SSH (socket unix) | 🔒 Bloqué par le noyau (macOS), ⚠️ env uniquement sur Linux | Empêche la signature d'opérations git ou SSH vers des hôtes. **Linux :** le `connect()` sur socket unix n'est pas filtré, donc le `SSH_AUTH_SOCK` retenu est la seule barrière et un agent qui le définit lui-même peut utiliser les clés chargées. `bwrap` masque le socket OpenSSH standard sous `/tmp`, mais pas un agent gnome-keyring/gcr ou systemd sous `$XDG_RUNTIME_DIR`. Voir [Limitations Linux](https://github.com/navikt/cplt/blob/main/docs/security.md#linux) |
| Outils de développement (`~/.cargo`, `~/.gradle`, `~/.m2`, `~/.sdkman`, `~/.jenv`, `~/.pyenv`, `~/.konan`, etc.) | ✅ Autorisé (lecture+écriture pour les caches) | Uniquement les répertoires qui existent sur le disque. Resserré à l'exécution selon ce que `cplt doctor` détecte |
| Fichiers d'identifiants de registre (`~/.m2/settings.xml`, `~/.gradle/gradle.properties`, `~/.cargo/credentials`) | 🔒 Bloqué par le noyau sur macOS. Sur Linux le répertoire outil parent reste lisible | Outrepassez avec `--allow-read`. Voir [Registres privés](https://github.com/navikt/cplt/blob/main/docs/known-impacts.md#private-registries) |
| Lecture de `~/.npmrc` | 🔒 Bloqué par le noyau (les deux plateformes) | Outrepassez avec `--allow-read`. Casse yarn 1, voir [yarn 1](https://github.com/navikt/cplt/blob/main/docs/known-impacts.md#yarn-1-and-unreadable-home-rc-files) |
| Code source Go (`~/go/src`) | 🔒 Bloqué par le noyau | Seuls `~/go/bin` et `~/go/pkg` sont lisibles |
| Lecture de `~/.ssh`, `~/.gnupg`, `~/.aws`, `~/.azure` | 🔒 Bloqué par le noyau | |
| Lecture de `~/.kube`, `~/.docker`, `~/.nais` | 🔒 Bloqué par le noyau | |
| Lecture de `~/.password-store`, `~/.terraform.d` | 🔒 Bloqué par le noyau | |
| Lecture de `~/.config/gcloud`, `~/.config/op` | 🔒 Bloqué par le noyau | Les fichiers individuels sont surchargeables avec `--allow-read`. Voir [Identifiants cloud](https://github.com/navikt/cplt/blob/main/docs/known-impacts.md#cloud-credential-directories) |
| Lecture ou écriture de `~/.config/cplt`, `~/.nav-pilot` | 🔒 Bloqué par le noyau | État de l'outil qui décide ce que le *prochain* lancement peut faire. `~/.config/cplt` est non-surchargeable en tant qu'arborescence entière ; à l'intérieur de `~/.nav-pilot`, un chemin nommé reste accordable afin qu'une charge utile agentpakke épinglée puisse être lue |
| Lecture de `~/.netrc`, `~/.pypirc`, `~/.vault-token` | 🔒 Bloqué par le noyau | Non-surchargeable sur les deux plateformes. En nommer un dans `allow.read` est une erreur au démarrage |
| Lecture de `~/.gem/credentials` | 🔒 Bloqué par le noyau | Non-surchargeable sur les deux plateformes. En nommer un dans `allow.read` est une erreur au démarrage |
| Opérations destructrices de la CLI `gh` (merge, delete, release) | 🔒 Filtré par commande (activé par défaut) | Désactivez avec `--no-gh-guard`. Voir [garde gh](https://github.com/navikt/cplt/blob/main/docs/gh-guard.md) |
| `git push` vers la branche par défaut | 🔒 Filtré par commande (activé par défaut) | Bloque les push vers `main`/`master` ; les push de branches de fonctionnalité fonctionnent toujours. `protect_default_branch_only = false` bloque chaque push, `git_guard.mode = "warn"` avertit seulement, `--no-git-guard` désactive |
| Héritage des processus enfants | ✅ Toutes les restrictions s'appliquent aux sous-processus | |
Ce tableau est un résumé. Le sandbox autorise aussi l'accès aux fichiers système (certificats SSL, `/etc/hosts`), aux répertoires temporaires (lecture et écriture, pas d'exécution) et aux chemins d'outils système (`/usr/bin`, `/opt/homebrew`). Lancez `cplt --print-profile` pour les règles SBPL complètes.
Pour le modèle de sécurité complet, l'analyse des menaces et la stratégie de test, lisez [SECURITY.md](https://github.com/navikt/cplt/blob/main/SECURITY.md).
## Comment cplt se compare
### Le sandbox de Codex CLI
| Domaine | cplt | Sandbox de Codex CLI |
| --- | --- | --- |
| Contrôle du réseau sortant | Proxy CONNECT avec listes d'autorisation/blocage de domaines | Pas de filtrage au niveau des domaines |
| Gestion de l'environnement | Liste blanche plus injection d'env durcie | Modèle de pass-through plus basique |
| Protection des fichiers secrets | Motifs de refus tels que `.env*`, `.pem`, `.key` dans le dépôt | Accès principalement limité au répertoire |
| Politique de dépôt | [`.cplt.toml`](https://github.com/navikt/cplt/blob/main/docs/configuration.md#per-repo-configuration-cplttoml) avec un flux explicite de confiance/approbation | Pas de fichier de politique au niveau du dépôt |
| Support d'agents | Copilot, OpenCode, Gemini CLI, Antigravity CLI, Pi, Claude Code, goose, DeepSeek Harness, ou shell | Codex uniquement |
cplt n'est pas plus fort partout. Codex CLI dispose aujourd'hui de l'isolation par namespaces Linux, et il expose déjà des modes sandbox explicites tels que lecture seule et workspace-write. cplt n'a pas encore cette matrice de modes.
### Sandboxes basés sur Docker
| Domaine | cplt | Sandbox basé sur Docker |
| --- | --- | --- |
| Temps de démarrage | Quasiment instantané pour un usage CLI normal | Démarrage de conteneur généralement plus lent |
| Contrôle réseau | Filtrage sortant par requête via proxy | Accès réseau généralement tout ou rien |
| Contrôles de fichiers | Règles par chemin et par motif | Contrôles par montage |
| Prérequis de l'hôte | Un seul binaire | Démon Docker requis |
| Adéquation aux laptops d'entreprise | Fonctionne là où Docker est indisponible ou restreint | Souvent bloqué par la politique locale |
Docker offre toujours une isolation plus forte dans certains environnements, surtout si vous voulez un système de fichiers et un namespace de processus entièrement séparés. cplt échange cela contre une installation plus légère et une intégration plus étroite avec la machine sur laquelle vous développez déjà.
### Permissions du mode agent de VS Code
Des outils comme le mode agent de VS Code reposent principalement sur des permissions d'interface. cplt applique ses restrictions dans le noyau, donc l'agent ne peut pas les contourner par un prompt ou une instruction modifiée. Cela compte surtout pour les agents CLI et l'exposition des identifiants :
- cplt fonctionne en dehors de l'IDE
- les variables d'environnement sont filtrées avant que l'agent démarre
- les fichiers sensibles peuvent être bloqués même lorsqu'ils vivent dans le dépôt
- les mêmes restrictions s'appliquent aux processus enfants
### Le sandbox de Claude Code (Anthropic Sandbox Runtime)
[Anthropic Sandbox Runtime](https://github.com/anthropic-experimental/sandbox-runtime) (`srt`) est la couche de sandboxing utilisée par Claude Code. Même approche de haut niveau que cplt, Seatbelt macOS plus application au niveau du noyau Linux plus un proxy HTTP, implémentation différente.
| Domaine | cplt | Anthropic srt |
| --- | --- | --- |
| Langage / livraison | Un seul binaire Rust | Node.js + paquet npm + dépendances externes |
| Backend Linux | Landlock LSM (sans dépendances, sans namespaces) | bubblewrap (conteneur via namespaces utilisateur) |
| Filtrage de l'environnement | Liste blanche stricte + refus par suffixe (`_TOKEN`, `_SECRET`) | Hérite de tout l'env parent (les secrets passent) |
| Protection des répertoires d'identifiants | 15+ répertoires refusés par défaut | L'utilisateur doit configurer manuellement |
| Protection contre le DNS rebinding | ✅ IP post-DNS vérifiée contre les plages privées | ❌ Non implémenté |
| Proxy réseau | HTTP CONNECT + autorisation/blocage de domaines | HTTP + SOCKS5 + MITM TLS expérimental |
| Git via SSH | Bloqué au niveau du noyau sur macOS (socket d'agent refusé) ; sur Linux seul `SSH_AUTH_SOCK` est retenu | Proxifié via SOCKS5 |
| Scripts de gestionnaires de paquets | Bloqués par défaut (`npm_config_ignore_scripts`) | Non bloqués |
| Support d'agents | Copilot, OpenCode, Gemini, Antigravity, Pi, Claude Code, goose, DSH, Shell | Claude Code |
| Configuration | TOML (global + par dépôt) | JSON (global uniquement) + mises à jour en direct via `--control-fd` |
| API de bibliothèque | ❌ Binaire uniquement | ✅ Bibliothèque TypeScript intégrable |
cplt est plus sécurisé dès le départ : filtrage de l'env, protection des identifiants, vérifications de DNS rebinding, blocage des scripts de cycle de vie. srt est plus flexible : SOCKS5, inspection TLS, callbacks par requête, intégration en bibliothèque. Le choix du backend Linux compte. bwrap nécessite des contournements sur Ubuntu 24.04+ à cause des restrictions AppArmor sur les userns, tandis que Landlock exige un noyau 5.13 ou plus récent mais n'a aucune dépendance externe.
### Le sandbox propre de GitHub Copilot CLI
Copilot CLI est livré avec un sandbox local depuis juin 2026, inclus dans le
siège standard. Il exécute les commandes shell via Microsoft MXC avec un accès
restreint au système de fichiers, au réseau et au système, sur macOS, Linux et Windows.
`/sandbox enable` l'active.
Si cela vous suffit, utilisez-le. Cela ne coûte rien de plus, et il fonctionne sous Windows,
ce que cplt ne fait pas.
Deux choses qu'il ne fait pas.
La politique réside chez l'administrateur, pas dans le dépôt. Les entreprises définissent
la politique de sandbox via Intune ou un autre MDM. Rien ne se trouve à côté du code,
donc une règle qui compte pour un dépôt ne peut pas le suivre jusqu'à un contributeur,
jusqu'à la CI, ou jusqu'à un laptop que le MDM ne gère pas. Dans cplt la politique est
`.cplt.toml` dans le dépôt. Les relecteurs voient les modifications dans la pull
request, et le fichier peut resserrer la configuration propre d'un développeur mais jamais
la relâcher.
Il confine le processus, pas ce que le processus fait des identifiants qu'il
détient. Les onglets `/sandbox` couvrent le système de fichiers, le réseau et les capacités
système, et à l'intérieur d'un dépôt Git l'agent se voit accorder la lecture et l'écriture
sur `.git` par défaut. Un agent sandboxé a toujours votre jeton `gh` et votre
accès push. Pousser une branche, fusionner une pull request et supprimer un
dépôt sont tous des appels API bien formés d'un client autorisé, et une
règle de système de fichiers ou de réseau n'a aucun avis à leur sujet. cplt enveloppe `git` et
`gh` à la place. L'agent commit, branche et rebase librement. `gh pr merge`,
`gh repo delete` et `gh release create` sont bloqués par défaut. Il en va de même pour
`git push` vers `main`/`master` ; les push de branches de fonctionnalité fonctionnent toujours, car
`protect_default_branch_only` est activé. Mettez-le à `false` pour bloquer chaque push, ou
`git_guard.mode = "warn"` pour seulement avertir.
Exécuter les deux est raisonnable. MXC confine le processus. Les gardes décident ce que
l'agent peut faire des identifiants qu'il détient.
### Lacunes honnêtes
- macOS a aujourd'hui l'application au niveau des fichiers la plus forte. La couverture Linux s'améliore mais n'est pas identique.
- cplt n'offre pas encore de préréglages de politique simples lecture seule / workspace-write / accès complet.
- Si vous voulez une isolation complète par conteneur, cplt ne cherche pas à remplacer Docker.
## Installation
### Homebrew (recommandé)```bash
brew install navikt/tap/cplt
mise use -g 'github:navikt/cplt@'
mise sélectionne l'asset de release approprié pour votre plateforme et vérifie son attestation de provenance de build.
Épinglez la version. Nos chaînes de version ne sont pas des semver comparables — elles comportent des zéros initiaux et deux tirets — donc `mise latest` peut résoudre vers une release plus ancienne que la plus récente ([navikt/copilot#818](https://github.com/navikt/copilot/issues/818)).
### apt (Debian/Ubuntu, recommandé sous Linux)
[navikt/apt](https://navikt.github.io/apt/) est une archive signée servie via
GitHub Pages, contenant cplt et nav-pilot pour amd64 et 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
C'est un simple miroir de dépôt apt de nos versions, pas un paquet de distribution
avec son propre mainteneur. Sa tâche de publication s'exécute toutes les heures et récupère le .deb
le plus récent de la dernière version de chaque outil, donc une version publiée il y a quelques minutes prend jusqu'à une
heure pour devenir installable de cette façon.
Le paquet place le binaire dans /usr/bin/cplt, et les mises à niveau passent ensuite par
sudo apt upgrade. cplt update refuse de toucher à une installation apt
et renvoie vers sudo apt upgrade à la place : remplacer le binaire dans le dos de dpkg
serait annulé par la prochaine exécution d'apt.
Sans l'archive, le même .deb est un actif de version :```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
Pour les distributions qui ne sont pas des dérivées de Debian, et pour la CI :```bash
curl -fsSL https://raw.githubusercontent.com/navikt/cplt/main/install.sh | bash
Options :```bash
curl -fsSL ... | bash -s -- --version 2026.05.05-174753-75bae5b
curl -fsSL ... | bash -s -- --dir ~/.local/bin
curl -fsSL ... | bash -s -- --no-brew
### Télécharger depuis les releases
Récupérez la dernière version pour votre plateforme depuis [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/
Chaque binaire de release est accompagné d'une attestation de provenance de build. Vérifiez-la :```bash gh attestation verify cplt -o navikt
### Compiler depuis les sources```bash
git clone https://github.com/navikt/cplt.git && cd cplt
cargo build --release
sudo cp target/release/cplt /usr/local/bin/
Ou avec mise :```bash mise run install
`mise run install` et les compilations manuelles placent cplt dans `/usr/local/bin/cplt`. Si vous avez également la version Homebrew à `/opt/homebrew/bin/cplt`, placez `/usr/local/bin` en premier dans `PATH` afin que votre version de développement soit prioritaire :```bash
# Check which cplt is active
which cplt
# If it shows /opt/homebrew/bin/cplt, reorder your PATH:
export PATH="/usr/local/bin:$PATH"
Ou exécutez simplement /usr/local/bin/cplt explicitement et ignorez entièrement la résolution du PATH.
cplt n'a pas de backend de sandbox Windows. L'application est assurée par Apple Seatbelt sur macOS et Landlock LSM sur Linux, il n'y a donc rien à exécuter nativement sur Windows. La voie prise en charge est WSL2, où cplt est une installation Linux ordinaire et la sandbox est appliquée par le noyau. Chaque branche du noyau Microsoft compile CONFIG_SECURITY_LANDLOCK=y et liste landlock en premier dans CONFIG_LSM (config-wsl), livré depuis le noyau 5.15.57.1, et la ligne de commande par défaut du noyau WSL ne définit aucun remplacement lsm=.
Dans PowerShell, une fois :```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
Tout ce qui suit s'exécute **à l'intérieur de la distro** (`wsl`, ou le profil Ubuntu dans Windows Terminal), pas dans PowerShell :```bash
# 1. Node. Copilot CLI requires Node 22+
# Ubuntu 26.04 ships 22.x, so apt is enough:
sudo apt update && sudo apt install -y nodejs npm
# Ubuntu 24.04 ships Node 18, too old. Use nvm, fnm, or NodeSource there instead.
# 2. GitHub CLI, and log in. Ubuntu's universe package works but lags
# (2.45 on 24.04); add GitHub's apt repo if you want a current gh:
# https://github.com/cli/cli/blob/trunk/docs/install_linux.md
sudo apt install -y gh
gh auth login
# 3. The agent, installed in the distro, never on the Windows side
npm install -g @github/copilot
# 4. cplt, from the apt archive. The default distro is Ubuntu, so this is
# the same route as on any other Debian derivative.
curl -fsSL https://navikt.github.io/apt/keyring/navikt-archive-keyring.gpg \
| sudo tee /usr/share/keyrings/navikt-archive-keyring.gpg >/dev/null
echo "deb [signed-by=/usr/share/keyrings/navikt-archive-keyring.gpg] https://navikt.github.io/apt stable main" \
| sudo tee /etc/apt/sources.list.d/navikt.list
sudo apt update && sudo apt install cplt
# 5. Check the result
cplt doctor
N'installez pas Copilot CLI côté Windows. Avec l'interopérabilité activée (par défaut), le PATH Windows est ajouté à celui de la distribution, donc un npm install -g @github/copilot côté Windows apparaît dans la distribution sous la forme /mnt/c/Users/<user>/AppData/Roaming/npm/copilot. Il s'agit d'une installation Windows atteinte via l'interopérabilité. Elle ne peut pas s'exécuter dans le bac à sable Linux, et le shim npm exécute un node que la distribution n'aura pas, sauf si vous en avez installé un là aussi. Le symptôme était auparavant une erreur d'extraction d'exécution sans rapport. cplt nomme désormais la cause lorsqu'il résout un agent sous /mnt/<drive>/ et qu'il s'exécute sous WSL, et cplt doctor le signale comme une vérification en échec au lieu de la réussir (#188). WSL est détecté à partir d'un état détenu par le noyau, soit /run/WSL, soit le nom du noyau dans /proc/sys/kernel/osrelease et /proc/version, et non à partir de WSL_DISTRO_NAME, qui est absent sous sudo et dans les unités systemd et que tout processus peut définir. Sur une machine Linux ordinaire, /mnt/c est laissé tranquille. C'est un point de montage ordinaire là-bas.
Cette vérification a deux limites, toutes deux délibérées. Elle se base sur la racine de montage automatique par défaut, donc si vous l'avez déplacée ([automount] root dans /etc/wsl.conf), l'installation côté Windows n'est pas reconnue et vous obtenez l'ancien échec, moins utile, avec le chemin dedans. Et désactiver l'interopérabilité empêche le PATH Windows de fuiter mais ne démonte pas /mnt/c.
Noyau et ABI Landlock. WSL actuel (2.7.x et versions ultérieures) fournit Linux 6.18, ce qui donne Landlock ABI 7 — tout ce que cplt utilise, sauf le droit connect() sur socket unix, qui nécessite ABI 9 (noyau 7.1). Une installation encore sur la ligne du noyau 6.6 obtient ABI 3 : les règles de système de fichiers sont appliquées, mais les règles de port TCP (ABI 4), la restriction ioctl (ABI 5) et le cadrage signal/socket abstrait (ABI 6) ne sont pas disponibles, et le filtrage réseau retombe sur le proxy CONNECT. wsl --update vous fait avancer. cplt doctor affiche la version du noyau et l'ABI qu'il a trouvé, ce qui est la vérification qui compte sur votre machine.
Ne désactivez pas Landlock dans
.wslconfig. Un[wsl2] kernelCommandLineavec une listelsm=qui ometlandlock, ou un[wsl2] kernel=personnalisé compilé sansCONFIG_SECURITY_LANDLOCK, supprime l'application par le noyau dont cplt dépend, etcplt doctorsignalera Landlock comme indisponible.
Gardez le projet dans le système de fichiers Linux. Travaillez dans ~/src/... à l'intérieur de la distribution plutôt que dans /mnt/c/Users/.... Les propres recommandations de Microsoft indiquent que l'accès aux fichiers entre systèmes d'exploitation est nettement plus lent, et /mnt/c est servi via 9p par défaut à partir de WSL 2.9.x (virtiofs est opt-in via [wsl2] virtiofs=true). Plus important encore, nous n'avons pas vérifié comment Landlock applique les règles sur ce point de montage. Le noyau ne documente aucune exclusion pour les systèmes de fichiers adossés au réseau ou à FUSE, seulement les pipes, les sockets et nsfs, et la propre suite de tests de Landlock exerce 9p et FUSE, donc nous nous attendons à ce que cela fonctionne. Personne ici ne l'a confirmé. Considérez un projet sous /mnt/c comme non prouvé plutôt que pris en charge.
Bubblewrap. Ubuntu 23.10+ bloque les espaces de noms utilisateur non privilégiés via kernel.apparmor_restrict_unprivileged_userns, ce qui casse bwrap. Ce sysctl provient d'un correctif du noyau Ubuntu absent du noyau Microsoft, donc la couche Bubblewrap optionnelle est censée fonctionner sur Ubuntu-sous-WSL2. C'est une inférence à partir du code source du noyau, pas quelque chose que nous avons exécuté. Si bwrap échoue là-bas, veuillez le signaler dans #189. Le propre filtre seccomp de cplt est un simple programme BPF PR_SET_SECCOMP, qui s'empile au-dessus du filtre que WSL installe dans chaque processus.
Pas encore vérifié sur une véritable installation WSL2. Vérifié à partir du code source : Landlock est compilé et premier dans
CONFIG_LSMsur le noyau Microsoft ; la détection/mnt/<drive>/, les signaux WSL qu'il utilise et leur texte d'erreur ; quecplt doctoréchoue sur un tel agent et affiche le noyau + l'ABI Landlock ; les exigences 5.13+/6.7+ ; et queinstall.shinstalle le binaire de version Linux. Toujours non vérifié par quiconque ici : le comportement de Landlock sur/mnt/c, si Bubblewrap fonctionne sous WSL2, les versions exactes des paquets fournies par votre distribution, et la séquence ci-dessus de bout en bout. Si vous l'exécutez, veuillez rapporter ce qui s'est réellement passé dans #189.
Par défaut, vous obtenez le bac à sable en tapant cplt. Pour que le simple copilot s'exécute aussi en bac à sable :```bash
cplt --shell-install
Cela détecte votre shell, ajoute l'alias à votre fichier rc et affiche ce qu'il a fait. Exécutez-le autant de fois que vous le souhaitez, il n'ajoutera pas de doublons.
`--agent` choisit la commande qui reçoit l'alias, et chaque agent que cplt peut lancer est disponible :```bash
cplt --shell-install --agent opencode # 'opencode' runs sandboxed
cplt --shell-install --agent claude # and 'claude', alongside the others
Chaque installation s'ajoute à votre fichier rc plutôt que de remplacer ce qui s'y trouve, vous pouvez donc isoler autant d'agents que vous en utilisez. Sans --agent, vous obtenez copilot, ce que le flag a toujours installé.
| Shell | Fichier modifié | Ce qui est ajouté (pour --agent opencode) |
|---|---|---|
| zsh (par défaut sur 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 installe des alias pour antigravity et agy, car l'un ou l'autre nom lance le même agent.
Redémarrez votre shell ou faites un source du fichier pour activer.
Il n'y a pas d'alias pour --agent shell : il n'existe pas de binaire shell à masquer. Tapez cplt --agent shell pour un shell isolé, ou cplt exec -- <command> pour une seule commande.
Si vous préférez ne pas utiliser --shell-install, ajoutez la ligne vous-même :```bash
eval "$(cplt --shell-setup --agent opencode)"
alias opencode 'cplt --agent opencode'
Le même schéma que celui utilisé par mise, direnv et starship.
</details>
**Pourquoi chaque alias nomme son agent.** `alias opencode=cplt` ne ferait pas ce qu'il laisse penser. Un simple `cplt` choisit son agent via `--agent`, puis le fichier de configuration, puis ce qu'il trouve dans le PATH — et la détection dans le PATH privilégie `copilot`. Taper `opencode` mettrait donc Copilot en sandbox à la place, sans rien à l'écran pour le signaler. L'alias passe `--agent` afin que la commande que vous tapez soit l'agent que vous obtenez.
**Pourquoi un alias plutôt qu'un lien symbolique ?** cplt et Copilot CLI s'installent dans le même répertoire bin de Homebrew (`/opt/homebrew/bin/`), et un seul fichier nommé `copilot` peut y résider, donc un lien symbolique entrerait en conflit. Un alias contourne ce problème. Le véritable binaire `copilot` reste dans le PATH où cplt peut le trouver et l'envelopper, et l'alias redirige votre commande.
> **Remarque :** cplt refuse de s'imbriquer. S'il détecte qu'il s'exécute déjà à l'intérieur d'un sandbox (via la variable d'environnement `__CPLT_WRAPPED`), il ne se lancera pas à nouveau. Les sous-commandes en lecture seule telles que `--print-profile` et `cplt doctor` fonctionnent toujours à l'intérieur d'un sandbox existant.
## Utilisation```
cplt [OPTIONS] [-- <AGENT_ARGS>...]
Tout ce qui suit -- est transmis directement au processus de l'agent (copilot, opencode, gemini, antigravity, pi, claude, goose, dsh ou shell).
Un préréglage définit une base pour les cinq principaux commutateurs de sandbox avec un seul drapeau au lieu d'une liste de ceux-ci. Les drapeaux individuels l'emportent toujours sur le préréglage, donc --preset permissive --no-allow-tmp-exec fait ce qu'il dit. Également définissable via [sandbox] preset = "..." dans la configuration.
Matrice complète des préréglages et ordre de résolution : docs/configuration.md.
Le répertoire du projet est l'espace de travail inscriptible, plus une liste d'autorisation étroite nécessaire pour l'authentification, l'exécution et l'outillage (voir le tableau ci-dessus). Le noyau bloque tout le reste, y compris les clés SSH et les identifiants cloud.
cplt assainit l'environnement enfant par défaut. Seules les variables sûres passent, et les identifiants cloud, les URL de bases de données et les jetons de paquets sont supprimés. Il injecte également des variables de durcissement qui bloquent les scripts de cycle de vie npm/yarn/pnpm (hooks postinstall, le vecteur d'attaque de chaîne d'approvisionnement numéro un), désactivent la signature des commits et tags git (puisque ~/.ssh et ~/.gnupg sont inaccessibles à l'intérieur du sandbox), et désactivent la télémétrie des outils de développement (DO_NOT_TRACK=1, NEXT_TELEMETRY_DISABLED=1, TURBO_TELEMETRY_DISABLED=1, CHECKPOINT_DISABLE=1, et autres).
Ce qui passe :
Liste d'autorisation par préfixe avec protection contre les suffixes secrets. Une variable correspondant à un préfixe autorisé tel que COPILOT_* ou YARN_* est tout de même supprimée si elle se termine par un suffixe porteur de secret : _TOKEN, _AUTH, _SECRET, _SECRET_KEY, _KEY, _PASSWORD ou _CREDENTIALS. Ainsi COPILOT_DEBUG passe et COPILOT_API_KEY ne passe pas.
Toujours bloqués : AWS_*, AZURE_*, NPM_TOKEN, DATABASE_URL, VAULT_TOKEN, SSH_AUTH_SOCK, les variables Docker, les jetons CI, et tout ce qui n'est pas dans la liste d'autorisation.
| Drapeau | Ce qu'il fait |
|---|---|
--pass-env <VAR> | Transmettre une variable d'environnement à l'agent. Répétable |
--inherit-env | ⚠️ Dangereux. Hériter de l'environnement parent complet. Supprime uniquement , , , . Débogage uniquement |
cplt découvre automatiquement les outils installés et écrit les règles de sandbox en conséquence. Généralement, seuls les répertoires qui existent sur le disque reçoivent des règles, donc il n'y a pas de chemins fantômes. Sous macOS, les répertoires d'applications inscriptibles sont inclus lorsqu'ils sont découverts même s'ils n'existent pas encore, afin qu'ils puissent être créés à la première utilisation. Linux ne peut pas autoriser une écriture vers un chemin inexistant, donc la création doit avoir lieu en dehors du sandbox dans ce cas.
Exécutez cplt doctor pour voir si cplt fonctionnera ici pour votre agent, et cplt doctor --verbose pour tout ce qu'il a détecté sur votre machine.
Ceux-ci se traduisent en drapeaux de session propres à l'agent, donc vous n'avez pas besoin d'un séparateur --.
--continue et --resume sont également mappés pour OpenCode, Antigravity et Claude Code :
¹ Ni OpenCode ni Antigravity n'a de sélecteur de session interactif, donc un --resume seul signifie « continuer la dernière session ». Claude Code en a un, donc il est mappé directement.
--remote et --name sont réservés à Copilot. Le mode Pi et shell ne reçoit aucune traduction du tout, donc les quatre drapeaux sont abandonnés pour eux. La reprise automatique est un mécanisme distinct : lorsque vous invoquez cplt sans arguments de passage ni drapeaux de session, il ajoute --resume pour vous, et cela ne s'applique qu'à Copilot.
Combinez-les avec les drapeaux de sandbox et les arguments de passage -- :```bash
cplt --resume=my-task # resume by name
cplt --remote --name my-task -- -p "fix tests" # remote + named + prompt
### Agents
Choisissez-en un avec `--agent <name>`, ou définissez-le par défaut avec `cplt config set sandbox.agent <name>`. Copilot, OpenCode et Antigravity sont auto-détectés depuis `PATH` dans cet ordre lorsque vous n'en nommez aucun.
| Agent | Valeur `--agent` | Auto-détecté | Auth |
| --- | --- | --- | --- |
| GitHub Copilot CLI | `copilot` | oui, priorité 1 | Jeton GitHub, depuis le Keychain ou `gh` |
| [OpenCode](https://opencode.ai/) | `opencode` | oui, priorité 2 | Abonnement Copilot via `/connect`, ou `--pass-env ANTHROPIC_API_KEY` |
| [Antigravity CLI](https://github.com/google-antigravity/antigravity-cli) | `antigravity`, alias `agy` et `agi` | oui, priorité 3 | Google OAuth dans le navigateur |
| [Pi](https://github.com/earendil-works/pi) | `pi` | non | `--pass-env ANTHROPIC_API_KEY` et autres |
| [Claude Code](https://docs.anthropic.com/en/docs/claude-code) | `claude`, alias `cc` et `claude-code` | non | OAuth d'abonnement dans `~/.claude` ou le Keychain, `CLAUDE_CODE_OAUTH_TOKEN` (supprime l'autorisation Keychain), ou `--pass-env ANTHROPIC_API_KEY` |
| [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) | `dsh`, alias `deepseek` et `deepseek-harness` | non | `--pass-env DEEPSEEK_API_KEY`, ou `$DSH_HOME/.env` (`~/.dsh/.env`) |
| Votre shell | `shell` | non | aucun |
- **Pi, Claude Code, goose et DeepSeek Harness ne sont jamais auto-détectés.** `pi` et `dsh` sont des noms de binaires génériques qui pourraient entrer en collision avec autre chose sur votre machine, et Claude Code doit être choisi intentionnellement.
- **Les clés API tierces sont opt-in.** `ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, `GEMINI_API_KEY`, `OPENROUTER_API_KEY`, `ANTHROPIC_AUTH_TOKEN`, `CLAUDE_CODE_OAUTH_TOKEN` et les variables de routage Bedrock/Vertex (`CLAUDE_CODE_USE_BEDROCK`, `AWS_BEARER_TOKEN_BEDROCK`, `CLAUDE_CODE_USE_VERTEX`, `ANTHROPIC_VERTEX_PROJECT_ID`, `GOOGLE_CLOUD_PROJECT`) ne passent jamais à moins que vous ne les nommiez avec `--pass-env`.
- **L'authentification par abonnement ne nécessite aucune variable d'environnement.** Le flux d'appareil `/connect` d'OpenCode stocke son jeton dans `~/.local/share/opencode/auth.json`, et le jeton OAuth de Claude Code réside dans `~/.claude` (`.credentials.json` sous Linux) ou dans le Keychain macOS. Les deux sont accessibles à l'intérieur du sandbox, donc cplt ne vous ennuie pas avec une clé API manquante pour l'un ou l'autre.
- **Les flux OAuth dans le navigateur nécessitent `--allow-browser`** lorsqu'une invite de connexion apparaît. Cela couvre Antigravity ; tous les autres agents ici utilisent un flux d'appareil qui affiche un code et une URL et ne nécessite aucun navigateur. Le flag permet à l'agent de lancer n'importe quelle application en dehors du sandbox et ne peut pas être restreint à des URLs, donc activez-le pour la connexion puis désactivez-le — voir le [tableau des flags](#sandbox-toggles) et [docs/security.md](https://github.com/navikt/cplt/blob/main/docs/security.md#--allow-browser-is-a-sandbox-escape-and-cannot-be-scoped).
- **La mise à jour automatique de Claude Code est désactivée** avec `DISABLE_AUTOUPDATER=1`. Claude Code n'a pas de flag `--no-auto-update`, l'auto-mise à jour à l'intérieur du sandbox est un vecteur de persistance, et elle échouerait de toute façon contre des chemins d'installation en lecture seule.
- **`CLAUDE_CONFIG_DIR` est honoré.** Lorsqu'il est défini, cplt accorde ce répertoire au lieu de `~/.claude` et transmet la variable, afin qu'une racine de configuration déplacée continue de fonctionner.
- OpenCode est [un client Copilot officiellement pris en charge](https://github.blog/changelog/2026-01-16-github-copilot-now-supports-opencode/), donc votre abonnement Copilot existant fonctionne avec `/connect` dans OpenCode.
Les répertoires de configuration par agent, l'utilisation du Keychain, les permissions d'exécution et l'isolation de l'environnement sont dans [SECURITY.md](https://github.com/navikt/cplt/blob/main/SECURITY.md#supported-agents).
### Prise en charge de goose
cplt peut sandboxer [goose](https://github.com/aaif-goose/goose), l'agent IA open-source (binaire `goose`). Vérifié avec 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
Notes de sécurité pour goose :
--agent goose ou définissez sandbox.agent = "goose" dans la configANTHROPIC_API_KEY, OPENAI_API_KEY, AZURE_OPENAI_API_KEY, GOOGLE_API_KEY, DATABRICKS_HOST/DATABRICKS_TOKEN, GROQ_API_KEY, OPENROUTER_API_KEY, XAI_API_KEY, AWS_BEARER_TOKEN_BEDROCK) sont reconnues comme indices d'authentification et doivent être passées via --pass-env. goose lit , pas . Tout fournisseur en dehors de ce sous-ensemble fonctionne quand même : nommez sa variable avec cplt peut sandboxer DeepSeek Harness (binaire dsh), le harnais d'agent orienté plugins de DeepSeek. En amont, il est livré en aperçu développeur et son propre SAFETY.md indique de ne pas se fier à ses contrôles comme seule frontière, ce qui est précisément le cas pour lequel cplt existe.```bash
cplt --agent dsh
cplt --agent dsh --pass-env DEEPSEEK_API_KEY
cplt config set sandbox.agent dsh
**Notes de sécurité pour DSH :**
- **Non détecté automatiquement** : sélectionnez-le avec `--agent dsh` (alias `deepseek`, `deepseek-harness`) ou définissez `sandbox.agent = "dsh"`. `dsh` est un nom de commande court et générique qui pourrait appartenir à autre chose sur votre machine
- **Désactivez le sandbox propre à DSH à l'intérieur de cplt** : DSH enveloppe chaque appel d'outil shell et fichier dans son propre sandbox de processus — Seatbelt sur macOS, bwrap ou Landlock sur Linux. Ni l'un ni l'autre ne s'imbrique dans cplt. macOS ne prend pas en charge les appels `sandbox-exec` imbriqués (la même limitation qui fait que cplt désactive le sandbox interne de Gradle, voir [Limitations](#limitations)), et bwrap construit son namespace avec `unshare`, que le filtre seccomp de cplt refuse. cplt est la frontière d'application dans tous les cas, donc choisissez le preset de permissions `danger-full-access` fourni par DSH pour les sessions sandboxées. Laissez le runner interne activé et les appels d'outils échoueront avec une erreur de runner sandbox plutôt qu'une erreur de tâche
- **Une seule racine home, et cplt suit la substitution** : DSH conserve les sessions, les paramètres, le cache et les profils sous `$DSH_HOME` (`~/.dsh` par défaut). `DSH_HOME` figure sur la liste d'autorisation des variables d'environnement, donc le processus enfant résout la même racine que celle accordée par cplt. Une valeur pointant vers une racine système ou votre répertoire home est refusée avant le lancement, le même veto que subit `CLAUDE_CONFIG_DIR`
- **Garde de persistance de l'hôte** : `$DSH_HOME/cordis.patch.yml`, l'overlay au niveau du home que le Loader lit au démarrage, est en écriture refusée. `$DSH_HOME/profiles/` reste accessible en écriture car DSH réécrit la racine d'inclusion `cordis.yml` de chaque profil à chaque démarrage, donc un `cordis.patch.yml` par profil et les plugins installés constituent un résidu documenté — effectuez les modifications de profil et de `dsh plugin` en dehors de cplt, et lancez toujours `dsh` via cplt pour que tout ce qui est planté s'exécute malgré tout en sandbox
- **Domaines par défaut** : `deepseek.com` uniquement. L'adaptateur `dsh-llm-deepseek` fourni pointe par défaut vers `https://api.deepseek.com`. Pointez `DEEPSEEK_BASE_URL` vers une passerelle et vous devrez ajouter le domaine de cette passerelle via `allowed_domains`
- **Authentification** : passez la clé avec `--pass-env DEEPSEEK_API_KEY`, ou conservez-la dans `$DSH_HOME/.env`. Une clé enregistrée via l'interface de modèles propre à DSH atterrit dans `$DSH_HOME/.credentials.yaml`, à l'intérieur de la même racine accessible en écriture. Le Keychain macOS est refusé, donc `git push` via HTTPS nécessite le token de `gh` dans `hosts.yml` ou `--pass-env GH_TOKEN`
### Mode shell
Lancez un shell sandboxé simple sans agent IA et avec les mêmes restrictions. Pratique pour tester des outils de build, déboguer des problèmes de sandbox, ou simplement travailler soigneusement à la main.```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
Les mêmes règles de refus par défaut s'appliquent : isolation du système de fichiers, restrictions réseau, assainissement de l'environnement. Les répertoires de configuration du shell (variables et historique fish, historique zsh) restent inscriptibles.
Pour une seule commande, cplt exec est plus propre que cplt --agent shell -- -c 'cmd'.
Exécutez n'importe quelle commande à l'intérieur du bac à sable sans démarrer d'agent. Pas de bannière de démarrage, pas d'invite de confirmation, ce qui le rend adapté aux scripts, aux pipes et aux 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"
Chaque option de premier niveau de `cplt` s'applique : `--project-dir`, `--allow-read`, `--deny-path`, `--with-proxy`, `--pass-env`, et le reste. Ajoutez `--no-quiet` pour voir le résumé complet de la configuration du bac à sable avant l'exécution de la commande.
### Exemples```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 configuration s'effectue à deux niveaux : global, pour les préférences du développeur, et par dépôt, pour la politique d'équipe.```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` est l'éditeur interactif, avec les vues Effective, Global et Repository, la recherche, les modifications indexées et une confirmation explicite avant d'enregistrer tout élément sensible pour la sécurité. `cplt config` reste l'interface non interactive stable pour les scripts et la CI. Les propositions de dépôt sont toujours validées et approuvées séparément avec `cplt trust`. L'éditeur ne les valide ni ne les approuve automatiquement.
La priorité s'applique aux indicateurs CLI, puis au fichier de configuration global à `~/.config/cplt/config.toml`, puis aux valeurs par défaut intégrées. La configuration par dépôt dans `.cplt.toml` est une couche distincte plutôt qu'un échelon de cette hiérarchie : `[deny]` se resserre inconditionnellement, et les permissions approuvées sont uniquement additives, de sorte qu'un dépôt peut activer une fonctionnalité mais ne peut jamais désactiver quelque chose défini par un indicateur CLI ou la configuration globale.
Un `.cplt.toml` à la racine du dépôt porte la politique d'équipe :```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 le lit depuis git HEAD, donc l'agent ne peut pas altérer sa propre politique en cours de session, et les approbations de confiance sont épinglées au contenu du fichier. Un .cplt.toml non commité n'accorde rien tant qu'il n'est pas commité, bien que ses clés [deny] s'appliquent toujours. Dans les CI et les scripts, où personne ne peut répondre à une invite, --accept-repo-config approuve les propositions du fichier commité pour cette exécution unique sans persister aucune confiance. cplt init en écrit un pour vous en détectant l'outillage du projet :```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
Il connaît la JVM (Gradle/Maven), Node.js, Docker, Python, Rust, Go, Playwright, Spring Boot, Ktor, TestContainers, Next.js, Vite, Flyway, Cypress, et les secrets d'environnement depuis `.env.example`. Les permissions dangereuses sortent du générateur avec un avertissement de risque attaché. `--global` examine plutôt les éléments au niveau de la machine : navigateurs Playwright, signature GPG, identifiants de registre, agents alternatifs.
Certaines clés sont globales uniquement et rejetées depuis `.cplt.toml` car elles sont spécifiques à la machine ou constituent une préférence 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`, et toutes les clés `[gh_guard]` et `[git_guard]`.
Détails complets, y compris le modèle de confiance, les règles d'expansion des chemins, et la référence complète du fichier de configuration : [docs/configuration.md](https://github.com/navikt/cplt/blob/main/docs/configuration.md).
## Architecture```
┌──────────────────────────────────┐
│ 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 │
└──────────────────────────────────┘
Le modèle de sécurité est un système de fichiers en refus par défaut avec application au niveau du noyau. Sur macOS, et sur Linux avec un noyau 6.7+ (Landlock ABI v4), le réseau est restreint au port 443 par défaut, avec --allow-port pour les exceptions. Sur les noyaux Linux plus anciens, le proxy CONNECT assure cette restriction à la place, ce qui explique qu'il soit activé par défaut. L'accès à l'agent SSH et les connexions sortantes vers localhost sont bloqués dans le noyau sur macOS. Sur Linux, ni l'un ni l'autre ne l'est : les règles Landlock basées sur les ports ne peuvent pas distinguer localhost d'un hôte distant, et le connect() sur socket unix n'est pas contrôlé par Landlock avant le noyau 7.1, donc à part les sockets masqués par bubblewrap, le SSH_AUTH_SOCK retenu est la seule chose qui sépare l'agent de vos clés chargées. Le générateur de profil découvre votre environnement (cplt doctor --verbose affiche les mêmes résultats de sonde) et n'émet des règles que pour les répertoires d'outils qui existent réellement sur le disque. Moins de règles, un bac à sable plus strict.
sandbox-execpre_exec (noyau 5.13+, filtrage des ports TCP sur 6.7+)Fonctionnement interne et organisation des modules : docs/architecture.md. Modèle de menace, couches de défense et lacunes assumées : SECURITY.md.
Un seul binaire, des dépendances minimales, aucun service d'exécution, aucune télémétrie. Trois couches de défense, avec des frontières claires entre elles :
Ce contre quoi cplt protège :
.env) : bloqué par le noyau.git/hooks est en refus d'écriture au niveau du noyau sur macOS. Sur Linux, avec Landlock et sans Bubblewrap, il reste accessible en écriture, et le git parent de cplt s'exécute alors avec core.hooksPath=/dev/null pour ne jamais exécuter un hook planté, bien qu'un git que vous lancez vous-même le fera toujoursPNPM_HOME, ~/.deno/bin, ~/.bun/bin) : l'écriture y est accordée pour que pnpm add -g et compagnie fonctionnent dans le bac à sable, donc un agent peut y laisser un binaire qu'un shell ultérieur récupérera depuis votre PATHCe contre quoi cplt ne protège pas :
sandbox.keychain_substitute peut échanger cette autorisation là où un agent dispose d'un autre identifiantNos priorités, dans l'ordre : correct (chaque affirmation est testée, chaque cas limite a une référence CVE ou de recherche), transparent (SECURITY.md ne cache rien), simple (un binaire, aucune configuration requise, des valeurs par défaut raisonnables), et utile (s'effacer et laisser l'agent travailler, en toute sécurité).
Plus : docs/security.md · SECURITY.md
Le proxy est activé par défaut. Tout le trafic sortant de Copilot CLI, gh et curl passe par un proxy CONNECT localhost via HTTP_PROXY/HTTPS_PROXY et NODE_USE_ENV_PROXY=1. Il écoute sur un port éphémère attribué par l'OS, donc rien n'entre en collision. Vous obtenez la journalisation des connexions en temps réel, le blocage de domaines, la liste d'autorisation de domaines, un journal d'audit persistant, et la même politique de ports que celle appliquée par le bac à sable (443 plus tout ce qui figure dans 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>` écrit l'ensemble observé, un domaine par ligne, et
`--proxy-upstream-no-proxy <HOST>` liste les hôtes à joindre directement plutôt qu'en passant par
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"
Le mode forcé par proxy est opt-in. Il restreint la sortie réseau du noyau au port du proxy afin qu'un socket ouvert directement, ou un env -u HTTPS_PROXY, ne puisse pas passer outre. L'application est complète sur macOS, qui s'ancre à localhost:<proxy_port>. Sur Linux, il bloque le TCP direct :443, et une règle seccomp n'autorise que SOCK_STREAM avec le protocole 0 ou IPPROTO_TCP pour AF_INET/AF_INET6, de sorte qu'UDP, raw, SCTP et DCCP sont également fermés — au prix de tout ce qui ouvre un tel socket, pas seulement du code qui envoie de l'UDP. Ce qui subsiste est un résidu basé sur le port, evil.com:<proxy_port>, jusqu'à #114.
En dehors du mode forcé par proxy, Linux ne restreint pas l'UDP. Les droits réseau de Landlock sont TCP uniquement jusqu'à l'ABI v10, cplt ne gère que AccessNet::ConnectTcp, et la règle seccomp ci-dessus n'est délibérément pas appliquée — refuser SOCK_DGRAM à cet endroit casserait getaddrinfo(3), et donc tout le DNS, pour chaque outil non proxifié. L'UDP sortant vers n'importe quel hôte, le bind UDP entrant, le tunnelling DNS et QUIC/HTTP-3 sont donc non médiés en mode par défaut, et le proxy CONNECT ne transporte que du TCP, donc rien de tout cela n'apparaît dans le journal du proxy. macOS restreint l'UDP en mode par défaut mais ne le route pas non plus : remote ip "*:443" couvre l'UDP, donc QUIC/HTTP-3 sur 443 sort sans passer par le proxy là aussi. Sous proxy.forced, le journal du proxy est un enregistrement complet de la sortie réseau sur macOS. Sur Linux, il est complet sauf pour le résidu evil.com:<proxy_port> ci-dessus, qui ne traverse pas le proxy et n'apparaît donc pas dans son journal.
Les deux listes fonctionnent de la même manière : example.com couvre le domaine exact et tous les sous-domaines, la correspondance est insensible à la casse, et les points finaux sont supprimés. Les fichiers de blocklist et d'allowlist sont relus toutes les cinq secondes, vous pouvez donc les modifier à chaud. Le trafic localhost contourne le proxy via NO_PROXY et n'apparaît jamais dans le journal d'audit. --proxy-timeout <SECONDS> borne les lectures de requête et d'en-tête (60 par défaut) et ne démonte pas les tunnels CONNECT établis, qui peuvent rester inactifs jusqu'à une heure.
Chaque option de proxy, détail de filtrage de domaine, chaînage de proxy d'entreprise en amont, et le format du journal de connexion : docs/proxy.md.
Activez-les et cplt intercepte gh et git via des scripts wrapper dans $PATH :
C'est la couche 3, une barrière souple. Elle empêche un agent conforme de faire quelque chose de destructeur par accident. Pour une frontière dure, appuyez-vous sur le sandbox noyau et la protection de branche côté serveur.
Avec la garde gh activée, cplt met aussi en cache le token GitHub au lancement et le sert une fois via le callback gh auth token, puis supprime le cache. Cela réduit les fuites accidentelles et liées à l'environnement. Ce n'est pas une frontière contre un agent hostile, car le cache réside dans le TMPDIR de l'agent lui-même et un agent qui le lit avant le consommateur légitime obtient quand même le token. SECURITY.md contient la déclaration complète sur block_auth_token.
Comportement complet : docs/gh-guard.md · docs/git-guard.md
Le sandbox bloque certains workflows à dessein. Les plus courants et leurs correctifs :
Playwright Chromium nécessite cplt config set sandbox.allow_cache_exec ms-playwright, et Chromium doit s'exécuter sans son propre sandbox imbriqué. Sur macOS, ses helpers ne peuvent pas initialiser un second sandbox Seatbelt à l'intérieur de cplt (forbidden-sandbox-reinit) ; sur Linux, le filtre seccomp de cplt bloque les appels système de namespace dont ce sandbox a besoin. Playwright en tant que bibliothèque se lance déjà avec --no-sandbox, et cette même option active PLAYWRIGHT_MCP_SANDBOX=false pour Playwright MCP, qui sinon le réactiverait. Tout autre lanceur Chromium a besoin de --no-sandbox lui-même. cplt reste la frontière noyau appliquée, mais un renderer compromis reçoit alors le profil Playwright complet de cplt au lieu du profil enfant plus restreint de Chromium. Voir Cache exec et SECURITY.md.
Git commit fonctionne pour chaque agent ; le bon fonctionnement de git push en HTTPS dépend de l'agent. Trois prérequis : utilisez des remotes HTTPS plutôt que SSH (git remote set-url origin https://github.com/org/repo.git, ou réécrivez globalement avec git config --global url."https://github.com/".insteadOf "[email protected]:"), exécutez gh auth login une fois en dehors du sandbox, et exécutez gh auth setup-git si le credential helper n'est pas encore configuré. Le push exécute alors gh auth git-credential, qui a besoin d'un token que gh peut atteindre depuis l'intérieur du sandbox — cela diffère selon l'agent, voir Git workflow. Les pushs vers la branche par défaut et tous les force pushs sont refusés par défaut par la garde git ; poussez une branche de fonctionnalité. Le socket de l'agent SSH est bloqué car il déverrouille toutes les clés chargées et peut s'authentifier auprès de n'importe quel hôte, tandis que le credential helper gh est limité à GitHub.
La JVM est proxy-aware, donc un dépôt Maven interne sur une IP privée doit désormais être autorisé. cplt injecte http(s).proxyHost/proxyPort dans JAVA_TOOL_OPTIONS, de sorte que la résolution de dépendances Gradle et Maven passe par le proxy CONNECT et apparaît dans le journal du proxy au lieu de le contourner. La garde SSRF du proxy refuse alors un Nexus ou Artifactory interne qui se résout dans l'espace d'adressage privé, exactement comme elle le fait déjà pour curl, npm et pip. Ajoutez son nom DNS à proxy.allow_private_domains. Une URL de dépôt écrite comme un littéral IP nu (https://10.20.30.40/repository/maven-public/) ne peut être autorisée par aucune clé — cette vérification s'exécute avant que l'allow list ne soit consultée — donc un tel dépôt a besoin d'un nom DNS. Les forks de plugin WorkerExecutor, et un daemon Gradle démarré en dehors de cplt et réutilisé à l'intérieur, ne sont pas proxifiés. Voir Internal Maven/Gradle repositories on private IPs.
Gradle 9+ exécute son propre sandbox imbriqué, et cplt le désactive. Depuis Gradle 8.8, le daemon s'enveloppe dans sandbox-exec (contrôlé par GRADLE_MACOS_SANDBOX, auparavant la propriété org.gradle.daemon.sandbox). macOS ne prend pas en charge les appels sandbox-exec imbriqués, donc le sandbox interne échoue avec « Operation not permitted » sur les opérations de socket. cplt injecte GRADLE_MACOS_SANDBOX=off, puisqu'il fournit déjà un sandboxing au niveau noyau. C'est un problème amont connu qui touche tout outil enveloppant Gradle dans un sandbox externe. Remplacez avec --pass-env GRADLE_MACOS_SANDBOX si vous voulez vraiment le sandbox propre de Gradle.
Copilot CLI 1.0.83 exécute son propre sandbox imbriqué, et cplt le désactive. Sur Linux, ce sandbox construit un namespace réseau — slirp4netns, iptables, /dev/net/tun — et le filtre seccomp de cplt refuse le unshare qu'il effectue. cplt définit aussi HTTP_PROXY/HTTPS_PROXY, ce qui en 1.0.83 place un sandbox Linux sur le chemin de sortie proxy que vous l'ayez demandé ou non, de sorte que les deux entrent en collision à chaque lancement. Symptôme : [cplt] Starting Copilot in sandbox... puis rien. cplt injecte l'opt-out propre de Copilot, COPILOT_CLI_SANDBOX_SUPPORT_OVERRIDE=unsupported ; Copilot se met en retrait pour la session, le signale, et laisse votre sandbox.enabled enregistré intact. cplt est la frontière, comme pour Gradle et Chromium. Remplacez avec --pass-env COPILOT_CLI_SANDBOX_SUPPORT_OVERRIDE. Une politique gérée d'entreprise qui exige le sandbox prime sur tout cela — voir Copilot CLI's own command sandbox.
Chaque impact, avec les tableaux par outil, les notes sur les daemons JVM et Kotlin, le dépannage GPG, et les différences de plateforme pour les registres privés : docs/known-impacts.md.
sandbox-exec est déprécié. Apple ne l'a pas supprimé, mais pourrait le faire dans une future version de macOS.lsopen de SBPL n'a pas de filtre non plus, donc --allow-browser c'est tout Launch Services ou rien. Avec cette option activée, l'agent peut lancer n'importe quelle application en dehors du sandbox, et aucun wrapper ne peut restreindre cela — voir docs/security.md..env dans le répertoire du projet n'est pas appliquée par le noyau. Les écritures dans .git/hooks sont bloquées lorsque Bubblewrap est actif.--deny-path nécessite Bubblewrap. Il est appliqué via des masques de montage lorsque bwrap est actif. Sans lui, Landlock est en allowlist uniquement et cplt avertit à propos du deny au lieu de l'appliquer.Plus d'informations : docs/security.md
Les contributions sont les bienvenues.```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
Ouvrez une issue avant d’entamer une modification importante. Chaque PR doit passer la CI (fmt, clippy, tests).
## Références
- [SECURITY.md](https://github.com/navikt/cplt/blob/main/SECURITY.md), le modèle de sécurité complet, l’analyse des menaces, la stratégie de test et les travaux antérieurs
- [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)
- [Documentation de Landlock LSM](https://docs.kernel.org/userspace-api/landlock.html)
- [Documentation de seccomp-BPF](https://www.kernel.org/doc/html/latest/userspace-api/seccomp_filter.html)
- [Aide-mémoire OWASP sur la prévention des SSRF](https://cheatsheetseries.owasp.org/cheatsheets/Server_Side_Request_Forgery_Prevention_Cheat_Sheet.html)
- [michaelneale/agent-seatbelt-sandbox](https://github.com/michaelneale/agent-seatbelt-sandbox)
## Licence
[MIT](https://github.com/navikt/cplt/blob/main/LICENSE)
| Drapeau | Ce qu'il fait |
|---|
--preset strict | Verrouillage réseau complet. Les cinq commutateurs désactivés, plus gh_guard, git_guard, proxy.forced (sortie via proxy forcé) et proxy.default_allowlist (liste d'autorisation de domaines en échec fermé) activés. Échappatoire : --allow-all-domains désactive uniquement la liste d'autorisation |
--preset standard | Les valeurs par défaut actuelles. Les cinq désactivés, le répertoire scratch reste activé. Identique à ne passer aucun préréglage |
--preset permissive | Active allow_localhost_any, allow_tmp_exec et allow_lifecycle_scripts |
--preset full-trust | ⚠️ Dangereux. Active les cinq, en ajoutant allow_env_files et allow_docker |
| Drapeau | Ce qu'il fait |
|---|
-d, --project-dir <DIR> | Le répertoire dans lequel Copilot peut travailler. Par défaut, la racine du dépôt git courant |
--allow-read <PATH> | Autoriser Copilot à lire des fichiers en dehors du projet, en lecture seule. Répétable |
--allow-write <PATH> | Autoriser Copilot à lire et écrire en dehors du projet. À utiliser avec précaution. Répétable. L'arborescence est inscriptible mais non exécutable — une arborescence qui est les deux constitue un chemin de dépôt de binaires, donc un allow.write sur ~/.cargo empêche aussi l'exécution de ~/.cargo/bin. Utilisez --allow-exec sur une arborescence distincte et non chevauchante lorsque vous avez besoin des deux |
--allow-exec <PATH> | ⚠️ Dangereux. Autoriser l'agent à exécuter des binaires depuis une arborescence en dehors des répertoires d'outils par défaut — un Homebrew ou un préfixe de chaîne d'outils déplacé, par exemple. Accorde la lecture et l'exécution, jamais l'écriture. Répétable. Refusé pour une racine non sûre (/, /tmp, $HOME et ses parents, les répertoires système de la plateforme) et pour toute arborescence qui chevauche une arborescence inscriptible — le répertoire du projet, une autorisation --allow-write, un répertoire d'outils inscriptible tel que ~/.cache, un répertoire de données d'agent inscriptible (~/.claude, ~/.local/share/opencode, ~/.pi/agent et similaires), le vrai .git d'un worktree ou d'un dépôt nu, ou une arborescence que les backends rendent inscriptible sans aucune autorisation (/tmp et /dev/shm sous Linux ; /private/tmp et /private/var/folders sous macOS) : inscriptible plus exécutable constitue un chemin de dépôt de binaires, et aucun backend ne peut soustraire l'autorisation d'écriture de l'autorisation d'exécution |
--allow-socket <PATH> | ⚠️ Dangereux. Autoriser un chemin de socket de domaine Unix, par exemple un démon LSP personnalisé ou un socket de base de données. Répétable. Tout ce qui se trouve à l'autre bout s'exécute en dehors du sandbox, donc pointer ceci vers docker.sock ou un socket d'agent équivaut à --allow-docker, et le seul garde-fou est que les chevauchements avec --deny-path sont rejetés. Sous Linux, cela ne fait rien en dessous du noyau 7.1, car les connexions aux sockets unix ne sont pas contrôlées par Landlock avant l'ABI v9 (voir Limitations Linux) |
--deny-path <PATH> | Bloquer un chemin qui serait autrement autorisé. Le refus l'emporte toujours. Répétable |
--allow-port <PORT> | Autoriser le trafic sortant sur un port supplémentaire. Seulement 443 par défaut. Répétable. Sous macOS, la règle est (remote ip "*:PORT"), qui est agnostique en termes de famille et transporte donc UDP ainsi que TCP ; Landlock ne contrôle que la connexion TCP. Sous proxy.forced, le port n'ouvre aucun socket direct — il est joignable via le proxy, donc les outils compatibles proxy continuent de fonctionner |
--allow-localhost <PORT> | Autoriser le trafic sortant vers localhost sur un port. Localhost est bloqué par défaut. À utiliser pour les serveurs MCP ou les serveurs de développement. Répétable |
--allow-localhost-any | Autoriser le trafic sortant vers localhost sur tous les ports. Nécessaire pour les outils de build comme Turbopack (Next.js) et Vite qui utilisent des ports éphémères aléatoires pour l'IPC |
| Catégorie | Exemples | Comment |
|---|
| Système de base | HOME, USER, PATH, SHELL, TMPDIR, LANG | Liste d'autorisation explicite |
| Terminal | TERM, COLORTERM, TERM_PROGRAM | Liste d'autorisation explicite |
| Éditeur | EDITOR, VISUAL, PAGER | Liste d'autorisation explicite |
| Jetons d'authentification | GH_TOKEN, GITHUB_TOKEN, COPILOT_GITHUB_TOKEN | Transmis uniquement si vous les avez déjà définis. Le garde gh utilise un fichier à usage unique à la place |
| Configuration Copilot | COPILOT_DEBUG, COPILOT_* | Liste d'autorisation par préfixe |
| Runtimes de langage | NODE_*, GOPATH, CARGO_HOME, JAVA_HOME, VIRTUAL_ENV, PYTHONPATH | Liste d'autorisation explicite |
| Gestionnaires d'outils | NVM_*, FNM_*, PYENV_*, MISE_*, SDKMAN_*, COREPACK_*, YARN_* | Liste d'autorisation par préfixe |
| OpenTelemetry | OTEL_EXPORTER_OTLP_ENDPOINT, OTEL_SERVICE_NAME, OTEL_RESOURCE_ATTRIBUTES, OTEL_* | Liste d'autorisation par préfixe (OTEL_EXPORTER_OTLP_HEADERS peut porter une authentification opt-in) |
| Répertoires XDG | XDG_CONFIG_HOME, XDG_DATA_HOME, XDG_STATE_HOME, XDG_CACHE_HOME | Liste d'autorisation explicite |
NO_COLORFORCE_COLORSSH_AUTH_SOCKSSH_AGENT_PID| Drapeau | Ce qu'il fait |
|---|
--allow-lifecycle-scripts | Autoriser l'exécution des scripts de cycle de vie npm/yarn/pnpm (hooks postinstall). Bloqués par défaut. À utiliser lorsque npm install en a besoin |
--allow-gpg-signing | Autoriser la signature GPG des commits et tags à l'intérieur du sandbox. Accorde un accès en lecture seule au trousseau de clés publiques et au socket de l'agent GPG. Les clés privées restent refusées. Voir Signature GPG |
--allow-jvm-attach | Autoriser les sockets unix de l'API JVM Attach dans /tmp. Nécessaire pour le mocking inline MockK, les agents inline Mockito, ByteBuddy. Voir API JVM Attach |
--allow-msbuild | Autoriser les sockets unix des nœuds de travail MSBuild dans /tmp. Nécessaire pour dotnet build. N'active pas le MSBuild Server persistant. Voir IPC des nœuds de travail MSBuild |
--no-scratch-dir | Désactiver le répertoire scratch par session, qui est activé par défaut. TMPDIR ne sera pas redirigé |
--scratch-dir | Activer explicitement le répertoire scratch par session. Déjà la valeur par défaut, donc ceci sert à remplacer scratch_dir = false dans la configuration |
--brief | 🧪 Expérimental. Écrire le brief de sandbox destiné à l'agent dans le répertoire scratch (CPLT_BRIEF.md). Désactivé par défaut. Également sandbox.brief = true dans la configuration. Instable, donc susceptible de changer ou d'être supprimé dans une future version |
--no-brief | Désactiver le brief de sandbox pour cette exécution, en remplaçant sandbox.brief = true dans la configuration. Supprime également le bloc AGENTS.md, qui est conditionné au brief |
--agents-md | 🧪 Expérimental. Avec --brief, écrire également le bloc cplt géré dans le AGENTS.md du projet. Désactivé par défaut. Également sandbox.agents_md = true dans la configuration. Sans effet sans --brief. Instable, donc susceptible de changer ou d'être supprimé dans une future version |
--no-agents-md | Désactiver le bloc AGENTS.md pour cette exécution, en remplaçant sandbox.agents_md = true dans la configuration. Laisse le brief du répertoire scratch intact |
--allow-tmp-exec | ⚠️ Dangereux. Autoriser l'exécution depuis les répertoires temporaires système (/private/tmp, /private/var/folders). Préférez le répertoire scratch |
--allow-cache-exec <SUBDIR> | Autoriser l'exécution depuis un ~/Library/Caches/<SUBDIR>. Répétable. Pour les outils qui mettent en cache des binaires compilés à cet endroit, tels que Playwright et pnpm dlx |
--allow-cache-exec-any | ⚠️ Dangereux. Autoriser l'exécution depuis tout ~/Library/Caches. Préférez --allow-cache-exec <SUBDIR> |
--allow-browser | ⚠️ Dangereux. Avec ceci activé, l'agent peut lancer n'importe quelle application sur votre machine en dehors du sandbox. L'autorisation est Launch Services, pas un navigateur : launchd démarre la cible en dehors du profil Seatbelt, donc open -a Terminal /tmp/x.sh s'exécute sans sandbox. Cela ne peut pas être limité aux URL — le lsopen de SBPL ne prend aucun filtre, et l'autorisation est accessible via LSOpenCFURLRef() sans même le binaire open, donc aucun wrapper ne peut la restreindre (#251, et docs/security.md). Ne l'activez que lorsqu'une invite de connexion est réellement à l'écran (OAuth de serveur MCP, ré-authentification), puis désactivez-le. Désactivé par défaut |
--deny-clipboard | Bloquer la lecture ou l'écriture du presse-papiers macOS par l'agent (pbpaste/pbcopy) en refusant le service Mach com.apple.pasteboard. Tous les autres services Mach (Keychain, DNS, Security framework) ne sont pas affectés. Activé par défaut — ce drapeau réaffirme la valeur par défaut |
--allow-clipboard | Rendre à l'agent le presse-papiers macOS, que cplt refuse par défaut. Équivalent à sandbox.deny_clipboard = false |
--use-bubblewrap | Linux uniquement. Exiger la couche de namespaces bubblewrap (PID, mount, IPC, UTS, cgroup, user namespaces plus un /tmp privé) en plus de Landlock et seccomp. Renvoie une erreur si bwrap est absent. Détecté automatiquement lorsqu'aucun des deux drapeaux n'est fourni |
--no-bubblewrap | Linux uniquement. Ne jamais utiliser bubblewrap, même s'il est installé. Revient à Landlock et seccomp. À utiliser lorsque bwrap casse un outil spécifique |
| Runtime | Répertoires personnels | Variables d'env / préfixes | Découverte |
|---|
| 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 | aucun | aucun |
| Python | .pyenv | VIRTUAL_ENV, PYTHONPATH, PYENV_ROOT, PYENV_* | python3 |
| Yarn Berry | .yarn | YARN_* (le durcissement remplace YARN_ENABLE_SCRIPTS) | yarn |
| pnpm | Library/pnpm, .local/share/pnpm | PNPM_HOME | pnpm |
| Corepack | aucun | COREPACK_* | aucun |
| mise | .local/share/mise, .mise | MISE_* | mise |
| Drapeau | Ce qu'il fait |
|---|
--doctor | Obsolète. Utilisez plutôt la sous-commande cplt doctor |
--print-profile | Afficher le profil de sandbox généré (SBPL) et quitter |
--show-denials | Diffuser les journaux de refus du sandbox macOS en temps réel |
--no-validate | Ignorer la vérification au démarrage qui confirme que les restrictions du sandbox sont actives |
-y, --yes | Ignorer l'invite de confirmation interactive. Le résumé de configuration s'affiche toujours, pour l'auditabilité. Requis lorsque stdin n'est pas un TTY, donc CI et scripts en ont besoin |
-q, --quiet | Supprimer la bannière de démarrage et les messages non essentiels. Les erreurs et avertissements s'affichent toujours. Également sandbox.quiet = true dans la configuration |
--no-quiet | Remplacer sandbox.quiet = true et afficher le résumé de démarrage quand même |
--no-audit | Ignorer le rapport de modifications post-session. cplt compare normalement l'arborescence de travail à un commit de référence épinglé avant l'exécution et liste ce que la session a touché, en signalant les chemins sensibles. -q le supprime aussi |
--init-config | Créer un fichier de configuration de démarrage à ~/.config/cplt/config.toml et quitter |
| Drapeau | Ce qu'il fait |
|---|
--resume[=SESSION] | Reprendre une session précédente. --resume seul choisit de manière interactive, --resume=NAME choisit par nom ou ID |
--continue | Reprendre la session la plus récente dans le répertoire courant |
--remote | Activer le contrôle à distance, afin que vous puissiez surveiller et piloter la session depuis GitHub.com ou mobile |
--name SESSION | Nommer la session afin que --resume=NAME puisse la retrouver plus tard |
| Drapeau 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 | ignoré | ignoré | ignoré |
--name NAME | --name NAME | ignoré | ignoré | ignoré |
GOOGLE_API_KEYGEMINI_API_KEY--pass-env--observe-domains, donc sa liste d'autorisation intégrée se limite à la base partagée des registres de paquets. Ajoutez le domaine de votre fournisseur via allowed_domains avant d'activer --default-allowlistGOOSE_DISABLE_KEYRING=1 fait que goose utilise un secrets.yaml dans son répertoire de config à la place, et passer la clé avec --pass-env évite complètement le stockage des secrets. Sur Linux, goose utilise le D-Bus Secret Service, que l'autorisation du trousseau n'affecte pas~/.config/goose/config.yaml déclare des entrées extensions: dont goose lance le cmd à chaque démarrage de session, donc un répertoire de config inscriptible est un vecteur de persistance sur l'hôte. Les sessions normales ne l'écrivent pas ; les changements de /mode et les permissions d'outils persistées ne survivent pas à une exécution en sandbox. Reconfigurez avec goose configure en dehors de cplt~/.local/share/goose/) et d'état (~/.local/state/goose/) de goose sont inscriptibles, avec l'exécution refusée. goose utilise ces chemins XDG sur macOS aussi, et honore les surcharges XDG_* à cet endroit--continue et --resume seul correspondent à goose session --resume ; --resume=ID à goose session --resume --session-id ID ; --name X à goose session --name X. Ce sont des flags de sous-commande, donc cplt injecte la sous-commande session avec eux. --remote est ignoré (pas d'équivalent goose)| Couche | Application | Contournable ? | Ce qu'elle protège |
|---|
| 1. Bac à sable noyau | macOS Seatbelt / Linux Landlock+seccomp | ❌ Non | Accès aux fichiers, exec, ports réseau |
| 2. Proxy réseau | Proxy CONNECT, filtrage de domaine | ❌ Non (au sein du bac à sable) | Connexions sortantes, exfiltration |
| 3. Garde de commandes | Scripts enveloppes basés sur le PATH | ⚠️ Barrière souple | Pushes, merges, releases, écritures API |
gitbwrapsandbox-execmiseghPATHcplt doctor : ses sondes --version exécutent chaque binaire d'agent qu'il trouve sur votre PATH, dans le parent, donc un binaire planté s'y exécute — la même exposition au chemin découvert que le lancement ci-dessus, ce qui explique que doctor soit un rapport et non une frontière. Sa vérification de gh est résolue depuis les répertoires de confiance et sa lecture de la version du noyau ne lance rien du tout| Commande | Action |
|---|
gh pr merge, gh repo delete, gh release create | 🔒 Bloqué |
git push origin main, git push --force | 🔒 Bloqué |
gh api (écriture vers d'autres dépôts) | 🔒 Vérification de portée |
gh pr list, gh issue list, git commit | ✅ Autorisé |
git push origin feature-branch | ✅ Autorisé avec protect_default_branch_only |
| Impact | Correctif |
|---|
Fichiers .env bloqués | cplt config set sandbox.allow_env_files true |
| Hooks postinstall npm bloqués | cplt config set sandbox.allow_lifecycle_scripts true |
go test / mise run bloqués (exec temporaire) | Le répertoire scratch est activé par défaut. Si vous en avez encore besoin, cplt config set sandbox.allow_tmp_exec true |
| Connexions localhost bloquées | cplt config set allow.localhost 3000, ou cplt config set sandbox.allow_localhost_any true |
| Docker bloqué | cplt config set sandbox.allow_docker true ⚠️ |
| SSH bloqué | Utilisez plutôt des remotes HTTPS |
| Signature GPG désactivée | cplt config set sandbox.allow_gpg_signing true |
| Échec de JVM MockK/Mockito | cplt config set sandbox.allow_jvm_attach true |
Nœuds worker MSBuild de dotnet build bloqués | cplt config set sandbox.allow_msbuild true |
| Identifiants de registre privé bloqués | cplt config set allow.read "~/.m2/settings.xml" |
| Dépôt Maven/Nexus interne inaccessible (Gradle/Maven) | cplt config set proxy.allow_private_domains "intern.example.com". Une URL de dépôt en littéral IP ne peut pas être autorisée — donnez un nom DNS à l'hôte ; voir ci-dessous |
| Playwright Chromium ne se lance pas | Autorisez l'exec du cache, puis désactivez le sandbox imbriqué de Chromium ; voir ci-dessous |