Files
Void-Homelab/docs/superpowers/specs/2026-06-02-void-v2-plan6-sacred-valley-design.md
root f2f1ee4b10 docs: Plan 6 (Sacred Valley widgets) design spec
Two-band dashboard: draggable data cards (clock/weather/host-perf/speedtest/
jobs/inbox/search) + Little Blue read-only Health band (config registry +
pg-boss health engine + grouped service tiles w/ auto-icons). Refined-B chrome,
server-side layout persistence, polling refresh. Fix-it agent deferred.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-02 22:03:11 +10:00

204 lines
9.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Void 2.0 — Plan 6: Sacred Valley Widgets — Design Spec
> Status: APPROVED 2026-06-02. Design-led phase; brainstormed with the
> `frontend-design` skill and the visual companion. Next step: `writing-plans`.
> Supersedes the prep brief `docs/plan-6-brainstorm-brief.md`.
## 1. Purpose
Bring the Sacred Valley homelab dashboard to Void 2.0, on v2's real backend.
The `#/sacred-valley` route currently holds a stub (`public/views/sacred_valley.js`);
this plan replaces it with a working dashboard composed of **two distinct bands**:
1. **Data cards** — a draggable, reorderable set of widget cards.
2. **Little Blue's Health band** — a separate, fixed zone owned by the Little Blue
agent persona, showing homelab service health & uptime.
The two bands are kept visually and structurally separate, by explicit request.
## 2. Scope
### In scope (ships as `2.0.0-alpha.8`)
- The two-band Sacred Valley view + grid framework + drag-to-reorder.
- Seven data cards: clock, weather, host-perf, speedtest, jobs, inbox, search.
- Little Blue **read-only** Health band: config-file service registry, a pg-boss
health-check engine, grouped status tiles with auto-pulled icons, links, and a
placeholder avatar/identity.
- Server-side, owner-scoped layout persistence.
### Explicit non-goals (deferred)
- Little Blue as a **fix-it agent** — chat + repair/Proxmox tools. Belongs with the
later Yerin/Orthos agent-wiring phase.
- DB-backed and Proxmox-auto-discovery service registries (future upgrade path).
- Multi-host host-perf (Plan 6 monitors CT 311 only; "other hosts up?" is the
health band's job).
- Per-agent hue theming, `--rail-accent`, final Little Blue avatar art.
- Agent-output cards (Dross briefing / Yerin pulse / Orthos council).
## 3. Decisions (from the brainstorm)
| Decision | Choice |
|---|---|
| Grid engine | Custom CSS-grid auto-flow + hand-rolled drag-to-reorder. **No resize, zero deps.** Sizes are S/M/L presets (column spans 1/2/3). |
| Scope of dashboard | One **global** homelab dashboard (not per-Space). |
| Layout persistence | **Server-side**, owner-scoped, so desktop ↔ phone sync. localStorage mirrors for instant paint. |
| Card chrome | **"Refined B"** — dark panel, faint engraved texture, accent-underlined Cinzel title, low blackflame glow that **intensifies on hover**. |
| 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). |
## 4. Architecture
### 4.1 View structure
`public/views/sacred_valley.js` (replaces stub) renders into `#main`:
- `#sv-cards` — the data-card band (draggable).
- `#sv-health` — Little Blue's health band (fixed).
A small scheduler starts each card's refresh timer on view-mount and clears all
timers on unmount (route change), so polling never leaks across views.
### 4.2 Card component contract
`public/components/sv_card.js` is a factory producing the refined-B chrome
(`.sv-card` + theme tokens). Each data card is a self-contained module in
`public/views/cards/` exporting a uniform interface:
```js
export default {
id: 'host-perf',
title: 'Host Perf',
size: 'm', // 's' | 'm' | 'l' → grid column span 1 | 2 | 3
mount(bodyEl) { /* build DOM via safe el()/mount() — never innerHTML from data */ },
start() { /* begin refresh interval */ },
stop() { /* clear interval */ }
};
```
One module per card — each independently understandable and testable. All DOM is
built with the existing safe `el()/mount()`/`safeHref()` helpers (Plan 2 safe-DOM
invariant: no `innerHTML` from API data).
### 4.3 Grid + reorder
CSS-grid auto-flow; S/M/L = column spans. A hand-rolled pointer-drag reorder on
`#sv-cards` (no resize). Reordering updates the in-memory order and persists it
(§4.4). The health band is not draggable.
### 4.4 Layout persistence
- Migration: `dashboard_layout` table, owner-scoped, single logical row:
`{ order: text[]/jsonb, hidden: jsonb, sizes: jsonb }` + `updated_at`.
- API: `GET /api/dashboard/layout`, `PUT /api/dashboard/layout` (owner bearer).
- Client: localStorage mirror for first paint; server is source of truth.
## 5. Data cards
| Card | Size | Source | Backend |
|---|---|---|---|
| clock | S | Client only. Melbourne primary; optional secondary TZ. | none |
| weather | S | Open-Meteo, Melbourne, no key. | **new** `GET /api/weather` — server proxy, 15-min cache |
| host-perf | M | CT 311 `/proc` CPU/RAM/disk/net, 30s. | **new** `GET /api/host` — port v1 `lib/resources.js``lib/host/resources.js` |
| speedtest | M | Latest + ~30-bar history. | **new** `speedtest_results` migration + pg-boss recurring `speedtest` job (hourly `speedtest-cli`) + `GET /api/speedtest/history` + `POST /api/speedtest/run` (enqueue) |
| jobs | M | pg-boss states — counts by state + recent. | **reuse** Jobs API |
| inbox | S | Pending changes awaiting approval — count + recent; links `#/inbox`. | **reuse** pending_changes API |
| search | L | Spotlight: type → results → navigate. | **reuse** `GET /api/search` (FTS+vector RRF) |
Notes:
- `speedtest-cli` (or `speedtest` Ookla CLI) must be present on the worker box;
`push-workers.sh` / deploy notes updated accordingly.
- `/api/weather` caches server-side (15 min) to avoid hammering Open-Meteo.
## 6. Little Blue Health band
### 6.1 Identity
- `public/components/littleblue_avatar.js`**placeholder** blue humanoid
water-creature (inline SVG, cyan `--lb` glow). Marked placeholder; final art later.
- Header subtitle: "Health & Uptime of the lab".
### 6.2 Service registry (config file)
`config/services.yaml`, authored fresh — **v1 tile titles are NOT inherited**
(they were mislabeled); every entry gets a correct title here.
```yaml
- id: gitea
name: Gitea
category: infrastructure # agents | infrastructure | media
host: ct105 # display label on the tile
url: http://192.168.1.223:3000 # link target — "how to get there"
icon: gitea # optional; defaults to slugified name
check:
type: http # http | tcp
# url defaults to `url`; expect 2xx3xx; short timeout
```
Group render order: **Agents → Infrastructure → Media → other**. Each group header
shows a "X/Y healthy" count. The build seeds the file with the real homelab
services for the user to correct.
### 6.3 Health-check engine
- A **Node pg-boss recurring job** `health.check` (every ~60s) pings each service
(HTTP/TCP, short timeout) from void-server and writes a cache row to a
`service_status` table: `{ service_id, status, latency_ms, detail, checked_at }`.
- Status semantics: `ok` = check passed · `warn` = reachable but slow/degraded ·
`down` = unreachable/error.
- Decoupled from page load → the band renders instantly from cache.
- The checker only fetches URLs from the operator-authored registry (no user
input) — pinned to the configured list (SSRF surface is operator-controlled).
### 6.4 API
- `GET /api/health/services` → services grouped by category, each with cached
status + per-group healthy counts.
- `POST /api/health/check` → enqueue an immediate pass (owner-only). Backs the
"Run checks" button.
### 6.5 Rendering
- `public/views/health_band.js` + `public/components/service_tile.js`.
- Tile: auto-icon (`<img loading="lazy"
src="https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/<slug>.png">`,
slug = `icon` or slugified `name`, first-letter `<div>` 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.
## 7. Theme tokens
Add to `public/style.css :root`:
- Reuse `--accent` / `--accent-dim` / `--panel` / `--border` for refined-B chrome.
- Add `--lb: #7dd3d8` (Little Blue cyan) for the health band.
- A commented per-agent-hue block (Dross/Yerin/Orthos) reserved, unused this plan.
## 8. Testing (TDD, real DB per v2 norms)
Backend (vitest):
- weather proxy cache behavior (mock fetch).
- `/api/host` response shape.
- speedtest repo + `/api/speedtest/history`.
- health engine status computation (ok/warn/down from mocked fetch/connect).
- registry loader: parse + group + ordering + icon-slug derivation.
- `dashboard_layout` repo: get/put + owner scoping.
- `/api/health/services` grouping + healthy counts.
Frontend (pure-logic units):
- card module contract (each exports id/title/size/mount/start/stop).
- reorder ordering logic.
- category ordering (Agents→Infra→Media→other).
- safe-DOM: no `innerHTML` from data.
Security: confirm the health checker rejects targets outside the registry.
## 9. Build order (design risk → integration risk)
1. Grid framework + card contract + refined-B chrome + reorder + layout
persistence, proven on **clock / weather / host-perf**.
2. Reuse cards — **jobs / inbox / search**.
3. **speedtest** (worker + table + cron).
4. **Little Blue health band** (registry + engine + tiles + icons + avatar
placeholder).
## 10. Release
- Version → `2.0.0-alpha.8` (bump `server.js` VERSION const + `package.json` +
CHANGELOG).
- New migrations: `dashboard_layout`, `service_status`, `speedtest_results`.
- Standard deploy: snapshot CT 310 + 311, `deploy/push.sh`, run `npm run migrate`,
refresh workers if `push-workers.sh` changed, verify `/health` = alpha-8.
## 11. Future (out of this plan)
- 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).