Skip to content

Respect Claude OAuth app quota#40

Merged
lawrencecchen merged 2 commits into
mainfrom
fix/claude-oauth-app-quota
Jul 3, 2026
Merged

Respect Claude OAuth app quota#40
lawrencecchen merged 2 commits into
mainfrom
fix/claude-oauth-app-quota

Conversation

@lawrencecchen

@lawrencecchen lawrencecchen commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Summary

  • include Claude seven_day_oauth_apps in scheduler/status usage windows
  • show the Claude OAuth app weekly bucket in sr status
  • stop forwarding client IP attribution headers upstream

Tests

  • go test ./...

Live logs showed Anthropic rejecting claude requests on anthropic-ratelimit-unified-7d_oi while sr only displayed the ordinary weekly bucket.


View with Codesmith Autofix with Codesmith
Need help on this PR? Tag /codesmith with what you need. Autofix is disabled.


Summary by cubic

Respect Claude OAuth app weekly quota in routing and status to prevent 429s when the OAuth bucket is exhausted. Also stop forwarding client IP headers to upstream for cleaner attribution and privacy.

  • Bug Fixes
    • Include the SevenDayOAuthApps window in scheduling and scoring, with a 7-day duration; route away when saturated.
    • Show the OAuth bucket in sr status: new "OAuth wk" column and "OAuth apps (weekly)" label.
    • Strip outbound client IP attribution headers (Forwarded, X-Forwarded-For, X-Forwarded-Host, X-Forwarded-Proto, X-Forwarded-Ssl, X-Real-IP) for HTTP and WebSocket requests.
    • Add tests for OAuth apps window handling and header stripping.

Written for commit 2770090. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • New Features

    • Added a new weekly OAuth apps usage view in Claude profiles, with an additional usage column and updated labels for clearer quota tracking.
    • Improved usage reporting so weekly OAuth app limits are included alongside existing usage windows.
  • Bug Fixes

    • Proxy requests now remove client IP forwarding headers before reaching upstream services, improving request privacy and consistency.

@coderabbitai

coderabbitai Bot commented Jul 3, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds a new "oauth-apps-weekly" Claude usage window (construction, detection, labeling, grid column, and scoring) across cmd/subrouter/sr.go and internal/proxy/proxy.go with tests, and introduces a stripOutboundForwardingHeaders helper to remove client-IP forwarding headers from outbound HTTP/WebSocket proxy requests, replacing the Director-based reverse proxy with a Rewrite-based one.

Changes

Claude OAuth apps weekly usage window

Layer / File(s) Summary
Window construction, detection, and labeling
cmd/subrouter/sr.go
Adds windowSeconds/LimitWindowSeconds support, 5h/7d constants, an oauth-apps-weekly window from usage.SevenDayOAuthApps, an isClaudeOAuthAppsWeeklyWindow matcher, and an "OAuth apps (weekly)" label.
OAuth wk grid column
cmd/subrouter/sr.go
Adds a conditional "OAuth wk" grid column, widens it on terminal expansion, and populates its values via the new matcher.
sr.go tests
cmd/subrouter/sr_test.go
Updates grid labeling test with the new window/label and adds TestClaudeUsageWindowsIncludeOAuthAppsWeekly plus an srFloatPtr helper.
proxy.go window entry and tests
internal/proxy/proxy.go, internal/proxy/claude_ratelimit_routing_test.go
Adds the oauth-apps-weekly window entry to claudeUsageWindows and a test verifying its presence, window length, and headroom scoring.

Strip outbound forwarding headers in proxy

Layer / File(s) Summary
stripOutboundForwardingHeaders helper and wiring
internal/proxy/proxy.go
Adds a helper removing Forwarded/X-Forwarded-*/X-Real-IP headers, applies it to outbound HTTP requests and WebSocket dial headers, and replaces the Director-based reverse proxy with a Rewrite-based httputil.ReverseProxy.
Header-stripping test
internal/proxy/claude_ratelimit_routing_test.go
Adds TestHTTPProxyDoesNotForwardClientIPHeaders verifying spoofable client IP headers are stripped before reaching upstream.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant Proxy as HTTPProxy
    participant Upstream

    Client->>Proxy: Request with Forwarded/X-Forwarded-*/X-Real-IP headers
    Proxy->>Proxy: Rewrite: set upstream URL
    Proxy->>Proxy: stripOutboundForwardingHeaders(req.Header)
    Proxy->>Upstream: Forward request without client IP headers
    Upstream-->>Proxy: Response
    Proxy-->>Client: Response
Loading

