# πŸ«€ PulseBase **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. ![FastAPI](https://img.shields.io/badge/FastAPI-009688?style=flat-square&logo=fastapi&logoColor=white) ![Python](https://img.shields.io/badge/Python-3.14-3776AB?style=flat-square&logo=python&logoColor=white) ![TimescaleDB](https://img.shields.io/badge/TimescaleDB-PostgreSQL%2016-4169E1?style=flat-square&logo=postgresql&logoColor=white) ![Docker](https://img.shields.io/badge/Docker-Compose-2496ED?style=flat-square&logo=docker&logoColor=white) ![scikit-learn](https://img.shields.io/badge/scikit--learn-ML-F7931E?style=flat-square&logo=scikitlearn&logoColor=white) ![Self-hosted](https://img.shields.io/badge/Self--hosted-Privacy--first-10B981?style=flat-square) [Quickstart](#-quickstart) Β· [Features](#-whats-inside) Β· [Docs](#-documentation) Β· [Security](#-security-at-a-glance)
--- > *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. --- ## ✨ Why PulseBase - πŸ”’ **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. --- ## πŸ“¦ What's inside ### Data sync - Automatic Garmin Connect sync (activities, sleep, HRV, body battery, stress) - Optional continuous glucose via LibreLinkUp (Libre 3, every 5 min) ### The dashboard - 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 ### The intelligence - 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 `/help` methodology page ### Built for real use - 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) --- ## πŸš€ Quickstart Two deployment modes: | Mode | Command | Reverse proxy / TLS | Reachable | |------|---------|---------------------|-----------| | **Home** (default) | `make up` | [homelab-gateway](https://github.com/gerfru/homelab-gateway) (Caddy, Tailscale) | Tailnet only | | **Public SaaS** | `make up-public` | bundled Caddy + Let's Encrypt | public internet | ```bash 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/dashboard ``` **Running a homelab?** Start [homelab-gateway](https://github.com/gerfru/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](docs/deployment-public.md)**. β†’ Full walkthrough: **[docs/setup.md](docs/setup.md)** --- ## πŸ“š Documentation Pick your depth: | | | |---|---| | πŸ§’ **[ELI5](docs/eli5.md)** | The whole system explained like you're five | | πŸš€ **[Setup Guide](docs/setup.md)** | Complete installation walkthrough | | βš™οΈ **[Configuration](docs/configuration.md)** | Every env var, per service | | πŸ› οΈ **[Developer Guide](docs/development.md)** | Local dev, tests, CI/CD β€” and the full `make` command reference | | πŸ›οΈ **[Architecture](docs/architecture.md)** | Services, data paths, network setup | | πŸ—„οΈ **[Database](docs/database.md)** | Schema, hypertables, real column names | | πŸ”Œ **[API Reference](docs/api.md)** | Every endpoint with request/response format | | πŸ€– **[ML Deep Dive](docs/ml-deep-dive.md)** | Algorithms, formulas, thresholds, training pipeline | | ⚑ **[Energy Metrics](docs/energy-metrics.md)** | The physical / autonomic / cognitive scores in detail | | πŸ” **[Security](docs/security.md)** | Threat model, auth layers, crypto, headers | | πŸ—οΈ **[Production Hardening](docs/production-hardening.md)** | Going live: secrets, backups, monitoring | | 🌐 **[External Services](docs/external-services.md)** | Let's Encrypt, Sentry, Uptime Kuma setup | | 🧭 **[Design Decisions](docs/design-decisions.md)** | Why no Grafana, no ORM, no JWT, Caddy everywhere | | πŸ“‹ **[ADRs](docs/adr/)** | Architecture Decision Records | --- ## πŸ” Security at a glance 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](docs/security.md)** --- ## 🧱 Stack 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](docs/design-decisions.md)** for the why. ---
Built for people who'd rather own their health data than rent it back.