Project · Lifebot V2

lifebot-v2 — High-Level Design

Successor to [[Bobs-lifebot/README]] (single-user, server-readable) built to the admin-blind bar set in [[SAAS-Lifebot/HLD]]. Governing requirement (owner): **blast-radius reduction on server compromise** — the operator must never be able to decrypt any user's health data. Health data is GDPR special-category, so minimising what the server can read is also the compliance posture. Plan of record: adversarially-reviewed rev. 3 (4-persona review: skeptic, security, architect, pragmatist); build staged local → Linux box → SaaS → iOS → Android.

type hldstatus activelifebot · health · zero-knowledge · architecture

Architecture

flowchart TB
  subgraph Clients [Clients — plaintext exists ONLY here]
    RN[React Native app<br/>HealthKit read + aggregate<br/>encrypt on device]
    WEB[React dashboard SPA<br/>WebCrypto/libsodium WASM<br/>decrypt + aggregate in browser]
  end
  IDP[Clerk IdP<br/>identity only]
  subgraph Box [Linux box — Docker Compose]
    PLAT[Next.js control plane<br/>SaaS-Boilerplate: sign-up, orgs, account]
    API[FastAPI zero-knowledge sync API<br/>stores ciphertext + opaque metadata]
    PG[(Postgres<br/>lifebot: RLS data plane<br/>platform: Drizzle control plane)]
    RED[(Redis)]
    WRK[worker]
  end
  LLM[Gemini API<br/>coach, client-direct]
  RN & WEB -->|OIDC JWT| IDP
  RN & WEB -->|Bearer JWT + ciphertext| API
  RN & WEB -.->|on-device summary| LLM
  PLAT --> IDP
  API --> PG
  API --> RED
  CF[Cloudflare Tunnel] --> PLAT & WEB & API

Zero-knowledge model

Master password → Argon2id (per-user salt) → master key → unwraps a random data keyXChaCha20-Poly1305 encrypts every record on the client. Server stores: salt, verifier, wrapped keys, ciphertext, opaque HMAC dedupe keys, version vectors — nothing readable. Identity (Clerk) and unlock (master password) are deliberately two secrets: an OAuth token the server sees can never be an encryption key. See lifebot-v2/adr/0001-zero-knowledge-e2ee and lifebot-v2/adr/0002-one-libsodium-master-password-key.

Control plane vs data plane

Plane Owner Can read health data?
www. control plane (Next.js, SaaS-Boilerplate, Clerk, Drizzle, platform DB) account, orgs, billing-ready No — separate DB, no key
app. dashboard SPA (React) render decrypted data Only on-device after unlock
api. data plane (FastAPI, lifebot DB, FORCE RLS) ciphertext sync No — ciphertext only

See lifebot-v2/adr/0003-saas-boilerplate-control-plane.

AI coach

Runs client-side: decrypt → build minimised summary on device → call Gemini directly (scoped short-lived credential; paid tier = not trained on). The Lifebot server is never in the plaintext path. See lifebot-v2/adr/0004-client-side-ai-coach-gemini.

Components

Piece Role
packages/crypto envelope: Argon2id, XChaCha20, AAD header; libsodium injected per platform
packages/client API client, sync engine, client-side aggregation, coach — shared web/RN
packages/ui tokens.json + presentational primitives (Tailwind 3.4 preset, NativeWind-compatible)
apps/api FastAPI ZK sync API: /v1/vault, /v1/sync; JWKS verify on (iss,sub); RLS
apps/web dashboard SPA (Vite/React); Clerk gate → master-password unlock
apps/platform vendored SaaS-Boilerplate control plane (Next 16, Clerk, Drizzle)
apps/mobile RN scaffold (typechecks; Expo build deferred to Stage 4)

GDPR / data protection

  • Special-category data is ciphertext server-side; cleartext metadata minimised to routing only.
  • Crypto-erasure: destroying the wrapped key renders all ciphertext permanently unreadable (Art. 17).
  • Coach disclosure: consented, minimised summary to Gemini under paid-tier no-training terms.
  • DR: the wrapped-key/vault tier is the backup crown jewel (ciphertext is worthless without it).

Decisions

Non-goals

  • Server-side analytics/search/AI over health data (impossible by design — accepted).
  • Org-visible member health data (orgs govern membership/billing only).
  • HIPAA (decide applicability before any US health-context onboarding).
Compiled from wiki/projects/lifebot-v2/HLD.md · git is the source of truth