Skip to content

fix(desktop): profiles unusable over a global SSH remote — don't dial doomed per-profile sockets - #85665

Merged
teknium1 merged 1 commit into
mainfrom
fix/global-remote-profile-socket
Aug 13, 2026
Merged

fix(desktop): profiles unusable over a global SSH remote — don't dial doomed per-profile sockets#85665
teknium1 merged 1 commit into
mainfrom
fix/global-remote-profile-socket

Conversation

@teknium1

Copy link
Copy Markdown
Contributor

What

Fixes "Hermes gateway is not connected" when clicking into any non-primary profile while connected to a global SSH/remote gateway.

Reported by a Bot Mode user (@signalbydel): the roster (primary socket) shows all agents, but opening any of them lands on the gateway-disconnected screen.

Root cause

ensureGatewayForProfile() / openGatewayForProfile() unconditionally dial a per-profile secondary WebSocket for any non-primary profile. Under a global remote, the electron routing table (resolveProfileBackendRoute, case 3) routes every profile to the SHARED primary backend — one host, scoped per request; the descriptor getConnection() returns is the primary connection tagged with profile. There is no per-profile backend to dial: over SSH the tunnel/ticket belong to the primary connection, so the duplicate dial fails and the CLOSED socket becomes the active gateway. Every RPC then throws "Hermes gateway is not connected" even though the healthy primary socket is right there. The renderer's socket registry was contradicting the routing table it sits on top of.

Fix

Detect the shared-primary route (descriptor tagged with profile — only the global-remote share does this; own-remote-override and local pooled descriptors are never tagged) and activate the primary socket instead of dialing a duplicate. $activeGatewayProfile still moves to the clicked profile, so ?profile= request scoping and profile-aware surfaces behave identically. The hover pre-warm becomes a no-op on this route for the same reason.

Local multi-profile (pooled backends) and per-profile remote overrides are untouched — pinned by the second test.

Verification

  • New gateway-shared-remote.test.ts: shared descriptor → primary socket activated, no dial; untagged descriptor → pooled path still dials. 2/2 pass.
  • Existing behavior unchanged for the primary profile fast path.

…he shared global-remote primary

Under a global SSH/remote gateway, resolveProfileBackendRoute routes every
profile to the shared primary backend (case 3) and getConnection() returns
the primary descriptor tagged with the profile. ensureGatewayForProfile
still dialed a per-profile secondary socket at that descriptor; over SSH
the duplicate dial fails (per-backend tunnel/ticket) and the closed socket
became the ACTIVE gateway — every profile except the primary showed
'Hermes gateway is not connected' even though the primary socket was open.

Detect the shared-primary route and activate the primary socket instead;
$activeGatewayProfile still tracks the selected profile so per-request
?profile= scoping is unchanged. Hover pre-warm no-ops on this route.
Local pooled profiles and per-profile remote overrides are untouched
(pinned by test).
@github-actions

github-actions Bot commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

૮ >ﻌ< ა ci review

ran on 4d4500d — fix(desktop): don't dial per-profile sockets for profiles se

⚠️ Warnings

OSV vulnerability scan · View job

5 known vulnerabilities found in pinned dependencies.

How to fix:

Review the findings in the Security tab. Update the affected dependencies if a patched version is available.


debug info

CI timings

CI timings · View report · View job

Wall time 9m25s vs 10m40s (-11.7%). 6 job(s) slower, 7 faster, 3 unchanged.

  • JS & TS checks / tests-js / check: +23.0s
  • JS & TS checks / apps/desktop / check:lint: -17.0s
  • JS & TS checks / apps/desktop / check:test:ui: +14.0s
  • JS & TS checks / apps/desktop / check:test:desktop:platforms: +9.0s
  • JS & TS checks / web / check: -6.0s

@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists comp/desktop Electron desktop app (apps/desktop/*) backend/ssh SSH remote execution labels Aug 13, 2026
@teknium1
teknium1 merged commit d16e236 into main Aug 13, 2026
38 checks passed
@teknium1
teknium1 deleted the fix/global-remote-profile-socket branch August 13, 2026 22:06
OutThisLife added a commit that referenced this pull request Aug 14, 2026
…0.20.1

sharedPrimaryRoute() inferred "served by the shared primary backend" from
the mere presence of connection.profile. But pooled backends (a local named
profile, or a per-profile remote override) also carry `profile` so their
WebSocket URL mints against the right backend. Both descriptors looked the
same, so ensureGatewayForProfile() took the shared-primary branch for a
pooled profile and never dialed its socket — Desktop stayed on the default
profile's socket even though the sidebar (REST) listed the right sessions.

Regression from #85665 (d16e236). Tag only the true shared-primary
descriptor with an explicit `sharedPrimary: true` marker in ensureBackend()
and check that marker instead of `profile`. Covers both the local-pool and
remote-override routes — the whole bug class, not one path.

Test asserts both sides of the invariant: a { profile, sharedPrimary: true }
descriptor activates the primary socket without dialing, and a pooled
descriptor carrying { profile } dials its own exact WebSocket URL.

Supersedes #85750, #85778, #85932
Fixes #85777

Co-authored-by: Tigrannnnnnn <122704900+Tigrannnnnnn@users.noreply.github.com>
Co-authored-by: Don Tuttle <11698271+wdon@users.noreply.github.com>
Co-authored-by: plcunha <145560011+plcunha@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backend/ssh SSH remote execution comp/desktop Electron desktop app (apps/desktop/*) P2 Medium — degraded but workaround exists type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants