Add Hermes web dashboard at hermes.apps.williamturner.eu
Enabled via HERMES_DASHBOARD=1 (supervised in-container alongside the
gateway, per docs/user-guide/docker.md), bound to 0.0.0.0:9119 so Traefik
can reach it. Two independent auth layers, not one:
1. Traefik basicauth middleware in front of the whole route.
2. Hermes's own basic-auth gate (mandatory once the bind is non-loopback).
Hermes's docs explicitly call basic-auth-alone "not suitable for direct
public-internet exposure" and cite a real June 2026 incident where
internet scanners reached exposed dashboards and drove agents into
planting SSH-key backdoors — hence the extra Traefik-level gate rather
than relying on Hermes's own login page alone.
Also fixes: the htpasswd hash for Traefik's basicauth needs its literal
'$' characters escaped as '2824147' in .env, or docker compose's own variable
interpolation corrupts it (mistook '' for further
references). Also switched the hash from Python's default SHA-512
crypt ('$...') to apr1 ('$...', via openssl passwd -apr1) —
Traefik's basicauth middleware doesn't accept SHA-512-crypt.
Also re-adds Hermes's OPENAI_BASE_URL/OPENAI_API_KEY routing through the
local litellm gateway (instead of OPENROUTER_API_KEY direct) — this was
part of the now-abandoned PR #12 and never actually landed on main.
This commit is contained in:
+30
-1
@@ -77,13 +77,28 @@ services:
|
||||
# rooms (DMs to it would respond unprompted, per Hermes's own default behavior).
|
||||
MATRIX_ALLOWED_USERS: ${MATRIX_HUMAN_USER_ID}
|
||||
MATRIX_REQUIRE_MENTION: "true"
|
||||
OPENROUTER_API_KEY: ${OPENROUTER_API_KEY}
|
||||
# Routed through the local litellm gateway, not OpenRouter directly — one place to
|
||||
# hold the OpenRouter credential and swap models. Does NOT grant Hermes access to
|
||||
# the Claude subscription (Anthropic-side restriction, proven earlier — the
|
||||
# subscription only works through the real `claude` CLI binary, which Hermes isn't).
|
||||
OPENAI_BASE_URL: http://litellm:4000/v1
|
||||
OPENAI_API_KEY: ${LITELLM_MASTER_KEY}
|
||||
# Left disabled: Hermes itself warns that a network-reachable API server combined
|
||||
# with the default unsandboxed ('local') terminal backend gives any caller full
|
||||
# terminal/file access within the container. Matrix is the actual interface in use;
|
||||
# re-enable (API_SERVER_HOST: 0.0.0.0) only alongside terminal.backend: docker if
|
||||
# claude-agent ever needs to call Hermes programmatically.
|
||||
API_SERVER_ENABLED: "false"
|
||||
# Web dashboard, supervised in-container alongside the gateway (same process group,
|
||||
# same s6 tree) — see docs/user-guide/docker.md "Running the dashboard". Binds
|
||||
# 0.0.0.0 so Traefik (a separate container) can reach it; that makes Hermes's own
|
||||
# auth gate mandatory, which it enforces automatically once the bind isn't loopback.
|
||||
HERMES_DASHBOARD: "1"
|
||||
HERMES_DASHBOARD_HOST: 0.0.0.0
|
||||
HERMES_DASHBOARD_PORT: "9119"
|
||||
HERMES_DASHBOARD_BASIC_AUTH_USERNAME: ${HERMES_DASHBOARD_USERNAME}
|
||||
HERMES_DASHBOARD_BASIC_AUTH_PASSWORD: ${HERMES_DASHBOARD_PASSWORD}
|
||||
HERMES_DASHBOARD_BASIC_AUTH_SECRET: ${HERMES_DASHBOARD_SECRET}
|
||||
volumes:
|
||||
- /home/william/hermes-data:/opt/data
|
||||
networks:
|
||||
@@ -92,6 +107,20 @@ services:
|
||||
# immediately exits ("Input is not a terminal") since a detached container has no
|
||||
# stdin — the container then just sits there having done nothing, every restart.
|
||||
command: ["gateway", "run"]
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.routers.hermes-dashboard.rule=Host(`${HERMES_DASHBOARD_HOSTNAME}`)"
|
||||
- "traefik.http.routers.hermes-dashboard.entrypoints=websecure"
|
||||
- "traefik.http.routers.hermes-dashboard.tls.certresolver=letsencrypt"
|
||||
# Second, independent auth layer in front of Hermes's own login page — its docs
|
||||
# explicitly call basic-auth-only "not suitable for direct public-internet
|
||||
# exposure" and cite a real June 2026 incident where scanners reached exposed
|
||||
# dashboards and drove agents into planting SSH-key backdoors. This means an
|
||||
# attacker has to clear Traefik's gate before ever reaching Hermes's own auth,
|
||||
# not just guess one password.
|
||||
- "traefik.http.routers.hermes-dashboard.middlewares=hermes-dashboard-auth"
|
||||
- "traefik.http.middlewares.hermes-dashboard-auth.basicauth.users=${TRAEFIK_HERMES_AUTH_HASH}"
|
||||
- "traefik.http.services.hermes-dashboard.loadbalancer.server.port=9119"
|
||||
|
||||
claude-agent:
|
||||
# Gitea PR-review only now — no Matrix presence (see hermes above; only one agent
|
||||
|
||||
Reference in New Issue
Block a user