- persistent files and context per user or project
- an agent that works inside the workspace and can debug anything (the box ships five, see Integrated agents)
- a private preview URL of what the user is building
- controlled sandbox cost per user
The two rules
- Always create user boxes with
--no-env(noEnv: true). A no-env box receives none of your account’s secrets or credentials and cannot act on your account or other boxes. Pass what the box does need explicitly with--env. See No-env boxes. - Tag each box with per-box environment variables so your backend can identify it.
ready or idle before running commands in it. This is enforced: a command sent while the box is still provisioning is refused with a retryable 409 box_starting error (or machine_not_running even earlier in provisioning), so wait for ready instead of racing startup. Commands sent too early would also run before your env is applied.
Pattern 1: one always-on box per project
Simplest, perfectly isolated. Spawn a box per user project with--no-auto-stop. Clone or copy the user’s code in, run their dev server, and expose it:
_token URL is private to whoever you give it to. The preview stays up as long as the box runs.
Cost: a default box running 24/7 is about $26/month ($0.00001 per second); a large box is double that. Use this when the project has real traffic or the user pays you enough to cover it.
Pattern 2: one always-on box per user
Like pattern 1, but one box holds all of a user’s projects, each in its own folder on its own port. A small daemon you install in the box multiplexes agent sessions. Works well up to 5 to 10 fullstack projects per user. Cost: about $26/month per user instead of per project. Watch isolation: projects of the same user share a machine.Pattern 3: stop and resume around usage
Cheapest, isolated, and what most platforms end up with. The box only runs while the user is actively working:- User sends a message. If their box is stopped,
box resumeit (a few seconds), relaunch your daemon and the preview in parallel, and send the message to the agent. - When the agent finishes, wait a short countdown, then
box stop. Stop snapshots the filesystem and pauses billing.
When the user is done for good
Stop is a pause; delete is the end.DELETE /boxes/{boxId} (box delete, or the ⋯ menu in the dashboard) force-stops the box and permanently deletes the snapshots only it uses. There is no undo and no resume afterwards, so wire it to an explicit “delete my project” action, never to an idle timeout: stopping is what you want when the user might come back. Snapshot data a fork, a resume or a named template still reads is kept. See Snapshots.
When a stop is refused
Stopping saves the box’s disk first. If that save is failing, we refuse the stop and leave the box running rather than throw away the user’s work. You are not billed for that time: the meter pauses on its own from the first failed attempt, so a stuck box never shows up on your invoice and there is nothing to reclaim. See When a stop is refused. Your backend should treat a refused stop as retryable rather than fatal; we retry too, and email the box owner. If you would rather stop it now and accept losing everything written since the last successful snapshot, passforce. It is irreversible, so offer it only after a stop has already failed, and check the last snapshot time first so you know what is being given up.
default machine time, half that on large. A typical user costs $1 to $5 per month, a power user $10 to $20.
To pass that cost on, read each box’s own meter for the billing period with GET /boxes/{boxId}/usage (box usage <id>). It works on running and stopped boxes and counts exactly what your balance was charged for that box. See Per-box usage.
Two refinements:
- Zero preview downtime: publish successful builds to a static host or CDN. The box is only for the agent and dev preview; production traffic never depends on a box being up.
- Zero agent latency: run a tool-less copy of the agent on your own server that answers immediately and stalls while the box resumes. See optibox for a working implementation.
Use the integrated agents
Before writing an agent loop, look at what the box already runs.box prompt drives five coding agents (Claude Code, Codex, pi, OpenCode, Prime Agent) with memory, parallel conversations, streaming events, and per-prompt model choice built in. For most platforms it replaces the daemon below. How it works, and every seam to customize it, is on Integrated agents. The tips that matter when you build a product on it:
- Your user’s key, not yours. Create the box
--no-envand pass the user’s key as a per-box variable:-e ANTHROPIC_API_KEY=…,-e OPENAI_API_KEY=…. Every harness reads it; nothing of your account is on the box. Keys are per box, so users whose keys must stay apart get their own box (patterns 1 and 3), while a box you pay for yourself can be shared across users through conversations (pattern 2). - One conversation per user session.
POST /promptwithnew: truereturns aconversationId; store it next to the session and pass it back asconversationIdon every later message. Conversations run in parallel on one box, each with its own memory, and survive stop and resume, so pattern 3 works unchanged: resume the box, prompt the same conversation. - Agent and model picker. A selector in your UI maps directly onto the
providerandmodelfields ofPOST /prompt. Switching mid-conversation keeps the history, so users can change their mind per message. - Stream to your UI.
GET /eventswith a cursor gives structured prompt, response, and tool-call events, each tagged with itsconversationId. Filter withconversationto show one user only their own session. - A stop button.
POST /interruptwithconversationstops one user’s turn and leaves the others running. - House rules, tools, MCP servers. The agents read ordinary config files from the box home (
AGENTS.md,CLAUDE.md, MCP registrations, skills). Put yours in the template once and every fork starts with them; per-user rules go in the prompt or in per-user boxes. See Customize the harness. - Attachments.
box prompt --attachputs the user’s files under~/attachmentson the box and sends images inline to vision models. From a backend, write the file with the file endpoint and name its path in the prompt.
Bring your own harness: the daemon pattern
You do not have to use the built-in agents. If your harness lives in your own infrastructure, or you want full control over the agent loop, put a small HTTP daemon in the box and talk to it directly:- Install it:
box scpthe binary in, or bake it into your template. - Keep it alive: run it as an always-on systemd service so it survives stop, resume, and fork.
- Expose it:
host <port> --privategives it a stable HTTPS URL gated by a_token; treat that token as the daemon’s credential and store it in your backend. - Drive it from your backend over plain HTTPS: your harness sends work, the daemon runs it with full machine access and streams results back.
POST /boxes/{boxId}/commands runs any command in the box, file endpoints read and write data, and first-class endpoints cover lifecycle, prompts, events, desktop, and snapshots. In-box tools such as host are used by running their commands through that same endpoint. A daemon earns its place when you need streaming, concurrency, or lower latency than one-shot commands give you.
Provision from a template
Never install your stack on a fresh box per user. Build it once, then fork:env replaces the per-box variables the fork would inherit from the source, in the CLI (box fork <id> -e KEY=V), the API, and the SDKs alike.
Forks inherit the whole filesystem and are usable in seconds at roughly constant cost, whatever the template holds. To update the template: resume it, change it, stop it again. Forks always take the latest snapshot. See Template Boxes.