# Helios — design spec 2026-08-18 · status: approved pending Marcus's review ## What Helios is a self-hosted, MIT-licensed web app: a FOSS alternative to Illume Labs / Superpower. One Docker container per user (or household). The pitch: **your health data lives on your server, and the AI runs on your key.** v1 features: - Connect wearables with the user's own credentials: Oura, Whoop, Fitbit (official APIs), Garmin (unofficial library, clearly labeled). - Upload lab PDFs → LLM extraction → user reviews/confirms → normalized biomarkers with units, reference ranges, and flags. - Log meals by photo or text → LLM macro estimate → editable card. - Grounded AI chat: tool-calls into the local DB, so only relevant data slices are sent to the LLM — never a full dump. - Daily brief generated at a user-chosen hour from the last 24 h + 30-day trends + flagged labs. - Dashboards: Today (brief + cards), Sleep, Training, Labs, Nutrition, Settings. Non-goals for v1: multi-user SaaS, mobile app (planned fast-follow for HealthKit/Health Connect sync), Apple Health ingestion (needs the companion app), medical advice claims of any kind. ## Architecture API-first monolith. The web UI is a pure client of a typed REST API — that API is the contract a future mobile app reuses unchanged. ``` Web UI (Vite + React SPA) Today/brief · Chat · Sleep · Train · Labs · Nutrition · Settings │ typed REST (Zod-validated) Server (Bun + Hono, one process) vendor pollers (in-process cron, per-token, with backfill) lab PDF → LLM extraction → review queue meal photo/text → LLM macro estimate chat orchestrator (typed tools over the DB) daily brief generator (cron) │ SQLite (single file, WAL, Drizzle ORM) + uploads dir on one /data volume LLM: any OpenAI-compatible endpoint — OpenRouter documented default, Ollama/LM Studio for zero-third-party operation ``` Stack rationale: TypeScript end to end (shared types between API, UI, and LLM tool schemas); SQLite over Postgres because a per-user instance needs one-file backup and zero ops (Open WebUI ships the same way); SPA over Next.js because an API-first self-hosted app gains nothing from SSR and the SPA keeps the mobile-later contract honest. `health-api` (marcus/health-api) is **reference only**: its lab normalization (`normalize.ts`, `units.ts`) and schema shapes inform clean re-implementations. No code copied with Marcus-specific assumptions, no personal data, MIT throughout. ## Data model (Drizzle/SQLite) - `settings` — LLM base URL + key, vendor tokens, brief hour. Secret columns encrypted (see Security). - `daily_metrics` — per-day, per-source rollups: steps, resting HR, HRV, active calories, readiness/recovery scores. - `sleep_sessions` — start/end, stage durations, scores; raw vendor JSON retained in a `raw` column. - `workouts` — type, duration, load metrics; raw JSON retained. - `lab_draws` + `biomarkers` — draw metadata; marker slug, value, normalized unit, reference range, flag. - `meals` — timestamp, photo path, macro estimate, LLM confidence, user-edited flag. - `chat_threads` + `chat_messages`. - `briefs` — generated markdown + input snapshot. - `sync_state` — per-connector cursor/watermark for incremental polls. ## Connectors | Vendor | Path | User setup | | --- | --- | --- | | Oura | official API | paste personal access token | | Whoop | official API | register own free dev app, paste client id/secret (documented 5-min flow) | | Fitbit | official API | same own-app flow as Whoop | | Garmin | unofficial `garmin-connect` lib | username/password; labeled "may break without notice" | Pollers run on an in-process cron; first connect triggers historical backfill; `sync_state` makes polls incremental. Every connector normalizes into the tables above and keeps raw vendor JSON. Labs: PDF upload → LLM extraction prompt → **review/confirm screen** — nothing writes to `biomarkers` until the user approves the parsed values. Trust in medical numbers requires the human check. Nutrition: photo or free-text → LLM estimates items + macros (Open Food Facts as grounding where possible) → editable card; edits stored, model estimate retained for calibration. ## AI layer - Provider abstraction: base URL + key + model id; anything OpenAI-compatible works. OpenRouter is the documented default; Ollama is the fully-local option. - Chat tools (typed, Zod-schema'd): `query_sleep`, `query_metrics`, `query_workouts`, `query_labs`, `query_meals`, `log_meal`. Tools return bounded slices (date-ranged, capped row counts). - Daily brief: cron at the user's hour; prompt assembles yesterday's data, 30-day trends, and out-of-range biomarkers; output stored in `briefs` and rendered on Today. - Honest privacy wording everywhere (README + Settings): chat context is sent to the LLM endpoint the user chose. Only Ollama-class local endpoints mean zero third parties. ## Security - Auth: single password (argon2id), rate-limited login, httpOnly session cookie. Passkeys are a later upgrade. - Secrets at rest: AES-256-GCM column encryption; key generated at first boot into the /data volume (`secret.key`). - The SQLite file itself is not app-encrypted in v1 (bun:sqlite lacks SQLCipher); docs prescribe disk encryption + encrypted backups. Column crypto covers the credential-theft blast radius. - No telemetry, no update phone-home. Outbound traffic only: chosen LLM endpoint + connected vendor APIs. - CSP on the SPA; uploads served with safe content-type handling. ## Testing - Vitest: unit normalization (units/ranges/marker slugs), connector transforms against recorded vendor fixtures, tool-schema round-trips. - API integration tests over an in-memory SQLite. - CI: Gitea Actions — typecheck + tests on push. ## Deploy - One Dockerfile (bun base), `docker compose up`, single `/data` volume. - Caddy reverse-proxy recipe in docs. - Repo: git.rehbock.xyz/marcus/helios (public). GitHub mirror optional later for discoverability. ## Milestones 1. Scaffold: repo, CI, Docker, auth, settings (LLM + encryption plumbing). 2. First connector (Oura) end to end: poll → normalize → Sleep/Today UI. 3. Chat + tools over whatever data exists. 4. Labs pipeline (upload → extract → review → dashboard). 5. Meals + daily brief. 6. Whoop/Fitbit/Garmin connectors; docs site; v0.1 announcement.