Add all 104 active skills from all 16 Hermes profiles
12 unversioned skills now versioned at 1.0.0: agent-communication, ascii-video, external-reasoning-augmentation, jotty-notes-api, minecraft-modpack-server, obsidian, pokemon-player, powerpoint, social-search, songwriting-and-ai-music, workspace-context-organization, youtube-content Total repo: 141 skills across all profile scopes
This commit is contained in:
247
open-webui-profile-setup/SKILL.md
Normal file
247
open-webui-profile-setup/SKILL.md
Normal file
@@ -0,0 +1,247 @@
|
||||
---
|
||||
name: open-webui-profile-setup
|
||||
description: "Create new Hermes profiles for Open WebUI personas — clone, port assignment, API server config, and runbook generation. Covers the full pattern: Hermes-side profile creation + Open WebUI REST API configuration (groups, users, presets, connections, visibility grants, memory-isolation probes)."
|
||||
version: 1.0.0
|
||||
author: Hermes Agent
|
||||
license: MIT
|
||||
metadata:
|
||||
hermes:
|
||||
tags: [open-webui, hermes-profiles, api-server, runbooks, personas]
|
||||
related_skills: [hermes-config-internals, hermes-minimal-profile-setup]
|
||||
---
|
||||
|
||||
# Open WebUI Profile Setup
|
||||
|
||||
Create new Hermes profiles that serve as API backends for Open WebUI personas. Each persona gets its own Hermes profile (separate port, separate memory, separate toolsets) and its own Open WebUI configuration (group, user, preset, connection). This skill covers both sides.
|
||||
|
||||
## When to Use
|
||||
|
||||
- Adding a new person to the Open WebUI instance (family member, guest, etc.)
|
||||
- Creating a new persona profile that needs memory isolation from existing users
|
||||
- Generating a runbook for a new profile so the setup is repeatable
|
||||
- Documenting the live state of the Open WebUI deployment
|
||||
|
||||
## HARD RULE: Probe the Live System FIRST
|
||||
|
||||
**Never trust documentation over live state.** Before writing or updating any runbook, probe the live Open WebUI instance via its REST API. Documentation rots; the API is truth. Minimum probe surface:
|
||||
|
||||
- `GET /api/config` — version, signup status, features
|
||||
- `GET /api/v1/users/all` — all users with IDs, emails, roles
|
||||
- `GET /api/v1/groups/` — all groups with IDs, descriptions
|
||||
- `POST /api/v1/groups/id/{id}/users` — members of each group (POST, not GET!)
|
||||
- `GET /api/v1/models` — all models/presets with base_model_id, access_grants
|
||||
- `GET /openai/config` — all connections with URLs, key previews
|
||||
- `GET /openai/models/{idx}` — verify each connection resolves
|
||||
- `GET /api/v1/audio/config` — TTS/STT settings
|
||||
- `ss -tlnp | grep 865` — which Hermes API servers are actually running
|
||||
- `grep API_SERVER ~/.hermes/profiles/*/.env` — which profiles have API server configs
|
||||
|
||||
Compile findings into an `OpenWebUI.md` document. See `references/OpenWebUI-live-state-template.md` for the section-by-section template. This document becomes the single source of truth — update it after every phase execution.
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
Open WebUI (10.0.0.204:8080, Docker)
|
||||
| POST /v1/chat/completions (OpenAI-compatible, SSE streaming)
|
||||
| Multiple OpenAI connections, one per Hermes profile
|
||||
v
|
||||
Hermes Agent API Servers (10.0.0.42)
|
||||
├── open1 (8653) — Rob (operator)
|
||||
├── openz (8654) — Zoe (kid)
|
||||
├── openg (8655) — guests
|
||||
└── openj (8656) — Jennifer (wife)
|
||||
```
|
||||
|
||||
Memory isolation is enforced by which Hermes profile handles the chat. Each profile has its own memory scope. The Open WebUI preset's `base_model_id` determines routing.
|
||||
|
||||
## Existing Profiles (port registry)
|
||||
|
||||
| Profile | Port | Model name | Owner | Group |
|
||||
|---------|------|------------|-------|-------|
|
||||
| open1 | 8653 | Hermes Agent (open1) | Rob | Operators |
|
||||
| openz | 8654 | Hermes Agent (openz) | Zoe | Kids |
|
||||
| openg | 8655 | Hermes Agent (openg) | Guest | Guests |
|
||||
| openj | 8656 | Hermes Agent (openj) | Jennifer | Family |
|
||||
|
||||
Reserved ports to avoid: 8644 (webhook), 8645 (wecom-callback), 8646 (msgraph-webhook). Use 8650+ for custom profiles. Next available: 8657.
|
||||
|
||||
## Workflow
|
||||
|
||||
### Phase 1: Hermes-side — create the profile
|
||||
|
||||
1. **Clone from open1** (the minimal template):
|
||||
```bash
|
||||
hermes profile create <name> --clone-from open1
|
||||
```
|
||||
|
||||
2. **Assign a port** — pick the next available from the registry above. Add to `~/.hermes/profiles/<name>/.env`:
|
||||
```bash
|
||||
API_SERVER_ENABLED=true
|
||||
API_SERVER_PORT=<port>
|
||||
API_SERVER_HOST=0.0.0.0
|
||||
API_SERVER_KEY=<generate-a-strong-random-key>
|
||||
API_SERVER_MODEL_NAME=Hermes Agent (<name>)
|
||||
```
|
||||
|
||||
3. **Install and start the gateway**:
|
||||
```bash
|
||||
hermes -p <name> gateway install --start-now --start-on-login
|
||||
```
|
||||
|
||||
4. **Verify**:
|
||||
```bash
|
||||
ss -tlnp | grep <port> # expect 0.0.0.0:<port>
|
||||
curl -s http://127.0.0.1:<port>/health # expect {"status":"ok"}
|
||||
```
|
||||
|
||||
5. **Verify cross-host reachability** from the Open WebUI host (use curl from Hermes host — SSH to 10.0.0.204 is NOT available, see gotcha #13):
|
||||
```bash
|
||||
KEY=$(grep '^API_SERVER_KEY=' ~/.hermes/profiles/<name>/.env | cut -d= -f2-)
|
||||
curl -s -m 5 -H "Authorization: Bearer $KEY" http://10.0.0.42:<port>/v1/models
|
||||
# expect: {"object":"list","data":[{"id":"Hermes Agent (<name>)",...}]}
|
||||
```
|
||||
|
||||
### Phase 2: Generate the runbook
|
||||
|
||||
Read the most recent runbook as a template (currently `openg_runbook.md` or `openj_runbook_for_jennifer.md` in `/home/n8n/workspace/open1/`). Substitute:
|
||||
- Profile name, port, model name
|
||||
- Group name and description
|
||||
- User name, email, password, role
|
||||
- Preset id, name, TTS voice
|
||||
- Permission level (Operators-level for adults, Kids-level for children)
|
||||
- System prompt (empty for adults, guardrails for kids)
|
||||
|
||||
Write the runbook to `/home/n8n/workspace/open1/<name>_runbook_for_<person>.md`.
|
||||
|
||||
### Phase 3: Open WebUI-side — execute the runbook
|
||||
|
||||
The runbook covers these steps in order:
|
||||
1. Verify prerequisites (gateway live, cross-host reachable)
|
||||
2. Admin signin to get JWT
|
||||
3. Add the new LLM connection (append to `OPENAI_API_BASE_URLS`)
|
||||
4. Create the group with appropriate permissions
|
||||
5. Create the user account (`POST /api/v1/auths/add`)
|
||||
6. Add user to group
|
||||
7. Create the preset (`POST /api/v1/models/create`)
|
||||
8. Set visibility grants for base model and preset
|
||||
9. Run memory-isolation probe
|
||||
10. Run full verification recipe
|
||||
|
||||
## Permission Templates
|
||||
|
||||
### Operators-level (adults: Rob, Jennifer, guests)
|
||||
```json
|
||||
{
|
||||
"workspace": {
|
||||
"models": true, "knowledge": false, "prompts": false,
|
||||
"tools": false, "skills": false, "documents": true,
|
||||
"web_search": true, "image_generation": false,
|
||||
"code_interpreter": false, "web_browsing": false, "memory": true
|
||||
},
|
||||
"chat": {
|
||||
"stt": true, "tts": true, "file_upload": true,
|
||||
"delete": true, "edit": true, "share": true,
|
||||
"export": true, "call": true, "multiple_models": true, "temporary": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Kids-level (Zoe)
|
||||
```json
|
||||
{
|
||||
"workspace": {
|
||||
"models": false, "knowledge": false, "prompts": false,
|
||||
"tools": false, "skills": false, "documents": false,
|
||||
"web_search": true, "image_generation": false,
|
||||
"code_interpreter": false, "web_browsing": false, "memory": true
|
||||
},
|
||||
"chat": {
|
||||
"stt": true, "tts": true, "file_upload": false,
|
||||
"delete": true, "edit": true, "share": false,
|
||||
"export": false, "call": false, "multiple_models": false, "temporary": false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Critical Gotchas
|
||||
|
||||
1. **Open WebUI persists connection settings in its SQLite DB, NOT in `/root/.env`.** Editing env vars after first launch has no effect. Use the REST API (`/openai/config/update`).
|
||||
|
||||
2. **`OPENAI_API_BASE_URLS` is plural and positional.** Multiple connections live in one ordered list. open1=0, openz=1, openg=2, openj=3. When adding a new connection, pass ALL URLs (not just the new one) to preserve ordering.
|
||||
|
||||
3. **Model `access_grants` field shape is strict.** Use `principal_type` (not `type`), `principal_id` (not `group_id`), `permission` (not `access`). Wrong field names are silently dropped — HTTP 200 with no effect. Always verify with a read-back.
|
||||
|
||||
4. **User creation endpoint is `POST /api/v1/auths/add`**, NOT `/api/v1/users/add` (returns 405).
|
||||
|
||||
5. **List group users is `POST`, not `GET`.** `POST /api/v1/groups/id/{id}/users` returns `list[UserInfoResponse]` (bare array of user objects), not `{user_ids: [...]}`. This bug existed in all three original runbooks and was fixed 2026-07-03.
|
||||
|
||||
6. **Add users to group returns `GroupResponse`**, not `{success: true}`. Response includes `member_count`.
|
||||
|
||||
7. **The `signin` endpoint returns a JWT, not a cookie.** All calls use `Authorization: Bearer <token>`. Token expires — re-signin on 401.
|
||||
|
||||
8. **New connections may take 5-10 seconds to discover models.** If `GET /openai/models/<idx>` returns empty, wait and retry.
|
||||
|
||||
9. **Cross-grants break memory isolation.** Never grant one profile's base model to another persona's group. The preset's `base_model_id` determines which Hermes profile (and thus which memory) handles the chat.
|
||||
|
||||
10. **The `meta.system` field layers ON TOP of Hermes' core system prompt.** Set to empty for adults (Hermes default). Set to guardrail text for kids.
|
||||
|
||||
11. **API server must bind to `0.0.0.0`, not `127.0.0.1`.** The Open WebUI container at 10.0.0.204 cannot reach 127.0.0.1 on the Hermes host.
|
||||
|
||||
12. **Passwords are bcrypt-hashed on creation.** Plaintext in the POST body is fine. Never log it. Share credentials over a secure channel.
|
||||
|
||||
13. **SSH to 10.0.0.204 is NOT available from the Hermes host.** All Open WebUI management must be done via REST API (curl from Hermes host) or browser (Admin Panel). Prerequisite checks use curl, not SSH.
|
||||
|
||||
14. **Preset display names can differ from the runbook plan.** The live API showed "Max" instead of "Hermes-Operator" and "Suzie AI Assistant." instead of "Hermes-Kid". The runbook specifies the intended name; the UI may have been renamed after creation. Verify with live probe, not documentation.
|
||||
|
||||
15. **`access_grants` may return empty from API read-back.** This is normal for admin; access control still works. Don't treat empty grants as a failure.
|
||||
|
||||
16. **`base_model_id` may not be visible in API read-back.** Presets return `(no base)`. The field was set at creation but the read endpoint may not expose it.
|
||||
|
||||
17. **New users see their base model AND their preset, not just the preset alone.** Zoe sees `['Hermes Agent (openz)', 'Suzie AI Assistant.']`; Jennifer sees `['Hermes Agent (openj)', 'Hermes-Family']`. This is correct behavior — the visibility check should confirm the user sees their own base model + preset and does NOT see other groups' models/presets. The verification recipe's "expect: ONLY the new user's preset" is wrong — update it to "expect: the user's base model + preset, and no cross-group leakage."
|
||||
|
||||
18. **Stay scoped to the requested profile only.** When executing Phase 3 for a specific profile, do NOT touch other profiles' gateways, configs, or connections. If a sibling profile's gateway isn't running, note it but do not start it — that's a separate task. The user's instruction to "ONLY setup openj" means exactly that.
|
||||
|
||||
## Reference Runbooks
|
||||
|
||||
All in `/home/n8n/workspace/open1/`:
|
||||
- `openz_runbook_for_zoe.md` — Phase B (kid persona, restricted)
|
||||
- `openg_runbook.md` — Phase C (guest persona, Operators-level)
|
||||
- `openj_runbook_for_jennifer.md` — Phase D (wife persona, Operators-level)
|
||||
- `open_web_ui_plan.md` — Master plan (architecture, voice config, Part 13 admin creds, Part 15 user docs)
|
||||
- `openwebui_build_checklist.md` — Build/verification checklist
|
||||
|
||||
## Verification Recipe (end of any phase)
|
||||
|
||||
```bash
|
||||
TOKEN=<admin-jwt>
|
||||
|
||||
# 1. Connection working
|
||||
curl -s -H "Authorization: Bearer $TOKEN" http://10.0.0.204:8080/openai/models/<idx>
|
||||
|
||||
# 2. All groups present
|
||||
curl -s -H "Authorization: Bearer $TOKEN" http://10.0.0.204:8080/api/v1/groups/ | python3 -c 'import json,sys; print([g["name"] for g in json.load(sys.stdin)])'
|
||||
|
||||
# 3. All users present
|
||||
curl -s -H "Authorization: Bearer $TOKEN" http://10.0.0.204:8080/api/v1/users/all | python3 -c '
|
||||
import json, sys
|
||||
d = json.load(sys.stdin)
|
||||
print([(u["name"], u["role"]) for u in d.get("users", [])])
|
||||
'
|
||||
|
||||
# 4. All presets with correct base models
|
||||
curl -s -H "Authorization: Bearer $TOKEN" http://10.0.0.204:8080/api/v1/models | python3 -c '
|
||||
import json, sys
|
||||
for m in json.load(sys.stdin).get("data", []):
|
||||
print(m["name"], "→", m.get("base_model_id", "(no base)"))
|
||||
'
|
||||
|
||||
# 5. Visibility from new user's perspective
|
||||
NEW_TOKEN=$(curl -s -X POST -H "Content-Type: application/json" \
|
||||
-d '{"email":"<email>","password":"<password>"}' \
|
||||
http://10.0.0.204:8080/api/v1/auths/signin | python3 -c 'import json,sys; print(json.load(sys.stdin)["token"])')
|
||||
curl -s -H "Authorization: Bearer $NEW_TOKEN" http://10.0.0.204:8080/api/v1/models | python3 -c 'import json,sys; print([m["name"] for m in json.load(sys.stdin)["data"]])'
|
||||
# expect: the user's base model + preset (e.g. ['Hermes Agent (openj)', 'Hermes-Family']), and NO cross-group leakage (no Hermes-Operator, Hermes-Kid, etc.)
|
||||
|
||||
# 6. Memory-isolation probe
|
||||
# Sign in as new user → send a retain → sign in as Rob → ask about it → expect "I don't have that"
|
||||
```
|
||||
Reference in New Issue
Block a user