fix(cli): bypass proxy env for loopback HTTP (hooks, runner control, local MCP)#868
Merged
Conversation
Bun's fetch and node:http honor HTTP_PROXY/HTTPS_PROXY env vars, which can route loopback traffic through a configured proxy (e.g. Surge/Clash). When NO_PROXY doesn't explicitly exclude localhost, this breaks loopback communication: SessionStart hooks fail to arrive (transcripts don't sync, web UI stays empty), runner control client times out, and MCP server connections fail. Normalize NO_PROXY at the CLI entrypoint to always cover loopback (localhost, 127.0.0.1, ::1). Child processes inherit the patched env so their loopback traffic is covered too. Non-loopback traffic continues using the configured proxy. Supersedes the runner control client workaround from #563.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
With a proxy exported in the shell (e.g. Surge/Clash:
HTTP_PROXY=http://127.0.0.1:1080) and aNO_PROXYthat doesn't literally cover loopback (NO_PROXY=npmjs.org, or the glibc-style127.*wildcard Bun doesn't match), every loopback HTTP request in the CLI is routed through the proxy. Depending on the proxy's active policy, "127.0.0.1" gets forwarded to a remote node where nothing listens, and the request dies.This breaks, flakily (depends on proxy policy at that moment):
/session-startedwebhook → "Session webhook timeout", the bug in fix(cli): bypass HTTP_PROXY for runner webhook via raw socket #563)autoStartServerhealth checksRoot cause
Bun honors
HTTP_PROXY/HTTPS_PROXYfor bothfetchand itsnode:httpimplementation. Evidence: posting to a dead loopback port returns the proxy's HTTP 503 error page instead ofECONNREFUSED.Contrary to the analysis in #563, runtime mutation of
process.env.NO_PROXYdoes take effect on Bun 1.3.14 (the version our CI builds with) — verified for bothfetchandnode:http, including after a priorfetch(no startup caching):Fix
ensureLoopbackProxyBypass()appendslocalhost,127.0.0.1,::1toNO_PROXY/no_proxy(preserving existing entries, no-op on*), called first thing inrunCli(). One fix covers all in-process loopback calls and all child processes (claude, runner daemon, hook-forwarder), which inherit the patched env. Non-loopback traffic keeps using the configured proxy.This also fixes the #563 reproducer:
NO_PROXY="127.*,localhost"becomes127.*,localhost,127.0.0.1,::1, and Bun matches the literal entry.Testing
proxyEnv.test.ts); cli suite: 972 passed, 1 pre-existing env-dependent failure (apiMachine.test.ts/varvs/private/var, fails on clean main too)HTTP_PROXYset: hook-forwarder went from proxy-503 to directECONNREFUSEDon a dead port; against a live session, hook delivery + transcript sync resumed immediatelySupersedes #563 (thanks @XWang20 for the report and reproducer — same disease, this treats all infected call sites instead of one).