Themelio compatibility reference. This page describes the retained Themelio implementation or its historical design. Check the parallel-operation guide before applying it to the current platform.
DD-0011: Chat UI and Client Access — Implementation
Related ADR: ADR-0011: Chat UI and Client Access for Shared LLM Services
Status: Draft
Date: 2026-06-30
Authors: TBD
Implementation branches
All branches are stacked on top of earlier local/* branches and diverge from origin/main
by approximately 110 commits total.
Tested on a local Minikube cluster in CPU mode only. Full SSO login against the production AAI (GRNET MyAccessID) and multi-project isolation at scale require staging/production validation.
Open WebUI deployment
Open WebUI is deployed by the open-webui-deploy Ansible role into the open-webui namespace
using the official Helm chart.
Disabled features (set via Helm values):
Open WebUI's default OpenAI connection (openaiBaseApiUrl) is set to LiteLLM's internal
Service URL with a disabled key (disabled-until-project-connection). Per-project connections
are provisioned separately by llm-access-root-actor (see below).
OIDC / SSO configuration
The project_ids OIDC groups claim is a local development shim only. Keycloak is
configured with a custom mapper that injects project_ids from seeded group memberships
to exercise the SSO login flow end-to-end in the local cluster. In staging and production,
the external Keycloak / MyAccessID realm does not carry project group data. Group membership
is set exclusively by llm-access-root-actor via the membership sync loop (see DD-0020).
Local CA trust (local mode only)
In local mode, an init container merges the local SSL proxy CA into the Open WebUI Python
certifi bundle so that OIDC back-channel calls succeed despite the self-signed certificate.
This init container runs only when local_mode=true; it is not present in staging/production.
Per-project connection model
Open WebUI stores its OpenAI-compatible backend connections in three parallel arrays in its
internal config (retrieved via GET /openai/config and updated via POST /openai/config/update):
One entry is maintained per Waldur project resource by llm-access-root-actor:
When a resource is terminated, llm-access-root-actor sets config.enable: false and
clears the api_key field; the entry is not removed (prevents index shift in the arrays).
What the prefix_id controls
Open WebUI uses prefix_id + model_ids to generate per-connection model entries. The
resulting model IDs visible to users are <prefix_id>/<model_id> internally, but the
root actor explicitly creates separate model records named <resource_backend_id>.<model_id>
with group-scoped access grants.
Model naming and ACL
Models in Open WebUI are created by the root actor with the naming convention:
For example, project buffermusic with model qwen2.5-0.5b → model ID buffermusic.qwen2.5-0.5b.
Each model has access grants set via PUT /api/v1/models/model:
Where group_id is the Open WebUI group corresponding to the project (see below).
Users who are not members of the group cannot see or use the model.
When a resource is terminated or its model allowlist changes:
- Removed models have their
access_grantscleared (no group restriction → effectively hidden from all non-admin users; the root actor does not delete model records). - New models in the allowlist get the project group ACL applied.
Group management
One Open WebUI group per Waldur project resource:
The group is created idempotently via POST /api/v1/groups/create if it does not exist;
otherwise POST /api/v1/groups/id/<id>/update updates the description if it changed.
Group membership is managed separately by the membership_sync agent (see DD-0020).
The group creation and model ACL wiring happen during project_create / project_update;
user additions/removals happen on every membership sync cycle (every 5 minutes).
Request routing (UI path)
When a human user chats in Open WebUI:
Open WebUI does not have direct access to the inference namespace or vLLM endpoints. All routing goes through LiteLLM. Budget enforcement is identical to the API path.
API client access
API clients (OpenAI SDK, Claude Code, Codex CLI) authenticate to LiteLLM directly using the per-project virtual key. The key is distributed via the Waldur offering:
- Virtual keys do not expire automatically — there is no TTL. A key remains valid until explicitly rotated.
- The
rotate_project_keyendpoint on the root actor (service.py: rotate_project_key) generates a brand-new LiteLLM virtual key viaPOST /key/generate, persists it to the project's Kubernetes Secret (bumping arotation_versioncounter), updates the Open WebUI connection to use the new key, and then deletes the previous key from LiteLLM. Rotation is therefore immediate and destructive to the old key: any client still configured with the previous key starts failing right after rotation completes. - The Waldur offering exposes a "view API key" action that triggers this endpoint. Rotation is on-demand only — there is no automatic/periodic rotation schedule.
- API clients configure their base URL to the LiteLLM external endpoint and use the key.
For Codex CLI clients requiring POST /v1/responses: a separate responses-proxy service
(from local/codex-responses-api branch) translates /v1/responses to Chat Completions.
This is outside the LiteLLM gateway and the Open WebUI stack.
Page source
Source snapshot · Revision f928834c8.