Skip to content

Run Claude Code Safely on Untrusted Repos

Pointing an AI agent at a repo you did not write is a trust problem: the repo’s files, CLAUDE.md, and settings all end up in front of a tool that can run commands. ccpod does not make that risk disappear, but it lets you shrink what a bad repo can reach. This guide builds a dedicated sandbox profile for that job.

Keep untrusted work out of your everyday profile so loosening one never loosens the other.

Terminal window
ccpod profile create sandbox

Then edit ~/.ccpod/profiles/sandbox/profile.yml:

name: sandbox
description: Untrusted repos. Restricted network, no SSH, ephemeral state.
config:
source: local
path: ~/.my-claude-config
image:
use: ghcr.io/yorch/ccpod:latest
auth:
type: api-key
keyEnv: ANTHROPIC_API_KEY_SANDBOX
state: ephemeral
ssh:
agentForward: false
mountSshDir: false
network:
policy: restricted
allow:
- api.anthropic.com

config.path can point at an empty directory if you do not want your usual settings and skills inside the sandbox, or at a trimmed copy of your config as described in Move your ~/.claude config into a profile.

Each section below explains one of these choices.

With policy: restricted, the entrypoint installs firewall rules before Claude starts and drops all outbound traffic except loopback, DNS, and the hosts in allow. If the rules cannot be installed, the container exits instead of running open.

Start with only api.anthropic.com and add hosts as Claude needs them, for example a package registry:

network:
policy: restricted
allow:
- api.anthropic.com
- registry.npmjs.org

Hostnames are resolved once at startup, so if a host’s IPs change mid-session, restart the container. See Network Policy for the details.

Both agentForward and mountSshDir are off in the profile above, so the container cannot use your keys. If Claude needs to clone a private repo, pass a narrowly scoped token instead and add your git host to network.allow:

env:
- GITHUB_TOKEN

See SSH Forwarding for why agent forwarding is still the better choice for repos you trust.

4. Keep state ephemeral, or isolate it per project

Section titled “4. Keep state ephemeral, or isolate it per project”

state: ephemeral wipes Claude’s history and todos when the container exits. If you want history to persist, isolate it per project so one repo’s conversations are never visible to another:

state: persistent
stateIsolation: per-project

See State Persistence.

The profile above reads its key from ANTHROPIC_API_KEY_SANDBOX, so you can create a separate key with its own spend limit and revoke it without touching your main one. Using auth.type: proxy is another option: the container never receives your OAuth credentials at all.

A repo’s .ccpod.yml is treated as untrusted. These profile flags default to false, so leave them off in the sandbox profile:

FlagWhat it would allow
allowProjectServicesThe repo starts its own sidecar containers.
allowProjectInitThe repo runs its own init commands.
allowProjectHostMountsThe repo’s sidecars mount host paths. Only matters when allowProjectServices is also on.
allowProjectEnvForwardA list of host variable names (for example GITHUB_TOKEN) the repo may forward into the container by name. Empty by default, so a repo cannot pull your secrets out of your shell. Keep it empty in the sandbox profile.

The repo can never change the network policy or allow-list. It cannot set environment variables that would redirect your credentials or hijack the container (ANTHROPIC_API_KEY, HTTPS_PROXY, PATH, LD_PRELOAD, NODE_OPTIONS, and similar), and it cannot use ${VAR} interpolation to read host variables. Symlinked .claude/, CLAUDE.md and .claude/settings.json in the repo are ignored, so they cannot point at files like ~/.ssh or ~/.aws/credentials.

Select the profile on the command line rather than relying on anything inside the repo. (If a repo’s .ccpod.yml names a profile:, ccpod asks you to approve it the first time and remembers the answer; --profile never prompts and always wins.)

Terminal window
cd path/to/untrusted-repo
ccpod run --profile sandbox
  • The project is mounted read-write at /workspace. Claude, or anything it runs, can modify or delete files in that directory. Work on a fresh clone or a branch.
  • Project Claude settings still apply. A repo’s .claude/settings.json merges into your profile’s settings, so hooks defined there run inside the container. That is why the network, SSH, and state settings above matter.
  • Restricted mode is defense in depth, not a sandbox. It limits where traffic can go, not what runs inside the container.

For a full list of what ccpod enforces, see Internals.