本文へ移動
cccskills
無料GitHub で公開

sandbox-lifecycle

The lifecycle gate for a local Codespace-equivalent sandbox. Routed to when the user invokes /ca-sandbox:sandbox to pull an untrusted repo into an ephemeral, host-FS-isolated Docker container, or any of the interaction commands (/ca-sandbox:sandbox-shell, /ca-sandbox:sandbox-exec, /ca-sandbox:sandbox-cp, /ca-sandbox:sandbox-destroy) against an existing box. Five gated phases — pre-flight, clone+build, isolated run, interact, teardown. The load-bearing invariant is structural: untrusted code in the box can never reach the host filesystem (no bind mount, no docker socket, never --privileged, cap-drop ALL, non-root, read-only root). Network defaults to offline; egress out is host-initiated only. Every object is labeled ca.sandbox=1 and torn down on exit.

インストール方法を見る

含まれるファイル(1)

  • SKILL.md10.2 KB

SKILL.md(原文)

インストールする前に、エージェントに与えられる指示の中身を確認できます。

sandbox-lifecycle

Pull an untrusted repo into a throwaway box, explore it without risking the host, then burn the box. This skill owns the whole arc — clone into a named volume, build a dep-cached image, run it under structural isolation, interact (shell / exec / cp out), destroy — and the one invariant that makes it safe: the code inside the box can never touch the host filesystem. That guarantee is enforced by construction (no bind mounts, no docker socket, never --privileged), not by trusting the repo.

The driver lives in ${CLAUDE_PLUGIN_ROOT}/tools. The skill never hand-rolls a docker run argv — every container is started through runContainer in ${CLAUDE_PLUGIN_ROOT}/tools/run.ts, whose mount argv comes only from buildMountArgs in ${CLAUDE_PLUGIN_ROOT}/tools/mounts.ts (the chokepoint that throws on any bind spec).

Pre-flight

Read these, or STOP and surface the gap — never guess a Docker capability, a mount layout, or an egress posture:

  • ${CLAUDE_PLUGIN_ROOT}/tools/mounts.ts — the mount-arg chokepoint. Every mount is built here; it throws (BindMountRejectedError) on any type=bind spec. The structural half of the host-FS invariant.
  • ${CLAUDE_PLUGIN_ROOT}/tools/run.ts — the isolation flags (--cap-drop ALL, non-root --user 1000:1000, --read-only, --security-opt no-new-privileges, resource caps) and the offline => --network none default.
  • ${CLAUDE_PLUGIN_ROOT}/tools/network.ts — the network policies (offline / clone-then-cut / allowlist). The IP allowlist is EXPERIMENTAL (ALLOWLIST_EXPERIMENTAL); offline and clone-then-cut are the solid defaults.

