Skip to content

Feature: macOS system proxy discovery for proxy: "auto" (slice 2 of #1525) #5853

Description

@JinHanAI

Area

Proxy and routing

What are you trying to accomplish?

Run OpenCodex on macOS while switching local proxy/VPN clients frequently (Clash Verge, Shadowrocket, a corporate VPN, and so on). Each switch moves the local proxy port. Today every such change silently breaks outbound requests with 502 Provider unreachable until config.proxy is updated by hand and the service is restarted.

On Windows, proxy: "auto" (#1525) already removes that maintenance. macOS has no equivalent, so a macOS user is left with the exact workflow #1525 set out to fix.

What prevents this today?

proxy: "auto" is Windows-only. In src/config/proxy-env.ts, applyProxyEnvWith() takes the "auto" branch, calls readWindowsSystemProxy(), and on macOS the reader returns kind: "unsupported", logging:

[opencodex] proxy "auto": only Windows system proxy discovery is supported; using direct egress on this OS

So on macOS the setting degrades to direct egress — which is precisely the case that fails when the OpenAI endpoints are only reachable through a local proxy.

There is no macOS reader wired into that call site, even though the test seam already accepts an injectable platform. macOS exposes the system proxy through the standard system configuration, so the missing piece is a reader, not an architectural change.

One implementation trap is worth recording, because it makes the obvious approach produce a false negative: on macOS the per-service queries (networksetup -getwebproxy <service> and friends) reported Enabled: No on every configured network service while the system proxy was in fact active and in use. Proxy clients commonly publish the proxy through the session-scoped system configuration dynamic store, which scutil --proxy (SCDynamicStoreCopyProxies) reports but the per-service preference reads do not. A macOS reader therefore has to be built on the dynamic store, not on networksetup.

What should OpenCodex do?

When config.proxy is "auto" on macOS, read the current macOS system proxy at startup and mirror it into HTTP_PROXY/HTTPS_PROXY, matching the Windows slice:

  • HTTP/HTTPS proxy enabled → mirror them; never copy the literal "auto" into the environment
  • proxy disabled, not configured, or unreadable → direct egress, routing unchanged
  • precedence identical to Windows: a pre-existing HTTP_PROXY/HTTPS_PROXY in the environment wins and the system proxy is not consulted
  • the same one-line proxy "auto": ... startup banner naming the outcome

Two macOS-specific design questions I would rather settle before writing code:

  1. SOCKS-only. Windows falls back to direct on a SOCKS-only system proxy, because HTTP_PROXY cannot express it. On macOS a SOCKS-only system proxy is an ordinary configuration. config.proxy already accepts socks5:// URLs and validates the scheme, so the macOS reader could map a SOCKS-only system proxy to socks5://host:port (plus ALL_PROXY) instead of giving up. Should it do that, or stay consistent with the Windows kind: "socks-only" → direct behaviour?

  2. Refresh. Feature: auto-detect Windows system proxy with smart fallback ("proxy": "auto") #1525 slice 1 is deliberately a single startup read. If the proxy port changes while the service is already running, the mirrored value goes stale and the original symptom returns. Is a re-read — on connectivity failure, or on an interval — in scope here, or deliberately out of scope for this slice?

Example usage or interface

# today on macOS — the "auto" value silently degrades to direct egress
$ ocx config set proxy auto
$ ocx start
[opencodex] proxy "auto": only Windows system proxy discovery is supported; using direct egress on this OS
   outbound proxy: (none)

# expected on macOS with a local proxy listening on 127.0.0.1:1082
$ ocx config set proxy auto
$ ocx start
[opencodex] proxy "auto": using macOS system proxy HTTP 127.0.0.1:1082, HTTPS 127.0.0.1:1082
   outbound proxy: http://127.0.0.1:1082

The values come from the dynamic store, in this shape:

$ scutil --proxy
<dictionary> {
  ExceptionsList : <array> {
    0 : *.local
    1 : localhost
  }
  ExcludeSimpleHostnames : 1
  HTTPEnable : 1
  HTTPPort : 1082
  HTTPProxy : 127.0.0.1
  HTTPSEnable : 1
  HTTPSPort : 1082
  HTTPSProxy : 127.0.0.1
  ProxyAutoConfigEnable : 0
  ProxyAutoDiscoveryEnable : 0
  SOCKSEnable : 0
}

HTTPSEnable/HTTPSProxy/HTTPSPort and SOCKSEnable/SOCKSProxy/SOCKSPort follow the same shape, and ExceptionsList has an obvious noProxy mapping if that is wanted. ProxyAutoConfigEnable (PAC) and ProxyAutoDiscoveryEnable (WPAD) are the macOS analogues of the Windows unsupported cases and could keep the same "cannot express → direct egress" outcome.

Alternatives or workarounds

Workarounds in use or considered:

  1. Hardcode config.proxy to the current port and edit it whenever the proxy client changes port — the manual maintenance Feature: auto-detect Windows system proxy with smart fallback ("proxy": "auto") #1525 exists to remove.
  2. Run the proxy client in a TUN / full-tunnel mode so direct egress is intercepted below the application layer. It works, but it hides the misconfiguration rather than fixing it, and it changes the machine's whole network posture in order to route one client.
  3. An external launcher wrapper that reads scutil --proxy, probes the reported port, and re-runs ocx start --socks5 127.0.0.1:<port> before the client launches. This works and is effectively the macOS implementation of the missing slice, but it lives outside OpenCodex, has to re-implement the --socks5 plumbing, and every macOS user would have to reinvent it.

I searched existing issues and the documentation for an existing macOS proposal and found none; #1525 is Windows-scoped and closed.

Additional context

Related: #1525 (Windows system proxy auto-detect with smart fallback — the slice 1 precedent), #2894 (SOCKS5 outbound provider calls, including fail-fast on unsupported proxy schemes), #5119 (SOCKS5 routing documentation alignment).

Source pointers: src/config/proxy-env.ts — applyProxyEnvWith() holds the "auto" branch and documents the injectable platform / reader test seam; the Windows reader is readWindowsSystemProxy.

I am willing to implement the macOS slice with regression tests against the existing seam if the direction above is acceptable. I would just like the answers to the two design questions first, so the tests assert the behaviour you actually want.

Checks

  • I searched existing issues and documentation.
  • This request describes a concrete OpenCodex workflow rather than merely naming a desired technology.
  • I removed secrets and personal data.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestplatformOS/service/tray/ACL (Windows-heavy, not Windows-only)proxyHTTP proxy, routing, reverse-proxy / management auth

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions