The sandbox routes report aiod's environment and its current capabilities. Capabilities come from probing each subsystem, so the report tracks the live runtime rather than a fixed manifest.
A missing subsystem degrades a capability. The request still succeeds. Results are cached; repeated polling is cheap.
The v1 and v2 routes side by side are in Migration from 1.x.
A client's first request asks where it is, which runtimes are installed, and which planes have a backend:
The response contains all three. capabilities is the snapshot described in Capability snapshot and branching, with no extra request.
data is a text summary, and detail, version, and workspace sit at the top level.
The response also includes home_dir, another name for workspace_dir that is repeated under detail.system. Only the v1 endpoint returns this field.
There is no capabilities field here, so read it with a second call to GET /v1/capabilities.
When you want identity, workspace, and capabilities from one request, switch to v2:
Both responses contain the same environment fields:
workspace_dir, workspace — two names for one value: the daemon account's home, where a command that names no working directory runsversion — the daemon versiondetail.system — os, os_version, arch, user, timezone, occupied_ports, and the same two workspace fieldsdetail.runtime — python and nodejs, each an array of {ver, bin, alias}detail.utils — the tools found on PATH, grouped into categories such as editors, network, and searchdetail.system.sandbox_user appears only where the image bakes an unprivileged account; it reports the {name, uid, gid, home} that account actually resolved to.
occupied_ports is read from /proc/net/tcp, so it lists listening ports on Linux and is empty on every other platform.
The workspace is not configurable, and there is no --workspace flag. workspace_dir is the account's home.
These docs call a family of related interfaces a plane: commands, files, terminals, code, browser, and desktop. Read the snapshot once and branch on its capability fields before calling a plane.
Each fact a client needs at startup is one field:
| Fact | Field |
|---|---|
| The working directory of a command | workspace_dir |
| The browser plane's status | capabilities.browser.status |
| The Python interpreter that runs code | capabilities.code_interpreter.default_python |
| The desktop plane's status | capabilities.computer.status |
Why a plane is not ready | that plane's missing |
The snapshot is organized in groups:
| Group | Fields |
|---|---|
files | Twelve flags, one per file operation |
exec | shell, bash, pty |
bins | Resolved binaries: bash, sh, ps, and on Windows powershell and cmd |
code_interpreter | status, backend, javascript_backend, jupyter_backend, kinds, default_python, default_node, python_kernels, endpoint |
browser | status, executable, executable_source, process, cdp, cdp_endpoint |
computer | status, provider, display, screenshot, actions, clipboard, recording, accessibility, resolution |
Three entries carry a probed status: browser (the browser plane), computer (the desktop plane), and code_interpreter (the code plane). Each has status, missing, and warnings. missing names what keeps the plane from ready; warnings explains a partial answer. status is:
ready — the plane responds normallydegraded — the dependencies are present, but the plane does not respond, as with a browser executable whose CDP port is silentabsent — nothing to work with; code_interpreter uses this and ready onlyOn a host without a browser, the browser plane looks like this:
Read the snapshot and branch on it:
Aio is the envelope-aware helper from Examples:
The Python SDK reads /v1/sandbox over HTTP here; which SDK calls need that is listed in 1.x SDK compatibility.
A plane whose backend is absent keeps its routes and answers 503, with a hint for the fix:
Branch on the snapshot rather than on a 404: an absent plane still routes, so the status code alone cannot tell a missing backend from a wrong path.
Results are cached for 5 s, and the daemon probes once at startup, so the first read has a snapshot. A stale snapshot returns immediately and a new probe runs in the background. GET /v1/capabilities?refresh=true waits for the new probe; GET /v2/sandbox never re-probes on demand, so a forced re-probe goes through /v1/capabilities. The browser probe is a single GET /json/version with a 300 ms budget.
The daemon lists the globally installed Python and Node.js packages.
| Route | Purpose | Notes |
|---|---|---|
GET /v2/sandbox/packages?lang=python | The Python listing | lang is required |
GET /v2/sandbox/packages?lang=nodejs | The Node.js listing | One route for both runtimes |
A request with no lang is a 422 naming the field: errors[0].location is ["query", "lang"]. An unrecognised value is a 400 in the envelope — unknown lang "ruby": expected python or nodejs.
| Route | Purpose | Notes |
|---|---|---|
GET /v1/sandbox/packages/python | The Python listing | One route per runtime |
GET /v1/sandbox/packages/nodejs | The Node.js listing | sandbox.get_nodejs_packages() in the SDK |
data is a text listing, not a structured one. Python comes from pip list on the interpreter the code plane resolved, one - name==version line each. Node.js comes from npm list -g --depth=0, under a header line:
The same listing is the MCP tool sandbox_get_packages, whose language argument is python or nodejs and defaults to python. See MCP.