580 lines
36 KiB
Markdown
580 lines
36 KiB
Markdown
# Architectural Specification: GLP-1 Companion Platform
|
|
|
|
**Document Version:** 1.0.0
|
|
**Target Runtimes:** iOS, Android, Web (Wasm/CanvasKit), macOS, Windows
|
|
**Framework:** Flutter 3.x (Dart 3.x)
|
|
**Security & Compliance Level:** HIPAA & GDPR-Ready Tier 1 Health Application
|
|
|
|
---
|
|
|
|
## 1. System Vision & Domain Boundaries
|
|
|
|
The GLP-1 Companion is designed to address the clinical and behavioral realities of GLP-1/GIP receptor agonist pharmacotherapy (Semaglutide, Tirzepatide, Liraglutide). The architecture is structured around five core pillars:
|
|
|
|
1. **Pharmacokinetics & Dose Logistics:** Modeling dosage curves, biological half-lives, titrations, and physical injection site rotation.
|
|
2. **Lean Mass Preservation:** Monitoring protein intake targets and hydration volumes to mitigate muscle atrophy and kidney/GI adverse events.
|
|
3. **Recipe Scraping & Pantry Ingestion:** De-duplicating grocery overhead via automated microdata extraction and local inventory delta checks.
|
|
4. **Decoupled Biometric Ingestion:** Unifying OS-native health ecosystems (HealthKit, Health Connect) with cloud APIs (MyFitnessPal, Withings) across mobile, desktop, and web.
|
|
5. **Zero-Knowledge / High-Privacy Local Persistence:** End-to-end encrypted local databases (SQLCipher) paired with passwordless federated authentication.
|
|
|
|
---
|
|
|
|
## 2. Architectural Paradigm: Feature-First Clean Architecture
|
|
|
|
The platform rejects monolithic and layered-by-technical-type layouts in favor of **Bounded Contexts (Feature-First Clean Architecture)**. Each business domain operates as an autonomous module.
|
|
|
|
```text
|
|
lib/
|
|
├── app/ # Top-level application bootstrap
|
|
│ ├── app.dart # Multiplatform root widget
|
|
│ ├── observers/ # Routing & state lifecycle observers
|
|
│ └── router/ # GoRouter route declarations & redirect guards
|
|
├── core/ # Shared foundation & cross-cutting concerns
|
|
│ ├── error/ # Failures, exceptions, and error boundary handlers
|
|
│ ├── network/ # HTTP/REST clients, retry policies, TLS pinning
|
|
│ ├── platform/ # Abstract hardware & OS platform wrappers
|
|
│ ├── security/ # SQLCipher key generation, biometric locks, keychain
|
|
│ ├── theme/ # Dynamic Material 3 & Apple Cupertino theme engines
|
|
│ └── utils/ # DateTime calculations, pharmacokinetics math, formatters
|
|
└── features/ # Modular business domains
|
|
├── auth/ # Passwordless, OAuth SSO, passkey integration
|
|
├── medication/ # Titration tracking, dose logging, injection rotation
|
|
├── nutrition/ # Micro/macro logs, protein density, hydration
|
|
├── recipes/ # Schema.org JSON-LD web scraper, recipe catalog
|
|
├── meal_planner/ # Weekly calendar, portion scaler
|
|
├── pantry/ # Food inventory, expiration date manager
|
|
└── health_sync/ # HealthKit, Health Connect, MFP abstraction
|
|
```
|
|
|
|
### Clean Architecture Layers per Feature
|
|
|
|
Within each feature module in `lib/features/<feature_name>/`, separation of concerns is strictly enforced:
|
|
|
|
```text
|
|
<feature_name>/
|
|
├── domain/ # Pure Dart - ZERO external framework imports
|
|
│ ├── entities/ # Immutable domain business objects
|
|
│ ├── value_objects/ # Validated value types (e.g., DosageMg, ProteinTarget)
|
|
│ ├── repositories/ # Abstract repository interfaces
|
|
│ └── usecases/ # Isolated business operations (single responsibility)
|
|
├── data/ # Infrastructure & data persistence
|
|
│ ├── datasources/
|
|
│ │ ├── local/ # Drift tables, secure key-value stores
|
|
│ │ └── remote/ # Supabase REST, GraphQL, third-party APIs
|
|
│ ├── models/ # Data Transfer Objects (DTOs), JSON serialization
|
|
│ └── repositories/ # Concrete repository implementations
|
|
└── presentation/ # UI & State management
|
|
├── controllers/ # Riverpod AsyncNotifiers / StateNotifiers
|
|
├── screens/ # Responsive top-level views
|
|
└── widgets/ # Reusable components (Adaptive Dialogs, Pickers, Cards)
|
|
```
|
|
|
|
---
|
|
|
|
## 3. Technology Stack & Multiplatform Dependencies
|
|
|
|
| Capability | Selection | Rationale |
|
|
|---|---|---|
|
|
| **Core UI Framework** | Flutter 3.x (Dart 3.x) | Native AOT compilation on iOS, Android, macOS, and Windows; CanvasKit/Wasm rendering on Web. |
|
|
| **State Management** | Riverpod 2.x (`riverpod_generator`) | Compile-time safe dependency injection, automatic provider disposal, testability without `BuildContext`. |
|
|
| **Navigation & Deep Linking** | `go_router` 14.x | Declarative routing, browser URL synchronizer, deep linking for dose alerts and magic links. |
|
|
| **Encrypted Database** | `drift` + `sqlcipher_flutter_libs` | Type-safe reactive SQLite interface with AES-256 transparent database encryption at rest. |
|
|
| **Secure Vault** | `flutter_secure_storage` | OS-native credential storage: Apple Keychain, Android EncryptedSharedPreferences, Windows Credential Manager, Linux Secret Service. |
|
|
| **Backend & Identity** | Supabase (PostgreSQL + RLS + GoTrue) | Built-in passwordless authentication, OAuth provider support, Row-Level Security, Edge Functions. |
|
|
| **Recipe Scraping** | Dart `html` parser + Supabase Edge Node | Client-side parsing of `application/ld+json` Schema.org/Recipe with cloud fallback for bot-protected sites. |
|
|
| **Platform Health Sync** | `health` package (Mobile) + OAuth REST (Web/Desktop) | Unified interface isolating native HealthKit/Health Connect from cross-platform desktop/web code. |
|
|
| **Push Notifications** | `flutter_local_notifications` | Cross-platform offline-scheduled alerts for injections and hydration checks. |
|
|
|
|
---
|
|
## 4. Multiplatform UX/UI Adaptation Engine
|
|
|
|
The UI layer implements an **Adaptive Design Strategy** rather than a lowest common denominator approach:
|
|
|
|
```text
|
|
┌────────────────────────────────────┐
|
|
│ Platform Theme Director │
|
|
└─────────────────┬──────────────────┘
|
|
│
|
|
┌───────────────────────┴───────────────────────┐
|
|
▼ ▼
|
|
[Apple: iOS & macOS] [Material: Android, Web, Win]
|
|
┌─────────────────────────────────┐ ┌─────────────────────────────────┐
|
|
│ Cupertino Engine │ │ Material 3 Engine │
|
|
│ • CupertinoNavigationBar │ │ • NavigationRail / NavigationBar│
|
|
│ • CupertinoSliverRefreshControl │ │ • FloatingActionButtons │
|
|
│ • SF Pro System Typography │ │ • Roboto / Adaptive Font Scaler │
|
|
│ • Haptic Feedback & Swipe-Back │ │ • Dynamic Color / ColorScheme │
|
|
└─────────────────────────────────┘ └─────────────────────────────────┘
|
|
```
|
|
|
|
### Breakpoint Matrix
|
|
|
|
- **Compact (< 600dp - Mobile):** Bottom `CupertinoTabBar` or `NavigationBar`. Full-screen sheets for dose logging.
|
|
- **Medium (600dp - 1024dp - Foldables / Tablets):** Persistent navigation rail, split-view list/detail pane for recipe browsing and pantry audits.
|
|
- **Expanded (> 1024dp - Desktop / Web):** Multi-column dashboard, persistent sidebar navigation rail, keyboard shortcuts (e.g., `Cmd/Ctrl + N` for quick food logging), modal popovers rather than full-screen sheets.
|
|
|
|
---
|
|
|
|
### 4.7 Phase 3B: Platform Health Integrations & HealthGateway Architecture
|
|
|
|
#### 4.7.1 System Overview
|
|
The `HealthGateway` subsystem provides an abstracted, platform-agnostic bridge between application domains (Nutrition, Biometrics, Exercise) and external health telemetry ecosystems. It decouples high-level clinical metric analysis from platform-specific APIs (Apple HealthKit, Google Health Connect, Samsung Health) and external enterprise standards (SMART on FHIR), ensuring full cross-platform stability across iOS, Android, macOS, Windows, and Web.
|
|
|
|
```text
|
|
┌──────────────────────────────────┐
|
|
│ IHealthGateway (Core) │
|
|
└─────────────────┬────────────────┘
|
|
│
|
|
┌─────────────────────────┬───────────────┴────────────────┬────────────────────────┐
|
|
│ (iOS / macOS) │ (Android) │ (Cloud / Enterprise) │ (Windows / Web)
|
|
▼ ▼ ▼ ▼
|
|
┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐
|
|
│ Apple HealthKit │ │ Health Connect │ │ SMART on FHIR │ │ Local Fallback │
|
|
│ - Weight (lbs) │ │ & Samsung Health│ │ OAuth2 / REST │ │ - Manual inputs │
|
|
│ - Active Energy │ │ - Weight (lbs) │ │ - HbA1c panels │ │ - Local SQLite │
|
|
│ - Blood Glucose │ │ - Active Burn │ │ - Lipid markers │ │ persistence │
|
|
└──────────────────┘ └──────────────────┘ └──────────────────┘ └──────────────────┘
|
|
```
|
|
|
|
#### 4.7.2 Core Gateway Contracts & Data Payloads
|
|
|
|
```dart
|
|
// lib/core/platform/health_gateway.dart
|
|
|
|
import 'dart:io';
|
|
import 'package:flutter/foundation.dart';
|
|
|
|
enum HealthProviderType {
|
|
appleHealth,
|
|
healthConnect,
|
|
samsungHealth,
|
|
enterpriseFhir,
|
|
manualFallback,
|
|
}
|
|
|
|
enum HealthConnectionStatus {
|
|
notConfigured,
|
|
permissionDenied,
|
|
connected,
|
|
unsupportedPlatform,
|
|
}
|
|
|
|
class HealthDataPayload {
|
|
final double? weightLbs;
|
|
final double? activeCaloriesToday;
|
|
final double? bloodGlucoseMgDl;
|
|
final DateTime timestamp;
|
|
final HealthProviderType source;
|
|
|
|
const HealthDataPayload({
|
|
this.weightLbs,
|
|
this.activeCaloriesToday,
|
|
this.bloodGlucoseMgDl,
|
|
required this.timestamp,
|
|
required this.source,
|
|
});
|
|
}
|
|
|
|
abstract class IHealthGateway {
|
|
/// Verifies current OS entitlement and read permissions.
|
|
Future<HealthConnectionStatus> checkStatus(HealthProviderType provider);
|
|
|
|
/// Triggers native OS permission request prompt.
|
|
Future<bool> requestAuthorization(HealthProviderType provider);
|
|
|
|
/// Queries biometrics aggregated across the current calendar day.
|
|
Future<HealthDataPayload?> fetchLatestBiometrics();
|
|
|
|
/// Revokes active session bindings or tears down local connections.
|
|
Future<void> disconnect(HealthProviderType provider);
|
|
}
|
|
```
|
|
|
|
#### 4.7.3 Platform Support & Fallback Strategy
|
|
|
|
| Target Platform | Health Provider Bridge | Mechanism / Fallback |
|
|
|---|---|---|
|
|
| **iOS** | Apple HealthKit | Native `package:health` querying `HKQuantityTypeIdentifierBodyMass`, `HKQuantityTypeIdentifierActiveEnergyBurned`, `HKQuantityTypeIdentifierBloodGlucose`. |
|
|
| **Android** | Health Connect | `androidx.health.connect.client` routing both Google Pixel and Samsung Health sensor feeds via Health Connect runtime. |
|
|
| **Windows / macOS** | Local Mock / Manual DB Driver | Native SDKs bypassed via `kIsWeb || (!Platform.isIOS && !Platform.isAndroid)` compile-time and runtime guards. Biometrics route to local Drift SQLCipher storage without crash exceptions. |
|
|
| **Web** | Web Storage Fallback | Direct Supabase cloud-synced biometrics and manual modal logging; SDK initialization omitted. |
|
|
| **Enterprise Cloud** | SMART on FHIR | Headless OAuth2 code grant returning standardized HL7 FHIR `Observation` bundles (HbA1c, metabolic panels, weight). |
|
|
|
|
#### 4.7.4 Native Entitlements & Manifest Configurations
|
|
|
|
##### Android (`android/app/src/main/AndroidManifest.xml`)
|
|
```xml
|
|
<!-- Declare Health Connect integration and permissions -->
|
|
<manifest xmlns:android="[http://schemas.android.com/apk/res/android](http://schemas.android.com/apk/res/android)">
|
|
<queries>
|
|
<package android:name="com.google.android.apps.healthdata" />
|
|
<intent>
|
|
<action android:name="androidx.health.ACTION_SHOW_PERMISSIONS_RATIONALE" />
|
|
</intent>
|
|
</queries>
|
|
|
|
<uses-permission android:name="android.permission.health.READ_WEIGHT"/>
|
|
<uses-permission android:name="android.permission.health.READ_ACTIVE_CALORIES_BURNED"/>
|
|
<uses-permission android:name="android.permission.health.READ_BLOOD_GLUCOSE"/>
|
|
|
|
<application>
|
|
<!-- Activity-alias for Health Connect permissions rationale -->
|
|
<activity-alias
|
|
android:name="ViewPermissionUsageActivity"
|
|
android:targetActivity=".MainActivity"
|
|
android:exported="true"
|
|
android:permission="android.permission.START_VIEW_PERMISSION_USAGE">
|
|
<intent-filter>
|
|
<action android:name="android.intent.action.VIEW_PERMISSION_USAGE" />
|
|
<category android:name="android.intent.category.HEALTH_PERMISSIONS" />
|
|
</intent-filter>
|
|
</activity-alias>
|
|
</application>
|
|
</manifest>
|
|
```
|
|
|
|
##### iOS (`ios/Runner/Info.plist`)
|
|
```xml
|
|
<key>NSHealthShareUsageDescription</key>
|
|
<string>GLP-1 Companion uses read-only HealthKit data to track lean body mass trends, active caloric expenditure, and glycemic markers alongside your dosing schedule.</string>
|
|
<key>NSHealthUpdateUsageDescription</key>
|
|
<string>GLP-1 Companion records weight and dose timings into Apple Health to keep clinical history synchronized.</string>
|
|
```
|
|
|
|
#### 4.7.5 Security, Authorization & Key Management
|
|
1. **Zero Raw Token Exposure:** Enterprise SMART on FHIR refresh and access tokens are held exclusively in hardware-backed storage (`flutter_secure_storage` utilizing Android Keystore and Apple Keychain).
|
|
2. **Read-Only Minimal Principle:** Scopes requested from HealthKit and Health Connect are strictly restricted to read permissions for weight, blood glucose, and active burn to maximize user trust and prevent third-party telemetry leakage.
|
|
3. **Local Encryption at Rest:** Any synced telemetry data cached locally is stored inside the SQLCipher-encrypted SQLite database (`glp1_encrypted.db`), keyed by the hardware-derived master key managed by `CryptoManager`.
|
|
|
|
### 4.8 Phase 3C: Nutritional Search Engine, Barcode Scanning & Dual-Source Food Cache
|
|
|
|
#### 4.8.1 Subsystem Overview
|
|
The Food Ingestion subsystem bridges cloud product databases with encrypted local storage (`FoodProductsTable` in Drift SQLCipher). To defend against rate-limiting, missing products, and non-profit server downtime (e.g., Open Food Facts 503 limits), the ingestion engine implements a deterministic multi-tier resolution ladder:
|
|
1. **L1 Local Cache:** Encrypted SQLite query by exact barcode or tokenized substring.
|
|
2. **L2 Packaged Foods Primary (Open Food Facts):** REST API v3 queries identifying branded packaged items with a strict custom `User-Agent` and a 4-second timeout.
|
|
3. **L3 Whole & Unbranded Foods Fallback (USDA FoodData Central):** Automated fallback querying Foundation, SR Legacy, and Survey databases for generic items, unbranded raw foods, or failed L2 lookups.
|
|
4. **L4 Manual Ingestion:** Client-side fallback modal allowing user-defined product creation and instant SQLCipher persistence.
|
|
|
|
```text
|
|
┌────────────────────────┐ ┌────────────────────────┐
|
|
│ Text Query Search │ │ Mobile Barcode Scan │
|
|
│ ("string cheesestick") │ │ (EAN-13, UPC-A) │
|
|
└───────────┬────────────┘ └───────────┬────────────┘
|
|
│ │
|
|
▼ ▼
|
|
┌───────────────────────────────────────────────────────────┐
|
|
│ IFoodSearchRepository │
|
|
└─────────────────────────────┬─────────────────────────────┘
|
|
│
|
|
┌─────────────────────────┼─────────────────────────┐
|
|
▼ (1. Check Cache) ▼ (2. Primary Query) ▼ (3. Auto Fallback)
|
|
┌─────────────────────┐ ┌─────────────────────┐ ┌─────────────────────┐
|
|
│ Local Drift Cache │ │ Open Food Facts API │ │ USDA FoodData API │
|
|
│ (SQLCipher AES-256) │ │ (Branded / Barcode) │ │ (Whole/Raw/Fallback)│
|
|
└──────────┬──────────┘ └──────────┬──────────┘ └──────────┬──────────┘
|
|
│ (Hit) │ (Success) │ (Success)
|
|
│ └────────────┬─────────────┘
|
|
│ │ (Write Through Cache)
|
|
▼ ▼
|
|
┌───────────────────────────────────────────────────────────────────────────┐
|
|
│ Normalized FoodProduct │
|
|
│ (UPC, Name, Brand, Serving, Calories, Protein, Carbs, Fat, Fiber) │
|
|
└─────────────────────────────────────┬─────────────────────────────────────┘
|
|
│
|
|
┌──────────────────────────┴──────────────────────────┐
|
|
▼ ▼
|
|
┌──────────────────────────────────────┐ ┌──────────────────────────────────────┐
|
|
│ Log to Daily Nutrition │ │ Add to Pantry Inventory │
|
|
│ (Inserts into FoodLogsTable) │ │ (Inserts into PantryItemsTable) │
|
|
└──────────────────────────────────────┘ └──────────────────────────────────────┘
|
|
```
|
|
|
|
#### 4.8.2 Domain Model & Contracts
|
|
|
|
```dart
|
|
// lib/features/nutrition/domain/entities/food_product.dart
|
|
|
|
class FoodProduct {
|
|
final String id;
|
|
final String? barcode;
|
|
final String productName;
|
|
final String? brand;
|
|
final double servingSizeGrams;
|
|
final String servingSizeUnit;
|
|
final double calories;
|
|
final double proteinGrams;
|
|
final double carbsGrams;
|
|
final double fatGrams;
|
|
final double? fiberGrams;
|
|
final double? sodiumMg;
|
|
final String? imageUrl;
|
|
final DateTime lastUpdated;
|
|
|
|
const FoodProduct({
|
|
required this.id,
|
|
this.barcode,
|
|
required this.productName,
|
|
this.brand,
|
|
required this.servingSizeGrams,
|
|
this.servingSizeUnit = 'g',
|
|
required this.calories,
|
|
required this.proteinGrams,
|
|
required this.carbsGrams,
|
|
required this.fatGrams,
|
|
this.fiberGrams,
|
|
this.sodiumMg,
|
|
this.imageUrl,
|
|
required this.lastUpdated,
|
|
});
|
|
}
|
|
```
|
|
|
|
#### 4.8.3 Persistence Schema (`FoodProductsTable`)
|
|
|
|
```dart
|
|
// lib/core/database/tables/food_products_table.dart
|
|
|
|
import 'package:drift/drift.dart';
|
|
|
|
class FoodProductsTable extends Table {
|
|
TextColumn get id => text()(); // UPC barcode or prefixed UUID ('usda_12345')
|
|
TextColumn get barcode => text().nullable()();
|
|
TextColumn get productName => text()();
|
|
TextColumn get brand => text().nullable()();
|
|
RealColumn get servingSizeGrams => real().withDefault(const Constant(100.0))();
|
|
TextColumn get servingSizeUnit => text().withDefault(const Constant('g'))();
|
|
RealColumn get calories => real()();
|
|
RealColumn get proteinGrams => real()();
|
|
RealColumn get carbsGrams => real()();
|
|
RealColumn get fatGrams => real()();
|
|
RealColumn get fiberGrams => real().nullable()();
|
|
RealColumn get sodiumMg => real().nullable()();
|
|
TextColumn get imageUrl => text().nullable()();
|
|
DateTimeColumn get lastUpdated => dateTime()();
|
|
|
|
@override
|
|
Set<Column> get primaryKey => {id};
|
|
}
|
|
```
|
|
|
|
#### 4.8.4 Data Source Specifications & Nutrient Mapping
|
|
|
|
| Nutrient Metric | Open Food Facts Nutriments Key | USDA FoodData Central Nutrient ID | Normalization Rule |
|
|
|---|---|---|---|
|
|
| **Energy (Calories)** | `energy-kcal_serving` / `energy-kcal_100g` | `1008` (Energy) | Expressed as `kcal` (double). |
|
|
| **Protein** | `proteins_serving` / `proteins_100g` | `1003` (Protein) | Grams per serving (double). |
|
|
| **Carbohydrates** | `carbohydrates_serving` / `carbohydrates_100g` | `1005` (Carbohydrate, by difference) | Grams per serving (double). |
|
|
| **Total Fat** | `fat_serving` / `fat_100g` | `1004` (Total lipid) | Grams per serving (double). |
|
|
| **Dietary Fiber** | `fiber_serving` / `fiber_100g` | `1079` (Fiber, total dietary) | Grams per serving; nullable. |
|
|
| **Sodium** | `sodium_serving` / `sodium_100g` | `1093` (Sodium, Na) | Converted to `mg` (OFF returns grams; multiply by 1000). |
|
|
|
|
#### 4.8.5 Network & Resiliency Rules
|
|
1. **Mandatory User-Agent:** All HTTP requests to Open Food Facts must include:
|
|
```text
|
|
User-Agent: GLP1Companion-App/1.0 (contact: mtkell@kellengineering.com; platform: Flutter)
|
|
```
|
|
Anonymous or default CLI user-agents will trigger upstream proxy 503 throttling.
|
|
2. **Circuit Timeout:** Remote calls to Open Food Facts enforce a 4-second timeout. Upon timeout or HTTP 5xx error, the client transparently routes the query to `UsdaFdcClient`.
|
|
3. **Write-Through Local Caching:** Every successfully resolved remote payload from OFF or USDA FDC is written into `FoodProductsTable` with `insertOnConflictUpdate`. Subsequent lookups for identical barcodes or query tokens resolve locally with zero network I/O.
|
|
|
|
### 4.9 Phase 4B: Private Recipe Integration (Mealie REST API Client)
|
|
|
|
#### 4.9.1 Subsystem Overview
|
|
The Private Recipe Provider subsystem allows connecting authenticated, self-hosted recipe platforms (initially Mealie v1.x) to ingest recipes into the encrypted local SQLite database.
|
|
|
|
```text
|
|
┌────────────────────────────────────────────────────────┐
|
|
│ Settings Screen │
|
|
│ - Base URL: [https://mealie.kellfamilyhome.com](https://mealie.kellfamilyhome.com) │
|
|
│ - API Token: Bearer <Encrypted Hardware Keystore> │
|
|
└───────────────────────────┬────────────────────────────┘
|
|
│
|
|
▼
|
|
┌────────────────────────────────────────────────────────┐
|
|
│ MealieApiClient │
|
|
│ - GET /api/recipes (Paginated summary & slug) │
|
|
│ - GET /api/recipes/{slug} (Full ingredients/macros) │
|
|
└───────────────────────────┬────────────────────────────┘
|
|
│
|
|
▼
|
|
┌────────────────────────────────────────────────────────┐
|
|
│ Normalized Recipe Domain Entity │
|
|
│ - Nutrition: calories, protein, carbs, fat │
|
|
│ - Source tag: 'mealie' │
|
|
└───────────────────────────┬────────────────────────────┘
|
|
│
|
|
▼
|
|
┌────────────────────────────────────────────────────────┐
|
|
│ RecipesTable (Drift SQLCipher) │
|
|
│ Appears natively in /recipes tab │
|
|
└────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
#### 4.9.2 Data Contracts & Mealie Normalization
|
|
- **Endpoint Structure:**
|
|
* Listing: `GET {baseUrl}/api/recipes?perPage=-1` returns all user recipes.
|
|
* Details: `GET {baseUrl}/api/recipes/{slug}` returns full structured steps, ingredient notes, and `nutrition` object (`calories`, `proteinContent`, `carbohydrateContent`, `fatContent`).
|
|
* Image Asset: `{baseUrl}/api/media/recipes/{recipeId}/images/min-original.webp`
|
|
- **Security:**
|
|
* Server Base URL is stored in Drift settings/preferences.
|
|
* API Token is persisted exclusively in `FlutterSecureStorage` (Android Keystore / Apple Keychain / Windows DPAPI).
|
|
|
|
|
|
|
|
## 5. Security & Passwordless Authentication Architecture
|
|
|
|
Password-based login is explicitly deprecated to eliminate credential-stuffing vectors and streamline onboarding.
|
|
|
|
```text
|
|
User Device Supabase Auth Gateway OAuth Identity Provider
|
|
│ │ │
|
|
├─── Selects Provider ───────────┼───────────────────────────────────>│ (Apple / Google / MS)
|
|
│ (Apple/Google/Microsoft) │ │
|
|
│<── Returns Auth Token/Nonce ───┼────────────────────────────────────┤
|
|
│ │ │
|
|
├─── Exchange Nonce / Verify ───>│ │
|
|
│<── Issue JWT + Refresh Token ──┤ │
|
|
│ │ │
|
|
│─── Or Request Magic Link ─────>│ │
|
|
│ (Email OTP / Deep Link) ├───── Dispatches Secure Link ──────>│ (User Email Box)
|
|
│<── Deep Link Activated ────────┤ │
|
|
│ │ │
|
|
└─── Store Session in Keychain ──┴────────────────────────────────────┘
|
|
```
|
|
|
|
### Zero-Knowledge Local Storage Scheme
|
|
|
|
Local encryption relies on a three-tier lifecycle:
|
|
|
|
1. On initial app bootstrap, a cryptographic salt and 256-bit AES master key are generated via `core/security/crypto_manager.dart`.
|
|
2. The master key is committed directly to the platform hardware vault.
|
|
3. The SQLCipher SQLite instance initializes using this hardware-backed key.
|
|
|
|
Platform vault targets:
|
|
|
|
- **iOS / macOS:** Apple Keychain Services (`kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly`)
|
|
- **Android:** Android KeyStore backing `EncryptedSharedPreferences` (AES-256 GCM)
|
|
- **Windows:** Windows Data Protection API (DPAPI / Credential Manager)
|
|
- **Web:** Ephemeral in-memory key unlocked by biometric WebAuthn / session credentials
|
|
|
|
SQLCipher database initialization parameters:
|
|
|
|
```sql
|
|
PRAGMA key = 'x2C5D8F91B4E6A30C8D7E1F2A5B8C0D3E...';
|
|
PRAGMA cipher_page_size = 4096;
|
|
PRAGMA kdf_iter = 256000;
|
|
PRAGMA cipher_hmac_algorithm = HMAC_SHA512;
|
|
```
|
|
|
|
---
|
|
|
|
## 6. Pharmacokinetics & GLP-1 Tracking Engine
|
|
|
|
The `medication` feature models drug serum concentration to deliver realistic physiological insights rather than simple calendar alarms.
|
|
|
|
### Half-Life Modeling Formulation
|
|
|
|
Active serum levels are calculated using standard single-compartment open pharmacokinetic decay curves:
|
|
|
|
$$C(t) = \sum_{i=1}^{n} D_i \cdot \left(\frac{1}{2}\right)^{\frac{t - t_i}{t_{1/2}}}$$
|
|
|
|
Where:
|
|
- $D_i$ = Dose administered at timestamp $t_i$ (mg)
|
|
- $t_{1/2}$ = Elimination half-life of compound:
|
|
- **Semaglutide (Ozempic / Wegovy):** $\approx 168\text{ hours (7 days)}$
|
|
- **Tirzepatide (Mounjaro / Zepbound):** $\approx 120\text{ hours (5 days)}$
|
|
- **Liraglutide (Saxenda):** $\approx 13\text{ hours}$
|
|
- $t$ = Current evaluation timestamp
|
|
|
|
### Injection Site Rotation Matrix
|
|
|
|
To prevent lipohypertrophy, injection events map to a 12-quadrant anatomical topological matrix:
|
|
- **Abdomen:** Right Upper, Right Lower, Left Upper, Left Lower
|
|
- **Thighs:** Right Anterior/Lateral, Left Anterior/Lateral
|
|
- **Upper Arms:** Right Tricep/Lateral, Left Tricep/Lateral
|
|
|
|
Rotation alerts automatically flag and suggest the least recently utilized site.
|
|
|
|
---
|
|
|
|
## 7. Recipe Parsing & Smart Pantry Delta Engine
|
|
|
|
```text
|
|
┌────────────────────────────────────────────────────────┐
|
|
│ Web Scraping Pipeline (Recipes) │
|
|
└───────────────────────────┬────────────────────────────┘
|
|
│
|
|
1. Fetch HTML Document ───────┴───────> 2. Query <script type="application/ld+json">
|
|
│
|
|
Found Valid Schema.org/Recipe?
|
|
├── YES ──> Parse Ingredients & Macros
|
|
└── NO ──> Trigger Edge LLM Regex Fallback
|
|
│
|
|
▼
|
|
[Parsed Recipe Entity]
|
|
- Title & Yield
|
|
- Ingredient List & Units
|
|
- Protein / Carb / Fat Counts
|
|
│
|
|
▼
|
|
┌────────────────────────────────────────────────────────────────────────────────────────────────┐
|
|
│ Smart Pantry Delta Generator │
|
|
│ │
|
|
│ [Weekly Meal Plan] [Active Pantry Inventory] [Shopping List Engine] │
|
|
│ • 800g Chicken Breast ─── Compare ──> • 300g Chicken Breast ──────> • Add 500g Chicken │
|
|
│ • 4 Avocados ─── Compare ──> • 4 Avocados ──────> • Item Satisfied (0) │
|
|
│ • 100g Greek Yogurt ─── Compare ──> • Empty ──────> • Add 100g Greek Yog. │
|
|
└────────────────────────────────────────────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
---
|
|
|
|
## 8. Cross-Platform Health Data Integration (`HealthGateway`)
|
|
|
|
Cross-platform parity requires an abstract interface with decoupled implementations:
|
|
|
|
```dart
|
|
// lib/core/platform/health_gateway.dart
|
|
abstract class IHealthGateway {
|
|
Future<bool> requestPermissions();
|
|
Future<List<BiometricEntry>> fetchWeightHistory({required DateTime start, required DateTime end});
|
|
Future<List<ExerciseEntry>> fetchWorkouts({required DateTime start, required DateTime end});
|
|
Future<void> pushNutritionalIntake({required NutritionalRecord record});
|
|
Stream<BiometricEntry> get realTimeWeightStream;
|
|
}
|
|
```
|
|
|
|
### Implementation Matrix
|
|
|
|
1. **iOS (`health_gateway_apple.dart`):** Wraps `HealthKit` (`HKQuantityTypeIdentifierBodyMass`, `HKQuantityTypeIdentifierActiveEnergyBurned`).
|
|
2. **Android (`health_gateway_android.dart`):** Wraps `Health Connect API` (`WeightRecord`, `ExerciseSessionRecord`).
|
|
3. **Desktop / Web (`health_gateway_cloud.dart`):** Communicates with cloud partner REST endpoints (MyFitnessPal API, Withings API, Google Fit REST) via OAuth token grants, falling back to manual entry dialogs.
|
|
|
|
---
|
|
|
|
## 9. Error Handling, Telemetry & Extensibility Architecture
|
|
|
|
### Functional Error Handling
|
|
|
|
The codebase bans throwing unhandled raw exceptions across layer boundaries. All use-cases return a functional `Result<T, Failure>` union pattern (`fpdart` / `freezed`):
|
|
|
|
```dart
|
|
typedef AsyncResult<T> = Future<Either<Failure, T>>;
|
|
```
|
|
|
|
### Modular Plugin Registration (Open-Closed Principle)
|
|
|
|
To support post-launch capabilities (e.g., continuous glucose monitor [CGM] Bluetooth LE integration, Telehealth lab panels), the app exposes a modular plugin registry:
|
|
|
|
```dart
|
|
abstract class ICompanionPlugin {
|
|
String get pluginId;
|
|
String get displayName;
|
|
Future<void> initialize(ProviderContainer container);
|
|
List<RouteBase> get pluginRoutes;
|
|
Widget buildDashboardWidget(BuildContext context);
|
|
}
|
|
```
|
|
|
|
Plugins declare isolated database tables, register their own routes into `GoRouter`, and inject dashboard summary tiles without altering the core application codebase.
|