Skip to main content
A sandbox is an isolated execution environment that Zuko provisions for each chat thread. When an AI agent needs to run code, read files, or execute shell commands, it does so inside the chat’s sandbox — keeping execution safe and scoped to that conversation.

Why sandboxes exist

AI agents in Zuko can use tools that run real code: executing shell commands, reading and writing files, running grep, and more. Without isolation, concurrent agent runs would share a filesystem and interfere with each other. Sandboxes solve this by giving every chat its own environment.

Two execution backends

Zuko supports two sandbox implementations selected at runtime:
ModeWhen usedHow it runs
Sprites (remote)SPRITES_ENABLED=trueCloud machine managed by Sprites — isolated VM per chat
LocalSPRITES_ENABLED=falseNode.js child_process.exec on the backend host — for development only
In production you should use Sprites. Local mode is for local development when you don’t need full isolation.

Lifecycle states

Each sandbox moves through a lifecycle managed by SandboxLifecycleService:
provisioning → active → hibernating → hibernated
                  ↑                        |
                  └──────── restoring ─────┘
StateMeaning
provisioningSandbox is being created for the first time
activeRunning and available for agent tool calls
hibernatingIn the process of being suspended
hibernatedSuspended — Sprites machine is stopped, no cost accrues
restoringWaking from hibernation in response to a resume request
failedAn error occurred; the error message is stored in the database

Auto-hibernation

A background job runs every minute and hibernates any sandbox that has been inactive for 30 minutes (configurable via SANDBOX_INACTIVITY_TIMEOUT_MS). Activity is refreshed each time the agent executes a tool in that sandbox.

Resuming a hibernated sandbox

When a user opens a chat whose sandbox is hibernated, the frontend detects the hibernated state via SSE and can call POST /api/sandboxes/:id/resume to wake it. The sandbox transitions through restoring → active.

How sandboxes are named

Every sandbox is named after its chat:
zuko-{chatId}-{threadId}
This name is used as the Sprites machine identifier, so it is stable across hibernation cycles.

Real-time status via SSE

The frontend connects to a Server-Sent Events stream to receive live state updates:
GET /api/proxy/sandboxes/:id/lifecycle/events
The SandboxStatusBadge component in the chat UI uses this stream to show the current state with color-coded badges:
ColorStates
Greenactive
Yellow (pulsing)provisioning
Blue (pulsing)restoring
Zinchibernated
Yellow (pulsing)hibernating
Redfailed

State reconciliation

When an SSE client connects, the backend performs a background reconciliation check. If the database says active but Sprites reports the machine is cold (e.g., auto-suspended by Fly), the backend writes hibernated to the database and emits the corrected state to all listeners. Reconciliation is debounced to once per 60 seconds per sandbox to prevent SSE reconnects from hammering the Sprites API.

Sandbox tools available to agents

The Sandbox interface exposed to agent tools provides:
MethodDescription
exec(command, cwd, timeoutMs)Run a shell command
readFile(path)Read a file
writeFile(path, content)Write a file
glob(pattern)Glob for files
grep(pattern, opts)Search file contents
stat(path)Get file metadata
mkdir(path)Create a directory
readdir(path)List directory contents

Filesystem API

The backend also exposes a REST API for reading and writing sandbox files directly (used by the frontend and debugging tools):
MethodEndpointDescription
GET/api/sandboxes/:id/fs/*path?list=trueList directory
GET/api/sandboxes/:id/fs/*pathRead file
PUT/api/sandboxes/:id/fs/*pathWrite file