plans/cloud-sandboxes.md
Generated by swarm planning session on 2026-02-12
Add a new "Cloud Sandbox" runtime mode to Dyad that executes user-generated apps in Vercel's cloud sandbox environment instead of on the local machine. This is a Dyad Pro feature that removes local toolchain requirements, provides instant cloud-based previews, and enables shareable preview URLs. Initially supports Vercel Sandbox SDK, with a provider-agnostic interface to support additional providers later.
Today, Dyad users must run generated apps on their local machine (host mode) or inside Docker containers. Both modes require local compute resources, proper toolchain setup (Node.js, pnpm, Docker Desktop), and can encounter platform-specific issues (port conflicts, PATH issues, Windows file locks). For new users, "getting the app to run" is a significant friction point before they can even evaluate whether the AI-generated code is correct. For advanced users, local execution limits collaboration -- you can't easily share a running preview with a teammate or client.
Cloud sandboxes solve this by offloading execution to a remote, pre-configured environment. The user's machine becomes a thin client: they send code, the cloud runs it, and the preview appears in the same iframe. This removes setup friction, improves reliability, and enables shareable preview URLs.
"cloud" to RuntimeMode2Schema enumexecuteAppInCloud() function using Vercel Sandbox SDK for sandbox creation, file upload, and log streamingstart_proxy_server.ts) for script injection (component selector, visual editing, console capture)/dashboard), NOT the proxy localhost URL. "Copy shareable link" and "Open in browser" buttons both use the direct sandbox URL.<Select> already uses proper form semantics — adding a new option maintains keyboard accessibility.aria-live regions for screen reader announcements.aria-label ("Running in cloud sandbox").User (Electron App)
|
v
RuntimeModeSelector (Settings)
| settings.runtimeMode2 = "host" | "docker" | "cloud"
v
executeApp() decision point (app_handlers.ts)
|
├── "host" → executeAppLocalNode() [existing]
├── "docker" → executeAppInDocker() [existing]
└── "cloud" → executeAppInCloud() [NEW]
|
v
Dyad Backend API
| (authenticates Pro user, manages Vercel credentials)
v
Vercel Sandbox SDK
| (create sandbox, upload files, stream logs)
v
Cloud Sandbox Running
| (preview URL: https://<id>.vercel.app)
v
Local Proxy Server (start_proxy_server.ts)
| (injects Dyad scripts, serves to iframe)
v
PreviewIframe (proxied URL: http://localhost:5XXXX)
Two-URL model:
appUrl (proxied): http://localhost:5XXXX — used in iframe, has injected scripts for Pro featuresoriginalUrl (direct): https://<sandbox-id>.vercel.app — shareable, used by "Copy link" and "Open in browser"This maps cleanly to the existing appUrlAtom which already stores both appUrl and originalUrl.
src/lib/schemas.ts — Add "cloud" to RuntimeMode2Schema enumsrc/ipc/handlers/app_handlers.ts — New executeAppInCloud() function, cloud branch in executeApp(), file sync hook in editAppFilesrc/ipc/utils/process_manager.ts — Generalize RunningAppInfo:
interface RunningAppInfo {
process: ChildProcess | null; // null for cloud
processId: number;
mode: "host" | "docker" | "cloud"; // replaces isDocker boolean
containerName?: string; // docker only
cloudSandboxId?: string; // cloud only
cloudPreviewUrl?: string; // cloud only
}
mode string union instead of boolean flags (isDocker, isCloud) for cleaner branching.src/ipc/utils/start_proxy_server.ts — Support proxying to remote HTTPS URLs (currently targets localhost only). Handle SSL, inject Dyad scripts into response HTML.src/components/RuntimeModeSelector.tsx — Add "Cloud Sandbox (Pro)" option. Pro gate with upgrade prompt for free users.src/hooks/useRunApp.ts — Handle cloud preview URL delivery. Emit synthetic app:output with [dyad-proxy-server] format for consistency.src/components/preview_panel/PreviewIframe.tsx — Cloud icon/badge in toolbar. "Copy shareable link" button using originalUrl. "Open in browser" uses direct URL. Conditional UI based on mode.src/components/preview_panel/PreviewPanel.tsx — Cloud-specific loading states, error messages.src/ipc/utils/cloud_sandbox_provider.ts — Provider interface and Vercel implementation:
interface CloudSandboxProvider {
name: string;
createSandbox(
appPath: string,
appId: number,
): Promise<{ sandboxId: string; previewUrl: string }>;
destroySandbox(sandboxId: string): Promise<void>;
streamLogs(sandboxId: string): AsyncIterable<string>;
uploadFiles(sandboxId: string, files: FileMap): Promise<void>;
}
MVP: No database schema changes. Sandbox state is stored in-memory in the runningApps Map. Sandboxes are ephemeral and don't survive Dyad restarts. On restart, the user simply re-runs the app (creates a new sandbox).
Settings: No new settings fields needed for MVP. The existing runtimeMode2 field gains a new enum value. Pro subscription state is already managed separately.
Future (if sandboxes become long-lived): Add cloudSandboxId and cloudSandboxUrl columns to the apps table for persistence and startup reconciliation.
No new IPC contracts needed for MVP. The existing runApp/stopApp/restartApp contracts work as-is. executeApp() routes internally to the cloud implementation.
New Dyad Engine API endpoints:
POST /api/sandboxes — Create a sandbox (requires Pro auth)DELETE /api/sandboxes/:id — Destroy a sandboxGET /api/sandboxes/:id/status — Check sandbox statusPOST /api/sandboxes/:id/files — Upload files to sandboxGET /api/sandboxes/:id/logs — Stream logs (SSE or WebSocket)Internal provider interface: CloudSandboxProvider as described above, with a Vercel implementation.
"cloud" to RuntimeMode2Schema in schemas.tsRuntimeModeSelector.tsx with "Cloud Sandbox (Pro)" option and Pro gateRunningAppInfo to use mode: "host" | "docker" | "cloud" instead of isDocker booleanstart_proxy_server.ts to support proxying remote HTTPS URLs with script injectionCloudSandboxProvider interface in cloud_sandbox_provider.tsexecuteAppInCloud() in app_handlers.ts: create sandbox, upload files, stream logs, wire preview URLstopAppByInfo() for sandbox teardownapp:output for consistency with existing floweditAppFile to sync changes to cloud sandbox when in cloud modePreviewIframe.tsx toolbar (uses originalUrl)runningApps, destroy cloud entries)CloudSandboxProvider. Test executeApp() routing, stopAppByInfo() cloud branch, file sync triggering, batched upload logic.IS_TEST_BUILD pattern that stubs Vercel API to localhost:3500). Test full flow: select cloud mode → run app → see preview → copy link → stop app.editAppFile, verify upload call). Test sandbox lifecycle (create, restart, stop). Test proxy with remote HTTPS target.| Risk | Likelihood | Impact | Mitigation |
|---|---|---|---|
| Dyad Engine dependency — if Engine is down, cloud sandboxes are broken | Medium | High | Monitoring, clear error messages ("Dyad cloud services temporarily unavailable"), fallback to local mode suggestion |
| Orphaned sandboxes consuming Dyad's cloud resources | High | High | Auto-hibernate after 15 min, max 1 concurrent sandbox, startup reconciliation, graceful teardown on quit |
| Proxy server complexity increases (HTTPS remote target, script injection) | Medium | Medium | Incremental upgrade to existing proxy, thorough testing of script injection with remote content |
| User confusion between cloud sandbox (preview) and Vercel Deploy (production) | Medium | Medium | Clear labeling in UI, note in Publish panel, distinct icons |
| File sync failures leave preview showing stale content | Medium | Medium | Surface sync failures as app:output stderr messages, "Fix with AI" flow still works |
| Pro subscription expires during active sandbox | Low | Medium | Graceful teardown with clear message, fall back to local mode |
| Vercel Sandbox SDK rate limits or API changes | Low | High | Abstract behind provider interface, monitor API usage, maintain relationship with Vercel |
DATABASE_URL, Supabase keys, etc. work in cloud mode? Deferred to follow-up, but needs a clear user-facing message in v1 ("Environment variables may not be available in cloud sandbox mode").restartApp with removeNodeModules: What's the cloud equivalent? Destroy and recreate vs. re-upload to existing sandbox? Needs decision during Phase 5.| Decision | Reasoning |
|---|---|
| Vercel Sandbox SDK as the first cloud provider | Real-time file sync and instant rebuilds. Existing Vercel relationship. SDK purpose-built for interactive development. |
| Proxy through localhost for iframe preview | Preserves all Pro features (component selector, visual editing, console capture) via script injection. Trade-off: proxied URL isn't directly shareable, but "Copy link" button provides the direct URL. |
| Dyad Pro feature (Dyad manages infrastructure) | Simplifies UX (no Vercel account setup needed), controls costs, creates Pro upsell opportunity. Requires Dyad Engine for sandbox management. |
| Shareable URLs in MVP | Highest-value, lowest-effort differentiator of cloud mode. "Copy link" button is ~10 lines of code. |
| Global runtime mode for v1 | Consistent with existing Docker mode. Simpler data model and UI. Per-app override deferred to v2. |
| In-memory sandbox state for MVP | Sandboxes are ephemeral. No DB migration needed. Revisit if sandboxes need to survive restarts. |
mode string union over boolean flags | mode: "host" | "docker" | "cloud" is cleaner than isDocker + isCloud boolean flags. Easier to extend. |
| Cloud mode is opt-in, not default | Local mode remains default for all users including Pro. Cloud mode is slower for iteration and requires internet. |
| Batch file sync per AI turn | Avoids thundering herd of 5-20 individual uploads during multi-file AI responses. Reduces API calls and prevents mid-batch rebuilds. |
| Auto-hibernate after 15 min | Prevents orphaned sandboxes from running up Dyad's cloud costs. Balance between convenience and cost control. |
Generated by dyad:swarm-to-plan