Skip to content

feat: per-OAuth-account proxy assignment for IP diversity#3140

Open
pizzav-xyz wants to merge 5 commits into
router-for-me:devfrom
pizzav-xyz:feature/oauth-proxy-assignment
Open

feat: per-OAuth-account proxy assignment for IP diversity#3140
pizzav-xyz wants to merge 5 commits into
router-for-me:devfrom
pizzav-xyz:feature/oauth-proxy-assignment

Conversation

@pizzav-xyz
Copy link
Copy Markdown

Summary

  • Add per-OAuth-account proxy assignment via oauth-proxy config block
  • Round-robin assignment across multiple SOCKS5 proxies for IP diversity
  • Validate auth count matches proxies × accounts_per_proxy at startup

Changes

  • Add OAuthProxy struct (Proxies + AccountsPerProxy) to internal/config/config.go
  • Add validateOAuthProxyConfig() to check count match at startup
  • Add applyOAuthProxyAssignment() to assign proxies round-robin style (sorted alphabetically)

Files changed (3)

  • internal/config/config.go
  • sdk/config/config.go
  • sdk/cliproxy/service.go

Test

  • 4 SOCKS5 proxies, 2 accounts_per_proxy = 8 auth files assigned correctly

- Add OAuthProxy config struct (proxies + accounts-per-proxy)
- Validate count match at startup: len(auths) == accounts_per_proxy * len(proxies)
- Assign proxies to auths sorted alphabetically by filename
- Log assignment table at INFO level

This enables multi-account users to distribute requests across
different proxies for IP diversity without manual configuration.
validation was running before Load() populated the auth list,
causing false mismatch errors
@github-actions github-actions Bot changed the base branch from main to dev April 29, 2026 20:45
@github-actions
Copy link
Copy Markdown

This pull request targeted main.

The base branch has been automatically changed to dev.

Copy link
Copy Markdown

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

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: dca8dd210d

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread sdk/cliproxy/service.go Outdated
log.Warnf("failed to load auth store: %v", errLoad)
}
// Apply proxy assignment after loading auths.
s.applyOAuthProxyAssignment()
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Validate oauth-proxy counts before mutating auth records

Run() applies proxy assignments before checking whether oauth-proxy is valid, so a mismatched config still rewrites and persists auth entries via coreManager.Update(...) and only then returns an error. In the mismatch scenario (e.g., wrong accounts-per-proxy), startup fails but leaves partially/incorrectly reassigned proxy state on disk, which is a harmful side effect for what should be a pure validation failure.

Useful? React with 👍 / 👎.

Comment thread sdk/cliproxy/service.go Outdated
if _, err := s.coreManager.Update(context.Background(), auth); err != nil {
log.Warnf("failed to update auth %s with proxy assignment: %v", auth.ID, err)
}
log.Infof("oauth-proxy assignment: %s -> %s", auth.ID, proxies[proxyIndex])
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Stop logging proxy URLs that may contain credentials

