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)