How sandboxes work

The SDK, the daemon, cua-spacesd and the backends, where a sandbox runs and on what, how it becomes ready, and how its lifetime works.

  Python (cua-sandbox, cua)   TypeScript (@trycua/cua)   Swift (Cua)   Rust (cua-sdk)
            \                         |                       |           /
             +------------- cua SDK (Rust core, UniFFI) -----------------+
                  embedded in your process, or a client of `cua daemon`
                                        |
        local (gVisor, runc, QEMU, Lume) | your cloud | direct | relay
                                        v
             sandbox: any OCI image; optional cua-spacesd on port 3211

The pieces#

PieceWhat it isRuns on
cua SDK and cua CLIThe client: a Rust core with generated bindingsYour process or machine
cua daemonThe SDK as a local service, so several processes (the CLI, the Spaces app, MCP clients, your script) share sandboxes, connections and credentials. cua.connect() instead of cua.embedded()Your machine
cua-spacesdThe optional daemon inside a sandbox: one port (3211), gRPC and gRPC-Web. Input and accessibility go to Cua Driver in the guestThe sandbox
RuntimesLocal: gVisor or runc (on Docker or Podman), QEMU, Lume, and microVMs on Firecracker, vz or Apple's Containerization. Your cloud: a VM in your AWS or Google Cloud account, or a Modal sandboxThis machine or your cloud

Where, what kind, which engine#

Every create takes the same three choices, all optional:

AxisSDKCLIValuesDefault
Whereon= (cua-sandbox also local=True)--onlocal, aws, gcp, modal (your cloud), direct:<addr> (an existing machine running cua-spacesd), a registered providerdefault.on, else local
What kindkind=--kindauto, container, vm, microvmauto
Which engineruntime=--runtimeauto or an engine belowauto
Locationcontainervmmicrovm
localgvisor (auto when installed), runcqemu (auto), lume (auto for macOS guests; Apple silicon only)firecracker (Linux with KVM), vz (auto on a Mac when installed), containerization (macOS 26)
direct:<addr>the existing machine: auto only

auto kind comes from the image: macOS and Windows images are VMs, an image with a container rootfs is a container, and a disk-only image is a VM. So ghcr.io/trycua/linux:24.04 is a gVisor container locally, and --kind vm picks its -disk variant (QEMU locally). A runtime implies its kind. A combination that does not exist fails with InvalidPlacement, listing the valid values. auto never picks a microVM; see Space kinds for what each kind is and which hosts run it.

Defaults resolve in this order, first match wins:

  1. explicit: --on / --kind / --runtime, on= / local= / kind= / runtime=
  2. env: CUA_DEFAULT_ON, CUA_DEFAULT_KIND, CUA_DEFAULT_RUNTIME
  3. config: $CUA_HOME/config.toml (cua config set default.on aws)
  4. built-in: local, auto, auto

With no location given and none configured, a Space goes to this machine. When you have linked a cloud and set a budget (cua cloud budget --max-per-hour 2), it goes to this machine first and overflows into your clouds once this machine is full, never past the budget; the Space then says so (cloud, cloud_place, and a line from cua spaces create). cua config set placement.overflow off keeps it on this machine. An explicit local never overflows; auto, a host or a cloud keep their meaning.

A default kind or runtime that does not fit an explicit request is skipped. See Choose where sandboxes run by default.

Sandboxes do not require a guest agent#

Creating, waiting for and deleting a sandbox never assumes anything runs in the guest. A sandbox is ready when its backend reports it running and every probe you declare passes (wait_for=tcp("mcp"), http("web", "/health")). If the command exits first, readiness fails at once.

GuestWhat works
Your own servicessb.service(name), sb.public_url(name), sb.tunnel.forward(port), sb.mcp(name)
cua-spacesdAlso shell, files, screenshots, input, windows, accessibility, streaming, and sb.spacesd()
NothingLifecycle, plus the runtime's agentless paths (QMP, VNC, SSH or ADB) where available

Lifetime#

A connection and a sandbox have separate lifetimes. Disconnecting never stops a sandbox; deleting destroys it and its state.

PatternLifetime
Sandbox.ephemeral(...)Deleted when the block exits
Sandbox.create(...), cua sb createUntil deleted; a sandbox in your cloud also lapses at its time limit
suspend / resumeLocal only
Direct sandboxDeleting only forgets the record

The SDK managed cloud capacity for your account until Cua Cloud ended on 18 October 2026. Sandbox.snapshot() is not available in cua-sandbox 0.11.7. Streaming, teleport and the other Spaces primitives need cua-spacesd.