feat(desktop-client): derive stable client_id from machine identity

Generate the desktop client_id deterministically from OS machine identifiers
(IOPlatformUUID / MachineGuid / /etc/machine-id) scoped per
tenant+workspace+user, with a persisted UUID fallback when the OS lookup
fails. The renderer now requests the id over a new IPC bridge instead of
keeping a localStorage UUID, so reinstalls and storage clears no longer
fork into duplicate clients. Server enforces the id as required and
rejects empty/malformed UUIDs. Also harden bootstrap reveal flow and
single-instance exit so a second launch focuses the existing window.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-27 20:06:37 +08:00
parent fee16d3ea9
commit 543fe0635f
7 changed files with 358 additions and 131 deletions
@@ -16,6 +16,12 @@ interface DesktopDeviceInfo {
cpu_arch: string;
}
interface DesktopClientIDScope {
tenant_id: number;
workspace_id: number;
user_id: number;
}
const rendererProxyRequestChannel = "desktop:renderer-devtools-proxy:request";
const rendererProxyResponseChannel = "desktop:renderer-devtools-proxy:response";
@@ -82,6 +88,8 @@ const desktopBridge = {
app: {
ping: () => ipcRenderer.invoke("desktop:ping") as Promise<string>,
deviceInfo: () => ipcRenderer.invoke("desktop:device-info") as Promise<DesktopDeviceInfo>,
clientId: (scope: DesktopClientIDScope) =>
ipcRenderer.invoke("desktop:client-id", scope) as Promise<string>,
runtimeSnapshot: () =>
ipcRenderer.invoke("desktop:runtime-snapshot") as Promise<DesktopRuntimeSnapshot>,
bindPublishAccount: (platformId: string) =>