Your fitness data. Your server. Your rules.
A privacy-first, self-hosted dashboard that turns your Garmin & Libre data into ML-grade health insights — running entirely on your own machine.
Quickstart · Features · Docs · Security
Your Garmin data lives in someone else's cloud. Your sleep, your heart rate, your recovery — measured by a watch on your wrist, then rented back to you behind a login you don't control.
PulseBase takes it back.
One make up, and every heartbeat syncs to a dashboard on your server — with the kind
of ML insights Garmin never surfaces, optional continuous glucose alongside it, and passwords
that are never stored. No cloud. No subscription. No data broker in the middle.
🔒 Self-hosted by design. PulseBase runs entirely on hardware you control. Your health data never leaves your server, and Garmin/LibreLinkUp passwords are wiped from memory the moment a token is obtained.
- 🔒 Privacy by architecture, not by promise — runs entirely on your machine. Garmin and LibreLinkUp passwords are wiped from memory the moment a token is obtained; only a Fernet-encrypted token is ever persisted.
- 🧠 Insights, not just charts — anomaly detection, sleep→HRV correlation, a Random Forest readiness model, body-battery clustering and more turn raw samples into "what does this mean?"
- 🩺 Honest about its own limits — every metric ships an EN 62366-inspired disclosure: intended use, limitations, time horizon, evidence level. No black-box scores.
- 🧬 More than Garmin shows you — energy triptych (physical / autonomic / cognitive), ACWR, training monotony, running economy, SpO₂ trends, and an optional epilepsy seizure diary with a rule-based risk indicator.
- 🐳 One command to run it — Docker Compose, self-service registration, multi-user. No admin.
- Automatic Garmin Connect sync (activities, sleep, HRV, body battery, stress)
- Optional continuous glucose via LibreLinkUp (Libre 3, every 5 min)
- Slate/Emerald instrument-panel UI — tabbed (Training / Verlauf / Erholung), dark + light mode (WCAG 2.1 AA, axe-getestet in beiden Themes)
- Unified Tagesstatus hero: Oura-style readiness arc with HRV/sleep/pulse contributors, energy triptych (Physisch / Autonom / Kognitiv), vitals strip
- Time navigation (← →) across all charts — browse any historical period
- Activity detail pages with GPS map (Leaflet), HR/pace/elevation/cadence charts
- ML insights with dedicated detail pages: anomaly detection (resting HR + SpO₂ + stress Z-score), sleep→HRV Pearson correlation, Random Forest readiness, body-battery K-Means patterns, ACWR, training monotony, running economy, sleep consistency, SpO₂ trend, and more
- 👍 / 👎 item-level feedback on every ML insight
- Metrics overview (
/metrics) — all 21 metrics as tiles with colour-coded evidence badges (Meta-Analysis / Replicated / Model), each linked to a searchable/helpmethodology page
- Central settings page (Garmin + LibreLinkUp in one place), self-service registration
- DSGVO/GDPR data export + account deletion, accessibility statement (BFSG)
- Optional epilepsy seizure diary with rule-based risk indicator (6 biomarker heuristics)
Two deployment modes:
| Mode | Command | Reverse proxy / TLS | Reachable |
|---|---|---|---|
| Home (default) | make up |
homelab-gateway (Caddy, Tailscale) | Tailnet only |
| Public SaaS | make up-public |
bundled Caddy + Let's Encrypt | public internet |
cp env/.env.example env/.env # HOST_IP / DB admin
cp env/.env.app.example env/.env.app # DB roles + FERNET_KEY → make gen-secrets
cp env/.env.api.example env/.env.api # SESSION_SECRET (≥32 chars) + APP_BASE_URL
cp env/.env.sync.example env/.env.sync
cp env/.env.ml.example env/.env.ml
make up # home: build + start behind homelab-gateway (proxy network)
# → https://your-domain.com/register
# → https://your-domain.com/garmin/link
make trigger-sync # pull your Garmin data now (no rebuild — runs within a minute)
# → https://your-domain.com/dashboardRunning a homelab? Start homelab-gateway first,
then make up.
Going public (self-hosted SaaS)? Bundled Caddy gets you automatic HTTPS on a public
domain — cp env/.env.public.example env/.env.public, set PUBLIC_DOMAIN + ACME_EMAIL,
then make up-public. Full runbook: docs/deployment-public.md.
→ Full walkthrough: docs/setup.md
Pick your depth:
| 🧒 ELI5 | The whole system explained like you're five |
| 🚀 Setup Guide | Complete installation walkthrough |
| ⚙️ Configuration | Every env var, per service |
| 🛠️ Developer Guide | Local dev, tests, CI/CD — and the full make command reference |
| 🏛️ Architecture | Services, data paths, network setup |
| 🗄️ Database | Schema, hypertables, real column names |
| 🔌 API Reference | Every endpoint with request/response format |
| 🤖 ML Deep Dive | Algorithms, formulas, thresholds, training pipeline |
| ⚡ Energy Metrics | The physical / autonomic / cognitive scores in detail |
| 🔐 Security | Threat model, auth layers, crypto, headers |
| 🏗️ Production Hardening | Going live: secrets, backups, monitoring |
| 🌐 External Services | Let's Encrypt, Sentry, Uptime Kuma setup |
| 🧭 Design Decisions | Why no Grafana, no ORM, no JWT, Caddy everywhere |
| 📋 ADRs | Architecture Decision Records |
Self-hosting your health data only helps if the app itself is hard to break into. PulseBase ships with rate-limited auth + account lockout, email verification, CSRF protection, bcrypt password hashing, signed httpOnly session cookies, Fernet-encrypted Garmin/Libre tokens, least-privilege per-service DB roles, a strict nonce-based CSP, and SAST/SCA/container scanning in CI (bandit · semgrep · pip-audit · Trivy).
→ Full threat model and controls: docs/security.md
FastAPI · TimescaleDB (PostgreSQL 16) · Docker Compose · Chart.js · scikit-learn · Tailwind CSS
Three services — api (dashboard), sync-service (Garmin/Libre ingest), ml-service
(analytics) — behind a reverse proxy. No Grafana, no ORM, no JWT, no framework on the frontend.
See Design Decisions for the why.