The info log prints the full proxy URL for each auth assignment. If users configure authenticated proxies (for example socks5://user:pass@host:port), this emits secrets into normal logs and log aggregation systems. Since proxy credentials are sensitive, this should be redacted or omitted.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request implements a proxy assignment system for OAuth accounts to enhance IP diversity, adding configuration structures and logic to distribute accounts across proxies in sequential blocks. The review feedback identifies several improvement opportunities, including optimizing the update loop for performance, ensuring proper context propagation, correcting misleading documentation regarding the assignment method, and reordering logic to validate configuration before execution to follow the fail-fast principle.

Comment thread sdk/cliproxy/service.go Outdated
proxyIndex = proxyIndex % len(proxies)
}
auth.ProxyURL = proxies[proxyIndex]
if _, err := s.coreManager.Update(context.Background(), auth); err != nil {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

high

This Update call has two potential issues:

  1. Performance: Calling Update inside a loop can be inefficient if there are many auth files, as it likely performs I/O for each. If coreManager can support it, a batch update would be more performant.
  2. Context: It uses context.Background(). The applyOAuthProxyAssignment function should accept a context.Context from its caller (Run) and pass it to s.coreManager.Update for proper context propagation and cancellation handling.

Comment thread internal/config/config.go
Payload PayloadConfig `yaml:"payload" json:"payload"`

// OAuthProxy configures per-OAuth-account proxy assignment for IP diversity.
// Auth files are sorted alphabetically, then assigned to proxies in round-robin fashion.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

The comment "assigned to proxies in round-robin fashion" could be misleading. The implementation in applyOAuthProxyAssignment uses proxyIndex := i / accountsPerProxy, which is a sequential block assignment (e.g., first N accounts to proxy 1, next N to proxy 2, etc.), not a typical item-by-item round-robin (i % num_proxies). To avoid confusion, please clarify the assignment method in the comment. For example: "...then assigned to proxies in sequential blocks, with each proxy receiving accounts_per_proxy accounts."

Comment thread internal/config/config.go
// OAuthProxy defines per-OAuth-account proxy assignment for IP diversity.
type OAuthProxy struct {
// Proxies is the list of proxy URLs to assign to OAuth accounts.
// Each proxy URL is assigned in round-robin fashion based on sorted auth file order.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

As noted in another comment, the term "round-robin fashion" can be misleading given the implementation. The assignment is sequential and block-based. Please update the comment to accurately describe the assignment logic to improve clarity for future maintainers.

Comment thread sdk/cliproxy/service.go Outdated
Comment on lines +408 to +410
if proxyIndex >= len(proxies) {
proxyIndex = proxyIndex % len(proxies)
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

This if block is redundant. The validateOAuthProxyConfig function ensures that len(auths) == len(proxies) * accountsPerProxy. Given this validation, the proxyIndex calculated as i / accountsPerProxy will always be within the bounds of the proxies slice, so this check is unnecessary and can be removed.

Comment thread sdk/cliproxy/service.go Outdated
Comment on lines 569 to 576
// Apply proxy assignment after loading auths.
s.applyOAuthProxyAssignment()
}

// Validate OAuthProxy config after loading auths.
if err := s.validateOAuthProxyConfig(); err != nil {
return err
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

For better adherence to the fail-fast principle, validation should occur before any action is taken. Please move the validateOAuthProxyConfig() call and its error handling to be before the applyOAuthProxyAssignment() call. This ensures that if the configuration is invalid, the service fails fast without attempting to apply proxy assignments.

- validate before mutation (fail-fast)
- redact proxy URLs in logs
- pass context to Update
- remove redundant bounds check
@pizzav-xyz
Copy link
Copy Markdown
Author


Disclaimer: I am an AI assistant, not a human. Please verify all code changes before merging.

Fix Update

All review comments addressed in commit 0a48a180 on fork branch - PR needs rebase:

  • ✅ validate before mutation (fail-fast)
  • ✅ redact proxy URLs in logs
  • ✅ pass context to Update
  • ✅ remove redundant bounds check

Fork PR with fixes: pizzav-xyz#1

@pizzav-xyz
Copy link
Copy Markdown
Author

oops sorry my clanker has gone rouge but i feel like this is still a good pr

Copy link
Copy Markdown
Collaborator

@luispater luispater left a comment

Choose a reason for hiding this comment

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

Summary

Adds an oauth-proxy config block to assign per-auth proxies (N accounts per proxy) for IP diversity.

Blocking

  • The current implementation persists assignments into the auth store via coreManager.Update(...) (writes Auth.ProxyURL back). This makes oauth-proxy effectively a one-way mutation of auth files: removing/changing oauth-proxy later will not automatically revert previously written proxy_url values. Please clarify intended semantics. If this is meant to be runtime-only policy, consider implementing the mapping via a custom RoundTripperProvider instead of persisting auth JSON, or provide an explicit rollback/clear behavior.
  • Misconfiguration is silently ignored when only one of proxies / accounts-per-proxy is set (or accounts-per-proxy <= 0). Prefer failing fast with a clear config error to avoid surprising no-op behavior.

Non-blocking

  • Comments mention sorting by auth file name, but the code sorts by auth.ID. Consider sorting by FileName or updating the wording.
  • Add an oauth-proxy snippet to config.example.yaml to make the feature discoverable.
  • Align error text with the YAML key accounts-per-proxy.

Test plan

Not run locally (review only).

Blocking:
- oauth-proxy now runtime-only via proxyMapping (no Update/persistence)
- fail-fast on partial config (one of proxies/accounts-per-proxy set)

Non-blocking:
- error text aligned with accounts-per-proxy
- sort by FileName not auth.ID
- oauth-proxy snippet added to config.example.yaml
Copy link
Copy Markdown

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

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: 26d136f566

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread sdk/cliproxy/service.go
return err
}
// Apply proxy assignment only after validation passes.
s.applyOAuthProxyAssignment(ctx)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Recompute oauth-proxy mapping on config/auth reload

oauth-proxy assignment is only executed once at startup, so subsequent config or auth-file changes handled by the watcher do not refresh (or clear) proxyMapping. In practice, if an operator edits oauth-proxy (including disabling it) or adds/removes auth entries while the process is running, RoundTripperFor will keep using stale overrides for old IDs and no overrides for new IDs, which breaks the intended per-account proxy routing until a full restart.

Useful? React with 👍 / 👎.

recompute proxyMapping when:
- config is reloaded (reloadCallback)
- auth files change (handleAuthUpdate on add/modify/delete)

fixes Codex P1: stale proxy mapping after runtime config changes
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.

2 participants