Add lair/containers image-build repo; hermes as first image
Some checks failed
images / hermes (push) Has been cancelled
Some checks failed
images / hermes (push) Has been cancelled
Builds container images for lair infra and publishes to git.lair.cafe. Hermes Agent (NousResearch) is built directly from its upstream Dockerfile at the latest release tag, published as git.lair.cafe/lair/hermes; the build is release-triggered (daily API poll) and self-healing (gated on registry presence, not a committable pin). Includes a draft rootful quadlet for bob matching the agent-zero/open-webui convention. Convention follows gongfoo. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011D3YeWKpjg5bT488fVanCH
This commit is contained in:
27
images/hermes/build.sh
Executable file
27
images/hermes/build.sh
Executable file
@@ -0,0 +1,27 @@
|
||||
#!/usr/bin/env bash
|
||||
# Build the Hermes Agent image locally, mirroring what the `images` workflow does.
|
||||
#
|
||||
# Hermes ships its own Dockerfile, so there is no vendored Containerfile here — we
|
||||
# build straight from the upstream git context at a release tag. Override the ref
|
||||
# with HERMES_REF (e.g. v0.2.0); empty resolves the latest upstream release.
|
||||
set -euo pipefail
|
||||
|
||||
REGISTRY="${REGISTRY:-git.lair.cafe}"
|
||||
IMAGE_NAME="${REGISTRY}/lair/hermes"
|
||||
HERMES_REF="${HERMES_REF:-}"
|
||||
|
||||
if [ -z "${HERMES_REF}" ]; then
|
||||
HERMES_REF=$(curl -fsS 'https://api.github.com/repos/NousResearch/hermes-agent/releases/latest' | jq -r '.tag_name // empty')
|
||||
[ -n "${HERMES_REF}" ] || HERMES_REF=$(curl -fsS 'https://api.github.com/repos/NousResearch/hermes-agent/tags' | jq -r '.[0].name // empty')
|
||||
fi
|
||||
[ -n "${HERMES_REF}" ] || { echo "could not resolve an upstream hermes ref"; exit 1; }
|
||||
VERSION="${HERMES_REF#v}"
|
||||
|
||||
echo "building ${IMAGE_NAME}:${VERSION} from NousResearch/hermes-agent#${HERMES_REF}"
|
||||
podman build --pull=newer \
|
||||
-t "${IMAGE_NAME}:${VERSION}" \
|
||||
-t "${IMAGE_NAME}:latest" \
|
||||
"https://github.com/NousResearch/hermes-agent.git#${HERMES_REF}"
|
||||
|
||||
echo "built ${IMAGE_NAME}:${VERSION} and :latest"
|
||||
echo "push with: podman push ${IMAGE_NAME}:${VERSION} && podman push ${IMAGE_NAME}:latest"
|
||||
49
images/hermes/hermes.container
Normal file
49
images/hermes/hermes.container
Normal file
@@ -0,0 +1,49 @@
|
||||
# DRAFT 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 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.
|
||||
#
|
||||
# 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.
|
||||
|
||||
[Unit]
|
||||
Description=Hermes Agent
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
|
||||
[Container]
|
||||
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.
|
||||
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).
|
||||
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.
|
||||
|
||||
[Service]
|
||||
Restart=always
|
||||
TimeoutStartSec=300
|
||||
|
||||
[Install]
|
||||
WantedBy=default.target
|
||||
59
images/hermes/readme.md
Normal file
59
images/hermes/readme.md
Normal file
@@ -0,0 +1,59 @@
|
||||
# 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#<tag> \
|
||||
-t git.lair.cafe/lair/hermes:<version> -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
|
||||
```
|
||||
|
||||
## One image, two roles
|
||||
|
||||
Upstream's compose runs a `gateway` (the agent) and a `dashboard` (web UI on
|
||||
`127.0.0.1:9119`) from the **same image**. Persistent state — `config.yaml`,
|
||||
`.env`, sessions, memory, skills — all lives under `/opt/data` (the single
|
||||
volume). Provider keys and the model backend go in those mounted files, never in
|
||||
the image.
|
||||
|
||||
## Deploying on bob
|
||||
|
||||
See [`hermes.container`](hermes.container) — a rootful quadlet matching the
|
||||
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.
|
||||
4. Install `hermes.container` to `/etc/containers/systemd/`, `daemon-reload`,
|
||||
`start hermes.service`.
|
||||
|
||||
### Open item: dashboard LAN exposure
|
||||
|
||||
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.
|
||||
Reference in New Issue
Block a user