Skip to main content

cua-sandbox

Sandboxed VM environments with a unified Python API. Cloud by default.

pip install cua-sandbox

Fleet support is provided by the published cua-fleet wheel. It bundles the platform-specific fleet_sdk native binding. Install from the Cua wheel index when resolving dependencies with pip:

pip install --extra-index-url https://wheels.cua.ai/simple cua-sandbox

For typed desktop control of a Fleet sandbox through sb.driver.connect(), install the optional Driver SDK:

pip install --extra-index-url https://wheels.cua.ai/simple 'cua-sandbox[driver]'

The driver extra pins cua-driver==0.27.0, which provides the typed-window API and compatible remote channel bridge. It requires that version to be published for your platform. The sandbox image must also run a compatible Driver service.

Optional MCP envelope carrier

The SDK can carry the same typed Driver interface through a named MCP service. This requires the guest's explicit typed-envelope extension, not just an ordinary MCP tools endpoint:

from cua_driver import GetScreenSizeInput


async def observe_guest(pool):
    async with pool.claim() as sb:
        async with sb.driver.connect(service="mcp", transport="mcp") as driver:
            # This is the generated cua_driver.CuaDriver, not an MCP facade.
            result = await driver.get_screen_size(GetScreenSizeInput(session=None))
        return result

sb.driver.connect() and sb.driver.connect(service="driver") keep the existing envelope HTTP path. CuaDriver.connect(socket_path) is unchanged. Shell, files, terminals, existing Sandbox desktop calls, and claim/pool lifecycle still use their existing interfaces; this option affects only sb.driver.

MCP selection performs initialization and verifies capabilities.experimental["ai.cua.driver.envelopes"].version == 1 before opening a receiver. An old tools-only image fails before a desktop action. The connection preserves the receiver's generation, host-selected permissions, and cancellation. It does not reconnect, replay actions, or fall back to the local desktop. On exit, the SDK attempts bounded receiver and MCP-session cleanup. An unconfirmed cleanup warns; it is not proof of rollback or guest deletion.

See the wire and launcher contract for the opt-in and limits. Test the exact image and matching bindings/native library before advertising support; use the pinned optional extra and a compatible guest runtime.

Ephemeral sandbox

Created on enter, destroyed on exit.

from cua_sandbox import Sandbox, Image

async with Sandbox.ephemeral(
    Image.from_registry("registry.example/desktop-workspace@sha256:...")
) as sb:
    await sb.shell.run("uname -a")
    await sb.screenshot()

Persistent sandbox

Provision a new sandbox that stays alive after your script exits.

from cua_sandbox import Sandbox, Image

sb = await Sandbox.create(
    Image.from_registry("registry.example/desktop-workspace@sha256:...")
)
await sb.shell.run("uname -a")
print(sb.claim_name)  # Fleet lifecycle identifier; save this to reconnect later
await sb.disconnect()

Connect to existing sandbox

Attach to a sandbox that's already running. Works as a plain await or context manager.

from cua_sandbox import Sandbox

# plain await
sb = await Sandbox.connect("my-sandbox")
await sb.shell.run("whoami")
await sb.disconnect()

# context manager — disconnects on exit, sandbox keeps running
async with Sandbox.connect("my-sandbox") as sb:
    await sb.shell.run("whoami")

Destroy a sandbox

await sb.destroy()  # disconnect + permanently delete

Local VM

Spins up a local VM using QEMU or Lume, destroyed on exit.

from cua_sandbox import Sandbox, Image
from cua_sandbox.runtime import QEMURuntime

async with Sandbox.ephemeral(Image.linux(), local=True, runtime=QEMURuntime()) as sb:
    await sb.shell.run("uname -a")

Localhost (unsandboxed)

Direct host control — not sandboxed, use with caution.

from cua_sandbox import Localhost

async with Localhost.connect() as host:
    await host.shell.run("echo hello")
    await host.screenshot()

Cloud sandbox

Fleet is the OAuth cloud backend. Configure OAuth credentials once; Fleet uses https://run.cua.ai by default and can be overridden with configure(fleet_base_url=...) or CUA_FLEET_BASE_URL. The legacy API-key VM API continues to use https://api.cua.ai. Cloud images must use a registry reference; expose() declares additional Fleet services.

Fleet does not support snapshots or custom disks, and currently supports only us-east-1. await sb.tunnel.forward(3000) returns the authenticated Fleet service URL for an exposed port; it does not open a local SSH tunnel.

Fleet sandboxes can also create time-limited, revocable public URLs for an exposed service. Treat each URL as a bearer credential and revoke it as soon as the recipient no longer needs access.

signed_url = await sb.services.create_signed_url(
    "mcp",
    label="Customer demo",
    expires_in_seconds=3600,
)
print(signed_url.url)

active_urls = await sb.services.list_signed_urls()
await sb.services.revoke_signed_url(signed_url)

Fleet pools and durable claims

For production workloads, claim from an existing pool. Supplying pool= never changes its configuration; name= names the claim, while sb.name is the separately bound sandbox resource.

from cua_sandbox import Sandbox

sb = await Sandbox.create(
    pool="workspace",
    name="workflow-123",
    service="mcp",
    keep_alive_minutes=30,
)

reference = sb.to_dict()
await sb.disconnect()  # claim remains held

# A later process or Temporal activity re-resolves the live claim.
sb = await Sandbox.from_dict(reference)
await sb.keep_alive(minutes=30)
await sb.close()  # idempotently releases the claim

