Skip to main content
Glama
nnishad
by nnishad
README.md
# health-relay

Self-hosted bridge between your **Samsung Watch → Sync2Ra1 phone app** and your
**Hermes AI agent**. The app pushes health readings here over your LAN; the data is
stored locally (SQLite) and exposed via REST **and** MCP so your agent can query it.

```
Galaxy Watch ─▶ Samsung Health ─▶ Sync2Ra1 (phone) ──push──▶ health-relay ──MCP/REST──▶ Hermes
```

All data stays on your network. Nothing is sent anywhere else.

---

## Quick start (Docker or Podman)

```bash
echo "HR_AUTH_TOKEN=<choose-a-secret>" > .env
docker compose up -d          # or: podman compose up -d
curl http://localhost:8790/healthz     # -> {"ok":true}
```

Data persists in `./data/ledger.db` (SQLite, WAL mode).

### Run without Docker

```bash
python3 -m venv .venv && .venv/bin/pip install -r requirements.txt
HR_AUTH_TOKEN=<secret> ./run.sh
```

---

## Point the phone app at it

In **Sync2Ra1 → Data tab → Relay (Hermes bridge)**:

| Field | Value |
|---|---|
| Relay URL | `http://<this-machine-ip>:8790` |
| Token | same secret as `HR_AUTH_TOKEN` |

Tap **Save → Sync now**. Readings arrive within seconds.

---

## Connectivity

```
phone (Sync2Ra1) ──HTTP :8790──▶ health-relay ◀──MCP stdio / HTTP── Hermes
        same LAN · or Tailscale for anywhere-access
```

| Rule | Detail |
|---|---|
| Direction | Phone → relay (**outbound** from phone; nothing connects into the phone) |
| Port | **TCP 8790** inbound to the machine running the relay — open it in that host's firewall |
| Protocol | Plain HTTP + `Authorization: Bearer <token>`. Keep it LAN-only, or front with a reverse proxy (Caddy/nginx) for TLS |
| Anywhere-access | Install **Tailscale** on the box and phone; point the app at the Tailscale IP (`http://100.x.y.z:8790`) — encrypted, works over mobile data, no ports exposed to the internet |
| Cadence | App auto-syncs hourly + on app launch + manual *Sync now* |
| Offline | Phone buffers everything in local SQLite; on reconnect it pushes the full backlog (oldest first) and re-syncs the last 48 h so Samsung's revisions propagate |
| Duplicates | Impossible to double-count — every reading has a unique key; replays are ignored server-side |
| Hermes location | Same machine (MCP runs as a local subprocess reading the SQLite file) or remote (use the REST endpoints instead of MCP) |

---

## REST API (bearer auth)

| Endpoint | Purpose |
|---|---|
| `GET /healthz` | liveness (no auth) |
| `POST /api/v1/health/samples` | batch ingest — idempotent, replays are no-ops |
| `GET /api/v1/health/samples?type=&from_ms=&to_ms=&limit=&offset=` | raw readings |
| `GET /api/v1/health/daily-summary?date=YYYY-MM-DD&tz=Europe/London` | per-metric min/max/avg/count |
| `GET /api/v1/health/trends?type=X&days=N&tz=Europe/London` | daily rollups |
| `GET /api/v1/health/status` | totals per type |

```bash
curl -H "Authorization: Bearer <secret>" \
  "http://localhost:8790/api/v1/health/daily-summary?date=$(date +%F)&tz=Europe/London"
```

---

## Plug into Hermes (MCP)

The MCP server reads the same SQLite file — run it from this repo:

```bash
python3 -m venv .venv-mcp
.venv-mcp/bin/pip install fastmcp
```

Add to your agent's MCP config:

```json
{
  "mcpServers": {
    "health-relay": {
      "command": "<repo-path>/.venv-mcp/bin/python",
      "args": ["<repo-path>/mcp_server.py"],
      "env": { "HR_DATA_DIR": "<repo-path>/data" }
    }
  }
}
```

### Tools your agent gets

| Tool | Answers questions like |
|---|---|
| `list_streams()` | *"what health data do you have?"* |
| `get_samples(type, from_ms, to_ms)` | *"show my heart rate this morning"* |
| `daily_summary(date, tz)` | *"how active was I yesterday?"* |
| `sleep_breakdown(nights)` | *"how did I sleep this week?"* |

Day boundaries respect your timezone (`tz="Europe/London"` etc., DST handled).

---

## Recorded streams

Heart rate · SpO₂ · skin temperature · respiration · resting HR · VO₂max ·
sleep duration/score/stages · steps & floors (hourly + daily) · workouts ·
activity calories/time/distance · body composition (BIA) · energy score ·
blood pressure · glucose · hydration · meals.

---

## Configuration

| Env var | Default | |
|---|---|---|
| `HR_AUTH_TOKEN` | *(empty = auth off)* | set this in production |
| `HR_PORT` | `8790` | |
| `HR_HOST` | `0.0.0.0` | keep on trusted LAN |
| `HR_DATA_DIR` | `~/health-relay-data` | SQLite lives here |

## Tests

```bash
.venv/bin/python -m pytest tests/ -q
```