Skip to content

feat: aibrige BYOK#216

Open
evgeniy-scherbina wants to merge 4 commits intomainfrom
yevhenii/aibridge-byok-v3
Open

feat: aibrige BYOK#216
evgeniy-scherbina wants to merge 4 commits intomainfrom
yevhenii/aibridge-byok-v3

Conversation

@evgeniy-scherbina
Copy link

@evgeniy-scherbina evgeniy-scherbina commented Mar 12, 2026

Changes

aibridge:

  • config/config.go — Added BYOKBearerToken field to Anthropic config for OAuth-based BYOK (Claude Max/Pro).
  • provider/anthropic.goCreateInterceptor detects user LLM credentials from surviving request headers and sets them on the config copy. InjectAuthHeader skips centralized key injection when user credentials are already present (passthrough BYOK).
  • intercept/messages/base.gonewMessagesService uses option.WithAuthToken() (Authorization: Bearer) when BYOKBearerToken is set, otherwise option.WithAPIKey() (X-Api-Key).

How the flow works end-to-end

Centralized: Client sends Authorization: Bearer <coder-token> → http.go extracts token, strips all auth headers → providers get no user credentials → inject centralized key.

BYOK (Claude Max/Pro): Client sends Authorization: Bearer <oauth-token> + X-Coder-AI-Governance-BYOK-Token: <coder-token> → http.go extracts Coder token from BYOK header, strips only BYOK header → Authorization: Bearer <oauth-token> survives → CreateInterceptor picks it up as BYOKBearerToken → SDK sends it via WithAuthToken(). Passthrough: InjectAuthHeader sees existing Authorization, skips injection.

BYOK (personal API key): Client sends X-Api-Key: <api-key> + X-Coder-AI-Governance-BYOK-Token: <coder-token> → same flow but CreateInterceptor picks up X-Api-Key as cfg.Key → SDK sends it via WithAPIKey(). Passthrough: InjectAuthHeader sees existing X-Api-Key, skips injection.

@evgeniy-scherbina evgeniy-scherbina force-pushed the yevhenii/aibridge-byok-v3 branch 2 times, most recently from c2791d1 to d5903b0 Compare March 16, 2026 20:48
@evgeniy-scherbina evgeniy-scherbina force-pushed the yevhenii/aibridge-byok-v3 branch from dc26160 to 597cbf5 Compare March 16, 2026 21:01
Comment on lines +113 to +115
// In centralized mode, http.go strips Authorization and X-Api-Key
// (they carried the Coder token), so neither header is present
// here and cfg keeps the centralized key.
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm confused by this comment, is this a reference to the coder repo? If yes, I would suggest not using http.go and making it clear that the upstream caller needs to strip Authorization and X-API-Key headers.

IIUC, at this point, the headers are as follows:

  1. Centralized: no Authorization header or X-API-Key
  2. BYOK (oauth): Authorization header includes user's oauth token, no X-API-key
  3. BYOK Api key: no Authorization header, X-API-Key is user's API key

Comment on lines +129 to +131
} else if apiKey := r.Header.Get("X-Api-Key"); apiKey != "" {
cfg.Key = apiKey
}
Copy link
Contributor

Choose a reason for hiding this comment

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

From this point on, we no longer know whether this interception is using a centralized (global) key or a BYOK (user's personal) API key, right? This could be useful to store and to show in the logs (the same for BYOK oauth token). For example, if Anthropic returns a 401, we wouldn't know if the failing key is the global key (affecting everyone) or a single user's personal key.

Additionally, this is probably out of scope for this PR, but it might make sense to store this information in the interception so we can later surface it in the UI, wdyt?


// MaskSecret returns the first 4 and last 4 characters of s
// separated by "...", or the full string if 8 characters or fewer.
func MaskSecret(s string) string {
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this a good idea? I think logging the auth mode ("centralized", "byok_bearer", "byok_apikey") rather than a hint of the secret might be cleaner 👀
Additionally, if we need to correlate a failure to a specific user, I believe we already log this in some cases.

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