Skip to content
anysdaPublic

About

Self-hosted health data aggregator with AI analysis.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

75 Commits

Folders and files

Repository files navigation

biomon

Self-hosted агрегатор данных о здоровье с AI-чатом поверх. Один развёрнутый инстанс обслуживает одну семью. Всё хранится на твоём железе, наружу уходят только запросы к AI-провайдеру, ключ к которому ты прописал сам.

biomon в работе

что внутри

Три вкладки:

  • chat — спросить AI «как я сплю», «какой был пульс в 12:35», «нарисуй график глюкозы за неделю». AI сам ходит в БД через тулзы (query_metrics, render_chart, list_metric_types) — не угадывает «по памяти», достаёт точные значения. Графики рендерятся прямо в чате.
  • database — таймлайн всех точек данных. Фильтр по типу, ручной ввод, infinite-scroll (доскролл вниз → загрузится ещё пачка по 500).
  • integrations — подключённые источники, AI-модели, темы, system prompt и memory — долгосрочная markdown-память AI про юзера (что AI сам запомнил между диалогами + что попросил юзер).

Роли:

  • admin — настраивает AI-модели (общие на семью), раздаёт инвайты, видит таймлайн и чат по данным всех. Своих устройств не подключает.
  • user — подключает свои источники (Apple Health / Oura / Libre / погода и т.д.), смотрит свой таймлайн, общается с AI поверх своих данных. Использует AI-модели прописанные админом.

источники данных

Все интеграции в integrations/<id>/ — папка с index.ts экспортирует HealthProvider, остальное (UI, scheduling, маппинг, дедуп) подхватится автоматически.

провайдер режим что тянет
apple-health push (webhook) Apple Watch + iPhone через iOS-приложение Health Auto Export. HR / HRV / sleep_analysis / steps / workouts / SpO2 / temp / 30+ метрик
oura pull (OAuth2, 15м) sleep / readiness / activity / HRV / heartrate per-sample / workouts / spo2 / VO2max / tags. HR с backfill'ом 48ч (ring → phone → cloud имеет лаг)
libre pull (LibreLinkUp, 15м) глюкоза с FreeStyle Libre 2/3 через LLU sharing
weather pull (Open-Meteo, 60м) текущая погода по городу + прогноз на 48ч вперёд (типы weather_*_forecast, обновляются каждый sync)

Apple Health push-режим: после подключения карточка показывает персональный webhook-URL с токеном — вставляешь его в HAE → Automations → REST API, и iPhone начинает раз в N минут POST'ить JSON на твой биомон.

AI-чат

Поддерживаются 4 провайдера: OpenAI, Anthropic, Google Gemini, OpenRouter (агрегатор сотен моделей по одному ключу). Все стримятся SSE, все понимают tool-calling в своих нативных форматах.

Tools (AI вызывает сам когда нужно):

  • query_metrics(type, since?, until?, hours_back?, aggregation?, limit?) — выборка точек. since/until — в локальной TZ юзера, aggregation = raw / hourly / daily (min/avg/max по бакетам).
  • render_chart(type, ..., kind?: line | bar) — рисует SVG-график инлайн в чат, AI получает только summary (не дублирует точки в тексте).
  • list_metric_types() — какие типы есть у юзера (если AI не уверен в имени).
  • remember(note) — append bullet - (YYYY-MM-DD) <note> в долгосрочную память юзера. Вызывается обязательно когда юзер сказал «запомни X», + по инициативе AI для важных фактов (диагнозы, аллергии, цели, режим).
  • replace_memory(content) — полная перезапись markdown'а.

Контекст, который AI получает в system prompt:

  1. system-prompt юзера (кастомный или сгенерированный из подключённых источников)
  2. блок MEMORY — то что AI запомнил между сессиями
  3. инструкции про tools (когда звать, когда нет)
  4. сжатые bucket'ы метрик за год — обзорный фон, для точных значений AI должен звать query_metrics

История сообщений в БД хранится с meta (tool-chip'ы и charts), поэтому при перезагрузке чата UI восстанавливает всё что юзер видел в стриме.

стек

Nuxt 3 + Nitro + PostgreSQL 16 + Drizzle ORM + nuxt-auth-utils

  • Tailwind CSS + Caddy 2 (HTTPS reverse-proxy с self-signed CA). Сессионные cookie, AES-256-GCM на все third-party креды в БД, ключ MASTER_KEY живёт в .env на сервере. SSE-стримы пробрасываются через Caddy с flush_interval -1.

Темы — в themes/<id>/index.ts, доступны через переключатель в Integrations или cookie (для auth-страниц до логина).

install

git clone <this-repo> biomon && cd biomon
./install.sh

Скрипт сгенерит MASTER_KEY и session secret, поднимет Postgres, приложение и Caddy в docker compose, прогонит миграции и распечатает URL. Бэкап MASTER_KEY ОБЯЗАТЕЛЕН — потеря ключа = все сохранённые API-ключи и креды устройств нечитаемы.

После первого запуска зайди на https://<host>/setup — заведи админский аккаунт. Регистрация закроется; новые юзеры заходят только по инвайту.

добавить источник данных

Положи папку в integrations/:

integrations/
  fitbit/
    index.ts        // export default HealthProvider

Минимум для pull-провайдера:

const fitbit: HealthProvider = {
  id: 'fitbit', version: 1, displayName: 'Fitbit',
  description: '...', glyph: '◉', syncIntervalMinutes: 30,
  getSetupSteps()        { return [...] },
  validateCredentials(c) { ... },
  async sync(connection, since, creds) {
    return { metrics: [...], credentialsPatch?, configPatch?,
             deleteFutureTypes? }
  },
}
export default fitbit

Для push-провайдера (webhook вместо опроса) дополнительно реализуй ingest(connection, payload, creds) и создай роут /server/api/integrations/<id>/ingest.post.ts (см. apple-health как референс). sync() можно оставить no-op возвращающим { metrics: [] }.

deleteFutureTypes: string[] — список типов, у которых перед инсертом стираются все будущие записи (recorded_at > now) этой connection. Нужно для forecast'ов чтобы старые предсказания не накапливались.

разработка

pnpm install
pnpm dev                 # http://localhost:3000
pnpm db:generate         # после изменения server/db/schema.ts

Хук в nuxt.config.ts авто-сканит /integrations/ и /themes/ и генерит registry.generated.ts на каждый билд/dev-start — реестры обновляются сами.

безопасность

  • Все API-ключи моделей и креды устройств шифруются AES-256-GCM на MASTER_KEY перед попаданием в БД.
  • Расшифровка только на бэкенде в момент использования.
  • AI обычного юзера физически не может видеть данные другого: buildHealthContext(userId) принимает ровно один user_id. Tool query_metrics использует ctx.userId из сессии — нельзя подменить через arguments. Семейный путь — отдельный метод buildFamilyHealthContext(), доступный только из админских роутов (session.scope === 'all' требует requireAdmin).
  • Apple Health webhook авторизуется по per-connection токену (192 бита, cleartext в config.ingestToken — это shared-secret, юзер видит и копирует в HAE).
  • Session cookie httpOnly. На проде Caddy подаёт tls internal → HTTPS обязателен, COOKIE_SECURE=true.

лицензия

MIT — см. LICENSE.

About

Self-hosted health data aggregator with AI analysis.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Contributors

Languages