Four ways into every box.
CLI, REST, SDK or MCP. One engine behind all four.
From a fresh checkout to a command in a box.
- 1
Install dependencies
Run it from the checkout.
step 1pnpm install alias workbox='node packages/cli/src/main.js' - 2
Prepare the host
As root. doctor exits 4 until the host is ready.
step 2sh deploy/install.sh workbox doctor - 3
Build the rootfs, install the runtime
Docker builds the shared rootfs once.
step 3workbox rootfs build minimal workbox runtime install - 4
Serve
Listens on 127.0.0.1:8790.
step 4export WORKBOX_TOKEN=$(openssl rand -hex 24) workbox serve & - 5
Run something
Create, run, destroy.
step 5S='--server http://127.0.0.1:8790' # prints the sandbox row; note its id workbox sandbox create --name demo $S workbox sandbox exec <id> $S -- sh -c 'wb status; wb run -- echo hello' workbox sandbox rm <id> $S
Same task, your favorite surface.
workbox — operator CLI
$ workbox sandbox create --name demo --network none $S
$ workbox sandbox run <id> --name test --wait $S -- make test
$ workbox sandbox cp <id>:/workspace/report.json ./report.json $S
$ workbox events --sandbox <id> --type 'session.*' --follow $SThe complete API at a glance.
methodroutescope
- GET
/healthznone - GET
/readyznone - GET
/v1/openapi.jsonnone - GET
/metricsread - POST
/v1/reconcileadmin - GET
/v1/whoamiany key - GET
/v1/sandboxesread - POST
/v1/sandboxesadmin - GET
/v1/sandboxes/:idread - DELETE
/v1/sandboxes/:idadmin - POST
/v1/sandboxes/:id/drainadmin - POST
/v1/sandboxes/:id/execexec - GET
/v1/sandboxes/:id/filesread - PUT
/v1/sandboxes/:id/filesexec - DELETE
/v1/sandboxes/:id/filesexec - POST
/v1/sandboxes/:id/files/mkdirexec - GET
/v1/sandboxes/:id/files/rawread - PUT
/v1/sandboxes/:id/files/rawexec - GET
/v1/sandboxes/:id/sessionsread - POST
/v1/sandboxes/:id/sessionsexec - GET
…/sessions/:sessionIdread - DELETE
…/sessions/:sessionIdexec - GET
…/sessions/:sessionId/outputread - POST
…/sessions/:sessionId/waitread - POST
…/sessions/:sessionId/inputexec - POST
…/sessions/:sessionId/signalexec - POST
…/sessions/:sessionId/stopexec - POST
…/sessions/:sessionId/resizeexec - WS
…/sessions/:sessionId/wsexec - GET
/v1/sandboxes/:id/evidenceread - GET
/v1/sandboxes/:id/artifactsread - GET
/v1/sandboxes/:id/artifacts/:sha256read - GET
/v1/sandboxes/:id/checkpointsread - POST
/v1/sandboxes/:id/checkpointsadmin - GET
/v1/sandboxes/:id/eventsread - WS
/v1/sandboxes/:id/events/wsread - GET
/v1/eventsread - WS
/v1/events/wsread - GET
/v1/asksread - POST
/v1/asks/:askId/answeranswer - POST
/v1/keysadmin - GET
/v1/keysadmin - DELETE
/v1/keys/:keyIdadmin - POST
/v1/webhooksadmin - GET
/v1/webhooksread - GET
/v1/webhooks/:webhookIdread - PATCH
/v1/webhooks/:webhookIdadmin - DELETE
/v1/webhooks/:webhookIdadmin - POST
/v1/webhooks/:webhookId/rotate-secretadmin - POST
/v1/webhooks/:webhookId/testadmin - GET
/v1/webhooks/:webhookId/deliveriesread - POST
…/deliveries/:deliveryId/retryadmin
52 of 52 routes
Give any model a sandbox in one config block.
Everything else, the agent finds with wb tools.
mcp client configuration
{
"mcpServers": {
"workbox": {
"command": "node",
"args": ["/path/to/workbox/packages/mcp/src/main.js"],
"env": {
"WORKBOX_SERVER": "http://127.0.0.1:8790",
"WORKBOX_TOKEN": "<token>"
}
}
}
}sandbox_create→ The sandbox id, once it is ready
sandbox_list→ One page of sandboxes with state and health
sandbox_exec→ Exit code, stdout and stderr, each capped at 64 KiB
sandbox_destroy→ Destroys the sandbox and deletes its writable layer
agent_run→ {sandboxId, sessionId, running, exitCode, signal, output, truncated}
agent_output→ The same run, read again from the start