Repository navigation
02 System Architecture
HealthBook AI (internal code name: BabyCare) is a three-tier system: two thin clients, one FastAPI application that owns every rule, and two private stores (a relational database and an encrypted object store). This page shows how the parts connect, which external services the code actually calls, and how a request travels through the API. The same code base also runs fully on one machine with SQLite and local encrypted files.
- Keep all clinical and authorization logic on the server; clients only render and submit.
- Keep originals and clinical text encrypted at the application layer before they reach any store.
- Call external services only for transport (email, push, optional vision) and never send clinical detail through them unless the guardian consents per document.
flowchart LR
subgraph clients["Clients"]
WEB["Web app<br/>vanilla JS, served by Firebase Hosting"]
DOC["doctor.html<br/>clinician share viewer"]
AND["Android app<br/>Expo and React Native"]
end
subgraph render["Render free web service"]
API["FastAPI API<br/>uvicorn, Docker"]
OCR["Local Tesseract OCR<br/>optional, in the container"]
end
subgraph supa["Supabase"]
PG[("PostgreSQL<br/>session pooler")]
BUCKET[("Private storage bucket<br/>Fernet-encrypted objects")]
CRON["pg_cron + pg_net<br/>scheduler wake-up"]
end
subgraph ext["External services"]
BREVO["Brevo HTTPS email API"]
EXPO["Expo push service"]
WPUSH["Browser push services<br/>Web Push VAPID"]
VISION["Opt-in vision provider<br/>OpenAI-compatible HTTPS"]
end
subgraph home["Home network"]
CAMW["Local camera worker<br/>python -m api.camera_worker"]
CAM["IP camera<br/>RTSP or HTTP"]
end
WEB -->|"HTTPS JSON, cookie session"| API
DOC -->|"HTTPS, share token"| API
AND -->|"HTTPS JSON, bearer token"| API
API -->|"SQL over TLS"| PG
API -->|"service-role HTTPS"| BUCKET
API --> OCR
CRON -->|"POST scheduler tick<br/>X-Scheduler-Key"| API
API -->|"password reset mail"| BREVO
API -->|"generic push text"| EXPO
API -->|"generic push text"| WPUSH
API -.->|"only with per-document consent"| VISION
CAM -->|"local stream"| CAMW
CAMW -->|"derived events only<br/>X-Camera-Worker-Key"| API
The dashed edge is disabled by default: render.yaml sets BABYCARE_VISION_URL to an empty string, and api/providers.py refuses to send anything unless the URL, model and key are configured and the request carries explicit consent naming the configured host.
| Component | Responsibility | Key modules / files |
|---|---|---|
| Web app | Single-page UI built from plain script files; same-origin /api locally, apiBase on the hosted domains; service worker caches only the shell, never /api/
|
web/index.html, web/app.js, web/care.js, web/config.js, web/sw.js
|
| Doctor viewer | Read-only page for a share link; fetches /api/share/public/{token}
|
web/doctor.html, web/doctor.js
|
| Android app | Native screens; token in expo-secure-store; offline queue for daily entries; Expo push registration via POST /api/devices
|
mobile/App.tsx, mobile/DailyCareScreen.tsx, mobile/src/config.ts
|
| FastAPI API | Auth, profile isolation, ingestion, review, care journal, exports, sharing, reminders, camera ingest |
api/main.py plus routers in api/*_api.py, api/workflows.py, api/share.py, api/cloud.py
|
| Security middleware | Origin check on writes, rate limiting, request size cap, security headers | api/guardrails.py |
| Database | All structured records; clinical text in EncryptedText columns |
api/database.py, api/models.py, api/care_models.py, api/lifetime_models.py, migrations/
|
| Object store | Encrypted originals, stool photos; never decrypts, never public URLs |
api/storage.py (LocalStorage, SupabaseStorage) |
| Reminder worker | Reconciles confirmed plans into notifications and delivers them; runs as a loop (python -m api.worker) or one tick per scheduler call |
api/worker.py, api/reminders.py, api/cloud.py
|
| Mailer | Transactional email over HTTPS (Brevo or Resend); SMTP only for local use |
api/mailer.py, api/password_reset.py
|
| Push | Expo push for the Android app, RFC 8030 Web Push with VAPID for the installed web app |
api/worker.py, api/push_web.py
|
| OCR | PyMuPDF text layer first, Tesseract for scanned pages and images, all local | api/extract.py |
| Camera worker | Runs inside the home network, reads the stream locally, posts movement/presence events only |
api/camera_worker.py, api/camera.py, api/camera_api.py
|
A typical authenticated write, for example adding a fluid entry from the web app:
-
Client.
request()inweb/app.jsprefixes the path withapiBase(fromweb/config.js) and sendsX-Requested-With: BabyCare. The web client authenticates with thebabycare_sessioncookie; the Android app sendsAuthorization: Bearer <token>. -
CORS. When
BABYCARE_CORS_ORIGINSis set,CORSMiddlewareallows only those origins, with credentials. -
Guardrails middleware (
secure_headersinapi/guardrails.py): rejects state-changing requests whoseOriginis not the API itself or an allowed origin; applies a fixed-window rate limit per client IP (30/minute on/api/auth/*, 600/minute elsewhere; in memory on Render, in therate_windowstable otherwise); rejects bodies over 13 MB;/api/healthskips the limiter so the health check never touches the database. -
Authentication (
require_userinapi/security.py): looks up the SHA-256 of the token insessions, checks expiry (14 days), and for cookie-authenticated writes requires theX-Requested-Withheader as a CSRF check. -
Authorization (
own_patient/own_resource): the profile must belong to the user or be granted throughcaregiver_access; read-only grants are refused on writes; unknown and foreign ids both return 404. - Validation. Pydantic models bound every field (lengths, enumerations, timezone names, dates not in the future, HH:MM schedules).
-
Persistence. SQLAlchemy writes rows;
EncryptedTextcolumns are Fernet-encrypted transparently; anauditrow records the action; file bytes are encrypted beforewrite_document(). -
Response. JSON without password hashes, token hashes, storage names or ciphertext (
as_jsondrops them); the middleware addsCache-Control: no-store, a strict Content-Security-Policy,X-Frame-Options: DENYand, in production, HSTS. Storage failures map to 404/503 without object names (install_handlersinapi/cloud.py).
Importing api/main.py runs, in order: upgrade() (Alembic to head under a file lock; an encrypted SQLite snapshot is taken first when an existing database is behind), harden_database() (PostgreSQL only: RLS on every table and Supabase client roles revoked; production refuses to start if this fails), prepare_storage() (creates the private bucket, fails closed if it is public) and then mounts the routers and, last, the static web/ directory.
The default configuration needs no cloud service at all:
| Concern | Hosted mode | Local mode |
|---|---|---|
| Database | Supabase PostgreSQL via DATABASE_URL
|
SQLite at BABYCARE_DATA_DIR/babycare.sqlite3 (default when DATABASE_URL is unset) |
| Originals |
BABYCARE_STORAGE=supabase private bucket |
BABYCARE_STORAGE=local, files in encrypted_files/ with mode 0600 |
| Key |
BABYCARE_FERNET_KEY (required in production) |
file_crypt.key generated once; refuses to generate a new key if encrypted data already exists |
| Web app | Firebase Hosting, API on another origin | Served by the API itself from web/, same origin |
| Reminders | pg_cron wakes the API every 10 minutes |
worker service in docker-compose.yml loops every 15 seconds |
Production refuses BABYCARE_STORAGE=local unless BABYCARE_ALLOW_LOCAL_STORAGE=true is set for a host with a persistent volume (api/config.py).
- The Supabase service-role key exists only on the API host; neither client ever talks to Supabase directly.
- Database tables have RLS enabled and no grants for the
anonandauthenticatedroles; the bucket has zero object policies. - Push and email bodies never contain names, diagnoses, medicines or appointment details.
- The camera worker authenticates with a per-camera worker key and sends derived events, never video.
- OCR extracted ≠ clinically verified: every transcription is stored as
needs_review, whichever engine produced it. - AI never prescribes or changes doses; the optional vision provider only transcribes, and its output is reviewed like OCR.
- A notification/reminder is never proof a dose was given.
api/main.py, api/guardrails.py, api/security.py, api/config.py, api/database.py, api/storage.py, api/cloud.py, api/worker.py, api/mailer.py, api/push_web.py, api/providers.py, api/camera_worker.py, web/app.js, mobile/src/config.ts.
Deployment Architecture · Backend Architecture · Web Application Architecture · Android Application Architecture · Encryption and Private Storage · API Design
Overview
Platform
Identity and access
Medical records
- 10 Document Ingestion
- 11 OCR and Human Verification
- 13 Medicines and Administration
- 14 Vaccination Model
- 15 Labs, Growth, Longitudinal
- 16 Illness and Lifetime Health
Daily care
Output and sharing
Security
Engineering
- 26 Testing and Validation
- 27 Deployment and CI/CD
- 28 Technology Stack
- 29 Design Decisions
- 30 Limitations
- 31 Roadmap