Possibly related PRs

  • manaflow-ai/subrouter#5: Both PRs modify Claude usage scoring in internal/proxy/proxy.go, with this PR adding an oauth-apps-weekly window feeding headroom/exhaustion logic used by the related PR's failover behavior.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: adding support for Claude OAuth app quota handling.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/claude-oauth-app-quota

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@lawrencecchen lawrencecchen merged commit 6878d9a into main Jul 3, 2026
4 of 5 checks passed
@lawrencecchen lawrencecchen deleted the fix/claude-oauth-app-quota branch July 3, 2026 03:50

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2770090d7f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread internal/proxy/proxy.go
Comment on lines +1488 to +1489
Rewrite: func(pr *httputil.ProxyRequest) {
pr.SetURL(upstream)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve raw query strings in the proxy rewrite

Switching this handler to ReverseProxy.Rewrite changes query forwarding semantics: httputil cleans Out.URL.RawQuery before this callback runs, whereas the previous Director-based NewSingleHostReverseProxy path preserved the raw query here. For proxied API requests whose query contains a raw semicolon, such as cursor/filter values that were not percent-encoded, Go treats that parameter as unparsable and it is now dropped before reaching the upstream. If the goal is only to suppress forwarding headers, copy the inbound raw query back after SetURL (or otherwise preserve it) so the proxy remains transparent for those requests.

Useful? React with 👍 / 👎.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
cmd/subrouter/sr.go (1)

1269-1301: 📐 Maintainability & Code Quality | 🟠 Major | 🏗️ Heavy lift

Duplicate claudeUsageWindows implementation risks drift.

This closure is now byte-for-byte equivalent to claudeUsageWindows in internal/proxy/proxy.go (lines 687-730, see relevant snippet), down to the windowSeconds argument, the fixed 5h/7d constants, and the oauth-apps-weekly entry added by this very PR. Having two copies of this logic means every future window addition (or bucket removal) must be manually kept in sync across cmd/subrouter/sr.go and internal/proxy/proxy.go — exactly the kind of drift this PR is fixing (the routing side started respecting the OAuth-apps bucket while sr status display lagged behind). Consider moving this into a shared package (e.g. internal/accounts or a new internal/agents/claude helper) that both sr.go and proxy.go import.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cmd/subrouter/sr.go` around lines 1269 - 1301, The `claudeUsageWindows` logic
in `cmd/subrouter/sr.go` is duplicated from the same helper in
`internal/proxy/proxy.go`, so future usage-window changes will drift between the
two call sites. Extract the shared conversion logic into a common helper in a
reusable package such as `internal/accounts` or `internal/agents/claude`, then
update both `claudeUsageWindows` and the proxy-side caller to use that single
implementation while preserving the existing window names, constants, and
`ExtraUsage` handling.
🧹 Nitpick comments (1)
internal/proxy/claude_ratelimit_routing_test.go (1)

141-202: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Consider adding WebSocket coverage for header stripping.

This test only covers the HTTP proxy path. proxyWebSocket also calls stripOutboundForwardingHeaders (proxy.go line 1594) for the dial headers, but that path isn't exercised by a test here. A WebSocket-based variant (dialing through the subrouter to a mock upstream WS server and inspecting the handshake headers it receives) would close this gap.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/proxy/claude_ratelimit_routing_test.go` around lines 141 - 202, The
current coverage only verifies header stripping in the HTTP proxy path;
`proxyWebSocket` also relies on `stripOutboundForwardingHeaders`, but that
behavior is untested. Add a WebSocket test alongside
`TestHTTPProxyDoesNotForwardClientIPHeaders` that dials through
`Server.Handler()` to a mock upstream WS server, inspects the handshake headers
received by the upstream, and asserts the same client IP forwarding headers are
removed. Use `proxyWebSocket` and `stripOutboundForwardingHeaders` as the key
symbols to locate the affected path.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@cmd/subrouter/sr.go`:
- Around line 1269-1301: The `claudeUsageWindows` logic in `cmd/subrouter/sr.go`
is duplicated from the same helper in `internal/proxy/proxy.go`, so future
usage-window changes will drift between the two call sites. Extract the shared
conversion logic into a common helper in a reusable package such as
`internal/accounts` or `internal/agents/claude`, then update both
`claudeUsageWindows` and the proxy-side caller to use that single implementation
while preserving the existing window names, constants, and `ExtraUsage`
handling.

---

Nitpick comments:
In `@internal/proxy/claude_ratelimit_routing_test.go`:
- Around line 141-202: The current coverage only verifies header stripping in
the HTTP proxy path; `proxyWebSocket` also relies on
`stripOutboundForwardingHeaders`, but that behavior is untested. Add a WebSocket
test alongside `TestHTTPProxyDoesNotForwardClientIPHeaders` that dials through
`Server.Handler()` to a mock upstream WS server, inspects the handshake headers
received by the upstream, and asserts the same client IP forwarding headers are
removed. Use `proxyWebSocket` and `stripOutboundForwardingHeaders` as the key
symbols to locate the affected path.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: df5eb648-2a8d-462b-be2c-9d65644517a7

📥 Commits

Reviewing files that changed from the base of the PR and between 63b57ec and 2770090.

📒 Files selected for processing (4)
  • cmd/subrouter/sr.go
  • cmd/subrouter/sr_test.go
  • internal/proxy/claude_ratelimit_routing_test.go
  • internal/proxy/proxy.go

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.

1 participant