NamespaceSummary (test-first) renders the substrate ns line from live cluster
namespaces (system-filtered, sorted, capped). Handler now overlays both node
specs and the ns line via a single cached liveOverlay(). Removed the inline
SUBSTRATE/STAGES/NS data — /api/atlas.json is the single source; the page shows
an error banner on fetch failure instead of stale data.
GPU model naming intentionally NOT done: koala's node has no GPU product label
(only nvidia.com/gpu count), so "1× GPU" is the API's truth — naming the RTX 5070
would need GPU-feature-discovery, out of scope.
Verified: build/vet/lint(0)/test green.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The substrate machines are now rendered from the live k3s nodes (arch, cpu,
memory, GPU, k3s version), read in-cluster via the mounted ServiceAccount with a
stdlib HTTP client (no client-go) and a 30s cache; falls back to the authored
substrate whenever the cluster is unreachable. Placement per the homelab decision
"in-cluster only for in-workload ops" — a running web app rendering live data fits.
HostsFromNodes parser built test-first. RBAC (read-only node-reader SA) shipped in
infra k3s/apps/cad-atlas/.
Verified: go build/vet/lint(0)/test green.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>