From cdae928f373c0391a17218ec411d3d4935e653ac Mon Sep 17 00:00:00 2001 From: Frank Date: Sun, 23 Aug 2026 02:59:48 -0700 Subject: [PATCH] Homepage: settle design (grilling Q1-Q5), log open questions Q6-Q43, progress report MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Homepage.md: rewritten to the settled design (single site, user + /admin/ pages, server-side status.json via a 3-min systemd-timer checker, terminal TUI theme). Added mermaid diagrams: site structure, status states, checker flow. Recorded the classification (user vs admin; *arr excluded; LLM admin; backends status-only) and the documented future shift to client-side probing. - Homepage Design Grilling.md (new): the decision log — settled Q1-Q5 plus 38 open questions (Q6-Q43) across 10 categories, each with a recommendation. - docs/index.md: link the grilling page (satisfies the index-links-every-page test). - report/homepage-grilling-2026-08-22.html: self-contained terminal-TUI progress report (settled decisions, verified stack baseline, overnight processing, work threads, the open questions, and how to continue). Baseline recorded: Dankclaw fully up (10/10); the Dankserver media stack is down (only Calibre-Web + the LLM are up; no .env on the target) — a separate thread. No homepage code built yet; the design is pending the operator's answers to Q6-Q43. --- .../Homepage Design Grilling.md | 167 +++++++++++++ docs/Infrastructure/Homepage.md | 133 +++++++++-- docs/index.md | 1 + report/homepage-grilling-2026-08-22.html | 219 ++++++++++++++++++ 4 files changed, 499 insertions(+), 21 deletions(-) create mode 100644 docs/Infrastructure/Homepage Design Grilling.md create mode 100644 report/homepage-grilling-2026-08-22.html diff --git a/docs/Infrastructure/Homepage Design Grilling.md b/docs/Infrastructure/Homepage Design Grilling.md new file mode 100644 index 0000000..57709c6 --- /dev/null +++ b/docs/Infrastructure/Homepage Design Grilling.md @@ -0,0 +1,167 @@ +# Homepage Design Grilling + +The decision log for the homepage design. It records what is settled and the open questions that remain. Answer the open questions in the morning report; the design is built only after they are settled. + +## Decided + +| # | Decision | Value | +|---|----------|-------| +| Q1 | How the page learns up/down | **Server-side** checker writes `status.json`; the page reads only that file. (Eventual target: client-side probing.) | +| Q2 | How the checker runs | **systemd timer**, rewrites `status.json` every **3 minutes**. | +| Q3 | Page model | **Single site** on Dankclaw. Root = user services (no host labels). `/admin/` = admin services, separate password. Optional `/how/` tourist page. | +| Q4 | (superseded by Q3 restructure) | — | +| Q5 | Service split | User: SearXNG, ComfyUI, OpenWebUI, Gitea, Calibre-Web, RomM, Copyparty, Plex, Jellyfin. Admin: Hermes, Grafana, Prometheus, Loki, Caddy, dnsmasq, Gluetun, LLM fleet, low-level backends (status-only). The *arr stack is **excluded** (separate effort, not started). | + +Styling is settled in principle: terminal TUI, Terminus (monospace fallback), green-on-black, 1995 phosphor palette (green `#33ff66`, red `#ff5555`, amber `#ffcc33`, cyan `#33ccff`, bg `#050805`). + +## Open questions + +Each question carries a recommendation. Answer them in the morning report. + +### A. Architecture and structure + +**Q6 — The optional `/how/` "tourist" page: build now or defer?** +Recommendation: **defer** to a later effort. When built, make it **public** (no auth) so a visitor can see the architecture. It can render the mermaid diagrams from this repo. + +**Q7 — One `status.json` or two (`user-status.json` + `admin-status.json`)?** +Recommendation: **one** `status.json`. Each service carries a `category` field. Each page filters client-side. One file, one checker output, one source of truth. + +**Q8 — One `services.json` or split by page?** +Recommendation: **one** `services.json` with all services and a `category` field. The checker reads one file. Simpler to maintain. + +### B. The checker + +**Q9 — What language for the checker script?** +Recommendation: **python3** (available on Dankclaw, no dependencies, emits valid JSON cleanly). A bash + `nc` variant works too, but JSON by hand is error-prone. + +**Q10 — Probe method: TCP connect or HTTP GET?** +Recommendation: **TCP connect** to `host:port`. Uniform, works for non-HTTP backends, and "port listening = up" matches the verified ground truth. + +**Q11 — Probe timeout per service?** +Recommendation: **3 seconds**. A dead port refuses fast; a black-holed one times out at 3 s. With ~25 services the worst-case run is well under the 3-minute interval. + +**Q12 — LLM "loading" state (health returns 503 during model load)?** +Recommendation: keep **three** states. A listening port shows **Up**. Optionally probe `/health` and treat 503 as Up (the process is alive). A dedicated "loading" state is a later refinement. + +**Q13 — Where does the checker live in the repo?** +Recommendation: `scripts/homepage-status.py` plus `scripts/homepage-status.service` and `scripts/homepage-status.timer`, all tracked in git like the other units. + +**Q14 — How does the checker reach the Dankserver backends?** +Recommendation: the `probe` field carries a full `host:port`. Dankclaw services use `127.0.0.1:PORT`. Dankserver services use `192.168.0.235:PORT`. The checker (on Dankclaw) connects to each over the LAN. Verified reachable. + +### C. The status model + +**Q15 — How is a service marked WIP?** +Recommendation: a static `"wip": true` field. The checker skips the probe and the page shows WIP. Drop the old self-referential "Homepage" entry (a page does not list itself). + +**Q16 — Staleness: what if the timer fails and `status.json` is old?** +Recommendation: write a `generated_at` timestamp into `status.json`. The page shows a "status may be stale" warning when it is older than 10 minutes. + +**Q17 — Atomic writes?** +Recommendation: the checker writes to a temp file, then renames over `status.json`. A crash never leaves a half-written file. The last good status survives. + +### D. Auth + +**Q18 — The admin password: a second `.env` variable?** +Recommendation: yes. `CADDY_ADMIN_HASH` in the host `.env` (git-ignored), rendered into the Caddyfile at deploy, operator-only. The root page keeps `CADDY_BASICAUTH_HASH`. + +**Q19 — Per-path basicauth in Caddy v2?** +Recommendation: a `@admin path /admin/*` matcher with a second `basicauth @admin` block. Confirm the exact syntax against the Caddy docs during implementation. + +### E. The pages and content + +**Q20 — Where do the one-line descriptions come from?** +Recommendation: hand-written one-liners in `services.json`, drafted from the existing reference docs. + +**Q21 — The "Open" link target: the Caddy site or the direct backend?** +Recommendation: the **Caddy site** URL (what a user actually opens). Status-only backends set `showLink: false` (no Open link). + +**Q22 — The "docs" link: usage page or the existing reference doc?** +Recommendation: link to the **existing** reference doc (`docs/.../X.md`) as a placeholder. Swap to the per-service usage page when that workstream is built. + +**Q23 — Page layout: cards or a terminal-style list?** +Recommendation: a **terminal-style list** (one service per line, like `systemctl list-units`). Columns: `STATUS | NAME | description | (open) (docs)`. Fits the TUI aesthetic better than cards. + +**Q24 — A summary line at the top?** +Recommendation: yes. One line, e.g. `UP 9 DOWN 2 WIP 1`, per page. + +### F. Styling + +**Q25 — Terminus: bundle the font or use a fallback?** +Recommendation: **bundle** Terminus (woff2) via `@font-face`, with a monospace fallback stack. Confirm the license (Terminus is freely distributable) and drop the files in `fonts/`. + +**Q26 — Confirm the palette?** +Recommendation: as proposed (green `#33ff66`, red `#ff5555`, amber `#ffcc33`, cyan `#33ccff`, bg `#050805`). Tune for contrast and readability. + +**Q27 — CRT effects (scanlines + glow)?** +Recommendation: subtle scanlines + a soft green `text-shadow`. Make it toggleable (a `?crt=0` param) for those who find it much. + +**Q28 — Blinking cursor / animation?** +Recommendation: a subtle CSS-only blinking block cursor on the header. Respect `prefers-reduced-motion`. + +### G. The usage-docs workstream + +**Q29 — Scope of the per-service usage pages?** +Recommendation: concise **how-to** guides (what it is, how to open it, the common tasks). Distinct from the deep reference docs in `docs/`. + +**Q30 — Where do the usage pages live?** +Recommendation: served from the homepage site, same origin, at `/usage//`. Source in the repo under `shared/homepage/usage//`. Same origin keeps the "docs" link working. + +**Q31 — Are the usage pages password-protected?** +Recommendation: yes. User usage pages behind the shared password, admin usage pages behind the operator password. Consistent with the pages they extend. + +**Q32 — Is the usage-docs workstream a separate effort?** +Recommendation: yes, after the core homepage is live. For now the "docs" link points to the existing reference doc (Q22). + +### H. Deployment and operations + +**Q33 — How is the homepage deployed?** +Recommendation: extend `deploy.sh` (Dankclaw path): copy `shared/homepage/` to `/srv/homepage/`, install the timer + service, render the Caddyfile (both hashes), reload Caddy. + +**Q34 — Who creates `/srv/homepage/`?** +Recommendation: `deploy.sh` creates it and copies the assets. The checker writes `status.json` there at runtime. + +**Q35 — Is the timer enabled at boot?** +Recommendation: yes. A systemd timer + service, enabled, so it survives reboots and runs every 3 minutes. + +### I. Testing + +**Q36 — Tests for the checker?** +Recommendation: yes. A bats test runs the checker against a fake up port, a fake down port, and a WIP service, then validates the `status.json` schema. TDD, per repo rules. + +**Q37 — Tests for the pages and config?** +Recommendation: yes. Validate the `services.json` schema (required fields, valid categories), run `caddy validate` on the Caddyfile, and check the pages reference `status.json`. + +**Q38 — A security test for the two passwords?** +Recommendation: yes. Assert the Caddyfile carries two distinct basicauth hashes (root vs `/admin/`). + +### J. Edge cases and robustness + +**Q39 — A backend moves hosts (e.g. the *arr stack to a new box)?** +Recommendation: no code change. The `probe` field is a full `host:port`. A move is a config edit. + +**Q40 — A slow LAN hop to a Dankserver backend?** +Recommendation: the 3 s timeout handles it. A slow-but-alive backend connects (Up); a dead one times out (Down). + +**Q41 — Client-side auto-refresh?** +Recommendation: the page polls `status.json` every **60 s** (the file updates every 3 min; 60 s is responsive without hammering). + +**Q42 — Mobile responsiveness?** +Recommendation: yes. Stack the columns and shrink the font on narrow screens. + +**Q43 — A "last checked" timestamp on the page?** +Recommendation: yes. The footer shows `last checked HH:MM:SS` from `generated_at`. + +## How to continue + +Answer the open questions (Q6–Q43) in the morning report. Group the answers by category. Once settled, the build order is: + +```mermaid +flowchart LR + A[settle Q6-Q43] --> B[write services.json + checker] + B --> C[write the pages + theme] + C --> D[extend deploy.sh + Caddyfile] + D --> E[tests green] + E --> F[deploy to Dankclaw] + F --> G[verify in browser] +``` diff --git a/docs/Infrastructure/Homepage.md b/docs/Infrastructure/Homepage.md index c0aa8c2..36b4d36 100644 --- a/docs/Infrastructure/Homepage.md +++ b/docs/Infrastructure/Homepage.md @@ -1,56 +1,147 @@ # Homepage -A password-protected homepage on each host lists the services that are up, with a short description of each and a link to both the service and its documentation. +A password-protected web site on Dankclaw lists the services that are up, with a short description of each and a link to both the service and its usage documentation. -**Served by:** Caddy, `file_server` + `basicauth` — **Docs:** n/a (static page in this repo) +**Served by:** Caddy, `file_server` + `basicauth` — **Docs:** this page -## Purpose +## Status — 2026-08-22: work in progress -- One landing page per host instead of a bookmark folder. -- Shows at a glance which services are currently up and active. -- Answers "what is this?" for every service with a one-line description. -- Links to the service and to this repo's docs page for that service. +The homepage is not finished. The static assets exist in `shared/homepage/`. They are not yet copied to `/srv/homepage/` on the target. Caddy serves the `basicauth` challenge (HTTP 401) and the `:80` redirect (HTTP 302). The page content is absent. + +Do not treat the missing page as an outage. The Caddy site block and the redirect are live. Only the content and the deploy step remain. + +## Design decision — 2026-08-22 + +The live up/down check runs server-side. A checker on Dankclaw probes each backend and writes a same-origin `status.json`. The page fetches only that file and renders the badges. The browser never calls a backend directly. The checker runs as a systemd timer every 3 minutes. + +The site has two pages. The root lists user-facing services only, with no host labels. An `/admin/` subpage lists the admin and infrastructure services behind a separate password for the operator. Gitea is a user service. Hermes is an admin service. A "how it is built" page for visitors is optional and undecided. + +The client-side probe design is blocked in this topology. The page origin is `https://dankclaw.franksdank.cloud`. The service URLs are `http://.franksdank.cloud`. That is cross-origin, mixed content, and auth-gated (HTTP 401). All three fail a browser `fetch`. + +Intended direction: move to client-side probing. Serve every service as a same-origin `https` path under `dankclaw.franksdank.cloud`. Add a health endpoint and CORS headers to each backend. Then the page probes each service directly. Keep this note until that shift is complete. + +## Structure + +The site has three pages. The root page lists the user services. The `/admin/` page lists the admin and infrastructure services. The `/how/` page (optional) shows the architecture to visitors. + +```mermaid +graph TD + subgraph site["dankclaw.franksdank.cloud (Caddy)"] + ROOT["/ user services
(shared password)"] + ADMIN["/admin/ admin services
(operator password)"] + HOW["/how/ how it is built
(optional)"] + STATUS["/status.json"] + end + TIMER["systemd timer
every 3 minutes"] + CHECKER["checker script
probes each backend"] + BACKENDS["backends on both hosts"] + TIMER --> CHECKER + CHECKER -->|writes| STATUS + STATUS -->|polled| ROOT + STATUS -->|polled| ADMIN + CHECKER -->|probes| BACKENDS +``` + +## Status model + +Each service shows one of three states. + +```mermaid +stateDiagram-v2 + [*] --> Up + [*] --> Down + [*] --> WIP + Up --> Down: probe fails + Down --> Up: probe succeeds + WIP --> Up: work completes +``` + +- **Up** — the probe reached the backend. +- **Down** — the probe failed (connection refused or timeout). +- **WIP** — a static override. The service is intentionally not finished. The checker does not probe it. + +## Checker + +The checker runs on Dankclaw as a systemd timer. Every run probes each backend directly, then writes `status.json`. It probes the backend `host:port`, not the Caddy site. A Caddy `502` or `401` cannot tell up from down. + +```mermaid +flowchart TD + A[timer fires] --> B[read service config] + B --> C{for each service} + C -->|wip| D[mark WIP] + C -->|probe| E[TCP connect to host:port] + E -->|connected| F[mark Up] + E -->|refused / timeout| G[mark Down] + D --> H[write status.json] + F --> H + G --> H +``` + +## Service classification + +**User services** (root page): SearXNG, ComfyUI, OpenWebUI, Gitea, Calibre-Web, RomM, Copyparty, Plex, Jellyfin. + +**Admin services** (`/admin/`): Hermes, Grafana, Prometheus, Loki, Caddy, dnsmasq, Gluetun, the LLM fleet, and the low-level backends (mariadb, redis, Postgres, node-exporter, alloy). The backends show status only. They get a link only where a UI is meaningful. + +**Excluded for now:** the *arr stack (Radarr, Sonarr, Lidarr, Readarr, Prowlarr, qBittorrent). It is not started. It is a separate effort, possibly on a different host. ## Layout ``` -shared/ -└── homepage/ - ├── index.html # the page, shared logic, per-host service list injected - ├── style.css - └── services.json # service list: name, hostname, port, description, docs link +shared/homepage/ +├── index.html # root page (user services) +├── admin.html # admin + infrastructure services +├── how.html # optional architecture page +├── style.css # terminal TUI theme +├── app.js # loads status.json, renders the badges +├── services.json # service config (user + admin) +└── fonts/ # Terminus (woff2) for @font-face ``` -The page fetches `services.json` and pings each service's health endpoint (or the proxy hostname) to set an up/down badge. It is static; the status check runs in the browser via `fetch`. +`status.json` is generated at runtime in `/srv/homepage/`. It is not committed. ## Auth -Caddy wraps the homepage with HTTP basic auth: +Caddy wraps the site with HTTP basic auth. The root page uses the shared password. The `/admin/` page uses a second, operator-only password. The hashes come from the host `.env` (git-ignored). Do not commit the hash or the password. ``` -dankclaw.local { +dankclaw.franksdank.cloud { + @admin path /admin/* basicauth { - dank $2a$14$HASH + dank {env.CADDY_BASICAUTH_HASH} + } + basicauth @admin { + frank {env.CADDY_ADMIN_HASH} } root * /srv/homepage file_server } ``` -Generate the hash with `caddy hash-password` at deploy time. Do not commit the hash or the password. - -## Service Entry Format +## Service entry format ```json { "name": "SearXNG", - "hostname": "http://search.dankclaw.local", + "category": "user", "description": "Privacy-respecting metasearch engine.", - "docs": "/docs/Dankclaw/SearXNG.md" + "url": "http://search.franksdank.cloud", + "docs": "/usage/searxng/", + "probe": "127.0.0.1:8081", + "wip": false, + "showLink": true } ``` +- `category` — `user` or `admin`. Selects the page. +- `url` — the "Open" link (the Caddy site). +- `docs` — the per-service usage page. +- `probe` — the backend `host:port` the checker probes. +- `wip` — when true, the badge shows WIP and the checker skips the probe. +- `showLink` — when false, the card shows status only (no "Open" link). + ## See Also +- [[Homepage Design Grilling]] — the decision log and open questions - [[Caddy]] — the proxy that serves it - [[Dankclaw Stack]], [[Dankserver Stack]] — the services it lists diff --git a/docs/index.md b/docs/index.md index 62f708f..fd3b38c 100644 --- a/docs/index.md +++ b/docs/index.md @@ -65,6 +65,7 @@ covers both workloads). - [[LAN-DNS]] — name resolution, dnsmasq, router DNS hand-off - [[Caddy]] — reverse proxy and TLS - [[Homepage]] — password-protected service index +- [[Homepage Design Grilling]] — homepage decision log and open questions - [[Observability]] — Grafana, Prometheus, Loki, Alloy - [[Grafana]] — dashboards - [[Prometheus]] — metrics collection diff --git a/report/homepage-grilling-2026-08-22.html b/report/homepage-grilling-2026-08-22.html new file mode 100644 index 0000000..8228b68 --- /dev/null +++ b/report/homepage-grilling-2026-08-22.html @@ -0,0 +1,219 @@ + + + + + +Frank Program — Homepage Grilling — 2026-08-22 + + + +
+ +

frank@program:~/homepage$ grilling --progress

+

Frank Program · homepage design grilling · overnight progress report · 2026-08-22 · from the chief designer

+ +
+
+summary   settled: 5 decisions + styling   open: 38 questions (Q6-Q43)
+baseline  dankclaw: UP (10/10)   dankserver: media stack DOWN (no .env)
+state     WIP — /srv/homepage/ not deployed; Caddy site live (401 / 302)
+next      answer Q6-Q43 in the morning; then build order is fixed
+
+
+ +

What is settled

+ + + + + + + +
#DecisionValue
Q1How the page learns up/downServer-side checker writes status.json; the page reads only that file. (Eventual target: client-side probing — documented.)
Q2How the checker runssystemd timer, rewrites status.json every 3 minutes.
Q3Page modelSingle site on Dankclaw. Root = user services (no host labels). /admin/ = admin services, second operator-only password. Optional /how/ tourist page.
Q5Service splitUser: SearXNG, ComfyUI, OpenWebUI, Gitea, Calibre-Web, RomM, Copyparty, Plex, Jellyfin. Admin: Hermes, Grafana, Prometheus, Loki, Caddy, dnsmasq, Gluetun, LLM fleet, low-level backends (status-only). *arr stack excluded (separate effort).
—StylingTerminal TUI. Terminus (monospace fallback). Green-on-black. 1995 phosphor palette.
+

Palette: bg #050805 · text #33ff66 · up #33ff66 · down #ff5555 · WIP #ffcc33 · links #33ccff · headers #aaffcc.

+ +

Stack baseline (verified 2026-08-22)

+

Source: real HTTP fetch through Caddy + ss -ltn (they agree). The earlier /dev/tcp probe was the false one — it reported every port down while Caddy returned 200.

+ +

Dankclaw 192.168.0.143 — AI / search / dev / observability

+ + + + + + + + + + + + +
ServiceSiteHTTPPortState
Homepagedankclaw401—WIP auth-gated, not deployed
SearXNGsearch4018081 ✓UP (auth-gated)
ComfyUIcomfyui2008188 ✓UP
OpenWebUIchat4013005 ✓UP (auth-gated)
Hermeshermes4018090 ✓UP (auth-gated)
Giteagitea2003000 ✓UP
Grafanagrafana4013002 ✓UP (auth-gated)
Prometheusprometheus4019090 ✓UP (auth-gated)
Lokiloki4043100 ✓UP (no root page)
frank-default-large:7770ok7770 ✓UP
+ +

Dankserver 192.168.0.235 — media

+ + + + + + + + + +
ServiceSiteHTTPPortState
Calibre-Webbooks3028083 ✓UP
frank-default-small:7770ok7770 ✓UP
Plexplex502 / 00032400 boundUNDETERMINED port bound, no HTTP reply
RomMromm4018085 ✗DOWN (401 = auth challenge only)
Copypartyfiles4013923 ✗DOWN (401 = auth challenge only)
Jellyfinjellyfin5028096 ✗DOWN
Radarr / Sonarr / Lidarr / Readarr / Prowlarr / qBittorrent—502✗DOWN (not started — *arr effort)
+ +

