Skip to content

Commit 4fcbbf8

Browse files
authored
fix(desktop): support --no-sandbox when launched as uid 0 (#356)
## What Two Chrome/Chromium spawn sites in the desktop app crash immediately when launched as uid 0, with: ``` FATAL: Running as root without --no-sandbox is not supported. See https://crbug.com/638180. ``` 1. **`apps/desktop/scripts/dev.cjs`** — spawns Electron via electron-vite. `pnpm dev` exits within seconds, before the renderer can attach. 2. **`apps/desktop/src/main/preview-runtime.ts`** — spawns Chrome via puppeteer-core to render preview artifacts. **9 of the 17 `preview-runtime.test.ts` cases fail** as root, all with the same FATAL line in the failure payload. Both make the repo effectively unusable inside any rootful container, dev VM, or CI runner — environments where Node toolchains are commonly already root. ## Fix Gated to `process.getuid?.() === 0` so the macOS / Windows / user-mode-Linux launch paths are unchanged: - **dev launcher** sets `NO_SANDBOX=1`, which electron-vite already reads and forwards as `--no-sandbox` to the spawned Electron process (see `electron-vite/dist .../lib-q6ns0vZr.js:234`). - **preview-runtime** conditionally appends `'--no-sandbox'` to the puppeteer launch args. 8 + 4 = 12 added lines, 2 files, no other code touched. ## Verification Local environment: Linux container, uid 0, Node 22.22.3, pnpm 10.33.4. **Before patch:** - `pnpm dev` → `[FATAL:electron_main_delegate.cc:216] Running as root without --no-sandbox is not supported.` - `pnpm --filter @open-codesign/desktop test` → `Test Files 1 failed | 109 passed (110)`, `Tests 9 failed | 1332 passed (1341)` — every failure points back to the same FATAL line. **After patch:** - `pnpm dev` → Electron now reaches `Missing X server or $DISPLAY` (expected on a headless box; out of scope for this PR). - Full pre-push gate green: `pnpm -r typecheck` ✅, `pnpm lint` ✅ (Biome, 524 files), `pnpm test` ✅ **110/110 files, 1341/1341 tests** (vs 9 failing on `main`). ## Principles check - **Compatibility** ✅ — non-root behavior unchanged; uid 0 is the only branch. - **Upgradeability** ✅ — no schema, no IPC, no config. - **No bloat** ✅ — uses an env var electron-vite already supports; no new deps; ~12 lines. - **Elegance** ✅ — both call sites use the same `getuid?.() === 0` guard; comments explain *why* (per CLAUDE.md). No changeset added — this is an internal dev-experience fix that doesn't change shipped behavior. Happy to add one if the project prefers.
1 parent b5248a8 commit 4fcbbf8

2 files changed

Lines changed: 12 additions & 0 deletions

File tree

apps/desktop/scripts/dev.cjs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,14 @@ const env = { ...process.env };
1010
// so Electron behaves like Node. The desktop dev app must launch real Electron.
1111
env.ELECTRON_RUN_AS_NODE = undefined;
1212

13+
// Electron exits fatally when launched as uid 0 unless --no-sandbox is passed.
14+
// Containers, dev VMs, and CI runners commonly run as root; without this they
15+
// can never `pnpm dev` at all. electron-vite reads NO_SANDBOX=1 and forwards
16+
// --no-sandbox to the spawned Electron process (see electron-vite/dist startElectron).
17+
if (process.getuid && process.getuid() === 0 && !env.NO_SANDBOX) {
18+
env.NO_SANDBOX = '1';
19+
}
20+
1321
const child = spawn(process.execPath, [electronViteBin, 'dev', ...process.argv.slice(2)], {
1422
env,
1523
stdio: ['inherit', 'inherit', 'pipe'],

apps/desktop/src/main/preview-runtime.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,10 @@ export async function runPreview(opts: RunPreviewOptions): Promise<PreviewResult
118118
// dialogs that would otherwise stall the launch handshake.
119119
'--no-first-run',
120120
'--no-default-browser-check',
121+
// Chrome's zygote refuses to start as uid 0 without this. Containers,
122+
// dev VMs, and CI runners frequently run as root; gating to root keeps
123+
// the production launch path unchanged on macOS/Windows/user-mode Linux.
124+
...(process.getuid?.() === 0 ? ['--no-sandbox'] : []),
121125
],
122126
});
123127
page = await browser.newPage();

0 commit comments

Comments
 (0)