AI News HubLIVE
站内改写

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

EngineersAdvanced

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]