My processing — what I worked out overnight

+
+

1The port-probe contradiction is resolved. My first probe (/dev/tcp nested in bash -c over SSH) reported every port down while Caddy returned 200 in the same call. The ss -ltn + HTTP-fetch pair is the ground truth and they agree. Do not trust the /dev/tcp one-liner in an SSH'd subshell.

+

2The 401 trap. For basicauth sites, Caddy serves the 401 challenge before the proxy. So a 401 does not prove the backend is up — RomM and Copyparty return 401 while their ports are down. This is why the checker must probe the backend host:port directly, never the Caddy site.

+

3The homepage's core mechanism is architecturally blocked. The page is https://dankclaw.franksdank.cloud; the service URLs are http://<name>.franksdank.cloud. A browser fetch of those is cross-origin (CORS-blocked), mixed content (https→http, hard-blocked), and auth-gated (401). Three independent failures. Hence the server-side status.json design.

+

4The Dankserver media stack is down, and the cause is a missing .env. Only Calibre-Web (no secret deps) and the LLM (separate systemd unit) are up. compose.yaml needs MARIADB_ROOT_PASSWORD, ROMM_*, COPAY_PASS, … — none present on the target. This is a separate, urgent work item, distinct from the homepage.

+

5Plex is in an odd state. Port 32400 is bound on 0.0.0.0 but a direct HTTP request gets no reply (000). Needs a look — it may be a stale socket or a non-HTTP listener. Flagged as UNDETERMINED, not counted up or down.

