Skip to content

jimmy-vps /events endpoint — deploy spec (SHIPPED)

project: hinata-infrastructuretype: deploy-specstatus: shippedshipped: 2026-05-26 20:30 (server + tunnel) · 20:48 (Studio Worker proxy) End-to-end live as of 2026-05-26. Studio Worker /api/events → CF Tunnel → api.michael-engineer.dev → FastAPI on jimmy-vps. ActivityLog renders the table.

Reality corrections (5 deltas this deploy uncovered)

  1. Database name: hinata (NOT hinata_collector). Football lives in the football. schema inside the same DB; events lives in public.events.

  2. DB access: synchronous psycopg via from db import get_conn (NOT asyncpg). with get_conn() as conn, conn.cursor() as cur: then cur.execute(...).

  3. Auth header: x-hinata-key enforced server-side per router via from auth import require_key (NOT Authorization: Bearer). All sibling tenants use the same header.

  4. Router shape: bare router = APIRouter() with no prefix in-file. Prefix applied at app.include_router(events.router, prefix="/events", tags=["events"]) in main.py.

  5. Token storage: hinata_collector_api_key.json (NOT .md). The 43-char token lives in the value field.

Final shipped artifacts

ArtifactPathRole

Install script~/Sandpit/hinata/scripts/jimmy-vps-add-events-tenant.shidempotent: schema + router + main.py wire + restart Tunnel install script~/Sandpit/hinata/scripts/jimmy-vps-cloudflared-tunnel-api.shidempotent: cloudflared install + tunnel create + DNS route + systemd service Public hosthttps://api.michael-engineer.devsingle VPS-facing subdomain; all tenants share it Studio Worker proxyapplications/hinata-studio/api/worker/index.ts route /api/eventsholds HINATA_COLLECTOR_KEY as wrangler secret; key never in browser bundle Producer~/Sandpit/hinata/scripts/emit-event.sh``x-hinata-key auth; reads JSON token's value field Consumerapplications/hinata-studio/src/components/ActivityLog.tsxfetches ${VITE_API_URL}/api/events?limit=50

Producer instrumentation

ProducerKind emittedTrigger

poll-monzo.py``monzo.writtennew transactions appended parse-apple-health.py``health.normalisednon-empty inbox parse result calendar-nudge.py``nudge.{pre|start|mid}Telegram nudge fired (per event tag)

Tunnel design — single backend subdomain

Cloudflare Tunnel hinata-collector-api (UUID 67a84c34-3ba0-43d6-ae39-03025a5b1c97) publishes one hostname → localhost:8080. FastAPI multiplexes by path (/events, /football, /musicmastery, /[next]). One DNS record, one ingress rule, all current + future tenants reachable.

Gotchas captured

  • Itachi token guard: deploy-studio.command now refuses any CLOUDFLARE_API_TOKEN value containing whitespace or non-ASCII (the placeholder template string was being exported, em dash breaking wrangler's Authorization header).

  • wrangler 4 commit-subject header: wrangler auto-reads git log -1 --format=%s and ships it as a deploy-metadata HTTP header. Latin-1 only. Sandpit commit subjects routinely contain em dashes. Both deploy scripts now pass explicit --commit-message "<ASCII string>" to bypass auto-detection.

◆ hinata · projects/hinata-infrastructure/events-endpoint-deploy.html · phase-19 conversion