Skip to content

feat(gateway): negotiate scoped mobile WebSocket grants - #72635

Open
freemanconsulting wants to merge 6 commits into
NousResearch:mainfrom
Freeman-Consulting:feat/mobile-contract-hello-scopes-v2
Open

feat(gateway): negotiate scoped mobile WebSocket grants#72635
freemanconsulting wants to merge 6 commits into
NousResearch:mainfrom
Freeman-Consulting:feat/mobile-contract-hello-scopes-v2

Conversation

@freemanconsulting

@freemanconsulting freemanconsulting commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds a fail-closed, server-authorized mobile WebSocket contract at the gateway transport boundary.

  • Mobile clients request an explicit hermes.mobile audience and supported conversation scopes when minting a single-use WebSocket ticket.
  • The accepted socket carries the server-derived grant into tui_gateway.dispatch.
  • gateway.ready advertises the versioned protocol, contract, capabilities, and effective authorization grant.
  • Mobile requests are denied before dispatch when a method is unmapped, a required scope is missing, or parameters exceed the method policy.
  • Legacy dashboard tickets retain their existing full-authority behavior.

This is a current-main reconstruction of #62858. It preserves Eric Lewis's three original commits and authorship while reconciling the implementation with current gateway session, busy-turn redirect, MCP ownership, native auth, and transport-handoff behavior.

Why

Current /api/ws authenticates the connection and then exposes the full TUI JSON-RPC dispatcher. Authentication establishes identity, but it does not define which gateway methods a mobile client may invoke. This PR adds that missing authorization boundary without weakening the dashboard or stdio TUI.

Security and compatibility

  • Fail closed for mobile methods not present in the versioned policy.
  • conversation.read is mandatory for every mobile grant.
  • conversation.write, conversation.control, and conversation.delete remain distinct.
  • Approval resolution, shell execution, raw tool access, and unsupported parameters are not grantable through this contract version.
  • A writer without conversation.control may queue the next turn but cannot seize or interrupt the currently attached client's busy turn.
  • Bodyless legacy dashboard ticket requests are unchanged.
  • No new model tools, environment variables, prompt mutation, or telemetry.

Current-main reconciliation

  • Preserves current profile-scoped MCP discovery ownership; the WebSocket transport does not restart discovery.
  • Preserves current active-turn redirect, compute-host isolation, queue draining, and race-safe busy-submit behavior.
  • Rechecks write-only mobile busy submits under history_lock, preventing a prompt from being stranded if turn teardown drains the queue first.
  • Preserves current native desktop authorization routes added after the original PR base.
  • Adapts the positive dispatch test to the current offload model without leaking a background response into unrelated tests.

Test plan

Focused current-main suite:

uv run pytest -q \
  tests/tui_gateway/test_mobile_contract.py \
  tests/hermes_cli/test_dashboard_auth_ws_auth.py \
  tests/hermes_cli/test_dashboard_auth_ws_tickets.py \
  tests/test_tui_gateway_ws.py \
  tests/test_tui_gateway_server.py \
  tests/test_tui_gateway_queue_on_busy.py

Result: contract-focused tests pass. The final GitHub slice passed 8,243 tests and failed only the current-main test_write_json_serializes_concurrent_writes background-output isolation flake; that test passes repeatedly alone. The repository's ci-reviewed workflow automatically reruns failed jobs when a maintainer reviews the PR.

uv run ruff check \
  hermes_cli/dashboard_auth/routes.py \
  hermes_cli/dashboard_auth/ws_tickets.py \
  hermes_cli/web_server.py \
  tests/hermes_cli/test_dashboard_auth_ws_auth.py \
  tests/hermes_cli/test_dashboard_auth_ws_tickets.py \
  tests/tui_gateway/test_mobile_contract.py \
  tui_gateway/mobile_contract.py \
  tui_gateway/server.py \
  tui_gateway/ws.py

git diff --check

Result: passed.

The WebSocket ticket test exercises the real FastAPI ticket → upgrade → gateway.ready → denied dispatch path rather than only mocking policy helpers.

Attribution

Reconstructed from #62858 with the original commits and authorship preserved for Eric Lewis.

Checklist

  • Read the repository development and contribution guidance
  • Added security-boundary and transport-level regression coverage
  • Preserved legacy behavior
  • Focused tests pass
  • Ruff and git diff --check pass
  • No unrelated generated files or dependency changes

@alt-glitch alt-glitch added type/feature New feature or request P3 Low — cosmetic, nice to have comp/dashboard Web dashboard / control panel UI (dashboard/, landing) comp/tui Terminal UI (ui-tui/ + tui_gateway/) area/auth Authentication, OAuth, credential pools needs-decision Awaiting maintainer decision before any implementation sweeper:risk-security-boundary Sweeper risk: may affect sandboxing, auth, credentials, or sensitive data sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages labels Jul 27, 2026
@freemanconsulting
freemanconsulting marked this pull request as ready for review July 27, 2026 12:23
@freemanconsulting
freemanconsulting force-pushed the feat/mobile-contract-hello-scopes-v2 branch 3 times, most recently from 47d2b88 to 8717b4e Compare July 29, 2026 16:32
@teknium1

Copy link
Copy Markdown
Contributor

Thanks for the focused transport-boundary work. The underlying gap remains on current main: hermes_cli/web_server.py:14472-14474 consumes a gated ticket but drops its grant, hermes_cli/web_server.py:15580-15590 passes no authorization into handle_ws, and tui_gateway/server.py:1703-1739 dispatches requests without a scoped transport gate. The PR's pre-dispatch policy at tui_gateway/mobile_contract.py:200-247 is therefore directed at a real boundary and preserves the legacy authority path.

The current branch is conflicting, and its prompt/busy changes target code moved by f67ca220ab into tui_gateway/methods_prompt.py:67-240. Salvage should port those changes onto that current handler rather than treating the old server.py context as live.

Automated hermes-sweeper review.

@teknium1 teknium1 added sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform labels Jul 30, 2026
@freemanconsulting
freemanconsulting force-pushed the feat/mobile-contract-hello-scopes-v2 branch from 8717b4e to dbdbac1 Compare July 31, 2026 10:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/auth Authentication, OAuth, credential pools comp/dashboard Web dashboard / control panel UI (dashboard/, landing) comp/tui Terminal UI (ui-tui/ + tui_gateway/) needs-decision Awaiting maintainer decision before any implementation P3 Low — cosmetic, nice to have 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 sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages sweeper:risk-security-boundary Sweeper risk: may affect sandboxing, auth, credentials, or sensitive data type/feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants