From 91de3beb7232e8dd54efbd3b2883de30ffd79dd7 Mon Sep 17 00:00:00 2001 From: a-espinoza Date: Sun, 9 Aug 2026 22:21:50 -0500 Subject: [PATCH] fix(desktop-ssh): raise remote backend file limit --- apps/desktop/electron/remote-lifecycle.test.ts | 6 ++++++ apps/desktop/electron/remote-lifecycle.ts | 10 +++++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/apps/desktop/electron/remote-lifecycle.test.ts b/apps/desktop/electron/remote-lifecycle.test.ts index 151da86830..5781b4de98 100644 --- a/apps/desktop/electron/remote-lifecycle.test.ts +++ b/apps/desktop/electron/remote-lifecycle.test.ts @@ -832,6 +832,12 @@ test('buildSpawnCommand always uses serve, never dashboard', () => { assert.doesNotMatch(cmd, /--no-open/) }) +test('buildSpawnCommand raises the SSH child file limit before execing Hermes', () => { + const cmd = buildSpawnCommand('/x/hermes', '', { logPath: spawnLogPath(OWNERSHIP_ID, SPAWN_NONCE) }) + assert.match(cmd, /ulimit -n 65536 2>\/dev\/null \|\| true; exec env HERMES_DESKTOP=1/) + assert.ok(cmd.indexOf('ulimit -n 65536') < cmd.indexOf('serve --isolated')) +}) + test('spawnRemoteDashboard removes a token file when upload reporting fails', async () => { const failure = new Error('channel closed') diff --git a/apps/desktop/electron/remote-lifecycle.ts b/apps/desktop/electron/remote-lifecycle.ts index 1dcd74e3b7..9be878bfd5 100644 --- a/apps/desktop/electron/remote-lifecycle.ts +++ b/apps/desktop/electron/remote-lifecycle.ts @@ -37,6 +37,11 @@ const REMOTE_LOCK_DIR = '~/.hermes/desktop-ssh' const SUPPORTED_REMOTE_OS = new Set(['Linux', 'Darwin']) const DEFAULT_READY_TIMEOUT_MS = 45_000 const READY_POLL_INTERVAL_MS = 750 +// macOS sshd starts non-interactive shells with a 256-FD soft limit even when +// the hard limit is unlimited. A Desktop backend can legitimately exceed that +// while serving several profiles/tools, so raise only the child process limit. +// Keep startup portable: restricted hosts retain their existing limit. +const REMOTE_NOFILE_SOFT_LIMIT = 65_536 function mintToken() { return crypto.randomBytes(32).toString('hex') @@ -442,7 +447,10 @@ function buildSpawnCommand(hermesPath, profile, opts: any = {}) { const tokenArg = tokenFilePath ? ` --ssh-session-token-file ${expandRemotePath(tokenFilePath)}` : '' const ownerArg = opts.spawnNonce ? ` --ssh-owner-nonce ${validateSpawnNonce(opts.spawnNonce)}` : '' const subCmd = `serve --isolated --host 127.0.0.1 --port 0${tokenArg}${ownerArg}` - const dashCmd = `env HERMES_DESKTOP=1 ${hermes} ${profileArgs}${subCmd}` + + const dashCmd = + `ulimit -n ${REMOTE_NOFILE_SOFT_LIMIT} 2>/dev/null || true; ` + + `exec env HERMES_DESKTOP=1 ${hermes} ${profileArgs}${subCmd}` return ( `mkdir -p "$(dirname ${logPath})" && ` +