Back to Dyad

Multi-Platform Dyad: Web + Mobile Roadmap

plans/multi-platform-web-and-mobile.md

0.44.028.6 KB
Original Source

Multi-Platform Dyad: Web + Mobile Roadmap

Generated by swarm planning session on 2026-02-14

Summary

Turn Dyad from a desktop-only Electron app into a multi-platform product supporting web browsers and mobile devices (via PWA), while preserving the desktop app as the free, local, open-source flagship. The web version runs on server-side containers with a freemium business model. Data syncs between platforms via GitHub (code) and proprietary cloud sync (settings, chat history, metadata).

Problem Statement

Dyad is currently limited to users who download and install a desktop app on macOS, Windows, or Linux. This creates three problems:

  1. High friction onboarding -- competitors like v0, Bolt, Lovable, and Replit are browser-first, letting users try instantly from a link. Dyad's installation requirement kills conversion before users experience the product.
  2. No cross-device access -- users can only interact with their projects from the machine where Dyad is installed. There's no way to check project status, send a quick prompt, or review changes from a phone or another computer.
  3. Limited viral distribution -- browser-based tools can be shared via URL. Desktop apps require "download this app, install it, then try it." Every step loses users.

Target users:

  • Web: Same developer/non-developer persona as desktop, but reached through lower-friction channels (links, embeds, social sharing)
  • Mobile (PWA): Existing Dyad users who want lightweight access away from their desk -- reviewing, monitoring, quick chat interactions

Scope

In Scope (MVP -- Phase 0 + Phase 1)

  • Backend service extraction from Electron main process into standalone Node.js server
  • Server-side container infrastructure for live app preview (via E2B or Fly Machines)
  • Web client with core chat + build + preview + deploy loop
  • User authentication (GitHub OAuth, extending Dyad Pro infrastructure)
  • Freemium tier (limited projects/compute on free, expanded on Pro)
  • Responsive layout foundations (preparing for PWA mobile)
  • Server-side database (better-sqlite3 or PostgreSQL with same Drizzle schema)
  • Cloud-side secret storage for API keys (replacing Electron safeStorage)
  • Basic usage tracking and rate limiting

In Scope (Follow-up Phases)

  • Full handler parity (all 40+ IPC handler categories via HTTP API)
  • PWA mobile optimization (bottom tab nav, touch targets, service worker)
  • GitHub-based code sync between desktop and web
  • Proprietary cloud sync for settings, chat history, app metadata
  • Billing/payment integration for Pro tier
  • Offline messaging for PWA ("you're offline" states)

Out of Scope

  • Native mobile apps (iOS/Android via Capacitor) -- only if PWA hits hard walls
  • Real-time collaboration / multi-user editing
  • Desktop-to-web migration of local files (GitHub is the bridge)
  • Local model support on web (Ollama/LM Studio -- desktop only)
  • MCP server support on web (requires local process spawning -- desktop only)
  • Capacitor/Xcode/Android Studio integration on web (desktop only)

User Stories

Web (Phase 1)

  • As a new user, I want to try Dyad in my browser without installing anything, so I can evaluate it in under 3 minutes
  • As a web user, I want to chat with the AI and see my generated app running in a live preview, so I get the same instant feedback loop as desktop
  • As a web user, I want to deploy my app to Vercel from the browser, so I can ship without switching to desktop
  • As a web user, I want to connect my own API keys (OpenAI, Anthropic, etc.), so I maintain the BYOK philosophy
  • As a free-tier user, I want to see my usage limits clearly before I hit them, so I'm not surprised by a paywall mid-workflow
  • As a free-tier user who hits limits, I want to be offered both "Upgrade to Pro" and "Download desktop (free, unlimited)" as options

Mobile/PWA (Phase 3)

  • As an existing Dyad user on my phone, I want to check my project status and recent chat history, so I can stay informed away from my desk
  • As a mobile user, I want to send a quick prompt to my AI agent, so I can kick off work while commuting
  • As a mobile user, I want to review generated code diffs and approve changes, so I don't block progress when away from desktop

Sync (Phase 4)

  • As a user with both desktop and web, I want my code synced via GitHub so I can work on either platform
  • As a user, I want my settings, chat history, and preferences to sync across devices, so I don't have to reconfigure on each platform

UX Design

Platform-Specific Use Cases

PlatformPrimary Use CaseLayout
DesktopFull creation (chat, code, preview, deploy, git, local models)Three-panel: sidebar + chat + preview
WebFull creation (cloud-backed, same loop minus local-only features)Three-panel: sidebar + chat + preview (no custom titlebar)
Mobile/PWAMonitoring, reviewing, lightweight chatSingle-panel with bottom tab navigation

User Flow (Web MVP)

  1. User visits dyad.sh/app (or similar)
  2. Sees landing page with "Try now" CTA -- no signup required for first experience
  3. After 5 minutes or first meaningful action, prompted for GitHub OAuth signup
  4. Creates or opens a project -> enters chat view
  5. Sends prompt to AI -> streaming response with code generation
  6. Server-side container spins up (5-15s, "Starting environment..." indicator)
  7. Live preview appears in side panel once container is ready
  8. User iterates via chat -> code updates -> preview hot-reloads
  9. User deploys to Vercel via existing integration
  10. Usage tracked against free tier limits (visible in sidebar)

Key States

  • Container starting: Progress indicator in preview panel ("Starting environment...") -- honest about 5-15s cold start
  • Container active: Live preview iframe connected to remote container
  • Container disconnected: "Preview disconnected -- Reconnecting..." overlay (not blank white screen)
  • Offline (PWA): Cached project list and chat history (read-only) with "You're offline. Connect to continue building." banner
  • Free tier limit approaching: Usage indicator in sidebar (similar to existing AICreditStatus)
  • Free tier limit reached: Dual-CTA dialog: "Upgrade to Pro" or "Download desktop (free, unlimited)"
  • Build error (remote): Same DyadOutput/DyadLogs components with "Cloud Build" label to indicate remote origin

Interaction Details

InteractionDesktopWebMobile/PWA
NavigationSidebar + keyboard shortcutsSidebar (collapsible)Bottom tab bar (< 768px)
File attachmentDrag-drop + native dialogDrag-drop + <input file>Camera/photo library/file picker
Context menusRight-clickRight-clickLong-press
Code editingMonaco editorMonaco editorRead-only code viewer
Panel layoutResizable multi-panelResizable multi-panelSingle panel, swipe/tab to switch
PreviewLocal dev server in side panelRemote container in side panelFull-screen with toggle button
Window controlsCustom titlebar (macOS/Windows)None (browser chrome)None (OS chrome)
SettingsLocal file pickers, node path selectorCloud storage, account management, billingDrill-down navigation pattern

Accessibility

  • Touch targets: minimum 44x44pt on mobile breakpoints (current h-7/h-8 buttons upscaled via responsive classes)
  • Safe areas: env(safe-area-inset-*) for mobile notches/home indicators
  • Motion sensitivity: Audit Framer Motion animations for prefers-reduced-motion
  • Screen readers: Base UI ARIA support carries over; audit hidden radio input pattern
  • Keyboard navigation: Standard browser Tab/Enter/Escape on web (no custom interception)

Technical Design

Architecture

Option A: Backend Service Extraction (selected)

Extract the Electron main process business logic into a standalone Node.js server. The existing IPC contracts (Zod schemas) become HTTP/WebSocket endpoints. Desktop Electron becomes a thin shell that spawns the local server. Web and mobile clients talk to the same API over HTTP.

Desktop:  Electron shell -> Local Node.js server -> Local SQLite + Local filesystem
Web/PWA:  Browser        -> Cloud Node.js server -> Cloud DB + Container filesystem

Why this approach:

  • IPC contracts map mechanically to HTTP routes (channel name -> route, Zod input -> request body, Zod output -> response body)
  • Stream contracts map to SSE/WebSocket
  • React frontend has zero direct Electron references in components -- it's already web-ready
  • Single codebase with PlatformProvider context for capability-based conditional rendering
  • createClient() in src/ipc/contracts/core.ts is the single function to swap (IPC invoke -> HTTP fetch)

Components Affected

Must Change (Infrastructure):

ComponentCurrentTarget
src/ipc/handlers/*.ts (40+ files)ipcMain.handle()Extract pure service functions; thin handler wrappers for both IPC and HTTP
src/ipc/contracts/core.tswindow.electron.ipcRenderer.invoke()HTTP fetch adapter for web, keep IPC for desktop
src/preload.tsElectron contextBridgeHTTP client for web
src/db/index.tsbetter-sqlite3 (local)Same for desktop; server-side DB for web
src/main/settings.tsFile JSON + safeStoragePlatform-agnostic SettingsProvider (safeStorage for desktop, encrypted cloud storage for web)
src/paths/paths.tselectron.app.getPath()Abstract path provider
src/main.tsFull Electron app lifecycleThin Electron shell; business logic extracted
src/ipc/utils/git_utils.tsdugite + isomorphic-gitisomorphic-git for web; keep dugite for desktop
src/ipc/utils/simpleSpawn.tschild_process.spawn()Remote container execution for web
electron-log (all handlers)Electron-specific loggerPlatform-agnostic logger (pino)

Unchanged (Portable):

  • src/components/** -- React UI (zero Electron refs)
  • src/atoms/** -- Jotai state
  • src/pages/**, src/routes/** -- TanStack Router
  • src/db/schema.ts -- Drizzle ORM schema (fully portable)
  • src/ipc/types/** -- Zod schemas (pure validation)

Data Model Changes

Schema changes:

  • Add userId column to: apps, chats, messages, versions, prompts, themes, languageModels tables (for multi-tenant web)
  • Add WHERE userId = ? to all queries in multi-tenant mode
  • These are standard Drizzle migrations (drizzle-kit generate)
  • Desktop: userId is always a fixed local-user ID (no behavior change)

Database driver strategy:

  • Desktop: better-sqlite3 (keep current, fast, synchronous)
  • Web server: better-sqlite3 per-user (simple) or PostgreSQL (scalable) -- same Drizzle schema either way
  • Mobile/PWA: No local DB -- all data from web server API

Settings storage:

  • Desktop: File JSON + safeStorage encryption (unchanged)
  • Web: Server-side encrypted storage for API keys (AES-256-GCM with server key or secrets manager); non-sensitive settings in DB
  • PWA: No local settings -- use web server

File storage:

  • Desktop: Local filesystem (~/dyad-apps/) -- unchanged
  • Web: Container filesystem (ephemeral per session) + persistent cloud storage per project (S3/GCS or container volumes)

API Changes

New HTTP/WebSocket API layer:

IPC contracts transform mechanically:

IPC:  ipcMain.handle("create-app", handler)  ->  HTTP:  POST /api/create-app
IPC:  ipcRenderer.invoke("create-app", data)  ->  HTTP:  fetch("/api/create-app", {body: data})

Stream contracts transform to SSE:

StreamContract: onChunk/onEnd/onError  ->  SSE: event: chunk/end/error

New endpoints (web-specific):

  • POST /api/auth/login -- GitHub OAuth flow
  • POST /api/auth/session -- Session management
  • POST /api/containers/create -- Provision container for project
  • DELETE /api/containers/:id -- Teardown container
  • GET /api/containers/:id/proxy/* -- Proxy to container dev server
  • POST /api/sync/upload -- Upload local DB data to cloud
  • GET /api/sync/download -- Download cloud data to local
  • GET /api/usage -- Current usage vs. tier limits

Authentication middleware:

  • All API endpoints require auth (except initial anonymous trial)
  • Session-based (JWT or secure cookie)
  • Built on existing Dyad Pro infrastructure

Container Architecture

Platform recommendation: Start with E2B (purpose-built AI code execution sandboxes)

Container lifecycle:

  1. User opens/creates project -> ContainerManager.provision(projectId, userId)
  2. Container initializes with project files from cloud storage
  3. Runs npm install && npm run dev inside container
  4. Preview URL proxied to browser via /api/containers/:id/proxy/*
  5. Container idle timeout: 15 min (free) / 60 min (Pro)
  6. Container shutdown: files persisted to cloud storage, container destroyed
  7. Next session: new container, files restored from cloud storage

Cost controls:

  • Free tier: 1 concurrent container, 30 min/day active time
  • Pro tier: 3+ concurrent containers, extended active time
  • Container auto-sleep after idle timeout
  • Resource limits per container (CPU, memory, disk)

Sync Architecture

Code sync (GitHub -- already built):

  • Existing github_handlers.ts with push/pull/clone/fetch/rebase
  • apps table already stores githubOrg, githubRepo, githubBranch
  • Desktop user pushes to GitHub; web user clones from GitHub
  • No new infrastructure needed

Everything-else sync (new proprietary service):

  • Simple export/import for MVP: user clicks "Sync to cloud" on desktop, uploads relevant table data via API
  • Tables synced: apps (metadata only, not path), chats, messages, prompts, themes, languageModels, settings
  • Conflict resolution: last-write-wins for settings/preferences (with "Settings updated from your other device" toast); explicit conflict picker for project metadata
  • Lazy-load old chat histories (only sync recent chats; messages.aiMessagesJson can be large)

Implementation Plan

Phase 0: Service Extraction (Foundation)

Extract business logic from Electron main process into pure, testable service functions. No abstract interfaces -- just separation of concerns.

  • Extract service functions from the 5 most critical handler files: chat_stream_handlers.ts, chat_handlers.ts, app_handlers.ts, github_handlers.ts, settings_handlers.ts
  • Replace electron-log imports with platform-agnostic logger (pino) across all handler files
  • Create PlatformProvider context and expand useSystemPlatform hook to detect desktop/web/mobile
  • Ensure all existing desktop tests still pass (zero user-facing changes)

Phase 1: Web MVP with Server-Side Containers

Build the web version with the full chat + build + preview + deploy experience.

Backend:

  • Create standalone Node.js HTTP server (Hono/Express) with routes generated from IPC contracts
  • Implement HTTP client adapter to replace ipcRenderer.invoke() for web
  • Set up server-side database (better-sqlite3 or PostgreSQL with Drizzle)
  • Add userId columns to all user-facing tables; add tenant isolation middleware
  • Implement authentication (GitHub OAuth, session management, extending Dyad Pro)
  • Replace safeStorage with server-side encrypted secret storage for API keys
  • Implement cloud file storage for projects (S3/GCS)
  • Set up E2B (or Fly Machines) container infrastructure
  • Build ContainerManager: provision, proxy, idle timeout, shutdown, file persistence
  • Implement streaming chat via SSE/WebSocket
  • Add usage tracking middleware and rate limiting
  • Security hardening: container sandboxing, input validation, resource limits

Frontend:

  • Remove custom titlebar on web (relocate chat tabs, title bar actions)
  • Replace Electron file dialogs with web <input type="file">
  • Replace shell.openExternal() with window.open()
  • Add login/signup flow (GitHub OAuth)
  • Add container status widget in preview panel ("Starting environment..." indicator)
  • Add "Preview disconnected -- Reconnecting..." overlay
  • Add freemium usage indicator in sidebar
  • Add free tier limit dialog with dual CTA (upgrade / download desktop)
  • Web-compatible onboarding flow (replace YouTube link with interactive walkthrough)
  • Error messaging adapted for remote builds ("Cloud Build" label on DyadOutput)

Phase 2: Backend Convergence + Full Handler Parity

Unify desktop and web backends to prevent codebase divergence.

  • Port remaining 35+ IPC handler categories to HTTP endpoints
  • Desktop Electron app spawns local Node.js server (same server as web, running locally)
  • Unified feature development: new features work on both desktop and web from a single implementation
  • Audit isomorphic-git coverage vs. dugite -- implement web-compatible git for all critical operations
  • Unify Dyad Pro subscription: one account, one Pro status, works on desktop + web
  • Billing/payment integration (Stripe or similar)
  • Feature flags for platform-specific capabilities (hasFilesystem, hasShell, hasLocalModels, etc.)

Phase 3: PWA Mobile Optimization

Make the web version excellent on mobile devices.

  • Responsive layout: sidebar collapses to bottom tab bar at < 768px viewport
  • Touch target audit: all interactive elements >= 44x44pt on mobile breakpoints
  • Safe area support: env(safe-area-inset-*) for notches/home indicators
  • Service worker for PWA installability and offline shell
  • Web app manifest for install-to-homescreen
  • Mobile-specific input adaptations: camera/photo picker for file attachments, virtual keyboard handling
  • Offline state: cached project list + chat history (read-only) with offline banner
  • Chat/preview as swipeable tabs instead of side-by-side panels on mobile
  • Code viewer (read-only) replacing Monaco editor on mobile
  • Long-press for context menus (replacing right-click)

Phase 4: Cross-Device Sync

Enable seamless work across desktop and web.

  • GitHub code sync: prominent "Push to GitHub" / "Pull from GitHub" UI flows
  • Sync API: export/import for chat history, messages, prompts, themes, settings, language model configs
  • Sync status indicator in sidebar footer (checkmark/spinner/warning)
  • Conflict resolution: last-write-wins for settings (with toast notification), explicit picker for project metadata
  • First-time sync setup flow: "Sign in -> Choose apps to sync -> Done"
  • Lazy-load old chat histories during sync (skip large aiMessagesJson for old chats)
  • Desktop "Connect to cloud" opt-in flow

Phase 5: Polish + Advanced Features

  • Audit and optimize container cold start times
  • Advanced offline support (queued operations that sync when back online)
  • Evaluate native mobile (Capacitor) if PWA hits hard walls (push notifications, performance)
  • Collaboration features (share project link, real-time co-editing)
  • Advanced sync (CRDT-based or operational transforms if last-write-wins proves insufficient)

Testing Strategy

  • Unit tests: Extract service functions are tested independently with mocked platform APIs (Vitest, existing test infrastructure)
  • Integration tests: HTTP API layer tested with supertest -- verify Zod contract compatibility between IPC and HTTP transports
  • E2E tests (Desktop): Keep existing Playwright Electron tests; verify Electron shell + extracted backend still works
  • E2E tests (Web): Standard Playwright browser tests against the web app; cover auth flow, chat+preview loop, deploy, freemium limits
  • Container tests: Integration tests for container provisioning, preview proxying, idle timeout, crash recovery
  • Sync tests: Round-trip tests for GitHub code sync and proprietary data sync; conflict resolution scenarios
  • Responsive tests: Playwright at mobile viewport sizes for PWA breakpoints, touch target sizes
  • Contract tests: Automated verification that IPC contracts and HTTP API contracts stay in sync (they share Zod schemas)
  • Security tests: Container escape testing, API key storage encryption verification, tenant isolation verification

Risks & Mitigations

RiskLikelihoodImpactMitigation
Container infrastructure complexity delays Phase 1HHStart with managed platform (E2B) to minimize ops; self-host only at scale
Container costs exceed freemium revenueMHAggressive idle timeouts, free tier time limits, monitor cost-per-user closely
Two-codebase divergence (desktop vs. web backend)HMTimebox: Phase 2 convergence must start within 6 months of web MVP launch
User trust concerns storing API keys in cloudMHClear trust indicators in UI, key rotation support, consider browser-side key storage with proxy pattern
isomorphic-git missing critical git features for webMMAudit git usage early; fall back to server-side git proxy for unsupported operations
Freemium conversion < 5% makes web unsustainableMHTrack conversion from day one; adjust free tier limits; desktop download as fallback CTA
Container security (arbitrary npm install, user code execution)MHUse gVisor/Firecracker sandboxing; network isolation; resource limits; security audit before public launch
Sync conflicts cause data lossLHLast-write-wins with confirmation toast for low-stakes data; explicit conflict UI for high-stakes data; never auto-delete
PWA limitations frustrate mobile usersMLMonitor PWA capability gaps; Capacitor wrapping is the escape hatch if needed
Desktop development slows due to web investmentMMRun as parallel tracks; shared component library (Storybook already set up); convergence in Phase 2

Open Questions

  1. Container platform selection: E2B vs. Fly Machines vs. self-hosted. Recommend E2B proof-of-concept spike as first action to validate latency, cost, and reliability.
  2. Exact freemium tier limits: How many free projects? How much active container time per day? What storage limits? These directly control infrastructure burn rate and need data from the E2B spike to set.
  3. Web version URL/branding: Is it dyad.sh/app? app.dyad.sh? A separate domain? Affects auth cookies, CORS, and SEO.
  4. Desktop-only feature messaging: When a web user encounters a feature that only works on desktop (local models, MCP servers, Capacitor builds), what's the UX? Banner? Tooltip? Redirect to download?
  5. Existing DYAD_ENGINE_URL pattern: The codebase already has a remote engine URL concept. How much of the backend extraction has already been contemplated or partially built? This could accelerate Phase 0-1 significantly.

Decision Log

DecisionOptions ConsideredChosenReasoning
Web preview approachWebContainers, server-side containers, deploy-only (no preview)Server-side containersPreserves Dyad's core value prop (instant preview). WebContainers is Chromium-only and can't run shell commands. Deploy-only is too degraded.
Business modelFree web, paid-only web, freemiumFreemiumDesktop stays free/local (preserves open-source identity). Web Pro is the upsell. Freemium maximizes reach while generating revenue to cover infrastructure.
Mobile approachNative (Capacitor), PWA, defer entirelyPWA firstLowest cost, instant updates, no App Store friction. Mobile use case is lightweight (review, chat) which PWA handles well. Capacitor is the escape hatch if needed.
Data sync modelGitHub only, proprietary sync only, hybrid, no syncHybrid (GitHub for code + proprietary for everything else)GitHub for code leverages existing infrastructure and aligns with "Bridge, Don't Replace." Proprietary sync for settings/metadata fills the gap GitHub can't cover.
Backend architectureCloud-only, local-only-everywhere, backend extraction (Option A)Backend extractionPreserves local-first for desktop. Same business logic serves both local (Electron) and cloud (web). IPC contracts map mechanically to HTTP.
Phase orderingAbstraction first, web backend first, simultaneousService extraction first, then web backendPM argued abstraction-first is premature. Eng agreed to drop TransportAdapter but keep service extraction. Build working web backend, then converge.
Database for websql.js (WASM in browser), server-side SQLite, PostgreSQLServer-side DBWeb version is inherently cloud-based. No reason to run SQLite in browser when there's a server. Same Drizzle schema works with any driver.
Single vs. separate frontendsSingle React codebase, separate frontends per platformSingle codebaseFrontend has zero direct Electron references. Platform differences handled via PlatformProvider context and responsive CSS. Separate frontends would diverge immediately.

Generated by dyad:swarm-to-plan