Skip to main content

Health tracking

Body stats, bloodwork panels, biomarker trends, Garmin Connect daily summaries, immunization registry, and medical history. Per-user SQLite on local disk at Config.module_db_path(user, "health") (default {db_path.parent}/modules/{user}/health.db); user-facing uploads stay in the workspace on the mount. All measurements stored metric (kg, cm, °C, mmHg, bpm); the display layer converts to the user's preferred units.

Health is an on-by-default module with per-user opt-out via disabled_modules in user settings.

Features​

Body stats — Time series for weight, blood pressure, resting HR, body fat %, body temp, respiratory rate, SpO2. Manual logging via skill CLI or auto-populated from Garmin sync. Unit-aware input (accepts lb, °F, etc. and converts at log time). BMI derived from latest weight + profile height.

Bloodwork — Panel ingestion from three sources: drag-and-drop OCR upload (PDF/image → LLM extraction with review-and-confirm), CSV bulk import, or manual entry via skill CLI. 60+ canonical biomarkers with sex-specific reference ranges, alias normalization, and auto-flagging (H/L/C). Blood-pressure and resting-HR biomarker rows fan out to the stats time series.

Biomarker trends — Per-marker trend charts with out-of-range zones shaded. LLM-generated educational explainer cards for flagged markers (never diagnoses or prescriptions — hard guardrails in the prompt). Explainers cached per-user per (name, direction).

Garmin Connect — OAuth connection via the general Settings → Connected services page. Garmin is a cross-module connected service, shared with the location track importer — connect once and both features use the same token. Sync runs on demand (the "Sync health data" button on that Garmin card, the garmin-sync skill CLI, or a user-configured CRON job), not on an automatic schedule. It pulls sleep (duration, score, stages), stress, body battery, steps, active calories, SpO2, HRV, VO2 max, and respiration. A multi-day backfill is available but not auto-triggered on connect.

Immunizations — Registry of administered vaccines with date, product, manufacturer, lot, site, route, and facility. Bundled canonical vaccine reference list with recommended schedules. Coverage tracker shows due-soon and overdue immunizations. Bulk import from MyChart/clipboard paste with dry-run preview. Static educational explainers per vaccine.

Medical history — Encounters (doctor visits, procedures, screenings, hospitalizations) and diagnoses (active, resolved, chronic) with ICD-10 codes. history-summary command generates a new-doctor packet.

A condition is linked to every appointment it was seen at, not just one: something a GP found, a specialist was referred for and a follow-up reviewed carries all three, and appears under each of them. Link and unlink from either side — an encounter's page lists its conditions, a condition lists its appointments — and unlinking keeps both records; removing the last link does not delete the condition. Links recorded before this existed are carried over on first upgrade.

Setup​

Health requires no additional configuration — it's enabled by default for all users. To disable for a specific user, add health to their disabled_modules list in user settings or via the web UI Preferences page.

Install optional dependencies for full functionality:

uv sync --extra all
# or specifically for OCR:
uv sync --extra transcribe # pytesseract + Pillow

OCR upload also requires pdftotext (from poppler-utils) for PDF text extraction.

Database​

Per-user SQLite on local disk (see above). Tables:

TablePurpose
statsBody stat time series (metric, value, unit, date, source)
panelsBloodwork panels (drawn_at, lab, type, draft/confirmed, content_hash)
biomarkersIndividual biomarker results linked to panels
biomarker_explainersCached LLM explainer text per (name, direction)
biomarker_refsBundled canonical biomarker reference ranges and aliases
immunizationsVaccine administration records
immunization_refsBundled canonical vaccine reference list and schedules
encountersMedical encounters (visits, procedures, screenings)
diagnosesDiagnoses with status (active, resolved, chronic)
diagnosis_encountersWhich appointments a condition was seen at (many-to-many)
health_settingsKey/value store for profile (DOB, height, sex) and unit display preferences
documentsStored paperwork — scans, discharge summaries, vaccination cards
document_linksWhich records a document evidences (encounter / diagnosis / immunization)

