From cee90eea526707572ee32ba14ee60c7b7c00859d Mon Sep 17 00:00:00 2001 From: Frank Date: Wed, 7 Oct 2026 23:27:23 -0700 Subject: [PATCH] homepage: amber CRT theme, VT323, current-fleet docs + API access guide - style.css: green phosphor -> amber CRT-AMBIOS palette; docs styles for screenshots, code blocks, key warning (full + bare mode) - index/admin/docs: VT323 from the Google Fonts CDN - docs.html: rewrite #chat/#models for the live fleet (flash-server v2, radiance native FP8, Qwen3.5-9B Q6_K); new #api-access with OpenWebUI key walkthrough + Opencode and Pi configs; three OpenWebUI screenshots - services.json: AI card details match the current fleet - deploy-remote.sh: ship shared/homepage/img/ to /srv/homepage/ - docs: Homepage.md theme wave + OpenWebUI.md picker access-control law --- scripts/deploy-remote.sh | 3 + serverAppConfigs-docs/Dankclaw/OpenWebUI.md | 16 ++- .../Infrastructure/Homepage.md | 19 ++- shared/homepage/admin.html | 3 + shared/homepage/docs.html | 117 +++++++++++++++--- shared/homepage/img/openwebui-api-keys.png | Bin 0 -> 102416 bytes shared/homepage/img/openwebui-chat.png | Bin 0 -> 43521 bytes .../homepage/img/openwebui-model-picker.png | Bin 0 -> 57662 bytes shared/homepage/index.html | 3 + shared/homepage/services.json | 8 +- shared/homepage/style.css | 41 +++--- 11 files changed, 169 insertions(+), 41 deletions(-) create mode 100644 shared/homepage/img/openwebui-api-keys.png create mode 100644 shared/homepage/img/openwebui-chat.png create mode 100644 shared/homepage/img/openwebui-model-picker.png diff --git a/scripts/deploy-remote.sh b/scripts/deploy-remote.sh index a11fa1d..51e23b5 100755 --- a/scripts/deploy-remote.sh +++ b/scripts/deploy-remote.sh @@ -322,6 +322,9 @@ deploy_homepage() { if [[ -d shared/homepage/fonts ]]; then run_privileged cp -r shared/homepage/fonts /srv/homepage/ fi + if [[ -d shared/homepage/img ]]; then + run_privileged cp -r shared/homepage/img /srv/homepage/ + fi local deployed_at deployed_at="$(git log -1 --format=%cI)" printf 'window.DEPLOYED_AT = "%s";\n' "$deployed_at" > /tmp/deployed.js diff --git a/serverAppConfigs-docs/Dankclaw/OpenWebUI.md b/serverAppConfigs-docs/Dankclaw/OpenWebUI.md index 173d88a..d895813 100644 --- a/serverAppConfigs-docs/Dankclaw/OpenWebUI.md +++ b/serverAppConfigs-docs/Dankclaw/OpenWebUI.md @@ -46,6 +46,17 @@ The `model` table carries exactly one decoration row, `frank-secondary` (context length 200,704 = the per-slot window; description: Qwen3.5-9B Q6_K + BF16 vision mmproj). +A third gate sits *after* the table, and it is the one that bites family +accounts (2026-10-07): a model row is only visible to a non-admin user when +its **access permission** grants them. A seeded row defaults to admin-only, +so the picker renders "No results found" for every regular user even though +the connection is enabled, `model_ids` is correct, and the endpoint answers +`/v1/models` — and `GET /api/v1/models` returns `[]` for that user while an +admin token returns the model. Fix: set the row's `access_control` to public +(the `access` column on the `model` row) via the admin UI (Admin Panel → +Models → visibility) or the DB. The verification battery below must therefore +run **as a non-admin user**, not only as an admin. + Two gotchas make the wiring non-obvious: - **llama.cpp `/v1/models` is Ollama-shaped.** llama.cpp returns @@ -113,8 +124,9 @@ The Ollama connection is disabled (`ollama.enable=false`). `host.containers.internal`), not a model problem. **Durability:** the model table + connections are DB-only (not git-managed); -a fresh data volume loses them. For reproducibility, add an idempotent -first-start seed script or document the setup in the service doc. +a fresh data volume loses them — including the public access grant above. +For reproducibility, add an idempotent first-start seed script or document +the setup in the service doc. ## API access (auth-gated) diff --git a/serverAppConfigs-docs/Infrastructure/Homepage.md b/serverAppConfigs-docs/Infrastructure/Homepage.md index a05ad12..a9bf7d4 100644 --- a/serverAppConfigs-docs/Infrastructure/Homepage.md +++ b/serverAppConfigs-docs/Infrastructure/Homepage.md @@ -36,6 +36,20 @@ deliberately unprobeable) via the `wip` value (`"open"` / `"closed"`). Tautulli (Plex statistics, `192.168.0.235:8181`) joined the admin page's Media Library Apps section. +**Amber CRT theme + API docs (2026-10-07):** the palette moved from green +phosphor to amber CRT-AMBIOS (`--fg #ffb000` on `--bg #0a0600`), and the font +stack now leads with **VT323** loaded from the Google Fonts CDN (the deploy +also ships `shared/homepage/img/` beside the page, so `/img/*` serves the docs +screenshots). The docs page gained a full **API access** walkthrough +(`/docs#api-access`): per-user OpenWebUI API keys (User menu → Settings → +Account → API keys), the base URL `https://ai.franksdank.cloud/api/v1`, and +copy-paste configs for **Opencode** (`~/.config/opencode/opencode.json`) and +**Pi** (`~/.pi/agent/models.json`) pointing at `frank-secondary`. Shipped +together with the OpenWebUI side: `features.api_keys=true` for regular users +and the restored `frank-secondary` model row + public `read` access grant +without which the picker showed "No results found" for every family account +(see [[OpenWebUI]]). + ## 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. @@ -111,7 +125,7 @@ flowchart TD ## Service classification **User services** (root page), in section order: -- **AI** — OpenWebUI, and the three LLM fleet cards (frank-large, frank-medium, frank-secondary — status + docs link, no Open link). +- **AI** — OpenWebUI, and the three LLM fleet cards (frank-large, frank-primary, frank-secondary — status + docs link, no Open link). - **Web Apps** — SearXNG, Gitea, Copyparty, Marreta. - **Media Library Apps** — Calibre-Web, RomM, Plex, Jellyfin, Seerr. @@ -131,7 +145,8 @@ shared/homepage/ ├── 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) -└── fonts/ # Terminus (woff2) for @font-face +├── img/ # screenshots served at /img (docs page; deployed with the page) +└── fonts/ # optional self-hosted fonts (deployed with the page; VT323 comes from the Google Fonts CDN, this dir is absent) ``` `status.json` is generated at runtime in `/srv/homepage/`. It is not committed. diff --git a/shared/homepage/admin.html b/shared/homepage/admin.html index 102f3e4..f79c122 100644 --- a/shared/homepage/admin.html +++ b/shared/homepage/admin.html @@ -5,6 +5,9 @@ frank — admin + + + diff --git a/shared/homepage/docs.html b/shared/homepage/docs.html index f002382..ab14b3b 100644 --- a/shared/homepage/docs.html +++ b/shared/homepage/docs.html @@ -5,6 +5,9 @@ frank — docs + + + @@ -31,13 +34,18 @@

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.

-

Images go to frank-primary or frank-secondary - (both see them). Audio (WAV) goes to frank-secondary only.

+

The everyday front door is Open WebUI, + the family chat app. Open the address, click Continue with authentik, + and use the same single sign-on as everything else — no separate password. Your + conversations are saved to your account, so history follows you between devices.

+ Open WebUI chat screen with a question and the AI's answer +

Below the text box, the model box on the right chooses which model answers you. + Family accounts get frank-secondary — the fleet's fast all-rounder, + which also sees images: drag a photo or screenshot straight into the chat box. + Frank's account can additionally reach the bigger models.

+ The Open WebUI welcome screen with frank-secondary selected in the model box +

Open WebUI keeps your chat history; the raw model endpoints (used by developer + tools, see API access) do not.

@@ -47,27 +55,26 @@

frank-large — deep reasoning

frank-primary — the primary model

frank-secondary — the fast utility model

Which one to pick? Small utility work, RAG, search, and @@ -76,6 +83,82 @@ frank-large (with Frank's permission).

+
+

API access — the AI on your own laptop

+

Developer assistants like Opencode and Pi are + chat programs that run on your own computer and can read your files, run commands, + and edit code while they talk. They are all built to speak the same standard + language as the big commercial products (the "OpenAI-compatible" API), and our + server speaks it too. You give the program three things — an address, a personal + key, and a model name — and your laptop thinks through our hardware.

+ +

Step 1 — get your personal key

+
    +
  1. Open ai.franksdank.cloud and log in.
  2. +
  3. Click your circle (bottom-left corner) → Settings → Account.
  4. +
  5. Scroll to API keys → press Show → Create new secret key.
  6. +
  7. Press the copy icon. The key looks like sk-3f2a….
  8. +
+ The Open WebUI Account settings page with the API key field at the bottom +

The key is a password. Anyone who has it can use our AI as + you. Paste it only into the program's config file — never into a shared chat, + a screenshot, or a git repository.

+ +

Step 2 — the three details every tool asks for

+ +

Proof that it works, from any terminal (replace the key):

+
curl https://ai.franksdank.cloud/api/v1/chat/completions \
+  -H "Authorization: Bearer PASTE-YOUR-KEY" \
+  -H "Content-Type: application/json" \
+  -d '{"model": "frank-secondary", "messages": [{"role": "user", "content": "Hello!"}]}'
+ +

Step 3a — Opencode

+

Install once (npm install -g opencode-ai), then create the file + ~/.config/opencode/opencode.json with:

+
{
+  "$schema": "https://opencode.ai/config.json",
+  "provider": {
+    "frank": {
+      "npm": "@ai-sdk/openai-compatible",
+      "name": "Frank fleet",
+      "options": {
+        "baseURL": "https://ai.franksdank.cloud/api/v1",
+        "apiKey": "PASTE-YOUR-KEY"
+      },
+      "models": {
+        "frank-secondary": { "name": "frank-secondary" }
+      }
+    }
+  }
+}
+

Start opencode and pick the model with /model — + it appears as frank/frank-secondary.

+ +

Step 3b — Pi

+

Create the file ~/.pi/agent/models.json with:

+
{
+  "providers": {
+    "frank": {
+      "baseUrl": "https://ai.franksdank.cloud/api/v1",
+      "api": "openai-completions",
+      "apiKey": "PASTE-YOUR-KEY",
+      "models": [
+        { "id": "frank-secondary" }
+      ]
+    }
+  }
+}
+

Then start Pi with pi --model frank/frank-secondary.

+ +

The key works only for the family AI — it is not your + Authentik password, and losing it costs nothing beyond asking Frank to rotate it. + Want a bigger model (frank-primary, frank-large)? Ask Frank to raise your access.

+
+

Media & files