Skip to content

/sandbox-claude-inside

Run Claude Code INSIDE a ca-sandbox box (`--with-claude`). Routed to when the user wants an agent loop running against an isolated, ephemeral sandbox rather than the host. Authenticates via an env-injected CLAUDE_CODE_OAUTH_TOKEN with no host bind of ~/.claude; the image pins

shell
$ npx -y skills add arbiterForge/codeArbiter --skill sandbox-claude-inside --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.
  • You can call itInvoke it directly when you want it.
  • Slash command/sandbox-claude-inside
How auto-invocation works

Context preview

The summary Claude sees to decide when to auto-load this skill.

Run Claude Code INSIDE a ca-sandbox box (`--with-claude`). Routed to when the user wants an agent loop running against an isolated, ephemeral sandbox rather than the host. Authenticates via an env-injected CLAUDE_CODE_OAUTH_TOKEN with no host bind of ~/.claude; the image pins

SKILL.md

sandbox-claude-inside.SKILL.md
name: sandbox-claude-inside
description: Run Claude Code INSIDE a ca-sandbox box (`--with-claude`). Routed to when the user wants an agent loop running against an isolated, ephemeral sandbox rather than the host. Authenticates via an env-injected CLAUDE_CODE_OAUTH_TOKEN with no host bind of ~/.claude; the image pins the CLI and disables the autoupdater; HOME is backed by a named volume so the .claude state persists across restart. Five gated phases — posture, image, token, run, teardown. The hard default is offline or Anthropic-domains-only egress, and the token volume is NEVER co-mounted with an untrusted-code run; both are enforced, not advised.

sandbox-claude-inside

Put Claude Code in the box, not the box on your machine. `--with-claude` runs the CLI inside a host-FS-isolated ca-sandbox container, authenticating from an env-injected token with no host bind of `~/.claude` — the mechanism proven by Spike B (`.codearbiter/spikes/ca-sandbox-claude-auth.md`, CONFIRM-07). It is the deliberately-hardened lane: a token in a box is stealable, so the posture is locked down by construction (offline or Anthropic-only egress, token volume never shared with untrusted code), never left to operator discipline.

Pre-flight

Read these, or STOP and surface the gap — never guess the token source, the egress posture, or the persistence mechanism:

  • `${CLAUDE_PROJECT_DIR}/.codearbiter/spikes/ca-sandbox-claude-auth.md` — the

proven auth path (env token → real `401` on a dummy), the named-volume HOME persistence mechanism, and the load-bearing caveat that fixes the hard default.

  • `${CLAUDE_PROJECT_DIR}/.codearbiter/spikes/ca-sandbox-egress.md` — why the

egress allowlist is EXPERIMENTAL (CDN drift + DNS-exfil hole), so `offline` is the only GUARANTEED posture for a token-bearing box.

  • `${CLAUDE_PROJECT_DIR}/.codearbiter/decisions/0007-second-plugin-ca-sandbox.md`

— the governing decision; ca-sandbox is infrastructure, sibling to `ca`.

The shipped driver is `plugins/ca-sandbox/tools/claude-inside.js`, its OWN binary rather than a `sandbox` subcommand (#377):

node <plugin>/tools/claude-inside.js --image <tag> --home-volume <name> [--net offline|anthropic-only]

That separation IS the gate. A `sandbox with-claude` subcommand would let anyone start a token-bearing box with one ungated command, which would turn the five BLOCK phases below from enforcement into advice. This routine is the only sanctioned caller.

The token MUST come from the approved store as `CLAUDE_CODE_OAUTH_TOKEN`, in the ENVIRONMENT. The entry point refuses a `--token` flag outright: an argument list is world-readable, so passing a credential there publishes it to every process on the host. In tests use a DUMMY token only.

Sources: `plugins/ca-sandbox/tools/claude-inside.ts` (`buildClaudeImageDockerfile`, `buildClaudeRunArgs`, `runClaudeInside`, `TokenCoMountRejectedError`) and `claude-inside-cli.ts` (the entry).

Phase 1 — Posture · gate: BLOCK

Fix the egress posture and the trust boundary BEFORE anything is built or started. A token-bearing box is the one place ca-sandbox's FS-isolation invariant and a live credential are in direct tension — resolve it here, explicitly.

  • **Egress** — choose exactly one: `offline` (default, GUARANTEED — no interface at

all) or `anthropic-only` (the EXPERIMENTAL Anthropic-domains allowlist, for interactive inference). No third option exists; a wide-open policy is rejected.

  • **Trust boundary** — the box runs Claude, NOT the untrusted source repo. Confirm

the run mounts only the token/home volume, never the source volume at `/work/repo`. If the user wants Claude to read an untrusted repo, that is a SEPARATE, source-only box without the token — say so.

Gate: a named egress posture (`offline` or `anthropic-only`) AND an explicit statement that this box carries the token and NOT untrusted source. If the user asks for both at once, STOP and split them — the co-mount is forbidden (Phase 4).

Phase 2 — Image · gate: BLOCK

Build (or reuse) the pinned `--with-claude` image via `buildClaudeImageDockerfile`.

  • The image installs `@anthropic-ai/claude-code@<pinned>` — a PINNED semver,

never `@latest` — and bakes `DISABLE_AUTOUPDATER=1` so the box never silently pulls an unreviewed CLI into a token-bearing environment.

  • The base is `node:22-slim` (Spike B installed the CLI cleanly there), bound to a

reviewed content DIGEST — `node:22-slim@sha256:…`, the `CLAUDE_BASE_IMAGE` constant. The tag is kept only as human-readable provenance; docker resolves the digest. This is the driver's highest-stakes pin: the base image's code runs in the SAME container as `CLAUDE_CODE_OAUTH_TOKEN`, so a retag or registry compromise would otherwise execute unreviewed code alongside a live credential. Pinning the CLI version alone is not enough. HOME is baked to the in-container claude home so the named volume has a writable mount point.

Gate: the image carries the exact pinned version (`claude --version` reports it), `DISABLE_AUTOUPDATER=1`, and a digest-pinned base. A floating or unpinned CLI — or an unpinned base image — fails the gate; image reproducibility is non-negotiable for a token box. Changing the digest is a reviewed dependency change: re-resolve it with `docker buildx imagetools inspect`, then re-run the credential-boundary and isolation suites.

Phase 3 — Token · gate: BLOCK

Source the OAuth token and confirm it is injected as ENV, never bound from the host.

  • The token comes from the approved secret store as `CLAUDE_CODE_OAUTH_TOKEN`

(auth-precedence #5, from `claude setup-token`). It is env-injected (`-e CLAUDE_CODE_OAUTH_TOKEN=…`) — this IS the auth path; no host bind of `~/.claude` is required or permitted.

  • The token MUST NOT be echoed to logs, written to a file the source volume can

read, or passed into any LLM prompt. Prefer a scoped/short-lived setup-token.

  • Persistence: HOME is backed by a docker NAMED VOLUME, so the credential
Read more
Read it on GitHub ↗

Showing the first part of this file.

Ships withcodearbiter

When you can't trust yourself with your code base, trust Arbiter.

Get the whole plugin, auto-invoked
Stats
138
Stars
0
Views
7
Forks
Active
Maintenance
Python
Language
AGPL-3.0
License
5h ago
Last commit
2mo ago
Created

Repo: arbiterForge/codeArbiter

Other skills on codearbiter.