Cplt: Run AI coding agents or a plain shell inside a kernel-level sandbox
Cplt is a sandbox wrapper for AI coding agents that provides kernel-level filesystem and environment isolation on macOS and Linux. It protects secrets by restricting access to credentials, SSH keys, cloud configs, and blocking lifecycle scripts. Supports Copilot, OpenCode, and other agents with fine-grained control over files, network, and environment variables.
Article intelligence
Key points
- Kernel-level sandbox for AI coding agents using Apple Seatbelt (macOS) or Landlock+seccomp (Linux).
- Blocks access to .env, SSH keys, cloud credentials, git hooks, and other sensitive paths by default.
- Sanitizes environment variables and blocks npm lifecycle scripts as supply chain attack prevention.
- Network control: only port 443 outbound by default, localhost blocked, with options to allow additional ports.
Why it matters
This matters because kernel-level sandbox for AI coding agents using Apple Seatbelt (macOS) or Landlock+seccomp (Linux).
Technical impact
May affect model selection, inference cost, product capability, and evaluation benchmarks.
Notifications You must be signed in to change notification settings
Fork 6
Star 64
BranchesTags
Open more actions menu
Folders and files
NameName
Last commit message
Last commit date
Latest commit
History
173 Commits
173 Commits
.github
.github
assets
assets
docs
docs
hack
hack
src
src
tests
tests
.cplt.toml
.cplt.toml
.gitignore
.gitignore
AGENTS.md
AGENTS.md
Cargo.lock
Cargo.lock
Cargo.toml
Cargo.toml
LICENSE
LICENSE
README.md
README.md
SECURITY.md
SECURITY.md
blocked-domains.txt
blocked-domains.txt
blocked.txt
blocked.txt
cliff.toml
cliff.toml
install.sh
install.sh
mise.toml
mise.toml
Repository files navigation
Sandbox wrapper for AI coding agents. Runs GitHub Copilot CLI, OpenCode, Google Gemini CLI, Pi, or a plain shell inside a kernel-level sandbox so the agent can work on your project but cannot access your secrets.
macOS: Apple Seatbelt/SBPL via sandbox-exec
Linux: Landlock LSM + seccomp-BPF (kernel 5.13+; full network filtering on 6.7+)
Table of contents
Quick start
Install
What it does
Usage
Configuration
Architecture
Security
Contributing
References
Detailed docs: Configuration · Proxy & domain filtering · Known impacts · Security details
Quick start
Install
brew install navikt/tap/cplt
Make 'copilot' run the sandboxed version (persistent)
cplt --shell-install
Check your environment
cplt doctor
Run Copilot in sandbox
cplt -- -p "fix the tests"
Or run OpenCode in sandbox (with Copilot subscription — no API key needed)
cplt --agent opencode
Or with a third-party provider
cplt --agent opencode --pass-env ANTHROPIC_API_KEY
Or just a sandboxed shell (no AI agent, same security restrictions)
cplt --agent shell
Primary control: filesystem isolation. The sandbox blocks access to credentials and secrets at the kernel level. All restrictions apply to the agent and every process it spawns.
Resource Status Notes
Read/write project directory ✅ Allowed
Read/write/delete .env*, .pem, .key in project 🔒 Kernel-blocked Prevents secret exfiltration and destruction; --allow-env-files to override
Write .git/hooks, .git/config, .gitmodules 🔒 Kernel-blocked Prevents persistence via git hooks, hooksPath redirect, submodule hijacking
Execute from /tmp, /var/folders 🔒 Kernel-blocked Prevents write-then-exec; scratch dir redirects TMPDIR to safe location (on by default)
Execute from ~/Library/Caches 🔒 Kernel-blocked by default Prevents binary-drop staging; Copilot native modules exempted via carve-out; --allow-cache-exec to add targeted exemptions (e.g. ms-playwright)
Modify .vscode/tasks.json, launch.json ⚠️ Allowed — known risk IDE trust boundary; see SECURITY.md for mitigations
Read/write ~/.copilot (auth, settings) ✅ Allowed Includes file-map-executable for keytar.node, pty.node, computer.node
Write ~/.copilot/pkg (native modules) 🔒 Kernel-blocked Prevents persistence via native module replacement
Environment variables 🔒 Sanitized + hardened Only safe allowlist passes through; lifecycle scripts blocked; --pass-env VAR to add
Read ~/.config/gh/hosts.yml + config.yml ✅ Allowed (read-only) Only these two files — rest of .config/gh is blocked
Read ~/.config/mise ✅ Allowed (read-only) Tool versions and PATH — no secrets
Read ~/.gitconfig, ~/.config/git/config ✅ Allowed (read-only)
Read global git hooks (core.hooksPath) ✅ Allowed (read-only, write-denied) Auto-detected; must be under $HOME with depth ≥3; writes explicitly blocked
Commit/tag signing (commit.gpgsign, tag.gpgsign) 🔒 Disabled Private keys (~/.ssh, ~/.gnupg) are blocked; signing disabled via env var override
Read ~/Library/Application Support/Microsoft ✅ Allowed (read-only) Device ID for telemetry
Access macOS Keychain ✅ Allowed (read+write) Security framework locks db during access; Copilot uses keytar.node for token storage
Outbound network (port 443) ✅ Allowed All other ports blocked — use --allow-port to add extras
Localhost outbound 🔒 Kernel-blocked Prevents local service access; inbound still works for proxy
SSH agent (unix socket) 🔒 Kernel-blocked Prevents signing git operations or SSH to hosts
Developer tools (~/.cargo, ~/.mise, ~/.gradle, ~/.m2, ~/.sdkman, ~/.jenv, ~/.pyenv, ~/.konan, etc.) ✅ Allowed (read+write for caches) Only dirs that exist on disk; tightened at runtime via --doctor
Registry credential files (~/.m2/settings.xml, ~/.gradle/gradle.properties, ~/.cargo/credentials) 🔒 Kernel-blocked (macOS) Override with --allow-read; see Private registries
Go source code (~/go/src) 🔒 Kernel-blocked Only ~/go/bin and ~/go/pkg are readable
Read ~/.ssh, ~/.gnupg, ~/.aws, ~/.azure 🔒 Kernel-blocked
Read ~/.kube, ~/.docker, ~/.nais 🔒 Kernel-blocked
Read ~/.password-store, ~/.terraform.d 🔒 Kernel-blocked
Read ~/.config/gcloud, ~/.config/op 🔒 Kernel-blocked
Read ~/.netrc, ~/.npmrc, ~/.pypirc, ~/.vault-token 🔒 Kernel-blocked
Read ~/.gem/credentials 🔒 Kernel-blocked
Child process inheritance ✅ All restrictions apply to subprocesses
This table is a summary. The sandbox also allows access to system files (SSL certs, /etc/hosts), temp directories (read/write but no exec), and system tool paths (/usr/bin, /opt/homebrew). Run cplt --print-profile to see the complete SBPL rules.
For the full security model, threat analysis, and test strategy, see SECURITY.md.
Install
Homebrew (recommended)
brew install navikt/tap/cplt
curl | bash
curl -fsSL https://raw.githubusercontent.com/navikt/cplt/main/install.sh | bash
Options:
Install a specific version
curl -fsSL ... | bash -s -- --version 2026.05.05-174753-75bae5b
Install to a custom directory
curl -fsSL ... | bash -s -- --dir ~/.local/bin
Skip Homebrew (force direct download)
curl -fsSL ... | bash -s -- --no-brew
Download from releases
Download the latest release for your platform from GitHub Releases:
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/
Every release binary has build provenance attestation — verify it with:
gh attestation verify cplt -o navikt
Build from source
git clone https://github.com/navikt/cplt.git && cd cplt cargo build --release sudo cp target/release/cplt /usr/local/bin/
Or with mise:
mise run install
Shell setup (recommended)
By default, you run the sandboxed version with cplt. To make copilot run the sandboxed version too, use the one-command installer:
cplt --shell-install
This detects your shell, appends the alias to your rc file, and prints what it did. Safe to run multiple times — it won't add duplicates.
Shell File modified What's added
zsh (macOS default) ~/.zshrc eval "$(cplt --shell-setup)"
bash ~/.bashrc eval "$(cplt --shell-setup)"
fish ~/.config/fish/conf.d/cplt.fish alias copilot cplt
After installing, restart your shell or source the file to activate.
Manual setup (alternative)
If you prefer not to use --shell-install, add the appropriate line to your shell rc file manually:
zsh / bash
eval "$(cplt --shell-setup)"
fish
alias copilot cplt
This is the same pattern used by tools like mise, direnv, and starship.
Why an alias instead of a symlink? Both cplt and Copilot CLI install into the same Homebrew bin directory (/opt/homebrew/bin/). A symlink would conflict — only one file named copilot can exist there. A shell alias avoids this entirely: the real copilot binary stays in PATH (so cplt can find and wrap it), and the alias transparently redirects your command.
Note: cplt has recursion prevention built in. If it detects it's already running inside a sandbox (via the __CPLT_WRAPPED environment variable), it will refuse to launch again. Read-only subcommands like --print-profile and --doctor still work inside an existing sandbox.
What it does
Usage
cplt [OPTIONS] [-- ...]
Everything after -- is passed directly to the agent process (copilot, opencode, or shell).
File access
The project directory is the primary writable workspace, plus a narrow allowlist required for auth, runtime, and tooling (see capability table above). Everything else (SSH keys, cloud credentials, etc.) is blocked by the kernel.
Flag What it does
-d, --project-dir Which directory Copilot can work in. Defaults to the current git repo root.
--allow-read Let Copilot read (read-only) files outside the project (e.g. shared libraries, docs). Can be repeated.
--allow-write Let Copilot read AND write outside the project. Use carefully. Can be repeated.
--deny-path Block a path that would otherwise be allowed. Deny always wins. Can be repeated.
--allow-port Allow outbound TCP on an extra port (default: only 443). Can be repeated.
--allow-localhost Allow outbound to localhost on a specific port (localhost is blocked by default). Use for MCP servers or dev servers. Can be repeated.
--allow-localhost-any Allow outbound to localhost on all ports. Needed for build tools like Turbopack (Next.js) and Vite that use random ephemeral ports for IPC. When combined with --allow-jvm-attach, also covers Java's IPv4-mapped addresses (see SECURITY.md).
Environment variables
By default, cplt sanitizes the child environment — only safe variables pass through. Cloud credentials, database URLs, and package tokens are stripped. Additionally, security hardening variables are injected to block npm/yarn/pnpm lifecycle scripts (postinstall hooks) — the #1 supply chain attack vector — and disable git commit/tag signing (since ~/.ssh and ~/.gnupg are inaccessible inside the sandbox).
What passes through:
Category Examples How
Core system HOME, USER, PATH, SHELL, TMPDIR, LANG Explicit allowlist
Terminal TERM, COLORTERM, TERM_PROGRAM Explicit allowlist
Editor EDITOR, VISUAL, PAGER Explicit allowlist
Auth tokens GH_TOKEN, GITHUB_TOKEN, COPILOT_GITHUB_TOKEN Explicit allowlist (needed for Copilot)
Copilot config COPILOT_DEBUG, COPILOT_* Prefix allowlist
Language runtimes NODE_*, GOPATH, CARGO_HOME, JAVA_HOME, VIRTUAL_ENV, PYTHONPATH Explicit allowlist
Tool managers NVM_*, PYENV_*, MISE_*, SDKMAN_*, COREPACK_*, YARN_* Prefix allowlist
XDG dirs XDG_CONFIG_HOME, XDG_DATA_HOME, XDG_STATE_HOME, XDG_CACHE_HOME Explicit allowlist
Prefix allowlist with secret-suffix protection: Variables matching allowed prefixes (e.g. COPILOT_*, YARN_*) are passed through unless they end with a secret-bearing suffix: _TOKEN, _AUTH, _SECRET, _SECRET_KEY, _KEY, _PASSWORD, or _CREDENTIALS. For example, COPILOT_DEBUG passes through but COPILOT_API_KEY is blocked.
Always blocked: AWS_*, AZURE_*, NPM_TOKEN, DATABASE_URL, VAULT_TOKEN, SSH_AUTH_SOCK, Docker vars, CI tokens, and anything not in the allowlist.
Flag What it does
--pass-env Explicitly pass an environment variable through to Copilot. Can be repeated.
--inherit-env ⚠️ Dangerous. Inherit the full parent environment (only strips NO_COLOR, FORCE_COLOR, SSH_AUTH_SOCK, SSH_AGENT_PID). Use only for debugging.
--allow-lifecycle-scripts Allow npm/yarn/pnpm lifecycle scripts (postinstall hooks) to run. Blocked by default. Use when npm install needs postinstall hooks.
--allow-gpg-signing Allow GPG commit/tag signing inside the sandbox. Grants read-only access to public keyring and GPG agent socket (private keys stay denied). See GPG signing.
--allow-jvm-attach Allow JVM Attach API unix sockets in /tmp. Needed for MockK inline mocking, Mockito inline agents, B
[truncated for AI cost control]