unify bridge auth and server env names

This commit is contained in:
Haitao Pan 2026-04-14 23:44:46 +08:00
parent 235af032b4
commit cf92887eb1
7 changed files with 52 additions and 17 deletions

View File

@ -52,7 +52,7 @@
bridge 的认证与跨域规则来自:
- `ACP_AUTH_TOKEN`
- `BRIDGE_AUTH_TOKEN`
- `ACP_ALLOWED_ORIGINS`
默认 allowed origins
@ -67,8 +67,8 @@ bridge 的认证与跨域规则来自:
- 空 `Origin` 默认允许。
- `:*` 表示前缀匹配,例如 `http://localhost:*`
- auth 使用 bearer header。
- 如果 `ACP_AUTH_TOKEN` 为空,则接受任意非空 bearer header
- 如果 `ACP_AUTH_TOKEN` 非空,则必须匹配裸 token 或 `Bearer <token>`
- 如果 `BRIDGE_AUTH_TOKEN` 为空,则 bridge auth 默认放行
- 如果 `BRIDGE_AUTH_TOKEN` 非空,则必须匹配裸 token 或 `Bearer <token>`
错误行为:
@ -165,7 +165,7 @@ HTTP 与 WebSocket 统一使用 JSON-RPC 2.0 结构:
说明:
- `bridgeOrigin` 读取 `BRIDGE_PUBLIC_BASE_URL`
- `bridgeOrigin` 读取 `BRIDGE_SERVER_URL`
- 默认值:`https://xworkmate-bridge.svc.plus`
## 4. Bridge JSON-RPC Methods
@ -816,13 +816,13 @@ bridge 在 session 执行期间会通过 `session.update` 推送通知,统一
| 变量 | 默认值 | 作用 |
| --- | --- | --- |
| `ACP_LISTEN_ADDR` | `127.0.0.1:8787` | bridge listen 地址 |
| `ACP_AUTH_TOKEN` | 空 | bridge bearer 校验 token |
| `BRIDGE_AUTH_TOKEN` | 空 | bridge bearer 校验 token |
| `ACP_ALLOWED_ORIGINS` | `https://xworkmate.svc.plus,http://localhost:*,http://127.0.0.1:*` | bridge allowed origins |
| `ACP_MULTI_AGENT_ENABLED` | `true` | `acp.capabilities` 中的 `multiAgent` 开关 |
| `ACP_MULTI_AGENT_MODEL` | `gpt-4o` | multi-agent 默认模型 |
| `BRIDGE_PUBLIC_BASE_URL` | `https://xworkmate-bridge.svc.plus` | bootstrap health 中的 public base URL |
| `BRIDGE_SERVER_URL` | `https://xworkmate-bridge.svc.plus` | bootstrap health 与外部调用统一使用的 bridge base URL |
| `INTERNAL_SERVICE_TOKEN` | 空 | upstream provider / gateway 优先使用的内部服务 token |
| `BRIDGE_AUTH_TOKEN` | 空 | `INTERNAL_SERVICE_TOKEN` 的 fallback用于 upstream forwarding |
| `BRIDGE_AUTH_TOKEN` | 空 | bridge 入站 bearer token`INTERNAL_SERVICE_TOKEN` 为空时也作为 upstream forwarding token |
| `IMAGE` | 空 | `/api/ping` 版本信息来源 |
### 9.2 Gemini adapter

View File

@ -159,10 +159,10 @@ single-agent 主链路优先走 bridge 内建 provider catalog。`runSingleAgent
### 4.1 Auth
bridge 主入口使用 `ACP_AUTH_TOKEN` 驱动的 bearer auth
bridge 主入口使用 `BRIDGE_AUTH_TOKEN` 驱动的 bearer auth
- 如果配置了 token则必须完全匹配该 token 或 `Bearer <token>`
- 如果未配置 token只要求存在非空 bearer header
- 如果未配置 token默认放行
Gemini adapter 的 auth 更宽松:

View File

@ -60,7 +60,7 @@ APP-facing bridge 主控面。负责 HTTP / WebSocket 路由、JSON-RPC method d
- `func (s *Server) HandleBridgeBootstrapHealth(w http.ResponseWriter, r *http.Request)`
- 参数:标准 HTTP writer/request。
- 返回:无显式返回;输出 bridge health JSON。
- 副作用:读取 `BRIDGE_PUBLIC_BASE_URL`。
- 副作用:读取 `BRIDGE_SERVER_URL`。
- 场景bootstrap 自检或部署健康检查。
- `func RunStdio(input io.Reader, output io.Writer)`

View File

@ -1,11 +1,11 @@
# Example configuration for xworkmate-bridge public ingress and provider sync.
# The bridge itself validates inbound requests with:
# Authorization: Bearer ${ACP_AUTH_TOKEN}
# Authorization: Bearer ${BRIDGE_AUTH_TOKEN}
# Each external provider can also carry its own outbound Authorization header.
bridge:
listenAddr: 127.0.0.1:8787
authToken: ${ACP_AUTH_TOKEN}
authToken: ${BRIDGE_AUTH_TOKEN}
allowedOrigins:
- https://xworkmate.svc.plus
- http://localhost:*
@ -37,6 +37,6 @@ providers:
enabled: true
notes:
- The bridge reads its own auth token from ACP_AUTH_TOKEN.
- The bridge reads its own auth token from BRIDGE_AUTH_TOKEN.
- The provider catalog is normally synced through xworkmate.providers.sync.
- session.message should resend routing on follow-up turns for codex and opencode.

View File

@ -22,7 +22,7 @@ func (s *Server) HandleBridgeBootstrapHealth(w http.ResponseWriter, r *http.Requ
}
func bridgePublicBaseURL() string {
value := strings.TrimSpace(shared.EnvOrDefault("BRIDGE_PUBLIC_BASE_URL", "https://xworkmate-bridge.svc.plus"))
value := strings.TrimSpace(shared.EnvOrDefault("BRIDGE_SERVER_URL", "https://xworkmate-bridge.svc.plus"))
if value == "" {
return "https://xworkmate-bridge.svc.plus"
}

View File

@ -96,7 +96,7 @@ func NewServer() *Server {
gateway: gatewayruntime.NewManager(),
providerCatalog: providerCatalog,
providerOrder: providerOrder,
authService: service.NewStaticTokenAuthService(strings.TrimSpace(shared.EnvOrDefault("ACP_AUTH_TOKEN", ""))),
authService: service.NewStaticTokenAuthService(strings.TrimSpace(shared.EnvOrDefault("BRIDGE_AUTH_TOKEN", ""))),
}
}
@ -300,7 +300,14 @@ func (s *Server) HandleRPC(w http.ResponseWriter, r *http.Request) {
}
func (s *Server) authorized(r *http.Request) bool {
if s == nil || s.authService == nil {
if s == nil {
return false
}
expected := strings.TrimSpace(shared.EnvOrDefault("BRIDGE_AUTH_TOKEN", ""))
if expected == "" {
return true
}
if s.authService == nil {
return false
}
return s.authService.ValidateAuthorizationHeader(r.Header.Get("Authorization"))

View File

@ -110,7 +110,26 @@ func TestHandleRPCAllowsPreflightForConfiguredOrigin(t *testing.T) {
}
}
func TestHandleRPCRequiresBearerAuthorization(t *testing.T) {
func TestHandleRPCAllowsRequestsWhenBridgeAuthTokenUnset(t *testing.T) {
server := NewServer()
recorder := httptest.NewRecorder()
request := httptest.NewRequest(
http.MethodPost,
"http://127.0.0.1/acp/rpc",
strings.NewReader(`{"jsonrpc":"2.0","id":1,"method":"acp.capabilities"}`),
)
request.Header.Set("Content-Type", "application/json")
server.HandleRPC(recorder, request)
if recorder.Code != http.StatusOK {
t.Fatalf("expected 200 when BRIDGE_AUTH_TOKEN is unset, got %d", recorder.Code)
}
}
func TestHandleRPCRequiresBearerAuthorizationWhenBridgeAuthTokenConfigured(t *testing.T) {
t.Setenv("BRIDGE_AUTH_TOKEN", "bridge-test-token")
server := NewServer()
recorder := httptest.NewRecorder()
request := httptest.NewRequest(
@ -195,6 +214,14 @@ func TestHandleRPCCapabilitiesStillReturnsJSONResult(t *testing.T) {
}
}
func TestAuthorizedAllowsRequestsWhenBridgeAuthTokenUnset(t *testing.T) {
server := NewServer()
request := httptest.NewRequest(http.MethodGet, "http://127.0.0.1/acp", nil)
if !server.authorized(request) {
t.Fatal("expected requests to be authorized when BRIDGE_AUTH_TOKEN is unset")
}
}
func TestHandleRPCCapabilitiesReturnsCanonicalProviderContract(t *testing.T) {
server := NewServer()
recorder := httptest.NewRecorder()
@ -338,6 +365,7 @@ func mustStringList(t *testing.T, value any) []string {
func TestHandleWebSocketRequiresBearerAuthorization(t *testing.T) {
t.Setenv("ACP_ALLOWED_ORIGINS", "https://xworkmate.svc.plus")
t.Setenv("BRIDGE_AUTH_TOKEN", "bridge-test-token")
server := NewServer()
recorder := httptest.NewRecorder()