refactor: centralize relay vs direct E2EE

This commit is contained in:
Mohamed Boudra
2026-02-03 14:13:03 +07:00
parent 4fb93d92ed
commit d68596bcbe
12 changed files with 83 additions and 68 deletions
@@ -22,11 +22,11 @@ function buildCandidateUrls(daemon: HostProfile): string[] {
};
const isLastKnownRelay = !!relayEndpoint && lastKnownGood === relayEndpoint;
const directEndpoints = relayEndpoint
? endpoints.filter((endpoint) => endpoint !== relayEndpoint)
: endpoints;
const directEndpoints = endpoints;
if (lastKnownGood && !isLastKnownRelay) {
if (sessionId && relayEndpoint && isLastKnownRelay) {
push(buildRelayWebSocketUrl({ endpoint: relayEndpoint, sessionId }));
} else if (lastKnownGood) {
push(buildDaemonWebSocketUrl(lastKnownGood));
}
@@ -35,9 +35,6 @@ function buildCandidateUrls(daemon: HostProfile): string[] {
}
if (sessionId && relayEndpoint) {
if (isLastKnownRelay) {
push(buildRelayWebSocketUrl({ endpoint: relayEndpoint, sessionId }));
}
push(buildRelayWebSocketUrl({ endpoint: relayEndpoint, sessionId }));
}
@@ -2,7 +2,6 @@ import { createContext, useCallback, useContext } from "react";
import type { ReactNode } from "react";
import AsyncStorage from "@react-native-async-storage/async-storage";
import { useQuery, useQueryClient } from "@tanstack/react-query";
import { z } from "zod";
import {
buildDaemonWebSocketUrl,
decodeOfferFragmentPayload,
@@ -10,6 +9,10 @@ import {
extractHostPortFromWebSocketUrl,
normalizeHostPort,
} from "@/utils/daemon-endpoints";
import {
ConnectionOfferV1Schema,
type ConnectionOfferV1,
} from "@server/shared/connection-offer";
const REGISTRY_STORAGE_KEY = "@paseo:daemon-registry";
const LEGACY_SETTINGS_KEY = "@paseo:settings";
@@ -41,15 +44,6 @@ type CreateHostInput = {
type UpdateHostInput = Partial<Omit<HostProfile, "id" | "createdAt">>;
const ConnectionOfferV1Schema = z.object({
v: z.literal(1),
sessionId: z.string().min(1),
endpoints: z.array(z.string().min(1)).min(1),
daemonPublicKeyB64: z.string().min(1),
});
export type ConnectionOfferV1 = z.infer<typeof ConnectionOfferV1Schema>;
interface DaemonRegistryContextValue {
daemons: HostProfile[];
isLoading: boolean;
@@ -134,7 +128,12 @@ export function DaemonRegistryProvider({ children }: { children: ReactNode }) {
const existing = readDaemons();
const now = new Date().toISOString();
const normalizedEndpoints = offer.endpoints.map((endpoint) => normalizeHostPort(endpoint));
const relayEndpoint = normalizedEndpoints[normalizedEndpoints.length - 1];
const relayEndpoint =
offer.relay?.endpoint
? normalizeHostPort(offer.relay.endpoint)
: offer.relay === undefined && normalizedEndpoints.length > 0
? normalizedEndpoints[normalizedEndpoints.length - 1]
: null;
const matchIndex = existing.findIndex((daemon) => daemon.daemonPublicKeyB64 === offer.daemonPublicKeyB64);
if (matchIndex !== -1) {
@@ -142,7 +141,7 @@ export function DaemonRegistryProvider({ children }: { children: ReactNode }) {
...existing[matchIndex],
daemonPublicKeyB64: offer.daemonPublicKeyB64,
endpoints: normalizedEndpoints,
relay: { endpoint: relayEndpoint, sessionId: offer.sessionId },
relay: relayEndpoint ? { endpoint: relayEndpoint, sessionId: offer.sessionId } : null,
updatedAt: now,
};
const next = [...existing];
@@ -156,7 +155,7 @@ export function DaemonRegistryProvider({ children }: { children: ReactNode }) {
label: deriveLabelFromEndpoint(normalizedEndpoints[0] ?? "Unnamed Host"),
endpoints: normalizedEndpoints,
daemonPublicKeyB64: offer.daemonPublicKeyB64,
relay: { endpoint: relayEndpoint, sessionId: offer.sessionId },
relay: relayEndpoint ? { endpoint: relayEndpoint, sessionId: offer.sessionId } : null,
createdAt: now,
updatedAt: now,
metadata: null,
+4 -16
View File
@@ -13,15 +13,6 @@ type DaemonClientOptions = {
daemonPublicKeyB64?: string;
};
function isRelayWebSocketUrl(url: string): boolean {
try {
const parsed = new URL(url);
return parsed.searchParams.get("role") === "client" && parsed.searchParams.has("session");
} catch {
return false;
}
}
export function useDaemonClient(
url: string,
options: DaemonClientOptions = {}
@@ -29,21 +20,18 @@ export function useDaemonClient(
const client = useMemo(
() => {
const tauriTransportFactory = createTauriWebSocketTransportFactory();
const relayConnection = isRelayWebSocketUrl(url);
return new DaemonClientV2({
url,
suppressSendErrors: true,
...(tauriTransportFactory
? { transportFactory: tauriTransportFactory }
: {}),
...(relayConnection
e2ee: options.daemonPublicKeyB64
? {
e2ee: {
enabled: true,
daemonPublicKeyB64: options.daemonPublicKeyB64,
},
enabled: true,
daemonPublicKeyB64: options.daemonPublicKeyB64,
}
: {}),
: undefined,
});
},
[options.daemonPublicKeyB64, url]