Repository navigation
fix(proxy): fail closed for OAuth usage account proxies - #3051
Conversation
There was a problem hiding this comment.
Code Review
This pull request introduces a fail-closed mechanism for provider limits when using account-scoped proxies with OAuth connections. Specifically, it adds helper functions to detect account-scoped proxy resolutions and prevent falling back to direct retries without a proxy if a proxy-related network failure occurs. Additionally, comprehensive unit tests have been added under tests/unit/ to verify this behavior for both Claude and non-Claude OAuth providers, while ensuring that non-account proxy failures still preserve the existing direct retry behavior. No review comments were provided, and the changes look solid and conform to the repository style guides.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
5350b5e
into
cheaper-inference:release/v3.8.8
…add contributor hall Audited all 687 commits / 60 release/v3.8.8 PRs since v3.8.7 against the CHANGELOG: - Added 5 missing Fixed entries: #3052 (heap-pressure auto-calibration), #3051/#3048 (proxy fail-closed + registry assignments, @terence71-glitch), #3049/#3046 (session-pool fingerprint rotation + claude-web cf_clearance, @oyi77). - Credited previously-uncredited contributors: @branben (#2958 scope fix, #2959 Notion context source) and @JxnLexn (per-API-key stream default mode). - Added an Added entry for the per-API-key stream default mode feature. - Added the "🏆 Contributors" hall (24 contributors), matching the v3.8.6 format. Maintainer fix-PRs (#2966–#3030) are intentionally referenced by their original issue numbers in the body rather than the fix-PR number; @diegosouzapw is in the hall.
* fix(providers): honor configured proxy on Grok Build egress The grok-cli executor reaches Grok Build over raw `https.request()` (forced IPv4, to dodge Cloudflare blocking on the direct path) rather than the process-wide patched `fetch()` that every other executor uses. `https.request()` never consults the proxy AsyncLocalStorage context, so the proxy the caller already pinned upstream in chatHelpers.ts (`runWithProxyContext`) was silently ignored on BOTH grok-cli paths: chat inference (`nativePost`) and OAuth token refresh (`nativeHttpsPost`, POST https://auth.x.ai/oauth2/token). User-visible effect: an operator who assigns a proxy to a Grok Build connection (or provider/global scope) still egresses on the host's real IP — an IP leak that defeats account-isolation/anonymity setups, and breaks Grok Build entirely for operators who must egress through a proxy. Fix is delta-only: `resolveGrokRequestDispatch()` reads the already-resolved proxy via the shared `resolveProxyForRequest()` and returns either an HttpsProxyAgent bound to it, or — when no proxy is configured — the existing forced-IPv4 direct options, unchanged. Only HTTP/HTTPS CONNECT proxies are supported on this path; an explicitly configured proxy of another kind (SOCKS5) fails closed rather than silently leaking direct, matching the fail-closed convention for OAuth/account proxies (#3051). The proxy URL is never logged, so proxy credentials cannot leak into logs. Regression test: tests/unit/grok-cli-proxy-selection.test.ts (RED before the fix — `resolveGrokRequestDispatch` did not exist and both request builders hardcoded `family: 4` with no agent; GREEN after). Co-authored-by: ryanngit <74137224+ryanngit@users.noreply.github.com> Inspired-by: decolua/9router#2343 * chore(changelog): fragment for #7244 --------- Co-authored-by: ryanngit <74137224+ryanngit@users.noreply.github.com>
…add contributor hall Audited all 687 commits / 60 release/v3.8.8 PRs since v3.8.7 against the CHANGELOG: - Added 5 missing Fixed entries: cheaper-inference#3052 (heap-pressure auto-calibration), cheaper-inference#3051/cheaper-inference#3048 (proxy fail-closed + registry assignments, @terence71-glitch), cheaper-inference#3049/cheaper-inference#3046 (session-pool fingerprint rotation + claude-web cf_clearance, @oyi77). - Credited previously-uncredited contributors: @branben (cheaper-inference#2958 scope fix, cheaper-inference#2959 Notion context source) and @JxnLexn (per-API-key stream default mode). - Added an Added entry for the per-API-key stream default mode feature. - Added the "🏆 Contributors" hall (24 contributors), matching the v3.8.6 format. Maintainer fix-PRs (cheaper-inference#2966–cheaper-inference#3030) are intentionally referenced by their original issue numbers in the body rather than the fix-PR number; @diegosouzapw is in the hall.
…inference#7244) * fix(providers): honor configured proxy on Grok Build egress The grok-cli executor reaches Grok Build over raw `https.request()` (forced IPv4, to dodge Cloudflare blocking on the direct path) rather than the process-wide patched `fetch()` that every other executor uses. `https.request()` never consults the proxy AsyncLocalStorage context, so the proxy the caller already pinned upstream in chatHelpers.ts (`runWithProxyContext`) was silently ignored on BOTH grok-cli paths: chat inference (`nativePost`) and OAuth token refresh (`nativeHttpsPost`, POST https://auth.x.ai/oauth2/token). User-visible effect: an operator who assigns a proxy to a Grok Build connection (or provider/global scope) still egresses on the host's real IP — an IP leak that defeats account-isolation/anonymity setups, and breaks Grok Build entirely for operators who must egress through a proxy. Fix is delta-only: `resolveGrokRequestDispatch()` reads the already-resolved proxy via the shared `resolveProxyForRequest()` and returns either an HttpsProxyAgent bound to it, or — when no proxy is configured — the existing forced-IPv4 direct options, unchanged. Only HTTP/HTTPS CONNECT proxies are supported on this path; an explicitly configured proxy of another kind (SOCKS5) fails closed rather than silently leaking direct, matching the fail-closed convention for OAuth/account proxies (cheaper-inference#3051). The proxy URL is never logged, so proxy credentials cannot leak into logs. Regression test: tests/unit/grok-cli-proxy-selection.test.ts (RED before the fix — `resolveGrokRequestDispatch` did not exist and both request builders hardcoded `family: 4` with no agent; GREEN after). Co-authored-by: ryanngit <74137224+ryanngit@users.noreply.github.com> Inspired-by: decolua/9router#2343 * chore(changelog): fragment for cheaper-inference#7244 --------- Co-authored-by: ryanngit <74137224+ryanngit@users.noreply.github.com>
…add contributor hall Audited all 687 commits / 60 release/v3.8.8 PRs since v3.8.7 against the CHANGELOG: - Added 5 missing Fixed entries: cheaper-inference#3052 (heap-pressure auto-calibration), cheaper-inference#3051/cheaper-inference#3048 (proxy fail-closed + registry assignments, @terence71-glitch), cheaper-inference#3049/cheaper-inference#3046 (session-pool fingerprint rotation + claude-web cf_clearance, @oyi77). - Credited previously-uncredited contributors: @branben (cheaper-inference#2958 scope fix, cheaper-inference#2959 Notion context source) and @JxnLexn (per-API-key stream default mode). - Added an Added entry for the per-API-key stream default mode feature. - Added the "🏆 Contributors" hall (24 contributors), matching the v3.8.6 format. Maintainer fix-PRs (cheaper-inference#2966–cheaper-inference#3030) are intentionally referenced by their original issue numbers in the body rather than the fix-PR number; @diegosouzapw is in the hall.
…add contributor hall Audited all 687 commits / 60 release/v3.8.8 PRs since v3.8.7 against the CHANGELOG: - Added 5 missing Fixed entries: cheaper-inference#3052 (heap-pressure auto-calibration), cheaper-inference#3051/cheaper-inference#3048 (proxy fail-closed + registry assignments, @terence71-glitch), cheaper-inference#3049/cheaper-inference#3046 (session-pool fingerprint rotation + claude-web cf_clearance, @oyi77). - Credited previously-uncredited contributors: @branben (cheaper-inference#2958 scope fix, cheaper-inference#2959 Notion context source) and @JxnLexn (per-API-key stream default mode). - Added an Added entry for the per-API-key stream default mode feature. - Added the "🏆 Contributors" hall (24 contributors), matching the v3.8.6 format. Maintainer fix-PRs (cheaper-inference#2966–cheaper-inference#3030) are intentionally referenced by their original issue numbers in the body rather than the fix-PR number; @diegosouzapw is in the hall.
…inference#7244) * fix(providers): honor configured proxy on Grok Build egress The grok-cli executor reaches Grok Build over raw `https.request()` (forced IPv4, to dodge Cloudflare blocking on the direct path) rather than the process-wide patched `fetch()` that every other executor uses. `https.request()` never consults the proxy AsyncLocalStorage context, so the proxy the caller already pinned upstream in chatHelpers.ts (`runWithProxyContext`) was silently ignored on BOTH grok-cli paths: chat inference (`nativePost`) and OAuth token refresh (`nativeHttpsPost`, POST https://auth.x.ai/oauth2/token). User-visible effect: an operator who assigns a proxy to a Grok Build connection (or provider/global scope) still egresses on the host's real IP — an IP leak that defeats account-isolation/anonymity setups, and breaks Grok Build entirely for operators who must egress through a proxy. Fix is delta-only: `resolveGrokRequestDispatch()` reads the already-resolved proxy via the shared `resolveProxyForRequest()` and returns either an HttpsProxyAgent bound to it, or — when no proxy is configured — the existing forced-IPv4 direct options, unchanged. Only HTTP/HTTPS CONNECT proxies are supported on this path; an explicitly configured proxy of another kind (SOCKS5) fails closed rather than silently leaking direct, matching the fail-closed convention for OAuth/account proxies (cheaper-inference#3051). The proxy URL is never logged, so proxy credentials cannot leak into logs. Regression test: tests/unit/grok-cli-proxy-selection.test.ts (RED before the fix — `resolveGrokRequestDispatch` did not exist and both request builders hardcoded `family: 4` with no agent; GREEN after). Co-authored-by: ryanngit <74137224+ryanngit@users.noreply.github.com> Inspired-by: decolua/9router#2343 * chore(changelog): fragment for cheaper-inference#7244 --------- Co-authored-by: ryanngit <74137224+ryanngit@users.noreply.github.com>
Problem
Provider-limits / usage sync resolves the selected connection proxy before fetching usage for OAuth connections. However, if that proxied usage fetch fails with a network/proxy error, the code currently retries the same usage fetch without any proxy:
That direct retry is unsafe for account-isolated setups. If a connection has an explicit account/key proxy configured, provider-limits traffic is account-bound traffic and should not silently fall back to the OmniRoute host IP when the account proxy is unavailable.
This was first identified through Claude/Anthropic hardening, where risky endpoints include:
https://api.anthropic.com/api/oauth/usagehttps://api.anthropic.com/api/claude_cli/bootstraphttps://api.anthropic.com/v1/...But the isolation rule is broader than Claude:
Solution
Add a narrow fail-closed policy in
src/lib/usage/providerLimits.tsfor OAuth connections whose resolved proxy is account-scoped:When this policy applies:
This keeps the change scoped to explicit account isolation:
Tests
Added
tests/unit/provider-limits-proxy-fail-closed.test.ts.Coverage:
Claude provider limits fail closed when an account proxy is unreachablefetchAndPersistProviderLimits()non-Claude OAuth provider limits fail closed when an account proxy is unreachablefetchAndPersistProviderLimits()Claude provider limits preserve direct retry for non-account proxy failuresfetchAndPersistProviderLimits()Targeted test command:
Result:
Typecheck command:
Result: