Guide

Advanced Topics

Deeper tooling for sandboxes, file sets, and power-user workflows.

Sandbox

A sandbox is an ephemeral container that runs a single command and is discarded as soon as it exits. It is the same machinery Overplane uses internally for agent runs, exposed directly through the overplane sandbox run command so you can run ad-hoc commands inside your project's image — or any image — without writing a script or remembering long docker/podman invocations.

The sandbox abstracts over both Docker and Podman, so the same command behaves identically on either engine. Every run is fresh and isolated: there is no shared state between runs, and the container is removed when the command finishes.

Always-on guarantees

These mounts are applied to every sandbox run:

  • The project repository is mounted read-only at /project. Commands can read your project tree but cannot mutate the host. In a project sandbox, the specs directory is also mounted read-only at /specs.
  • Two fresh read-write scratch directories are mounted at /output and /error. Each run gets new, empty ones; the container's working directory defaults to /output. Files a command writes there are captured when it exits, and --output DIR / --error DIR extract them to host directories after a successful run.
  • The overplane binary is mounted at /usr/local/bin/overplane. The exact binary you invoked is on PATH inside the container — no image rebuild needed — so tooling that shells out to overplane just works.

Running ad-hoc commands

By default, sandbox run uses your project's built image (overplane-<project>:latest) and the runtime configured in overplane.yaml, so sandbox build must have built the image first. The command's output is streamed through, and its exit code becomes overplane's exit code.

# Run a command in the project image. The natural form needs no '--':
# flag parsing stops at the first non-flag token, so '-l' is ls's flag.
overplane sandbox run ls -l

# Use '--' when the command itself starts with a dash.
overplane sandbox run -- some-tool --version

With --image (and optionally --runtime), the sandbox runs against any image and does not require a project:

overplane sandbox run --image debian:bookworm-slim -- cat /etc/os-release
overplane sandbox run --image alpine --runtime podman -- sh -c 'echo hi'

Customizing the environment

sandbox run exposes a flag for every option of the underlying abstraction:

  • --image REF — run this image instead of the project image (no project required).
  • --runtime docker|podman — pick the container engine (defaults to the project runtime, else docker).
  • --env KEY=VALUE — set an environment variable (repeatable).
  • --env-from-host KEY — copy a variable from your host environment (repeatable).
  • --mount HOST:CONTAINER[:ro|rw] — bind-mount extra host paths (repeatable; defaults to rw).
  • --network MODE — set the network mode, e.g. host or none.
  • --user UID:GID — override the run identity (defaults to your current host uid/gid).
  • --workdir DIR — set the container working directory (defaults to /output).
  • --project DIR — choose which host directory is mounted read-only at /project (defaults to the project root).
  • --output DIR — extract captured /output files to this host directory after a successful run.
  • --error DIR — extract captured /error files to this host directory after a successful run.
  • --no-self-mount — do not mount the overplane binary into the container.
  • --json — emit a structured result instead of streaming output.
overplane sandbox run \
  --env CI=1 \
  --env-from-host HOME \
  --mount ./data:/data:ro \
  --network none \
  -- ./build.sh

Structured output

Pass --json to capture the result instead of streaming it. The object carries the exit code, engine, image, wall-clock duration, optional memory/CPU usage, and base64-encoded stdout/stderr — so binary output and separate streams survive intact.

{
  "cpu_ms": ...,            // omitted when the engine cannot report it
  "engine": "docker",
  "exit_code": 0,
  "image": "overplane-myproject:latest",
  "max_rss_bytes": ...,     // omitted when unavailable
  "stderr": "ZXJyCg==",     // base64("err\n")
  "stdout": "b3V0Cg==",     // base64("out\n")
  "wall_ms": 84
}