# hermes [NousResearch Hermes Agent](https://github.com/NousResearch/hermes-agent) — a self-improving AI agent — packaged for lair infra and published to `git.lair.cafe/lair/hermes`. ## How it's built Upstream ships its own `Dockerfile` (debian 13 + s6-overlay), so there is **no vendored Containerfile here**. The `images` workflow (and `build.sh`) build straight from the upstream git context at the latest release tag: ``` podman build github.com/NousResearch/hermes-agent.git# \ -t git.lair.cafe/lair/hermes: -t git.lair.cafe/lair/hermes:latest ``` Builds are **release-triggered** (daily poll of the GitHub releases API; a build runs only when that version isn't already in our registry) and **self-healing** (a failed build leaves the version absent, so the next poll retries). Force a rebuild via the workflow's `force` dispatch input, or locally: ``` HERMES_REF=v0.2.0 ./build.sh ``` ## How it runs (single container, gateway + dashboard) The image is an s6-overlay supervisor. The **command selects the mode** — the default (no command) is the *interactive CLI*, which exits without a TTY under systemd and crash-loops. The supported headless setup (per the image's own startup guidance) is **one container running `gateway run` with the dashboard supervised alongside** via `HERMES_DASHBOARD`: - `Exec=gateway run` → the agent daemon (`hermes gateway run --replace`), s6-supervised. - `HERMES_DASHBOARD=1` → the dashboard web UI (binds `0.0.0.0:9119`) in the **same** container — which is what lets it reach the gateway (two bridge-networked containers could not, unlike upstream's host-networked compose). - The image **fails closed** on a non-loopback dashboard bind: it refuses `0.0.0.0` unless OAuth is configured *or* `HERMES_DASHBOARD_INSECURE=1` is set. We expose on the trusted LAN without auth, so we opt in. ⚠ Anyone on the LAN can reach the API-key-storing UI — switch to `HERMES_DASHBOARD_OAUTH_CLIENT_ID` for real auth if that's not acceptable. Persistent state — `config.yaml`, `.env`, sessions, memory, skills — all lives under `/opt/data` (the single `:Z` volume). Keys/backend go in those mounted files, never in the image or quadlet. ## Deploying on bob See [`hermes.container`](hermes.container) — a rootful quadlet matching the existing `agent-zero` / `open-webui` services. As deployed: 1. Publish `git.lair.cafe/lair/hermes:latest` (the `images` workflow). 2. `sudo install -d -o 10000 -g 10000 /var/lib/hermes` 3. Drop `config.yaml` into `/var/lib/hermes` (owned `10000:10000`) — **LLM backend → local sovereign inference** via Hermes' `custom` OpenAI-compatible provider: ```yaml # /var/lib/hermes/config.yaml model: provider: "custom" base_url: "http://hanzalova.internal:31313/v1" # same endpoint open-webui uses api_key: "" default: "" # see: curl …/v1/models # context_length / max_tokens optional ``` Other secrets (web-search/tool keys, messaging tokens) go in `/var/lib/hermes/.env`. 4. Install `hermes.container` to `/etc/containers/systemd/`, `daemon-reload`, `start hermes.service`. Dashboard then serves on the LAN at `http://bob.hanzalova.internal:5100/`; `AutoUpdate=registry` enrolls it in the host's `podman-auto-update.timer`.