live: heartbeat sync 2026-10-18T16:47:59-07:00 (Refs #11)

This commit is contained in:
Frank committed 2026-10-18 16:47:59 -07:00
1 parent a550db6027
commit 979e9ef711
173 files changed
+3844 -57

No files matched your search

@@ -1,29 +1,30 @@
---
name: openwebui-picker-acl-repair
description: "Fix OpenWebUI 0.6 \"No results found\" / Model not found in the model picker for non-admin users (Dankclaw fleet) after a connection rebuild."
description: "Add, enable, or repair a model in the Dankclaw OpenWebUI 0.6 picker: connection enable + model row + access_grant trio, verified via /api/models as admin and role=user."
---
# OpenWebUI 0.6 picker ACL repair (Dankclaw)
# OpenWebUI 0.6 picker: add / enable / repair a model (Dankclaw)
Symptom: admin sees models in OpenWebUI; regular users get "No results found" in the picker, or chat answers "Model not found". Cause: an OpenAI-connection rebuild (config keys `openai.api_base_urls` / `openai.api_configs` in the `config` table) that dropped the ACL pair for the model.
Symptoms: non-admin sees "No results found"/"Model not found" while admin sees the model (repair); endpoint live, model_ids correct, model absent from picker (add).
## Law
A non-admin user sees an external OpenAI-connection model only if BOTH exist in `webui.db`:
1. `model` table (singular) row: `id` == upstream model id, `base_model_id` NULL, `is_active` 1, `meta` JSON (description etc.).
2. `access_grant` row: `(resource_type='model', resource_id=<model id>, principal_type='user', principal_id='*', permission='read')`.
## Law — a picker entry needs ALL legs, keyed by the EXACT upstream served id
1. openai.api_configs (config table, JSON map by idx): that connection has enable: true and model_ids containing the upstream id. Non-empty model_ids = no upstream fetch (llama.cpp's Ollama-shaped /v1/models is invisible to discovery; halogen/radiance are OpenAI-shaped, model_ids stays authoritative).
2. model table row: id == upstream id (alias rows with base_model_id set render in the picker but chat fails "Model not found"), base_model_id NULL, is_active 1, user_id = admin uid, params {"context":{"length": PER-SLOT window}}, meta {"description": "..."}.
3. access_grant row: resource_type=model, resource_id=<id>, principal_type=user, principal_id='*', permission=read. Without it only admins see the model (get_filtered_models drops info-less rows for role=user).
The aggregation (`utils/models.py get_filtered_models`) drops info-less models for `role=user`; admins bypass. `get_all_models` responses are served from `model_ids` in `api_config` when non-empty (no upstream fetch). An alias row (`id` != upstream, `base_model_id` set) renders in the picker but chat fails "Model not found" — id must equal the upstream id.
URLs: on-host endpoints via host.containers.internal:<port> (bridge cannot hairpin the host LAN IP); other hosts via LAN IP.
## Steps
1. Reproduce as the affected role: in a browser tab, `fetch("/api/models", {headers:{Authorization:"Bearer "+localStorage.token}})` → `data: []` while an admin API key (`api_key` table, column `key`, `sk-…`) lists the model.
2. DB at `/home/dank/Containers/open-webui/data/webui.db` (host; no sqlite3 CLI on Dankclaw — use host `python3` heredoc over ssh; container path `/app/backend/data`).
3. Insert/repair the pair (uuid4 id, epoch-ms `created_at`):
- `model`: id/name = upstream id, base_model_id NULL, is_active 1, user_id = admin uid (role='admin' in `user` table), meta `{"description": "…"}`.
- `access_grant`: shape above.
4. Cache: `MODELS_CACHE_TTL` defaults to 1 s — no restart needed for lists; a restart (`systemctl --user restart open-webui`, rootless) only if config edits were cached oddly. An OpenWebUI restart invalidates browser JWTs (401) — expect a silent SSO re-round-trip; kill stale tabs first so OAuth `state` cookies don't race (MismatchingStateError → clear cookies/storage, one clean flow).
5. Verify end-to-end: pick the model in the UI, send a short exact-match prompt, confirm the reply; check `/api/models` as the user role lists the id.
1. Snapshot: cp -a webui.db webui.db.bak-<date><letter>.
2. Apply with host python3 over ssh (no sqlite3 CLI on Dankclaw) on /home/dank/Containers/open-webui/data/webui.db: read-modify-write the whole openai.api_configs JSON value; insert model row (epoch-ms created_at/updated_at); insert access_grant (uuid4 id).
3. Restart: systemctl --user restart open-webui.service (rootless quadlet) to drop the cached model list. Restart kills browser JWTs: expect one SSO re-round-trip per tab; kill stale tabs first so OAuth state cookies don't race.
4. Verify (mint tokens in the container; create_token is in open_webui.utils.auth; export WEBUI_SECRET_KEY from /app/backend/.webui_secret_key):
- GET /api/models (key data) lists the id as admin AND as a role=user token (e.g. qa-e2e-admin uid). This is the picker truth.
- POST /api/chat/completions, prompt "Reply with exactly one word: pong", max_tokens 1500 (thinking models give empty content + finish_reason length on small budgets).
- NEVER gate on GET /api/v1/models/list (key items): on 0.6.x it lists workspace models only, [] for connection-backed models (measured 2026-10-09).
## Related knobs
- User API keys (User menu → Settings → Account → API keys → Show → Create new secret key) require `user.permissions` JSON `features.api_keys=true` in `config` (default false).
- New SSO signups land as role `pending` (`ui.default_user_role=pending`) — activate: `UPDATE user SET role='user' WHERE email LIKE …`. Table is `user`, singular; no `active` column.
- Anubis gates `ai.franksdank.cloud` for headless browsers (webdriver detection): drive direct `:3005` for UI work, or emulate a desktop Chrome UA.
- User API keys need user.permissions JSON features.api_keys=true in config (default false).
- SSO signups land role pending (ui.default_user_role=pending): UPDATE "user" SET role='user' WHERE email=... (table user, singular, no active column); row exists only after first login.
- Anubis gates ai.franksdank.cloud for headless browsers; drive direct :3005.
- The trio is DB-only, lost on a fresh volume. Record changes in serverAppConfigs-docs/Dankclaw/OpenWebUI.md (Facts, backends table, History): the model id is a foreign key across three tables, so any endpoint alias rename must ship the same re-key.