跳到正文
DEV Community· Quentin Caron·· 2 小時前AI 評分55

Quentin Caron 為何打造 Open Health Sync,作為 Terra 的穿戴裝置數據開源替代方案

Why I built an open source alternative to Terra for wearable data

AI 導讀

Quentin Caron 打造 Open Health Sync,作為穿戴裝置數據的開源自託管聚合工具,採用單一 Go binary、Postgres 和 TimescaleDB。

正文

Every wearable speaks its own dialect. Apple Health sends one JSON line per HealthKit sample, with heart rate in count/min and oxygen saturation as a fraction. Withings sends a daily activity object keyed by date and intraday series keyed by Unix epoch. Oura, WHOOP and Fitbit each have their own idea of what a day, a device and a duplicate are.

So every health app I worked on ended up writing the same layer: a parser per provider, a table per provider, a cron job per provider. It works until the second provider, the first re-sent upload, the first mapper bug that needs a backfill, or the first person who asks you to delete everything you hold about them.

The obvious answer is a hosted API

There is a whole category of products that solve this for you. Terra is the best known: one API, one widget for your users to connect their devices, and data pushed to your webhook or database. It advertises more than 500 integrations, Garmin included, and states SOC 2 Type II, HIPAA and GDPR compliance. If you need Garmin today, or a long tail of devices under one contract, and you don't want to run a server, it is a very reasonable choice.

It did not fit what I was building, for four reasons.

Price per connected user. Terra's public pricing starts at $399 a month billed annually ($499 monthly) with 100,000 credits, and each source a user connects costs 200 credits a month. By my arithmetic that covers about 500 connections. A product with 2,000 users who each connect one source would pay around $2,000 a month at the listed starting prices, before any volume discount. That is fair for a funded product. It is a lot for a free tier, a research study or a side project.

The data passes through someone else. With a hosted API, your users' health data lands in the vendor's cloud before it reaches you. Terra's end-user privacy policy says it is usually kept there for two days and at most six months. That is a short window, but for some projects any third party in the path is one too many.

Raw payloads. I wanted every payload kept verbatim, on my side, forever. Mappers have bugs. When I fix one, I want to re-process a user's whole history from the original files, not from a normalized copy that already lost the detail.

Standards. I wanted FHIR R4 that passes the official HL7 validator and Open mHealth data points, so the data can go to a clinical system or an analysis tool without another conversion.

What I built instead

Open Health Sync is an open source, self-hosted health data aggregator. It is one Go binary plus Postgres with TimescaleDB, and it does the boring parts once:

  • 13 sources: Apple Health and Health Connect (uploaded by a phone app, or imported from the Health app export), plus Withings, Oura, WHOOP, Fitbit, Polar, Strava, Suunto, Ultrahuman, intervals.icu, Dexcom and OMRON over OAuth, with provider webhooks triggering pulls of just what changed.
  • Raw first: every payload is stored verbatim on disk or S3 before anything else happens, and re-sent uploads are de-duplicated by content hash.
  • One model, one unit per quantity: UCUM units, LOINC codes, and "today" means the local calendar day of the record's time zone, not UTC.
  • One answer per question: when a ring and a watch both record the same night, reads return one merged view, with priorities per user and metric, and the raw rows stay available.
  • Many ways out: a REST API (OpenAPI 3.1), FHIR R4 Observations, Open mHealth, CSV and Parquet exports, signed webhooks, and a read-only MCP server so an AI agent can read one person's data with a token that cannot write.
  • Erasable: DELETE /v1/users/{id} removes the rows and the raw files.

It idles at about 22 MB of memory and runs on a Raspberry Pi 5.

Try it in a few minutes

You need Docker with Compose, plus curl, jq and openssl:

git clone https://github.com/soma-intelligence/open-health-sync.git && cd open-health-sync
cp .env.example .env
sed -i.bak "s|^OHS_ENCRYPTION_KEY=.*|OHS_ENCRYPTION_KEY=$(openssl rand -base64 32)|" .env
docker compose --env-file .env -f deploy/docker-compose.yml up -d --wait
docker compose --env-file .env -f deploy/docker-compose.yml run --rm ohs doctor

No wearable at hand? This loads 90 days of synthetic data from six overlapping sources and prints an API key with calls to try:

docker compose --env-file .env -f deploy/docker-compose.yml exec ohs /ohs seed --demo

It prints the demo user's id and an API key once. Export them, then read one merged day, or the same heart rate data as FHIR:

export OHS_URL=http://localhost:8080 OHS_API_KEY=ohs_sk_... USER_ID=...   # values printed above
curl -s -H "Authorization: Bearer $OHS_API_KEY" "$OHS_URL/v1/users/$USER_ID/daily"
curl -s -H "Authorization: Bearer $OHS_API_KEY" \
  "$OHS_URL/v1/users/$USER_ID/fhir/Observation?code=http://loinc.org%7C8867-4&limit=5"

Your own Apple Health history loads from the export the Health app writes on your iPhone (Health app, your picture, Export All Health Data), with the ohs binary from the releases page and a user token (the README shows how to create one):

OHS_TOKEN=$TOKEN ohs import apple-health export.zip --url $OHS_URL

It reads the zip as a stream, so memory stays flat whatever the size of the export, and running it twice uploads nothing twice.

What you give up

Self-hosting is not free of trade-offs, and I would rather say them up front:

  • You register your own developer app with Oura, WHOOP, Withings and the others. A hosted API does that part for you. Each connector guide lists the steps.
  • 13 sources, not 500. Garmin is not available.
  • No certification. There are building blocks for a compliant setup (consent per source and data category, an append-only audit log, hard deletes, a per-user archive), but compliance depends on how and where you run it.
  • AGPL-3.0. Using it unmodified, or calling it from closed source apps over HTTP, is fine. If you run a modified version as a service, you share your changes.
  • Early days. It is v0.1.0, and the API may change in any 0.x release.

It is not the only open source option

Open Wearables solves the same problem and came first. It has the larger community, a web dashboard, Garmin, and an MIT license, which matters if AGPL is a problem for you. I benchmarked both on the same machine with the same Apple Health data: Open Health Sync stored the records 2 to 3 times faster end to end and used about 22 MB of memory idle against about 494 MB, while Open Wearables used 3.4 times less disk. Those are numbers for one user, one machine and synthetic data, and the scripts are in the repository for anyone to run again.

I wrote two comparison pages that try to be fair, including when the other option is the better pick:

Feedback welcome

The code is at github.com/soma-intelligence/open-health-sync. I would love to hear which source you need next, what you think of the data model, and what you would build if your users' health data were one SQL query away. Issues, discussions and pull requests are all open.

來源:DEV Community · dev.to