fix(desktop): reuse migrated primary SSH backend

Avoid opening a second SSH lifecycle when a migrated registry request targets the same primary/default backend already booted through the legacy route. Compare effective SSH configuration for representation-only drift, treat empty and default as the same root profile, and keep named profiles isolated.
This commit is contained in:
Jaime Marques
2026-08-23 20:20:38 +01:00
committed by Teknium
parent 1b8f3eda38
commit 3263ca2af6
5 changed files with 320 additions and 19 deletions
@@ -32,6 +32,7 @@ import {
removeConnection,
resolvedConnectionId,
resolveRegistryLocalRoute,
reuseMatchingPrimarySshBackend,
setConnectionLaunchMode,
setLastUsedConnection,
setPrimaryConnection,
@@ -59,6 +60,148 @@ test('labelSlug kebab-cases and never returns empty for non-empty input', () =>
assert.equal(labelSlug('!!!'), 'connection')
})
test('matching primary/default SSH route reuses the existing descriptor once', async () => {
const registry = migrateV1ToRegistry({
mode: 'ssh',
remote: { mode: 'ssh', host: 'build-host', user: 'alice' },
profiles: {}
})
const source = registry.connections.find(connection => connection.id === registry.primary)
const descriptor = {
mode: 'remote' as const,
remoteKind: 'ssh' as const,
ssh: {
effectiveConfigFingerprint: 'same-effective-config',
host: 'build-host',
keyPath: '~/.ssh/id_ed25519',
remoteProfile: 'default',
user: 'alice'
}
}
let ensureCalls = 0
let fingerprintCalls = 0
assert.equal(source?.kind, 'ssh')
assert.equal(
await reuseMatchingPrimarySshBackend({
connectionId: registry.primary,
effectiveFingerprint: async () => {
fingerprintCalls += 1
return 'same-effective-config'
},
ensurePrimary: async () => {
ensureCalls += 1
return descriptor
},
profile: 'default',
registry,
source: source!
}),
descriptor
)
assert.equal(ensureCalls, 1)
assert.equal(fingerprintCalls, 1)
})
test('non-default or non-primary SSH routes do not resolve the primary backend', async () => {
const registry = migrateV1ToRegistry({
mode: 'ssh',
remote: { mode: 'ssh', host: 'build-host', user: 'alice' },
profiles: {}
})
const source = registry.connections.find(connection => connection.id === registry.primary)!
let ensureCalls = 0
const opts = {
effectiveFingerprint: async () => 'same',
ensurePrimary: async () => {
ensureCalls += 1
return { mode: 'remote' as const, remoteKind: 'ssh' as const }
},
registry,
source
}
assert.equal(
await reuseMatchingPrimarySshBackend({ ...opts, connectionId: registry.primary, profile: 'researcher' }),
null
)
assert.equal(
await reuseMatchingPrimarySshBackend({ ...opts, connectionId: LOCAL_CONNECTION_ID, profile: 'default' }),
null
)
assert.equal(ensureCalls, 0)
})
test('primary SSH reuse rejects a descriptor with different effective dialing config', async () => {
const registry = migrateV1ToRegistry({
mode: 'ssh',
remote: { mode: 'ssh', host: 'build-host', user: 'alice' },
profiles: {}
})
const source = registry.connections.find(connection => connection.id === registry.primary)!
assert.equal(
await reuseMatchingPrimarySshBackend({
connectionId: registry.primary,
effectiveFingerprint: async () => 'registry-config',
ensurePrimary: async () => ({
mode: 'remote',
remoteKind: 'ssh',
ssh: {
effectiveConfigFingerprint: 'active-config',
host: 'other-host',
remoteProfile: ''
}
}),
profile: 'default',
registry,
source
}),
null
)
})
test('primary SSH reuse rejects a descriptor with a different remote Hermes path', async () => {
const registry = migrateV1ToRegistry({
mode: 'ssh',
remote: { mode: 'ssh', host: 'build-host', remoteHermesPath: '/srv/hermes', user: 'alice' },
profiles: {}
})
const source = registry.connections.find(connection => connection.id === registry.primary)!
assert.equal(
await reuseMatchingPrimarySshBackend({
connectionId: registry.primary,
effectiveFingerprint: async () => 'same-effective-config',
ensurePrimary: async () => ({
mode: 'remote',
remoteKind: 'ssh',
ssh: {
effectiveConfigFingerprint: 'same-effective-config',
host: 'build-host',
remoteHermesPath: '/opt/hermes',
remoteProfile: '',
user: 'alice'
}
}),
profile: 'default',
registry,
source
}),
null
)
})
test('resolvedConnectionId identifies local and migrated remote descriptors', () => {
const registry = migrateV1ToRegistry({
mode: 'local',
@@ -185,6 +185,7 @@ export interface RegistryLocalRoute {
}
export interface ResolvedConnectionSshDescriptor {
effectiveConfigFingerprint?: string
host?: string
keyPath?: string
port?: number
@@ -333,6 +334,53 @@ export function resolvedConnectionId(
return matchingConnectionId(registry, route, 'unique') ?? null
}
export interface ReuseMatchingPrimarySshBackendOptions {
connectionId: null | string | undefined
effectiveFingerprint: (source: RegistryConnection) => Promise<string>
ensurePrimary: () => Promise<ResolvedConnectionDescriptor>
profile: null | string | undefined
registry: ConnectionRegistry
source: RegistryConnection
}
/**
* Reuse the already-booted v1 window SSH backend only when its actual dialing
* identity matches the registry primary. Guards run before either async
* dependency so secondary profiles and sources never bootstrap the primary.
*/
export async function reuseMatchingPrimarySshBackend({
connectionId,
effectiveFingerprint,
ensurePrimary,
profile,
registry,
source
}: ReuseMatchingPrimarySshBackendOptions): Promise<null | ResolvedConnectionDescriptor> {
const id = String(connectionId ?? '').trim()
const profileKey = String(profile ?? '').trim() || 'default'
if (profileKey !== 'default' || !id || id !== registry.primary || source.id !== id || source.kind !== 'ssh') {
return null
}
const sourceFingerprint = String(await effectiveFingerprint(source)).trim()
const descriptor = await ensurePrimary()
const activeSsh = descriptor.mode === 'remote' && descriptor.remoteKind === 'ssh' ? descriptor.ssh : null
const rootProfile = (value: unknown) => String(value || '').trim() || 'default'
if (
!sourceFingerprint ||
!activeSsh ||
sourceFingerprint !== String(activeSsh.effectiveConfigFingerprint || '').trim() ||
String(source.remoteHermesPath || '').trim() !== String(activeSsh.remoteHermesPath || '').trim() ||
rootProfile(source.remoteProfile) !== rootProfile(activeSsh.remoteProfile)
) {
return null
}
return descriptor
}
function normalizedSshTarget(route: { host?: unknown; port?: unknown; user?: unknown }): null | string {
const ssh = normalizeSshConfig({ ...route, mode: 'ssh' })
+95 -19
View File
@@ -137,6 +137,7 @@ import {
removeConnection,
resolvedConnectionId,
resolveRegistryLocalRoute,
reuseMatchingPrimarySshBackend,
setConnectionLaunchMode,
setLastUsedConnection,
setPrimaryConnection,
@@ -9669,7 +9670,7 @@ async function reachablePreviewUrl(webContentsId: number, rawUrl: string): Promi
}
}
function effectiveSshConfigFingerprint(sshConfig) {
async function effectiveSshConfigFingerprint(sshConfig) {
const ssh =
process.platform === 'win32'
? path.join(process.env.SystemRoot || 'C:\\Windows', 'System32', 'OpenSSH', 'ssh.exe')
@@ -9686,14 +9687,14 @@ function effectiveSshConfigFingerprint(sshConfig) {
}
args.push('--', sshConfig.user ? `${sshConfig.user}@${sshConfig.host}` : sshConfig.host)
const output = execFileSync(ssh, args, { encoding: 'utf8', timeout: 10_000, windowsHide: true })
const output = await execText(ssh, args, { timeout: 10_000 })
return crypto.createHash('sha256').update(output).digest('hex')
}
async function bootstrapSshConnection(profile, sshConfig, reuseToken, source) {
async function bootstrapSshConnection(profile, sshConfig, reuseToken, source, resolvedEffectiveFingerprint?) {
const scope = sshScopeKey(profile)
const effectiveConfigFingerprint = effectiveSshConfigFingerprint(sshConfig)
const effectiveConfigFingerprint = resolvedEffectiveFingerprint || (await effectiveSshConfigFingerprint(sshConfig))
const resolvedConfig = { ...sshConfig, effectiveConfigFingerprint }
const fingerprint = sshConfigFingerprint(scope, resolvedConfig)
@@ -9835,7 +9836,19 @@ async function bootstrapSshConnectionInner(profile, sshConfig, reuseToken, sourc
result.ownershipId
)
return { ...connection, remoteHermesVersion: result.hermesVersion || '' }
return {
...connection,
remoteHermesVersion: result.hermesVersion || '',
ssh: {
effectiveConfigFingerprint: sshConfig.effectiveConfigFingerprint,
host: sshConfig.host,
keyPath: sshConfig.keyPath,
port: sshConfig.port,
remoteHermesPath: sshConfig.remoteHermesPath,
remoteProfile: sshConfig.remoteProfile,
user: sshConfig.user
}
}
}
function persistSshConnectionToken(profile, source, token) {
@@ -10492,6 +10505,64 @@ async function ensureRegistryBackend(connectionId, profile) {
throw new Error(`No connection with id "${id}".`)
}
const profileKey = String(profile ?? '').trim() || 'default'
let resolvedRegistrySshConfig
let registryEffectiveFingerprintPromise: null | Promise<string> = null
const resolveRegistrySshConfig = () => {
if (source.kind !== 'ssh') {
return null
}
if (!resolvedRegistrySshConfig) {
resolvedRegistrySshConfig = normalizeSshConfig({
mode: 'ssh',
host: source.host,
user: source.user,
port: source.port,
keyPath: source.keyPath,
remoteHermesPath: source.remoteHermesPath,
remoteProfile: source.remoteProfile || (profileKey === 'default' ? '' : profileKey)
})
}
return resolvedRegistrySshConfig
}
const resolveRegistryEffectiveFingerprint = () => {
if (!registryEffectiveFingerprintPromise) {
const sshConfig = resolveRegistrySshConfig()
registryEffectiveFingerprintPromise = sshConfig
? effectiveSshConfigFingerprint(sshConfig)
: Promise.reject(new Error(`SSH connection "${source.label}" has no host configured.`))
}
return registryEffectiveFingerprintPromise
}
// The v2 registry is migrated from (but intentionally coexists with) the
// v1 primary connection config. Reuse the already-booted primary descriptor
// when both identities match; otherwise a default-profile registry request
// opens a second SSH dashboard under a different scope and the competing
// lifecycle probes repeatedly tear down each other's tunnel.
const primary = await reuseMatchingPrimarySshBackend({
connectionId: id,
effectiveFingerprint: resolveRegistryEffectiveFingerprint,
ensurePrimary: () => ensureBackend(profile),
profile,
registry,
source
})
if (primary) {
return {
...primary,
profile: profileKey,
connectionId: id
}
}
if (source.kind === 'local') {
// The registry's 'local' entry means THIS machine's runtime — always.
// ensureBackend() follows the v1 routing table, which resolves to a
@@ -10502,8 +10573,6 @@ async function ensureRegistryBackend(connectionId, profile) {
// the v1 route is genuinely local; otherwise spawn/reuse a forced-local
// child pooled under the composite 'conn:local::<profile>' key so it
// can't collide with the v1 remote descriptor cached at the bare key.
const profileKey = String(profile ?? '').trim() || 'default'
profileDeletionGate.assertCanStart(profileKey)
const localRoute = resolveRegistryLocalRoute(profileKey, {
@@ -10613,7 +10682,14 @@ async function ensureRegistryBackend(connectionId, profile) {
remoteBaseUrl: null
}
entry.connectionPromise = connectRegistryBackend(source, profile, key, entry).catch(error => {
entry.connectionPromise = connectRegistryBackend(
source,
profile,
key,
entry,
resolveRegistrySshConfig(),
source.kind === 'ssh' ? resolveRegistryEffectiveFingerprint() : null
).catch(error => {
if (backendPool.get(key) === entry) {
backendPool.delete(key)
}
@@ -10629,7 +10705,14 @@ async function ensureRegistryBackend(connectionId, profile) {
// Dial a non-local registry connection for one profile. Never spawns a local
// child (entry.process stays null — stopPoolBackend/evict already tolerate
// that shape from remote per-profile overrides).
async function connectRegistryBackend(source, profile, key, poolEntry) {
async function connectRegistryBackend(
source,
profile,
key,
poolEntry,
resolvedSshConfig?,
resolvedEffectiveFingerprint?: null | Promise<string>
) {
const profileKey = String(profile ?? '').trim() || 'default'
if (source.kind === 'ssh') {
@@ -10637,15 +10720,7 @@ async function connectRegistryBackend(source, profile, key, poolEntry) {
// pair owns its own tunnel + remote dashboard; the profile that re-homes
// the REMOTE process is the entry's remoteProfile or the requested one —
// never the composite string.
const sshConfig = normalizeSshConfig({
mode: 'ssh',
host: source.host,
user: source.user,
port: source.port,
keyPath: source.keyPath,
remoteHermesPath: source.remoteHermesPath,
remoteProfile: source.remoteProfile || (profileKey === 'default' ? '' : profileKey)
})
const sshConfig = resolvedSshConfig
if (!sshConfig) {
throw new Error(`SSH connection "${source.label}" has no host configured.`)
@@ -10655,7 +10730,8 @@ async function connectRegistryBackend(source, profile, key, poolEntry) {
key,
sshConfig,
decryptDesktopSecret(source.token),
`registry:${source.id}`
`registry:${source.id}`,
resolvedEffectiveFingerprint ? await resolvedEffectiveFingerprint : undefined
)
poolEntry.remoteBaseUrl = connection.baseUrl
@@ -48,6 +48,30 @@ test('primary remote descriptor preserves a resolved registry connection id', ()
assert.equal(connection.isFullscreen, false)
})
test('primary remote descriptor preserves the effective SSH dialing identity', () => {
const ssh = {
effectiveConfigFingerprint: 'effective-config',
host: 'build-host',
remoteHermesPath: '/srv/hermes',
remoteProfile: 'default',
user: 'alice'
}
const connection = createPrimaryRemoteConnection(
{
baseUrl: 'http://127.0.0.1:49152',
remoteKind: 'ssh',
ssh,
token: 'secret',
wsUrl: 'ws://127.0.0.1:49152/api/ws'
},
[],
{}
)
assert.equal(connection.ssh, ssh)
})
test('primary remote descriptor keeps legacy unregistered routes unqualified', () => {
const connection = createPrimaryRemoteConnection(
{
@@ -20,6 +20,15 @@ interface ResolvedPrimaryRemote {
remoteHost?: string
remoteKind?: 'cloud' | 'ssh' | 'url'
source?: string
ssh?: {
effectiveConfigFingerprint?: string
host?: string
keyPath?: string
port?: number
remoteHermesPath?: string
remoteProfile?: string
user?: string
}
token: unknown
wsUrl: string
}
@@ -43,6 +52,7 @@ export function createPrimaryRemoteConnection<State extends object>(
remoteKind: remote.remoteKind,
remoteHermesVersion: remote.remoteHermesVersion,
...(remote.connectionId ? { connectionId: remote.connectionId } : {}),
...(remote.ssh ? { ssh: remote.ssh } : {}),
token: remote.token,
wsUrl: remote.wsUrl,
logs,