fix(dashboard): add true lightweight mode - #61121
Conversation
Related: #58721 (competing lightweight-dashboard approach) and #58694 (the underlying v0.17.0 dashboard memory regression). Both PRs target #58694, but via different mechanisms: #58721 hides admin routes while still initializing the full FastAPI/Pydantic stack (the reporter noted RAM still jumps after first page access), whereas this PR branches before |
48b315f to
3cbf461
Compare
|
Thanks for flagging the cluster @alt-glitch. To make the intended relationship explicit: I see #58721 as the lighter-touch/full-dashboard route-filtering approach, while this PR is the root memory fix for the reporter's follow-up: it branches before \hermes_cli.web_server\ is imported, so lightweight mode does not initialize the FastAPI/Pydantic/admin route stack at all.\n\nI kept full dashboard behavior as the default, made the new path loopback-only with Host-header validation, and preserved the readiness contract. If maintainers prefer, #58721's route-filtering/config work could still be layered onto the full dashboard later, but it does not replace this backend split for the post-page-load RSS regression. |
teknium1
left a comment
There was a problem hiding this comment.
Thanks for taking the deeper stdlib-server route; current main still imports FastAPI/Pydantic in the ordinary dashboard path (hermes_cli/main.py:12041-12043, hermes_cli/web_server.py:89-98), so the memory premise is valid.
Problems
- The named-profile compatibility claim is not met. Main routes a profile launch to
/?profile=<name>(hermes_cli/main.py:11971), but the new handler parses noprofilevalue and/api/sessionscalls the root-scoped payload builder (hermes_cli/light_dashboard_server.py:293-309).initial_profileonly affects the auto-open URL (:378-388), so a rerouted worker dashboard shows root-profile data.
Suggested changes
- Add validated profile-aware session/status resolution without importing
hermes_cli.web_server, and cover/?profile=workeror the named-profile reroute intests/hermes_cli/test_light_dashboard.py.
Automated hermes-sweeper review.
| offset = _coerce_int((params.get("offset") or [None])[0], default=0, minimum=0, maximum=100000) | ||
| self._send_json( | ||
| HTTPStatus.OK, | ||
| build_sessions_payload(limit=limit, offset=offset, order=order), |
There was a problem hiding this comment.
/?profile=<name> is the machine-dashboard routing contract, but this request path never reads or forwards profile; build_sessions_payload() therefore opens the root DEFAULT_DB_PATH. Please add validated profile-aware resolution here (and to status) so named-profile lightweight launches do not silently show root-profile data.
There was a problem hiding this comment.
@teknium1 Implemented the requested named-profile scoping in dc8de37.
The lightweight page now forwards its ?profile= selection to both status and sessions. The backend normalizes and validates that profile through the existing profile helpers, rejects malformed/missing profiles, and reads the selected profile's state.db, gateway.pid, and gateway_state.json. Status also preserves the launch-managed runtime PID fallback, scoped with expected_home to prevent cross-profile PID reuse.
Added coverage for named-profile DB resolution, status/runtime resolution, browser-to-API propagation, and invalid/missing profiles. Verified with 17 lightweight/serve tests, Ruff, Ty, the Windows-footgun scan, a real HTTP integration check, and a clean disposable merge onto latest upstream main (291eae6).
Please take another look when you have a moment. If you see any remaining edge case or want the profile contract shaped differently, I will adjust it :)
…lightweight-mode # Conflicts: # tests/hermes_cli/test_web_server.py
What does this PR do?
Adds a true lightweight dashboard mode for memory-constrained self-hosted installs.
hermes dashboard --lightnow starts a small stdlib HTTP server instead of importing the full FastAPI/React dashboard backend. It exposes only the monitoring surface needed by constrained hosts:GET /lightweight status/session pageGET /api/statusGET /api/sessionsIt deliberately skips the full admin backend stack: FastAPI/Pydantic route initialization, React bundle build/serving, dashboard plugin route loading, and background dashboard MCP discovery.
The mode can also be persisted with:
Why
Fixes #58694.
PR #58721 adds a useful lightweight UI/config mode, but the issue reporter confirmed that after first page access the backend still jumps to roughly the full dashboard memory footprint because the full FastAPI/Pydantic route stack is still initialized. This PR takes the deeper route: the lightweight path branches before
hermes_cli.web_serveris imported.Safety / security
dashboard.mode: full).hermes serveremains unchanged and ignoresdashboard.mode.Hostheader to reject DNS-rebinding style requests even on loopback.HERMES_DASHBOARD_READYand writingHERMES_DESKTOP_READY_FILEwhen present.Changes Made
hermes_cli/light_dashboard_server.pywith a focused stdlib backend.hermes dashboard --lightplus hidden--legacyalias.dashboard.mode: full|lightweightconfig support and dashboard schema metadata.--lightthrough the re-exec path.Validation
Results:
compileall: passruff: pass14 passed13 passedgit diff --check: passhermes dashboard --light --port 0 --no-openemittedHERMES_DASHBOARD_READY, served/with200, returned/api/statuswithmode: lightweight, returned/api/sessionswith the expected JSON shape, and rejected a badHost: evil.examplerequest with400.Note:
scripts/run_tests.shcould not run in this Windows checkout because WSL Bash sees the repo as/mnt/e/...and no POSIX.venv/bin/activateexists there. I ran the same underlying per-file runner directly via the repo dev environment.Checklist
Code
pytest tests/ -qand all tests passDocumentation & Housekeeping
cli-config.yaml.examplefor the new config key