Skip to content

fix(dashboard): reject port-binding channels on secondary multiplexed profiles (#62791) - #62803

Closed
PRATHAMESH75 wants to merge 1 commit into
NousResearch:mainfrom
PRATHAMESH75:fix/dashboard-multiplex-port-binding
Closed

fix(dashboard): reject port-binding channels on secondary multiplexed profiles (#62791)#62803
PRATHAMESH75 wants to merge 1 commit into
NousResearch:mainfrom
PRATHAMESH75:fix/dashboard-multiplex-port-binding

Conversation

@PRATHAMESH75

Copy link
Copy Markdown
Contributor

What does this PR do?

Stops the dashboard Channels API from persisting a configuration that fatally breaks a multiplexed gateway.

With gateway.multiplex_profiles on, the default profile owns the single shared HTTP listener, and gateway/run.py fail-fasts with MultiplexConfigError when a secondary profile enables a port-binding platform. But PUT /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.yaml write 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 and body.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.py as PORT_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

  • 🐛 Bug fix (non-breaking change that fixes an issue)

Changes Made

  • gateway/config.py — new PORT_BINDING_PLATFORM_VALUES frozenset (moved verbatim from gateway/run.py), documented as the shared policy for gateway startup and dashboard validation.
  • gateway/run.py — imports the set from gateway.config under the existing _PORT_BINDING_PLATFORM_VALUES name (no behavior change; existing tests that import it keep passing).
  • hermes_cli/web_server.py — new _multiplex_port_binding_conflict() helper; update_messaging_platform rejects enabling a port-binding platform on a secondary profile with 409 before any write when the default profile's gateway config (plus the GATEWAY_MULTIPLEX_PROFILES override, via load_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 — new TestMultiplexPortBindingGuard covering: rejection of every platform in the shared set on a secondary profile (both ?profile= and body.profile); rejected requests leave .env and config.yaml byte-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

  1. scripts/run_tests.sh tests/hermes_cli/test_web_server_messaging_profiles.py tests/gateway/test_multiplex_adapter_registry.py — 26 passed, 0 failed.
  2. Repro from the issue: set gateway.multiplex_profiles: true on the default profile, then PUT /api/messaging/platforms/api_server?profile=<secondary> with {"enabled": true} → now returns 409 with an explanation, and the secondary profile's .env/config.yaml are untouched; the multiplexed gateway restarts cleanly.
  3. Same request with {"enabled": false} or clear_env still returns 200, so an existing invalid profile can be repaired from the dashboard.

Checklist

Code

  • I've read the Contributing Guide
  • My commit messages follow Conventional Commits (fix(scope):, feat(scope):, etc.)
  • I searched for existing PRs to make sure this isn't a duplicate
  • My PR contains only changes related to this fix/feature (no unrelated commits)
  • I've run pytest tests/ -q and all tests pass
  • I've added tests for my changes (required for bug fixes, strongly encouraged for features)
  • I've tested on my platform: macOS 15 (Darwin 25.5.0)

Documentation & Housekeeping

  • I've updated relevant documentation (README, docs/, docstrings) — or N/A
  • I've updated cli-config.yaml.example if I added/changed config keys — or N/A (no new config keys)
  • I've updated CONTRIBUTING.md or AGENTS.md if I changed architecture or workflows — or N/A
  • I've considered cross-platform impact (Windows, macOS) per the compatibility guide — or N/A (pure Python path/config logic, no OS-specific behavior)
  • I've updated tool descriptions/schemas if I changed tool behavior — or N/A

Screenshots / Logs

Rejected request (secondary profile, multiplexing on):

HTTP 409
{"detail": "Cannot enable 'api_server' on profile 'worker_alpha': it binds its own listener port, and gateway.multiplex_profiles is on, so the default profile owns the single shared HTTP listener for every profile. Configure this channel on the default profile instead (disabling or clearing it here is still allowed)."}

… 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
@teknium1

Copy link
Copy Markdown
Contributor

Thanks for the focused pre-write guard. The premise is confirmed on current main: hermes_cli/web_server.py:7858-7879 persists enabled profile-scoped channel configuration without a topology check, while gateway/run.py:8570-8580 rejects the same enabled port-binding platform during secondary-profile startup. The PR validates before that write path, preserves repair operations, and shares the platform policy between the dashboard and startup guard.

Automated hermes-sweeper review.

@alt-glitch alt-glitch added type/bug Something isn't working comp/dashboard Web dashboard / control panel UI (dashboard/, landing) comp/gateway Gateway runner, session dispatch, delivery area/config Config system, migrations, profiles P2 Medium — degraded but workaround exists sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades duplicate This issue or pull request already exists labels Jul 11, 2026
@alt-glitch

Copy link
Copy Markdown
Collaborator

This was generated by AI during triage.

Duplicate of #62801 (earlier by ~2 min, OPEN) — both fix #62791 by moving the port-binding platform set into gateway/config.py as a single source of truth and rejecting the enable with HTTP 409 before any .env/config.yaml write in PUT /api/messaging/platforms/{id}. #62801 is the earliest-open canonical.

@teknium1 teknium1 added the sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform label Jul 11, 2026
teknium1 added a commit that referenced this pull request Jul 16, 2026
The #62803 branch predates PR #64636's Telegram token-shape validation
on the messaging platform PUT endpoint; align the new guard test's
fixture with the validated format.
@teknium1

Copy link
Copy Markdown
Contributor

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 clear_env recovery handling, get_active_profile_name() resolution for the dashboard's own profile, and reuse of the canonical load_gateway_config() chain. Note @Ahmett101's #62801 was submitted first the same day — both contributions acknowledged. Thanks!

@teknium1 teknium1 closed this Jul 16, 2026
@teknium1 teknium1 added the area/profiles Multi-profile isolation, HERMES_HOME scoping label Jul 19, 2026
Gravezzz pushed a commit to Gravezzz/hermes-agent that referenced this pull request Jul 21, 2026
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.
randlee pushed a commit to randlee/hermes-agent that referenced this pull request Aug 11, 2026
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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/config Config system, migrations, profiles area/profiles Multi-profile isolation, HERMES_HOME scoping comp/dashboard Web dashboard / control panel UI (dashboard/, landing) comp/gateway Gateway runner, session dispatch, delivery duplicate This issue or pull request already exists P2 Medium — degraded but workaround exists sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Dashboard allows invalid port-binding channels on secondary multiplexed profiles

3 participants