Sandbox providers
Configure Cloudflare Containers, Daytona, or an external runtime host, and their required fields.
The sandbox is the isolated environment where the agent inspects repositories and produces output. The Worker remains the trusted controller; every provider speaks the same tasuku-runtime HTTP protocol described in Architecture. Sandbox configuration uses a strict decoder — unknown fields are rejected.
Cloudflare Containers
The default provider, and the only one the Deploy to Cloudflare button configures without further setup. Two instance classes share one image:
| Class | Instance type | Default max_instances | Use for |
|---|---|---|---|
standard (AgentContainerStandard) | standard-2 | 3 | Reviews and most agent work. |
large (AgentContainerLarge) | standard-4 | 1 | Heavier toolchains or larger repositories. |
{
"provider": "CLOUDFLARE_CONTAINER",
"config": {
"instance_class": "standard"
}
}max_instances is a wrangler.jsonc-level setting, not a per-repository one — raise it in containers[] if your Cloudflare container quota allows more concurrent instances, and keep TASUKU_GLOBAL_MAX_RUNNING at or below what it can actually satisfy (see Deployment). There is no pooling: every attempt gets a fresh container instance and workspace, destroyed when the attempt finishes. Credentials are injected by an Outbound Worker on egress — the container itself never holds a GitHub or Claude token; see Security.
Daytona
{
"provider": "DAYTONA",
"config": {
"api_url": "https://app.daytona.io/api",
"snapshot": "docker.io/amalshaji/tasuku-agent-runtime:0.1.0",
"cpu": 2,
"memory_gb": 4,
"disk_gb": 10,
"auto_stop_minutes": 15
}
}There is no api_key field in this config — the Daytona API key always comes from the stored ${secrets.DAYTONA_API_KEY} reference at the sandbox profile's own scope (organization or repository), never embedded in the config JSON itself; see Secrets. api_url, cpu, memory_gb, disk_gb, and auto_stop_minutes are required — the strict decoder rejects a config that omits any of them, and the dashboard's suggested values must still be submitted, not just displayed. Only snapshot (and the internal image field) are optional. api_url must resolve to Daytona's own SaaS API host (https://app.daytona.io) — the API key is never sent anywhere else, even if this field is pointed at a different host. Tasuku creates the sandbox from the pinned image, starts tasuku-runtime serve as a detached session command, and talks to it over the sandbox's port-8787 preview URL. Because Daytona can't attach to Cloudflare's Outbound Worker credential-injection hooks, credentials for this provider are placed directly in the sandbox (credential_mode: INLINE) — the same trust model an external runtime host uses. Every sandbox is created ephemeral: it's deleted immediately once it auto-stops after auto_stop_minutes, with no separate grace period to configure.
An optional network_allow_list names additional hostnames (not CIDR ranges — Daytona's separate CIDR allow-list isn't exposed here) the sandbox may reach, on top of whatever the run's own egress policy already computes; the two are combined, not one replacing the other. With nothing to allow at all (an empty computed policy and no network_allow_list), the sandbox blocks all outbound network access rather than allowing it.
External runtime host
Self-host the same image behind your own HTTPS endpoint when you need a sandbox host outside Cloudflare and Daytona:
{
"provider": "EXTERNAL_RUNTIME",
"config": {
"base_url": "https://runtime.internal.example.com",
"auth_header_template": "Bearer ${secrets.EXTERNAL_RUNTIME_TOKEN}",
"timeout_minutes": 30
}
}base_url must be HTTPS with no userinfo or IP-literal host. auth_header_template renders a header value from a stored secret — typically Bearer ${secrets.NAME} — sent as Authorization on every request to that host. timeout_minutes is required, with no default — it bounds how long a single run may occupy this host. One host can serve many runs; Tasuku checks capacity with GET /healthz before acquiring a run.
Run the image with runtime/deploy/docker-compose.external.yml:
TASUKU_RUNTIME_TOKEN=$(openssl rand -hex 32) docker compose -f docker-compose.external.yml up -dPut this host behind TLS yourself — a reverse proxy or a Cloudflare Tunnel terminating HTTPS — the runtime itself only speaks plain HTTP on port 8787. The compose file carries forward today's container hardening: cap_drop: [ALL], no-new-privileges, a read-only root filesystem, tmpfs mounts for /tmp, /run/tasuku, /workspace, /home/bun/go, /home/bun/.cache, and /home/bun/.codex, and a pids_limit of 512.
TASUKU_RUNTIME_PROFILE is a free-form label — the runtime doesn't validate or branch on it, it's only echoed back in the GET /healthz response and startup logs (see Architecture) so you can tell at a glance which toolchain an instance is actually running. Set it to match the three toolchain profiles the image is published with (runtime/Dockerfile's AGENT_TOOLCHAIN_PROFILE build arg): review skips compilers, Python, pnpm/yarn, and osv-scanner for the fastest sandbox mount; default adds the Python/matplotlib and osv-scanner stack most workflows need; full adds Go and Rust on top of that. Also set TASUKU_RUNTIME_MAX_RUNS to size concurrency for your host. Credentials for this provider are placed directly in the sandbox (credential_mode: INLINE), the same as Daytona — protect the bearer token and the host's network path accordingly.
Profiles per repository
A repository can select a different sandbox profile from the instance default — for example, routing review runs to a smaller Cloudflare Container class while implementation runs use Daytona for more disk. See the repository's Agent runtime → Sandboxes settings in the dashboard.
Shared expectations
Every provider must make the agent toolchain available, isolate the agent from control-plane resources, enforce the configured egress tier (see Security), and return bounded output to the Worker. Changing providers does not move the GitHub publication boundary into the sandbox — the sandbox never publishes directly, on any provider.