Fleet pool names are globally unique across accounts, so Sandbox.create requires an explicitly named pool for registry images: apply one with Pool.apply(image, name=...) and pass it as pool=. Sandbox.ephemeral(image) instead creates an isolated temporary pool under a random name and deletes it after releasing the claim, preserving teardown-by-default semantics. If a chosen pool name is already owned by another account, Fleet refuses it and the SDK raises PoolAccessDeniedError — pick a different name.

from cua_sandbox import Image, Sandbox

image = Image.from_registry("registry.example/desktop-workspace@sha256:...")

async with Sandbox.ephemeral(
    image,
    name="job-123",
    cpu=4,
    memory_mb=4096,
    server_port=5000,
) as sb:
    await sb.shell.run("uname -a")

To deliberately retain warm capacity for later calls, opt in with keep_pool=True. It requires name= so later runs can find the kept pool:

async with Sandbox.ephemeral(image, name="shared-pool", keep_pool=True) as sb:
    await sb.shell.run("uname -a")

The equivalent lower-level reusable-pool API is:

from cua_sandbox import Image, Pool

pool = await Pool.apply(
    Image.from_registry("registry.example/desktop-workspace@sha256:..."),
    name="desktop-workspace",
    replicas=1,
    cpu=4,
    memory_mb=4096,
    services={"server": 8000, "mcp": 3000},
)

sb = await pool.claim(name="job-123", service="mcp")
await sb.close()

Pool.claim() is both awaitable and an async context manager, so existing scoped usage remains valid:

async with pool.claim(name="job-123") as sb:
    await sb.shell.run("echo hello")

Instead of a static replicas count, a pool can scale with claim demand by passing autoscaling=. The pool then grows toward max_pool_size while claims are pending and shrinks back to min_pool_size as they are released; initial_pool_size seeds a one-time warm head start at creation:

from cua_sandbox import Image, Pool, WarmPoolAutoscaling

pool = await Pool.apply(
    Image.from_registry("registry.example/desktop-workspace@sha256:..."),
    name="desktop-workspace",
    cpu=4,
    memory_mb=4096,
    autoscaling=WarmPoolAutoscaling(
        min_pool_size=0,
        initial_pool_size=2,
        max_pool_size=10,
    ),
)

Pool.reconcile(CreatePoolRequest(...)) and Template.reconcile(CreateTemplateRequest(...)) remain available for advanced generated-schema configuration. The public generated builders should be used instead of constructing builder-enabled Fleet records directly.

The image must run the CUA computer-server /cmd API on the configured server_port. Windows computer-server images continue to use the default port 8000.

Fleet currently supports registry images, CPU, memory, replica count, claim-demand autoscaling, and named TCP services. Local image builds, layers, injected files or environment, snapshots, custom disks, unsupported regions, and provider-crossing serialization raise NotImplementedError.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

cua_sandbox-0.7.0.tar.gz (181.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

cua_sandbox-0.7.0-py3-none-any.whl (218.0 kB view details)

Uploaded Python 3

File details

Details for the file cua_sandbox-0.7.0.tar.gz.

File metadata

  • Download URL: cua_sandbox-0.7.0.tar.gz
  • Upload date:
  • Size: 181.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.12.14

File hashes

Hashes for cua_sandbox-0.7.0.tar.gz
Algorithm Hash digest
SHA256 72ab0b92db94c2aa5d89e719da0e6a106eec90a95b501269de074053b845cc86
MD5 54a9a824466c32f25acdc5bac58de022
BLAKE2b-256 10dd10116eeeb74083cd65dc03433a0fa7c6d3b14f1b7602eb3716d46130050b

See more details on using hashes here.

File details

Details for the file cua_sandbox-0.7.0-py3-none-any.whl.

File metadata

  • Download URL: cua_sandbox-0.7.0-py3-none-any.whl
  • Upload date:
  • Size: 218.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.12.14

File hashes

Hashes for cua_sandbox-0.7.0-py3-none-any.whl
Algorithm Hash digest
SHA256 c3a4d8249fb04eef6f6c3f53eee25a4c016912c33956dd783434060e5dceaa16
MD5 2b602c587e6354e3736775f1d0b5f332
BLAKE2b-256 83f2371a4f00137040bd31afcd6ab547ccd2d3218ca89554afe34492b85b8403

See more details on using hashes here.

Release history Release notifications | RSS feed

0.8.0

2 files

This release

0.7.0 This release

2 files

0.6.0

2 files

0.5.0

2 files

0.4.3

2 files

0.4.2

2 files

0.4.1

2 files

0.4.0

2 files

0.3.6

2 files

0.3.5

2 files

0.3.4

2 files

0.3.3

2 files

0.3.2

2 files

0.3.1

2 files

0.3.0

2 files

0.2.0

2 files

0.1.28

2 files

0.1.27

2 files

0.1.26

2 files

0.1.25

2 files

0.1.24

2 files

0.1.23

2 files

0.1.22

2 files

0.1.21

2 files

0.1.20

2 files

0.1.19

2 files

0.1.18

2 files

0.1.17

2 files

0.1.16

2 files

0.1.15

2 files

0.1.14

2 files

0.1.13

2 files

0.1.12

2 files

0.1.10

2 files

0.1.9

2 files

0.1.8

2 files

0.1.7

2 files

0.1.6

2 files

0.1.5

2 files

0.1.4

2 files

0.1.3

2 files

0.1.2

2 files

0.1.1

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page