Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .github/workflows/windows-recovery.yml
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,9 @@ on:
- 'packages/runtime-host/src/control/startup-diagnostic.ts'
- 'packages/runtime-host/src/operator/local-deployment-owner.ts'
- 'packages/runtime-host/src/operator/managed-deployment.ts'
- 'packages/runtime-host/src/peer-mesh/owner.ts'
- 'packages/runtime-host/src/peer-mesh/store.ts'
- 'packages/runtime-host/src/peer-reachability/owner.ts'
- 'packages/runtime-host/src/peer-reachability/publisher.ts'
- 'packages/runtime-host/src/protocol/host-status.ts'
- 'packages/runtime-host/src/protocol/skill-catalog.ts'
- 'packages/runtime-host/src/server/access-credential-store.ts'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,9 +119,17 @@ test('persists authenticated route rotation for reconnect and restart', async ()
const imported = await first.importInvitation(peerInvitation('guest-routes'), false, 'routes');
assert.equal(imported.kind, 'connected');
observePeerEndpoint({
peerId: '12D3KooWpeer',
routeHints: ['/ip4/198.51.100.2/udp/42000/quic-v1'],
coordinationRelays: ['/memory/fresh-relay'],
lease: {
version: 1,
peerId: '12D3KooWpeer',
revision: 2,
issuedAt: 1,
expiresAt: 2,
directRoutes: ['/ip4/198.51.100.2/udp/42000/quic-v1'],
coordinationRoutes: ['/memory/fresh-relay'],
},
publicKey: 'AA',
signature: 'AA',
});
await first.close();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,11 @@ test('enabling remote access hands the same root to one managed service before D
routeHints: ['/ip4/192.0.2.1/udp/41000/quic-v1'],
coordinationRelays: [],
};
const livePeer = {
...peer,
coordinationRelays: ['/dns4/relay.example/udp/443/quic-v1/p2p/12D3KooWrelay'],
};
const livePeer = peerReachability(
peer.peerId,
peer.routeHints,
['/dns4/relay.example/udp/443/quic-v1/p2p/12D3KooWrelay'],
);
const manager = {
async retireOwnedLocalHost() {
retired = true;
Expand Down Expand Up @@ -142,7 +143,12 @@ test('enabling remote access hands the same root to one managed service before D
assert.deepEqual(decodeRuntimeHostOwnerConnectionCode(result.connectionCode), {
name: decodeRuntimeHostOwnerConnectionCode(result.connectionCode).name,
rootId: 'a'.repeat(64),
transport: { kind: 'libp2p-direct', ...livePeer },
transport: {
kind: 'libp2p-direct',
peerId: livePeer.lease.peerId,
routeHints: livePeer.lease.directRoutes,
coordinationRelays: livePeer.lease.coordinationRoutes,
},
credential: 'pending-credential',
});
const lifecycle = JSON.parse(
Expand All @@ -166,10 +172,11 @@ test('shares the running Local Host endpoint instead of its persisted startup ro
routeHints: ['/ip4/192.0.2.1/udp/41000/quic-v1'],
coordinationRelays: [],
};
const livePeer = {
...configuredPeer,
coordinationRelays: ['/dns4/relay.example/udp/443/quic-v1/p2p/12D3KooWrelay'],
};
const livePeer = peerReachability(
configuredPeer.peerId,
configuredPeer.routeHints,
['/dns4/relay.example/udp/443/quic-v1/p2p/12D3KooWrelay'],
);
const service = createDesktopLocalRuntimeHostRemoteAccess({
ipcMain: { handle() {}, removeHandler() {} },
clientDataRoot,
Expand Down Expand Up @@ -212,7 +219,12 @@ test('shares the running Local Host endpoint instead of its persisted startup ro
const target = await service.createCollaborationConnectionTarget();
assert.deepEqual(target, {
name: target.name,
transport: { kind: 'libp2p-direct', ...livePeer },
transport: {
kind: 'libp2p-direct',
peerId: livePeer.lease.peerId,
routeHints: livePeer.lease.directRoutes,
coordinationRelays: livePeer.lease.coordinationRoutes,
},
});
});

Expand Down Expand Up @@ -964,6 +976,26 @@ async function writeManagedLifecycle(
);
}

function peerReachability(
peerId: string,
directRoutes: readonly string[],
coordinationRoutes: readonly string[],
) {
return {
lease: {
version: 1 as const,
peerId,
revision: 1,
issuedAt: 1,
expiresAt: 2,
directRoutes,
coordinationRoutes,
},
publicKey: Buffer.from('public').toString('base64url'),
signature: Buffer.from('signature').toString('base64url'),
};
}

function hostRegistration(
overrides: Partial<Pick<HostRegistration, 'rootId' | 'lifecycleMode'>> = {},
): HostRegistration {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -689,11 +689,12 @@ test('persists authenticated Owner routes imported through a connection code', a
routeHints: ['/ip4/192.0.2.8/udp/44001/quic-v1'],
coordinationRelays: ['/memory/stale-relay'],
};
const freshEndpoint = {
peerId: staleTransport.peerId,
routeHints: ['/ip4/198.51.100.9/udp/44002/quic-v1'],
coordinationRelays: ['/memory/fresh-relay'],
};
const freshEndpoint = peerReachability(
staleTransport.peerId,
2,
['/ip4/198.51.100.9/udp/44002/quic-v1'],
['/memory/fresh-relay'],
);
let observedIncarnation: string | undefined;
const service = createDesktopRuntimeHostProfileService({
clientDataRoot: root,
Expand Down Expand Up @@ -730,7 +731,12 @@ test('persists authenticated Owner routes imported through a connection code', a
assert.equal(persisted.profileIncarnationId, observedIncarnation);
assert.deepEqual(
persisted.profile.kind === 'remote' ? persisted.profile.transport : undefined,
{ kind: 'libp2p-direct', ...freshEndpoint },
{
kind: 'libp2p-direct',
peerId: freshEndpoint.lease.peerId,
routeHints: freshEndpoint.lease.directRoutes,
coordinationRelays: freshEndpoint.lease.coordinationRoutes,
},
);
const restarted = await resolveDesktopRuntimeHostStartup(root, { catalog });
assert.deepEqual(restarted.remotes, [persisted]);
Expand Down Expand Up @@ -773,11 +779,12 @@ test("keeps a managed Direct route on the SSH profile credential authority", asy
await managedServices.save(MANAGED_PROFILE, MANAGED_SERVICE);
const startup = await resolveDesktopRuntimeHostStartup(root, { catalog });
const activated: ResolvedRuntimeHostProfile[] = [];
const livePeer = {
peerId: "12D3KooWpeer",
routeHints: ["/ip4/192.0.2.9/udp/44002/quic-v1"],
coordinationRelays: ["/dns4/relay.example/udp/443/quic-v1/p2p/12D3KooWrelay"],
};
const livePeer = peerReachability(
'12D3KooWpeer',
2,
['/ip4/192.0.2.9/udp/44002/quic-v1'],
['/dns4/relay.example/udp/443/quic-v1/p2p/12D3KooWrelay'],
);
let exposeReadyState = false;
const service = createDesktopRuntimeHostProfileService({
clientDataRoot: root,
Expand Down Expand Up @@ -832,7 +839,12 @@ test("keeps a managed Direct route on the SSH profile credential authority", asy
await service.resolveCollaborationConnectionTarget(MANAGED_PROFILE),
{
name: MANAGED_PROFILE.name,
transport: { kind: "libp2p-direct", ...livePeer },
transport: {
kind: 'libp2p-direct',
peerId: livePeer.lease.peerId,
routeHints: livePeer.lease.directRoutes,
coordinationRelays: livePeer.lease.coordinationRoutes,
},
},
);
exposeReadyState = false;
Expand Down Expand Up @@ -1681,11 +1693,7 @@ function ready(target: ResolvedRuntimeHostProfile): RuntimeHostDesktopTargetStat

function readyWithPeerEndpoint(
target: ResolvedRuntimeHostProfile,
peerEndpoint: {
readonly peerId: string;
readonly routeHints: readonly string[];
readonly coordinationRelays: readonly string[];
},
peerEndpoint: ReturnType<typeof peerReachability>,
): RuntimeHostDesktopTargetState {
return {
epoch: `epoch-${target.profile.id}`,
Expand All @@ -1700,6 +1708,27 @@ function readyWithPeerEndpoint(
};
}

function peerReachability(
peerId: string,
revision = 1,
directRoutes: readonly string[] = ['/ip4/192.0.2.8/udp/44001/quic-v1'],
coordinationRoutes: readonly string[] = [],
) {
return {
lease: {
version: 1 as const,
peerId,
revision,
issuedAt: 1,
expiresAt: 2,
directRoutes,
coordinationRoutes,
},
publicKey: Buffer.from('public').toString('base64url'),
signature: Buffer.from(`signature-${revision}`).toString('base64url'),
};
}

function unavailable(
target: ResolvedRuntimeHostProfile,
error: Error,
Expand Down
67 changes: 45 additions & 22 deletions apps/desktop/src/main/runtime-host-boot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,14 +49,20 @@ import {
createClientRuntimeHostCredentialStore,
createClientRuntimeHostProfileCatalog,
createRuntimeHostCandidateLaunchBarrier,
createRuntimeHostPeerClientFromEnvironment,
LOCAL_RUNTIME_HOST_PROFILE,
loadOrCreateRuntimeHostClientInstanceId,
listRuntimeHostWslDistributions,
runtimeHostProfileAccess,
type ResolvedRuntimeHostProfile,
} from "@maka/runtime-host/client";
import { openRuntimeHostPeerMeshOwner } from '@maka/runtime-host/peer-mesh';
import {
openRuntimeHostPeerMeshComponent,
type RuntimeHostPeerMeshComponent,
} from '@maka/runtime-host/peer-mesh';
import {
openRuntimeHostPeerEndpointOwner,
type RuntimeHostPeerEndpointOwner,
} from '@maka/runtime-host/peer-reachability';
import type { WorkspaceTarget } from "@maka/runtime-host/protocol";
import { runtimeHostProfileUsesHostWorkspace } from "@maka/runtime-host/profile-kind";
import { createCredentialMcpOAuthStorage, McpClientManager } from "@maka/mcp";
Expand Down Expand Up @@ -264,33 +270,42 @@ const runtimeHostPeerConfiguration = await configureDesktopRuntimeHostPeerClient
resourcesPath: process.resourcesPath,
clientDataRoot: userDataDir,
});
let runtimeHostPeerOwner: Awaited<ReturnType<typeof openRuntimeHostPeerMeshOwner>> | undefined;
let runtimeHostPeerMesh: Awaited<ReturnType<typeof openRuntimeHostPeerMeshOwner>>['mesh'] | undefined;
let runtimeHostPeerClient:
| ReturnType<typeof createRuntimeHostPeerClientFromEnvironment>
| undefined;
let runtimeHostPeerEndpointOwner: RuntimeHostPeerEndpointOwner | undefined;
let runtimeHostPeerMeshComponent: RuntimeHostPeerMeshComponent | undefined;
let runtimeHostPeerMesh: RuntimeHostPeerMeshComponent['mesh'] | undefined;
let runtimeHostPeerClient: RuntimeHostPeerEndpointOwner['client'] | undefined;
if (runtimeHostPeerConfiguration) {
try {
runtimeHostPeerOwner = await openRuntimeHostPeerMeshOwner({
runtimeHostPeerEndpointOwner = await openRuntimeHostPeerEndpointOwner({
...runtimeHostPeerConfiguration,
dataRoot: join(userDataDir, 'peer-mesh'),
endpointKind: 'client',
onBackgroundReconcileError: (error) => {
console.error('[runtime-host] Peer Mesh background synchronization failed:', error);
onBackgroundReachabilityError: (error) => {
console.error('[runtime-host] peer reachability publication failed:', error);
},
});
runtimeHostPeerClient = runtimeHostPeerOwner.client;
runtimeHostPeerMesh = runtimeHostPeerOwner.mesh;
void runtimeHostPeerOwner.closed.catch((error) => {
runtimeHostPeerMesh = undefined;
console.error('[runtime-host] Peer Mesh stopped; Direct peer remains available:', error);
runtimeHostPeerClient = runtimeHostPeerEndpointOwner.client;
void runtimeHostPeerEndpointOwner.closed.catch((error) => {
console.error('[runtime-host] peer reachability publisher stopped:', error);
});
try {
runtimeHostPeerMeshComponent = await openRuntimeHostPeerMeshComponent({
dataRoot: join(userDataDir, 'peer-mesh'),
endpoint: runtimeHostPeerEndpointOwner,
endpointKind: 'client',
onBackgroundReconcileError: (error) => {
console.error('[runtime-host] Peer Mesh background synchronization failed:', error);
},
});
runtimeHostPeerMesh = runtimeHostPeerMeshComponent.mesh;
void runtimeHostPeerMeshComponent.closed.catch((error) => {
runtimeHostPeerMesh = undefined;
console.error('[runtime-host] Peer Mesh stopped; Direct peer remains available:', error);
});
} catch (error) {
console.error('[runtime-host] Peer Mesh is unavailable; continuing with Direct peer:', error);
}
} catch (error) {
console.error('[runtime-host] Peer Mesh is unavailable; continuing with Direct peer:', error);
runtimeHostPeerClient = createRuntimeHostPeerClientFromEnvironment(process.env, {
automaticRelayDiscovery: runtimeHostPeerConfiguration.automaticRelayDiscovery,
webRtcStunUrls: runtimeHostPeerConfiguration.webRtcStunUrls,
});
console.error('[runtime-host] Direct peer is unavailable:', error);
}
}
const runtimeHostDirectPeerAvailable = runtimeHostPeerClient !== undefined;
Expand Down Expand Up @@ -1926,7 +1941,15 @@ async function closeRuntimeHostDesktop(): Promise<void> {
.then(() => runtimeHostManager?.close());
const runtimeHostPeerShutdown = runtimeHostManagerShutdown
.catch(() => undefined)
.then(() => runtimeHostPeerOwner?.close() ?? runtimeHostPeerClient?.close());
.then(async () => {
const errors: unknown[] = [];
await runtimeHostPeerMeshComponent?.close().catch((error: unknown) => errors.push(error));
await runtimeHostPeerEndpointOwner?.close().catch((error: unknown) => errors.push(error));
if (errors.length === 1) throw errors[0];
if (errors.length > 1) {
throw new AggregateError(errors, 'Unable to close Desktop peer resources');
}
});
const results = await Promise.allSettled([
Promise.resolve().then(() => runtimeHostManagement.close()),
Promise.resolve().then(() => runtimeHostPeerMeshManagement.close()),
Expand Down
5 changes: 1 addition & 4 deletions apps/desktop/src/main/runtime-host-desktop-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -820,12 +820,9 @@ class RuntimeHostDesktopManagerImpl implements RuntimeHostDesktopManager {
const results = await Promise.allSettled(
[...this.#targets.values()].map((target) => this.#removeTarget(target)),
);
const peerResults = await Promise.allSettled(
this.#baseInput.peerClient ? [this.#baseInput.peerClient.close()] : [],
);
this.#baseInput.candidateLaunchBarrier?.release();
this.#ipcMain.close();
const failures = [...results, ...peerResults].filter(
const failures = results.filter(
(result): result is PromiseRejectedResult => result.status === 'rejected',
);
if (failures.length > 0) {
Expand Down
22 changes: 11 additions & 11 deletions apps/desktop/src/main/runtime-host-guest-session-mounts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -177,28 +177,27 @@ export function createDesktopGuestSessionMountService(input: {
if (
closed ||
mount.transport.kind !== 'libp2p-direct' ||
endpoint.peerId !== mount.transport.peerId ||
(endpoint.routeHints.length === 0 && endpoint.coordinationRelays.length === 0)
endpoint.lease.peerId !== mount.transport.peerId
) return;
void mutate(async () => {
if (removingMounts.has(mount.mountId)) return;
const current = await load();
const retained = current.get(mount.mountId);
if (
retained?.transport.kind !== 'libp2p-direct' ||
retained.transport.peerId !== endpoint.peerId ||
retained.transport.peerId !== endpoint.lease.peerId ||
(
sameStrings(retained.transport.routeHints, endpoint.routeHints) &&
sameStrings(retained.transport.coordinationRelays, endpoint.coordinationRelays)
sameStrings(retained.transport.routeHints, endpoint.lease.directRoutes) &&
sameStrings(retained.transport.coordinationRelays, endpoint.lease.coordinationRoutes)
)
) return;
const updated = decodeMount({
...retained,
transport: {
kind: 'libp2p-direct',
peerId: endpoint.peerId,
routeHints: endpoint.routeHints,
coordinationRelays: endpoint.coordinationRelays,
peerId: endpoint.lease.peerId,
routeHints: endpoint.lease.directRoutes,
coordinationRelays: endpoint.lease.coordinationRoutes,
},
});
await persist(new Map(current).set(mount.mountId, updated));
Expand Down Expand Up @@ -468,6 +467,10 @@ export function createDesktopGuestSessionMountService(input: {
};
}

function sameStrings(left: readonly string[], right: readonly string[]): boolean {
return left.length === right.length && left.every((value, index) => value === right[index]);
}

export function registerDesktopGuestSessionMountIpc(
ipcMain: Pick<Electron.IpcMain, 'handle' | 'removeHandler'>,
service: DesktopGuestSessionMountService,
Expand Down Expand Up @@ -573,9 +576,6 @@ function isPeerPathUnavailable(error: unknown): boolean {
return error.code === 'direct_path_unavailable' || error.code === 'transit_unavailable';
}

function sameStrings(left: readonly string[], right: readonly string[]): boolean {
return left.length === right.length && left.every((value, index) => value === right[index]);
}

function collaborationProgressForConnectionPhase(
phase: RuntimeHostConnectionPhase,
Expand Down
Loading
Loading