fix: concurrent .env writes + WS TOCTOU race + gateway guard + Desktop usage panel - #77358
Conversation
…p usage panel fix1 (NousResearch#77187): add portalocker.Lock to save_env_value/remove_env_value - cross-process lock around full read-modify-write on ~/.hermes/.env - prevents token clobbering when multiple Hermes processes write .env - import portalocker (already a transitive dependency) fix2 (NousResearch#77192): fix WS close_on_disconnect TOCTOU race - snapshot owned session ids outside _sessions_lock - re-lookup each session under _session_resume_lock -> _sessions_lock before acting, validating transport still matches - defer teardown work off both locks (consistent with _pop_session_by_id) - prevents RuntimeError from mutating dict during iteration fix3 (NousResearch#77173): tighten gateway lifecycle guard regex (Branch A) - anchor on start-of-command or delimiter before "hermes gateway" - stops false positives on absolute paths like /usr/bin/hermes gateway restart feat1 (NousResearch#77221): add Desktop Usage panel as a built-in full-page view - new /usage route: summary cards, daily sparkline, per-model breakdown - lazy-loaded UsageView, registered in routes + sidebar nav + keybinds - consumes existing /api/analytics/usage (no new backend surface)
Related: this omnibus mixes several independent fixes with a Desktop usage feature and overlaps the active .env/WebSocket/lifecycle-guard work. Please consider splitting the independently reviewable changes or confirming the intended combined scope. |
|
Thanks for the review @alt-glitch — you're right, this omnibus was too broad. Apologies for bundling independent fixes together. I've closed this PR and split it into 4 separate, clean PRs, each addressing one issue:
Each one is rebased on current main and only touches the relevant files. Thanks for the guidance — will keep PRs focused going forward 🙏 |
|
Closing in favor of 4 separate PRs listed above. |
|
Hi maintainers — thanks for the feedback on the original submission. I have taken it to heart and split this PR into 4 focused, single-purpose PRs:
Each PR now contains exactly one logical change, making review much easier. I have closed this omnibus PR in favor of the individual ones above. Apologies for the initial bundling — I will keep each PR scoped to a single fix going forward. |
Summary
Four fixes and one feature, one commit, all clean against current
main(rebased on 75901a2).fix1 (#77187) — concurrent .env writes cause token clobbering
portalocker.Lock(env_path, timeout=5, fail_when_locked=False)insave_env_value()andremove_env_value()covering the full read-modify-write path on~/.hermes/.envportalockeris already a transitive dependency (used byuvin the venv)fix2 (#77192) — WS
close_on_disconnectTOCTOU race (dup of #77192 / PR #77129)_close_sessions_for_transport()now snapshots session IDs outside_sessions_lock, then re-validates each session under_session_resume_lock -> _sessions_lockbefore actingRuntimeErrorfrom mutating_sessionsdict during iteration; reconnecting session is never silently closed or detachedfix3 (#77173) — gateway lifecycle guard false-positive on full paths
r"(?:hermes\s+gateway\s+(?:restart|stop))"tor"(?:^|[\s;&|\n])hermes\s+gateway\s+(?:restart|stop)\b"/usr/bin/hermes gateway restartetc.feat1 (#77221) — Desktop Usage panel (built-in full-page view at
/usage)UsageViewshowing summary cards, daily token/cost sparkline, and per-model breakdownUSAGE_ROUTEinroutes.ts, lazy import +<Route>insurfaces.tsx, sidebar nav entry, andnav.usagekeybind/api/analytics/usageendpoint (no new backend surface needed)Files changed
hermes_cli/config.pytui_gateway/server.py_close_sessions_for_transportcron/lifecycle_guard.pyapps/desktop/src/app/routes.tsUSAGE_ROUTE/usageview typesapps/desktop/src/app/contrib/surfaces.tsxapps/desktop/src/app/chat/sidebar/index.tsxapps/desktop/src/lib/keybinds/actions.tsapps/desktop/src/app/usage/{index,sparkline,store}.ts*Validation
main(75901a2); no conflicts against upstream changes in any of the touched files.