Problem
hermes_cli/web_server.py → update_config() calls save_config(_denormalize_config_from_web(body.config)), and hermes_cli/config.py → save_config() replaces the entire ~/.hermes/config.yaml file with the provided dict.
If a caller (e.g. a dashboard UI) sends a PUT payload containing only the sections it manages (agent, display), the write silently deletes everything else — model, custom_providers, compression, auxiliary, fallback_model, etc.
Reproduction
-
Configure Hermes with: model, custom_providers, compression (etc.) in config.yaml.
-
From a UI client or curl:
curl -X PUT http://127.0.0.1:9119/api/config \
-H "Authorization: Bearer " \
-H "Content-Type: application/json" \
-d '{"config": {"agent": {"max_turns": 90}, "display": {"personality": "concise"}}}'
-
Inspect ~/.hermes/config.yaml → all other sections are gone.
Impact
- Third-party dashboards (e.g. outsourc-e/hermes-workspace Settings screen) silently destroy user-managed fields they don't know about.
- Users have to re-edit config.yaml from memory.
- Happens invisibly — no warning, no backup before the overwrite.
Expected behavior (proposals, any of the below)
(a) Deep-merge on server side. In update_config():
existing = load_config()
merged = _deep_merge(existing, _denormalize_config_from_web(body.config))
save_config(merged)
(b) Require the client to send the full config and reject PUTs that omit top-level keys present on disk (409 Conflict).
(c) Make save_config() take an explicit scope parameter listing which top-level keys to overwrite. Anything outside that scope is preserved.
I'd vote for (a) — it's the least surprising behavior and matches what every third-party client is already assuming.
Env
- Hermes v0.10.0
- Triggered by: outsourc-e/hermes-workspace Settings screen on x.joao.date
- Lost fields included:
model, custom_providers, compression, auxiliary.vision
Happy to open a PR for option (a).
Problem
hermes_cli/web_server.py→update_config()callssave_config(_denormalize_config_from_web(body.config)), andhermes_cli/config.py→save_config()replaces the entire~/.hermes/config.yamlfile with the provided dict.If a caller (e.g. a dashboard UI) sends a PUT payload containing only the sections it manages (
agent,display), the write silently deletes everything else —model,custom_providers,compression,auxiliary,fallback_model, etc.Reproduction
Configure Hermes with:
model,custom_providers,compression(etc.) inconfig.yaml.From a UI client or
curl:curl -X PUT http://127.0.0.1:9119/api/config \
-H "Authorization: Bearer " \
-H "Content-Type: application/json" \
-d '{"config": {"agent": {"max_turns": 90}, "display": {"personality": "concise"}}}'
Inspect
~/.hermes/config.yaml→ all other sections are gone.Impact
Expected behavior (proposals, any of the below)
(a) Deep-merge on server side. In
update_config():(b) Require the client to send the full config and reject PUTs that omit top-level keys present on disk (409 Conflict).
(c) Make
save_config()take an explicitscopeparameter listing which top-level keys to overwrite. Anything outside that scope is preserved.I'd vote for (a) — it's the least surprising behavior and matches what every third-party client is already assuming.
Env
model,custom_providers,compression,auxiliary.visionHappy to open a PR for option (a).