Files
coder/coderd/aibridge/aibridge.go
T
Sas Swart d179266cc7 feat: capture, persist, and strip Agent Firewall correlation headers in AI Bridge (#26529)
Wire the Agent Firewall correlation headers
(`X-Coder-Agent-Firewall-Session-Id` and
`X-Coder-Agent-Firewall-Sequence-Number`) through the AI Bridge
interception processor so that each interception is linked to its
originating firewall session.

Closes https://linear.app/codercom/issue/AIGOV-259

> Generated by Coder Agents on behalf of @SasSwart

**Data flow:**
`request header` → `bridge.go` reads + strips → `InterceptionRecord` →
`translator.go` → proto `RecordInterceptionRequest` →
`aibridgedserver.go` → DB
2026-06-30 14:01:27 +02:00

82 lines
3.1 KiB
Go

// Package aibridge provides utilities for the AI Bridge feature.
package aibridge
import (
"net/http"
"strings"
)
// HeaderCoderToken is a header set by clients opting into BYOK
// (Bring Your Own Key) mode. It carries the Coder token so
// that Authorization and X-Api-Key can carry the user's own LLM
// credentials. When present, AI Bridge forwards the user's LLM
// headers unchanged instead of injecting the centralized key.
//
// The AI Bridge proxy also sets this header automatically for clients
// that use per-user LLM credentials but cannot set custom headers.
const HeaderCoderToken = "X-Coder-AI-Governance-Token" //nolint:gosec // This is a header name, not a credential.
// HeaderCoderRequestID is a header set by aibridgeproxyd on each
// request forwarded to aibridged for cross-service log correlation.
const HeaderCoderRequestID = "X-Coder-AI-Governance-Request-Id"
// HeaderAgentFirewallSessionID is injected by Agent Firewall on requests
// routed through it. It carries the firewall session UUID so that AI
// Gateway can correlate interceptions with firewall audit events.
const HeaderAgentFirewallSessionID = "X-Coder-Agent-Firewall-Session-Id"
// HeaderAgentFirewallSequenceNumber is injected alongside the session ID
// by Agent Firewall. It carries a monotonically increasing sequence
// number that orders network requests within a single firewall session.
const HeaderAgentFirewallSequenceNumber = "X-Coder-Agent-Firewall-Sequence-Number"
// Copilot provider.
const (
ProviderCopilotBusiness = "copilot-business"
HostCopilotBusiness = "api.business.githubcopilot.com"
ProviderCopilotEnterprise = "copilot-enterprise"
HostCopilotEnterprise = "api.enterprise.githubcopilot.com"
)
// ChatGPT provider.
const (
ProviderChatGPT = "chatgpt"
HostChatGPT = "chatgpt.com"
BaseURLChatGPT = "https://" + HostChatGPT + "/backend-api/codex"
)
// API route prefixes for the AI Gateway and legacy AI Bridge endpoints.
const (
// AIGatewayRootPath is the URL prefix the AI Gateway handler
// registers all of its routes under.
AIGatewayRootPath = "/api/v2/ai-gateway"
// AIBridgeRootPath is the legacy prefix kept for backward compatibility.
AIBridgeRootPath = "/api/v2/aibridge"
)
// IsBYOK reports whether the request is using BYOK mode, determined
// by the presence of the X-Coder-AI-Governance-Token header.
func IsBYOK(header http.Header) bool {
return strings.TrimSpace(header.Get(HeaderCoderToken)) != ""
}
// ExtractAuthToken extracts a token from HTTP headers.
// It checks the BYOK header first (set by clients opting into BYOK),
// then falls back to Authorization: Bearer and X-Api-Key for direct
// centralized mode. If none are present, an empty string is returned.
func ExtractAuthToken(header http.Header) string {
if token := strings.TrimSpace(header.Get(HeaderCoderToken)); token != "" {
return token
}
if auth := strings.TrimSpace(header.Get("Authorization")); auth != "" {
fields := strings.Fields(auth)
if len(fields) == 2 && strings.EqualFold(fields[0], "Bearer") {
return fields[1]
}
}
if apiKey := strings.TrimSpace(header.Get("X-Api-Key")); apiKey != "" {
return apiKey
}
return ""
}