Reference context api

Location: /home/justinwieb/forge/infra/context-api/

Service: forge-context-api.service (systemd, enabled, auto-restarts). Binds 127.0.0.1:7358 — LAN + Tailscale only, never Cloudflare Tunnel.

Auth: Bearer token in .env as CONTEXT_API_TOKEN (auto-generated on first boot; rotate with systemctl restart forge-context-api).

Database: SQLite WAL at /home/justinwieb/forge/data/context.db (gitignored). Tables: events_raw, facts_wellness, facts_home, sources, consents. Rule #1 tripwire — service refuses startup if any active consent row has email=[email protected].

Endpoints: - GET / — service info - GET /healthz — row counts + source status - GET /sources (auth) — source registry - POST /ingest/<source> (auth) — normalized fact payloads - GET /context?about=wellness|home|all&window=24h (auth) — curated JSON

Invoke from fleet agents: /context skill at .claude/skills/context/SKILL.md.

Poller: infra/context-api/scripts/ha_poller.py runs every 15 min via cron. Maps HA Eight Sleep + Garmin sensors → facts_wellness rows with UNIQUE(ts, source, user_side, metric) to de-dupe.

Operations:

bash infra/context-api/setup.sh                          # create venv + init DB
sudo systemctl [start|stop|restart|status] forge-context-api
journalctl -u forge-context-api -f                       # logs
infra/context-api/.venv/bin/python infra/context-api/scripts/ha_poller.py   # manual ingest

TOKEN=$(grep CONTEXT_API_TOKEN .env | cut -d\' -f2)
curl -H "Authorization: Bearer $TOKEN" "http://127.0.0.1:7358/context?about=wellness&window=7d" | jq .

Phase 0 status: - ✓ FastAPI + SQLite + auth live - ✓ HA → facts_wellness via ha_poller.py - ✓ /context endpoint returning curated JSON - ☐ Weather (NWS) normalizer - ☐ Frigate normalizer - ☐ Personal ICS normalizer - ☐ Daily rollup cron writes memory/daily/YYYY-MM-DD_rollup.md - ☐ 5 consecutive days of clean ingestion (exit criterion)