diff --git a/dankclaw/Caddyfile b/dankclaw/Caddyfile index 26a4d99..d27850c 100644 --- a/dankclaw/Caddyfile +++ b/dankclaw/Caddyfile @@ -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 diff --git a/scripts/deploy-remote.sh b/scripts/deploy-remote.sh index 6c4b9e9..273733a 100755 --- a/scripts/deploy-remote.sh +++ b/scripts/deploy-remote.sh @@ -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 diff --git a/serverAppConfigs-docs/Infrastructure/Homepage.md b/serverAppConfigs-docs/Infrastructure/Homepage.md index 4f2bcd1..9d3000f 100644 --- a/serverAppConfigs-docs/Infrastructure/Homepage.md +++ b/serverAppConfigs-docs/Infrastructure/Homepage.md @@ -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//` 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) diff --git a/shared/homepage/docs.html b/shared/homepage/docs.html new file mode 100644 index 0000000..934dbb9 --- /dev/null +++ b/shared/homepage/docs.html @@ -0,0 +1,126 @@ + + + + + +frank — docs + + + + +
+ frank@home:~$ docs + bare +
+
+ +
+

Getting in

+

Everything here lives behind two doors. The first is Anubis, 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 + Authentik, 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.

+
    +
  • Need an account? Ask Frank. Accounts are issued by hand; there is no signup.
  • +
  • Your clearance lives in a cookie. Clearing cookies means re-clearing both doors.
  • +
  • If a page asks you to log in repeatedly, that is a server-side problem — tell Frank, don't brute-force it.
  • +
+
+ +
+

Talking to the AI

+

The everyday front door is Open WebUI + (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 below. Open WebUI keeps your chat history; the raw + model endpoints do not.

+

There is no audio input anywhere on the fleet. Text in, and — on + frank-small only — images too.

+
+ +
+

The running models

+

Three models serve the family, each on its own hardware. The homepage status + cards above show whether each one is up right now.

+ +

frank-large — the big brain

+
    +
  • Model: Qwen3.8-Flash-Next, w4b quantization, served by halogen-flash-server.
  • +
  • Hardware: Dankclaw's Strix Halo iGPU (gfx1151), shared with the whole host.
  • +
  • Best for: long, thoughtful text conversations. 200k-token context per conversation.
  • +
  • Capacity: exactly two conversations resident at once. A third waits — politely, but it waits.
  • +
  • Not for: images. It cannot see them.
  • +
+ +

frank-small — the eyes

+
    +
  • Model: Gemma4-12B QAT (HauhauCS Balanced, Q4_K_M) with MTP + n-gram speculative decoding.
  • +
  • Hardware: Dankclaw's RTX 3090.
  • +
  • Best for: anything with a picture — screenshots, photos, diagrams. Fastest decode of the fleet at long context, and the endpoint that gets most daily traffic.
  • +
  • Capacity: one million tokens of pooled context across four simultaneous conversations (up to 262k each).
  • +
+ +

frank-medium — the 27B generalist

+
    +
  • Model: Swift-Qwen3.8-27B-Uncensored at UD-Q8_K_XL, with its own MTP head, on mainline llama.cpp.
  • +
  • Hardware: Dankserver's two Radeon R9700s, one process split 35:30 across the cards.
  • +
  • Best for: general chat and reasoning when the big brain is busy; the biggest pure-language model we run.
  • +
  • Capacity: 393k-token context over two conversations. Also sees images (BF16 projector).
  • +
+ +

Which one to pick? Screenshots and photos: frank-small. + Deep long-text work: frank-large (mind the two-seat limit). + When in doubt: frank-medium.

+
+ +
+

Media & files

+
    +
  • Movies & TV: request with Seerr; + watch with Plex or + Jellyfin. Don't ask the *arrs directly — + Seerr is the front door, and requests ride the tunnel from there.
  • +
  • Books: Calibre-Web — the e-book library, with its own login.
  • +
  • Games: RomM — ROM library and emulator collection.
  • +
  • Raw files: Copyparty — direct downloads and shares.
  • +
  • Search: SearXNG — metasearch, VPN-routed, no AI snoop.
  • +
+
+ +
+

When something looks broken

+
    +
  • A status card says DOWN. Reload the page once; the checker + re-probes every few minutes. If it stays down, that service really is unhappy — tell Frank.
  • +
  • "status unavailable" on the homepage. The page couldn't read + its status file. Hard-reload (Ctrl/Cmd-Shift-R).
  • +
  • Connection timeout on a home device. 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.
  • +
  • The bot-check spins forever. Let it finish; heavy JS blocking + (privacy extensions) can starve it. Pause the blocker, reload, resume.
  • +
+
+ +
+ + + + diff --git a/shared/homepage/index.html b/shared/homepage/index.html index f6d2c83..778342f 100644 --- a/shared/homepage/index.html +++ b/shared/homepage/index.html @@ -16,6 +16,7 @@
diff --git a/shared/homepage/services.json b/shared/homepage/services.json index 32bb85d..dd91063 100644 --- a/shared/homepage/services.json +++ b/shared/homepage/services.json @@ -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, diff --git a/shared/homepage/style.css b/shared/homepage/style.css index 505364d..068a2cd 100644 --- a/shared/homepage/style.css +++ b/shared/homepage/style.css @@ -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; } diff --git a/tests/homepage_pages.bats b/tests/homepage_pages.bats index a6772f7..de64e7d 100644 --- a/tests/homepage_pages.bats +++ b/tests/homepage_pages.bats @@ -48,3 +48,16 @@ setup() { grep -q '' "$BASE/index.html" grep -q '' "$BASE/admin.html" } + +@test "the user docs page exists and is reachable from the service pages" { + [ -f "$BASE/docs.html" ] + grep -q 'docs' "$BASE/index.html" + grep -q 'id="models"' "$BASE/docs.html" + grep -q '' "$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" +}