docs: add Claude rules and workspace configuration
This commit is contained in:
46
.claude/rules/fastapi-dashboard.md
Normal file
46
.claude/rules/fastapi-dashboard.md
Normal file
@@ -0,0 +1,46 @@
|
||||
---
|
||||
description: dashboard /api routes, tool-runner, metrics middleware, health routes, HAProxy edge, mount order
|
||||
paths:
|
||||
- "nike/server.py"
|
||||
---
|
||||
|
||||
# FastAPI dashboard, routing & edge
|
||||
|
||||
- **Mount order matters and is load-bearing.** `dashboard.mount("/mcp", _mcp_app)`
|
||||
goes **before** the SPA `StaticFiles` mount at `/` (which is greedy with
|
||||
`html=True` SPA fallback). MCP must be mounted first or `/` swallows `/mcp`. The
|
||||
MCP ASGI app is built early (`mcp.http_app(path="/")`) so its lifespan can be
|
||||
nested inside the FastAPI lifespan — keep that wiring.
|
||||
|
||||
- **`/api/*` routes are thin façades** over `db`, `sportsdb`, and `mcp`. `/api/status`
|
||||
(health cards), `/api/tools` (derived from `mcp.list_tools()` — the single source
|
||||
the dashboard reads), `/api/logs` (the in-memory request log), `/api/cache/invalidate`
|
||||
(flush both caches), `/api/run` (tool-runner: `mcp.get_tool` → `tool.fn(**args)`),
|
||||
`/api/v1/telemetry` (browser error sink). Keep `/api/tools` derived, never
|
||||
hand-maintained.
|
||||
|
||||
- **Prometheus middleware skips health/MCP paths.** `_SKIP_METRICS_PREFIXES =
|
||||
("/live","/ready","/metrics","/mcp")` — probes and MCP transport must not inflate
|
||||
`nike_http_requests_total`. It also normalises UUIDs in paths to `{id}` to bound
|
||||
label cardinality. Keep both behaviours; add new health-ish prefixes to the skip
|
||||
list.
|
||||
|
||||
- **`/metrics` is Prometheus exposition, IP-gated in Python.** `_METRICS_ALLOWED_NETS`
|
||||
= the standard four ranges (`10.10.0.0/24`, `172.16.0.0/12`, `127.0.0.0/8`,
|
||||
`::1/128`), no auth — HAProxy/Prometheus can't authenticate (`red_panda_standards.md`).
|
||||
Don't widen it or add app-side auth.
|
||||
|
||||
- **Health routes register both slash forms** (`/live` + `/live/`, `/ready` +
|
||||
`/ready/`). `/ready` returns 503 when `db.check_connection()` reports down. `/live`
|
||||
is a bare `{"status": "ok"}`.
|
||||
|
||||
- **HAProxy is the edge.** `uvicorn.run(..., proxy_headers=True,
|
||||
forwarded_allow_ips=config.TRUSTED_PROXY_IPS, ws="wsproto")`. The write routes
|
||||
(`/api/run`, `/api/cache/invalidate`) are **not individually authenticated** —
|
||||
access control is at HAProxy; the code comment says so. If you add a write route,
|
||||
keep that assumption explicit; if Nike could be exposed directly, it needs auth.
|
||||
`/api/v1/telemetry` stays unprotected on purpose (`sendBeacon` can't set headers).
|
||||
|
||||
- **Structured logging is re-applied in the lifespan** (`configure_logging` after
|
||||
`create_pool`) because uvicorn overwrites handlers on `config.load()`. Don't drop
|
||||
the second call, or JSON logging silently reverts to uvicorn's default.
|
||||
Reference in New Issue
Block a user