From 745a6767026c9d31676b84426d12ab46fd5d210a Mon Sep 17 00:00:00 2001 From: grenade Date: Tue, 23 Jun 2026 12:22:28 +0300 Subject: [PATCH] hermes: finalize dashboard exposure + local-inference config Confirmed against upstream: dashboard binds 0.0.0.0:9119 by default (HERMES_DASHBOARD_HOST/PORT), so bridge + PublishPort=5100:9119 needs no override. LLM backend uses Hermes' `custom` OpenAI-compatible provider pointed at the local sovereign inference (hanzalova.internal:31313/v1). Co-Authored-By: Claude Opus 4.8 (1M context) Claude-Session: https://claude.ai/code/session_011D3YeWKpjg5bT488fVanCH --- images/hermes/hermes.container | 37 +++++++++++++++------------------- images/hermes/readme.md | 37 ++++++++++++++++++++++------------ 2 files changed, 40 insertions(+), 34 deletions(-) diff --git a/images/hermes/hermes.container b/images/hermes/hermes.container index 7c23c95..c5abb98 100644 --- a/images/hermes/hermes.container +++ b/images/hermes/hermes.container @@ -1,21 +1,21 @@ -# DRAFT reference quadlet for deploying Hermes on bob (bob.hanzalova.internal). +# Reference quadlet for deploying Hermes on bob (bob.hanzalova.internal). # Deploy to /etc/containers/systemd/hermes.container (rootful, matching the # existing agent-zero.container and open-webui.container), then: # sudo install -d -o 10000 -g 10000 /var/lib/hermes # /opt/data owner = HERMES_UID -# # drop config.yaml + .env into /var/lib/hermes (LLM backend, secrets) — see readme.md +# sudo install -o 10000 -g 10000 /path/to/config.yaml /var/lib/hermes/config.yaml +# sudo install -o 10000 -g 10000 /path/to/.env /var/lib/hermes/.env # if needed # sudo systemctl daemon-reload && sudo systemctl start hermes.service # -# Once git.lair.cafe/lair/hermes:latest is published by the `images` workflow, -# this is a normal pull + AutoUpdate=registry quadlet — same lifecycle as the -# other two services on bob. +# Gated on git.lair.cafe/lair/hermes:latest being published by the `images` +# workflow first. After that it's a normal pull + AutoUpdate=registry quadlet — +# same lifecycle as the other two services, and now enrolled in the (enabled) +# podman-auto-update.timer. # -# UNRESOLVED before first deploy (confirm against hermes dashboard docs): -# The dashboard binds 127.0.0.1:9119 by default. To expose it on the LAN at -# :5100 (the agent-zero=5080 / open-webui=5090 convention) the dashboard must -# be told to bind 0.0.0.0 INSIDE the container — set that in -# /var/lib/hermes/config.yaml (or a hermes dashboard-host env) and keep the -# PublishPort below. ⚠ It stores provider API keys and has no auth, so only -# expose on a trusted LAN — consider a reverse proxy with auth for anything wider. +# Dashboard: the image binds the dashboard on 0.0.0.0:9119 by default +# (HERMES_DASHBOARD_HOST / HERMES_DASHBOARD_PORT), so bridge networking + +# PublishPort below exposes it on the LAN at :5100 with no override needed. +# ⚠ The dashboard stores provider API keys and has NO auth — keep it on a trusted +# LAN only; front it with an authenticating reverse proxy for anything wider. [Unit] Description=Hermes Agent @@ -26,20 +26,15 @@ Wants=network-online.target Image=git.lair.cafe/lair/hermes:latest ContainerName=hermes AutoUpdate=registry -# Bridge + PublishPort keeps the 50X0 LAN convention. Requires the dashboard to -# bind 0.0.0.0:9119 inside the container (see note above). If you instead accept -# host networking like upstream's compose, replace the next two lines with -# `Network=host` and configure the dashboard bind/port directly. +# Keeps the 50X0 LAN convention (agent-zero=5080, open-webui=5090, hermes=5100). PublishPort=5100:9119 Volume=/var/lib/hermes:/opt/data:Z # Upstream drops to the non-root hermes user (uid/gid 10000); /var/lib/hermes -# must be owned by 10000:10000 on the host (see install -d above). +# must be owned 10000:10000 on the host (see install -d above). Environment=HERMES_UID=10000 Environment=HERMES_GID=10000 -# LLM backend: point hermes at the local sovereign inference at -# http://hanzalova.internal:31313/v1 (same endpoint open-webui uses). Hermes is -# OpenRouter-first with per-provider base URLs and no plain OpenAI slot, so the -# model routing is configured in /var/lib/hermes/config.yaml, not here. See readme.md. +# LLM backend (local sovereign inference) is configured in +# /var/lib/hermes/config.yaml via provider: "custom" -> see readme.md. [Service] Restart=always diff --git a/images/hermes/readme.md b/images/hermes/readme.md index 2525712..6bf6afb 100644 --- a/images/hermes/readme.md +++ b/images/hermes/readme.md @@ -40,20 +40,31 @@ existing `agent-zero` / `open-webui` services on bob. Summary: 1. `git.lair.cafe/lair/hermes:latest` must be published first (run the `images` workflow). 2. `sudo install -d -o 10000 -g 10000 /var/lib/hermes` -3. Drop `config.yaml` + `.env` into `/var/lib/hermes`: - - **LLM backend → local sovereign inference.** Point hermes at - `http://hanzalova.internal:31313/v1` (the endpoint open-webui already uses). - Hermes is OpenRouter-first with per-provider base URLs and no plain - OpenAI-base slot, so define the OpenAI-compatible provider/model in - `config.yaml` (confirm the exact schema against hermes docs). - - Secrets (any provider keys, tool keys) go in `.env`, not the quadlet. +3. Drop `config.yaml` into `/var/lib/hermes` (owned `10000:10000`) — **LLM backend + → local sovereign inference.** Hermes exposes a `custom` provider for any + OpenAI-compatible endpoint, so point it at the same endpoint open-webui uses: + + ```yaml + # /var/lib/hermes/config.yaml + model: + provider: "custom" # OpenAI-compatible endpoint + base_url: "http://hanzalova.internal:31313/v1" + api_key: "beast" # matches open-webui's OPENAI_API_KEY + default: "" # see: curl http://hanzalova.internal:31313/v1/models + # context_length: 32768 # optional + # max_tokens: 4096 # optional, output ceiling + ``` + + Any other secrets (web-search/tool keys, messaging tokens) go in + `/var/lib/hermes/.env`, never in the quadlet. 4. Install `hermes.container` to `/etc/containers/systemd/`, `daemon-reload`, `start hermes.service`. -### Open item: dashboard LAN exposure +### Dashboard LAN exposure (resolved) -The dashboard defaults to `127.0.0.1:9119` and **stores API keys with no auth**. -The draft quadlet publishes it on the LAN at `:5100` (the 5080/5090 convention), -which requires telling the dashboard to bind `0.0.0.0` inside the container (a -`config.yaml`/env setting to confirm). Only expose on a trusted LAN; front it -with an authenticating reverse proxy for anything wider. +The image binds the dashboard on **`0.0.0.0:9119` by default** +(`HERMES_DASHBOARD_HOST` / `HERMES_DASHBOARD_PORT`), so bridge networking + +`PublishPort=5100:9119` in the quadlet exposes it on the LAN at `:5100` with no +override. ⚠ The dashboard **stores provider API keys and has no auth** — keep it +on a trusted LAN only, and front it with an authenticating reverse proxy for any +wider exposure.