--- type: reference title: WAN Model Access (ai.franksdank.cloud) description: How to drive the fleet's models from outside the home network — the OpenWebUI door in agent/models.yml, the key file and its rotation, what the door does and does not publish, and the off-network failure modes. tags: - omp - models-yml - openwebui - remote-access - fleet verified: 2026-10-21 timestamp: 2026-10-21 --- # WAN Model Access (ai.franksdank.cloud) Every provider in `agent/models.yml` except one is a LAN address, so a laptop that leaves the house loses the fleet. The exception is the `openwebui (franksdank.cloud)` block: the fleet's already-public OpenWebUI vhost speaks OpenAI-compatible HTTP at `/api/v1` and proxies to the same engines the LAN rows point at. One hop further out, no second inference stack. Read this page when you are off-network and a model call fails, when the key needs rotating, or before adding another row behind the door. ## Run a session away from home The door is added as a provider, never as a role default. `modelRoles` in `agent/config.yml` still names LAN providers, so away from home you must name the model explicitly: ```bash omp --model 'openwebui (franksdank.cloud)/frank-dragoon:medium' omp --model 'openwebui (franksdank.cloud)/frank-zealot:off' ``` The same two ids work in the `/model` picker inside a running session — the provider label is what disambiguates them from the LAN rows, since the wire ids are identical by design (OpenWebUI's picker row and the engine share the id, so there is no alias to keep in sync). ## The key `apiKey: "!cat ~/.config/secrets/openwebui_api_key"` — a **command-backed** value, resolved by `src/config/resolve-config-value.ts` in the harness: stdout is trimmed, an env-var name is tried next, and only then is the string taken literally. The secret itself is a 600-mode file in `~/.config/secrets/`, the same convention as `gitea` and `kagi_api_key`. Laws: - **Never inline the `sk-…`.** It is a tracked file; the pre-commit shape gate blocks `sk-…` anywhere in the staged diff, and a key that slipped through would live in every agent's context on every box that pulls this repo. - **Rotation is: rewrite the secret file, then restart omp.** The `!cmd` result is cached for the process lifetime, so a long-lived session keeps serving the old key — a stale-session failure reads exactly like a revoked key. - The key is an **OpenWebUI** key (User menu → Settings → Account → API keys), not a fleet endpoint key. It carries that user's model ACL, so what the door shows is whatever the picker shows that user. - Every other workstation needs its own copy of the file; nothing in the repo carries the value. ## What the door publishes Probed 2026-10-21 with the fleet key. |Behind the door|Notes| |---|---| |`frank-dragoon`|halogen flash-server, Dankclaw iGPU — same row as the LAN `llamacpp (dankclaw)`| |`frank-zealot`|llama.cpp on Dankclaw's 3090 — same row as `llamacpp (dankclaw-3090)`; also the WAN compaction target| |`frank-archon`|**not published.** `/api/v1/models` omits it and the API answers `400 "Model not found"`. Dankserver's endpoint has no public face at all. Giving it a WAN path is a fleet change on Dankclaw (publish it in the OpenWebUI picker), not a harness change| |`frank-tts`|route exists, contract does not. OpenWebUI forwards `/api/v1/audio/speech` to `frank-tts:7772` but substitutes its own voice enum first, and a cloned-pack name comes back `400` from the upstream. `modelRoles.speech` stays on the LAN `frank-tts (dankclaw)` row| ## Contract verified through the proxy Each of these was measured end-to-end on 2026-10-21 — they are why the compat blocks in the WAN rows are copies of their LAN twins rather than simplifications: - Streaming SSE carries `reasoning_content` **and** `tool_calls` deltas, `finish_reason: tool_calls`, and a final `usage` under `stream_options.include_usage`. - `image_url` content parts reach both vision towers. - `chat_template_kwargs` is forwarded — proven on `frank-zealot`, where `enable_thinking: false` yields zero reasoning and `true` yields a real thinking block on the same prompt. Drop the kwargs from a WAN row and you silently change thinking behaviour on the WAN path. - API-key completions create **no** OpenWebUI chat row: the chat list is unchanged after a probe, so an off-network session leaves nothing in the family's chat history. Context windows, `maxTokens`, sampling and effort maps are inherited from the engines and documented on the LAN rows and in the fleet vault (`serverAppConfigs-docs/LLM/`). The proxy neither widens a window nor truncates: an over-ceiling request is still a `400`. ## Off-network failure modes - **`Connection refused` / long timeout on a LAN id.** Not a service outage — you are not home. Switch to the WAN id. - **`Model not found` for `frank-archon`.** Expected; see the table above. - **Compaction wants a LAN address.** Each WAN row sets `compactionModel` to the WAN `frank-zealot`, but `compaction.remoteEndpoint` in `agent/config.yml` is a LAN URL. It is last in `methodOrder`, so the other methods run first and the remote attempt just fails — leave it unless off-network compaction turns out to need it. - **Roles still resolve to LAN.** A full off-network session that must not depend on hand-picked models needs `retry.fallbackChains` entries pointing the LAN defaults at the WAN rows; that is a deliberate `config.yml` change, not part of adding rows here. ## Verify the door ```bash # Key + model list (LAN clients resolve 192.168.0.143 via Pi-hole; the WAN # A record is what makes this reachable from elsewhere). curl -s https://ai.franksdank.cloud/api/v1/models \ -H "Authorization: Bearer $(cat ~/.config/secrets/openwebui_api_key)" | jq -r '.data[].id' # Registry loaded, WAN rows present, no validation failure (a failed load # silently disables EVERY custom provider — see # agent/models.yml's schema-law comment): omp models --no-extensions | grep -E 'franksdank|validation failed' ``` A scratch-HOME run is the only real proof that a harness change reaches the door: copy `models.yml` + `config.yml` to `$TMP/.omp/agent/`, symlink `$TMP/.config/secrets` at the real one (a `~` in a `!cmd` value expands to `$HOME`), rewrite the LAN baseUrls to a dead port so a success cannot be faked, then `HOME=$TMP omp --model 'openwebui (franksdank.cloud)/…' -p …`. Delete the scratch tree afterwards — it holds endpoint URLs. ## Pointers - Door topology: [Caddy](/caddy/index.md), [System hosts](/system/index.md). - Model registry law and per-provider limits: [Core config](index.md), [OMP Subagent Limits](subagent-limits.md). - Server-side record: `serverAppConfigs-docs/Dankclaw/OpenWebUI.md` (that repo's vault, not this bundle).