Skip to content

Latest commit

 

History

41 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

HealthTrack

License: MIT CI

Self-hosted personal & family health tracker. Medications, conditions, allergies, labs, vitals, procedures, vaccines, appointments and notes — for you and your dependents — in a single Docker container. All data stays in one SQLite database and an uploads folder on your server. No cloud services required.

HealthTrack dashboard — AI health overview, quick stats with trends, active medications and lab flags

Features

  • Medications — dosages, schedules, active/inactive history, AI interaction checks*
  • Conditions, allergies, procedures, vaccines — full clinical history
  • Labs — visits and results with reference ranges, plus AI-powered PDF parsing of lab and vaccine reports*
  • Vitals — blood pressure, heart rate, weight, glucose and more, with reference ranges and trend charts
  • Family & dependents — track children or family members under your account, with a transition flow when they grow up
  • Sharing — share selected sections of your (or a dependent's) health record with another user, with expiry
  • Delegates — grant another user read-only or read-write access to manage a record
  • Medical-history import* — upload a doctor-provided history PDF (yours or a dependent's) and AI extracts medications, conditions, allergies, procedures, vaccines, and lab results in one pass; large documents are processed in page chunks, and a review screen flags anything already on record before import
  • Fitness tracking — log workouts manually or via the API (set shorthand like 185x5 x3), with history, trend charts (est. 1RM, PR badges), weekly rollups, goals, and an exercise catalog
  • API access — personal access tokens with scoped permissions for the REST API (/api/v1/...), including read endpoints for every domain
  • Device integrations / bring your own bridge — push metrics from any device (CPAP, smart scale, watch, CGM, …) via POST /api/v1/vitals and /api/v1/vitals/batch with idempotent upserts; see docs/API.md. Every instance self-documents at /docs/api (human cookbook) and /api/v1/openapi.json (OpenAPI 3.1) — both public, API-shape only
  • Health Connect ingestion (Android) — receive Health Connect data from the Life Dashboard companion app over an HMAC-signed webhook: daily activity totals land in vitals and food-tracker nutrition becomes daily calories and macros. A new integration starts in inventory mode — every record is stored and catalogued, but nothing is written to your health data until you approve the exact apps you trust; see docs/health-connect.md
  • AI health assistant* — natural-language questions about your data, health summaries
  • Oura Ring sync* — sleep, heart rate and activity data
  • Sign in with Google* — alongside built-in email/password auth

* Optional. AI features need an ANTHROPIC_API_KEY, Google login needs a Google OAuth client, Oura sync needs an Oura OAuth client. Each feature is hidden in the UI until its keys are configured — the core tracker is fully functional without any of them.

Screenshots

All screenshots show a demo account with fictional data.

Vitals & wearables — every metric grouped by domain, with trend sparklines, reference-range bars, and per-device source badges. Data flows in from bridges, the API, or manual entry:

Vitals — all metrics view with trends and reference ranges

Focus view — the day-to-day question ("how am I doing?") answered at a glance: recovery verdict, body-composition trend, and weekly activity vs. your 30-day norms:

Vitals — focus view with recovery, body composition and activity summaries

Medications — active/past history with categories, plus AI interaction checks that run automatically when your med list changes:

Medications with AI interaction status bar

Fitness — workout history with per-set detail, working-set summaries, trends with estimated 1RM and PR badges, weekly rollups and goals:

Fitness history with expanded strength session

Medical-history import — upload a doctor-provided PDF; AI extracts medications, conditions, allergies, procedures, vaccines and labs in one pass, deduplicates against what's already on record, and imports only what you approve:

Import medical history review screen with dedupe badges

Quick start

docker run -d \
  --name healthtrack \
  -p 3000:3000 \
  -v ./data:/data \
  -e APP_URL=http://localhost:3000 \
  ghcr.io/ezekielthemad/healthtrack:latest

Open http://localhost:3000 and register — the first user becomes the instance admin. After that, registration is invite-only by default: create single-use invite links under Settings → Invites to add family members. (Set SIGNUPS_ENABLED=true to open registration to anyone, or false to close it completely.)

Port 3000 already in use? Map a different host port and point APP_URL at it, e.g. -p 3005:3000 with -e APP_URL=http://localhost:3005. The container always listens on 3000 internally; only the host side changes.

Docker Compose

See docker-compose.yml:

docker compose up -d

Unraid

HealthTrack ships a Community Applications template: unraid/healthtrack.xml. See docs/UNRAID.md for install instructions (via CA or manual template URL).

Adding users & family

Registration is invite-only by default — nobody can create an account on your instance without your say-so. The ways to bring people in:

  1. Invite links (new accounts) — as admin, go to Settings → Invites, create a link (optionally note who it's for) and send it. Each link is single-use and expires after 7 days. The recipient registers with email
    • password; afterwards they can also use "Sign in with Google" on the same email (accounts link automatically). Revoke unused links any time.
  2. Dependents (no account needed) — track children or family members as profiles under your own account (Settings → Family & Dependents), with a transition flow to hand the record over when they grow up.
  3. Delegates — grant another account read-only or read-write access to manage your record (Settings → Delegate Access).
  4. Sharing — share selected sections of a record with another account, with expiry (Settings → Health Sharing).

Prefer open registration (e.g. a trusted LAN)? Set SIGNUPS_ENABLED=true. Set SIGNUPS_ENABLED=false to close registration completely, invites included.

Configuration

All configuration is via environment variables (see .env.example):

Variable Required Default Purpose
APP_URL yes http://localhost:3000 Absolute URL of the instance — must match what users browse to; used for auth callbacks
AUTH_SECRET no auto-generated to /data/keys Session signing secret
ENCRYPTION_KEY no auto-generated to /data/keys Encrypts stored OAuth tokens
SIGNUPS_ENABLED no invite-only Unset: first account registers freely (becomes admin), everyone after needs a single-use invite link (Settings → Invites). true: open registration. false: fully closed
ANTHROPIC_API_KEY no Enables AI features (summaries, queries, PDF parsing, interaction checks)
ANTHROPIC_MODEL no current Sonnet Claude model for reasoning features (summaries, queries, interaction checks) — e.g. claude-opus-4-8
ANTHROPIC_MODEL_EXTRACTION no current Sonnet Claude model for PDF extraction — a cheaper model like claude-haiku-4-5 works well
GOOGLE_CLIENT_ID / GOOGLE_CLIENT_SECRET no Enables "Sign in with Google"
OURA_CLIENT_ID / OURA_CLIENT_SECRET no Enables Oura Ring sync
OURA_SYNC_ENABLED no true when Oura is configured Startup + recurring native Oura sync toggle
OURA_SYNC_INTERVAL_HOURS no 4 Native scheduler interval; every run self-heals an inclusive 7-day window
PUID / PGID no 99 / 100 Ownership of files under /data (Unraid conventions)
TZ no Etc/UTC Valid IANA timezone for the container and Oura calendar-date sync windows

Reverse proxy / HTTPS

HealthTrack serves plain HTTP on port 3000; put your reverse proxy of choice (Nginx Proxy Manager, Caddy, Traefik, Cloudflare Tunnel, …) in front of it for HTTPS. APP_URL must exactly match the URL users browse to (scheme, host, port) or login callbacks will fail.

Oura architecture

Oura OAuth tokens are encrypted in each user's connected_sources row and are the only credential store. Manual Sync Now and the native startup scheduler share the same per-user refresh lock, so concurrent requests cannot rotate a refresh token twice. The scheduler runs once on Node startup and at the configured interval (4 hours by default), isolates failures per user, and backfills an inclusive seven-day self-heal window. The OAuth callback keeps its 30-day initial backfill. HealthTrack writes directly to its canonical vitals store with source=oura; no Notion or bridge writes are involved.

Oura date windows use the HealthTrack TZ setting so a sync near UTC midnight still requests the owner's current local calendar day. TZ must be a valid IANA timezone (for example America/New_York); the portable default is Etc/UTC.

Serve the app on one public hostname. If your proxy answers on both example.com and www.example.com (a common default), pick one, put it in APP_URL, and have the other redirect to it. Auth cookies are scoped to the exact host that set them, while OAuth redirect URIs are built from APP_URL — so a visitor who starts on the hostname you didn't configure gets sent back to the one you did, without their cookie, and Google sign-in fails with ?error=state_mismatch. HealthTrack now redirects browser page requests to APP_URL's hostname for you (API routes under /api/ are left alone, as are localhost and LAN addresses), but fixing it at the proxy or DNS layer is still cleaner.

Authentication & OAuth

Built-in email/password sign-in works anywhere, including a bare LAN IP — nothing extra to configure. The optional social logins have provider-specific requirements:

  • Sign in with Google requires a public HTTPS URL on a real hostname. Google's OAuth rejects http:// and raw IP-address redirect URIs (only http://localhost is exempt), so it will not work against something like http://192.168.1.50:3000 — you must front the app with HTTPS (reverse proxy or tunnel) and set APP_URL to that address. Then, in the Google Cloud Console OAuth client, add this Authorized redirect URI:

    APP_URL/api/auth/callback/google
    
  • Oura Ring is more permissive (a LAN IP is fine). Register its redirect URI in the Oura developer portalnot the Google console; they are separate providers with separate credentials:

    APP_URL/api/oura/callback
    

In every case APP_URL must exactly match the URL your browser uses (scheme, host and port), or the OAuth round-trip fails. Restart the container after changing APP_URL. The first user to sign in — by any method — becomes the admin.

Backup

Everything lives under /data: the SQLite database (healthtrack.db), uploaded PDFs (uploads/) and auto-generated secrets (keys/). To back up: stop the container, copy the /data directory, start it again. Restore is the reverse.

Development

npm install
npm run dev     # http://localhost:3000, state lands in ./data
npm test        # vitest
npm run lint
npm run build

Local development state (SQLite db, uploads, generated keys) lives in ./data (override with DATA_DIR). Database tests run in the node environment — see existing *.test.ts files for the environment pragma convention.

Disclaimer

HealthTrack helps you organize health information. It is not a medical device and does not provide medical advice, diagnosis or treatment — always consult a qualified healthcare professional. AI-generated content can be wrong. You run this software self-hosted, on your own infrastructure, at your own responsibility; review SECURITY.md before exposing an instance to the internet.

License

MIT

About

Self-hosted personal & family health tracker — meds, labs, vitals, sharing. Single Docker container for Unraid/anywhere. Optional AI features.

Topics

Resources

Contributing

Security policy

Stars

3 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages