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.
1. Create a separate profile
Section titled “1. Create a separate profile”Keep untrusted work out of your everyday profile so loosening one never loosens the other.
ccpod profile create sandboxThen edit ~/.ccpod/profiles/sandbox/profile.yml:
name: sandboxdescription: 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.comconfig.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.
2. Restrict the network
Section titled “2. Restrict the network”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.orgHostnames are resolved once at startup, so if a host’s IPs change mid-session, restart the container. See Network Policy for the details.
3. Give it no SSH access
Section titled “3. Give it no SSH access”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_TOKENSee 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: persistentstateIsolation: per-projectSee State Persistence.
5. Use a dedicated API key
Section titled “5. Use a dedicated API key”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.
6. Leave the project opt-ins off
Section titled “6. Leave the project opt-ins off”A repo’s .ccpod.yml is treated as untrusted. These profile flags default to false, so leave them off in the sandbox profile:
| Flag | What it would allow |
|---|---|
allowProjectServices | The repo starts its own sidecar containers. |
allowProjectInit | The repo runs its own init commands. |
allowProjectHostMounts | The repo’s sidecars mount host paths. Only matters when allowProjectServices is also on. |
allowProjectEnvForward | A 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.
7. Run it explicitly
Section titled “7. Run it explicitly”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.)
cd path/to/untrusted-repoccpod run --profile sandboxWhat this does not protect against
Section titled “What this does not protect against”- 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.jsonmerges 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.