Architecture
Stack decisions, AI pipeline routing, cost model, and iOS PWA constraints.
# Stack Decisions
| Layer | Choice | Rationale |
|---|---|---|
| Frontend | SvelteKit | Lightweight, SSR+SPA, good offline story for write-heavy PWA |
| Local DB | Dexie (IndexedDB) | Offline-first, sync-ready, append-heavy single-author pattern |
| AI Runtime | ONNX Runtime Web | On-device inference via WebAssembly, WebGPU fallback |
| Object Storage | Cloudflare R2 | S3-compatible, zero egress fees ($150/mo vs $4,710/mo Supabase at 10K MAU) |
| Backend | Supabase | Postgres + PostGIS + pgvector + Auth + Realtime + Storage |
| Maps | MapLibre GL JS | Open-source, self-hostable, pmtiles on R2 for offline tiles |
# AI Identification Pipeline
User uploads media
|
v
Client-side EXIF extraction (exifr) --> GPS, timestamp, device
|
v
Pre-ID quality check --> blur / dark warning
|
|--- Plant detected? --------> PlantNet Pro API (78K species)
|
|--- Bird sound? ------------> BirdNET (commercial license)
|
+--- General image/video ---> Gemini 2.5 Flash-Lite (~$0.001)
|
+-- conf >= 0.8 --> Result
|
+-- conf < 0.8 --> Claude Haiku 4.5 (~$0.01)
|
+-- conf >= 0.7 --> Result
|
+-- Low conf --> Expert queue
Offline fallback:
EfficientNet-Lite0 INT8 ONNX (<3MB, top 500 species)
+ Regional packs (Oaxaca/Yucatan, 10-30MB, lazy-loaded into IndexedDB)
# Cost Model
| Scale | Monthly Cost | Per User |
|---|---|---|
| 10K MAU | ~$2,000 | $0.20 |
| 50K MAU | ~$6,000 | $0.12 |
| 100K MAU | ~$11,500 | $0.12 |
10K MAU breakdown: AI inference $800, object storage $400, compute $500, monitoring and misc $300. Includes Supabase Pro, Cloudflare R2, ~500K Haiku calls, 600K PlantNet Pro calls, and Gemini Flash-Lite first-pass.
# Data Model
Core tables in the Supabase Postgres schema, partitioned monthly via pg_partman. PostGIS geography types for spatial queries, pgvector for semantic species search.
| Table | Purpose | Key Fields |
|---|---|---|
| users | User accounts and profiles | id, display_name, role, language_pref |
| observations | Core observation records with location and environment | id, observer_id, observed_at, location (PostGIS), moon_phase, precipitation |
| species | Taxonomic reference with NOM-059 categories | id, scientific_name, common_names (jsonb), nom_059_category |
| media_files | Photos, audio, video with EXIF metadata | id, observation_id, media_type, r2_key, exif_data (jsonb) |
| identifications | AI and human ID results with confidence scores | id, observation_id, taxon_id, confidence, source, is_research_grade |
| expert_validations | Expert review with taxonomic badge verification | id, identification_id, validator_id, verdict, notes |