vnc sidecar: per-user desktop sessions via sidecar architecture

replaces the single hardcoded systemd VNC service with a dynamic
sidecar that manages per-user VNC sessions on demand. any authenticated
user can now access their own desktop, not just Super Admin.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-06 10:49:53 +00:00
co-authored by Claude Opus 4.6
parent a0d9ea63f9
commit daf5580c39
12 changed files with 467 additions and 190 deletions
+47
View File
@@ -83,6 +83,9 @@ export async function provisionLinuxUser(email: string, username: string): Promi
const settingsContent = await Bun.file(settingsFile).text();
await Bun.write(join(claudeDir, 'settings.json'), settingsContent);
// Provision VNC environment
await provisionVncEnv(homeDir);
// Service user (pastilhas) owns everything — server can always read/write.
// User's personal group gives only that user terminal access. Others get nothing.
const serviceUser = process.env.USER ?? 'pastilhas';
@@ -139,6 +142,50 @@ async function seedShellConfigs(homeDir: string): Promise<void> {
mkdirSync(join(homeDir, '.pi', 'agent', 'sessions'), { recursive: true });
}
async function provisionVncEnv(homeDir: string): Promise<void> {
const vncDir = join(homeDir, '.vnc');
mkdirSync(vncDir, { recursive: true });
// Skip if already provisioned
if (existsSync(join(vncDir, 'passwd'))) return;
// Generate random 8-char password
const password = Array.from(crypto.getRandomValues(new Uint8Array(6)))
.map((b) => String.fromCharCode(33 + (b % 94)))
.join('');
// Write plaintext password (for API to read)
await Bun.write(join(vncDir, 'password'), password);
// Create encrypted passwd using vncpasswd -f
const proc = Bun.spawnSync({
cmd: ['bash', '-c', `echo '${password.replace(/'/g, "'\\''")}' | vncpasswd -f`],
stdout: 'pipe',
stderr: 'ignore',
});
if (proc.exitCode === 0 && proc.stdout.byteLength > 0) {
await Bun.write(join(vncDir, 'passwd'), proc.stdout);
}
// Write xstartup
const xstartup = `#!/bin/sh
unset SESSION_MANAGER
unset DBUS_SESSION_BUS_ADDRESS
eval $(dbus-launch --sh-syntax)
export DBUS_SESSION_BUS_ADDRESS
exec startxfce4
`;
await Bun.write(join(vncDir, 'xstartup'), xstartup);
// Set permissions
run(['chmod', '+x', join(vncDir, 'xstartup')]);
run(['chmod', '600', join(vncDir, 'passwd')]);
run(['chmod', '600', join(vncDir, 'password')]);
console.log(`[provision] VNC environment provisioned at ${vncDir}`);
}
export function deprovisionLinuxUser(email: string, username: string): boolean {
const shellUsername = toShellUsername(username, email);
console.log(`[provision] deprovisioning Linux user ${shellUsername}`);