Seiten · Health Debug
Phase 6 — Electron Desktop Client Plan (Windows, Linux)
Project: Health Debug (healthdebug.com) Phase: 6 of 6 (final client phase in the execution sequence) Targets: Windows, Linux (macOS desktop is covered by the native Swift suite in Phase 4 and is explicitly out of scope here) Status: Planning only — no implementation. UI design is being finalized separately; every visual decision that depends on it is flagged PENDING DESIGN.
Purpose
This document plans the Electron desktop client for Health Debug's primary audience — desk-bound professionals (programmers, engineers) who live in IDEs and terminals on Windows and Linux workstations. The desktop client's job is to put the Protocol Engines at the desk: a system-tray Pomodoro countdown, an optional Workstation Lock that enforces Kinetic Pomodoro breaks, and global hotkeys for rapid logging (e.g., water) without leaving the IDE. Like every Health Debug client, it is a presentation and interaction layer only: it never computes or hardcodes protocol business logic locally. All temporal and state logic (cooldowns, windows, break validity) lives in the central ToGO REST API, the Single Source of Truth (SSOT).
1. Scope
In scope (Phase 6)
| Capability | Description |
|---|---|
| Electron + React shell | Desktop app whose renderer reuses the React + TanStack component library built for the Phase 2 web dashboard |
| System tray / notification-area icon | Live Kinetic Pomodoro countdown + quick-action menu |
| Workstation Lock / Break Enforcer | Optional modal window covering workspace screens during Kinetic Pomodoro breaks |
| Global hotkeys | Rapid logging without leaving the IDE (e.g., Ctrl+Alt+W for water) |
| Secure token storage | TOGO auth tokens persisted via OS credential vaults (Keytar / native keychains) |
| Theme engine | Follows the OS theme with manual override between Dark Cybernetic (#1A1A1A) and Light Clinical (#FFFFFF) |
| i18n | Full RTL support for Arabic (mirrored layout and progress bars), ICU Message Format, no string concatenation |
Out of scope (Phase 6)
- macOS / iOS / watchOS / CarPlay / Widgets — native Swift (Phase 4).
- Android / WearOS / Android Auto / ChromeOS — native Kotlin (Phase 5).
- Chrome Extension (Phase 3).
- Any on-device HealthKit / Health Connect reading — those integrations belong to the mobile clients. The desktop client only displays wearable-derived state (e.g., "movement detected → break registered") that the backend already holds.
- Doctor & B2B dashboards, gamification badges, marketplace APIs (future state, not this phase).
- Any protocol computation on the client (architectural rule — see §3).
2. Relationship to Other Phases
- Phase 1 (ToGO backend) owns every rule the desktop app surfaces: hydration units/limits/cooldown, caffeine block, GERD window, trigger families, medication grace, cycle engine, contraceptive security, Kinetic Pomodoro break validation. The desktop client consumes the REST contract defined there.
- Phase 2 (React web dashboard) produces the shared component library, theme engine tokens, and i18n/ICU message catalogs that this client reuses. Phase 6 must not fork these — it consumes them as shared packages (see §4).
- Phase 4/5 (wearable clients) are the source of the movement signals that make a Kinetic Pomodoro break "count". The Electron client never talks to wearables directly; it only reflects the break state the ToGO API reports.
Dependency direction: Phase 6 depends on Phases 1 and 2 being stable. It is intentionally last in the execution sequence.
3. Architectural Rules (Non-Negotiable)
- SSOT: The ToGO REST API is the single source of truth for all temporal and state logic. The Electron client never decides whether a water log is inside the 30-second cooldown, whether the GERD window is active, or whether a Pomodoro break "counted". It submits events and renders the state the server returns.
- Categorical protocol: All food/input evaluation is categorical ('Safe' vs 'Trigger-bearing'), computed server-side. The desktop UI presents categories; it never computes quantities or runs quantitative deduction.
- No invented medical certainty: Any AI-derived content shown on desktop is behavioral pattern analysis only, always deferring to "Consult your doctor." The desktop client renders this output verbatim from the server; it adds no interpretation.
- BYOK AI: All AI calls are proxied server-side. No AI provider API keys ever reach the Electron client — not in config, not in memory, not in the renderer, not in the main process.
- Native-only client policy: Electron is the sanctioned desktop shell for Windows and Linux specifically; it is not a hybrid replacement for the Apple/Google native suites.
Countdown display vs. countdown authority
The tray shows a live countdown (ticking every second). To reconcile "live" with "no local temporal logic": the server is authoritative for the timer's start time, duration, and phase; the client performs display-only interpolation between server syncs (rendering elapsed wall-clock time against server-provided anchors). Any decision — session complete, break valid, break registered — comes exclusively from the API. If the client's interpolated display and the server state disagree (sleep/resume, clock drift), the server state wins and the display re-anchors on the next sync.
Sync transport (polling interval vs. WebSocket/SSE push) is not defined in the current spec and must be settled with the Phase 1 API contract. See Open Questions.
4. Code Sharing with the Phase 2 Web Dashboard
The renderer is a React + TanStack application drawing from the same component library as the web dashboard.
Proposed monorepo layout (illustrative planning artifact; final layout is decided when Phase 2 is scaffolded):
Sharing rules:
packages/uicomponents must be platform-agnostic: nowindow.electron, no Node imports, no Electron API references. Desktop-only surfaces (tray menu, break overlay chrome) live inapps/desktop/src/renderer, composed from shared components.packages/api-clientis a transport layer only — request/response types and fetch wiring. It must contain zero protocol rules (no cooldown math, no window calculations), preserving the SSOT rule for both web and desktop.- Divergence policy: if desktop needs a variant of a shared component, it wraps/composes rather than forks. PENDING DESIGN: which components have desktop-specific variants (compact tray popover, overlay typography, etc.) depends on the finalized Figma designs.
5. Process Model
Windows:
| Window | Purpose | Notes |
|---|---|---|
| Main window | Full dashboard experience (shared React app) | Closable to tray; app keeps running in tray. PENDING DESIGN: default size, min size, layout |
| Tray popover / menu | Live countdown + quick actions | See §6 |
| Break Enforcer overlay | Full-screen modal during Kinetic Pomodoro breaks | One overlay per connected display ("covering workspace screens"); see §7 |
6. System Tray / Notification-Area Icon
Behavior
- Persistent tray icon on Windows (notification area) and Linux (system tray; see platform caveats below).
- Live Pomodoro countdown: the icon and/or its tooltip/menu reflect the current Kinetic Pomodoro state — focus running, break pending, break in progress, idle. Rendering approach (badge text on icon vs. tooltip vs. menu line vs. dynamically drawn icon frames) is PENDING DESIGN.
- Countdown values follow the display-interpolation rule from §3 — the tray ticks locally against server-provided anchors and re-anchors on every sync.
Quick-action menu
The tray menu offers rapid actions that map 1:1 to API calls — no local validation:
| Menu item | Action | Server-side rule that governs it (client does NOT enforce) |
|---|---|---|
| Log water (+250ml) | POST hydration event | 250ml units, 5000ml daily max, 30-second cooldown between entries — all evaluated by the Hydration Engine; client renders accept/reject result |
| Start / view Pomodoro | Start focus session or open countdown detail | Kinetic Pomodoro Engine owns session state; break only registers if wearables detect movement away from the screen |
| Log medication | Open medication logging UI | 60-minute Medication Grace window evaluated server-side |
| Log food / drink | Open categorical food logging UI (Safe / Trigger-bearing; Gout, IBS-GERD, Fatty Liver trigger families) | Trigger Families, Caffeine Block (no caffeine first 90 min post-wakeup), and GERD Window (4-hour pre-sleep, only water/chamomile/anise) all evaluated server-side |
| Open dashboard | Show/focus the main window | — |
| Theme: System / Dark / Light | Manual theme override (see §9) | — |
| Quit | Exit app (releases hotkeys, destroys tray) | — |
The exact set and ordering of quick actions beyond water/Pomodoro is PENDING DESIGN and must not exceed what the Phase 1 API exposes. The four alert/status colors of the color spec apply to tray-adjacent UI (e.g.,
#00FF33neon green glow for an active timer in dark mode,#FF3333for alerts).
Rejected-action feedback
When the server rejects a quick action (e.g., water logged within the 30-second cooldown, caffeine during the 90-minute block, non-allowed intake during the GERD window), the tray flow surfaces the server's response via an OS notification and/or popover state. The client shows what the server said — it never pre-computes the rejection. PENDING DESIGN: notification copy, iconography, and popover treatment.
Platform caveats (planning notes)
- Linux: tray support varies by desktop environment (GNOME requires an AppIndicator extension in many distros; KDE, XFCE, etc. differ). The plan must include a degraded mode: if no tray is available, the app stays in a normal window and hotkeys/notifications still work. Distro/DE support matrix is an open question.
- Windows: the icon may be hidden in the notification-area overflow by default; first-run guidance for pinning it is PENDING DESIGN.
7. Workstation Lock / Break Enforcer
An optional feature (off by default is assumed but not specified — see Open Questions) that makes Kinetic Pomodoro breaks physically real for desk-bound users: when a break starts, a modal window covers the workspace screens until the break resolves.
State machine
All transitions are driven by server state; the client renders them.
Overlay behavior
- Coverage: one always-on-top, full-screen modal overlay per connected display ("covering workspace screens"). Multi-monitor is a first-class requirement for the target audience.
- Content: break countdown, movement status ("waiting for movement…" → "movement detected — break registered"), and protocol messaging. Visuals are PENDING DESIGN (this is the most design-sensitive surface in the app).
- The overlay never validates the break itself. Closing the overlay, moving the mouse, or typing does NOT register a break. Only the server — informed by wearable movement data pushed from the Phase 4/5 clients — can mark a break as registered. The desktop app has no movement sensor role.
- Escape hatch: because the feature is optional and the app is not a punishment device, the overlay must offer a dismiss path (e.g., for meetings, on-call incidents). Dismissing ends enforcement for that break; whether the break then counts is still entirely the server's call (it will not count without movement). Dismiss friction level (immediate button vs. hold-to-dismiss vs. typed confirmation) is PENDING DESIGN; abuse/telemetry considerations are an open question.
- What the overlay is NOT: it is not an OS-level lock (no session locking, no input blocking at driver level, no interference with the OS lock screen, alt-tab shielding beyond normal always-on-top semantics). It is a full-screen always-on-top app window. Anything stronger is out of scope and would fight the "optional" requirement.
- Future state hook: doctor-prescribed 'Medical Restrictions' can dynamically alter Protocol Engines (e.g., requiring tracked workouts). The enforcer must render whatever break policy the server sends rather than hardcoding break lengths — this keeps the future doctor-dashboard capability free on the client side. (No doctor features are built in this phase.)
Settings
| Setting | Values | Notes |
|---|---|---|
| Break Enforcer | On / Off | The "optional" requirement. Default value: open question |
| Enforce on displays | All / selection | Multi-monitor selection UX PENDING DESIGN |
| Dismiss friction | TBD | PENDING DESIGN |
8. Security Architecture
Renderer hardening (hard requirements from the spec)
| Control | Setting | Rationale |
|---|---|---|
| Context isolation | contextIsolation: true on every BrowserWindow (main window, popover, every overlay) | Spec requirement; isolates preload/renderer worlds |
| Node in renderer | nodeIntegration: false — NO Node.js integration in any renderer process | Spec requirement; renderer is web-content only |
| Bridge | A secure context bridge: preload exposes a narrow, typed, allow-listed API via contextBridge; no generic IPC passthrough, no exposing ipcRenderer itself | Spec requirement |
| Sandbox | Renderer sandbox enabled | Follows from the two requirements above; standard hardening |
| Navigation | Renderer locked to app content; external links open in the OS browser; arbitrary navigation and window.open denied | Standard Electron hardening consistent with the spec's security posture |
| Content Security Policy | Strict CSP for renderer documents; remote code loading prohibited | Standard hardening consistent with the spec |
Context bridge surface (planning sketch — names illustrative)
The bridge is deliberately tiny. Everything else goes over HTTPS from the renderer to the ToGO API directly.
| Bridge capability | Direction | Purpose |
|---|---|---|
auth.getSession / auth.signIn / auth.signOut | renderer → main | Vault-backed token lifecycle (renderer never sees raw vault APIs) |
pomodoro.stateChanged | main → renderer | Push server-synced Pomodoro anchors to tray popover / overlay |
hotkeys.actionTriggered | main → renderer | Deliver global-hotkey intents to the React layer |
theme.get / theme.set / theme.systemChanged | both | §9 theme engine |
enforcer.show / enforcer.dismissRequested | both | Overlay lifecycle |
locale.get | main → renderer | OS locale for i18n bootstrapping |
Whether API requests are issued from the renderer (token attached via a bridge-provided accessor) or proxied through the main process (renderer never holds the token at all) is an implementation decision to settle in technical design. The stricter option — main-process proxying so tokens never enter the renderer — is the recommended default posture; final call is an open question for the security review.
Token storage
- TOGO auth tokens are persisted via OS credential vaults (Keytar / native keychains) per the spec:
- Windows → Windows Credential Manager.
- Linux → Secret Service API (GNOME Keyring / KWallet via libsecret).
- Tokens are never written to disk in plaintext, never placed in
localStorage, never logged. - Headless/vault-less Linux sessions (no Secret Service available) need a defined fallback behavior (fail closed and require re-login vs. session-only memory storage). Open question.
- Maintenance risk (flagged, not resolved): Keytar is in maintenance/archived status upstream. The spec names "Keytar / native keychains" — the plan treats "OS-native credential vault" as the requirement and the exact library (Keytar vs. a maintained equivalent vs. Electron
safeStoragelayered on OS crypto) as an open question for technical design.
AI & BYOK
- All AI calls are proxied server-side; user AI keys (OpenAI, Anthropic, Google, Apple Intelligence) are encrypted in the backend. The desktop client's only AI-related duty is UI: submitting the user's key once to the backend over HTTPS during BYOK setup and rendering proxied results. No key caching, no key display after entry.
Update integrity
- Installers and updates must be code-signed (Windows Authenticode; Linux package signing appropriate to the chosen format). Auto-update mechanism is not defined in the spec — open question (see §12).
9. Theme Engine
Per the spec: the desktop client listens to system theme changes with a manual override between the two themes.
Modes
| Mode | Behavior |
|---|---|
| System (default) | Main process subscribes to OS theme (nativeTheme on Windows; portal/GTK signals feeding the same Electron API on Linux) and pushes changes to the renderer live |
| Dark Cybernetic (manual) | Forces dark regardless of OS |
| Light Clinical (manual) | Forces light regardless of OS |
Override choice persists across launches (storage location: ordinary app settings — not the credential vault, which is for tokens only).
Palette (strict, from the color spec)
| Token | Dark Cybernetic | Light Clinical |
|---|---|---|
| Background | #1A1A1A charcoal | #FFFFFF |
| Active timers | #00FF33 neon green with glow effects | #20A060 saturated teal, flat |
| Active buttons | #00CCFF cyan | #0070A0 deep cyan blue, flat |
| Alerts | #FF3333 red | (light-mode alert color not specified — PENDING DESIGN / spec gap) |
| Effects | Glowing effects | Flat colors, no glow |
- Theme tokens live in the shared
packages/themepackage (single definition serving web and desktop — §4). - Theme applies everywhere the app paints: main window, tray popover, Break Enforcer overlays. Tray icon variants per theme are PENDING DESIGN.
- Full mapping of the palette onto components (surfaces, text ramps, disabled states, focus rings) is PENDING DESIGN — only the five/four anchor colors above are specified.
10. Global Hotkeys
Rapid logging without leaving the IDE — the signature desk-professional feature.
Behavior
- Registered by the main process as system-wide shortcuts (work while any app — IDE, terminal, browser — has focus).
- A hotkey press fires the corresponding API call (or opens the minimal capture UI) and confirms via OS notification / tray feedback. It does not need to raise the main window.
- Server verdict is always displayed honestly: a
Ctrl+Alt+Wpress during the 30-second hydration cooldown results in the server's rejection being shown, not a silent success.
Bindings
| Action | Default binding | Status |
|---|---|---|
| Log water (+250ml) | Ctrl+Alt+W | Given in spec (as an example) |
| Other actions (Pomodoro start, medication, food) | — | Not defined in the spec. To be decided with the finalized design; must map only to existing API actions |
- All bindings must be user-remappable with conflict detection, because the target audience's IDEs (JetBrains, VS Code, vim/tmux users with global tools) are hotkey-dense.
Ctrl+Alt+Wremains the shipped default for water per the spec. - Registration failures (binding already taken by the OS or another app) must surface visibly in settings, not fail silently.
- Linux caveat: global shortcut registration is limited under Wayland; a fallback path (portal-based shortcuts where available, or X11-only support with a documented limitation) must be chosen in technical design. Open question.
11. i18n & RTL
Inherited requirements, applied to desktop surfaces:
- Full RTL support for Arabic: layout mirroring across the main window, tray popover, and Break Enforcer overlay — including progress bars (e.g., Pomodoro progress and hydration progress render right-to-left).
- No string concatenation; all strings via ICU Message Format (plurals, genders, number/date formatting).
- Message catalogs are shared with Phase 2 (
packages/i18n); desktop adds only desktop-specific strings (tray menu items, hotkey settings, enforcer copy). - Tray menus and OS notifications use the same localized catalogs. OS-level tray menu RTL rendering follows the OS; app-rendered surfaces (popover, overlay) mirror explicitly.
- PENDING DESIGN: Arabic typography choices and mirrored layouts for the overlay and popover.
12. Packaging, Distribution & Updates
What the spec fixes: Electron, targeting Windows and Linux. Everything else below is proposal-level and marked accordingly.
| Concern | Plan | Status |
|---|---|---|
| Build tooling | An Electron packaging toolchain (e.g., electron-builder or Forge) producing Windows and Linux artifacts | Proposal — not specified |
| Windows artifact | Signed installer (NSIS/MSIX class decision) | Open question |
| Linux artifacts | Distribution format(s) — deb/rpm/AppImage/Flatpak/Snap | Open question; affects tray + keychain + autostart behavior per format |
| Auto-update | Mechanism and cadence | Open question — not in spec |
| Launch at login | Expected for a tray-resident health companion; opt-in vs. default | Open question |
| Telemetry/crash reporting | Whether any exists at all | Open question — nothing in spec; must respect the health-data-sensitive posture |
13. Testing Strategy (Planning Level)
- Shared UI: component tests live with
packages/ui(owned by Phase 2); desktop must not duplicate them. - Main process: unit tests for tray state rendering from server anchors, hotkey registration/teardown, vault adapter (mocked keychain), theme mode resolution (system/manual matrix).
- Bridge contract: tests asserting the preload surface exposes exactly the allow-listed API and nothing else (guarding the
contextIsolationposture). - E2E: Electron-driver-based smoke tests (launch, sign in against a stubbed ToGO API, tray tick, hotkey → API call → notification, enforcer overlay appears on simulated break state, theme switch, RTL launch in Arabic).
- SSOT conformance check: a dedicated review/test gate asserting no protocol constants (250ml, 5000ml, 30s, 90min, 4h, 60min, 3 cycles) appear in client code — they may appear only in display strings received from the server or in test stubs.
- Multi-monitor matrix: enforcer coverage on 1/2/3-display setups, mixed DPI.
- Linux matrix: at minimum one GNOME and one KDE environment for tray, keychain, and theme-signal behavior (exact distro matrix: open question).
14. Milestones (Within Phase 6)
- M1 — Shell & auth: Electron scaffold with hardened window defaults; TOGO auth sign-in; tokens in OS vault; main window hosts the shared Phase 2 React app.
- M2 — Tray & countdown: tray icon, server-anchored live Pomodoro countdown, quick-action menu wired to API, rejection feedback.
- M3 — Global hotkeys:
Ctrl+Alt+Wwater logging end-to-end; remapping UI; conflict handling; Wayland fallback decision implemented. - M4 — Break Enforcer: optional multi-display overlay driven by server break state; dismiss path; settings.
- M5 — Theme & i18n: system-theme listener + manual override across all surfaces; Arabic RTL pass including mirrored progress bars.
- M6 — Packaging: signed Windows and Linux artifacts; update story per §12 decisions; release candidate.
(Milestone ordering assumes Phases 1–2 are complete; M2–M4 need the Kinetic Pomodoro and logging endpoints live.)
15. Open Questions
Consolidated list of items that are genuinely undefined in the current spec (not to be invented unilaterally):
- Pomodoro sync transport: polling vs. WebSocket/SSE push from the ToGO API for live countdown anchors — must be settled in the Phase 1 API contract.
- Token residency: renderer-held token vs. main-process API proxying (recommended: main-process proxy so tokens never enter the renderer). Needs a security-review decision.
- Keytar succession: spec names Keytar, which is archived upstream; confirm whether "Keytar / native keychains" means Keytar specifically or any OS-native vault implementation.
- Break Enforcer defaults: on or off by default; dismiss friction; whether dismissals are recorded server-side.
- Hotkey set beyond water: which other quick-log actions get default global bindings.
- Wayland strategy for global hotkeys and the Linux desktop-environment support matrix (tray + keychain availability per DE).
- Vault-less Linux fallback: behavior when no Secret Service is available.
- Windows/Linux packaging formats, code-signing identities, and auto-update mechanism.
- Launch-at-login default for the tray-resident experience.
- Light-mode alert color: the color spec defines
#FF3333for dark mode only. - Crash reporting/telemetry: whether any is permitted given the health-data posture.
- All PENDING DESIGN items: tray countdown rendering, tray icon variants per theme, popover layout, Break Enforcer overlay visuals and dismiss UX, notification copy, quick-action menu composition, main-window layout/sizing, Arabic typography — blocked on the Figma design work.