Skip to content

Fix macOS+Bun E2E hang by running the bridge under the CLI's runtime#266

Merged
jancurn merged 6 commits into
mainfrom
claude/fix-macos-bun-keychain-hang
Jun 10, 2026
Merged

Fix macOS+Bun E2E hang by running the bridge under the CLI's runtime#266
jancurn merged 6 commits into
mainfrom
claude/fix-macos-bun-keychain-hang

Conversation

@jancurn

@jancurn jancurn commented Jun 8, 2026

Copy link
Copy Markdown
Member

Completes #265 (which added only the per-test timeout watchdog): fixes the macOS+Bun E2E hang. Root cause, pinned via the watchdog's sample backtrace: a Bun CLI spawned the bridge under a hardcoded node, so the Node bridge did a cross-binary macOS Keychain read that blocks on a Security prompt in headless CI.

  • Run the bridge under the CLI's runtime (process.execPath) — one keychain identity; a Bun user no longer needs Node installed.
  • Deliver the proxy bearer token to the bridge over IPC, so the bridge's only keychain access is the OAuth-refresh path (Bridge credential timeout (5s) too short when macOS Keychain prompts for password #55).
  • --insecure works under both runtimes (Bun via NODE_TLS_REJECT_UNAUTHORIZED=0 on the bridge, since Bun ignores undici's TLS-bypass).
  • The timeout watchdog now dumps the process tree, a native sample backtrace, and the bridge log.
  • Seed unauthorized-auto-detect's keychain via the test's own runtime.

Green on macOS + Linux E2E (Bun & Node), unit, build, lint.

Refs #248, #265

https://claude.ai/code/session_01417BuEkifr5jSSx6R2MYCB

claude added 2 commits June 8, 2026 15:15
…eout

When the watchdog kills a hung test, capture diagnostics first so the failure
log is actionable instead of opaque: the hung process tree, a native stack
(macOS `sample`, no privileges needed) of each stuck mcpc/bridge process, and
the per-test bridge log. Written to a side file while the test is still alive
to avoid racing its own output, then appended after the kill.

This is what's needed to pin down the macOS+Bun hang in sessions/proxy and
sessions/unauthorized-auto-detect, which only reproduces in CI.

Refs #265

https://claude.ai/code/session_01417BuEkifr5jSSx6R2MYCB
…hang

The macOS+Bun E2E hang (sessions/proxy, sessions/unauthorized-auto-detect) was
a synchronous native macOS Keychain read blocking on a Security access prompt:
keychain ACLs are per-binary, so reading an item created by a *different* binary
prompts — and blocks forever in headless CI. The CLI runs under Bun but spawned
the bridge under a hardcoded `node`, so the bridge read keychain items the Bun
CLI had written (e.g. the proxy bearer token).

Spawn the bridge with process.execPath so the CLI and bridge share one runtime —
and one keychain identity. A Bun user also no longer needs Node installed for the
bridge to start.

Bun's fetch ignores undici's TLS-bypass dispatcher, so `--insecure` cannot skip
certificate verification under a Bun bridge. Rather than be silently ineffective,
startBridge now fails with a clear error when `--insecure` is used under Bun;
covered by a runtime-aware e2e test.

Also seed the unauthorized-auto-detect keychain via the test's own runtime
instead of a hardcoded `node` (same per-binary reason).

Refs #248, #265

https://claude.ai/code/session_01417BuEkifr5jSSx6R2MYCB
@jancurn jancurn force-pushed the claude/fix-macos-bun-keychain-hang branch from 034f39f to 12b5714 Compare June 8, 2026 15:55
@jancurn jancurn changed the title Fix macOS+Bun E2E hang: deliver proxy bearer token to bridge via IPC Fix macOS+Bun E2E hang by running the bridge under the CLI's runtime Jun 8, 2026
claude and others added 4 commits June 8, 2026 16:34
With the bridge now running under the CLI's runtime, its keychain access is
same-binary — but the bridge still read the proxy bearer token directly from the
keychain in startProxyServer(), the lone keychain read outside the sanctioned
OAuth-refresh path (CLAUDE.md / #55). A post-spawn keychain read can also race
the bridge's IPC-credential timer if the keychain is locked.

Read the token in the CLI before spawn and hand it to the bridge over the
existing set-auth-credentials IPC message, exactly as headers and OAuth
credentials are already delivered. The keychain stays the at-rest store (so
authenticated proxy sessions survive restarts without re-passing the flag); only
the reader moves from the bridge to the CLI. The bridge now touches the keychain
only on the OAuth-refresh path.

Refs #248, #265

https://claude.ai/code/session_01417BuEkifr5jSSx6R2MYCB
Bun's fetch ignores undici's TLS-bypass dispatcher, so under a Bun bridge the
`--insecure` flag could not skip certificate verification. Rather than fail loudly,
set NODE_TLS_REJECT_UNAUTHORIZED=0 in the bridge process's environment when
--insecure is given: Bun honors it (verified against a self-signed server, on the
undici-fetch path the bridge actually uses), and it is a harmless no-op alongside
the existing undici dispatcher on Node. --insecure now works under both runtimes —
which also matches the pre-PR behaviour, where the bridge ran under Node.

Set via the spawn env so it is in place before the runtime initializes TLS, and
scoped to the one bridge process. The insecure e2e test returns to runtime-agnostic
(it asserts --insecure works under whatever runtime runs it), and the CHANGELOG no
longer claims a Bun --insecure change — net of this PR there is none.

Also log proxyBearerToken presence in the bridge's auth-credentials debug summary.

Refs #248, #265

https://claude.ai/code/session_01417BuEkifr5jSSx6R2MYCB
@jancurn jancurn merged commit 32a2463 into main Jun 10, 2026
8 checks passed
@jancurn jancurn deleted the claude/fix-macos-bun-keychain-hang branch June 10, 2026 20:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants