From 6632d0a2f6dda6e4d5a042da91d2ba6f843cb723 Mon Sep 17 00:00:00 2001 From: Frank Date: Wed, 7 Oct 2026 18:49:22 -0700 Subject: [PATCH] docs(openwebui,authentik): frank-secondary-only picker, API gating, artem account OpenWebUI model picker narrowed to exactly frank-secondary (connections frank-large/frank-primary disabled in openai.api_configs, model table rebuilt, stale frank-small model_id retargeted to the served alias). Documented the auth-gated API surface (anonymous probes all 401; keys per account). Authentik roster gains artem (pk 16) in the human-user template. --- serverAppConfigs-docs/Dankclaw/OpenWebUI.md | 49 ++++++++++++++----- .../Infrastructure/Authentik.md | 5 +- 2 files changed, 40 insertions(+), 14 deletions(-) diff --git a/serverAppConfigs-docs/Dankclaw/OpenWebUI.md b/serverAppConfigs-docs/Dankclaw/OpenWebUI.md index c3484d8..173d88a 100644 --- a/serverAppConfigs-docs/Dankclaw/OpenWebUI.md +++ b/serverAppConfigs-docs/Dankclaw/OpenWebUI.md @@ -24,22 +24,27 @@ https://github.com/open-webui/open-webui | Upstream latest | 0.11.4 (2026-09-21) — security/access-control advisory; `:main` is the rolling image, not a stable pin. Imagegen endpoint is `POST /api/v1/images/generations` (the `comfyui` engine is still upstream). | | Port | 3005 (host) → 8080 (container). Port 3000 is Gitea's HTTP, so it is not usable. | | Data volume | `/home/dank/Containers/open-webui/data` → `/app/backend/data` | -| Backends | Three fleet endpoints: `frank-large` (host.containers.internal:7770), `frank-medium` (192.168.0.235:7770), `frank-secondary` (host.containers.internal:7771) — see [The LLM backends](#the-llm-backends) | +| Backends | Picker exposes exactly one model — `frank-secondary` (host.containers.internal:7771). The `frank-large` and `frank-primary` connections remain configured in the DB, disabled (2026-10-07) — see [The LLM backends](#the-llm-backends) | | Network | Pinned bridge `systemd-open-webui` (`10.89.3.0/24`, gateway `10.89.3.1`) — see [History](#history) | | Persistence | Quadlet unit `open-webui.service`; `[Install] WantedBy=default.target` | | Deploy | `scripts/deploy-open-webui-quadlet.sh dankclaw` | ## The LLM backends -OpenWebUI serves the three fleet LLM endpoints. The model picker is built from -the `model` table in `webui.db` (one row per model), NOT from live connection -discovery — so the picker shows exactly the models seeded there. +The picker is built from the `model` table in `webui.db` (one row per model) +plus the `model_ids` of each **enabled** OpenAI connection, NOT from live +connection discovery — so the picker shows exactly what is seeded there. +As of 2026-10-07 the picker exposes **exactly one model: `frank-secondary`**. -| Picker model | Connection URL | Runtime | Host | -|--------------|----------------|---------|------| -| `frank-large` | `http://host.containers.internal:7770/v1` | unsloth llama.cpp (Strix Halo iGPU, 200k ctx) | Dankclaw | -| `frank-medium` | `http://192.168.0.235:7770/v1` | vllm-radiance TP=2 (FP8, DFlash2, 262k ctx) | Dankserver | -| `frank-secondary` | `http://host.containers.internal:7771/v1` | llama.cpp (RTX 3090, Gemma4-12B, 262k ctx) | Dankclaw | +| Connection (`openai.api_configs` idx) | URL | Enabled | `model_ids` | Runtime | Host | +|---|---|---|---|---|---| +| 0 | `http://host.containers.internal:7770/v1` | no (2026-10-07) | `frank-large` | halogen flash-server (Strix Halo iGPU) | Dankclaw | +| 1 | `http://192.168.0.235:7770/v1` | no (2026-10-07) | `frank-primary` | radiance TP=2 (2× R9700) | Dankserver | +| 2 | `http://host.containers.internal:7771/v1` | yes | `frank-secondary` | llama.cpp CUDA (RTX 3090, 2×200,704) | Dankclaw | + +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). Two gotchas make the wiring non-obvious: @@ -62,7 +67,9 @@ The Ollama connection is disabled (`ollama.enable=false`). > All three endpoints are bare-metal systemd services managed by this > repository. Model swaps, context sizing, and spec tuning happen there. See -> the [LLM knowledge base](../LLM/index.md). +> the [LLM knowledge base](../LLM/index.md). Disabling a connection here only +> hides it from this picker — the endpoint itself keeps serving every other +> client (the harness, Hermes, API consumers). ## Container introspection (DB and backend python) @@ -96,9 +103,11 @@ The Ollama connection is disabled (`ollama.enable=false`). 1. `GET /api/v1/models/list?limit=100` → returns EXACTLY the intended models. Response key is `items`, not `data`. -2. `POST /openai/chat/completions` per model with a trivial prompt ("Reply +2. `POST /api/chat/completions` per model with a trivial prompt ("Reply with exactly one word: pong") → each returns content `pong`. Reasoning - models need `max_tokens>=100` — the thinking block eats the budget. + models need a generous budget: frank-secondary (Qwen3.5 template) returns + empty content + `finish_reason: "length"` at `max_tokens 200` (the + thinking block eats it) and answers `pong` at 1500. 3. A 500 "Server Connection Error" → `podman logs open-webui` for `ClientConnectorError`: a network-reachability problem (host LAN IP vs `host.containers.internal`), not a model problem. @@ -107,6 +116,20 @@ The Ollama connection is disabled (`ollama.enable=false`). a fresh data volume loses them. For reproducibility, add an idempotent first-start seed script or document the setup in the service doc. +## API access (auth-gated) + +Every `/api/*` endpoint requires an authenticated session (SSO login JWT) +or a per-user API key (`auth.enable_api_keys=true`; keys are minted per +account under Profile → API Keys). Anonymous web visitors get the `pending` +role (`ui.default_user_role=pending`), which cannot chat. Verified +2026-10-07 from loopback `:3005` and over the public vhost: +`GET /api/v1/models/list`, `GET /api/v1/models` and +`POST /api/chat/completions` all return **401** with no key (also with a +bogus bearer token); only `/api/config`, `/api/version` and `/health` +answer publicly, and none of them expose the model list. An external +program needs an account + API key — there is no keyless access. + + ## Web search Chat web search is served by [[SearXNG]] on this host, wired in the quadlet @@ -230,6 +253,8 @@ on large sizes ⇒ check ComfyUI resources; 401 ⇒ the token is bad. - **2026-09-24 — flipped to SSO-only.** The OIDC round-trip verified in a real browser (login view renders no password form, only "Continue with authentik", and the flow lands on a signed-in session), so the local username/password path was retired: `ENABLE_LOGIN_FORM=false` + `ENABLE_PASSWORD_AUTH=false` in the quadlet and `ui.enable_login_form` set to `false` in the config DB (the DB wins over env, so both were changed together). `qa-e2e-admin` was demoted out of the `authentik Admins` group (it keeps `openwebui_users` / `homepage-admins` for app access). +- **2026-10-07 — picker narrowed to `frank-secondary` only.** Per Frank: the only model accessible in OpenWebUI is `frank-secondary`. Rebuilt the `model` table to a single `frank-secondary` row (the old rows `frank-large`/`frank-primary`/`frank-small` are deleted), set `openai.api_configs` indices 0 (frank-large) and 1 (frank-primary) to `enable: false`, and retargeted index 2's `model_ids` from the stale `frank-small` label to `frank-secondary` — llama.cpp serves by alias, so the old label addressed a model name the server does not know. Pre-change snapshot: `/home/dank/Containers/open-webui/data/webui.db.bak-20261007` (+ `-wal`/`-shm`). Verified: `/api/v1/models/list` returns exactly `["frank-secondary"]`, chat completion answers `pong` (needs `max_tokens≥1500` — the Qwen3.5 thinking block consumes a 200-token budget), and anonymous API probes are all 401 (see [API access](#api-access-auth-gated)). + ## See Also - [[Dankclaw Stack]] diff --git a/serverAppConfigs-docs/Infrastructure/Authentik.md b/serverAppConfigs-docs/Infrastructure/Authentik.md index b132d02..9fc8f26 100644 --- a/serverAppConfigs-docs/Infrastructure/Authentik.md +++ b/serverAppConfigs-docs/Infrastructure/Authentik.md @@ -451,8 +451,9 @@ access is decided entirely by GROUP MEMBERSHIP consumed downstream — (filter `(memberOf=cn=jellyfin_users,ou=groups,dc=franksdank,dc=cloud)`). - `Non-Admin Users` = the plain-user marker group (precedents: qa-e2e, and every human account). -- **The human-user template** (ridley, kassie, alex — the last two created - 2026-10-08): `username` + `@franksdank.cloud` + display name + +- **The human-user template** (ridley, kassie, alex, artem — kassie and alex + created 2026-10-08, artem (pk 16) created 2026-10-07): `username` + + `@franksdank.cloud` + display name + `is_active=True` + the password set via `set_password`, membership in `Non-Admin Users` + `jellyfin_users` + `openwebui_users`. `alex` was recreated from scratch on 2026-10-08 into this shape (the old row had an