Skip to content

feat(sandbox): E2B sandbox provider with idle pause/resume - #72

Merged
skrishnan22 merged 33 commits into
mainfrom
t3code/evaluate-daytona-e2b-sandboxes
Oct 2, 2026
Merged

skrishnan22 merged 33 commits into
mainfrom
t3code/evaluate-daytona-e2b-sandboxes

Conversation

@skrishnan22

Copy link
Copy Markdown
Owner

Summary

Adds a provider-neutral sandbox layer and an E2B sandbox provider, with pause-when-idle. The goal is to fix CPU-starved installs and previews, flaky R2 workspace snapshots, and the long preview path on Cloudflare standard-1 (½ vCPU).

  • Provider interface (packages/worker/src/sandbox-provider/): SandboxProvider / SandboxHandle, modeled on Flue's adapter pattern. The Cloudflare adapter wraps the existing code with no behavior change.
  • E2B adapter (2 vCPU / 4 GiB template):
    • Sandboxes are created with network: { allowPublicTraffic: false }. Preview requests go to E2B port URLs with the traffic token injected by the Worker, and the client never sees it.
    • Agent logs are written to files; files are written symlink-safe; sandboxes are destroyed by ref without resuming them.
    • The E2B API key and traffic token are redacted everywhere.
  • Idle pause and resume (E2B):
    • The Orchestrator alarm pauses the sandbox after max_idle_time (default 10m) with no agent input or preview traffic. E2B pause keeps the filesystem, memory and running processes.
    • An Agent Request, an authenticated preview request, or a preview start/stop resumes it. A fresh WebSocket token is written to /run/codevil/ws-token, and the agent adopts it within about 2s.
    • Lease renewal runs while the sandbox is active, and teardown covers paused sandboxes.
  • Image: Dockerfile.sandbox takes a SANDBOX_BASE arg (default unchanged), and CI builds both variants. pnpm --filter @codevil/sandbox-image e2b:template publishes the E2B template (supports --dry-run).
  • Rollout safety: the code default is e2b, but wrangler.toml pins SANDBOX_PROVIDER = "cloudflare" until the template, secret and E2E are in place (see Deploy below).

Design: docs/superpowers/specs/2026-10-01-e2b-sandbox-provider-design.md (includes live E2B feasibility findings). Plan: docs/superpowers/plans/2026-10-01-e2b-sandbox-provider.md.

Note: this branch also carries 13 earlier Slack commits (ded3e55…ade0485) that aren't on main yet.

Deploy (to switch production to E2B)

  1. Start Docker, then publish the template:
    E2B_API_KEY=... CODEVIL_SANDBOX_IMAGE=<registry>/codevil-sandbox:<tag> \
    [CODEVIL_REGISTRY_USERNAME=... CODEVIL_REGISTRY_PASSWORD=...] \
    pnpm --filter @codevil/sandbox-image e2b:template
    
  2. wrangler secret put E2B_API_KEY
  3. Set SANDBOX_PROVIDER = "e2b" in packages/worker/wrangler.toml (or your operator config), then deploy.

Test plan

  • pnpm verify passes (worker 699, shared 212, sandbox-image 140, admin-cli 34, cli 28; web/site green)
  • Live E2B feasibility probe: SDK runs in workerd; token-gated HTTP and WebSocket preview work (403 without the token); pause/resume keeps processes; the Hobby clock resets on resume
  • Template published, and the E2B image builds on node:22-slim (first real build)
  • Manual E2E on E2B (set max_idle_time to 2m to save time):
    • clone + npm install
    • preview with HMR
    • idle → paused
    • resume via prompt, then via preview; the dev server is still running
    • stop → sandbox gone in the E2B dashboard
    • agent still alive more than 2 minutes after start
  • Cloudflare sessions behave as before

skrishnan22 and others added 30 commits October 1, 2026 20:03
Co-Authored-By: Claude <noreply@anthropic.com>
…vider plumbing

Co-Authored-By: Claude <noreply@anthropic.com>
Deviation from the original plan, per feasibility findings: create uses
network.allowPublicTraffic=false (secure and a top-level allowPublicTraffic
are ignored by e2b 2.x and left the sandbox public).
…B template

Adds SANDBOX_BASE build arg (default cloudflare/sandbox:0.12.7), installs bun when
missing, prepares /var/log/codevil and /run/codevil, and an e2b:template script
(docker build -> push -> Template.build via fromImage). Template was not published
and no image was built or pushed (Docker daemon unavailable; publishing is deferred).
…troy, and Cloudflare production default

- background agent start passes timeoutMs: 0 so the SDK's 60s deadline cannot kill it
- retry E2B create on 429/5xx/network errors; stop forwarding inbound request.cf
- token parent directory via install -d -m 0755
- optional SandboxProvider.destroyByRef (E2B static kill)
- wrangler.toml pins SANDBOX_PROVIDER=cloudflare until E2B is ready; README and env example document E2B_API_KEY
…iately, and build the E2B image in CI

- worker passes CODEVIL_SANDBOX_PROVIDER; preview manager sets __VITE_ADDITIONAL_SERVER_ALLOWED_HOSTS=.e2b.app on e2b
- agent polls the ws-token file every 2s and reconnects at once when a new token is adopted
- readTokenFile non-ENOENT WARN branch is tested
- CI sandbox-image job also builds and smoke-tests the E2B variant
…ng reasons, and clean up on failure

- createSession/handleCreateSession resolve the provider before the D1 insert (missing E2B_API_KEY fails clearly, no row)
- provisioning failure broadcasts the redacted reason and destroys an already-created sandbox
- agent error outside a run destroys the sandbox
- warn when max_time exceeds the E2B continuous runtime limit
- answering a question counts as activity
…nce, and recover from mid-pause eviction

- per-Orchestrator handle cache invalidated on pause, resume, terminate, loss, ref change and SandboxNotFoundError
- terminate/destroy use destroyByRef for paused or unconnectable sandboxes; failed resume kills by reference
- assume paused after a DO eviction between provider pause and the paused marker
- remove the never-shipped pending_preview_start legacy field and migration
When wrangler.toml selects SANDBOX_PROVIDER = "e2b", a new e2b-template
job builds this commit's sandbox image, pushes it to GHCR with
GITHUB_TOKEN, and publishes it as the E2B template tagged with the commit
SHA (plus "default", so the untagged name follows the latest publish).
The deploy job waits for it and pins the Worker to
E2B_TEMPLATE_ID = "<id>:<sha>". Cloudflare deploys skip the publish.

Publishing runs in its own job so the GITHUB_TOKEN E2B receives as pull
credentials expires once the publish finishes, and the job that holds
Cloudflare credentials never has packages: write.

The deploy pipeline reads SANDBOX_PROVIDER and E2B_TEMPLATE_ID from the
top-level [vars] table only, and requires SANDBOX_PROVIDER to be explicit
because the Worker otherwise defaults to E2B.
@skrishnan22
skrishnan22 merged commit 1b4aaa8 into main Oct 2, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant