Skip to content

Desktop Electron app: native install, tray, process management #13

@jayzalowitz

Description

@jayzalowitz

Context

SkyTwin is a local-first product — everything runs on the user's machine. But today, launching SkyTwin means opening a terminal and running `pnpm dev`. There's a 427 LOC Electron shell at `apps/desktop/` but it has minimal UI and no process management. For the HN launch, the desktop app is the product: users download an installer, launch, and go.

Claude Code estimate: ~4-6h

Current State (verified 2026-04-04)

  • `apps/desktop/src/main.ts`: Basic Electron app with `ServiceManager` and tray icon (~427 LOC)
  • `bin/skytwin-dev`: Shell script that starts processes with `&` — no restart, no health monitoring
  • `docker-compose.yml`: No restart policies, health checks only for CockroachDB
  • Web dashboard: Fully functional SPA at `apps/web/` — can be loaded in BrowserWindow

Proposed Change

Turn the Electron shell into a full desktop application that manages the entire SkyTwin lifecycle. Initial build targets macOS; Windows and Linux builds in #16.

Process management

  • Start CockroachDB, API, worker, web as supervised child processes
  • Health monitoring every 5s, restart with backoff (1s → 2s → 4s → max 30s)
  • 5 failures in 5 minutes → mark failed, alert user
  • Graceful shutdown: SIGTERM → 5s wait → SIGKILL

System tray

  • Status: green (healthy) / yellow (degraded) / red (failed)
  • Menu: Open Dashboard, Pause Twin, Resume Twin, Settings, About, Quit

Main window

  • BrowserWindow loading localhost (existing web dashboard)
  • Window state persistence via electron-store

Native notifications

Auto-launch

  • Optional login item via `app.setLoginItemSettings()`
  • Default off, configurable in Settings

First-launch flow

  1. Check Node.js + CockroachDB
  2. Guide install if missing (Homebrew on macOS, Chocolatey/Scoop on Windows, apt/snap on Linux)
  3. Run migrations + seed
  4. Start services
  5. Open onboarding wizard

DMG packaging (macOS first)

  • electron-builder, app icon, Info.plist

Acceptance Criteria

  1. Download DMG → drag to Applications → launch → onboarding wizard visible within 10 seconds
  2. System tray icon shows green circle/dot when all 4 child processes (CockroachDB, API, worker, web) report healthy
  3. `kill -9` on API child process → tray icon turns yellow within 5 seconds → API restarts automatically → tray returns to green within 10 seconds total
  4. `kill -9` on API 5 times within 5 minutes → tray turns red → alert dialog shown: "SkyTwin API failed to start — check logs" → process not restarted again
  5. "Pause Twin" from tray menu → worker stops polling (no new signals ingested) → tray menu shows "Resume Twin" instead → "Resume Twin" restarts polling within 2 seconds
  6. Native macOS notification appears within 5 seconds of approval request creation (requires Live notification layer: SSE, approval expiry cron, push alerts #8 SSE) with title "SkyTwin" and approval summary as body
  7. Click native notification → Electron main window focuses → navigates to `/approvals` page
  8. Close main window → reopen from tray "Open Dashboard" → window appears at previous size and position
  9. First launch on clean system without CockroachDB → dialog explains what's needed → provides install command (`brew install cockroachdb`) → "Check Again" button verifies installation
  10. First launch with CockroachDB available → migrations run → seed data loaded → all services start → onboarding wizard opens — total time < 30 seconds
  11. Quit from tray menu → all child processes terminated within 5 seconds → no orphaned processes (`ps aux | grep skytwin` returns nothing)
  12. Enable "Launch at login" in Settings → macOS login items includes SkyTwin → disable removes it
  13. About panel (tray menu → About) shows: version number, uptime, count of connected accounts, current trust tier name, approximate DB size
  14. All 432 existing tests pass
  15. PR passes `/review` before merge

Testing Plan

Layer What Count
Unit ServiceManager: start, stop, restart, crash detection, max restarts +6
Unit Tray state machine: healthy → degraded → failed → healthy +3
Unit Window state persistence: save and restore +2
Integration First-launch: dependency check → migrate → seed → start +2
Integration Notification bridge: mock SSE event → Electron Notification created +2
E2E Full lifecycle: launch → onboard → use → quit → no orphans +1

Effort Estimate

  • Process management + ServiceManager: ~1.5h
  • System tray + menu + state machine: ~0.5h
  • Native notifications bridge: ~0.5h
  • First-launch flow + dependency checks: ~1h
  • DMG packaging + electron-builder: ~1h
  • Window state + About panel: ~0.5h
  • Testing: ~0.5h

Total: ~4-6h Claude Code time

Files Reference

File Change
`apps/desktop/src/main.ts` Expand from shell to full app
`apps/desktop/src/service-manager.ts` New: process lifecycle management
`apps/desktop/src/tray.ts` New: system tray with status + menu
`apps/desktop/src/notifications.ts` New: SSE → native notification bridge
`apps/desktop/src/first-launch.ts` New: dependency check + setup flow
`apps/desktop/src/window-state.ts` New: window position/size persistence
`apps/desktop/electron-builder.yml` New: macOS DMG config
`apps/desktop/package.json` Add electron-builder, electron-store deps

Out of Scope

Related


Working Context Protocol

During implementation, maintain two sources of truth to survive context compaction:

  1. Local context file: Write progress, decisions, and blockers to .context/issue-13-desktop-app.md (gitignored). Update this file after each meaningful step. On compaction, re-read this file to restore state.
  2. GitHub issue: Post progress comments on #13 at key milestones (subtask complete, blocker hit, design decision made). Reference the issue URL in your conversation so it persists across compaction: Desktop Electron app: native install, tray, process management #13

This ensures no quality loss across compaction events — the local file has granular state, the GitHub issue has durable history.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions