Skip to main content
Outcome: a persistent shell session in a sandbox, driven programmatically or handed to a human terminal. Level: beginner · Time: ~10 minutes · Pattern: multi-tenant platform.

When to use this

Most sandbox work is one-shot: run something, take the output, throw the box away. A different class of problem needs the session to stay alive:
  • Remote dev boxes. One clean isolated environment per user or per branch, where they install dependencies, edit files, and run tests.
  • A workbench for an agent. A coding agent that runs apt-get install, then git clone, then pytest — where each step depends on state the previous one left behind.
  • Multi-tenant terminals. A platform giving every tenant a shell isolated at the VM level, with no shared kernel or filesystem.
CodeBox.run cannot do this: every call is a fresh execution. InteractiveBox keeps a long-running shell inside the box, so repeated exec calls land in the same session and state persists between them. When a human needs it, the same box forwards a real PTY to their terminal.

Architecture

The shell outlives individual commands, so everything written to disk, installed, or exported stays available to the next call. Two ways in: programmatic exec for automation, or wait() to hand the terminal to a person.

Prerequisites

  • BoxLite installed and a working virtualization host — see Installation.
  • Any image with a shell. This guide uses alpine:latest.

Build it

Programmatic: a session that remembers

Interactive: hand the shell to a person

wait() forwards only when the host stdin is a real TTY — it detects this automatically. In scripts and CI, use the exec form above. The full InteractiveBox parameter table is on Interactive shell (PTY).

Run it

The last line is the one that matters: a file written by one exec was read back by another. That is the difference between a session and a series of one-shot runs.

Trust and limits

  • What the boundary covers. Each session is a microVM with its own kernel and filesystem. Tenants cannot see each other, and nothing a user installs or deletes reaches the host.
  • State persists, which cuts both ways. A long-lived session accumulates whatever ran in it. For multi-tenant use, one box per tenant and a fresh box per session are the safe defaults; reusing a box across tenants leaks state.
  • exec never raises on command failure. A failed command is a non-zero exit_code. Check it, especially in setup chains where a silent failure leaves later steps confused.
  • wait() needs a real terminal. In a non-TTY environment it will not forward. Use exec there.
  • The box lives as long as the block. Leave the async with scope and auto_remove destroys it along with everything installed. For a session that survives your process, set a name and auto_remove=False — and remember to reclaim it later with boxlite rm.

Troubleshooting

Next steps

  • One box per tenant. Give each box a stable name, set auto_remove=False, and reattach later with the runtime — see Lifecycle.
  • Bring your own toolchain. Start from an image that already has your compilers and CLIs — Use a custom agent image.
  • Let an agent use the workbench. Run Claude Code puts an autonomous coding agent in the same kind of persistent box.
  • Lock down what the session can reachRun untrusted tools safely.