Skip to content

v0.46.0: security, Docker UID/GID, model discovery, i18n, cancel fix - #249

Merged
nesquena-hermes merged 6 commits into
masterfrom
stage
Apr 11, 2026
Merged

v0.46.0: security, Docker UID/GID, model discovery, i18n, cancel fix#249
nesquena-hermes merged 6 commits into
masterfrom
stage

Conversation

@nesquena-hermes

Copy link
Copy Markdown
Collaborator

v0.46.0 — Security, Docker UID/GID, model discovery, i18n, cancel fix

Accumulates 5 PRs reviewed, fixed, and tested end-to-end before merge.

Included PRs

PR Title Fix applied
#239 fix: decode HTML entities before markdown processing Removed duplicate settings_label_notifications key in zh and zh-Hant locales
#238 fix: restore custom model list discovery with config api key No fixes needed — clean as submitted
#237 Docker UID/GID matching Removed duplicate hardcoded WANTED_GID=1000 line in docker-compose.yml that overrode the ${GID:-1000} variable
#243 security: redact credentials from API responses Fixed corrupted _AUTH_HDR_RE display (regex was valid), fixed module-level skipif in test that caused 5 integration tests to always skip, converted 3 live HTTP tests to inspect.getsource() structural tests to fix SESSION_DIR path mismatch
#244 fix: cancel button now interrupts agent No fixes needed — race condition fix and cleanup were correctly implemented

Test results

  • 624 passed, 0 skipped, 0 failed (up from 604 on v0.45.0 — +20 new tests)
  • QA harness: 20/20 passed (mock conversation round-trips)
  • Browser sanity: initial load, new session, 3 messages, 5 sidebar panels, settings panel open/close — zero JS errors

Changes summary

Security (PR #243)

  • Response-layer credential redaction: GET /api/session, GET /api/session/export, GET /api/memory, SSE done event
  • fix_credential_permissions() at startup: chmod 600 on .env, google_token.json, auth.json, etc.
  • 13 new tests in tests/test_security_redaction.py

Docker (PR #237)

  • docker_init.bash entrypoint: UID/GID matching via WANTED_UID/WANTED_GID env vars
  • Prevents .hermes volume mounts from being owned by root
  • In-container detection in server.py with informational note on 0.0.0.0 binding

Bug fixes (PRs #238, #239, #244)

  • Custom endpoint model discovery: reads api_key from config.yaml before env vars
  • HTML entity decode before markdown: fixes <code> double-escaping
  • 40+ missing zh translations + new zh-Hant (Traditional Chinese) locale with 163 keys
  • Cancel button: agent.interrupt() called to actually stop tool execution; race condition fixed with check-after-store pattern

…nslations (#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
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.
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.
…le 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.
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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants