A data-driven personal coach, built from my own health data
I wanted a coach that answers with my numbers, not generic advice. So I built one — a private pipeline that collects my training, nutrition and sleep data, and an agent that coaches from it. 🏋️
Why a data-driven coach
Generic fitness advice is everywhere. What's rare is advice grounded in your own data: your actual training volume, your real calorie balance, your sleep and resting heart rate over weeks. That's what I set out to build — a coach that looks at my numbers before it says anything.
Two constraints shaped everything:
- Private by design. 🛡️ Health data is the most sensitive data I own. It stays on my own machine — no third-party cloud, no analytics.
- Numbers, not vibes. Every answer must come from the data, never invented.
The sources: where the data comes from
I already track my training, food and health in apps I use daily, so the pipeline reads from them rather than asking me to log twice:
| Source | What it provides | How it gets in |
|---|---|---|
| Hevy | workouts, sets, exercises | official API (pull) |
| MacroFactor | calories, protein, targets | weekly export |
| Apple Health | weight, sleep, resting HR, steps | weekly export |
| RingConn | HRV, sleep detail | via Apple Health |
The exports land in a shared inbox folder and are picked up automatically — I don't have to do anything except keep using the apps I already use.
The pipeline: DuckDB as the single source of truth
All sources feed into one local DuckDB database. The key design decision was idempotent ingestion: each table has a unique key and uses INSERT OR REPLACE, so re-importing the same period overwrites rather than duplicates. Re-running a week's export is safe — no double counting.
Hevy API ─┐
MacroFactor ─┼─▶ inbox ─▶ DuckDB ─▶ KPIs ─▶ dashboard
Apple Health ─┘ │
└─▶ agent (coach)
A scheduled job runs each morning: pull the latest data, ingest what's new, and regenerate the dashboard. A file watcher catches exports that arrive during the day. Everything is incremental — adding a day takes seconds, not a full rebuild.
The dashboard
A lightweight, dark-themed web dashboard shows the KPIs that matter: weekly calorie balance, protein vs target, training volume, sleep, resting heart rate, and weight trend. It's a static page regenerated from the data — no database exposed to the browser, no analytics, served from my own machine.

The coach: an agent that reads the numbers
The most interesting piece: an agent that combines the data with sensible coaching rules. When I ask it a question — "why is my weight stuck?", "how's my protein this week?" — it queries the database first, then answers with the real figures.
The rules it applies are the ones a good coach would:
- Judge weight on the weekly average, not a single day's reading (daily weight is noise — water and glycogen swing ±1-2 kg).
- When progress stalls, look for the leak before cutting calories: unlogged days, sleep under 7 h, protein too low.
- Never invent a number, and never give a medical diagnosis — red flags go to a doctor.
The model proposes, the data decides. The agent is a coach with a spreadsheet, not a fortune teller.
Why this approach works
- It's mine. The data never leaves my machine.
- It's automatic. I keep using the apps I already use; the pipeline does the rest.
- It's honest. Answers are grounded in real numbers, and the coaching rules are explicit and adjustable.
- It's cheap. DuckDB, a static dashboard and a local agent — no per-seat SaaS, no data subscription.
In short
A private, data-driven coach: my training, nutrition and sleep data flow into a local DuckDB, a dashboard makes it visible, and an agent coaches from the real numbers. The payoff: advice that's about me, not about everyone — private, automatic, and grounded in data.
See also: Giving Hermes a headless browser (and a VPN) for the web — the same self-hosted, privacy-first approach applied to browsing.