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.
This commit is contained in:
@@ -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/<svc>/`. Source in the repo under `shared/homepage/usage/<svc>/`. 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]
|
||||
```
|
||||
+112
-21
@@ -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://<name>.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<br/>(shared password)"]
|
||||
ADMIN["/admin/ admin services<br/>(operator password)"]
|
||||
HOW["/how/ how it is built<br/>(optional)"]
|
||||
STATUS["/status.json"]
|
||||
end
|
||||
TIMER["systemd timer<br/>every 3 minutes"]
|
||||
CHECKER["checker script<br/>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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -0,0 +1,219 @@
|
||||
<!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 · homepage design grilling · overnight progress report · 2026-08-22 · 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://<name>.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/<svc>/</code>; source under <code>shared/homepage/usage/<svc>/</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 -> write services.json + checker -> write pages + theme
|
||||
-> extend deploy.sh + Caddyfile -> tests green -> deploy to Dankclaw
|
||||
-> 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>
|
||||
Reference in New Issue
Block a user