+

6LLM "loading" nuance. During model load the LLM /health returns 503. A TCP-connect probe still reads "up" (the port is listening). Kept to three states for now; a dedicated "loading" state is a later refinement.

+

7My host is outside the LAN. *.franksdank.cloud resolves to the public IP here; the services are LAN-only (dnsmasq maps them to 192.168.0.143 for LAN clients). So page loads and probes must run from Dankclaw.

+
+ +

Work threads identified

+ + + + + + + + + +
ThreadWhat it isStatus
1 · HomepageThis design. Server-side status, user + admin pages, terminal TUI.GRILLING 38 open questions
2 · Dankserver media stackDown. Needs a .env (secrets) + a compose deploy.BLOCKED separate, urgent
3 · *arr stackRadarr/Sonarr/Lidarr/Readarr/Prowlarr/qBittorrent. Not started; possibly a different host.NOT STARTED
4 · Usage-docsPer-service how-to pages, maintained separately from the reference docs.NOT STARTED
5 · "How it's built"Optional public architecture page for tourists.OPTIONAL
6 · PlexInvestigate the bound-but-silent port 32400.INVESTIGATE
7 · Client-side probingDocumented future intent: same-origin https paths + per-service health + CORS.LATER
+ +

Open questions — answer these in the morning

+

Each carries a recommendation. Reply AGREE to accept it, or state the change. Group by category letter.

+ +

A · Architecture and structure

+
Q6 — The optional /how/ "tourist" page: build now or defer?Rec: defer. When built, make it public (no auth) so a visitor can see the architecture; render the mermaid diagrams from this repo.verdict: AGREE · or change to: ____
+
Q7 — One status.json or two (user-status.json + admin-status.json)?Rec: one status.json; each service carries a category field; each page filters client-side.verdict: AGREE · or change to: ____
+
Q8 — One services.json or split by page?Rec: one services.json with a category field; the checker reads one file.verdict: AGREE · or change to: ____
+ +

B · The checker

+
Q9 — What language for the checker script?Rec: python3 (available on Dankclaw, no deps, clean JSON). A bash+nc variant works but hand-rolled JSON is error-prone.verdict: AGREE · or change to: ____
+
Q10 — Probe method: TCP connect or HTTP GET?Rec: TCP connect to host:port. Uniform, works for non-HTTP backends, matches the verified ground truth.verdict: AGREE · or change to: ____
+
Q11 — Probe timeout per service?Rec: 3 s. A dead port refuses fast; a black-holed one times out at 3 s. Well under the 3-minute interval.verdict: AGREE · or change to: ____
+
Q12 — LLM "loading" state (health 503 during model load)?Rec: keep three states; a listening port is Up. Optionally probe /health and treat 503 as Up. A "loading" state is a later refinement.verdict: AGREE · or change to: ____
+
Q13 — Where does the checker live in the repo?Rec: scripts/homepage-status.py + scripts/homepage-status.service + scripts/homepage-status.timer, tracked in git.verdict: AGREE · or change to: ____
+
Q14 — How does the checker reach the Dankserver backends?Rec: the probe field is a full host:port — 127.0.0.1:PORT for Dankclaw, 192.168.0.235:PORT for Dankserver. Verified reachable over the LAN.verdict: AGREE · or change to: ____
+ +

