Sandbox environment reference
What is preinstalled in an OpenInspect sandbox, how the workspace is laid out, which environment variables the runtime sets, and which helper commands are available.
This page describes the environment your setup.sh, start.sh, and the agent run in: the preinstalled tools, the workspace layout, the environment variables the runtime sets, and what is deliberately kept out of the sandbox.
Preinstalled software
Every provider's image installs the same tools. The per-provider image targets differ only in their base: the Vercel target is built on Amazon Linux, and the Vercel and OpenComputer targets use Node.js 24.
| Area | What is installed |
|---|---|
| Operating system | Debian with build-essential, git, curl, jq, unzip, openssh-client, ffmpeg, and ca-certificates |
| Source control | GitHub CLI (gh) |
| Node.js | Node.js 22 (Node.js 24 on the Vercel and OpenComputer targets) with npm and npx, plus pnpm, bun, and bunx |
| Python | Python 3.12 with uv and uvx. python and python3 point at the same interpreter |
| Browser automation | The agent-browser CLI with a headless Chrome for Testing build at /usr/local/bin/google-chrome |
| Agent harnesses | OpenCode, and the Claude Agent SDK whose wheel bundles the claude binary |
| Sandbox tools | code-server (browser VS Code), ttyd (web terminal), and Xvfb, Fluxbox, x11vnc, and noVNC for the VNC desktop |
Version pins live in packages/sandbox-images/toolchain.json. Updating the shared toolchain does not refresh existing prebuilt images; rebuild them from Settings › Images or Settings › Environments when they must pick up new tools.
Workspace layout
| Path | Contents |
|---|---|
/workspace | The working directory. A single-repository session is cloned here. |
/workspace/<repo-name> | In multi-repository sessions each repository is cloned into its own directory named after the repository. The first repository is the primary. |
/workspace/.tunnels.env | Written before start.sh when the session has tunnelPorts configured. Plain KEY=value lines usable with node --env-file, bun --env-file, or docker compose --env-file. Not written when no tunnel ports are set or in build mode. |
.opencode/skills under the working directory | Where OpenCode discovers skills. The runtime copies its bundled skills here before launch; managed skills are installed into the workspace before the agent starts. |
~/.openinspect/claude | The Claude Agent harness's CLAUDE_CONFIG_DIR. Managed and bundled skills are staged under ~/.openinspect/claude/skills. It sits outside every repository so a snapshot never captures a credential with it. |
Repository .opencode/ and .claude/ directories are read by their respective harness, the same trust boundary as .openinspect/setup.sh.
Environment variables
| Variable | Set when | Meaning |
|---|---|---|
OPENINSPECT_BOOT_MODE | Always, for setup.sh and start.sh | One of fresh, repo_image, snapshot_restore, or build. Use it to skip work that a prebuilt image or snapshot already did. |
TUNNEL_SANDBOX_ID | In /workspace/.tunnels.env when tunnel ports are configured | The sandbox the URLs were resolved for. The runtime uses it to tell a fresh file from one inherited from a snapshot. |
TUNNEL_<port> | In /workspace/.tunnels.env, one line per configured port | The public URL for that port, for example TUNNEL_3000=https://…. |
SANDBOX_ID | Always | Identifies this sandbox to the control plane. |
CONTROL_PLANE_URL | Always | Where the runtime, credential helper, and helper commands call back to. |
SANDBOX_AUTH_TOKEN | Always | The sandbox's own short-scoped token for control-plane calls. It authorises one session only. |
SESSION_CONFIG | Always | JSON describing the session: its id, repositories, MCP servers, and related settings. The runtime and helper commands read the session id from it. |
REPO_OWNER, REPO_NAME | Repository sessions | The primary repository. |
GITHUB_APP_TOKEN | Modal snapshot restores only | A clone token so snapshots taken before the credential helper can still boot; fresh and prebuilt-image sandboxes, and every other provider, use the credential helper instead. |
RESTORED_FROM_SNAPSHOT | Snapshot restores | Marks a restored sandbox. |
OPENCODE_CONFIG_CONTENT | OpenCode sessions | The generated OpenCode configuration. |
PYTHONUNBUFFERED, PATH, HOME, USER, SHELL, TERM, PWD, LANG | Always | Standard process environment. |
OPENAI_OAUTH_MANAGED=1 | OpenAI account mode | The session is bound to a connected OpenAI provider account. OPENAI_API_KEY is suppressed and the runtime fetches short-lived access tokens from the control plane. |
XAI_OAUTH_MANAGED=1 | xAI account mode | Same for a connected SuperGrok account; XAI_API_KEY and legacy xAI OAuth fields are removed. |
ANTHROPIC_OAUTH_MANAGED=1 | Claude Agent sessions bound to a connected Claude account | No Anthropic key is in the user secrets. The harness fetches the credential on every bridge start and passes it to the claude process only, as CLAUDE_CODE_OAUTH_TOKEN. In key mode the claude process receives ANTHROPIC_API_KEY instead, never both. |
CLAUDE_CONFIG_DIR | Claude Agent sessions | Points the claude binary at ~/.openinspect/claude. A user-supplied value that points inside a repository is replaced with the default. |
| Your secrets | Always | Global secrets plus the session target's secrets, injected at spawn. System variables above take precedence over a secret with the same key, and the reserved names cannot be saved as secrets. |
The reserved names are listed in full in Secrets.
What is deliberately absent
- Provider API keys in account mode. When a session is bound to a connected OpenAI, xAI, or Anthropic account, the matching key is removed from the environment and only the
*_OAUTH_MANAGEDmarker remains. - Refresh tokens. The sandbox only ever receives short-lived access tokens; refresh tokens stay in the encrypted credential store.
- The commit signing private key. Sandboxes receive only the public key; each commit is signed by the control plane. The key is never delivered to a sandbox file, environment, process, or snapshot.
- A long-lived git token in fresh and prebuilt-image sandboxes. Git credentials are minted on demand (next section).
- The Claude account token outside the
claudeprocess. OpenCode, code-server, the terminal, and user shells never see it. Code the agent runs from Bash and hooks registered in the repository's.claude/settings do inherit it.
Git credentials
git is configured system-wide with the credential helper oi-git-credentials. Every fetch, push, ls-remote, or submodule update asks the control plane for a fresh short-lived SCM credential instead of using a token captured at spawn. A successful response is cached in /run/oi/scm-creds.json until shortly before it expires, and concurrent git commands share one request. A refused refresh fails the git command rather than falling back to a stale token. The helper authorises the configured SCM host, so setup.sh can clone other private repositories the shared App installation can reach.
Helper commands
| Command | What it does |
|---|---|
oi-git-credentials | The git credential helper described above. You do not call it directly; git does. |
oi-git-sign | Signs a commit by sending the unsigned commit buffer to the control plane, which returns an SSH signature. Active only when commit signing is configured under Settings › Integrations › GitHub. |
upload-media <file> | Uploads a screenshot (PNG, JPEG, WebP) or MP4 recording to the session as a media artifact. MP4 files need --artifact-type video; --caption adds a caption. The file appears in the session's Media section. |