mirror of
https://gitverse.ru/kpa39l/hermes-webui-docker.git
synced 2026-09-29 09:15:10 +00:00
Initial commit: Hermes skill hermes-webui-docker
This commit is contained in:
@@ -0,0 +1,60 @@
|
||||
# Dashboard Systemd Service — Hermes WebUI Hybrid Deployment
|
||||
|
||||
## Full Unit File Template
|
||||
|
||||
Location: `/etc/systemd/system/hermes-dashboard.service`
|
||||
|
||||
```ini
|
||||
[Unit]
|
||||
Description=Hermes Agent Dashboard - Web Management UI
|
||||
After=network-online.target hermes-gateway.service
|
||||
Wants=network-online.target
|
||||
StartLimitIntervalSec=0
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=estorozhenko
|
||||
Group=estorozhenko
|
||||
ExecStart=/home/estorozhenko/.hermes/hermes-agent/venv/bin/python -m hermes_cli.main dashboard --host 127.0.0.1
|
||||
WorkingDirectory=/home/estorozhenko/.hermes/hermes-agent
|
||||
Environment="HOME=/home/estorozhenko"
|
||||
Environment="USER=estorozhenko"
|
||||
Environment="LOGNAME=estorozhenko"
|
||||
Environment="PATH=/home/estorozhenko/.hermes/hermes-agent/venv/bin:/home/estorozhenko/.hermes/hermes-agent/node_modules/.bin:/home/estorozhenko/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
|
||||
Environment="VIRTUAL_ENV=/home/estorozhenko/.hermes/hermes-agent/venv"
|
||||
Environment="HERMES_HOME=/home/estorozhenko/.hermes"
|
||||
Environment="GATEWAY_HEALTH_URL=http://localhost:8642"
|
||||
Restart=always
|
||||
RestartSec=5
|
||||
RestartMaxDelaySec=300
|
||||
RestartSteps=5
|
||||
KillMode=mixed
|
||||
KillSignal=SIGTERM
|
||||
TimeoutStopSec=30
|
||||
StandardOutput=journal
|
||||
StandardError=journal
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
```
|
||||
|
||||
## Pitfalls
|
||||
|
||||
- **`--host 0.0.0.0` blocked** — as of June 2026 hardening, Hermes dashboard refuses to bind on a non-loopback interface without a configured auth provider. Error message:
|
||||
```
|
||||
Refusing to bind dashboard to 0.0.0.0 — the auth gate engages on non-loopback binds,
|
||||
but no auth providers are registered.
|
||||
Configure an auth provider before exposing the dashboard:
|
||||
• Password: set dashboard.basic_auth.username + password_hash in config.yaml
|
||||
• OAuth: run `hermes dashboard register` (Nous Portal)
|
||||
There is no unauthenticated public-bind option — to keep it local, bind 127.0.0.1
|
||||
and tunnel in (SSH / Tailscale).
|
||||
```
|
||||
|
||||
- **Solution:** bind `127.0.0.1` and access via SSH tunnel:
|
||||
```bash
|
||||
ssh -L 9119:localhost:9119 user@bigbox # from client machine
|
||||
```
|
||||
Or via WireGuard: if the client IP can reach the host's loopback is impossible — bind on the WireGuard interface IP instead.
|
||||
|
||||
- **No need for `--insecure` flag** — it's deprecated and ignored as of June 2026.
|
||||
@@ -0,0 +1,116 @@
|
||||
# Host Gateway API Server Configuration — Hermes WebUI Hybrid
|
||||
|
||||
## The Problem
|
||||
|
||||
WebUI shows `"Check HERMES_WEBUI_GATEWAY_BASE_URL and Gateway API server health"`.
|
||||
Gateway systemd service shows `active (running)` but port 8642 isn't listening.
|
||||
|
||||
## Root Cause
|
||||
|
||||
`api_server` is a **gateway platform** (enum `Platform.API_SERVER = "api_server"`), not a standalone service. It activates only when configured — either via `config.yaml` or env vars. On a host-native (systemd) setup, env vars from the Docker `.env` file are NOT propagated.
|
||||
|
||||
## Fix — Two Options
|
||||
|
||||
### Option A: config.yaml (Recommended)
|
||||
|
||||
```yaml
|
||||
# At ROOT level of /home/estorozhenko/.hermes/config.yaml
|
||||
platforms:
|
||||
api_server:
|
||||
enabled: true
|
||||
host: 0.0.0.0
|
||||
port: 8642
|
||||
key: "7c8a2d9f4e1b3a5c6d7e8f9a0b1c2d3e4f5a6b7c"
|
||||
```
|
||||
|
||||
⚠️ **Field name note:** The field must be `key` (not `api_key`). The adapter reads `extra.get("key", os.getenv("API_SERVER_KEY", ""))`. A field named `api_key` in the YAML is silently ignored because the bridge only moves `key` into the `extra` dict.
|
||||
|
||||
### Option B: systemd unit env vars
|
||||
|
||||
```ini
|
||||
Environment="API_SERVER_ENABLED=true"
|
||||
Environment="API_SERVER_HOST=0.0.0.0"
|
||||
Environment="API_SERVER_KEY=7c8a2d9f4e1b3a5c6d7e8f9a0b1c2d3e4f5a6b7c"
|
||||
```
|
||||
|
||||
Then daemon-reload + restart.
|
||||
|
||||
## How the Code Works
|
||||
|
||||
From `gateway/config.py`:
|
||||
|
||||
1. **`_load_env_overrides`** (line ~2128): reads `API_SERVER_ENABLED`, `API_SERVER_KEY`, `API_SERVER_HOST`, `API_SERVER_PORT`, `API_SERVER_CORS_ORIGINS` from env
|
||||
2. **Platform connected checker** (line ~846): requires `key` in `extra` with `min_length=16`
|
||||
3. **YAML bridging** (line ~1471): moves `port`, `key`, `host`, `cors_origins`, `model_name` from the platform dict into `extra` so PlatformConfig preserves them
|
||||
4. **`_has_usable_api_server_key`** (line ~822): checks key is a string ≥ 16 chars
|
||||
|
||||
Without a valid key (≥16 chars), the platform won't be marked connected and the adapter won't start — no listener on 8642, no error message.
|
||||
|
||||
## Key Debugging Lesson: Multi-Source Divergence
|
||||
|
||||
The API server key lives in **four independent sources** that must all agree:
|
||||
|
||||
| Source | Where | How it's set |
|
||||
|--------|-------|-------------|
|
||||
| `config.yaml` | `platforms.api_server.key` | Edited directly |
|
||||
| `~/.hermes/.env` | `API_SERVER_KEY=...` | `hermes config set gateway.api_server_key ...` or manual edit |
|
||||
| systemd unit | `Environment="API_SERVER_KEY=..."` | `hermes gateway install --force` or manual edit |
|
||||
| docker `.env` for webui | `API_SERVER_KEY=...` | Docker compose env-file |
|
||||
|
||||
**Common failure modes:**
|
||||
|
||||
- **config.yaml says one thing, `.env` says another** → `.env` wins (`os.environ` is overwritten at startup), so even though systemd's `Environment=` and config.yaml agree, the process env is different. This is the most frequent root cause of "401 with correct key".
|
||||
- **systemd shows key A, `.env` shows key B** → process starts with systemd's env, but then load_gateway_config reads `.env` and calls `os.environ["API_SERVER_KEY"] = B`, overwriting A.
|
||||
|
||||
**To fix:** pick ONE canonical value and set it identically in all four sources. The simplest approach: pick a single key string (e.g. 40 hex chars), put it in config.yaml's `key:` field, and ensure `~/.hermes/.env`, the systemd unit, and docker `.env` all match.
|
||||
|
||||
### How to Verify All Sources
|
||||
|
||||
```bash
|
||||
# 1. Config YAML
|
||||
grep -A1 'key:' ~/.hermes/config.yaml | grep -v extra
|
||||
|
||||
# 2. User .env (read_file blocked — use terminal/xxd)
|
||||
grep API_SERVER_KEY ~/.hermes/.env
|
||||
|
||||
# 3. Systemd service
|
||||
grep API_SERVER_KEY /etc/systemd/system/hermes-gateway.service
|
||||
|
||||
# 4. Docker compose .env
|
||||
grep API_SERVER_KEY /opt/hermes/docker/.env
|
||||
|
||||
# 5. Live process env (verify the actual value the gateway uses)
|
||||
SYSTEMD_PID=$(systemctl show hermes-gateway.service -p MainPID | grep -oP '\d+')
|
||||
cat /proc/$SYSTEMD_PID/environ | tr '\0' '\n' | grep API_SERVER_KEY
|
||||
```
|
||||
|
||||
All five should show the identical value. If step 5 (live process) differs from 1-4, then something is overwriting the key at runtime — likely step 2.
|
||||
|
||||
## Verification
|
||||
|
||||
```bash
|
||||
# Confirm listener
|
||||
ss -tlnp | grep 8642
|
||||
|
||||
# Health check
|
||||
curl -s http://localhost:8642/health
|
||||
# → {"status": "ok", "platform": "hermes-agent", "version": "0.19.0"}
|
||||
|
||||
# From inside webui container
|
||||
docker exec hermes-webui curl -s http://host.docker.internal:8642/health
|
||||
|
||||
# Check gateway logs for platform startup
|
||||
journalctl -u hermes-gateway.service --no-pager -n 10 | grep -i api
|
||||
```
|
||||
|
||||
## Env Vars Expected by WebUI
|
||||
|
||||
In `docker-compose.yml` the webui expects:
|
||||
|
||||
```yaml
|
||||
- HERMES_WEBUI_GATEWAY_API_KEY=${API_SERVER_KEY:-}
|
||||
- HERMES_API_URL=http://host.docker.internal:8642
|
||||
- HERMES_WEBUI_GATEWAY_BASE_URL=http://host.docker.internal:8642
|
||||
```
|
||||
|
||||
The `API_SERVER_KEY` env var must be exported or set in the `.env` file that docker-compose reads.
|
||||
Reference in New Issue
Block a user