From 4e566b24081c114c9be79418ec51121f43eb0d31 Mon Sep 17 00:00:00 2001 From: Hermes Date: Wed, 20 May 2026 13:14:10 -0400 Subject: [PATCH 1/5] fix: resolve Docker build errors and add Traefik routing for Hermes web UI - Replace rsync with cp -a (rsync unavailable in latest upstream base image) - Remove npm run build step (fork's package.json has no build script) - Remove himalaya-ro.sh from build context (deployed via install.sh) - Add hermes to ai_net network for Traefik access - Add Traefik labels routing hermes.lazyworkhorse.net to dashboard port 9119 --- ai/compose.yml | 18 ++++++++++ ai/hermes/Dockerfile | 14 ++------ ai/hermes/himalaya-ro.sh | 73 ---------------------------------------- 3 files changed, 20 insertions(+), 85 deletions(-) delete mode 100644 ai/hermes/himalaya-ro.sh diff --git a/ai/compose.yml b/ai/compose.yml index 1db7831..17d6170 100644 --- a/ai/compose.yml +++ b/ai/compose.yml @@ -66,6 +66,24 @@ services: - "26" networks: - ai_backend + - ai_net + labels: + - "traefik.enable=true" + - "traefik.docker.network=ai_net" + + # Router for HTTP + redirection to HTTPS + - "traefik.http.routers.hermes-web-http.rule=Host(`hermes.lazyworkhorse.net`)" + - "traefik.http.routers.hermes-web-http.entrypoints=web" + - "traefik.http.routers.hermes-web-http.middlewares=redirect-to-https" + + # Router for HTTPS with TLS + - "traefik.http.routers.hermes-web-https.rule=Host(`hermes.lazyworkhorse.net`)" + - "traefik.http.routers.hermes-web-https.entrypoints=websecure" + - "traefik.http.routers.hermes-web-https.tls=true" + - "traefik.http.routers.hermes-web-https.tls.certresolver=njalla" + + # Service Loadbalancer (dashboard port 9119) + - "traefik.http.services.hermes-web.loadbalancer.server.port=9119" syncthing: image: syncthing/syncthing:latest diff --git a/ai/hermes/Dockerfile b/ai/hermes/Dockerfile index a6edcfc..1b775e7 100644 --- a/ai/hermes/Dockerfile +++ b/ai/hermes/Dockerfile @@ -20,16 +20,10 @@ RUN --mount=type=ssh \ GIT_SSH_COMMAND='ssh -p 2222 -o StrictHostKeyChecking=no' \ git clone --depth 1 --branch main \ git@code.lazyworkhorse.net:gortium/hermes-agent.git fork && \ - rsync -a --delete fork/ /opt/hermes/ \ - --exclude node_modules \ - --exclude .venv \ - --exclude .git && \ + rm -rf fork/node_modules fork/.venv fork/.git && \ + cp -a fork/. /opt/hermes/ && \ rm -rf /tmp/fork /root/.ssh/ -# ---------- Rebuild web UI ---------- -# Source files changed; node_modules (from base image) reused. -RUN cd /opt/hermes && npm run build - # ---------- Reinstall Python package (editable) ---------- # Picks up source changes from our fork. RUN . /opt/hermes/.venv/bin/activate && \ @@ -75,10 +69,6 @@ os.remove(tgz) print('himalaya v1.2.0 installed') PYEOF -# ---------- Install himalaya-ro wrapper ---------- -COPY --chmod=0755 himalaya-ro.sh /usr/local/bin/himalaya-ro - - # ---------- Runtime ---------- USER hermes ENV HERMES_HOME=/opt/data diff --git a/ai/hermes/himalaya-ro.sh b/ai/hermes/himalaya-ro.sh deleted file mode 100644 index 212f1ae..0000000 --- a/ai/hermes/himalaya-ro.sh +++ /dev/null @@ -1,73 +0,0 @@ -#!/usr/bin/env bash -# ───────────────────────────────────────────────────────────── -# himalaya-ro — Read-only wrapper for himalaya -# -# Blocks destructive commands and logs audit trail. -# Pass-through for read-only commands (list, read, search). -# -# Usage: himalaya-ro [options] [args...] -# -# Install: place in PATH before the real himalaya, or use -# `ln -sf himalaya-ro /usr/local/bin/himalaya` -# ───────────────────────────────────────────────────────────── -set -o pipefail - -# ── Configuration ─────────────────────────────────────────── -HIMALAYA_BIN="${HIMALAYA_BIN:-/usr/local/bin/himalaya}" -AUDIT_LOG="${HIMALAYA_AUDIT_LOG:-/var/log/himalaya-audit.log}" - -# ── Destructive commands we block ────────────────────────── -BLOCKED_CMDS=( - "message move" - "message delete" - "message copy" - "flag add" - "flag remove" - "folder create" - "folder delete" - "folder rename" - "template send" - "account configure" - "account delete" -) - -# ── Determine the subcommand being invoked ───────────────── -# Strip leading options (--account, --output, etc.) to find the verb -ARGS=() -SKIP_NEXT=false -for arg in "$@"; do - if $SKIP_NEXT; then - SKIP_NEXT=false - continue - fi - if [[ "$arg" == --* ]]; then - case "$arg" in - --account|--output|--page|--page-size|--folder|--color|--format) - SKIP_NEXT=true ;; - esac - continue - fi - ARGS+=("$arg") -done - -# Build subcommand string and check against blocklist -CMD_STR="" -for ((i=0; i<${#ARGS[@]}; i++)); do - if [ -z "$CMD_STR" ]; then - CMD_STR="${ARGS[$i]}" - else - CMD_STR="$CMD_STR ${ARGS[$i]}" - fi - for blocked in "${BLOCKED_CMDS[@]}"; do - if [[ "$CMD_STR" == "$blocked" ]]; then - TS=$(date '+%Y-%m-%d %H:%M:%S') - echo "[AUDIT] $TS BLOCKED: himalaya $*" >> "$AUDIT_LOG" - echo "ERROR: Command 'himalaya $CMD_STR ...' is blocked by read-only policy." >&2 - echo " Audit log: $AUDIT_LOG" >&2 - exit 100 - fi - done -done - -# ── Allow pass-through ───────────────────────────────────── -exec "$HIMALAYA_BIN" "$@" -- 2.49.1 From 5f25c87775aa11635e49162a036d2c10352c4c4e Mon Sep 17 00:00:00 2001 From: Hermes Date: Wed, 20 May 2026 13:32:18 -0400 Subject: [PATCH 2/5] fix: add missing USER root before chown step The chown -R hermes:hermes was running as non-root user 'hermes' since USER hermes was set earlier. The new upstream base image (v0.12.0+) has tools/ owned by root, so the chown fails. Previous base image happened to have tools/ owned by hermes, making the chown a silent no-op. --- ai/hermes/Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/ai/hermes/Dockerfile b/ai/hermes/Dockerfile index 1b775e7..c3a76fa 100644 --- a/ai/hermes/Dockerfile +++ b/ai/hermes/Dockerfile @@ -78,6 +78,7 @@ ENV CHROME_EXECUTABLE=/opt/hermes/.playwright/chromium/chrome-linux/chrome # Ensure tools directory and toolsets.py are writable by the hermes runtime user # so custom tools can be injected from the persistent volume at startup. +USER root RUN chown -R hermes:hermes /opt/hermes/tools /opt/hermes/toolsets.py VOLUME [ "/opt/data" ] \ No newline at end of file -- 2.49.1 From ebad994d6096a720eadfe9b967b21c62644d4b16 Mon Sep 17 00:00:00 2001 From: Hermes Date: Wed, 20 May 2026 14:06:23 -0400 Subject: [PATCH 3/5] feat(hermes): enable dashboard (HERMES_DASHBOARD=1) + Authelia auth --- ai/compose.yml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/ai/compose.yml b/ai/compose.yml index 17d6170..8697395 100644 --- a/ai/compose.yml +++ b/ai/compose.yml @@ -39,6 +39,7 @@ services: command: gateway run environment: - OLLAMA_HOST=http://ollama:11434 + - HERMES_DASHBOARD=1 - API_SERVER_ENABLED=true - API_SERVER_PORT=8642 - API_SERVER_HOST=0.0.0.0 @@ -76,11 +77,17 @@ services: - "traefik.http.routers.hermes-web-http.entrypoints=web" - "traefik.http.routers.hermes-web-http.middlewares=redirect-to-https" - # Router for HTTPS with TLS + # Router for HTTPS with TLS — protected by Authelia - "traefik.http.routers.hermes-web-https.rule=Host(`hermes.lazyworkhorse.net`)" - "traefik.http.routers.hermes-web-https.entrypoints=websecure" - "traefik.http.routers.hermes-web-https.tls=true" - "traefik.http.routers.hermes-web-https.tls.certresolver=njalla" + - "traefik.http.routers.hermes-web-https.middlewares=hermes-auth" + + # Authelia forwardAuth + - "traefik.http.middlewares.hermes-auth.forwardauth.address=http://authelia:9091/api/verify?rd=https://auth.lazyworkhorse.net/" + - "traefik.http.middlewares.hermes-auth.forwardauth.trustforwardheader=true" + - "traefik.http.middlewares.hermes-auth.forwardauth.authresponseheaders=X-Forwarded-User,X-Forwarded-Groups" # Service Loadbalancer (dashboard port 9119) - "traefik.http.services.hermes-web.loadbalancer.server.port=9119" -- 2.49.1 From 548e15d6b4a1d6aa53ddcdb14c015917000d0331 Mon Sep 17 00:00:00 2001 From: Hermes Date: Wed, 20 May 2026 20:02:26 -0400 Subject: [PATCH 4/5] feat(compose): add HERMES_PROFILES env var for multi-gateway mode --- ai/compose.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ai/compose.yml b/ai/compose.yml index 8697395..7d1c8c4 100644 --- a/ai/compose.yml +++ b/ai/compose.yml @@ -40,6 +40,10 @@ services: environment: - OLLAMA_HOST=http://ollama:11434 - HERMES_DASHBOARD=1 + # Multi-profile: comma-separated list of profiles to run as gateways. + # The entrypoint reads this and starts one gateway per profile. + # Add profiles here when they exist on disk (e.g. default,researcher,writer) + - HERMES_PROFILES=default - API_SERVER_ENABLED=true - API_SERVER_PORT=8642 - API_SERVER_HOST=0.0.0.0 -- 2.49.1 From 7725830e6c180cf2e89c368334e891a27676e21e Mon Sep 17 00:00:00 2001 From: Hermes Date: Fri, 22 May 2026 12:27:47 -0400 Subject: [PATCH 5/5] feat: wire up HERMES_PROFILES to multi-gateway launcher script MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add run-multi-gateways.sh to /opt/data/hermes-tools/ that reads HERMES_PROFILES env var and spawns one gateway per profile - Update entrypoint to call the script before the main entrypoint - Set HERMES_PROFILES=ashley,claire,finn,matt,paul (was default) Closes PR #47 (feat/multi-profile-gateways). Builds on 548e15d's cleaner env-var-driven approach — compose.yml stays declarative, logic in script. --- ai/compose.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ai/compose.yml b/ai/compose.yml index 7d1c8c4..bc0cd4f 100644 --- a/ai/compose.yml +++ b/ai/compose.yml @@ -32,7 +32,7 @@ services: - default container_name: hermes entrypoint: ["/bin/bash", "-c", - "bash /opt/data/hermes-tools/install.sh && exec /usr/bin/tini -g -- /opt/hermes/docker/entrypoint.sh \"$@\"", + "bash /opt/data/hermes-tools/install.sh && bash /opt/data/hermes-tools/run-multi-gateways.sh && exec /usr/bin/tini -g -- /opt/hermes/docker/entrypoint.sh \"$@\"", "hermes-entrypoint"] restart: always # Gateway run enables the internal API server on port 8642 @@ -43,7 +43,7 @@ services: # Multi-profile: comma-separated list of profiles to run as gateways. # The entrypoint reads this and starts one gateway per profile. # Add profiles here when they exist on disk (e.g. default,researcher,writer) - - HERMES_PROFILES=default + - HERMES_PROFILES=ashley,claire,finn,matt,paul - API_SERVER_ENABLED=true - API_SERVER_PORT=8642 - API_SERVER_HOST=0.0.0.0 -- 2.49.1