fix(desktop): stop spawning loopback serve children when the registry primary is remote (#91564, #90316)

'Make primary' on a registered remote/cloud/ssh gateway only rewrites
connections.json — the v1 config.mode stays 'local', so startHermes()
resolved no remote route and spawned a loopback 'hermes serve' the
desktop never uses (full MCP set duplicated, port squat, respawn on
poll). resolveDesktopRemoteRoute gains a lowest-precedence registry-
primary rung (source: 'registry', existing v1/env/profile precedence
untouched), and globalRemoteActive() now recognizes a remote registry
primary so local-entry routes force pooled local children instead of
delegating into a primary that dials remote. A 'local' registry
primary still resolves null — genuinely-local desktops unchanged, and
local-profile secondaries keep their forced-local pooled backends.
This commit is contained in:
Teknium
2026-08-26 07:50:17 -07:00
parent 62e2d6e1e4
commit 1e9a12a71f
3 changed files with 171 additions and 3 deletions
@@ -305,3 +305,90 @@ test('local route does not inherit an unrelated registry SSH connection', () =>
test('local config without overrides returns null', () => {
assert.equal(resolveDesktopRemoteRoute({ config: { mode: 'local' }, registry: registry('local', []) }), null)
})
// --- Registry-primary transport gating (#91564 / #90316) ---
//
// "Make primary" on a registered remote gateway only writes connections.json;
// the v1 config.mode stays 'local'. The route resolver must still expose that
// remote transport, or startHermes() spawns a loopback `hermes serve` the
// desktop never uses (duplicated MCP sets, port squat, respawn-on-poll).
test('falls back to a REMOTE registry primary when the v1 mode is local (#91564/#90316)', () => {
const route = resolveDesktopRemoteRoute({
config: { mode: 'local' },
profile: null,
registry: registry('gw-b', [
{ id: 'gw-b', kind: 'remote', label: 'Gateway B', url: 'https://gw-b.test', authMode: 'token', token: tokenB }
])
})
assert.equal(route?.kind, 'remote')
assert.equal(route?.source, 'registry')
assert.equal(route?.connectionId, 'gw-b')
assert.equal((route as any)?.url, 'https://gw-b.test')
assert.deepEqual((route as any)?.token, tokenB)
})
test('falls back to a CLOUD registry primary when the v1 mode is local', () => {
const route = resolveDesktopRemoteRoute({
config: { mode: 'local' },
profile: null,
registry: registry('cloud-1', [
{
id: 'cloud-1',
kind: 'cloud',
label: 'Hermes Cloud',
url: 'https://agent.hermes.cloud',
authMode: 'oauth',
org: 'nous'
}
])
})
assert.equal(route?.kind, 'cloud')
assert.equal(route?.source, 'registry')
assert.equal((route as any)?.authMode, 'oauth')
assert.equal((route as any)?.org, 'nous')
})
test('falls back to an SSH registry primary when the v1 mode is local', () => {
const route = resolveDesktopRemoteRoute({
config: { mode: 'local' },
profile: null,
registry: registry('spark', [
{ id: 'spark', kind: 'ssh', label: 'Spark', host: 'spark1', user: 'tek', port: 2222, token: tokenA }
])
})
assert.equal(route?.kind, 'ssh')
assert.equal(route?.source, 'registry')
assert.equal(route?.connectionId, 'spark')
assert.equal((route as any)?.ssh?.host, 'spark1')
assert.equal((route as any)?.ssh?.port, 2222)
})
test('a LOCAL registry primary keeps resolving local (null route)', () => {
const route = resolveDesktopRemoteRoute({
config: { mode: 'local' },
profile: null,
registry: registry('local', [
{ id: 'gw-b', kind: 'remote', label: 'Gateway B', url: 'https://gw-b.test', authMode: 'token', token: tokenB }
])
})
assert.equal(route, null)
})
test('the v1 global remote still outranks the registry primary', () => {
const route = resolveDesktopRemoteRoute({
config: { mode: 'remote', remote: { url: 'https://global.test', authMode: 'token', token: tokenA } },
profile: null,
registry: registry('gw-b', [
{ id: 'global', kind: 'remote', label: 'Global', url: 'https://global.test', token: tokenA },
{ id: 'gw-b', kind: 'remote', label: 'Gateway B', url: 'https://gw-b.test', authMode: 'token', token: tokenB }
])
})
assert.equal(route?.source, 'settings')
assert.equal((route as any)?.url, 'https://global.test')
})
+59 -2
View File
@@ -9,7 +9,7 @@ import {
import type { ConnectionRegistry } from './connection-registry'
import { matchingConnectionId, type StoredRoute } from './connection-route-identity'
type RouteSource = 'env' | 'profile' | 'settings'
type RouteSource = 'env' | 'profile' | 'registry' | 'settings'
interface SshRouteConfig {
host: string
@@ -133,7 +133,14 @@ export function resolveDesktopRemoteRoute({
}
if (!modeIsRemoteLike(config.mode)) {
return null
// Registry-primary fallback (#91564/#90316): "Make primary" on a
// registered remote/cloud/ssh gateway only rewrites connections.json —
// the v1 config.mode stays 'local'. Without this rung the primary boot
// resolves local and spawns a loopback `hermes serve` the desktop never
// uses (it dials the registry primary separately): duplicated MCP sets,
// port squat, and a respawn on every poll. A 'local' registry primary
// still resolves null, so genuinely-local desktops are untouched.
return resolveRegistryPrimaryRoute(registry)
}
const kind = config.mode === 'cloud' ? 'cloud' : 'remote'
@@ -153,3 +160,53 @@ export function resolveDesktopRemoteRoute({
matchingConnectionId(registry, route, 'primary')
)
}
/**
* Lowest-precedence rung: the v2 registry PRIMARY's own transport. Returns
* null unless the primary names a remote/cloud/ssh entry — i.e. only when the
* user explicitly made a non-local registered gateway their primary.
*/
function resolveRegistryPrimaryRoute(registry: ConnectionRegistry): DesktopRemoteRoute | null {
const primaryId = String(registry?.primary || '').trim()
if (!primaryId) {
return null
}
const entry = (registry.connections || []).find(connection => connection.id === primaryId)
if (!entry) {
return null
}
if (entry.kind === 'ssh') {
const ssh = normalizeSshConfig({ ...entry, mode: 'ssh' })
if (!ssh) {
return null
}
return { connectionId: entry.id, kind: 'ssh', source: 'registry', ssh, token: entry.token }
}
if (entry.kind !== 'remote' && entry.kind !== 'cloud') {
return null
}
const url = String(entry.url || '').trim()
if (!url) {
return null
}
return {
authMode: normAuthMode(entry.authMode),
connectionId: entry.id,
headers: entry.headers,
kind: entry.kind,
org: entry.kind === 'cloud' ? String(entry.org || '').trim() || undefined : undefined,
source: 'registry',
token: entry.token,
url
}
}
+25 -1
View File
@@ -10092,7 +10092,31 @@ function globalRemoteActive() {
const mode = readDesktopConnectionConfig().mode
return modeIsRemoteLike(mode) || mode === 'ssh'
if (modeIsRemoteLike(mode) || mode === 'ssh') {
return true
}
// Registry-primary transport (#91564/#90316): a registered remote/cloud/ssh
// gateway promoted to primary via connections.json makes the primary
// backend remote even while the v1 config.mode still says 'local'. Every
// consumer of this flag ("one remote host serves every profile") must see
// that, or the local-entry routes delegate into a primary that now dials
// remote — respawning the exact loopback children the resolver rung in
// desktop-remote-route.ts eliminates.
return registryPrimaryIsRemote()
}
// True when the v2 registry PRIMARY names a non-local connection. Mirrors the
// registry fallback rung in resolveDesktopRemoteRoute.
function registryPrimaryIsRemote() {
try {
const registry = readDesktopConnectionsRegistry()
const entry = registry.connections.find(c => c.id === registry.primary)
return Boolean(entry && (entry.kind === 'remote' || entry.kind === 'cloud' || entry.kind === 'ssh'))
} catch {
return false
}
}
// True when the PRIMARY profile's backend resolves to a remote/cloud host —