feat(sandbox): migrate opencode harness + snapshot template to Daytona#337
feat(sandbox): migrate opencode harness + snapshot template to Daytona#337ishaan-berri wants to merge 7 commits into
Conversation
Same pre-bake as e2b/e2b.Dockerfile (postgres + litellm[proxy] + uv) so the first execute on a Daytona sandbox doesn't wait 15 min for apt + pip. Notable diffs vs the E2B version: - FROM ubuntu:22.04 (e2bdev/code-interpreter had python preinstalled; this installs python3/python3-pip explicitly). - useradd `user` explicitly (E2B's base provided it). - No e2b.toml; resources are passed at snapshot/sandbox create.
Programmatic equivalent of `e2b template build`. Uses @daytona/sdk's Image.fromDockerfile + daytona.snapshot.create to register the snapshot once so subsequent sandbox creates reference it by name. Streams build logs and bakes resources (cpu/memory/disk) at snapshot time.
Same files the E2B template ships; the Daytona Dockerfile expects them in its build context. Kept verbatim to make the diff obvious.
The MCP now delegates every call through the platform sandbox endpoint, which picks the configured provider (Daytona/E2B) via SANDBOX_CHOICE. The direct-E2B path duplicated SDK logic that already lives in src/server/sandbox/e2b.ts and was a second place to chase provider quirks (sandbox.kill, sandbox.setTimeout keepalive, file read formats). When no session_id is available (env or tool arg) the four tools now return a single shared error pointing the agent at the <lap_session_id> tag — no silent fallback. The E2B *provider* (src/server/sandbox/e2b.ts) is kept intact so other LAP users can still pick "e2b" via SANDBOX_CHOICE.
The MCP no longer imports the e2b SDK after removing the direct fallback. @daytona/sdk isn't added here either — the MCP only speaks to the LAP platform's sandbox endpoint; the platform owns the provider SDK.
The first build attempt failed: 'Disk request 20GB exceeds maximum allowed per sandbox (10GB)'. Lower the default to 10 in build-snapshot.mjs and note in the README that callers need to ask Daytona support for more.
Greptile SummaryThis PR migrates the opencode harness sandbox MCP from a dual-mode (platform + direct-E2B fallback) implementation to a single platform-delegation mode, and adds a new
Confidence Score: 4/5The MCP refactor is clean and the removal of the direct-E2B fallback is well-scoped; no logic that could silently corrupt data or break auth flows was introduced. The core MCP change eliminates duplicated SDK code and the platform-only path is simpler to reason about. The findings are limited to a misleading error message, stale comments, and dev credentials baked into image ENV layers that must not be promoted to production. daytona/daytona.Dockerfile warrants a second look before any production snapshot is built from it, given the dev credentials baked into ENV layers.
|
| Filename | Overview |
|---|---|
| harnesses/opencode/sandbox-mcp.mjs | Removes the direct-E2B fallback path; all four tools now route exclusively through the LAP platform endpoint. Logic is clean, but the shared missingSessionError message conflates two distinct failure conditions (missing session_id vs missing BASE URL). |
| daytona/daytona.Dockerfile | Port of the E2B Dockerfile to ubuntu:22.04 with explicit user creation and CA bundle setup. Hardcoded dev credentials are baked into image ENV layers — acceptable for dev but risky if the same image is accidentally promoted to production. |
| daytona/build-snapshot.mjs | Straightforward one-shot script to build the Daytona snapshot; streams logs, validates API key presence, and passes a 1800-second (30 min) timeout which matches the Daytona SDK's seconds-based convention. |
| daytona/start-db.sh | Copied verbatim from e2b/; functionally correct but retains stale comments referencing E2B's start_cmd / e2b.toml which no longer apply in the Daytona context. |
| harnesses/opencode/package.json | Removes the e2b dependency and updates the description to reflect provider-agnostic delegation. |
| daytona/dev-up.sh | Copied from e2b/; starts postgres and exports dev env vars. Clean and correct for the Daytona context. |
| daytona/README.md | Documents the snapshot build process, resource overrides, and differences from the E2B template. Accurate and complete. |
| daytona/cloud-vault-ca.crt | Internal CA certificate copied from e2b/; used to trust the cloud-vault HTTPS proxy inside sandboxes. |
Reviews (1): Last reviewed commit: "fix(daytona): default disk to 10GB — Day..." | Re-trigger Greptile
| function missingSessionError(tool) { | ||
| return textResult( | ||
| `${tool} failed: no session_id available. Pass session_id from the ` + | ||
| `<lap_session_id> tag in your context (and ensure LAP_BASE_URL is set).`, | ||
| true, | ||
| ); | ||
| } |
There was a problem hiding this comment.
missingSessionError message is misleading when BASE is the failing condition
The guard !effectiveSid || !BASE fires when either value is absent, but the error text always leads with "no session_id available". If SESSION_ID is correctly set but LAP_BASE_URL is missing (e.g. a misconfigured deployment), the agent and operator will look for a session-ID problem first and waste time before finding the parenthetical "(and ensure LAP_BASE_URL is set)". Splitting the two cases into distinct messages would make the failure immediately actionable.
| # Used two ways: | ||
| # 1. As the E2B sandbox start command (e2b.toml `start_cmd`) so the DB is up | ||
| # the moment a sandbox boots — no agent action required. | ||
| # 2. By dev-up.sh, for humans in an interactive shell. | ||
| # | ||
| # The cluster is owned by `user` and postgres refuses to run as root. E2B runs | ||
| # the start_cmd as root, so we drop to `user` when invoked as root; when a human | ||
| # runs it (already `user`), we run directly. |
There was a problem hiding this comment.
Stale E2B reference: this script now lives in
daytona/ and Daytona has no start_cmd / e2b.toml equivalent. The comment describes the old E2B usage path, which no longer applies here.
| # Used two ways: | |
| # 1. As the E2B sandbox start command (e2b.toml `start_cmd`) so the DB is up | |
| # the moment a sandbox boots — no agent action required. | |
| # 2. By dev-up.sh, for humans in an interactive shell. | |
| # | |
| # The cluster is owned by `user` and postgres refuses to run as root. E2B runs | |
| # the start_cmd as root, so we drop to `user` when invoked as root; when a human | |
| # runs it (already `user`), we run directly. | |
| # Used two ways: | |
| # 1. Wired into the sandbox at provision time (e.g. as a wrapper command) so | |
| # the DB is up the moment a sandbox boots — no agent action required. | |
| # 2. By dev-up.sh, for humans in an interactive shell. | |
| # | |
| # The cluster is owned by `user` and postgres refuses to run as root. When | |
| # invoked as root we drop to `user`; when already `user`, we run directly. |
| ENV DATABASE_URL=postgresql://litellm:litellm@localhost:5432/litellm | ||
| ENV LITELLM_MASTER_KEY=sk-1234 | ||
| ENV LITELLM_SALT_KEY=sk-litellm-salt-dev-unsafe | ||
| ENV STORE_MODEL_IN_DB=True |
There was a problem hiding this comment.
Hardcoded dev credentials baked into image ENV layers
The LITELLM_MASTER_KEY and LITELLM_SALT_KEY are set as ENV instructions, making them permanently visible in docker inspect and docker history for any image derived from this snapshot. Because they are baked into a layer rather than injected at runtime, they cannot be removed by the orchestrator at run time — they are always present in the image metadata. The *-dev-unsafe naming signals this is intentional for local dev, but a production snapshot built from this Dockerfile would carry the same well-known values. It is worth explicitly noting in the README that this image must not be used as-is for a production snapshot.
Cut the opencode sandbox MCP over to the platform-side Daytona provider, port
e2b/e2b.Dockerfileto a Daytona snapshot template. Keeps the E2B provider (src/server/sandbox/e2b.ts) intact so other LAP users can still picke2bviaSANDBOX_CHOICE.What changes
1.
harnesses/opencode/sandbox-mcp.mjs— drop the direct-E2B fallbackThe MCP had two modes: platform delegation (when
session_idwas available) and a direct-E2B fallback (nosession_id). The direct branch duplicated SDK logic that already lives insrc/server/sandbox/e2b.tsand was a second place to chase provider quirks (sandbox.kill,setTimeoutkeepalive, file-read formats).Now: every tool routes through the platform endpoint, which picks the configured provider (Daytona/E2B) via
SANDBOX_CHOICE. When nosession_idis available the four tools return a shared error pointing the agent at the<lap_session_id>tag — no silent fallback.e2bis dropped fromharnesses/opencode/package.json.2.
daytona/— new snapshot templatePort of
e2b/:daytona.Dockerfile—FROM ubuntu:22.04(E2B base had python preinstalled; this installs it), explicituseradd user, same postgres +litellm[proxy]+uvpre-bake.build-snapshot.mjs—Image.fromDockerfile+daytona.snapshot.create. Run once:DAYTONA_API_KEY=<key> node daytona/build-snapshot.mjs.README.md— env vars to set after.cloud-vault-ca.crt,start-db.sh,dev-up.sh— copied verbatim frome2b/.Operational follow-up (not in this PR)
DAYTONA_API_KEY=<key>,DAYTONA_IMAGE=ubuntu:22.04(orDAYTONA_SNAPSHOT=litellm-8gbonce built),SANDBOX_CHOICE=daytona.E2B_API_KEY(keeping the E2B provider available for other users either way).