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
1 change: 1 addition & 0 deletions apps/desktop/src/main/runtime-host-boot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -626,6 +626,7 @@ const runtimeHostPeerMeshManagement = createDesktopRuntimeHostPeerMeshManagement
localMesh: () => runtimeHostPeerMesh,
localHost: localRuntimeHostRemoteAccess,
runLocal: localRuntimeHostOperator.runPeerMesh,
liveHost: (profileId) => runtimeHostManager?.current(profileId)?.candidate?.client,
profiles: runtimeHostProfileService,
runRemote: runtimeHostSshTerminal.runPeerMeshManagement,
});
Expand Down
175 changes: 146 additions & 29 deletions apps/desktop/src/main/runtime-host-peer-mesh-management.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,11 @@
*/

import type { IpcMain } from 'electron';
import { LOCAL_RUNTIME_HOST_PROFILE } from '@maka/runtime-host/client';
import type { PeerMeshNode } from '@maka/runtime-host/peer-mesh';
import {
decodePeerMeshInvitation,
type PeerMeshInvitationV1,
type PeerMeshInvitationResult,
type PeerMeshQueryResult,
} from '@maka/runtime-host/protocol';
Expand All @@ -34,6 +36,7 @@ import type {
createDesktopRuntimeHostSshTerminal,
} from './runtime-host-ssh-terminal.js';
import type { createDesktopRuntimeHostLocalOperator } from './runtime-host-local-operator.js';
import type { DesktopRuntimeHostClient } from './runtime-host-client.js';
import type {
DesktopRuntimeHostLocalManagementTarget,
DesktopLocalRuntimeHostRemoteAccess,
Expand All @@ -48,7 +51,7 @@ interface ManagedPeerMeshCommand {
readonly action: PeerMeshAction;
readonly meshId?: string | null;
readonly peerId?: string;
readonly invitation?: string;
readonly invitation?: PeerMeshInvitationV1;
readonly displayName?: string | null;
readonly signal?: AbortSignal;
}
Expand All @@ -60,6 +63,9 @@ export function createDesktopRuntimeHostPeerMeshManagement(input: {
readonly localMesh?: () => PeerMeshNode | undefined;
readonly localHost: Pick<DesktopLocalRuntimeHostRemoteAccess, 'getSnapshot' | 'inspectManaged'>;
readonly runLocal: LocalOperator['runPeerMesh'];
readonly liveHost: (
profileId: string,
) => Pick<DesktopRuntimeHostClient, 'request'> | undefined;
readonly profiles: Pick<DesktopRuntimeHostProfileService, 'resolveManagedService'>;
readonly runRemote: SshTerminal['runPeerMeshManagement'];
}): { close(): void } {
Expand Down Expand Up @@ -93,6 +99,7 @@ export function createDesktopRuntimeHostPeerMeshManagement(input: {
input.localMesh?.(),
input.localHost,
input.runLocal,
input.liveHost(LOCAL_RUNTIME_HOST_PROFILE.id),
signal,
);
}
Expand All @@ -107,28 +114,57 @@ export function createDesktopRuntimeHostPeerMeshManagement(input: {
);
}
if (target.kind === 'local_host') {
const live = input.liveHost(LOCAL_RUNTIME_HOST_PROFILE.id);
if (live) {
return executeManagedTarget(
input.localMesh?.(),
(command) => runLivePeerMeshCommand(live, command),
action,
meshId,
peerId,
invitation,
displayName,
signal,
);
}
return input.localHost.inspectManaged(async (managed) => {
const run: RunManagedPeerMeshCommand = async (command) => {
const { invitation, ...rest } = command;
const response = await input.runLocal({
operatorPath: managed.operatorPath,
target: managedTarget(managed),
...command,
...rest,
...(invitation ? { invitation: JSON.stringify(invitation) } : {}),
signal: command.signal,
});
if (response.kind === 'error') throw new Error(response.error.message);
return response.result;
};
if (action === 'reconcile') return reconcileManagedTarget(input.localMesh?.(), run, signal);
return run({
return executeManagedTarget(
input.localMesh?.(),
run,
action,
...(meshId !== undefined ? { meshId } : {}),
...(peerId ? { peerId } : {}),
...(invitation ? { invitation: JSON.stringify(invitation) } : {}),
...(displayName !== undefined ? { displayName } : {}),
meshId,
peerId,
invitation,
displayName,
signal,
});
);
});
}
const live = input.liveHost(target.profileId);
if (live) {
return executeManagedTarget(
input.localMesh?.(),
(command) => runLivePeerMeshCommand(live, command),
action,
meshId,
peerId,
invitation,
displayName,
signal,
);
}
const managed = await input.profiles.resolveManagedService(target.profileId);
if (
!managed ||
Expand All @@ -140,6 +176,7 @@ export function createDesktopRuntimeHostPeerMeshManagement(input: {
}
const transport = managed.profile.transport;
const run: RunManagedPeerMeshCommand = async (command) => {
const { invitation, ...rest } = command;
const response = await input.runRemote({
destination: transport.destination,
...(transport.sshPort === undefined ? {} : { sshPort: transport.sshPort }),
Expand All @@ -150,7 +187,8 @@ export function createDesktopRuntimeHostPeerMeshManagement(input: {
rootId: managed.profile.rootId,
deploymentId: managed.deployment.deploymentId,
},
...command,
...rest,
...(invitation ? { invitation: JSON.stringify(invitation) } : {}),
signal: command.signal,
});
if (response.kind === 'error') throw new Error(response.error.message);
Expand All @@ -159,15 +197,16 @@ export function createDesktopRuntimeHostPeerMeshManagement(input: {
}
return response.result;
};
if (action === 'reconcile') return reconcileManagedTarget(input.localMesh?.(), run, signal);
return run({
return executeManagedTarget(
input.localMesh?.(),
run,
action,
...(meshId !== undefined ? { meshId } : {}),
...(peerId ? { peerId } : {}),
...(invitation ? { invitation: JSON.stringify(invitation) } : {}),
...(displayName !== undefined ? { displayName } : {}),
meshId,
peerId,
invitation,
displayName,
signal,
});
);
};

const channel = 'runtime-host-peer-mesh:execute';
Expand Down Expand Up @@ -214,28 +253,106 @@ export function createDesktopRuntimeHostPeerMeshManagement(input: {
};
}

async function executeManagedTarget(
desktopMesh: PeerMeshNode | undefined,
run: RunManagedPeerMeshCommand,
action: PeerMeshAction,
meshId: string | null | undefined,
peerId: string | undefined,
invitation: PeerMeshInvitationV1 | undefined,
displayName: string | null | undefined,
signal?: AbortSignal,
): Promise<PeerMeshResult> {
if (action === 'reconcile') return reconcileManagedTarget(desktopMesh, run, signal);
return run({
action,
...(meshId !== undefined ? { meshId } : {}),
...(peerId ? { peerId } : {}),
...(invitation ? { invitation } : {}),
...(displayName !== undefined ? { displayName } : {}),
signal,
});
}

function runLivePeerMeshCommand(
client: Pick<DesktopRuntimeHostClient, 'request'>,
command: ManagedPeerMeshCommand,
): Promise<PeerMeshResult> {
switch (command.action) {
case 'status':
return client.request('peer.mesh.query', {});
case 'create':
return client.request('peer.mesh.create', {});
case 'invite':
return client.request('peer.mesh.invite', {
meshId: requiredValue(command.meshId, 'Mesh ID'),
});
case 'join':
return client.request('peer.mesh.join', {
invitation: requiredValue(command.invitation, 'Peer Mesh invitation'),
});
case 'remove':
return client.request('peer.mesh.remove', {
meshId: requiredValue(command.meshId, 'Mesh ID'),
peerId: requiredValue(command.peerId, 'Peer ID'),
});
case 'leave':
return client.request('peer.mesh.leave', {
meshId: requiredValue(command.meshId, 'Mesh ID'),
});
case 'close':
return client.request('peer.mesh.close', {
meshId: requiredValue(command.meshId, 'Mesh ID'),
});
case 'reconcile':
return client.request('peer.mesh.reconcile', {});
case 'transit':
return client.request('peer.mesh.transit.set', { meshId: command.meshId ?? null });
case 'rename':
return client.request('peer.mesh.display-name.set', {
displayName: requiredDisplayName(command.displayName),
});
case 'rename-mesh':
return client.request('peer.mesh.rename', {
meshId: requiredValue(command.meshId, 'Mesh ID'),
displayName: requiredDisplayName(command.displayName),
});
}
}

async function reconcileDesktopTarget(
desktopMesh: PeerMeshNode | undefined,
localHost: Pick<DesktopLocalRuntimeHostRemoteAccess, 'getSnapshot' | 'inspectManaged'>,
runLocal: LocalOperator['runPeerMesh'],
liveHost: Pick<DesktopRuntimeHostClient, 'request'> | undefined,
signal?: AbortSignal,
): Promise<PeerMeshQueryResult> {
if (!desktopMesh) throw new Error('This Desktop build does not include Direct peer support');
const failures: unknown[] = [];
const localSnapshot = await localHost.getSnapshot();
if (localSnapshot.state === 'on') {
await localHost.inspectManaged(async (managed) => {
const run: RunManagedPeerMeshCommand = async (command) => {
const response = await runLocal({
operatorPath: managed.operatorPath,
target: managedTarget(managed),
...command,
const reconciliation = liveHost
? reconcileManagedTarget(
desktopMesh,
(command) => runLivePeerMeshCommand(liveHost, command),
signal,
)
: localHost.inspectManaged(async (managed) => {
const run: RunManagedPeerMeshCommand = async (command) => {
const { invitation, ...rest } = command;
const response = await runLocal({
operatorPath: managed.operatorPath,
target: managedTarget(managed),
...rest,
...(invitation ? { invitation: JSON.stringify(invitation) } : {}),
signal: command.signal,
});
if (response.kind === 'error') throw new Error(response.error.message);
return response.result;
};
return reconcileManagedTarget(desktopMesh, run, signal);
});
if (response.kind === 'error') throw new Error(response.error.message);
return response.result;
};
await reconcileManagedTarget(desktopMesh, run, signal);
}).catch((error) => failures.push(error));
await reconciliation.catch((error) => failures.push(error));
}
await desktopMesh.reconcile(signal).catch((error) => failures.push(error));
if (failures.length > 0) {
Expand Down Expand Up @@ -266,7 +383,7 @@ async function reconcileManagedTarget(
authorityRouteNeedsRecovery(managedMembership)
) {
const invitation = await desktopMesh.invite(desktopMembership.meshId);
await run({ action: 'join', invitation: JSON.stringify(invitation), signal });
await run({ action: 'join', invitation, signal });
recovered = true;
continue;
}
Expand Down
8 changes: 7 additions & 1 deletion native/runtime-host-peer/src/bindings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ pub struct ConnectPeerOptions {
#[napi(object)]
pub struct ConfigurePeerTransitOptions {
pub allowed_peer_ids: Vec<String>,
pub approved_relay_peer_ids: Vec<String>,
pub relay_candidates: Vec<PeerTransitRelayCandidate>,
}

Expand Down Expand Up @@ -141,13 +142,17 @@ impl PeerEndpoint {
#[napi]
pub async fn configure_transit(&self, options: ConfigurePeerTransitOptions) -> Result<()> {
let allowed_peers = parse_peer_ids(options.allowed_peer_ids)?;
let approved_relays = parse_peer_ids(options.approved_relay_peer_ids)?;
let relays = parse_transit_relay_candidates(options.relay_candidates)?;
let trusted_relays = relays
.iter()
.map(|candidate| candidate.peer_id)
.collect::<HashSet<_>>();
let local_peer_id = parse_peer_id(&self.peer_id)?;
if allowed_peers.contains(&local_peer_id) || trusted_relays.contains(&local_peer_id) {
if allowed_peers.contains(&local_peer_id)
|| approved_relays.contains(&local_peer_id)
|| trusted_relays.contains(&local_peer_id)
{
return Err(Error::new(
Status::InvalidArg,
"peer endpoint cannot configure itself as a transit peer",
Expand All @@ -158,6 +163,7 @@ impl PeerEndpoint {
.send(EngineCommand::ConfigureTransit {
policy: engine::TransitPolicy {
allowed_peers,
approved_relays,
relays,
},
result: result_tx,
Expand Down
Loading
Loading