Run Daytona code on Spaces

Point the unmodified Daytona Python or TypeScript SDK at Cua: sandboxes become Spaces on your own machines. What maps, what answers with Daytona's own error, and how it is tested.

The Daytona SDKs (daytona for Python, sync and async, and @daytonaio/sdk for TypeScript, 0.225.0 and the latest release) run against a local Cua gateway with no code change. Every sandbox is a Space on your machines, your pool or your BYOC clouds.

cua run --compat daytona -- python agent.py

cua run --compat daytona starts the gateway for the life of the job and sets DAYTONA_API_URL, DAYTONA_API_KEY and SSL_CERT_FILE (and NODE_EXTRA_CA_CERTS for Node). For a long-running gateway use cua compat serve --provider daytona, then eval "$(cua compat env --provider daytona)".

from daytona import Daytona, CreateSandboxFromSnapshotParams
 
daytona = Daytona()
sandbox = daytona.create(CreateSandboxFromSnapshotParams(labels={"cua.kind": "vm"}))
print(sandbox.process.exec("uname -a").result)

What you get that Daytona does not have#

Labels place a sandbox: cua.on (local, host:<machine>, a pool, a BYOC cloud), cua.kind (vm, container, microvm). Computer use works on Linux, macOS and Windows Spaces. Forks and snapshots are copy-on-write.

What is covered#

Everything the SDKs expose is answered; the full matrix, with the test that exercises each method, is generated from the SDKs at libs/cua/crates/cua-compat/maps/daytona-coverage.md.

  • Sandboxes: create from a snapshot, an image or a Dockerfile (Image builder, local file and directory contexts), get, list with labels, states, resource filters, sorting and pages, start, stop, pause, archive, recover, fork, delete, labels, auto-stop/pause/archive/delete intervals, TTL, public flag, network policy, secrets, metrics.
  • Processes: exec, code_run, sessions (state carries over, async commands, ordered, logs with follow, input), PTYs, the Python code interpreter with contexts, matplotlib charts.
  • Files: single and bulk transfer, streaming both ways, signed URLs, permissions, search and replace; large files are verified by checksum.
  • Git, LSP servers (Python, TypeScript, JavaScript), preview links and signed preview URLs, snapshots, volumes, secrets, warm pools.
  • Computer use: mouse, keyboard, screenshots, displays and windows, recordings (MP4, needs ffmpeg on the gateway host), accessibility.
  • Errors: the same exception classes, status codes and codes (DaytonaFileNotFoundError, DaytonaProcessExecutionTimeoutError, DaytonaGitAuthFailedError, ...).

Where Cua differs, and why#

DaytonaOn Cua
resizeApplied: the Space stops and boots again from its own disk with the new vCPUs, memory and a larger disk (Daytona's rules hold: a running sandbox only grows cpu and memory, the disk grows only while stopped). Files are kept, running processes are not, and a stopped sandbox stays stopped. Placements that cannot be resized (containers, clouds) answer Daytona's 400 with the reason.
SSH access tokensAnswers with Daytona's 400. Use a PTY (process.create_pty_session), cua sandbox shell or space_bash.
archiveThe sandbox shows archived and keeps its disk on the host; there is no object-storage tier below stopped.
stopThe Space is suspended (memory kept) where its runtime cannot cold-stop, so background processes survive stop/start.
SecretsDaytona substitutes the value in outbound HTTPS at an egress proxy; Cua Spaces have none. Add the label cua.secrets=plaintext to inject the value into the environment variable; without it creating with secrets is refused with this reason.
VolumesA volume is a folder of Cua Volume bound at the mount path; it needs Cua Volume mounted in the Space. Cua Volume lets a Space write only its own folder, so a folder that several sandboxes share needs a grant that you confirm (cua volume grant space:<id> --prefix daytona-volumes/<volume id> --mode rw); without it the create answers 400 and names that command.
Warm poolsA Cua pool of an image snapshot, on this machine.
Egress rulesAn nftables policy in the guest (Linux guests); network_block_all, network_allow_list, domain_allow_list and update_network_settings are enforced there.

A sandbox boots with its desktop, because computer use is part of a Daytona sandbox. The label cua.desktop=false leaves it out for a faster boot and more sandboxes per machine; computer use calls then answer Daytona's 503 naming the missing desktop.

Keeping capacity warm#

cua run --compat daytona --prewarm IMAGE[=N] keeps N Spaces of a Cua image ready, so the first daytona.create() starts from warm capacity and finishes inside the SDK's 60 second create timeout even on a cold host. IMAGE is read the way a create reads its snapshot: empty or base is the default image, a snapshot you registered names its image (a Dockerfile snapshot its base image, with its layers built once and cached), and anything with a / or : is an image reference. A snapshot of a sandbox is a copy-on-write fork source that is already instant, so it is refused as nothing to warm. Daytona's own warm pools (warm_pool.create) are the same thing declared on the server: each pool's snapshot is read the same way and its pool size is the count, so the flag and the pools add up to one set of warm Spaces.

How it is tested#

The same cases run through the Python SDK (sync and async) and the TypeScript SDK, over mock Spaces on every pull request and on real Spaces nightly, and their observations are compared with a golden that is derived from the SDK sources (no Daytona service is ever called). A weekly job runs the latest SDK releases and fails on a changed signature, so drift is noticed. Wire translation is covered by property tests (proptest and hypothesis), and a nightly guard fails when the gateway's own overhead grows by more than 1.5x its recorded baseline.