fix: route --record proxy through the configured models gateway - #3428
Conversation
Ensures that recorded sessions using a models gateway keep auth and routing working by forwarding to the gateway rather than provider endpoints directly, while saving cassette entries under canonical provider URLs for replay. Assisted-By: claude-opus-4-5
Never forward Desktop token to non-Docker gateways; reject requests with unknown/missing X-Cagent-Forward host; validate gateway URL at startup; avoid logging full target URLs; preserve percent-encoded paths. Assisted-By: Claude
docker-agent
left a comment
There was a problem hiding this comment.
Assessment: 🟡 NEEDS ATTENTION
This PR correctly routes the --record proxy through the configured models gateway and hardens credential handling. The URL construction, cassette URL override via context (WithRecordURL), gateway URL validation at startup, and Docker-trusted gateway pass-through are all sound. One credential-handling edge case in the newly introduced gatewayAuthHeaderUpdater warrants attention.
| // credential headers the client set and re-apply the provider env key | ||
| // it would have sent to the gateway directly (SDKs default to env keys | ||
| // when no gateway token is available). | ||
| return func(host string, req *http.Request) { |
There was a problem hiding this comment.
[MEDIUM] gatewayAuthHeaderUpdater silently strips credentials for unrecognized provider hosts
When a non-Docker third-party gateway is configured and the X-Cagent-Forward host is not one of the 5 hard-coded providers (api.openai.com, api.anthropic.com, generativelanguage.googleapis.com, api.mistral.ai, openrouter.ai), envAPIKeyForHost returns "", so the if guard at line 195 prevents APIKeyHeaderUpdater from being called. However, the three Header.Del calls above it (lines 192–194) have already executed unconditionally, stripping Authorization, X-Api-Key, and X-Goog-Api-Key from the outgoing request.
The result is that any custom or unrecognized provider endpoint (Azure OpenAI, Cohere, Bedrock-compatible, private OpenAI-compatible deployments, etc.) routed through a non-Docker gateway will have all credential headers deleted with no replacement, producing silent 401 errors from the gateway. The comment says "re-apply the provider env key it would have sent to the gateway directly", but the re-apply is skipped for the unknown-host case with no warning or fallback.
Suggestion: For unrecognized hosts, preserve the original Authorization header instead of deleting it (the goal is stripping the Docker Desktop token specifically, which is only present for Docker-trusted URLs — the early return above already handles that case):
return func(host string, req *http.Request) {
if envAPIKeyForHost(host) != "" {
// Known provider: strip Desktop token and apply provider env key
req.Header.Del("Authorization")
req.Header.Del("X-Api-Key")
req.Header.Del("X-Goog-Api-Key")
APIKeyHeaderUpdater(host, req)
}
// Unknown host: leave whatever credentials the client sent intact
}…odels gateway)
When
docker agent run <agent> --recordwas used with a models gateway configured, the recording proxy bypassed the gateway entirely and forwarded requests directly to providers' public endpoints. This caused HTTP 401 errors (e.g. Anthropic "invalid x-api-key") because the gateway's managed credentials were never used, and environment API keys that happened to be set were injected instead.The recording proxy now chains through the configured upstream gateway.
GatewayTargetURLpreserves the gateway's path prefix, query parameters, and percent-encoded paths when rewriting request targets. Cassette entries are stored under the canonical provider URL (viaWithRecordURL) so existing--fakereplay continues to work without modification.The second commit hardens the forwarding path against credential leakage: the Docker Desktop token is never forwarded to non-Docker gateways (credential headers are stripped and provider env keys are re-applied instead); requests with an unknown or missing
X-Cagent-Forwardhost are rejected rather than silently misrouted; the gateway URL is validated at startup; and full target URLs are kept out of logs and cassettes so gateway query parameters that may carry secrets are never recorded. New tests inpkg/fake/proxy_gateway_test.gocover forwarding, auth handling, cassette contents, and credential-leakage prevention.