Skip to content

Meltdown Minder — Current Architecture & Capabilities

Last updated: 2026-08-12 Tags: category:app tier:0


Product Purpose

Meltdown Minder detects rising emotional dysregulation in neurodivergent children early enough for caregivers and therapists to intervene before a meltdown escalates. It pairs biometric data from the WHOOP 5 strap with clinician-labeled emotional states (Zones of Regulation + ABC events) to train per-child arousal-detection models.

Phase progress: - Phase 1 — Event logging (current): After-school check-ins, event timeline, trigger analysis, weekly summaries - Phase 2 — Tantrum prediction + coaching (in progress): WHOOP BLE ingestion, arousal detection, real-time alerts, parenting-style-aware coaching prompts - Phase 3 — Advanced (future): Population models, therapist dashboard, multi-wearable support, AI summaries


Architecture Overview

Three Azure Container Apps within a single ACA Environment:

meltdownminder.com
        │
  ┌─────┴──────────────────────────────────┐
  │  Azure Container Apps Environment      │
  │  Static IP: 4.153.57.168               │
  │  Region: eastus2                       │
  │                                        │
  │  ┌────────────────────┐                │
  │  │ meltdownminder-    │  HTTPS :80     │
  │  │ prod-frontend      │←─────────      │
  │  │ nginx → proxy_pass │                │
  │  └────────┬───────────┘                │
  │           │ http://<app>:8000          │
  │  ┌────────┴───────────┐  gRPC TLS :443│
  │  │ meltdownminder-    │───────────────→│
  │  │ prod-backend       │                │
  │  │ FastAPI + SQLite   │                │
  │  └────────────────────┘                │
  │           │ gRPC (internal)            │
  │  ┌────────┴───────────┐                │
  │  │ meltdownminder-    │                │
  │  │ prod-ingestor      │                │
  │  │ Rust + DuckDB      │                │
  │  └────────────────────┘                │
  └────────────────────────────────────────┘
Container App Technology Port CPU/RAM Replicas
Frontend nginx + React PWA 80 0.25 / 0.5Gi 1
Backend FastAPI + SQLAlchemy 8000 0.5 / 1.0Gi 1
Ingestor Rust + DuckDB (gRPC) 50051 0.25 / 0.5Gi 1

Frontend (frontend/)

Stack: React 19, Vite 8, TypeScript 6, Tailwind 4 (CSS-first), shadcn/ui, TanStack React Query 5, React Router 7, Playwright e2e

Routes

Public pages (wrapped in PublicLayout): | Route | Page | Purpose | |---|---|---| | /login | LoginPage | Email/password login, demo hint | | /register | RegisterPage | Registration with auto-login | | /about | AboutPage | Mission, features overview | | /privacy | PrivacyPage | Full privacy policy (HIPAA, CCPA) | | /terms | TermsPage | Terms of Service | | /research | ResearchPage | Evidence: studies, methodology, clinical glossary | | /help/contact | ContactPage | Contact form → POST /api/notifications/contact | | /hipaa, /accessibility, /impact, /careers, /press | Static pages |

Parent pages (wrapped in AppShell with 6-tab bottom nav): | Route | Page | Purpose | |---|---|---| | / | Dashboard | Child cards, zone badges, regulation sparklines, alert banners | | /monitor | Monitor | Real-time ZoneGauge, HR/metrics, alert list with AI suggestions | | /recap | DailyRecap | Event timeline, zone events, patterns | | /child | ChildDetails | CRUD child profiles (diagnoses, triggers, sensory profiles) | | /insights | Insights | Weekly trigger analysis, trend charts | | /calmspace | CalmSpace | Grounding exercises, breathing tools, SMS enrollment | | /parenting-style | ParentingStyle | Select style (authoritative/gentle/positive discipline/attachment) | | /pair-device | PairDevice | WHOOP band pairing codes | | /profile | ProfilePage | User profile, email management, password change | | /whoop-ble | WhoopBLETest | Dev: Web Bluetooth WHOOP 5 connection (raw frame viewer) | | /whoop-data | WhoopDataView | Debug: raw WHOOP readings from DuckDB |

Clinician pages: | Route | Page | Purpose | |---|---|---| | /clinician | ClinicianDashboard | Patient list, labeling sessions, ABC event review | | /clinician/child/:id | ChildView | Per-child diagnoses, strategies, session history | | /labeling | LabelingTool | ABC Antecedent-Behavior-Consequence labeling tool |

Auth Flow

  • No <ProtectedRoute> wrapper — pages call useAuth() and self-check
  • AuthContext.tsx provides { user, loading, login, register, logout, refreshUser }
  • Tokens (access + refresh) stored in localStorage
  • api.ts intercepts 401s, queues refresh requests, retries
  • Demo mode: auto-attempts parent@demo.com / demo1234, shows "Demo" badge in header

Key Components

  • AppShell.tsx — Sticky header (branding, demo badge, avatar dropdown) + 6-tab sub-nav
  • ZoneGauge.tsx — Circular SVG gauge (0-100, color by zone: blue < 25, green < 50, yellow < 75, red >= 75)
  • TrendSparkline.tsx — SVG polyline sparkline for regulation score history
  • SuggestedResponseSheet.tsx — Bottom sheet with parenting-style-aware response cards
  • Footer.tsx — 6-section footer, demo data loader, PWA install prompt
  • 22 shadcn/ui components in src/components/ui/

WHOOP BLE Web Worker

src/workers/whoopWorker.ts (288 lines, @ts-nocheck): - Parses WHOOP 5 BLE frames (packet types: K10, K16/K17/K18/K24, R22, T40/T47) - Computes pseudo-RMSSD from rolling HR buffer - Flushes accumulated readings every 500ms via POST /api/whoop/data/batch - Max batch size: 200 readings - Communicates with main thread via postMessage

PWA Features

  • Service worker: network-first for app shell, cache-first for static assets, bypasses /api and /ws
  • Web manifest: standalone display, SVG icons, warm-bg theme
  • beforeinstallprompt handler (useInstallPrompt)
  • Deep-link handler via __tcDeepLink

Backend (backend/)

Stack: FastAPI async, SQLAlchemy async, Alembic, Python 3.13, Pydantic v2

All API Routes (by prefix)

POST /api/auth/* — Auth

Method Endpoint Rate Limit Auth
POST /register 100/min None
POST /login 10/min None (5-fail lockout, 15min)
POST /refresh None (refresh token)
POST /logout Required
GET /me Required
PUT /profile Required
POST /change-password Required
POST /emails Required
DELETE /emails/{id} Required
PUT /emails/{id}/primary Required
POST /delete-account 3/hour Required
Method Endpoint Purpose
GET /api/children/status Dashboard card statuses (zone, score, trend)
GET /api/children List children
POST /api/children Create child
GET/PUT/DELETE /api/children/{id} CRUD single child
GET /api/children/{id}/readings List sensor readings (filtered by start/end/limit)
POST /api/children/{id}/readings Create reading (triggers alert + notification)
GET /api/children/{id}/trends Aggregated trends (days param, 1-90)
GET /api/children/{id}/recap Daily recap (alerts, readings, zone counts)

GET/PATCH /api/alerts — Alerts + Interventions

Method Endpoint Purpose
GET /api/alerts List alerts (filtered by child_id/status)
GET /api/alerts/{id} Single alert
PATCH /{id}/acknowledge Acknowledge
PATCH /{id}/snooze Snooze
PATCH /{id}/dismiss Dismiss (sets resolved_at)
PATCH /{id}/resolve Resolve (sets resolved_at)
POST /api/alerts/interventions Log intervention
GET /api/alerts/interventions List interventions

GET/POST/DELETE /api/devices — Device Pairing

Method Endpoint Purpose
GET /api/devices/{child_id} List paired devices
POST /api/devices/{child_id} Register device
DELETE /api/devices/{child_id}/{device_id} Remove device
POST /api/devices/{child_id}/generate-token Generate 5-min pairing token
POST /api/devices/claim-token Claim device via token
PATCH /api/devices/{child_id}/{device_id}/heartbeat Update last_seen_at

POST/GET /api/labeling/* — Therapy Labeling

Method Endpoint Purpose
POST /api/labeling/sessions Start session
GET /api/labeling/sessions List sessions
GET /api/labeling/sessions/{id} Single session
PATCH /api/labeling/sessions/{id}/end End session
POST /api/labeling/zone-events Record zone event
POST /api/labeling/abc-events Record ABC event
GET /api/labeling/sessions/{id}/review Full session review

POST /api/ai/* — AI Suggestions

Method Endpoint Purpose
POST /api/ai/suggest Get parenting suggestion (MD5-based pool)
POST /api/ai/regenerate Get adjacent suggestion

POST /api/notifications/* — Notifications

Method Endpoint Rate Limit Auth
POST /api/notifications/sms Required
POST /api/notifications/email Required
POST /api/notifications/inbound None (Telnyx webhook, Ed25519 verified)
GET /api/notifications/messages Required
POST /api/notifications/contact 10/min None (public)
POST /api/notifications/subscribe None (public)

POST /api/whoop/* — WHOOP Ingestion

Method Endpoint Purpose
POST /api/whoop/data Single reading (→ gRPC + SensorReading)
POST /api/whoop/data/batch Batch readings (500ms flush, 200 max)
GET /api/whoop/raw/{child_id} Raw DuckDB data (for ML export)

POST /api/seed/demo — Demo Data (dev only)

  • Creates 2 users, 2 children, 72h sensor readings, 30 days of alerts

POST /api/sim/* — Simulation (dev only)

  • Start/stop/speed/reset/trigger simulated sensor data

GET /api/stakeholder/* — Clinician Access

  • List shared children, read/create clinical notes

GET /api/ml/export — ML Training Export

  • Export intervention data with preceding sensor readings

Unauthenticated

Endpoint Purpose
GET /health Health check
GET /api/whoop/ingestor-health Ingestor connectivity + uptime
WebSocket /ws Real-time events (auth via first message: JWT)

Database Models (15 tables)

Table Key Columns Purpose
users id, email, password_hash, role, phone_number, failed_login_attempts, locked_until Parent/clinician accounts
children id, parent_id, name, date_of_birth, diagnoses, triggers, sensory_profile, preferred_strategies, alert_sensitivity Child profiles
sensor_readings id, child_id, timestamp, heart_rate, hrv, eda, movement, skin_temp, regulation_score, data_quality Aggregated biometric snapshots
alerts id, child_id, level (BLUE/GREEN/YELLOW/RED), regulation_score, predicted_meltdown_type, ai_suggestion, status Dysregulation alerts
interventions id, alert_id, parent_id, intervention_used, outcome_rating, time_to_resolution_min Caregiver interventions
labeling_sessions id, child_id, therapist_id, status, start_time, end_time Therapy labeling sessions
zone_events id, session_id, zone, trigger Zones of Regulation events
abc_events id, session_id, antecedent, behavior, consequence, severity, recovery_minutes ABC behavioral events
clinical_notes id, stakeholder_id, child_id, note_text, recommended_strategies Clinician notes (PHI)
stakeholder_access id, stakeholder_id, child_id, granted_by, access_level Cross-user data sharing
devices id, child_id, device_name, device_type, device_identifier WHOOP/pairing device registry
audit_logs id, timestamp, actor_id, action, resource_type, resource_id, child_id, summary, ip_address HIPAA audit trail
revoked_tokens id, jti, token_type, expires_at JWT revocation
user_emails id, user_id, email, is_primary Multi-email support
inbound_messages id, from_number, body, received_at, raw_payload Inbound SMS storage
email_subscriptions id, email, source, subscribed_at, unsubscribed_at Newsletter/waitlist
opt_outs id, phone_number, opted_out SMS opt-out registry

Key Services

Service File Purpose
arousal_service app/services/arousal_service.py Heuristic regulation score (hr=0.3, hrv=0.4, motion=0.15, temp=0.15), zone classification, alert creation
ml_service app/services/ml_service.py XGBoost ONNX inference → regulation score (falls back to heuristic)
whoop_ingestor app/services/whoop_ingestor.py gRPC client to Rust sidecar (ingest_batch, query_readings, ping)
notification_service app/services/notification_service.py SMS (Telnyx) + email (Azure ACS) alert dispatch
sms_processor app/services/sms_processor.py Parse inbound SMS: zone keywords, ABC format, severity, recovery time
alert_followup app/services/alert_followup.py 5-min delayed follow-up SMS (30-min debounce)
audit_logger app/services/audit_logger.py HIPAA e-PHI access logging
seed app/services/seed.py Demo data generator (2 users, 2 children, 72h readings)
email app/services/email.py Azure Communication Services email sender
sensor_registry app/services/sensor_registry.py Pluggable sensor adapters

Middleware & Auth

  • AuthDep (app/middleware/auth.py): JWT Bearer token → AuthContext(user_id, role, email, name, token_jti). Optional AuthOptDep for unauthenticated access. Role-based guard via require_role("PARENT", "STAKEHOLDER").
  • AuditMiddleware (app/middleware/audit.py): Logs all POST/PUT/PATCH/DELETE and 4xx/5xx responses. Sets X-Audit-Id header.
  • Rate Limiting (app/rate_limit.py): slowapi in-memory limiter. /login = 10/min, /register = 100/min, /delete-account = 3/hour, /contact = 10/min.

Background Tasks

  • _promote_loop() (every 60s): Backfills regulation_score for SensorReading rows that missed it (processes 50 at a time)

WebSocket

  • /ws endpoint: First message must contain {"token": "..."} (JWT). Sends real-time "alert" and "whoop_data" events via ConnectionManager.

Ingestor — Rust gRPC Sidecar (backend/rust/)

Stack: Rust + tonic 0.12 + prost 0.13 + DuckDB 1.x (bundled, ~7min build)

gRPC Endpoints (4 RPCs)

RPC Purpose
IngestBatch Accepts Reading list → writes to DuckDB whoop_raw (deduplicated by timestamp + packet_type + k_value)
QueryReadings Queries DuckDB by child_id + since_unix + limit (used by ML pipeline and promote loop)
RefreshChildStatus Materializes 24h aggregation into child_status table
Ping Health check (status, uptime_secs, total_readings)

DuckDB Schema: whoop_raw (29 fields)

Includes: recorded_at, device_id, heart_rate_bpm, heart_rate_source, rmssd_ms, rr_intervals (INT[]), accel_x/y/z_g, gyro_x/y/z_dps, motion_intensity, optical_sample_count/min/max/flags, temperature_celsius, temperature_source, respiratory_rate, ppg_channel, ppg_waveform (INT[]), packet_type, k_value, body_length, raw_frame_hex, child_id, session_id, zone_label, ingested_at

Indexed on (child_id, recorded_at).

Derived Computations (done in Rust)

  • RMSSD: sqrt(mean(squared(RR_diff))) / 10 from raw RR intervals
  • Motion Intensity: (abs(accel_x) + abs(accel_y) + abs(accel_z)) / 3, clamped to 1.0

Android Phone App (PhoneApp/)

Stack: Kotlin, Gradle, Android 10+ (API 29+), foreground service

Purpose: BLE bridge from WHOOP 5 strap to the cloud. Displays live status dashboard.

BLE Flow

  1. Login (email/password → JWT via /api/auth/login)
  2. Select child from /api/children
  3. Scan BLE for "WHOOP" prefix, connect GATT (MTU 247, 2M PHY)
  4. Enable notifications on 3 WHOOP V5 characteristics
  5. Deframer syncs on 0xAA, validates CRC16 header + CRC32 payload
  6. PacketProcessor dispatches type-40 (HR+RR), type-47 r22 (105fps accel), V18 (gravity+temp), V26 (PPG)
  7. Uploader flushes every 500ms or 200 readings via POST /api/whoop/data/batch
  8. Auto-reconnect with exponential backoff, auto-restart on boot

Historical Data Offload

State machine: Idle → DisablingR22 → Offloading → ReEnablingR22 → Complete. Sends WHOOP commands to dump historical V18/V26 data using cursor-based pagination.


ML Pipeline (ml/)

Stack: Python, XGBoost, Optuna, ONNX

Independence: Runs locally, pulls data from production API (not DuckDB directly)

Pipeline Steps

  1. export.py — Pulls raw WHOOP data from GET /api/whoop/raw/{child_id} → CSV
  2. feature_engineering.py — 30s sliding windows (10s stride) → 41 features across 7 groups → Parquet
  3. train.py — Optuna-tuned XGBoost (30 trials, 5-fold CV, ROC-AUC objective). Falls back to Isolation Forest if no labels.
  4. evaluate.py — Leave-one-session-out CV, lead-time metric, feature ablation
  5. model.py — ONNX export → backend/models/arousal_model.onnx

Feature Groups (41 total)

Group Features Count
HR mean, std, min, max, range, trend, percentiles, rate of change, onset/recovery 11
HRV rmssd_mean, sdnn, pnn50, mean_rr, delta/min, DFA alpha1, sample entropy 7
Motion acc_mag stats, axis ranges, jerk, energy, entropy 12
PPG pulse amp mean/std, upstroke slope 3
Temp mean, std, slope 3
Resp rate mean/std 2
Coherence motion-HR coherence, dissociation index 2

Reported Performance (Daniel's model)

  • Test AUC: 0.9872, Accuracy: 93%
  • Missed arousals: 1
  • HR dominates ablation (AUC drop 0.138), HRV (0.0037), Motion (0.0050)

Database Architecture

Current State

Database Engine Data Volume Deployment
Main app SQLite (via aiosqlite) Users, children, alerts, interventions, labeling sessions, clinical notes, sensor readings Trial: 5 children × 25 sessions Azure Files persistent volume mount at /data/meltdownminder.db
WHOOP raw DuckDB (Rust sidecar) Raw 29-field WHOOP frames including RR intervals, PPG waveforms, accelerometer High-velocity time-series Sidecar container, file at /data/meltdownminder.duckdb
MCP server SQLite (separate) mcp.db — agent tools data Small Local file in backend container

Write Path (CQRS)

WHOOP BLE frame → FastAPI /api/whoop/data/batch
  ├── gRPC IngestBatch → Rust sidecar → DuckDB (whoop_raw)  ← Hot path, high fidelity
  └── Direct INSERT → SQLite sensor_readings                  ← Immediate, aggregated

Scaling Consideration

SQLite is appropriate for the trial phase but cannot support multiple backend replicas (Azure Files + WAL does not handle concurrent writers). PostgreSQL is the natural upgrade path and is well prepared for by the SQLAlchemy async + Alembic abstraction. The .env.example still references MSSQL (design intent), but production uses SQLite after the Azure SQL server was decommissioned.


Deployment

Manual — no CI/CD pipeline.

Initial Deploy

powershell -File backend/deploy.ps1
Creates: RG, ACR, Key Vault, ACA environment, Azure Files share, builds + pushes 3 Docker images, deploys all 3 container apps, locks CORS.

Incremental Updates

Per-service: docker builddocker pushaz containerapp update (see docs/deployment_runbook.md).

Pre-deploy Checks

  • Backend: ruff check app/mypy app/
  • Frontend: pnpm tsc --noEmitpnpm buildpnpm test

Security

In-App Controls

  • JWT auth: 15-min access token, 7-day refresh token, revocation on logout
  • Rate limiting on auth endpoints
  • Account lockout: 5 failed attempts = 15 min
  • AuditMiddleware logs all PHI access (who, what, when, IP)
  • WebSocket auth via first-message JWT
  • All PHI endpoints require AuthDep + ownership scoping
  • HIPAA compliance docs: docs/Compliance/

Known Gaps (development phase)

  • proxy_ssl_verify off in nginx
  • Secrets pass through Key Vault but set as plain env vars (not runtime SDK fetch)
  • HS256 symmetric JWT signing (no RS256)
  • localStorage token storage (XSS-vulnerable, accepted residual risk)
  • No VNet isolation
  • No automated backup policy
  • All Defender plans disabled (cost)

Gotchas

Gotcha Detail
Tailwind v4 CSS-first — no postcss.config.js, no tailwind.config.*. Theme in globals.css @theme block
Auth hooks AuthContext.tsx is the only live auth. Legacy useAuth.ts deleted (0 imports)
Demo mode Auto-attempts demo login, shows "Demo" badge, seeded PRNG (mulberry32)
JWT expiration config.py defaults 15min, .env.example overrides to 1440min (24h)
Rust build ~7 minutes (DuckDB C lib compiled from source via bundled feature)
Nginx envsubst ${BACKEND_URL} required at container start or nginx refuses to boot
Docker tags Use unique timestamps (20260624-210147) — ACA may cache :latest
No route guards Pages call useAuth() and self-check. No <ProtectedRoute> wrapper
ignoreDeprecations frontend/tsconfig.json has "ignoreDeprecations": "6.0" for TS 6
dark mode next-themes installed but no UI toggle wired
WebSocket unused Frontend has no WebSocket consumer despite proxy and nginx config