Skip to content
draft Visibility internal Owner erik@uvilo.com Approver _ Created _ Updated _

Metrics Research

Research supporting (a) the proposed Metrics chapter of The Uvilo Method (Metrics_Method_Chapter.md) and (b) the upcoming implementation plan for uvilo-ai. Sources: the legacy Meteor implementation (/Users/erik/Dev/Uvilo), the uvilo-mono codebase, and web research. All prices, coverage claims, and URLs were verified 2026-07-29 unless noted; wearable-ecosystem facts move fast (several changed May–July 2026), so re-verify before contract signatures.

Headline decisions (settled by Erik, 2026-07-29)

  1. Wearable sync: phone-as-hub, no aggregator. Read Apple Health (iOS) and Health Connect (Android) through the @capgo/capacitor-health plugin in the existing Capacitor 8 shells — $0 per user, covers Apple Watch, Oura, Whoop, Garmin, Withings, Polar, Amazfit, Coros. Native background sync ships in the initial build (iOS HealthKit background-delivery observer + Android WorkManager), not deferred. If an aggregator is ever adopted it is ROOK ($399/mo), not Terra (~$499/mo entry, ~$5,000/mo at 5k users).
  2. Charts: Apache ECharts 6 — chosen after an interactive side-by-side demo against MUI X Charts Pro (same data, same gestures). Free (Apache-2.0), best-in-class mobile touch and canvas performance, and its mini-map slider is the closest analog to the Highstock navigator. MUI X Charts Pro (already licensed) remains the documented fallback. Highcharts is not re-licensed.
  3. Data model: port the legacy three-collection shape (metric catalog / per-user subscription / values) into ZenStack, fixing the legacy defects listed in §2.4 (no unique keys, string dates, client-side writes, vm code execution).
  4. Connections: one surface for every source. The Composio integration plan ships before Metrics and delivers the Connections tab in user settings. Apple Health, Google Health (Health Connect), and later other health connectors are listed alongside Composio connectors on that same surface — the user never sees which mechanism a connection uses. Oura is the first direct cloud connector, implemented now to scope the effort and the value of direct integrations.
  5. Legacy data: migrate Erik’s own history. Metric data belonging to legacy user hTsXvc9x8eyNNyNfE in the legacy MongoDB is migrated into Neon for both erik@erikdakoda.com (usr_cmflhqg0500009s8obw6jtucx) and erikschannen@gmail.com (usr_cmms0rurm000041uybelkx2bd). The legacy repo itself is not modified in any way.

1. Corrections to the brief

Two features on the keep-list turn out to be aspirations rather than legacy features — both are worth building, but they are net-new designs, not ports:

  • Habit auto-complete was never implemented. The legacy habit editor renders a placeholder: “Auto-complete will allow you to automatically set a task as done, partial or skipped based on metric values… This is not yet implemented.” (packages/uvilo-habits-ui/views/habitEditor.html:262-273). No rule schema, no evaluation code exists. The Uvilo Method §11.3 describes the intended behavior; §6 below proposes the engine.
  • Custom units were never supported. Units are a hard-coded code table (a vendored convert-units fork); the only per-user choice is metric vs US display. What legacy did support is custom metrics (user-authored metric definitions with userId set). The new design should support both (§7).

2. Legacy implementation inventory

Packages: uvilo-metrics (model + server), uvilo-metrics-ui (values table + chart), uvilo-metrics-setup-ui (catalog/setup/editor), uvilo-metric-goal (goal band + slider), plus uvilo-apps (provider framework), uvilo-apps-fitbit, uvilo-apps-jawbone, uvilo-apps-health-kit, convert-units, highstock, uvilo-dashboard (gauges). Last substantive metrics work: 2023-05-26.

2.1 Data model (the part worth porting)

Three Mongo collections (packages/uvilo-metrics/modules/uviloMetrics.js:28-32):

CollectionRoleKey fields
metricsCatalog — metric definitions, global (seeded) or user-authored. Slug _ids (bodyWeight, hkDailySteps).measure (dimension), unit (canonical storage unit), unitUs (US display default), betterValues (higher/lower), goalLow/goalMid (goal band defaults), chartType, decimals, min/max/valueStep, source (manual | calculated | appId), baseMetricId, dependencies + calculation (calculated metrics), group (co-entry list), sourceInfo (per-provider mapping bag), dashboardTimePeriod
userMetricsSubscription + per-user overrides — which metrics a user tracks.userId, metricId, unit (user’s display unit), per-user copies of goal band/bounds, synced (per-user-per-metric sync watermark)
metricValuesSamples.userId, metricId, date (ISO string), value (always stored in the canonical unit), source, sourceId (provider UUID), origin/originName

