homepage: live model info on the fleet cards, user docs page at /docs
- services.json: frank-large/medium/small move from admin-only to the user page with current descriptions (halogen w4b iGPU; 35:30 layer-split MTP 27B on the R9700s; Gemma4-12B QAT vision path on the 3090) and docs links to /docs#models - docs.html: static user documentation (access chain, model guide, media how-tos, troubleshooting); served at /docs via the post-auth homepage listener; shipped by deploy_homepage; footer + index link to it - tests: pages seam asserts the docs contract (page, link, Caddy route, deployer copy) - vault: Homepage.md synced (docs landing shipped, cards reclassified)
This commit is contained in:
@@ -323,6 +323,16 @@ http://:8017 {
|
||||
}
|
||||
respond "Forbidden: homepage admins only" 403
|
||||
}
|
||||
# User docs page (static). Anchors (#models etc.) are client-side,
|
||||
# so both /docs and /docs/ map onto the single docs.html.
|
||||
handle /docs {
|
||||
try_files /docs.html
|
||||
file_server
|
||||
}
|
||||
handle /docs/ {
|
||||
try_files /docs.html
|
||||
file_server
|
||||
}
|
||||
@data path /services.json /status.json /app.js /style.css /deployed.js
|
||||
handle @data {
|
||||
file_server
|
||||
|
||||
@@ -302,7 +302,7 @@ deploy_homepage() {
|
||||
fi
|
||||
run_privileged mkdir -p /srv/homepage
|
||||
run_privileged cp shared/homepage/index.html shared/homepage/admin.html shared/homepage/app.js \
|
||||
shared/homepage/style.css shared/homepage/services.json /srv/homepage/
|
||||
shared/homepage/style.css shared/homepage/services.json shared/homepage/docs.html /srv/homepage/
|
||||
if [[ -d shared/homepage/fonts ]]; then
|
||||
run_privileged cp -r shared/homepage/fonts /srv/homepage/
|
||||
fi
|
||||
|
||||
@@ -10,7 +10,12 @@ The homepage is live on Dankclaw. `./scripts/deploy.sh dankclaw` copies the stat
|
||||
|
||||
**Moved to the naked domain (2026-08-24):** the homepage now serves `franksdank.cloud` and `www.franksdank.cloud`. The old names `dankclaw.franksdank.cloud` and `dankserver.franksdank.cloud` redirect to it. Authentication moved from per-page basicauth to Authentik `forward_auth` (see [Authentik](Authentik.md)).
|
||||
|
||||
The per-service `/docs/` pages, the *arr stack, and the optional `/how/` page remain separate threads.
|
||||
**Docs page shipped (2026-09-17):** `/docs` serves a static user documentation
|
||||
page (`shared/homepage/docs.html`) — access walkthrough, the running-model
|
||||
guide, media how-tos, and troubleshooting. Per-service `/docs/<svc>/` pages
|
||||
remain a separate thread. The three LLM cards (`frank-large`, `frank-medium`,
|
||||
`frank-small`) moved from admin-only to the **user** page with live model
|
||||
descriptions and a `docs` link to `/docs#models`.
|
||||
|
||||
## Design decision — 2026-08-22
|
||||
|
||||
@@ -81,9 +86,9 @@ flowchart TD
|
||||
|
||||
## Service classification
|
||||
|
||||
**User services** (root page): SearXNG, ComfyUI, OpenWebUI, Gitea, Calibre-Web, RomM, Copyparty, Plex, Jellyfin.
|
||||
**User services** (root page): SearXNG, OpenWebUI, Gitea, Calibre-Web, RomM, Copyparty, Plex, Jellyfin, and the three LLM fleet cards (frank-large, frank-medium, frank-small — status + docs link, no Open link).
|
||||
|
||||
**Admin services** (`/admin/`): Hermes, Grafana, Prometheus, Loki, Caddy, Pi-hole, 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.
|
||||
**Admin services** (`/admin/`): Hermes, Grafana, Prometheus, Loki, Caddy, Pi-hole, Gluetun, 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 from the listing:** the *arr stack + qBittorrent (Prowlarr, Sonarr,
|
||||
Radarr, Lidarr, Readarr, qB) — live on FrankBook-Pro but operator-only: no
|
||||
@@ -95,7 +100,7 @@ vhosts, direct IP:port access only. See [[FrankBook-Pro]].
|
||||
shared/homepage/
|
||||
├── index.html # root page (user services)
|
||||
├── admin.html # admin + infrastructure services
|
||||
├── how.html # deferred (Q6)
|
||||
├── docs.html # user documentation page, served at /docs (2026-09-17)
|
||||
├── style.css # full theme; a toggle strips to bare HTML
|
||||
├── app.js # loads status.json once, renders the panels
|
||||
├── services.json # service config (user + admin)
|
||||
|
||||
@@ -0,0 +1,126 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>frank — docs</title>
|
||||
<base href="/">
|
||||
<link rel="stylesheet" href="style.css">
|
||||
</head>
|
||||
<body>
|
||||
<header class="top">
|
||||
<span class="title">frank@home:~$ docs</span>
|
||||
<a id="bare-toggle" class="toggle" href="?bare=1">bare</a>
|
||||
</header>
|
||||
<main class="docs">
|
||||
|
||||
<section id="getting-in">
|
||||
<h1>Getting in</h1>
|
||||
<p>Everything here lives behind two doors. The first is <strong>Anubis</strong>, a
|
||||
quiet bot-check: the first time you visit any guarded page in a session you see a
|
||||
short "making sure you're not a bot" screen. It runs once. The second door is
|
||||
<strong>Authentik</strong>, the family single sign-on: log in once and every
|
||||
guarded service (this homepage, search, chat, media requests, and friends) opens
|
||||
for you without a second password.</p>
|
||||
<ul>
|
||||
<li>Need an account? Ask Frank. Accounts are issued by hand; there is no signup.</li>
|
||||
<li>Your clearance lives in a cookie. Clearing cookies means re-clearing both doors.</li>
|
||||
<li>If a page asks you to log in repeatedly, that is a server-side problem — tell Frank, don't brute-force it.</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section id="chat">
|
||||
<h1>Talking to the AI</h1>
|
||||
<p>The everyday front door is <a href="https://ai.franksdank.cloud">Open WebUI</a>
|
||||
(it has its own login the first time; use the same email you use everywhere).
|
||||
Pick a model in the chat's model selector — the three models the fleet runs are
|
||||
described <a href="#models">below</a>. Open WebUI keeps your chat history; the raw
|
||||
model endpoints do not.</p>
|
||||
<p>There is no audio input anywhere on the fleet. Text in, and — on
|
||||
<code>frank-small</code> only — images too.</p>
|
||||
</section>
|
||||
|
||||
<section id="models">
|
||||
<h1>The running models</h1>
|
||||
<p>Three models serve the family, each on its own hardware. The homepage status
|
||||
cards above show whether each one is up right now.</p>
|
||||
|
||||
<h2>frank-large — the big brain</h2>
|
||||
<ul>
|
||||
<li><strong>Model:</strong> Qwen3.8-Flash-Next, w4b quantization, served by halogen-flash-server.</li>
|
||||
<li><strong>Hardware:</strong> Dankclaw's Strix Halo iGPU (gfx1151), shared with the whole host.</li>
|
||||
<li><strong>Best for:</strong> long, thoughtful text conversations. 200k-token context per conversation.</li>
|
||||
<li><strong>Capacity:</strong> exactly two conversations resident at once. A third waits — politely, but it waits.</li>
|
||||
<li><strong>Not for:</strong> images. It cannot see them.</li>
|
||||
</ul>
|
||||
|
||||
<h2>frank-small — the eyes</h2>
|
||||
<ul>
|
||||
<li><strong>Model:</strong> Gemma4-12B QAT (HauhauCS Balanced, Q4_K_M) with MTP + n-gram speculative decoding.</li>
|
||||
<li><strong>Hardware:</strong> Dankclaw's RTX 3090.</li>
|
||||
<li><strong>Best for:</strong> anything with a picture — screenshots, photos, diagrams. Fastest decode of the fleet at long context, and the endpoint that gets most daily traffic.</li>
|
||||
<li><strong>Capacity:</strong> one million tokens of pooled context across four simultaneous conversations (up to 262k each).</li>
|
||||
</ul>
|
||||
|
||||
<h2>frank-medium — the 27B generalist</h2>
|
||||
<ul>
|
||||
<li><strong>Model:</strong> Swift-Qwen3.8-27B-Uncensored at UD-Q8_K_XL, with its own MTP head, on mainline llama.cpp.</li>
|
||||
<li><strong>Hardware:</strong> Dankserver's two Radeon R9700s, one process split 35:30 across the cards.</li>
|
||||
<li><strong>Best for:</strong> general chat and reasoning when the big brain is busy; the biggest pure-language model we run.</li>
|
||||
<li><strong>Capacity:</strong> 393k-token context over two conversations. Also sees images (BF16 projector).</li>
|
||||
</ul>
|
||||
|
||||
<p class="foot-note">Which one to pick? Screenshots and photos: <code>frank-small</code>.
|
||||
Deep long-text work: <code>frank-large</code> (mind the two-seat limit).
|
||||
When in doubt: <code>frank-medium</code>.</p>
|
||||
</section>
|
||||
|
||||
<section id="media">
|
||||
<h1>Media & files</h1>
|
||||
<ul>
|
||||
<li><strong>Movies & TV:</strong> request with <a href="https://seerr.franksdank.cloud">Seerr</a>;
|
||||
watch with <a href="https://plex.franksdank.cloud/web/index.html">Plex</a> or
|
||||
<a href="https://jelly.franksdank.cloud">Jellyfin</a>. Don't ask the *arrs directly —
|
||||
Seerr is the front door, and requests ride the tunnel from there.</li>
|
||||
<li><strong>Books:</strong> <a href="https://books.franksdank.cloud">Calibre-Web</a> — the e-book library, with its own login.</li>
|
||||
<li><strong>Games:</strong> <a href="https://romm.franksdank.cloud">RomM</a> — ROM library and emulator collection.</li>
|
||||
<li><strong>Raw files:</strong> <a href="https://files.franksdank.cloud">Copyparty</a> — direct downloads and shares.</li>
|
||||
<li><strong>Search:</strong> <a href="https://search.franksdank.cloud">SearXNG</a> — metasearch, VPN-routed, no AI snoop.</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section id="troubleshooting">
|
||||
<h1>When something looks broken</h1>
|
||||
<ul>
|
||||
<li><strong>A status card says DOWN.</strong> Reload the page once; the checker
|
||||
re-probes every few minutes. If it stays down, that service really is unhappy — tell Frank.</li>
|
||||
<li><strong>"status unavailable" on the homepage.</strong> The page couldn't read
|
||||
its status file. Hard-reload (Ctrl/Cmd-Shift-R).</li>
|
||||
<li><strong>Connection timeout on a home device.</strong> Nine times out of ten
|
||||
the device isn't using the house DNS (Pi-hole at 192.168.0.143) and resolved
|
||||
our names to the public address, which doesn't answer. Point the device's DNS at
|
||||
192.168.0.143 and retry.</li>
|
||||
<li><strong>The bot-check spins forever.</strong> Let it finish; heavy JS blocking
|
||||
(privacy extensions) can starve it. Pause the blocker, reload, resume.</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
</main>
|
||||
<footer class="foot">
|
||||
<span><a href="/">← services</a></span>
|
||||
<span>docs · written by the chief designer · ask Frank for anything missing</span>
|
||||
</footer>
|
||||
<script>
|
||||
// The same bare-mode toggle the service pages use (?bare=1), for this static page.
|
||||
(function () {
|
||||
var bare = new URLSearchParams(window.location.search).get('bare') === '1';
|
||||
document.documentElement.setAttribute('data-bare', bare ? '1' : '0');
|
||||
var t = document.getElementById('bare-toggle');
|
||||
if (t) {
|
||||
t.textContent = bare ? 'styled' : 'bare';
|
||||
t.href = bare ? '?bare=0' : '?bare=1';
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -16,6 +16,7 @@
|
||||
<main id="grid" class="grid"></main>
|
||||
<footer class="foot">
|
||||
<span>last master deployment: <span id="deployed-at"></span></span>
|
||||
<a href="/docs">docs</a>
|
||||
<a href="/admin/">admin</a>
|
||||
</footer>
|
||||
<script src="deployed.js"></script>
|
||||
|
||||
@@ -232,10 +232,10 @@
|
||||
},
|
||||
{
|
||||
"name": "frank-large",
|
||||
"category": "admin",
|
||||
"description": "The large LLM (Qwen3.8-Flash-Next w4b, halogen-flash-server on the Strix Halo iGPU). Two 200k-token conversations resident.",
|
||||
"category": "user",
|
||||
"description": "The big brain. Qwen3.8-Flash-Next w4b on halogen-flash-server, running on Dankclaw's Strix Halo iGPU. Text-only; holds two 200k-token conversations resident.",
|
||||
"url": "",
|
||||
"docs": "",
|
||||
"docs": "/docs#models",
|
||||
"probe": "127.0.0.1:7770",
|
||||
"method": "http",
|
||||
"wip": false,
|
||||
@@ -243,10 +243,10 @@
|
||||
},
|
||||
{
|
||||
"name": "frank-medium",
|
||||
"category": "admin",
|
||||
"description": "The medium LLM (Qwen3.8-27B Uncensored UD-Q8_K_XL with the DFlash2 drafter, mainline llama.cpp) on Dankserver — both R9700s in one layer-split process behind the :7770 endpoint.",
|
||||
"category": "user",
|
||||
"description": "The 27B generalist. ajgazin Swift-Qwen3.8-27B-Uncensored at UD-Q8_K_XL with its own MTP head, mainline llama.cpp in one 35:30 layer-split process across Dankserver's two R9700s, 393k-token context.",
|
||||
"url": "",
|
||||
"docs": "",
|
||||
"docs": "/docs#models",
|
||||
"probe": "192.168.0.235:7770",
|
||||
"method": "http",
|
||||
"wip": false,
|
||||
@@ -254,10 +254,10 @@
|
||||
},
|
||||
{
|
||||
"name": "frank-small",
|
||||
"category": "admin",
|
||||
"description": "The small LLM (Qwen3.8-27B GSQ-RCO IQ3_S with MTP and vision, llama.cpp) on Dankclaw's RTX 3090. The fleet's image path.",
|
||||
"category": "user",
|
||||
"description": "The vision path. HauhauCS Gemma4-12B QAT (Balanced, Q4_K_M) with MTP + ngram spec-decode on Dankclaw's RTX 3090. Sees images; 1M-token pool across four conversations.",
|
||||
"url": "",
|
||||
"docs": "",
|
||||
"docs": "/docs#models",
|
||||
"probe": "127.0.0.1:7771",
|
||||
"method": "http",
|
||||
"wip": false,
|
||||
|
||||
@@ -77,6 +77,16 @@ body::before {
|
||||
}
|
||||
.foot a { color: var(--link); text-decoration: none; }
|
||||
|
||||
/* The user docs page (static prose). */
|
||||
.docs { max-width: 780px; }
|
||||
.docs h1 { color: var(--head); font-size: 1.25rem; border-bottom: 1px solid var(--border); padding-bottom: 4px; }
|
||||
.docs h2 { color: var(--head); font-size: 1.05rem; margin-top: 20px; }
|
||||
.docs a { color: var(--link); }
|
||||
.docs code { color: var(--head); }
|
||||
.docs .foot-note { color: var(--dim); }
|
||||
html[data-bare="1"] .docs h1, html[data-bare="1"] .docs h2 { color: #000; border-color: #000; }
|
||||
html[data-bare="1"] .docs a, html[data-bare="1"] .docs code { color: #000; }
|
||||
|
||||
/* Bare mode (Q27): strip to default browser rendering. */
|
||||
html[data-bare="1"] body { background: #fff; color: #000; font-family: serif; text-shadow: none; font-size: 16px; }
|
||||
html[data-bare="1"] body::before { display: none; }
|
||||
|
||||
@@ -48,3 +48,16 @@ setup() {
|
||||
grep -q '<base href="/">' "$BASE/index.html"
|
||||
grep -q '<base href="/">' "$BASE/admin.html"
|
||||
}
|
||||
|
||||
@test "the user docs page exists and is reachable from the service pages" {
|
||||
[ -f "$BASE/docs.html" ]
|
||||
grep -q '<a href="/docs">docs</a>' "$BASE/index.html"
|
||||
grep -q 'id="models"' "$BASE/docs.html"
|
||||
grep -q '<base href="/">' "$BASE/docs.html"
|
||||
grep -q 'style.css' "$BASE/docs.html"
|
||||
}
|
||||
|
||||
@test "the docs route is served and shipped" {
|
||||
grep -qE 'try_files /docs\.html' "$REPO_ROOT/dankclaw/Caddyfile"
|
||||
grep -q 'shared/homepage/docs.html' "$REPO_ROOT/scripts/deploy-remote.sh"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user