2026-08-18 18:33:49 -07:00
# Phase 2 — Surface B: cluster topology
~3 days. Proves the snapshot + SSE pipeline end to end.
## RED
- [ ] `stream_test.go` — SSE emits a delta within 5s of a pod state change
- [ ] `topology.test.tsx` — graph re-renders on delta without a full reload
2026-08-18 19:10:51 -07:00
- [ ] `topology.e2e.ts` (Playwright, hard requirement) — load `/topology` , assert graph nodes render, delete a pod, assert node count updates without reload
2026-08-18 18:33:49 -07:00
## GREEN
- [ ] `GET /api/topology` — nodes, namespaces, workload summaries; envelope `{"data": {}, "meta": {"snapshotAge", "generation"}}` ; capped 256 KB, `meta.truncated: true` on overflow, never a silent drop
- [ ] `GET /api/stream` — SSE, session cookie, 2 concurrent/IP, `topology` event type; keepalive comment frame every 30s; `Last-Event-ID` supported for resumable deltas
- [ ] Frontend: React Flow, force layout, node → namespace → workload
- [ ] Security headers on all responses: CSP (no `unsafe-inline` ), `X-Content-Type-Options: nosniff` , `Referrer-Policy: no-referrer` , HSTS; CORS same-origin only
- [ ] Rate limiting Tier 1 (Cloudflare edge — WAF, Bot Fight Mode, per-IP rules) + Tier 2 (Kong `rate-limiting` , `policy: redis` , generous profile for topology)
## REFACTOR
- [ ] `simplify` pass on SSE handler + React Flow wiring
## Verify
```bash
# delete a pod, observe graph update in <5s without reloading
kubectl delete pod <name> -n <ns>
2026-08-18 19:10:51 -07:00
pnpm test:e2e topology.e2e.ts # required, must pass before merge
2026-08-18 18:33:49 -07:00
```
Next: [04-phase3-delivery.md ](04-phase3-delivery.md )