diff --git a/CHANGELOG.md b/CHANGELOG.md index 82c9e68c4..b21724977 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,12 @@ ## [Unreleased] +## [v0.51.517] — 2026-06-19 — Release SB (multi-container gateway URL config) + +### Fixed + +- **Cron jobs and gateway-backed features are reachable again in multi-container deployments (#4483).** The two- and three-container compose files now set `HERMES_API_URL=http://hermes-agent:8642` on the `hermes-webui` service, which `api/agent_health.py` consumes to locate the gateway — without it, a multi-container setup showed a spurious "gateway not configured" banner and couldn't reach cron jobs. The single-container default is unchanged (the var is unset there, falling back to local checks), and `docs/docker.md`'s stale `hermes:8642` default is corrected to `hermes-agent:8642`. Thanks @franksong2702. + ## [v0.51.516] — 2026-06-19 — Release SA (stage per-session toolsets before the first message) ### Fixed diff --git a/docker-compose.three-container.yml b/docker-compose.three-container.yml index 4f9ac75dd..7e967624f 100644 --- a/docker-compose.three-container.yml +++ b/docker-compose.three-container.yml @@ -122,6 +122,10 @@ services: - HERMES_WEBUI_HOST=0.0.0.0 - HERMES_WEBUI_PORT=8787 - HERMES_WEBUI_STATE_DIR=/home/hermeswebui/.hermes/webui + # Let the WebUI health/status checks reach the gateway container over + # the compose network. Cron listing is local, but scheduled ticking and + # the Tasks/System gateway pill need a reachable gateway URL (#4483). + - HERMES_API_URL=http://hermes-agent:8642 # Match your host user's UID/GID for correct file permissions. # Run `id -u` and `id -g` to find your values. # On macOS, UIDs start at 501 (not 1000) — set these in a .env file: diff --git a/docker-compose.two-container.yml b/docker-compose.two-container.yml index f5237427e..0eb911859 100644 --- a/docker-compose.two-container.yml +++ b/docker-compose.two-container.yml @@ -105,6 +105,10 @@ services: - HERMES_WEBUI_HOST=0.0.0.0 - HERMES_WEBUI_PORT=8787 - HERMES_WEBUI_STATE_DIR=/home/hermeswebui/.hermes/webui + # Let the WebUI health/status checks reach the gateway container over + # the compose network. Cron listing is local, but scheduled ticking and + # the Tasks/System gateway pill need a reachable gateway URL (#4483). + - HERMES_API_URL=http://hermes-agent:8642 # Match your host user's UID/GID for correct file permissions. # In two-container setups the WebUI auto-detects UID/GID from the shared # hermes-home volume, but you can override explicitly if needed (#668): diff --git a/docs/docker.md b/docs/docker.md index 69a6cfa86..b5c7d45a5 100644 --- a/docs/docker.md +++ b/docs/docker.md @@ -158,7 +158,12 @@ provide host kernel drivers or the NVIDIA runtime. **Cause**: Scheduled cron ticks are not driven by the WebUI itself. The gateway daemon ticks the scheduler every 60 seconds; without one running, scheduled jobs sit idle. "Run now" / "Trigger" buttons still work because the WebUI handles those in-process. -In older gateway builds, or when the daemon runs in a separate container, `gateway_state.json` can become stale and WebUI may lose confidence even if the daemon is up. This is especially visible if only base URLs are configured (e.g. `HERMES_WEBUI_GATEWAY_BASE_URL`) and local daemon state files are not being refreshed. +The cron list itself is still read from the shared `HERMES_HOME` volume, not +from the gateway HTTP API. If the Tasks panel shows a gateway warning while the +job list loads, the warning is about scheduled ticking / gateway health, not +about the list endpoint. + +In older gateway builds, or when the daemon runs in a separate container, `gateway_state.json` can become stale and WebUI may lose confidence even if the daemon is up. This is especially visible if the WebUI container has no gateway URL and can only inspect local state files from its own container. **Fix**: Run a gateway container alongside the WebUI. The two-container compose file is the recommended path: @@ -169,18 +174,35 @@ docker compose -f docker-compose.two-container.yml up -d The three-container layout adds the dashboard but is otherwise the same shape. If you must stay single-container, you can run `hermes gateway` inside the container as a long-lived background process, but the compose split is sturdier. +If you maintain a custom compose file, make sure the **WebUI service** points at +the gateway service over the compose network: + +```yaml +services: + hermes-webui: + environment: + - HERMES_API_URL=http://hermes-agent:8642 + # HERMES_WEBUI_GATEWAY_BASE_URL=http://hermes-agent:8642 also works. +``` + +Do not copy only `API_SERVER_ENABLED=true` / `API_SERVER_HOST=0.0.0.0` into the +agent service as a standalone fix. If you intentionally enable the agent API +server, the agent also requires a real `API_SERVER_KEY` (at least 8 characters), +and the WebUI still needs `HERMES_API_URL` or `HERMES_WEBUI_GATEWAY_BASE_URL` to +reach that service from its container. + **Verify**: Once the gateway is up, the System Settings pill should turn green and the Tasks banner disappear. From the host: ```bash -export GATEWAY_BASE_URL="${HERMES_API_URL:-${HERMES_WEBUI_GATEWAY_BASE_URL:-http://hermes:8642}}" +export GATEWAY_BASE_URL="${HERMES_API_URL:-${HERMES_WEBUI_GATEWAY_BASE_URL:-http://hermes-agent:8642}}" docker compose -f docker-compose.two-container.yml exec hermes-agent hermes gateway status curl -sS "${GATEWAY_BASE_URL%/}/health/detailed" | jq '.gateway_state, .state' ``` If the service name differs in your compose file, `docker compose -f docker-compose.two-container.yml ps` lists the running services. -For container-to-container diagnostics, set one of `HERMES_API_URL` or `HERMES_WEBUI_GATEWAY_BASE_URL` in the WebUI environment when using gateway chat mode (`HERMES_WEBUI_CHAT_BACKEND=gateway`), then restart WebUI. +For container-to-container diagnostics, set one of `HERMES_API_URL` or `HERMES_WEBUI_GATEWAY_BASE_URL` in the WebUI environment, then restart WebUI. -Refs #2785. +Refs #2785, #4483. ## Three-service unified setup (v0.14+) diff --git a/tests/test_v050260_docker_invariants.py b/tests/test_v050260_docker_invariants.py index 3b06575a6..c800b930f 100644 --- a/tests/test_v050260_docker_invariants.py +++ b/tests/test_v050260_docker_invariants.py @@ -194,6 +194,23 @@ def test_compose_files_parse_as_valid_yaml(): assert "services" in data, f"{fname} must define a `services:` block" +def test_multi_container_webui_points_at_gateway_service(): + """REGRESSION (#4483): the multi-container WebUI service needs a compose + network URL for the gateway container. Cron listing reads shared state, but + scheduled ticking and the Tasks/System gateway health pill need a reachable + gateway base URL from inside the WebUI container.""" + import yaml + + for fname in ("docker-compose.two-container.yml", "docker-compose.three-container.yml"): + path = REPO / fname + data = yaml.safe_load(path.read_text(encoding="utf-8")) + env = data["services"]["hermes-webui"]["environment"] + assert "HERMES_API_URL=http://hermes-agent:8642" in env, ( + f"{fname}: hermes-webui must point at hermes-agent over the compose " + "network so gateway health and scheduled ticking work in multi-container setups." + ) + + # ── 7: agent vs webui HERMES_HOME_MODE semantic asymmetry ──────────────────