Design ideas that proved out and should carry forward:

  1. Catalog / subscription / values split — cleanly separates “what a step count is” from “Erik tracks steps with goal 8–10k, last synced at T” from the samples.
  2. Canonical storage unit + display-only conversion (formatMetricValue / parseMetricValue at the edges). All values stored in metric.unit (metric system); user unit preference applied on render/parse only.
  3. baseMetricId provider→canonical mirroringfitbitDailySteps and hkDailySteps both feed dailySteps, so charts/goals/programs target the canonical metric regardless of device. Rare in this category and genuinely good.
  4. sourceInfo mapping bags — adding a provider metric is a data change, not a code change.
  5. Source-scoped re-sync (replaceMetricValues: delete {source, date range} then bulk insert) — manual entries survive provider re-syncs. Plus the synced watermark for resumable sync.
  6. hasData() probe on connect (HealthKit) — only onboard metrics the device actually has data for.
  7. Sync transparency — per-user audit log (UviloLog) + live per-metric value counts in the connect modal. Cheap, high trust payoff.
  8. Metric groups as co-entry — chest/abdomen/thigh skinfolds entered on one row, derived body-fat % computes in the same row. A real workflow, not just taxonomy.
  9. Two-threshold goal band (goalLow/goalMid + betterValues) — maps to red/amber/green gauges and “8k steps is OK, 10k is the goal”.
  10. User-visible backfill horizon (profiles.syncStartMonths, default 3).
  11. Seeded catalog of ~30 metrics: body composition (weight, body fat + 3-point skinfold formulas), 11 girth measurements, steps, energy (basal/active/total), sleep, mindful minutes, back pain, plank test — a ready-made starting library.
  12. A Nightwatch E2E (tests/nightwatch/metrics/enter_calculated_metric.js) that doubles as an acceptance spec: add bodyFat3PointMale, enter weight + three skinfolds, assert the derived value.

2.2 Sync: what legacy actually had

Three providers via a plugin framework (uvilo-apps): Fitbit (OAuth 1.0a — broken as committed: webhook handler references an undefined variable, wrong-arity delete call, missing imports), Jawbone (company died 2017), and Apple HealthKit (Cordova plugin, device-local, the only one that worked). Withings/Endomondo/MyFitnessPal/Wahoo were “vote for this” placeholders. No aggregator was ever used. The working HealthKit path — permission sheet, per-metric day-bucketed sync, change monitoring, watermarks — is precedent for the phone-as-hub strategy in §4.

2.3 Charts: what “pan/zoom” actually was

Vendored Highstock 2.1.5 (2015) StockCharts: the range-selector button bar + navigator brush (no zoomType, no panning config). All values shipped to the client (publication had no date window or bucketing) and re-rendered on any change; app code manually stripped Highstock’s touch handlers so pages could scroll. The feature to preserve is explorable time-series (range presets + brush/zoom + touch pan); the implementation is fully replaceable.

2.4 Explicitly avoid (legacy defects)

  • vm.runInNewContext(metric.calculation, …) — user-editable JS from the DB executed server-side. An RCE; replace with a whitelisted expression evaluator (§6.3).
  • Live OAuth secrets committed in config/settings*.json (Fitbit key/secret, Jawbone id/secret). Rotate/retire; never repeat.
  • No unique constraint on (userId, metricId, date, source) — idempotency by upsert-selector only.
  • ISO date strings with mixed day/instant granularity, lexicographic range queries, and a date + '+' sentinel hack. Use timestamptz + a separate local-day column.
  • Client-side Mongo writes via allow-rules (goal slider, unit select, value edits).
  • Publish-everything charts — no windowing, no rollups; will not survive years of step data.
  • Zero vs missing conflated (!value drops legit zeros; missing coerced to 0) and minus signs stripped in parsing (negative metrics impossible).
  • Dead fields (goalStart/goalEnd/goalDays — declared, never used), dead publications, broken setup-UI handlers, the 428-line jQuery goal slider (which also displayed storage units instead of the user’s units).

3. Wearable-sync landscape (aggregators)

No mainstream aggregator has a free production tier in 2026. Verified pricing:

VendorEntry~500 users~5,000 usersCapacitor SDKViability notes
ROOK (pricing)$399/mo (≤750 active users)$399/mo$999/mo (Core+, ≤5k)Yes — official, current (docs)Pre-seed ($1.7M, 2023); webhook delivery with retries + pending buckets; some providers (Whoop, Strava, Dexcom, Samsung) need your own dev accounts
Junction (ex-Vital; pricing)$0.50/user/mo, $300/mo min; free sandbox ≤50 users$300/mo≲$2,500/mo (sales-gated rate)No (iOS/Android/RN/Flutter)Best-funded: $18M Series A 2025 (TechCrunch); strategic center shifting to lab testing
Terra (pricing, credits)$499/mo ($399 annual), 100k credits; 200 credits/active user/mo~$499/mo~$5,000/mo (overage $0.005/credit)No (iOS/Android/RN/Flutter/Expo)YC W21, only ~$3.3M raised, ~25 staff; high product velocity; priciest at scale
Spike (pricing)“from $450/mo”, rest sales-gated~$450+sales-gatedNo$4.2M seed 2024
Thryve (pricing)€499/mo (≤500 users)€499/mo€1,000/moNoEU/insurer-focused; Scale tier caps backfill at 14 days
Open Wearables (GitHub)$0 — MIT, self-hosted (~$20–50/mo VPS)~$0~$0No (OSS native/RN/Flutter SDKs)Pre-1.0 (Dec 2025 launch, 2.2k stars); no Withings, no CGMs; you own provider apps + breakage
Validicsales-gated enterpriseIncumbent, enterprise motion; wrong fit
Human APIExited: acquired by LexisNexis 2023, now insurance underwriting
Metriport devicesDiscontinued; pivoted to medical records. Do not build on it
WeFittersales-gatedDevelopment reportedly ceased ~2023; too risky

Independent market survey corroborating the above: Health API Guy, 2026-05-21.

Key structural fact: every aggregator still requires embedding their mobile SDK to get Apple Health / Health Connect data — an aggregator does not spare us mobile-side work; it spares us cloud-provider OAuth plumbing and normalization. Apple Health has no cloud API at all; the phone is the only exit.

4. Phone-as-hub: the no-aggregator path

4.1 Coverage (2026)

Most wearable vendors sync into the platform health stores:

Brand→ Apple Health (iOS)→ Health Connect (Android)Own free cloud API
Apple Watchnative— (can’t pair)none (no cloud exit)
Oura✅ free, webhooks
Whoop✅ (sleep/workouts/HR/energy; not its 5-stage sleep or Recovery score)✅ free (needs a membership to register)
Garmin✅ one-way pushsince 2025-07✅ free, business-only program
Fitbit / Google Health❌ today — write-back promised “later in 2026” after the 2026-05 Google Health rebrand (Google)Google Health API: free ≤100 users, then CASA assessment $500–$4,500 (verification); legacy Fitbit API shuts Sept 2026
Withings (scales)✅ (weight, fat %; not segmented masses)✅ free, webhooks
Polar✅ free, self-serve (no pre-link backfill)
Samsung / Galaxy Watchnone (Health Connect is the sanctioned route)
Amazfit/Zepp, Corospartner-gated
Suunto, Renpho scaleshaky/undocumented

So: iOS hub reaches everything except Fitbit (temporarily) and Samsung; Android hub reaches everything except Apple Watch. The branded scores (Whoop Recovery, Garmin Body Battery, Oura Readiness) mostly do not land in the hubs — you get raw metrics.

4.2 Capacitor plugin

Recommended: @capgo/capacitor-health — v8.10.0 published 2026-07-24, requires Capacitor ≥8 (matches our 8.4.2 shells), covers both HealthKit and Health Connect in one API, read+write, 25+ data types including steps, sleep sessions+stages, HR, HRV, weight, body fat, workouts, calories; queryAggregated day-bucketing; MPL-2.0; ~19.8k weekly downloads — an order of magnitude above alternatives. Alternatives checked and rejected: capacitor-health (mley — read-only, no sleep/HRV/weight), @perfood/capacitor-healthkit (pinned to Capacitor 4, dormant), Ubie capacitor-health-connect (stale, Cap 5).

Already in place in uvilo-mono: apps/uvilo-ai/ios/App/App/App.entitlements already carries HealthKit entitlements including background delivery (dormant — added with the Capacitor work). Missing: NSHealthShareUsageDescription/NSHealthUpdateUsageDescription in Info.plist, the plugin itself, and (to review) the health-records access entry, which we do not need and should remove — clinical records trigger extra App Review scrutiny.

