feat: stats API v1 — cached, observable, contract-first #5
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "feat/stats-api"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Implements the
stats-apiOpenSpec change — the gateway's reason to exist. All 16 tasks complete, fullnix flake checkgreen including the in-VM hurl contract suite.Public surface (v1, versioned, minimal):
GET /api/v1/summary?period=7d|30d(total + zero-filled per-day series),GET /api/v1/pages(top 20, events excluded),GET /api/v1/openapi.json(the contract, generated from the serving code). Everything else 404/405; stable{"error": code}shape; CORS*+Cache-Controlwith remaining TTL.Why it holds up: upstream recon from GoatCounter 2.7.0 source confirmed hard rate limits (4/s, 500/h per token). The cache (4 enum keys, single-flight, TTL 300s, stale-to-24h) caps worst-case upstream load at 48 refreshes/hour independent of public traffic; upstream failures degrade to stale-serving with the original
generated_at, then 502 only when nothing was ever cached. No retries against the rate-limited upstream, ever.Observability (rich, as demanded): tracing spans per request; Prometheus on an internal-only listener —
http_requests_total/duration,cache_events_total{hit,miss,stale_hit},upstream_requests_total{ok,timeout,connect_error,http_4xx,http_5xx,invalid_body}+ durations. Token provably never logged (redacted Debug + test).Contract workflow (first full run): 6 hurl files written before any code (verified red), now green locally and inside the NixOS VM e2e (seeded GoatCounter + sandboxed gateway with LoadCredential token).
openapi.jsoncommitted; newopenapi-snapshotflake check fails the build on contract drift. 18 unit/wiremock tests cover every failure mode.⚠️ Review flags (decided overnight, veto welcome — design.md D2/D3/D8): no public auth (public data + cache-bounded load), fixed period enum only, metrics listener defaults on at 127.0.0.1:9090 when deployed via the module.
Docs: new book chapter (Public API), dev/architecture chapters, README, CLAUDE.md.
🤖 Generated with Claude Code
https://claude.ai/code/session_01T5j3dAJVcUjZv9k76sRTt9