[Docker] Add the ability to run with the same user id/gid as the hermes installing user - #237
[Docker] Add the ability to run with the same user id/gid as the hermes installing user#237mmartial wants to merge 13 commits into
Conversation
|
Thanks for this PR @mmartial! The use-case is real — file ownership problems when mounting I've reviewed the full diff. Overall the approach is solid, but I have a few items I'd want addressed before merging: Issues to address1. +openaiThe 2. Hard-coded environment:
- WANTED_UID=1000
- WANTED_GID=1000UID 1000 is a reasonable default, but the ideal default is the calling user's UID (as you do in the environment:
- WANTED_UID=${UID:-1000}
- WANTED_GID=${GID:-1000}Users would then export 3. - - "127.0.0.1:8787:8787"
+# - "127.0.0.1:8787:8787"
+ - "8787:8787"The original 4. The The deferred import for 5. The init script exits with an error if if [ -z "${HERMES_WEBUI_DEFAULT_WORKSPACE+x}\" ]; then error_exit "HERMES_WEBUI_DEFAULT_WORKSPACE not set"; fiPreviously this variable was optional and had a reasonable internal default. Making it required is a breaking change for anyone who upgrades without updating their compose file or Summary
Happy to merge after these are addressed. Let me know if you have questions about any of the points. |
|
Seeing the new commit "recover original default" pushed just after the review — it looks like that addresses the port binding issue (item 3 in the review). To confirm where things stand, could you let me know which of the review items the new commits address? Specifically:
Once those are confirmed, I'll do a final pass and we can get this merged. |
…y in a hermes-webui venv
…at we are running within the container to show additional information from within the server .py start
…ional details when used "within the container"
… script; include uv setup and addition of hermes-agent's pyproject.toml for clean startup
|
Addressed remaining items. Manual review of |
|
Updated startup log: |
|
@nesquena-hermes ready for review |
|
Thanks for the detailed update and the new commits, @mmartial. Looking at what you've addressed since the last review:
If items 1 and 2 are addressed as described above, this looks ready for a final merge pass. Could you confirm:
|
Full Review: PR #237 — Docker user id/gid supportThanks @mmartial! Real problem, working solution. The UID/GID mismatch on mounted volumes is a genuine pain point. Security Audit
Code ReviewDocker changes are solid. The Dockerfile, init script, and compose file work together correctly. The Issues found:
Tests554 passed, 0 failed, 42 skipped. No regressions. VerdictGood to merge after fixing the duplicate |
|
…t will be ignored, please remove it to avoid potential confusion"
|
Note latest commit removes
|
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.
* 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 &lt;code&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>
|
Agent review — APPROVED ✅ (merged to v0.46.0) Reviewed full diff, PR comments, all 7 comment threads. What this adds: Fix applied during review: Removed a duplicate Security review: Passwordless sudo is scoped to the container only. Environment variable save/load obfuscates values matching
Tests: Docker changes don't affect Python tests. 624 passed on stage. Merged to master via stage branch in PR #249 (v0.46.0). |
* fix: decode HTML entities before markdown processing + zh/zh-Hant translations (nesquena#239) Adds decode() helper in renderMd() to fix double-escaping of HTML entities from LLM output (e.g. <code> becoming &lt;code&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 nesquena#240 * fix: restore custom model list discovery with config api key (nesquena#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 (nesquena#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 (nesquena#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 nesquena#237 container detection changes in server.py. * fix: cancel button now interrupts agent and cleans up UI state (nesquena#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>
* fix: decode HTML entities before markdown processing + zh/zh-Hant translations (nesquena#239) Adds decode() helper in renderMd() to fix double-escaping of HTML entities from LLM output (e.g. <code> becoming &lt;code&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 nesquena#240 * fix: restore custom model list discovery with config api key (nesquena#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 (nesquena#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 (nesquena#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 nesquena#237 container detection changes in server.py. * fix: cancel button now interrupts agent and cleans up UI state (nesquena#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>
Using as a non-root user (with
sudoprivileges if needed) installation of hermes-agent, this PR will allow the files in.hermesto not be created/modified by therootuser (the default user of the container).This PR proposes using a
hermeswebuiuser within the container; that user's UID/GID can be configured via theWANTED_UIDandWANTED_GIDenvironment variables to match the user who owns the.hermesfolder.I updated the
README.mdto reflect the change and explain its use case.The
docker_init.bashfile contains most of the user-switching logic.This is the same technique I have used successfully in the ComfyUI project I am maintaining.
I tested functionality using: