Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import {
encodeRuntimeHostAccessManagementFrame,
encodeRuntimeHostServiceManagementFrame,
encodeRuntimeHostSetupFrame,
encodeRuntimeHostPeerMeshManagementFrame,
runtimeHostAccessCredentialFingerprint,
RUNTIME_HOST_OPERATOR_PEER_MANAGEMENT_CAPABILITY,
RUNTIME_HOST_SETUP_FRAME_PREFIX,
Expand Down Expand Up @@ -637,6 +638,62 @@ test('keeps a prepared access credential out of the SSH terminal projection', as
await harness.terminal.close();
});

test('sends a Mesh invitation only after the authenticated remote operator requests it', async () => {
const harness = createHarness('pending');
const invitation = JSON.stringify({ secret: 'one-time-mesh-secret' });
const management = harness.terminal.runPeerMeshManagement({
destination: 'operator@example.com',
operatorPath: '/home/operator/.local/share/maka/operator',
action: 'join',
invitation,
expectedTarget: {
serviceId: 'b'.repeat(64),
rootPath: '/srv/maka',
rootId: 'a'.repeat(64),
deploymentId: '00000000-0000-4000-8000-000000000001',
},
});
await waitFor(() => harness.pty.hasDataListener());
const command = harness.launchArgs.at(-1)?.at(-1) ?? '';
assert.match(command, /mesh.*join.*--framed/u);
assert.doesNotMatch(command, /one-time-mesh-secret/u);
assert.deepEqual(harness.pty.writes, []);

harness.pty.emitData(
encodeRuntimeHostPeerMeshManagementFrame({ kind: 'input', action: 'join' }),
);
assert.deepEqual(harness.pty.writes, [`${invitation}\r`]);
harness.pty.emitData(
encodeRuntimeHostPeerMeshManagementFrame({
kind: 'result',
action: 'join',
result: {
localPeerId: 'peer-b',
available: true,
meshes: [
{
meshId: 'mesh-id',
role: 'member',
authorityPeerId: 'peer-a',
revision: 2,
closed: false,
members: [
{ peerId: 'peer-a', state: 'route_available', expiresAt: Date.now() + 60_000 },
{ peerId: 'peer-b', state: 'local' },
],
pendingInvitationCount: 0,
},
],
},
}),
);
harness.pty.exit(0);

assert.equal((await management).kind, 'result');
assert.doesNotMatch(JSON.stringify(harness.events), /one-time-mesh-secret/u);
await harness.terminal.close();
});

test('rejects a framed service result for a different action', async () => {
const harness = createHarness('pending');
const management = harness.terminal.runServiceManagement({
Expand Down Expand Up @@ -886,6 +943,7 @@ class FakePty {
deferKill = false;
exitOnForceKill = false;
readonly killSignals: Array<string | undefined> = [];
readonly writes: string[] = [];
readonly #dataListeners = new Set<(data: string) => void>();
readonly #exitListeners = new Set<(event: { exitCode: number; signal: number }) => void>();
#resolveExit!: () => void;
Expand Down Expand Up @@ -922,7 +980,9 @@ class FakePty {
this.#resolveExit();
}

write(): void {}
write(data: string): void {
this.writes.push(data);
}
resize(): void {}
kill(signal?: string): void {
this.killSignals.push(signal);
Expand Down
38 changes: 34 additions & 4 deletions apps/desktop/src/main/runtime-host-boot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ import {
loadOrCreateRuntimeHostClientInstanceId,
listRuntimeHostWslDistributions,
} from "@maka/runtime-host/client";
import { openRuntimeHostPeerMeshOwner } from '@maka/runtime-host/peer-mesh';
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 @@ -181,6 +182,7 @@ import { createDesktopRuntimeHostLocalOperator } from './runtime-host-local-oper
import { createDesktopLocalRuntimeHostRemoteAccess } from './runtime-host-local-remote-access.js';
import { createDesktopRuntimeHostOnboarding } from "./runtime-host-onboarding.js";
import { createDesktopRuntimeHostManagement } from "./runtime-host-management.js";
import { createDesktopRuntimeHostPeerMeshManagement } from './runtime-host-peer-mesh-management.js';
import { registerRuntimeHostOAuthIpc } from "./runtime-host-oauth-ipc-main.js";
import { RuntimeHostOAuthPresentation } from "./runtime-host-oauth-presentation.js";
import { registerRuntimeHostPermissionsIpc } from "./runtime-host-permissions-ipc-main.js";
Expand Down Expand Up @@ -221,15 +223,35 @@ await resolveShellEnv();
const MANAGED_UPDATE_RECONNECT_TIMEOUT_MS = 10_000;
const buildInfo = resolveBuildInfo(app.isPackaged, app.getAppPath());
const userDataDir = app.getPath("userData");
const runtimeHostDirectPeerAvailable = await configureDesktopRuntimeHostPeerClient({
const runtimeHostPeerConfiguration = await configureDesktopRuntimeHostPeerClient({
isPackaged: app.isPackaged,
appPath: app.getAppPath(),
resourcesPath: process.resourcesPath,
clientDataRoot: userDataDir,
});
const runtimeHostPeerClient = runtimeHostDirectPeerAvailable
? createRuntimeHostPeerClientFromEnvironment()
: undefined;
let runtimeHostPeerOwner: Awaited<ReturnType<typeof openRuntimeHostPeerMeshOwner>> | undefined;
let runtimeHostPeerMesh: Awaited<ReturnType<typeof openRuntimeHostPeerMeshOwner>>['mesh'] | undefined;
let runtimeHostPeerClient:
| ReturnType<typeof createRuntimeHostPeerClientFromEnvironment>
| undefined;
if (runtimeHostPeerConfiguration) {
try {
runtimeHostPeerOwner = await openRuntimeHostPeerMeshOwner({
...runtimeHostPeerConfiguration,
dataRoot: join(userDataDir, 'peer-mesh'),
});
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);
});
} catch (error) {
console.error('[runtime-host] Peer Mesh is unavailable; continuing with Direct peer:', error);
runtimeHostPeerClient = createRuntimeHostPeerClientFromEnvironment();
}
}
const runtimeHostDirectPeerAvailable = runtimeHostPeerClient !== undefined;
const runtimeHostClientInstanceId = await loadOrCreateRuntimeHostClientInstanceId(
join(userDataDir, "runtime-host-client.json"),
);
Expand Down Expand Up @@ -542,6 +564,12 @@ const runtimeHostManagement = createDesktopRuntimeHostManagement({
runAccessManagement: runtimeHostSshTerminal.runAccessManagement,
cleanupManagedDeployment: runtimeHostSshTerminal.cleanupManagedDeployment,
});
const runtimeHostPeerMeshManagement = createDesktopRuntimeHostPeerMeshManagement({
ipcMain,
localMesh: () => runtimeHostPeerMesh,
profiles: runtimeHostProfileService,
runRemote: runtimeHostSshTerminal.runPeerMeshManagement,
});
const defaultRuntimeHostRecovery = createRuntimeHostDefaultRecovery({
defaultProfileId: () =>
runtimeHostManager?.defaultProfileId() ??
Expand Down Expand Up @@ -1646,7 +1674,9 @@ async function closeRuntimeHostDesktop(): Promise<void> {
permissionOverlay.dismiss();
const results = await Promise.allSettled([
Promise.resolve().then(() => runtimeHostManagement.close()),
Promise.resolve().then(() => runtimeHostPeerMeshManagement.close()),
runtimeHostManager?.close(),
runtimeHostPeerOwner?.close() ?? runtimeHostPeerClient?.close(),
runtimeHostOnboarding.close(),
localRuntimeHostRemoteAccess.close(),
runtimeHostSetupPackage.close(),
Expand Down
18 changes: 10 additions & 8 deletions apps/desktop/src/main/runtime-host-peer-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,15 @@ export async function configureDesktopRuntimeHostPeerClient(input: {
readonly resourcesPath: string;
readonly clientDataRoot: string;
readonly environment?: NodeJS.ProcessEnv;
}): Promise<boolean> {
}): Promise<{ readonly nativePath: string; readonly keyPath: string } | undefined> {
const environment = input.environment ?? process.env;
const explicitNativePath = environment.MAKA_RUNTIME_HOST_PEER_NATIVE_PATH?.trim();
const explicitKeyPath = environment.MAKA_RUNTIME_HOST_PEER_KEY_PATH?.trim();
if (explicitNativePath || explicitKeyPath) return Boolean(explicitNativePath && explicitKeyPath);
if (explicitNativePath || explicitKeyPath) {
return explicitNativePath && explicitKeyPath
? { nativePath: explicitNativePath, keyPath: explicitKeyPath }
: undefined;
}
const nativePath = input.isPackaged
? join(input.resourcesPath, 'runtime-host-peer', NATIVE_FILE)
: join(
Expand All @@ -48,12 +52,10 @@ export async function configureDesktopRuntimeHostPeerClient(input: {
try {
await access(nativePath);
} catch {
return false;
return undefined;
}
environment.MAKA_RUNTIME_HOST_PEER_NATIVE_PATH = nativePath;
environment.MAKA_RUNTIME_HOST_PEER_KEY_PATH = join(
input.clientDataRoot,
'runtime-host-client.peer.key',
);
return true;
const keyPath = join(input.clientDataRoot, 'runtime-host-client.peer.key');
environment.MAKA_RUNTIME_HOST_PEER_KEY_PATH = keyPath;
return { nativePath, keyPath };
}
Loading
Loading