Document bytes live at {workspace}/health/uploads/documents/{id}/{filename} — beside the panel sources, under the one path root the file-serving routes guard. A document is stored once and can be linked to several records, so a visit summary covering a diagnosis and a vaccine given at the same visit is one file, not three copies. Size is capped by [health] max_document_bytes (default 25 MiB; 0 = unlimited). A document that ends up attached to nothing is deleted 24 hours after the last thing that referenced it — long enough that detaching a file on the way to re-attaching it elsewhere is safe, short enough that an import you abandoned doesn't linger.

Garmin Connect OAuth tokens are not stored here — they live in the framework-level encrypted secrets table under service="garmin" (Fernet via ISTOTA_SECRET_KEY).

Web pages​

PathContent
/health/statsNetdata-style sparkline grid for all body stats
/health/bloodworkDates-as-rows × markers-as-columns spreadsheet with category bands
/health/bloodwork/panel?id=…Panel detail with inline-edit table and source preview
/health/bloodwork/uploadDrag-and-drop OCR review-and-confirm
/health/bloodwork/marker?name=…Trend chart, related markers, clinical description, explainer card
/health/immunizationsRegistry table, coverage status, import controls
/health/history/encounter?id=…Encounter detail — linked diagnoses, panels, and its documents
/health/history/diagnosesConditions list; a paperclip badge opens that condition's documents
/health/immunizations/detail?id=…Per-record edit plus "Proof of immunization"
/health/settingsDOB/height/sex, display preferences (Garmin connect/sync lives on Settings → Connected services)

Skill CLI​

The health skill exposes istota-skill health <subcommand>. Key subcommands:

  • log, stats, latest — body stat CRUD and queries
  • panels, panel, add-panel, add-biomarker — bloodwork management
  • trend, summary — biomarker analysis
  • upload, import-csv, export-csv — bulk data operations
  • settings, set — profile and display preferences
  • encounters, add-encounter, update-encounter, delete-encounter — medical visits
  • diagnoses, diagnosis, add-diagnosis, resolve-diagnosis, update-diagnosis, delete-diagnosis — conditions
  • link-encounter, unlink-encounter — attach a condition to an appointment it was seen at, or detach it (both records are kept)
  • immunizations, add-immunization, update-immunization, delete-immunization — vaccine records
  • vaccine-refs, coverage, explain-immunization — reference data and coverage
  • import-immunizations — bulk import from clipboard/paste
  • garmin-status, garmin-sync, garmin-disconnect — Garmin integration
  • documents, document, attach-document, detach-document — file paperwork against a record

Attaching takes a TYPE:ID token: istota-skill health attach-document --path ~/inbox/card.jpg --to immunization:5. Types are encounter, diagnosis, immunization. The agent attaches files the user supplied; deleting a document stays a web-UI action behind a confirmation, since it removes the file from every record it is attached to.

Immunization coverage status​

coverage resolves each vaccine in the registry to one of seven states, from its schedule kind and the doses on record:

StatusMeaning
up_to_dateCovered, with nothing due inside the 30-day window
due_soonNext dose falls inside the 30-day window (annual, every_10y)
overdueNext dose has passed (annual, every_10y)
series_incompleteThe series was started and has doses left
never_recordedNo dose of this vaccine has been recorded
expiredA travel_pre_trip vaccine whose interval has run out
risk_basedRecommended on risk factors rather than a schedule, with no dose recorded — never auto-flagged as missing

series_incomplete means what it says: you started the series and have doses left. A series vaccine with zero recorded doses is never_recorded. It used to read part-done from the start, so an empty immunizations page opened on ten vaccines that looked half-finished, and the health summary the model reads listed all ten as needing action.

A schedule kind the code does not recognise falls back to never_recorded with no dose and up_to_date with one.

All mutating operations are deferred under sandbox (written to task_<id>_health_ops.json, replayed post-task by the scheduler).

Privacy​

Health data is the most sensitive data in the system. The health DB is the single source of truth.

  • Quantitative health data (measurements, biomarker values, lab dates, symptoms) must never be written to USER.md, the knowledge graph, dated memories, or KV.
  • Biomarker values are excluded from briefings and log channels.
  • Source files for uploaded labs are served only through the auth-gated /panels/{id}/source route; attached documents likewise through /documents/{id}/file. Neither is ever a public share link. Documents are always served as a download with nosniff, because one may have arrived from outside (an email attachment the agent filed).
  • Stable identity-level medical facts (allergies, named chronic conditions) belong in the knowledge graph; detailed records stay in the health DB.