Files
serverAppConfigs/report/homepage-grilling-2026-08-22.html
Frank cdae928f37 Homepage: settle design (grilling Q1-Q5), log open questions Q6-Q43, progress report
- 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.
2026-08-23 02:59:48 -07:00

220 lines
26 KiB
HTML
Raw Permalink 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.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Frank Program — Homepage Grilling — 2026-08-22</title>
<style>
:root{
--bg:#050805; --fg:#33ff66; --dim:#2f8f52; --down:#ff5555; --wip:#ffcc33;
--link:#33ccff; --head:#aaffcc; --border:#1a4d2e; --panel:#0a120a;
}
*{box-sizing:border-box}
body{
background:var(--bg); color:var(--fg);
font-family:"Terminus","Fira Code","DejaVu Sans Mono","Consolas",monospace;
font-size:15px; line-height:1.55; margin:0; padding:24px;
text-shadow:0 0 4px rgba(51,255,102,.30);
}
body::before{
content:""; position:fixed; inset:0; pointer-events:none; z-index:9999;
background:repeating-linear-gradient(0deg, rgba(0,0,0,0) 0 2px, rgba(0,0,0,.14) 2px 4px);
}
.wrap{max-width:1120px; margin:0 auto}
h1{color:var(--head); font-size:20px; border-bottom:1px solid var(--border); padding-bottom:10px; margin:0 0 4px}
h2{color:var(--head); font-size:16px; margin:34px 0 10px}
h2::before{content:"## "; color:var(--dim)}
h3{color:var(--head); font-size:14px; margin:22px 0 6px}
.sub{color:var(--dim); font-size:13px; margin-top:0}
.panel{border:1px solid var(--border); background:var(--panel); padding:14px 16px; margin:12px 0}
pre{background:#030603; border:1px solid var(--border); padding:12px; overflow:auto; color:var(--fg); font-size:13px; line-height:1.4}
code{color:var(--head)}
table{border-collapse:collapse; width:100%; font-size:13px; margin:8px 0}
th,td{border:1px solid var(--border); padding:6px 9px; text-align:left; vertical-align:top}
th{color:var(--head); background:#081108}
.up{color:var(--fg)} .down{color:var(--down)} .wip{color:var(--wip)} .und{color:var(--wip)}
.badge{border:1px solid currentColor; padding:0 6px; border-radius:2px; white-space:nowrap}
a{color:var(--link); text-decoration:underline}
.q{border-left:3px solid var(--border); padding:6px 14px; margin:14px 0}
.qn{color:var(--head); font-weight:bold}
.rec{color:var(--fg); background:#08130a; border:1px dashed var(--border); padding:7px 11px; margin-top:7px; display:block}
.rec b{color:var(--head)}
.verdict{color:var(--dim); font-size:12px; margin-top:7px}
.verdict b{color:var(--wip)}
.cursor{display:inline-block; width:9px; height:17px; background:var(--fg); vertical-align:-2px; animation:blink 1s steps(1) infinite}
@keyframes blink{50%{opacity:0}}
.dim{color:var(--dim)}
.ok{color:var(--fg)} .no{color:var(--down)}
.tag{display:inline-block; border:1px solid var(--border); padding:0 6px; border-radius:2px; font-size:12px; color:var(--dim); margin-right:6px}
</style>
</head>
<body>
<div class="wrap">
<h1>frank@program:~/homepage$ grilling --progress <span class="cursor"></span></h1>
<p class="sub">Frank Program &middot; homepage design grilling &middot; overnight progress report &middot; 2026-08-22 &middot; from the chief designer</p>
<div class="panel">
<pre>
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
</pre>
</div>
<h2>What is settled</h2>
<table>
<tr><th>#</th><th>Decision</th><th>Value</th></tr>
<tr><td>Q1</td><td>How the page learns up/down</td><td><b>Server-side</b> checker writes <code>status.json</code>; the page reads only that file. (Eventual target: client-side probing — documented.)</td></tr>
<tr><td>Q2</td><td>How the checker runs</td><td><b>systemd timer</b>, rewrites <code>status.json</code> every <b>3 minutes</b>.</td></tr>
<tr><td>Q3</td><td>Page model</td><td><b>Single site</b> on Dankclaw. Root = user services (no host labels). <code>/admin/</code> = admin services, second operator-only password. Optional <code>/how/</code> tourist page.</td></tr>
<tr><td>Q5</td><td>Service split</td><td>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). *arr stack <b>excluded</b> (separate effort).</td></tr>
<tr><td>—</td><td>Styling</td><td>Terminal TUI. Terminus (monospace fallback). Green-on-black. 1995 phosphor palette.</td></tr>
</table>
<p class="dim">Palette: bg <code>#050805</code> · text <code>#33ff66</code> · up <code>#33ff66</code> · down <code>#ff5555</code> · WIP <code>#ffcc33</code> · links <code>#33ccff</code> · headers <code>#aaffcc</code>.</p>
<h2>Stack baseline (verified 2026-08-22)</h2>
<p class="dim">Source: real HTTP fetch through Caddy + <code>ss -ltn</code> (they agree). The earlier <code>/dev/tcp</code> probe was the false one — it reported every port down while Caddy returned 200.</p>
<h3>Dankclaw 192.168.0.143 — AI / search / dev / observability</h3>
<table>
<tr><th>Service</th><th>Site</th><th>HTTP</th><th>Port</th><th>State</th></tr>
<tr><td>Homepage</td><td>dankclaw</td><td>401</td><td>—</td><td class="wip"><span class="badge wip">WIP</span> auth-gated, not deployed</td></tr>
<tr><td>SearXNG</td><td>search</td><td>401</td><td>8081 ✓</td><td class="up"><span class="badge up">UP</span> (auth-gated)</td></tr>
<tr><td>ComfyUI</td><td>comfyui</td><td>200</td><td>8188 ✓</td><td class="up"><span class="badge up">UP</span></td></tr>
<tr><td>OpenWebUI</td><td>chat</td><td>401</td><td>3005 ✓</td><td class="up"><span class="badge up">UP</span> (auth-gated)</td></tr>
<tr><td>Hermes</td><td>hermes</td><td>401</td><td>8090 ✓</td><td class="up"><span class="badge up">UP</span> (auth-gated)</td></tr>
<tr><td>Gitea</td><td>gitea</td><td>200</td><td>3000 ✓</td><td class="up"><span class="badge up">UP</span></td></tr>
<tr><td>Grafana</td><td>grafana</td><td>401</td><td>3002 ✓</td><td class="up"><span class="badge up">UP</span> (auth-gated)</td></tr>
<tr><td>Prometheus</td><td>prometheus</td><td>401</td><td>9090 ✓</td><td class="up"><span class="badge up">UP</span> (auth-gated)</td></tr>
<tr><td>Loki</td><td>loki</td><td>404</td><td>3100 ✓</td><td class="up"><span class="badge up">UP</span> (no root page)</td></tr>
<tr><td>frank-default-large</td><td>:7770</td><td>ok</td><td>7770 ✓</td><td class="up"><span class="badge up">UP</span></td></tr>
</table>
<h3>Dankserver 192.168.0.235 — media</h3>
<table>
<tr><th>Service</th><th>Site</th><th>HTTP</th><th>Port</th><th>State</th></tr>
<tr><td>Calibre-Web</td><td>books</td><td>302</td><td>8083 ✓</td><td class="up"><span class="badge up">UP</span></td></tr>
<tr><td>frank-default-small</td><td>:7770</td><td>ok</td><td>7770 ✓</td><td class="up"><span class="badge up">UP</span></td></tr>
<tr><td>Plex</td><td>plex</td><td>502 / 000</td><td>32400 bound</td><td class="und"><span class="badge und">UNDETERMINED</span> port bound, no HTTP reply</td></tr>
<tr><td>RomM</td><td>romm</td><td>401</td><td>8085 ✗</td><td class="down"><span class="badge down">DOWN</span> (401 = auth challenge only)</td></tr>
<tr><td>Copyparty</td><td>files</td><td>401</td><td>3923 ✗</td><td class="down"><span class="badge down">DOWN</span> (401 = auth challenge only)</td></tr>
<tr><td>Jellyfin</td><td>jellyfin</td><td>502</td><td>8096 ✗</td><td class="down"><span class="badge down">DOWN</span></td></tr>
<tr><td>Radarr / Sonarr / Lidarr / Readarr / Prowlarr / qBittorrent</td><td>—</td><td>502</td><td>✗</td><td class="down"><span class="badge down">DOWN</span> (not started — *arr effort)</td></tr>
</table>
<h2>My processing — what I worked out overnight</h2>
<div class="panel">
<p><span class="tag">1</span><b>The port-probe contradiction is resolved.</b> My first probe (<code>/dev/tcp</code> nested in <code>bash -c</code> over SSH) reported every port down while Caddy returned 200 in the same call. The <code>ss -ltn</code> + HTTP-fetch pair is the ground truth and they agree. Do not trust the <code>/dev/tcp</code> one-liner in an SSH'd subshell.</p>
<p><span class="tag">2</span><b>The 401 trap.</b> For basicauth sites, Caddy serves the 401 challenge <i>before</i> the proxy. So a 401 does <b>not</b> prove the backend is up — RomM and Copyparty return 401 while their ports are down. This is why the checker must probe the backend <code>host:port</code> directly, never the Caddy site.</p>
<p><span class="tag">3</span><b>The homepage's core mechanism is architecturally blocked.</b> The page is <code>https://dankclaw.franksdank.cloud</code>; the service URLs are <code>http://&lt;name&gt;.franksdank.cloud</code>. A browser <code>fetch</code> of those is cross-origin (CORS-blocked), mixed content (https→http, hard-blocked), and auth-gated (401). Three independent failures. Hence the server-side <code>status.json</code> design.</p>
<p><span class="tag">4</span><b>The Dankserver media stack is down, and the cause is a missing <code>.env</code>.</b> Only Calibre-Web (no secret deps) and the LLM (separate systemd unit) are up. <code>compose.yaml</code> needs <code>MARIADB_ROOT_PASSWORD</code>, <code>ROMM_*</code>, <code>COPAY_PASS</code>, … — none present on the target. This is a <b>separate, urgent work item</b>, distinct from the homepage.</p>
<p><span class="tag">5</span><b>Plex is in an odd state.</b> 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.</p>
<p><span class="tag">6</span><b>LLM "loading" nuance.</b> During model load the LLM <code>/health</code> 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.</p>
<p><span class="tag">7</span><b>My host is outside the LAN.</b> <code>*.franksdank.cloud</code> 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 <b>from Dankclaw</b>.</p>
</div>
<h2>Work threads identified</h2>
<table>
<tr><th>Thread</th><th>What it is</th><th>Status</th></tr>
<tr><td>1 · Homepage</td><td>This design. Server-side status, user + admin pages, terminal TUI.</td><td class="wip"><span class="badge wip">GRILLING</span> 38 open questions</td></tr>
<tr><td>2 · Dankserver media stack</td><td>Down. Needs a <code>.env</code> (secrets) + a compose deploy.</td><td class="down"><span class="badge down">BLOCKED</span> separate, urgent</td></tr>
<tr><td>3 · *arr stack</td><td>Radarr/Sonarr/Lidarr/Readarr/Prowlarr/qBittorrent. Not started; possibly a different host.</td><td class="wip"><span class="badge wip">NOT STARTED</span></td></tr>
<tr><td>4 · Usage-docs</td><td>Per-service how-to pages, maintained separately from the reference docs.</td><td class="wip"><span class="badge wip">NOT STARTED</span></td></tr>
<tr><td>5 · "How it's built"</td><td>Optional public architecture page for tourists.</td><td class="wip"><span class="badge wip">OPTIONAL</span></td></tr>
<tr><td>6 · Plex</td><td>Investigate the bound-but-silent port 32400.</td><td class="und"><span class="badge und">INVESTIGATE</span></td></tr>
<tr><td>7 · Client-side probing</td><td>Documented future intent: same-origin https paths + per-service health + CORS.</td><td class="dim"><span class="badge">LATER</span></td></tr>
</table>
<h2>Open questions — answer these in the morning</h2>
<p class="dim">Each carries a recommendation. Reply <b>AGREE</b> to accept it, or state the change. Group by category letter.</p>
<h3>A · Architecture and structure</h3>
<div class="q"><span class="qn">Q6</span> — The optional <code>/how/</code> "tourist" page: build now or defer?<span class="rec"><b>Rec:</b> defer. When built, make it <b>public</b> (no auth) so a visitor can see the architecture; render the mermaid diagrams from this repo.</span><span class="verdict">verdict: <b>AGREE</b> · or change to: ____</span></div>
<div class="q"><span class="qn">Q7</span> — One <code>status.json</code> or two (<code>user-status.json</code> + <code>admin-status.json</code>)?<span class="rec"><b>Rec:</b> one <code>status.json</code>; each service carries a <code>category</code> field; each page filters client-side.</span><span class="verdict">verdict: <b>AGREE</b> · or change to: ____</span></div>
<div class="q"><span class="qn">Q8</span> — One <code>services.json</code> or split by page?<span class="rec"><b>Rec:</b> one <code>services.json</code> with a <code>category</code> field; the checker reads one file.</span><span class="verdict">verdict: <b>AGREE</b> · or change to: ____</span></div>
<h3>B · The checker</h3>
<div class="q"><span class="qn">Q9</span> — What language for the checker script?<span class="rec"><b>Rec:</b> <b>python3</b> (available on Dankclaw, no deps, clean JSON). A bash+<code>nc</code> variant works but hand-rolled JSON is error-prone.</span><span class="verdict">verdict: <b>AGREE</b> · or change to: ____</span></div>
<div class="q"><span class="qn">Q10</span> — Probe method: TCP connect or HTTP GET?<span class="rec"><b>Rec:</b> <b>TCP connect</b> to <code>host:port</code>. Uniform, works for non-HTTP backends, matches the verified ground truth.</span><span class="verdict">verdict: <b>AGREE</b> · or change to: ____</span></div>
<div class="q"><span class="qn">Q11</span> — Probe timeout per service?<span class="rec"><b>Rec:</b> <b>3 s</b>. A dead port refuses fast; a black-holed one times out at 3 s. Well under the 3-minute interval.</span><span class="verdict">verdict: <b>AGREE</b> · or change to: ____</span></div>
<div class="q"><span class="qn">Q12</span> — LLM "loading" state (health 503 during model load)?<span class="rec"><b>Rec:</b> keep three states; a listening port is Up. Optionally probe <code>/health</code> and treat 503 as Up. A "loading" state is a later refinement.</span><span class="verdict">verdict: <b>AGREE</b> · or change to: ____</span></div>
<div class="q"><span class="qn">Q13</span> — Where does the checker live in the repo?<span class="rec"><b>Rec:</b> <code>scripts/homepage-status.py</code> + <code>scripts/homepage-status.service</code> + <code>scripts/homepage-status.timer</code>, tracked in git.</span><span class="verdict">verdict: <b>AGREE</b> · or change to: ____</span></div>
<div class="q"><span class="qn">Q14</span> — How does the checker reach the Dankserver backends?<span class="rec"><b>Rec:</b> the <code>probe</code> field is a full <code>host:port</code> — <code>127.0.0.1:PORT</code> for Dankclaw, <code>192.168.0.235:PORT</code> for Dankserver. Verified reachable over the LAN.</span><span class="verdict">verdict: <b>AGREE</b> · or change to: ____</span></div>
<h3>C · The status model</h3>
<div class="q"><span class="qn">Q15</span> — How is a service marked WIP?<span class="rec"><b>Rec:</b> a static <code>"wip": true</code> field; the checker skips the probe. Drop the old self-referential "Homepage" entry.</span><span class="verdict">verdict: <b>AGREE</b> · or change to: ____</span></div>
<div class="q"><span class="qn">Q16</span> — Staleness: what if the timer fails and <code>status.json</code> is old?<span class="rec"><b>Rec:</b> write a <code>generated_at</code> timestamp; the page warns "status may be stale" when it is older than 10 minutes.</span><span class="verdict">verdict: <b>AGREE</b> · or change to: ____</span></div>
<div class="q"><span class="qn">Q17</span> — Atomic writes?<span class="rec"><b>Rec:</b> write to a temp file, then rename over <code>status.json</code>. A crash never leaves a half-written file.</span><span class="verdict">verdict: <b>AGREE</b> · or change to: ____</span></div>
<h3>D · Auth</h3>
<div class="q"><span class="qn">Q18</span> — The admin password: a second <code>.env</code> variable?<span class="rec"><b>Rec:</b> yes — <code>CADDY_ADMIN_HASH</code> in the host <code>.env</code> (git-ignored), rendered at deploy, operator-only. Root keeps <code>CADDY_BASICAUTH_HASH</code>.</span><span class="verdict">verdict: <b>AGREE</b> · or change to: ____</span></div>
<div class="q"><span class="qn">Q19</span> — Per-path basicauth in Caddy v2?<span class="rec"><b>Rec:</b> an <code>@admin path /admin/*</code> matcher with a second <code>basicauth @admin</code> block. Confirm exact syntax against the Caddy docs during implementation.</span><span class="verdict">verdict: <b>AGREE</b> · or change to: ____</span></div>
<h3>E · The pages and content</h3>
<div class="q"><span class="qn">Q20</span> — Where do the one-line descriptions come from?<span class="rec"><b>Rec:</b> hand-written one-liners in <code>services.json</code>, drafted from the existing reference docs.</span><span class="verdict">verdict: <b>AGREE</b> · or change to: ____</span></div>
<div class="q"><span class="qn">Q21</span> — The "Open" link: the Caddy site or the direct backend?<span class="rec"><b>Rec:</b> the <b>Caddy site</b> URL (what a user opens). Status-only backends set <code>showLink: false</code>.</span><span class="verdict">verdict: <b>AGREE</b> · or change to: ____</span></div>
<div class="q"><span class="qn">Q22</span> — The "docs" link: usage page or the existing reference doc?<span class="rec"><b>Rec:</b> link to the <b>existing</b> reference doc as a placeholder; swap to the per-service usage page when that workstream is built.</span><span class="verdict">verdict: <b>AGREE</b> · or change to: ____</span></div>
<div class="q"><span class="qn">Q23</span> — Page layout: cards or a terminal-style list?<span class="rec"><b>Rec:</b> a <b>terminal-style list</b> (one service per line, like <code>systemctl list-units</code>). Columns: <code>STATUS | NAME | description | (open) (docs)</code>.</span><span class="verdict">verdict: <b>AGREE</b> · or change to: ____</span></div>
<div class="q"><span class="qn">Q24</span> — A summary line at the top?<span class="rec"><b>Rec:</b> yes — one line per page, e.g. <code>UP 9 DOWN 2 WIP 1</code>.</span><span class="verdict">verdict: <b>AGREE</b> · or change to: ____</span></div>
<h3>F · Styling</h3>
<div class="q"><span class="qn">Q25</span> — Terminus: bundle the font or use a fallback?<span class="rec"><b>Rec:</b> <b>bundle</b> Terminus (woff2) via <code>@font-face</code>, with a monospace fallback. Confirm the license (freely distributable) and drop files in <code>fonts/</code>.</span><span class="verdict">verdict: <b>AGREE</b> · or change to: ____</span></div>
<div class="q"><span class="qn">Q26</span> — Confirm the palette?<span class="rec"><b>Rec:</b> as proposed (green <code>#33ff66</code>, red <code>#ff5555</code>, amber <code>#ffcc33</code>, cyan <code>#33ccff</code>, bg <code>#050805</code>). Tune for contrast.</span><span class="verdict">verdict: <b>AGREE</b> · or change to: ____</span></div>
<div class="q"><span class="qn">Q27</span> — CRT effects (scanlines + glow)?<span class="rec"><b>Rec:</b> subtle scanlines + a soft green <code>text-shadow</code>. Make it toggleable (<code>?crt=0</code>).</span><span class="verdict">verdict: <b>AGREE</b> · or change to: ____</span></div>
<div class="q"><span class="qn">Q28</span> — Blinking cursor / animation?<span class="rec"><b>Rec:</b> a subtle CSS-only blinking block cursor on the header; respect <code>prefers-reduced-motion</code>.</span><span class="verdict">verdict: <b>AGREE</b> · or change to: ____</span></div>
<h3>G · The usage-docs workstream</h3>
<div class="q"><span class="qn">Q29</span> — Scope of the per-service usage pages?<span class="rec"><b>Rec:</b> concise <b>how-to</b> guides (what it is, how to open it, the common tasks). Distinct from the deep reference docs.</span><span class="verdict">verdict: <b>AGREE</b> · or change to: ____</span></div>
<div class="q"><span class="qn">Q30</span> — Where do the usage pages live?<span class="rec"><b>Rec:</b> served from the homepage site, same origin, at <code>/usage/&lt;svc&gt;/</code>; source under <code>shared/homepage/usage/&lt;svc&gt;/</code>.</span><span class="verdict">verdict: <b>AGREE</b> · or change to: ____</span></div>
<div class="q"><span class="qn">Q31</span> — Are the usage pages password-protected?<span class="rec"><b>Rec:</b> yes — user pages behind the shared password, admin pages behind the operator password.</span><span class="verdict">verdict: <b>AGREE</b> · or change to: ____</span></div>
<div class="q"><span class="qn">Q32</span> — Is the usage-docs workstream a separate effort?<span class="rec"><b>Rec:</b> yes, after the core homepage is live. For now the "docs" link points to the existing reference doc (Q22).</span><span class="verdict">verdict: <b>AGREE</b> · or change to: ____</span></div>
<h3>H · Deployment and operations</h3>
<div class="q"><span class="qn">Q33</span> — How is the homepage deployed?<span class="rec"><b>Rec:</b> extend <code>deploy.sh</code> (Dankclaw path): copy <code>shared/homepage/</code> to <code>/srv/homepage/</code>, install the timer + service, render the Caddyfile (both hashes), reload Caddy.</span><span class="verdict">verdict: <b>AGREE</b> · or change to: ____</span></div>
<div class="q"><span class="qn">Q34</span> — Who creates <code>/srv/homepage/</code>?<span class="rec"><b>Rec:</b> <code>deploy.sh</code> creates it and copies the assets; the checker writes <code>status.json</code> there at runtime.</span><span class="verdict">verdict: <b>AGREE</b> · or change to: ____</span></div>
<div class="q"><span class="qn">Q35</span> — Is the timer enabled at boot?<span class="rec"><b>Rec:</b> yes — a systemd timer + service, enabled, survives reboots, runs every 3 minutes.</span><span class="verdict">verdict: <b>AGREE</b> · or change to: ____</span></div>
<h3>I · Testing</h3>
<div class="q"><span class="qn">Q36</span> — Tests for the checker?<span class="rec"><b>Rec:</b> yes — a bats test runs the checker against a fake up port, a fake down port, and a WIP service, then validates the <code>status.json</code> schema. TDD, per repo rules.</span><span class="verdict">verdict: <b>AGREE</b> · or change to: ____</span></div>
<div class="q"><span class="qn">Q37</span> — Tests for the pages and config?<span class="rec"><b>Rec:</b> yes — validate the <code>services.json</code> schema, run <code>caddy validate</code>, and check the pages reference <code>status.json</code>.</span><span class="verdict">verdict: <b>AGREE</b> · or change to: ____</span></div>
<div class="q"><span class="qn">Q38</span> — A security test for the two passwords?<span class="rec"><b>Rec:</b> yes — assert the Caddyfile carries two distinct basicauth hashes (root vs <code>/admin/</code>).</span><span class="verdict">verdict: <b>AGREE</b> · or change to: ____</span></div>
<h3>J · Edge cases and robustness</h3>
<div class="q"><span class="qn">Q39</span> — A backend moves hosts (e.g. the *arr stack to a new box)?<span class="rec"><b>Rec:</b> no code change — the <code>probe</code> field is a full <code>host:port</code>; a move is a config edit.</span><span class="verdict">verdict: <b>AGREE</b> · or change to: ____</span></div>
<div class="q"><span class="qn">Q40</span> — A slow LAN hop to a Dankserver backend?<span class="rec"><b>Rec:</b> the 3 s timeout handles it — a slow-but-alive backend connects (Up); a dead one times out (Down).</span><span class="verdict">verdict: <b>AGREE</b> · or change to: ____</span></div>
<div class="q"><span class="qn">Q41</span> — Client-side auto-refresh?<span class="rec"><b>Rec:</b> the page polls <code>status.json</code> every <b>60 s</b> (the file updates every 3 min).</span><span class="verdict">verdict: <b>AGREE</b> · or change to: ____</span></div>
<div class="q"><span class="qn">Q42</span> — Mobile responsiveness?<span class="rec"><b>Rec:</b> yes — stack the columns and shrink the font on narrow screens.</span><span class="verdict">verdict: <b>AGREE</b> · or change to: ____</span></div>
<div class="q"><span class="qn">Q43</span> — A "last checked" timestamp on the page?<span class="rec"><b>Rec:</b> yes — the footer shows <code>last checked HH:MM:SS</code> from <code>generated_at</code>.</span><span class="verdict">verdict: <b>AGREE</b> · or change to: ____</span></div>
<h2>What I changed (knowledge base)</h2>
<table>
<tr><th>File</th><th>Change</th></tr>
<tr><td><code>docs/Infrastructure/Homepage.md</code></td><td>Rewritten to the settled design: structure, status model, checker, auth, classification, layout. Added three mermaid diagrams (site structure, status states, checker flow).</td></tr>
<tr><td><code>docs/Infrastructure/Homepage Design Grilling.md</code></td><td>New. The decision log: settled Q1–Q5 + the 38 open questions (Q6–Q43) with recommendations + the build-order diagram.</td></tr>
<tr><td><code>docs/index.md</code></td><td>Linked the new grilling page (satisfies the "index links every page" test).</td></tr>
<tr><td><code>~/.pi/agent/docs/log.md</code></td><td>Added a homepage-design bullet to the 2026-08-22 entry (design, baseline, threads).</td></tr>
<tr><td><code>report/homepage-grilling-2026-08-22.html</code></td><td>This report. Self-contained, terminal-TUI styled.</td></tr>
</table>
<h2>How to continue</h2>
<div class="panel">
<p>Reply to the open questions (Q6–Q43) — <b>AGREE</b> or the change, grouped by category. Once settled, the build order is fixed:</p>
<pre>
settle Q6-Q43 -&gt; write services.json + checker -&gt; write pages + theme
-&gt; extend deploy.sh + Caddyfile -&gt; tests green -&gt; deploy to Dankclaw
-&gt; verify in browser
</pre>
<p class="dim">Separate threads to schedule (not part of the homepage build): the Dankserver media stack (missing <code>.env</code>), the *arr stack, the usage-docs workstream, the optional "how it's built" page, and the Plex investigation.</p>
<p class="dim">Report is self-contained (no network needed). A copy is committed at <code>report/homepage-grilling-2026-08-22.html</code> and served at the URL below.</p>
</div>
<p class="dim" style="margin-top:28px; border-top:1px solid var(--border); padding-top:12px">
generated 2026-08-22 (night) · frank program · chief designer · <span class="ok">tests: 97/97 green</span>
</p>
</div>
</body>
</html>