fix(dashboard): reject port-binding channels on secondary multiplexed profiles (#62791) - #62803
Conversation
… profiles
The Channels API (PUT /api/messaging/platforms/{id}) accepted and persisted
enabling a port-binding platform on a secondary profile while
gateway.multiplex_profiles is on — a config the gateway only rejects on its
next start, aborting startup with MultiplexConfigError for every multiplexed
profile.
Validate before any .env/config.yaml write and return 409 for the enable
attempt. Disabling and clearing env stay allowed so an already-invalid
profile can be repaired. The port-binding platform set moves to
gateway/config.py (PORT_BINDING_PLATFORM_VALUES) as the single source of
truth shared by gateway startup validation and the dashboard, so the two
policies cannot drift. Platform config mutations now get a names-only audit
log line.
Fixes NousResearch#62791
|
Thanks for the focused pre-write guard. The premise is confirmed on current main: Automated hermes-sweeper review. |
Duplicate of #62801 (earlier by ~2 min, OPEN) — both fix #62791 by moving the port-binding platform set into |
|
Merged via PR #65700 — your commit was cherry-picked onto current main with your authorship preserved in git log (rebase merge). Your implementation was chosen over the competing same-day PR (#62801) on review: correct |
The NousResearch#62803 branch predates PR NousResearch#64636's Telegram token-shape validation on the messaging platform PUT endpoint; align the new guard test's fixture with the validated format.
The NousResearch#62803 branch predates PR NousResearch#64636's Telegram token-shape validation on the messaging platform PUT endpoint; align the new guard test's fixture with the validated format.
What does this PR do?
Stops the dashboard Channels API from persisting a configuration that fatally breaks a multiplexed gateway.
With
gateway.multiplex_profileson, the default profile owns the single shared HTTP listener, andgateway/run.pyfail-fasts withMultiplexConfigErrorwhen a secondary profile enables a port-binding platform. ButPUT /api/messaging/platforms/{platform_id}happily accepted and persisted exactly that config — the failure only surfaced at the next gateway start, taking down all multiplexed profiles.This PR validates before any
.env/config.yamlwrite and rejects the enable attempt with HTTP 409 and an explanatory message (surfaced by the existing dashboard error toast). It covers both the?profile=query parameter andbody.profile. Disabling and clearing env vars remain allowed so users can repair an already-invalid profile. Non-port-binding platforms and the default profile are unaffected, and everything stays permitted when multiplexing is off.To keep the dashboard and gateway policies from drifting, the port-binding platform set moves to
gateway/config.pyasPORT_BINDING_PLATFORM_VALUES— the single source of truth imported by both the gateway's startup validation and the new dashboard check (membership unchanged). The gateway's startup validation is kept as defense in depth.Platform config mutations (and rejections) now emit a names-only audit log line — profile, platform, enabled flag, env key names — never values.
Scope note: this is the API-side enforcement the issue asks for (its point 2). Proactively disabling the toggle in the Channels UI for secondary profiles (point 3) is left as a follow-up; the 409 detail already renders in the UI's error toast with the full explanation.
Related Issue
Fixes #62791
Type of Change
Changes Made
gateway/config.py— newPORT_BINDING_PLATFORM_VALUESfrozenset (moved verbatim fromgateway/run.py), documented as the shared policy for gateway startup and dashboard validation.gateway/run.py— imports the set fromgateway.configunder the existing_PORT_BINDING_PLATFORM_VALUESname (no behavior change; existing tests that import it keep passing).hermes_cli/web_server.py— new_multiplex_port_binding_conflict()helper;update_messaging_platformrejects enabling a port-binding platform on a secondary profile with 409 before any write when the default profile's gateway config (plus theGATEWAY_MULTIPLEX_PROFILESoverride, viaload_gateway_config()) has multiplexing on; names-only audit logging for platform mutations; fixed a stale comment referencing the set's old location.tests/hermes_cli/test_web_server_messaging_profiles.py— newTestMultiplexPortBindingGuardcovering: rejection of every platform in the shared set on a secondary profile (both?profile=andbody.profile); rejected requests leave.envandconfig.yamlbyte-identical; default profile still allowed with multiplexing on; secondary profiles allowed with multiplexing off; disable/clear still allowed on an invalid secondary config; non-port-binding platform (telegram) unaffected.How to Test
scripts/run_tests.sh tests/hermes_cli/test_web_server_messaging_profiles.py tests/gateway/test_multiplex_adapter_registry.py— 26 passed, 0 failed.gateway.multiplex_profiles: trueon the default profile, thenPUT /api/messaging/platforms/api_server?profile=<secondary>with{"enabled": true}→ now returns 409 with an explanation, and the secondary profile's.env/config.yamlare untouched; the multiplexed gateway restarts cleanly.{"enabled": false}orclear_envstill returns 200, so an existing invalid profile can be repaired from the dashboard.Checklist
Code
fix(scope):,feat(scope):, etc.)pytest tests/ -qand all tests passDocumentation & Housekeeping
docs/, docstrings) — or N/Acli-config.yaml.exampleif I added/changed config keys — or N/A (no new config keys)CONTRIBUTING.mdorAGENTS.mdif I changed architecture or workflows — or N/AScreenshots / Logs
Rejected request (secondary profile, multiplexing on):