Skip to content

Sandboxes

Every agent has its own sandbox: a container with a persistent home directory at /home/agent. The server drives sandboxes through a small driver interface (internal/sandbox.Driver).

Every agent has its own sandbox: a container with a persistent home directory at /home/agent. The server drives sandboxes through a small driver interface (internal/sandbox.Driver).

Lifecycle

State Meaning
creating Image pull, network, volume and container are being set up.
starting / stopping The sandbox is being started or put to sleep.
running The sandbox is up.
sleeping Stopped. The disk is kept; running processes are not.
rebuilding The container is recreated from the current image and resources; the home volume is kept.
destroying The sandbox, its home volume and its network are removed, then the agent.
error The last operation failed. The message says why; start or rebuild to retry.

State is stored in SQLite. Each operation first moves the state with a guarded update, so concurrent requests cannot both act, and then runs in the background. On startup the server reconciles the stored state with what the driver reports, so interrupted operations and changes made outside the platform (for example docker stop) are picked up.

Changing an agent’s image or resources marks it as needsRebuild; the change applies on the next rebuild.

Sleep and wake

Agents sleep when nobody uses them. An agent is idle when, for its idle timeout (idleMinutes, default 15):

  • no turn is running in any of its sessions,
  • no terminal is open,
  • nobody prompted it, opened a terminal or used its files, and
  • the processes in its sandbox used less than 5% of a CPU core. sandboxd reports their CPU time, so a long build or test run keeps the agent awake.

The sleeper checks every sandbox.idle_check_interval (default 30s) and stops idle sandboxes. The disk and the conversations survive; running processes do not. Any prompt, terminal or Start request wakes the agent again. When a prompt wakes an agent, the session log shows it (“Starting the sandbox…”). The harness then restores the conversation with session/load or session/resume (see sessions and harnesses). If the harness can do neither, a notice says that the context was reset.

While an agent runs, the API reports lastActivityAt and sleepsAt, and the UI shows when it will fall asleep. After a server restart, the idle clock of running agents starts again.

Docker driver

For each agent the Docker driver creates:

  • a container <prefix>-<agent id>, with --init, CPU, memory and PID limits and an optional runtime,
  • a volume <prefix>-home-<agent id> mounted at /home/agent,
  • an internal bridge network <prefix>-net-<agent id> with a subnet from network_pool. Internal networks have no route to the outside. The server’s own container is attached to every agent network and is the only way out (see the egress proxy, M4).

The server must be able to reach the Docker socket. When it runs in a container it detects its own container id automatically; set server_container if detection does not work.

sandbox:
  driver: docker            # or "fake" for development without Docker
  default_image: debian:bookworm-slim
  default_cpus: 2
  default_memory_mb: 4096
  max_cpus: 8
  max_memory_mb: 16384
  pids_limit: 4096
  docker:
    name_prefix: agenthold  # change to run several installations on one host
    network_pool: 10.213.0.0/16
    subnet_bits: 28         # 14 usable addresses per agent
    runtime: ""             # e.g. sysbox-runc or runsc
    server_container: ""    # autodetected

Docker-backed tests carry the docker build tag: make test-docker.

sandboxd and the tunnel

sandboxd is a static Linux binary and the entrypoint of every sandbox, which is why any OCI image works. The Docker driver mounts it read-only at /opt/agenthold from a volume (sandbox.docker.sandboxd_volume). The server copies its own sandboxd into that volume at startup, so sandboxes always run the version that matches the server. When the server runs on the host instead of in a container, the directory of sandbox.sandboxd_path is bind-mounted.

Before each start the server writes /etc/agenthold/boot.json into the stopped container. It holds the agent id, a fresh per-boot token and the tunnel URL. sandboxd then dials the server, which listens on sandbox.tunnel_listen (default :8090). It connects at the server container’s fixed address (.2) on the agent network, and containers only receive addresses from the upper half of the subnet. The server stores only a hash of the token and accepts it only while the sandbox is starting or running. A sandbox counts as running once its sandboxd has connected.

The connection is a WebSocket carrying a yamux session; each stream is one channel:

Channel Used for
pty Web terminal. Shells run as the agent account if the image has one, otherwise as the image’s user.
fs.list, fs.read, fs.write, fs.mkdir, fs.remove File browser. Relative paths are relative to /home/agent.
hello, ping Identification and health.

sandboxd reconnects with backoff if the connection drops. Opening a terminal wakes a sleeping agent; file operations need a running agent.

With sandbox.driver: fake the server runs sandboxd in-process for every sandbox, so terminals run on the server’s own machine as the server’s user. Use the fake driver only for development and tests.

Type to search…

↑↓ navigate↵ selectEsc close