C · The status model

+
Q15 — How is a service marked WIP?Rec: a static "wip": true field; the checker skips the probe. Drop the old self-referential "Homepage" entry.verdict: AGREE · or change to: ____
+
Q16 — Staleness: what if the timer fails and status.json is old?Rec: write a generated_at timestamp; the page warns "status may be stale" when it is older than 10 minutes.verdict: AGREE · or change to: ____
+
Q17 — Atomic writes?Rec: write to a temp file, then rename over status.json. A crash never leaves a half-written file.verdict: AGREE · or change to: ____
+ +

D · Auth

+
Q18 — The admin password: a second .env variable?Rec: yes — CADDY_ADMIN_HASH in the host .env (git-ignored), rendered at deploy, operator-only. Root keeps CADDY_BASICAUTH_HASH.verdict: AGREE · or change to: ____
+
Q19 — Per-path basicauth in Caddy v2?Rec: an @admin path /admin/* matcher with a second basicauth @admin block. Confirm exact syntax against the Caddy docs during implementation.verdict: AGREE · or change to: ____
+ +

E · The pages and content

+
Q20 — Where do the one-line descriptions come from?Rec: hand-written one-liners in services.json, drafted from the existing reference docs.verdict: AGREE · or change to: ____
+
Q21 — The "Open" link: the Caddy site or the direct backend?Rec: the Caddy site URL (what a user opens). Status-only backends set showLink: false.verdict: AGREE · or change to: ____
+
Q22 — The "docs" link: usage page or the existing reference doc?Rec: link to the existing reference doc as a placeholder; swap to the per-service usage page when that workstream is built.verdict: AGREE · or change to: ____
+
Q23 — Page layout: cards or a terminal-style list?Rec: a terminal-style list (one service per line, like systemctl list-units). Columns: STATUS | NAME | description | (open) (docs).verdict: AGREE · or change to: ____
+
Q24 — A summary line at the top?Rec: yes — one line per page, e.g. UP 9 DOWN 2 WIP 1.verdict: AGREE · or change to: ____
+ +

F · Styling

+
Q25 — Terminus: bundle the font or use a fallback?Rec: bundle Terminus (woff2) via @font-face, with a monospace fallback. Confirm the license (freely distributable) and drop files in fonts/.verdict: AGREE · or change to: ____
+
Q26 — Confirm the palette?Rec: as proposed (green #33ff66, red #ff5555, amber #ffcc33, cyan #33ccff, bg #050805). Tune for contrast.verdict: AGREE · or change to: ____
+
Q27 — CRT effects (scanlines + glow)?Rec: subtle scanlines + a soft green text-shadow. Make it toggleable (?crt=0).verdict: AGREE · or change to: ____
+
Q28 — Blinking cursor / animation?Rec: a subtle CSS-only blinking block cursor on the header; respect prefers-reduced-motion.verdict: AGREE · or change to: ____
+ +

G · The usage-docs workstream

+
Q29 — Scope of the per-service usage pages?Rec: concise how-to guides (what it is, how to open it, the common tasks). Distinct from the deep reference docs.verdict: AGREE · or change to: ____
+
Q30 — Where do the usage pages live?Rec: served from the homepage site, same origin, at /usage/<svc>/; source under shared/homepage/usage/<svc>/.verdict: AGREE · or change to: ____
+
Q31 — Are the usage pages password-protected?Rec: yes — user pages behind the shared password, admin pages behind the operator password.verdict: AGREE · or change to: ____
+
Q32 — Is the usage-docs workstream a separate effort?Rec: yes, after the core homepage is live. For now the "docs" link points to the existing reference doc (Q22).verdict: AGREE · or change to: ____
+ +

H · Deployment and operations

+
Q33 — How is the homepage deployed?Rec: extend deploy.sh (Dankclaw path): copy shared/homepage/ to /srv/homepage/, install the timer + service, render the Caddyfile (both hashes), reload Caddy.verdict: AGREE · or change to: ____
+
Q34 — Who creates /srv/homepage/?Rec: deploy.sh creates it and copies the assets; the checker writes status.json there at runtime.verdict: AGREE · or change to: ____
+
Q35 — Is the timer enabled at boot?Rec: yes — a systemd timer + service, enabled, survives reboots, runs every 3 minutes.verdict: AGREE · or change to: ____
+ +

I · Testing

+
Q36 — Tests for the checker?Rec: yes — a bats test runs the checker against a fake up port, a fake down port, and a WIP service, then validates the status.json schema. TDD, per repo rules.verdict: AGREE · or change to: ____
+
Q37 — Tests for the pages and config?Rec: yes — validate the services.json schema, run caddy validate, and check the pages reference status.json.verdict: AGREE · or change to: ____
+
Q38 — A security test for the two passwords?Rec: yes — assert the Caddyfile carries two distinct basicauth hashes (root vs /admin/).verdict: AGREE · or change to: ____
+ +

J · Edge cases and robustness

+
Q39 — A backend moves hosts (e.g. the *arr stack to a new box)?Rec: no code change — the probe field is a full host:port; a move is a config edit.verdict: AGREE · or change to: ____
+
Q40 — A slow LAN hop to a Dankserver backend?Rec: the 3 s timeout handles it — a slow-but-alive backend connects (Up); a dead one times out (Down).verdict: AGREE · or change to: ____
+
Q41 — Client-side auto-refresh?Rec: the page polls status.json every 60 s (the file updates every 3 min).verdict: AGREE · or change to: ____
+
Q42 — Mobile responsiveness?Rec: yes — stack the columns and shrink the font on narrow screens.verdict: AGREE · or change to: ____
+
Q43 — A "last checked" timestamp on the page?Rec: yes — the footer shows last checked HH:MM:SS from generated_at.verdict: AGREE · or change to: ____
+ +

What I changed (knowledge base)

+ + + + + + + +
FileChange
docs/Infrastructure/Homepage.mdRewritten to the settled design: structure, status model, checker, auth, classification, layout. Added three mermaid diagrams (site structure, status states, checker flow).
docs/Infrastructure/Homepage Design Grilling.mdNew. The decision log: settled Q1–Q5 + the 38 open questions (Q6–Q43) with recommendations + the build-order diagram.
docs/index.mdLinked the new grilling page (satisfies the "index links every page" test).
~/.pi/agent/docs/log.mdAdded a homepage-design bullet to the 2026-08-22 entry (design, baseline, threads).
report/homepage-grilling-2026-08-22.htmlThis report. Self-contained, terminal-TUI styled.
+ +

How to continue

+
+

Reply to the open questions (Q6–Q43) — AGREE or the change, grouped by category. Once settled, the build order is fixed:

+
+settle Q6-Q43  ->  write services.json + checker  ->  write pages + theme
+   ->  extend deploy.sh + Caddyfile  ->  tests green  ->  deploy to Dankclaw
+   ->  verify in browser
+
+

Separate threads to schedule (not part of the homepage build): the Dankserver media stack (missing .env), the *arr stack, the usage-docs workstream, the optional "how it's built" page, and the Plex investigation.

+

Report is self-contained (no network needed). A copy is committed at report/homepage-grilling-2026-08-22.html and served at the URL below.

+
+ +

+generated 2026-08-22 (night) · frank program · chief designer · tests: 97/97 green +

+ +
+ +