4.3 Background-sync reality (design the product around this)

  • iOS: HKObserverQuery + enableBackgroundDelivery (entitlement already provisioned). Cumulative types (steps) are capped at hourly; delivery is best-effort and stops entirely if the user force-quits the app. Wiring observers must happen in native Swift at app launch — a small custom local plugin/AppDelegate addition; no off-the-shelf Capacitor plugin ships it.
  • Android: foreground reads simple; background reads need the READ_HEALTH_DATA_IN_BACKGROUND permission + a WorkManager periodic worker (15–60 min realistic). Backfill defaults to 30 days pre-grant; older data needs READ_HEALTH_DATA_HISTORY (Android 15+).
  • Practical baseline: sync on app open/foreground + opportunistic background delivery. “Eventually fresh,” not real-time. This matches how the legacy HealthKit integration behaved and is fine for daily coaching (habit auto-complete evaluates end-of-day anyway; see §6).

4.4 Compliance (must-do, small)

  • Apple 5.1.3: HealthKit data may not be used for advertising/data-mining; must disclose collection; usage-description strings required. Sending data to our own backend for the user’s own coaching is permitted use.
  • Google Play: Health Connect permissions require an approved health apps declaration (per-permission justifications, approved use case — “fitness and wellness” fits, privacy policy). Unapproved apps get blocked at runtime. Background + history permissions need their own justification. Budget review latency into the release plan.
  • Worth a separate legal pass eventually: US state consumer-health-data laws (e.g., Washington My Health My Data) once health data is stored server-side.

4.5 Direct cloud APIs (for web-only users, later)

Best coverage per unit effort, all free: Oura (self-serve, OAuth2 + webhooks, full history — PATs were retired Dec 2025), Withings (self-serve, webhooks — fills body-composition detail), Garmin Health API (free, webhook-first, business-only application, ~2 business days). Whoop is a cheap fourth. Fitbit/Google Health only if demand justifies CASA cost — and native Apple Health write-back “later in 2026” may make it moot. Avoid Strava: since June 2026 API access requires an active subscription, new apps are capped to single-digit athletes, and the API agreement bans using its data to train AI models — a legal dead end for an AI coaching product (Strava).

Structural limit no vendor removes: web-only Apple Watch and Samsung users cannot be served — those ecosystems have no cloud API.

4.6 Cost comparison

PathEntry~500 users~5,000 usersRecurring vendor risk
Phone-as-hub (capgo plugin)$0$0$0plugin maintenance (healthy), OS policy churn
+ Oura/Withings/Polar/Garmin direct$0$0$0per-provider API churn (real: 5 breaking ecosystem changes in the last 14 months)
+ Fitbit/Google Health API$0 (≤100 users)CASA $500–$4,500sameGoogle program changes
ROOK$399/mo$399/mo$999/mopre-seed vendor; keep our normalized store + abstract ingestion
Junction$300/mo$300/mo~$2,500/mostrongest company; no Capacitor SDK
Terra$499/mo~$499/mo~$5,000/mothin funding; priciest at scale

DIY-vs-buy sanity check from a (vendor-biased but directionally useful) analysis: multi-provider DIY runs $63k–138k in year-one engineering; the crossover vs SaaS sits around 10–20k active users (The Momentum). Our situation inverts this: the phone-as-hub path is not multi-provider DIY — it is two integrations (HealthKit + Health Connect) that cover most devices, which is exactly the part aggregators can’t do for us anyway (their SDK would sit in our app just the same).

5. Charting

OptionCostZoom+pan incl. touchLarge seriesNotes
MUI X Charts Pro v9$0 incremental — Pro license already held (NEXT_PUBLIC_MUI_X_PRO_LICENSE, @mui/x-data-grid-pro@9.7.0 in 16 packages)✅ wheel/pinch/drag/pan (zoomInteractionConfig) (docs)Weakest of three (SVG); Pro sampling (lttb) mitigates; WebGL is Premium-onlyNative MUI theming; touch gestures are the newest of the three — prototype in the WebView first
Apache ECharts 6$0 (Apache-2.0)dataZoom inside (pinch/drag) + slider; best mobile recordBest (canvas, progressive, lttb)~368 KB gz full, tree-shakeable via echarts/core; manual MUI theme
Highcharts Core v13$366/yr/seat (SaaS annual; pan/zoom is in Core, Stock not needed) (shop)✅ mature, but open mobile pinch bugs (#19217, #19785)Very good (Boost/WebGL in Core)License scope: one external app per SaaS seat (uvilo-ai + forgentic could count as two)
Recharts / Observable Plot / visx / lightweight-charts$0❌ or DIY pinch; lightweight-charts requires TradingView attributionRejected

Decision (Erik, 2026-07-29): Apache ECharts 6. An interactive side-by-side demo (both libraries, identical weight/steps series, zoom/pan/pinch, goal lines, range presets) was built and evaluated; Erik chose ECharts. It is free, has the strongest mobile-touch and large-series record, and its mini-map slider mirrors the Highstock navigator users already knew. MUI X Charts Pro remains the documented fallback; Highcharts is not re-licensed.

6. Net-new designs

6.1 Habit auto-complete engine (Method §11.3)

  • Link: habit ↔ metric with doneThreshold, optional partialThreshold, and a comparator implied by the metric’s betterValues (at-least for higher-is-better like steps; at-most for lower-is-better like screen time).
  • Evaluation: immediate on ingest. The moment values land — manual entry and coach-recorded values evaluate in the same request; sync batches emit an evaluation event per affected user-day — linked habits update right away, so the checkmark appears as soon as the step count crosses the threshold. The one exception is lower-is-better habits (screen time under 2 h): “stayed under” is only provable once the day is over, so those confirm at the user’s local day close (grace-period aware). Auto-set only upward transitions on empty tasks (empty→partial→done); never overwrite a user’s manual state.
  • Prerequisite: a server-side completeHabitForDate(ownerId, habitId, isoDate, completion) primitive — does not exist today; habit completion is currently a client-side generated-hook update on Event.completed (packages/@erikdakoda/habit-ui/components/HabitToggleEvent.tsx).

6.2 Goals: band + SMART target

Keep the legacy two-threshold band (partial/done thresholds, red-amber-green gauges — it doubles as the auto-complete config) and add what legacy declared but never built: baseline, target value, target date per user-metric, aligning with Method §10 (SMART framing) and §2.11 (periodic reevaluation). Progress = position between baseline and target; trajectory = recent trend extrapolated to the target date.

6.3 Calculated metrics without the RCE

Replace vm.runInNewContext with a sandboxed expression grammar (arithmetic, dependency variables, age/gender/height pseudo-variables) — e.g. expr-eval-style parser with a whitelist, no property access, no function calls beyond an approved math set. Seeded formulas (body-fat 3-point, lean mass) become data; user-authored formulas become safe.

6.4 Custom units (new)

Ship a seeded units table (port the legacy 11 measures, fixing its defects: money no-op conversions, scale off-by-one on 1-N scales, volume anchor typo) and add user-defined units (name, plural, abbreviation, optional conversion to a canonical unit of an existing measure, or standalone count-like units: pomodoros, chapters, glasses). Canonical-unit storage + display conversion stays exactly as legacy did it. Scale metrics (1–N) support per-value labels (1 = Terrible … 5 = Great) and render as a labeled slider or a dropdown, so subjective metrics read as words while storing as numbers.

7. Fit with uvilo-mono (readiness facts)

  • Greenfield with footholds: zero metric/wearable/chart code exists; a commented-out Metrics nav entry is already reserved (apps/uvilo-ai/src/config/navigation.ts:71-77); iOS HealthKit entitlements already provisioned (apps/uvilo-ai/ios/App/App/App.entitlements, incl. background delivery; Info.plist usage strings still missing).
  • Schema conventions: new Metric-domain models slot into the established pattern — .zmodel per domain package, registered in packages/@erikdakoda/database/schema.zmodel + ModelConfig.ts; NamedItem/OwnedItem delegates give owner scoping and policies for free; ExecutionLog is the precedent for a high-volume child table with parent-delegated policies (check(...)). Global catalog rows will follow the Taxonomy/Resource publish pattern rather than boot-time reseeding.
  • Jobs: Inngest + ExecutionRun framework is mature (cron triggers, non-admin owner-scoped events, abort/checkpoint discipline) — sync jobs and the nightly auto-complete sweep have reference implementations to copy (packages/@erikdakoda/habit/server/*, docs/execution-jobs.md).
  • Connections ride the Composio surface. The Composio plan (.cursor/plans/composio_integration_7b9dc656.plan.md) ships before Metrics; its three pre-existing security fixes are already completed todos, and it delivers the Connections tab in user settings (TabConnections.tsxConnectionsPanel in composio-ui, both still pending todos there). Metrics extends that panel with health connector cards — Apple Health and Google Health (Health Connect) on native platforms, Oura for everyone — using the same card anatomy (logo, status chip, connect/disconnect, last-synced) so the user never sees which mechanism a connection uses. Oura’s OAuth tokens are held by us in a dedicated model following the Composio plan’s column-secrecy conventions (@omit, precedent Account.zmodel:30) and its authorize-helper and callback-hardening patterns.
  • Packaging: @erikdakoda/metric + @erikdakoda/metric-ui clone the habit/habit-ui template (scaffolding, tsconfig sync scripts, AI-tool five-tier layout, i18n, page re-export + nav entry).
  • Caution from recent history: generic ZenStack update calls with runtime-built selects have caused multi-GB TS type instantiations — metric CRUD code must use concrete, non-generic query signatures.

8. Decision summary — all settled (Erik, 2026-07-29)

#DecisionSettled outcome
D1Wearable-sync strategyPhone-as-hub, no aggregator (capgo plugin); native background sync (iOS observer + Android WorkManager) included up front
D2Aggregator, if ever neededROOK first (price + Capacitor SDK), Junction second; not Terra — documented fallback only
D3Direct cloud connectionsOura now, to scope the work and value of direct integrations; Withings/Garmin later on demand; skip Strava; Fitbit only if demand funds CASA
D4Chart libraryApache ECharts 6 (Erik’s preference after the interactive demo); MUI X Charts Pro documented fallback; Highcharts not re-licensed
D5Data modelPort catalog/subscription/values shape to ZenStack; timestamptz + local-day; unique (user, metric, time, source); publish-workflow catalog
D6Calculated metricsWhitelisted expression evaluator; never vm
D7UnitsSeeded units table + user-defined custom units; canonical storage; 1–N scales carry per-value labels rendered as labeled slider or dropdown
D8Goal modelKeep band (drives gauges + auto-complete) and add baseline/target/date (SMART)
D9Auto-complete engineImmediate evaluation on ingest (manual/AI in-request; sync via per-day events); day-close confirmation only for lower-is-better habits; upward-only transitions; new completeHabitForDate primitive
Connections surfaceHealth connectors listed alongside Composio connectors in the one Connections tab; Composio plan ships first
Legacy dataMigrate legacy user hTsXvc9x8eyNNyNfE → both usr_cmflhqg0500009s8obw6jtucx and usr_cmms0rurm000041uybelkx2bd; no changes to the legacy repo

9. Build phases (reflected in the implementation plan)

  1. Core domain: models (Metric, UserMetric, MetricValue, Unit incl. custom units + scale labels), versioned seeds, manual entry UI, metrics page + nav, safe calculation engine, AI tools, ECharts 6 charts.
  2. Goals & auto-complete: goal band + SMART fields, gauges/dashboard widgets, habit↔metric link, completeHabitForDate, immediate-on-ingest evaluation engine.
  3. Phone sync: capgo plugin, iOS usage strings + entitlement cleanup, connect UX with hasData probes + live counts, watermark/idempotent ingestion, Play health declaration, and native background delivery (iOS Swift observer + Android WorkManager) in the same phase.
  4. Connections & Oura: extend the Composio-plan Connections panel with health connector cards; implement the Oura OAuth connector (webhooks + backfill).
  5. Legacy migration: one-off script copying legacy user hTsXvc9x8eyNNyNfE’s metric data into both of Erik’s accounts; idempotent, dry-run first, read-only against the legacy MongoDB.

10. Key sources

Aggregators: ROOK pricing · ROOK Capacitor SDK · Junction pricing · Junction providers · Junction Series A · Terra pricing + credits · Spike · Thryve · Human API → LexisNexis · Open Wearables · Health API Guy market analysis

Native path: @capgo/capacitor-health · Apple background-delivery entitlement · Apple review guidelines 5.1.3 · Health Connect background/history reads · Play health declaration · Google Health API + verification · Garmin program FAQ · Oura auth · Withings API · Polar AccessLink · Whoop dev · Strava API changes · Google Health rebrand / Apple Health write-back · Garmin → Health Connect

Charts: MUI X zoom/pan docs · MUI pricing · Highcharts shop · Highcharts zooming · ECharts features · ECharts modular import