15 Commits

Author SHA1 Message Date
Frank Song be32b90cea docs: refresh current project snapshot 2026-05-13 16:47:14 +08:00
Michael De Gols 4ba31f9462 fix(docker_init): fall back when /tmp not root-writable (Railway)
On user-namespaced rootless runtimes (Railway), in-container UID 0 maps
to a host UID outside the writable subuid range, so /tmp writes fail
despite id -u returning 0. The existing read-only-rootfs guard only
covers /etc/{group,passwd} and doesn't catch this.

Probe /tmp writability before save_env and fall back through
$itdir → /app, exporting _HW_ROOT_ENV_PATH so the post-su phase reads
from the same path.

Closes #2010

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 19:14:49 +02:00
Michael Lam b1b0cedbe9 security: harden production Docker image 2026-05-08 20:48:39 +00:00
bergeouss d4385f8aa2 fix: false read-only detection in docker_init.bash (#1470 follow-up)
The read-only rootfs guard added in PR #1635 (issue #1470) checks
[ ! -w /etc/group ] as the current user (hermeswebuitoo, non-root).
On a normal writable rootfs this always fails because /etc/group is
owned by root — causing a false positive that crashes the container
with "Cannot modify /etc/group or /etc/passwd (read-only root fs)".

Fix: use sudo to test writability, since groupmod/usermod already
use sudo a few lines below. If sudo can write, the fs is not
read-only and the guard should not trigger.

Refs #1470
2026-05-04 22:38:38 +00:00
bergeouss 21ba37c486 fix: session list race condition (#1430) + read-only fs guard (#1470)
#1430 — renderSessionList() had no staleness guard. Multiple concurrent
callers (message send, rename, session switch) could race, allowing a
slower older API response to overwrite _allSessions with stale data.
Added a generation counter that increments on each call and discards
responses from superseded generations.

#1470 — docker_init.bash unconditionally called groupmod/usermod even
on read-only root filesystems (podman with read_only=true). Added a
writability check for /etc/group and /etc/passwd. If read-only and
UID/GID already match, the mod is skipped gracefully. If they don't
match, a clear error message suggests setting matching IDs or disabling
read_only mode.
2026-05-04 16:51:53 +00:00
nesquena-hermes 69bf2878bc v0.50.224: legacy @provider session models, Docker Hindsight dependency (#1131)
* Fix legacy at-provider session models

* Fix Hindsight dependency in Docker WebUI venv

---------

Co-authored-by: Frank Song <franksong2702@gmail.com>
2026-04-26 18:47:38 -07:00
Joe Maples ae7be6deba fix(docker): Install all dependencies for agent (#897) 2026-04-23 09:45:28 -07:00
bergeouss a72208eaf6 fix(docker): improve two-container agent path discovery and docs — v0.50.158 (PR #873 by @bergeouss, closes #858)
docker_init.bash now checks /opt/hermes as a fallback alongside the primary path. Warning updated with concrete mount guidance. Volume type notes added to compose files and README.
2026-04-22 23:35:09 +00:00
nesquena-hermes 352354790f fix: streaming scroll override, Gemini 3.x models, read-only workspace, two-container UID — v0.50.87 (closes #677 #669 #670 #668)
- #677: renderMessages() and appendThinking() use scrollIfPinned() during stream; scroll threshold 80→150px; floating ↓ scroll-to-bottom button added
- #669: Gemini 3.1 Pro Preview, 3 Flash Preview, 3.1 Flash Lite Preview added to all provider sections; gemini-3.1-flash-lite-preview was the missing ID causing API_KEY_INVALID; GEMINI_API_KEY env var detection added
- #670: docker_init.bash guards chown/write-test with [ -w ]; :ro workspace mounts no longer crash startup
- #668: UID/GID auto-detect probes /home/hermeswebui/.hermes and HERMES_HOME before /workspace; two-container Zeabur/Compose setups inherit correct UID automatically
- 18 new tests; 1441 total passing
2026-04-18 17:09:59 +00:00
nesquena-hermes 25d38a467a fix: Docker UID/GID auto-detect from workspace mount + message count tests — v0.50.69
Fixes #569: docker_init.bash auto-detects WANTED_UID/WANTED_GID from the mounted /workspace UID at Phase 1, before usermod remaps the container user. On macOS, host UIDs start at 501 — the default 1024 caused an empty workspace. Guards against root (0). Fallback 1024 preserved. Closes #579: topbar already correctly filters tool messages; sidebar count removed in #584. Regression tests added. Reviewed and approved by @nesquena. 1347 tests passing.
2026-04-16 12:19:25 -07:00
nesquena-hermes 45426bdcd1 fix: make hermes-agent source optional in Docker startup — v0.50.62
Squash-merges PR #577 (rebased from #573 by @nesquena). Docker hard-exit on missing hermes-agent → graceful warning. 1319 tests pass. Fixes #570.
2026-04-15 23:22:26 -07:00
Hermes Agent fbce1093b9 fix: install hermes-agent[honcho] extra in Docker init (fixes #553)
docker_init.bash was installing hermes-agent without the [honcho] optional
extra, causing honcho-ai to be missing from /app/venv. All Honcho memory
tools would fail with 'Honcho session could not be initialized' on every
fresh Docker build.

Adds [honcho] to the uv pip install invocation on line 238.
2026-04-15 23:22:20 +00:00
nesquena-hermes a6484f69a8 fix: Docker uv pre-install at build time + workspace permissions (#365)
* fix: pre-install uv in Docker image + fix workspace dir permissions (#357)

Two fixes for Docker startup reliability:

1. Install uv at build time in the Dockerfile so the container works
   without internet access at runtime. The init script now skips the
   download when uv is already on PATH.

2. Use sudo mkdir/chown for the workspace directory, matching the
   pattern used for /app. Docker auto-creates bind-mount directories
   as root, leaving them unwritable by the hermeswebui user.

Fixes #357

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: Docker uv pre-install as root to /usr/local/bin + tests + CHANGELOG

Dockerfile: install uv as root with UV_INSTALL_DIR=/usr/local/bin so it
lands in /usr/local/bin (system PATH) rather than /home/hermeswebuitoo/.local/bin
which the hermeswebui runtime user can't see.

tests/test_issue357.py: 15 structural tests covering Dockerfile uv build-time
install (system-wide, as root, before COPY), init script skip-if-present
logic, and workspace sudo mkdir/chown.

CHANGELOG.md: v0.50.17 entry; 915 tests (up from 900)

---------

Co-authored-by: Nathan Esquenazi <nesquena@gmail.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 12:36:11 -07:00
Nathan Esquenazi 26c24867e6 fix: Docker container restart without recreating (#324)
uv venv fails with 'A virtual environment already exists' when the
container is stopped and started (not removed). The venv persists in
the container filesystem between stop/start cycles.

Fix: skip venv creation and dependency installation if they already
exist from a previous run. Uses two checks:
- /app/venv/bin/python3 exists → skip venv creation
- /app/venv/.deps_installed marker → skip pip install

This also makes restarts much faster since deps don't need to be
reinstalled every time the container starts.

Fixes #324

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 13:27:21 -07:00
nesquena-hermes 27c2fd6c08 v0.46.0: security, Docker UID/GID, model discovery, i18n, cancel fix
* fix: decode HTML entities before markdown processing + zh/zh-Hant translations (#239)

Adds decode() helper in renderMd() to fix double-escaping of HTML entities
from LLM output (e.g. &lt;code&gt; becoming &amp;lt;code&amp;gt; instead
of rendering). XSS-safe: decode runs before esc(), only 5 entity patterns.

Also adds 40+ missing zh (Simplified Chinese) translation keys and a new
zh-Hant (Traditional Chinese) locale with 163 keys.

Fix applied: removed duplicate settings_label_notifications key in both
zh and zh-Hant locales.

Fixes #240

* fix: restore custom model list discovery with config api key (#238)

get_available_models() now reads api_key from config.yaml before env vars:
  1. model.api_key
  2. providers.<active>.api_key / providers.custom.api_key
  3. env var fallbacks (HERMES_API_KEY, OPENAI_API_KEY, etc.)

Also adds OpenAI/Python User-Agent header and a regression test covering
authenticated /v1/models discovery.

Fixes users with LM Studio / Ollama custom endpoints configured in
config.yaml whose model picker silently collapsed to the default model.

* feat: Docker UID/GID matching to avoid root-owned .hermes files (#237)

Adds docker_init.bash with hermeswebuitoo/hermeswebui user pattern so
container files match the host user UID/GID. Prevents .hermes volume
mounts from being owned by root when using a non-root host user.

Configure via WANTED_UID and WANTED_GID env vars (default 1000/1000).
Readme updated with setup instructions.

Fix applied: removed duplicate WANTED_GID=1000 line in docker-compose.yml
that was overriding the ${GID:-1000} variable expansion.

* security: redact credentials from API responses and fix credential file permissions (#243)

Adds response-layer credential redaction to three endpoints:
  - GET /api/session — messages[], tool_calls[], and title
  - GET /api/session/export — download also redacted
  - SSE done event — session payload in stream
  - GET /api/memory — MEMORY.md and USER.md content

Adds api/startup.py with fix_credential_permissions() at server startup.
Adds 13 tests in tests/test_security_redaction.py.

Merged with #237 container detection changes in server.py.

* fix: cancel button now interrupts agent and cleans up UI state (#244)

Wires agent.interrupt() into cancel_stream() so the backend actually
stops tool execution when the user clicks Cancel, rather than only
stopping the SSE stream while the agent keeps running.

Changes:
  - api/config.py: adds AGENT_INSTANCES dict (stream_id -> AIAgent)
  - api/streaming.py: stores agent in AGENT_INSTANCES after creation,
    checks CANCEL_FLAGS immediately after store (race condition fix),
    calls agent.interrupt() in cancel_stream(), cleans up in finally block
  - static/boot.js: removes stale setStatus(cancelling) call
  - static/messages.js: setBusy(false)/setStatus('') unconditionally on cancel

Race condition fix: after storing agent in AGENT_INSTANCES, immediately
checks if CANCEL_FLAGS[stream_id] is already set (cancel arrived during
agent init) and interrupts before starting. Check is inside the same
STREAMS_LOCK acquisition, making it atomic.

New test file: tests/test_cancel_interrupt.py with 6 unit tests.

* docs: v0.46.0 release notes, bump version, update test counts

---------

Co-authored-by: Nathan Esquenazi <nesquena@gmail.com>
2026-04-11 10:17:52 -07:00