Scheduled cron jobs created in the Tasks panel never tick on a
single-container Docker install because the WebUI doesn't run the
gateway daemon itself. The maintainer's analysis on #2785 spells this
out: the gateway ticks the scheduler every 60s, and without it
'Gateway not configured' just sits there.
The Tasks panel already shows a banner explaining this, but doesn't
give the user anywhere to go. Two small docs-shaped changes:
1. Add a 'Scheduled jobs require a gateway daemon' section to
docs/docker.md under 'What goes wrong' with the two-container
compose command and a verify step. Cross-linked from the existing
short paragraph higher up so both entry points land on the same
fix.
2. Append a 'How to enable scheduled jobs in Docker' link to the
cron panel banner (loadCronGatewayNotice) pointing at the new
docs anchor when the gateway is unconfigured. The banner text
itself is unchanged.
Verified locally by serving the WebUI without a gateway, opening
Tasks, and confirming the banner now shows the new link; clicked it
and confirmed it lands on the new docs section. With the gateway
running the banner stays hidden as before.
Refs #2785
The hermes-agent-src named volume in the two- and three-container compose
files is initialised from the agent image's /opt/hermes on first `up` and
Docker reuses it verbatim on every subsequent `up` — even after a fresh
`docker pull` of the agent image. This was the root cause of #1416 (the
'missing entrypoint' symptom was a stale cached volume hiding the new
image's source tree).
Changes:
- Add an 'Upgrading the agent container' section to docs/docker.md with
the canonical `down → docker volume rm → pull → up -d` recipe, plus the
same pointer as a comment block in both multi-container compose files
near the volume declarations.
- Switch the WebUI's hermes-agent-src mount to `:ro` in both multi-container
compose files. The WebUI only reads this volume to install the agent's
Python deps at startup; mounting it read-only enforces that at the kernel
layer and brings the actual mount mode in line with the existing
docs/docker.md architecture diagram (which already labelled this edge as
read-only).
- Align the workspace bind default in both multi-container compose files
with the single-container convention — `${HERMES_WORKSPACE:-${HOME}/workspace}`
instead of `${HERMES_WORKSPACE:-~/workspace}` — so the default resolves
the same way across Linux, macOS, WSL2, and Docker Desktop on Windows.
- Add a 'What the multi-container setup isolates (and what it doesn't)'
section to docs/docker.md to frame the two/three-container setups as
process/network/resource isolation, not filesystem isolation, so users
don't reach for multi-container expecting a trust boundary it doesn't
provide.
- Cross-link #1416 from the Related issues section.
Adds 9 regression tests in tests/test_docker_docs_and_readonly.py covering:
- :ro on the WebUI side of hermes-agent-src in both files
- agent side stays read-write (still needs to populate /opt/hermes on first run)
- ${HOME} (not ~) in workspace bind defaults in both files
- single-container file already uses ${HOME} (pin to prevent drift)
- docs/docker.md has the 'Upgrading the agent container' section + recipe
- compose files reference docs/docker.md + show the upgrade step inline
- docs/docker.md frames the isolation model honestly
Test suite: 42 passed (33 existing Docker tests + 9 new). No behaviour
change for users who set HERMES_WORKSPACE explicitly, and no migration is
required for existing deployments — Docker rebinds the existing volume
read-only on next `up`. Users upgrading the agent image should now follow
the documented `docker volume rm hermes-agent-src` recipe.
Closes#1416 (documented upgrade procedure) and addresses the read-only
half of the multi-container coupling concern raised on #2453.
Combines PR #1428 (UID/GID alignment) with a broader Docker reliability pass
that addresses recurring user reports about compose files not working.
Constituent PR:
- #1428 sunnysktsang - Align agent UID/GID with webui (fixes#1399).
Two- and three-container compose files had agent at UID 10000 (image
default) and webui at UID 1000 (WANTED_UID default), causing permission
denied on shared hermes-home volume. All services now use ${UID:-1000}.
Plus broader Docker UX overhaul:
- All 3 compose files document HERMES_SKIP_CHMOD/HERMES_HOME_MODE escape
hatches inline (the v0.50.254 fix wasn't surfaced for Docker users).
- New .env.docker.example template covering UID/GID, paths, password,
permission handling. UID/GID are uncommented with placeholder values
per Opus advisor (so macOS users don't skim past).
- New docs/docker.md - comprehensive guide: 5-min quickstart, failure
mode table with one-line fixes, bind-mount migration, multi-container
architecture diagram, macOS Docker Desktop VirtioFS note, link to
community sunnysktsang/hermes-suite all-in-one image.
- README Docker section rewritten - clearer quickstart, failure-mode
table, link to docs/docker.md. Stale /root/.hermes references removed.
Plus Opus pre-release advisor MUST-FIX:
- HERMES_HOME_MODE has DIFFERENT semantics in the WebUI vs the agent
image. WebUI: credential-file mode threshold (0640 allows group bits).
Agent: HERMES_HOME directory mode (default 0700). 0640 on a directory
has no owner-execute bit, so the agent can't traverse its own home and
bricks. My initial draft recommended HERMES_HOME_MODE=0640 in agent
service blocks - corrected to 0750 across all 4 surfaces (compose
files, .env.docker.example, docs/docker.md). 3 regression tests pin
the asymmetry.
12 regression tests total in test_v050260_docker_invariants.py.
Full suite: 3627 passed, 0 failed.
Nathan explicitly authorized merge with my own review + Opus only, no
independent review needed.