Host prerequisites: Docker and nixpacks on PATH (the plugin's description states this). If docker info fails, STOP and report "Docker is not available" — do not proceed to clone or build. If the user supplies no repo URL to /ca-sandbox:sandbox, ask for one — do not guess a repo.

Phase 1 — Pre-flight & policy · gate: BLOCK

Establish what is being sandboxed and under what egress posture before any clone:

  • Target — the repo URL (or local path) to pull. One source, stated explicitly.
  • Network policy — offline (default), clone-then-cut (fetch deps at build, cut egress at run), or allowlist (EXPERIMENTAL — name it as experimental every time it is selected). Default to offline unless the user names another.
  • Docker reachable — docker info returns 0. If not, STOP here.
  • --with-claude — if requested, route to the sandbox-claude-inside skill (${CLAUDE_PLUGIN_ROOT}/skills/sandbox-claude-inside/SKILL.md) for its hardened defaults; it is NOT enabled on the default path.

Gate: a named target, a named network policy, and a reachable Docker. A sandbox with no stated target or an unreachable Docker cannot be built — do not improvise either. If allowlist is chosen, the BLOCK is conditional on the user acknowledging it is experimental.

Phase 2 — Clone & build · gate: BLOCK

Clone the target into a docker named volume (never onto the host FS, never a bind), then build a dep-cached image:

  • Clone into the named volume via createSandbox (${CLAUDE_PLUGIN_ROOT}/tools/create.ts); the source lives at /work/repo inside the box.
  • Build through ${CLAUDE_PLUGIN_ROOT}/tools/build.ts: nixpacks wraps the repo, deps are relocated out of tree to /deps (exported via NODE_PATH/PYTHONPATH/GOPATH/CARGO_HOME), and the image is tagged ca-sbx:<repo>-<dephash>.
  • The dephash comes from computeDepHash (${CLAUDE_PLUGIN_ROOT}/tools/dephash.ts) over the manifest/lockfile set. An unchanged dep set is a cache hit — no rebuild, identical tag. A manifest/lockfile change bumps the dephash and forces a rebuild; a source-only edit does not.

Gate: a built (or cache-hit) image tagged ca-sbx:<repo>-<dephash>, with deps at /deps (out of tree). The naive "mount the volume over the app dir" layout shadows baked deps and is forbidden — the volume mounts ONLY at /work/repo. If nixpacks is not installed, STOP with the install hint, not a stack trace.

Phase 3 — Isolated run · gate: BLOCK

Start the container through runContainer (${CLAUDE_PLUGIN_ROOT}/tools/run.ts) — never a hand-written docker run. The run carries the structural isolation set, all by construction:

  • No host bind mount, no /var/run/docker.sock mount, never --privileged — the three negative guarantees. The mount argv is built only by buildMountArgs, which throws on any bind.
  • --cap-drop ALL, --user 1000:1000 (non-root), --read-only root, --security-opt no-new-privileges, resource caps (--pids-limit, --memory, --cpus).
  • The live source named volume mounts ONLY at /work/repo; /tmp is a tmpfs (writable scratch, no host backing).
  • Network per Phase 1: offline => --network none; the richer policies are applied by ${CLAUDE_PLUGIN_ROOT}/tools/network.ts.
  • Every object carries the ca.sandbox=1 label (the teardown/registry anchor).

Gate: docker inspect on the started container shows no "Type":"bind" mount, no docker-socket mount, and not Privileged:true. If any of the three appears, the run is rejected — there is no override; the chokepoint failed and that is a bug, not a policy decision.

Phase 4 — Interact · gate: BLOCK

Explore the running box. Each interaction routes to its own command but funnels through this skill's seams:

  • Shell (/ca-sandbox:sandbox-shell) — an interactive shell into the box at /work/repo.
  • Exec (/ca-sandbox:sandbox-exec) — a single command via execInSandbox (${CLAUDE_PLUGIN_ROOT}/tools/exec.ts), returning a JSON contract: exitCode, separate stdout/stderr, and a truncated flag past the byte cap.
  • Copy out (/ca-sandbox:sandbox-cp) — host-initiated egress ONLY, via cpOut (${CLAUDE_PLUGIN_ROOT}/tools/cp.ts): cp <id>:/work/<f> ./dest over docker cp. The reverse — a host→container bind — is impossible: the mount builder rejects it.

Gate: every file leaving the box is host-initiated (docker cp out), never a mount the container could write through to the host. No interaction re-introduces a bind, a socket, or a privilege the run dropped. Exec output honors the byte cap and reports truncated rather than streaming unbounded data.

Phase 5 — Teardown · gate: BLOCK

A sandbox is ephemeral by contract. On exit (/ca-sandbox:sandbox-destroy, or the close of an interactive session):

  • destroySandbox (${CLAUDE_PLUGIN_ROOT}/tools/destroy.ts) removes the container and its named volume. --keep-volume leaves the volume (for a deliberate re-run); nothing else survives.
  • prune (${CLAUDE_PLUGIN_ROOT}/tools/destroy.ts) reclaims any leaked ca.sandbox=1-labeled object — the safety net for a box whose driver died mid-run.
  • Cached images (ca-sbx:<repo>-<dephash>) are intentionally retained for the next cache hit; they are excepted from teardown.
  • Both verbs are best-effort but never silent: a failed removal does not abort the sweep (everything else is still reclaimed), every failure is retained in a bounded failures list with docker's own exit code, and a final label-scoped re-list reports whatever is still present. The CLI exits non-zero and names the leftovers.

Gate: after a create → interact → destroy cycle, zero ca.sandbox=1-labeled containers or volumes remain (cached images excepted). A run that leaves a labeled object behind without --keep-volume is a leak — prune must be able to find and reclaim it via the label alone. Teardown is verified, not assumed: a docker failure during discovery, removal, or verification means the phase FAILS loudly, because a leaked box is still running untrusted code.

Hard rules

  • MUST NOT give a sandbox container a host bind mount — every mount is built through buildMountArgs, which throws on any type=bind. The driver never hand-rolls a -v or type=bind.
  • MUST NOT mount /var/run/docker.sock into a sandbox container, and MUST NOT run one with --privileged. These are non-negotiable structural guarantees, not defaults to override.
  • MUST start every container through runContainer with --cap-drop ALL, non-root --user, --read-only root, and --security-opt no-new-privileges. A run missing any of these is rejected.
  • MUST mount the live source named volume ONLY at /work/repo; deps live out of tree at /deps. MUST NOT mount the volume over the app dir — that shadows baked deps (Spike A) and is the one layout that does not work.
  • MUST default the network policy to offline. The IP egress allowlist is EXPERIMENTAL — name it experimental every time it is selected; offline and clone-then-cut are the solid defaults.
  • MUST treat all egress out of the box as host-initiated (docker cp out) only. A host→container bind is impossible and MUST NOT be introduced as a "convenience."
  • MUST label every container and volume ca.sandbox=1, and MUST tear them down on exit (cached images excepted). prune reclaims a leaked labeled object via the label alone.
  • MUST NOT report a teardown as successful when docker refused a removal or the post-teardown verification could not confirm the scope is empty. destroy/prune exit non-zero and name every object left behind — automation must never read exit 0 over a still-running untrusted container.
  • MUST NOT enable --with-claude on the default path — it routes to sandbox-claude-inside, and MUST NEVER co-mount the token volume with an untrusted-code run.
  • MUST STOP rather than guess when Docker or nixpacks is absent — report the missing dependency, never a stack trace.

レビュー

まだレビューはありません。使ってみた感想をお寄せください。

同じリポジトリのスキル

概要と使いどころ

The Socratic spec-refinement front of /feature, and the planning front of /sprint. Routed to BEFORE any code — it turns a one-line idea into a concrete spec with testable criteria, ready for initial combined sprint review or explicit sequential approval. Five gated phases — frame, shape, refine, write, review-and-approve. No implementation before an approved spec; the caller retains required planning, execution and delivery ordering. Each acceptance criterion becomes one tdd Phase 1 obligation.

日本語の概要は準備中です。原文の説明を表示しています。

arbiterForge/codeArbiter1472026年10月11日 更新

Vet a new or changed third-party dependency for license, provenance, and supply-chain risk before any install runs.

日本語の概要は準備中です。原文の説明を表示しています。

arbiterForge/codeArbiter1472026年10月11日 更新

ca-adr

無料

Record user-decided ADRs or inspect their health read-only. Preserve attribution and acceptance evidence.

日本語の概要は準備中です。原文の説明を表示しています。

arbiterForge/codeArbiter1472026年10月11日 更新

Inspect ADR health read-only; optionally select one ADR with --adr N.

日本語の概要は準備中です。原文の説明を表示しています。

arbiterForge/codeArbiter1472026年10月11日 更新

ca-audit

無料

Assemble the governance record for a range — commits, overrides, ADRs, sprint auto-decisions, open questions, checkpoint findings — into one dated audit packet. Read-only.

日本語の概要は準備中です。原文の説明を表示しています。

arbiterForge/codeArbiter1472026年10月11日 更新

ca-btw

無料

Lightweight Q&A about the project — answer from context and return, no routing, no state change.

日本語の概要は準備中です。原文の説明を表示しています。

arbiterForge/codeArbiter1472026年10月11日 更新

arbiterForge のスキルをすべて見る

このスキルの問題を報告する