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. <code> becoming <code> 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>
This commit is contained in:
nesquena-hermes
2026-04-11 10:17:52 -07:00
committed by GitHub
parent 0e112455ec
commit 27c2fd6c08
21 changed files with 1324 additions and 56 deletions

View File

@@ -6,6 +6,32 @@
---
## [v0.46.0] — 2026-04-11
### Features
- **Docker UID/GID matching** (PR #237 by @mmartial): New `docker_init.bash` entrypoint adds `hermeswebui`/`hermeswebuitoo` user pattern so container-created files match the host user UID/GID. Prevents `.hermes` volume mounts from being owned by root. Configure via `WANTED_UID` and `WANTED_GID` env vars (default 1000/1000). README updated with setup instructions.
- `Dockerfile` — two-user pattern with passwordless sudo; `/.within_container` marker for in-container detection; starts as `hermeswebuitoo`, switches to correct UID/GID
- `docker-compose.yml` — mounts `.hermes` at `/home/hermeswebui/.hermes`; uses `${UID:-1000}/${GID:-1000}` for UID/GID passthrough
- `server.py` — detects `/.within_container` and prints a note when binding to 0.0.0.0
### Security
- **Credential redaction in API responses** (PR #243 by @kcclaw001): All API endpoints now redact credentials from responses at the response layer. Session files on disk are unchanged; only the API output is masked.
- `api/helpers.py``redact_session_data()` and `_redact_value()` apply pattern-based redaction to messages, tool_calls, and title; covers GitHub PATs, OpenAI/Anthropic keys, AWS keys, Slack tokens, HuggingFace tokens, Authorization Bearer headers, and PEM private key blocks
- `api/routes.py``GET /api/session`, `GET /api/session/export`, `GET /api/memory` all wrapped with redaction
- `api/streaming.py` — SSE `done` event payload redacted before broadcast
- `api/startup.py` — new `fix_credential_permissions()` called at startup; `chmod 600` on `.env`, `google_token.json`, `auth.json`, `.signing_key` if they have group/other read bits set
- `tests/test_security_redaction.py` — 13 new tests covering redaction functions and endpoint structural verification
### Bug Fixes
- **Custom model list discovery with config API key** (PR #238 by @ccqqlo): `get_available_models()` now reads `api_key` from `config.yaml` before env vars when fetching `/v1/models` from custom endpoints (LM Studio, Ollama, etc.). Priority: `model.api_key``providers.<active>.api_key``providers.custom.api_key` → env vars. Also adds `OpenAI/Python 1.0` User-Agent header. Fixes model picker collapsing to single default model for config-only setups. 1 new regression test.
- **HTML entity decode before markdown processing** (PR #239 by @Argonaut790): 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 (`&lt;`, `&gt;`, `&amp;`, `&quot;`, `&#39;`).
- **Simplified Chinese translations completed** (PR #239 by @Argonaut790): 40+ missing keys added to `zh` locale (123 → 164 keys). New `zh-Hant` (Traditional Chinese) locale with 163 keys.
- **Cancel button now interrupts agent execution** (PR #244 by @huangzt): `cancel_stream()` now calls `agent.interrupt()` to stop backend tool execution, not just the SSE stream. `AGENT_INSTANCES` dict (protected by `STREAMS_LOCK`) tracks active agents. Race condition fixed: after storing agent, immediately checks if cancel was already requested. Frontend: removes stale "Cancelling..." status text; `setBusy(false)` always called on cancel. 6 new unit tests in `tests/test_cancel_interrupt.py`.
**624 tests (up from 604 on v0.45.0 — +20 new tests)**
---
## [v0.45.0] — 2026-04-10
### Features