From 87412bc9fe4d62b67d081ee1a5292293179a170c Mon Sep 17 00:00:00 2001 From: Leechael Yim Date: Wed, 21 Oct 2026 13:34:51 +0800 Subject: [PATCH] fix(kms): raise Bun auth idleTimeout above the KMS webhook timeout Bun.serve defaults idleTimeout to 10s and closes the socket while the handler is still awaiting eth_call. That surfaces on GetMeta as "connection closed before message completed". Set idleTimeout to Bun's 255s maximum so the KMS client's 60s webhook timeout fires first. Signed-off-by: Leechael Yim --- dstack/kms/auth-eth-bun/README.md | 2 ++ dstack/kms/auth-eth-bun/index.test.ts | 10 ++++++++++ dstack/kms/auth-eth-bun/index.ts | 4 ++++ dstack/kms/auth-mock/index.ts | 3 +++ dstack/kms/auth-simple/index.ts | 3 +++ 5 files changed, 22 insertions(+) diff --git a/dstack/kms/auth-eth-bun/README.md b/dstack/kms/auth-eth-bun/README.md index 9fc755b6c..a3f48a47b 100644 --- a/dstack/kms/auth-eth-bun/README.md +++ b/dstack/kms/auth-eth-bun/README.md @@ -66,6 +66,8 @@ bun run check - `ETH_CHAIN_ID` - reject every decision if the RPC reports a different chain ID (default: unset, no check) - `ETH_FINALITY_CONFIRMATIONS` - read policy this many blocks behind the RPC head (default: 0, the head) +The Bun server sets `idleTimeout` to 255s (Bun's maximum). Bun's default of 10s closes the socket while a slow `eth_call` is still in flight, which surfaces on the KMS side as `connection closed before message completed`. + ## API endpoints ### GET / diff --git a/dstack/kms/auth-eth-bun/index.test.ts b/dstack/kms/auth-eth-bun/index.test.ts index fd97ffdb6..1dbf60b18 100644 --- a/dstack/kms/auth-eth-bun/index.test.ts +++ b/dstack/kms/auth-eth-bun/index.test.ts @@ -43,6 +43,16 @@ beforeEach(() => { mockGetBlockNumber.mockResolvedValue(100n); }); +describe('server runtime config', () => { + it('keeps Bun idleTimeout above the KMS webhook timeout', async () => { + // Vitest calls `fetch` directly and never hits Bun.serve, so this pins the + // config that production actually loads. Bun's default of 10s closes the + // socket while GET / is still awaiting eth_call. + const indexModule = await import('./index.ts'); + expect(indexModule.default.idleTimeout).toBeGreaterThanOrEqual(60); + }); +}); + describe('API Compatibility Tests', () => { describe('GET /', () => { it('should return system info matching OpenAPI spec', async () => { diff --git a/dstack/kms/auth-eth-bun/index.ts b/dstack/kms/auth-eth-bun/index.ts index 3ea279327..4df31df56 100644 --- a/dstack/kms/auth-eth-bun/index.ts +++ b/dstack/kms/auth-eth-bun/index.ts @@ -333,7 +333,11 @@ app.post('/bootAuth/kms', const port = parseInt(process.env.PORT || '3000'); console.log(`starting server on port ${port}`); +// Bun defaults idleTimeout to 10s and closes the socket while the handler is +// still awaiting eth_call. KMS's webhook timeout is 60s; stay above that so the +// client times out first. Bun's documented maximum is 255. export default { port, + idleTimeout: 255, fetch: app.fetch, }; diff --git a/dstack/kms/auth-mock/index.ts b/dstack/kms/auth-mock/index.ts index 83037c93c..577cae583 100644 --- a/dstack/kms/auth-mock/index.ts +++ b/dstack/kms/auth-mock/index.ts @@ -216,7 +216,10 @@ const port = parseInt(process.env.PORT || '3000'); const policy = getPolicy(); console.log(`starting mock auth server on port ${port} (policy: ${policy})`); +// Bun defaults idleTimeout to 10s and closes the socket while a handler is +// still awaiting work. Stay above the KMS webhook timeout (60s). Bun max: 255. export default { port, + idleTimeout: 255, fetch: app.fetch, }; diff --git a/dstack/kms/auth-simple/index.ts b/dstack/kms/auth-simple/index.ts index 7f9a7f036..9da149014 100644 --- a/dstack/kms/auth-simple/index.ts +++ b/dstack/kms/auth-simple/index.ts @@ -316,7 +316,10 @@ const defaultConfigPath = process.env.AUTH_CONFIG_PATH || './auth-config.json'; console.log(`starting auth-simple server on port ${port}`); console.log(`config path: ${defaultConfigPath}`); +// Bun defaults idleTimeout to 10s and closes the socket while a handler is +// still awaiting work. Stay above the KMS webhook timeout (60s). Bun max: 255. export default { port, + idleTimeout: 255, fetch: app.fetch, };