diff --git a/docs/superpowers/specs/2026-06-02-void-v2-plan6-sacred-valley-design.md b/docs/superpowers/specs/2026-06-02-void-v2-plan6-sacred-valley-design.md index 310ea51..e9435d0 100644 --- a/docs/superpowers/specs/2026-06-02-void-v2-plan6-sacred-valley-design.md +++ b/docs/superpowers/specs/2026-06-02-void-v2-plan6-sacred-valley-design.md @@ -46,7 +46,7 @@ The two bands are kept visually and structurally separate, by explicit request. | Real-time | **Per-card polling**, no new SSE. | | Service registry (LB) | **Config file** now; DB-backed + Proxmox auto-discovery are noted future upgrades. | | Little Blue fix-it tools | **Deferred** to a later agent phase. | -| Service icons | Auto-pulled from the walkxcode/dashboard-icons CDN, first-letter fallback (parity with v1). | +| Service icons | **Cached locally, server-side.** The server fetches each needed icon once from dashboard-icons into a persistent cache and serves it from the LAN. Browser never hits the CDN; no per-request slug leak. First-letter fallback on miss. | ## 4. Architecture @@ -151,12 +151,24 @@ services for the user to correct. ### 6.5 Rendering - `public/views/health_band.js` + `public/components/service_tile.js`. -- Tile: auto-icon (``, - slug = `icon` or slugified `name`, first-letter `
` fallback on `onerror`), - status dot (ok/warn/down), name, host, hover "open ↗" link → `url`. +- Tile: auto-icon (`` served from + the **local cache** §6.6; slug = `icon` or slugified `name`; first-letter `
` + fallback on `onerror`), status dot (ok/warn/down), name, host, hover "open ↗" + link → `url`. - Grouped sections with header + "X/Y healthy" + divider, refreshed every 60s. +### 6.6 Local icon cache (no CDN leak) +- `lib/health/icons.js` + a persistent cache dir `ICON_CACHE` + (default `/var/lib/void/icons`, like the blob store — survives deploys). +- `GET /api/icons/:slug.png` serves the cached file. On a **cache miss**, the + server fetches `.png` once from dashboard-icons + (`cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/.png`), validates it's + a PNG, writes it to the cache, then serves it. Subsequent requests are local. +- Slug is constrained to `[a-z0-9-]` (no path traversal, no arbitrary upstream). +- On upstream 404/error, respond 404 → the tile falls back to the first-letter + badge. Only the **server** ever contacts the CDN, once per distinct icon. +- "As needed": icons are fetched lazily on first reference, not bulk-synced. + ## 7. Theme tokens Add to `public/style.css :root`: - Reuse `--accent` / `--accent-dim` / `--panel` / `--border` for refined-B chrome. @@ -172,6 +184,8 @@ Backend (vitest): - registry loader: parse + group + ordering + icon-slug derivation. - `dashboard_layout` repo: get/put + owner scoping. - `/api/health/services` grouping + healthy counts. +- icon cache: miss → fetch-once → cached file served; slug sanitization rejects + traversal/invalid chars; upstream 404 → 404 (browser falls back). Frontend (pure-logic units): - card module contract (each exports id/title/size/mount/start/stop). @@ -200,4 +214,5 @@ Security: confirm the health checker rejects targets outside the registry. - Little Blue fix-it agent (chat + repair/Proxmox tools). - DB-backed + Proxmox-auto service registry. - Multi-host metrics; per-agent theming; final avatar art; agent-output cards. -- Optional self-hosted service-icon set (drop the CDN dependency). +- Optional: pre-seed/vendor the icon cache at deploy (fully offline, no first-use + CDN fetch) and a periodic refresh.