fix /desktop: break the VNC password deadlock, drop the vncpasswd dependency

The desktop page has never worked on a fresh install. Two faults, both fatal.

The password could never be created. DesktopView fetches /desktop/vnc-password
before opening the WebSocket, but ensureVncPassword ran only from startSession,
which only the WebSocket triggers — so the endpoint answered "not configured",
the UI stopped, and the socket that would have provisioned it was never opened.
A new vnc:ensure-password sidecar command provisions it directly; the endpoint
asks for it instead of returning 500.

The rfbauth file could never be written either. ensureVncPassword shelled out to
tigervnc's `vncpasswd -f`, which is not installed — and, contrary to the comment
in setup-desktop.sh, is not in tigervnc-common, which ships only tigervncconfig.
The failure was swallowed because only a zero exit wrote the file, so x11vnc got
-rfbauth pointing at nothing. x11vnc writes that format itself with -storepasswd,
so the dependency is gone and a failure now throws.

Verified on the box: the endpoint returns a password, .vnc/{passwd,password} are
written 0600, and the sidecar reports mirroring :0 on 5900 with x11vnc using the
generated rfbauth file.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
brunorezio
2026-07-26 05:15:46 +01:00
co-authored by Claude Opus 5
parent 96dd1bfe9e
commit 7556c9ed00
6 changed files with 73 additions and 22 deletions
+9
View File
@@ -409,6 +409,15 @@ export async function startVnc(params: VncStartParams): Promise<{ port: number;
throw new Error('Unexpected response');
}
// Provision the VNC password without starting a server. The desktop UI needs it before it can open
// the WebSocket that would start one, so asking vnc:start here would be circular.
export async function ensureVncPassword(email: string): Promise<string> {
const res = await sendCommand('vnc', { type: 'vnc:ensure-password', id: nextId(), email });
if (res.type === 'vnc:password') return res.password;
if (res.type === 'vnc:error') throw new Error(res.error);
throw new Error('Unexpected response from VNC sidecar');
}
export function stopVnc(email: string): void {
sendFire('vnc', { type: 'vnc:stop', id: nextId(), email });
}