Tighten bridge endpoint and auth contracts

This commit is contained in:
Haitao Pan 2026-04-21 10:34:44 +08:00
parent 095ef07b61
commit 8e24b697a7
7 changed files with 60 additions and 41 deletions

View File

@ -4,13 +4,13 @@
# BRIDGE_CONFIG_PATH=config.yaml ./xworkmate-bridge serve
# Upstream provider endpoints
# Priority: YAML > Environment Variable (e.g. OPENCLAW_CODEX_URL) > Default Constants
# Priority: YAML > Environment Variable (e.g. CODEX_RPC_URL) > Default Constants
upstream:
gateway_url: "ws://127.0.0.1:18789/"
codex_url: "http://127.0.0.1:9001/acp/rpc"
opencode_url: "http://127.0.0.1:38992/acp/rpc"
gemini_url: "http://127.0.0.1:8791/acp/rpc"
hermes_url: "http://127.0.0.1:3920/acp/rpc"
codex_url: "ws://127.0.0.1:9001/acp"
opencode_url: "http://127.0.0.1:38992"
gemini_url: "http://127.0.0.1:8791"
hermes_url: "http://127.0.0.1:3920"
# Legacy/Reference structure (Normally managed via code constants or environment)
bridge:

View File

@ -16,10 +16,10 @@ func TestResolveSingleAgentForwardEndpointFromExampleConfig(t *testing.T) {
}
expectedEndpoints := map[string]string{
"codex": "http://127.0.0.1:9001/acp/rpc",
"opencode": "http://127.0.0.1:38992/acp/rpc",
"gemini": "http://127.0.0.1:8791/acp/rpc",
"hermes": "http://127.0.0.1:3920/acp/rpc",
"codex": "ws://127.0.0.1:9001/acp",
"opencode": "http://127.0.0.1:38992",
"gemini": "http://127.0.0.1:8791",
"hermes": "http://127.0.0.1:3920",
}
for _, id := range order {

View File

@ -59,9 +59,6 @@ func resolveURL(yamlVal string, defaultVal string, envKeys ...string) string {
func bridgeUpstreamAuthorizationHeader() string {
token := strings.TrimSpace(shared.EnvOrDefault("BRIDGE_AUTH_TOKEN", ""))
if token == "" {
token = strings.TrimSpace(shared.EnvOrDefault("INTERNAL_SERVICE_TOKEN", ""))
}
if token != "" && !strings.HasPrefix(strings.ToLower(token), "bearer ") {
return "Bearer " + token
}
@ -91,21 +88,21 @@ func newProductionProviderCatalog() (map[string]syncedProvider, []string) {
label: "OpenCode",
yaml: config.Upstream.OpenCodeURL,
envKeys: []string{"OPENCODE_RPC_URL"},
defaultURL: "http://127.0.0.1:38992/acp/rpc",
defaultURL: "http://127.0.0.1:38992",
},
{
id: "gemini",
label: "Gemini",
yaml: config.Upstream.GeminiURL,
envKeys: []string{"GEMINI_RPC_URL"},
defaultURL: "http://127.0.0.1:8791/acp/rpc",
defaultURL: "http://127.0.0.1:8791",
},
{
id: "hermes",
label: "Hermes",
yaml: config.Upstream.HermesURL,
envKeys: []string{"HERMES_RPC_URL"},
defaultURL: "http://127.0.0.1:3920/acp/rpc",
defaultURL: "http://127.0.0.1:3920",
},
}

View File

@ -108,3 +108,18 @@ func TestProductionProviderCatalogPrefersDedicatedBridgeAuthToken(t *testing.T)
t.Fatalf("expected dedicated bearer header, got %q", got)
}
}
func TestProductionProviderCatalogIgnoresInternalServiceToken(t *testing.T) {
t.Setenv("BRIDGE_AUTH_TOKEN", "")
t.Setenv("INTERNAL_SERVICE_TOKEN", "legacy-token")
catalog, _ := newProductionProviderCatalog()
p, ok := catalog["codex"]
if !ok {
t.Fatal("missing codex")
}
if got := p.AuthorizationHeader; got != "" {
t.Fatalf("expected empty auth header when BRIDGE_AUTH_TOKEN is unset, got %q", got)
}
}

View File

@ -442,7 +442,7 @@ func TestHandleRPCCapabilitiesReturnsCanonicalProviderContract(t *testing.T) {
func TestHandleRPCSessionStartSucceedsWithExplicitProvider(t *testing.T) {
externalServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if got := r.Header.Get("Authorization"); got != "Bearer internal-test-token" {
if got := r.Header.Get("Authorization"); got != "Bearer bridge-test-token" {
t.Fatalf("unexpected auth header: %q", got)
}
_ = json.NewEncoder(w).Encode(map[string]any{
@ -457,8 +457,7 @@ func TestHandleRPCSessionStartSucceedsWithExplicitProvider(t *testing.T) {
}))
defer externalServer.Close()
t.Setenv("INTERNAL_SERVICE_TOKEN", "internal-test-token")
t.Setenv("BRIDGE_AUTH_TOKEN", "")
t.Setenv("BRIDGE_AUTH_TOKEN", "bridge-test-token")
t.Setenv("BRIDGE_CONFIG_PATH", "../../example/config.yaml")
server := NewServer()
@ -466,7 +465,7 @@ func TestHandleRPCSessionStartSucceedsWithExplicitProvider(t *testing.T) {
ProviderID: "opencode",
Label: "OpenCode",
Endpoint: externalServer.URL,
AuthorizationHeader: "Bearer internal-test-token",
AuthorizationHeader: "Bearer bridge-test-token",
Enabled: true,
})
@ -477,7 +476,7 @@ func TestHandleRPCSessionStartSucceedsWithExplicitProvider(t *testing.T) {
strings.NewReader(`{"jsonrpc":"2.0","id":"task-1","method":"session.start","params":{"sessionId":"s1","threadId":"t1","taskPrompt":"Reply with exactly pong","workingDirectory":"`+t.TempDir()+`","routing":{"routingMode":"explicit","explicitExecutionTarget":"singleAgent","explicitProviderId":"opencode"}}}`),
)
request.Header.Set("Content-Type", "application/json")
request.Header.Set("Authorization", "Bearer bridge-token")
request.Header.Set("Authorization", "Bearer bridge-test-token")
server.HandleRPC(recorder, request)

View File

@ -112,6 +112,12 @@ case "${scenario}" in
https://xworkmate-bridge.svc.plus/acp-server/*/acp/rpc)
printf '{"jsonrpc":"2.0","result":{"providers":["ok"]}}\n'
;;
https://xworkmate-bridge.svc.plus/acp-server/*)
printf '{"jsonrpc":"2.0","result":{"providers":["ok"]}}\n'
;;
https://xworkmate-bridge.svc.plus/gateway/openclaw)
printf '{"jsonrpc":"2.0","result":{"providers":["ok"]}}\n'
;;
https://xworkmate-bridge.svc.plus/acp/rpc)
printf 'curl: (28) Operation timed out after 20001 milliseconds with 0 bytes received\n' >&2
exit 1
@ -138,6 +144,12 @@ case "${scenario}" in
https://xworkmate-bridge.svc.plus/acp-server/*/acp/rpc)
printf '{"jsonrpc":"2.0","result":{"providers":["ok"]}}\n'
;;
https://xworkmate-bridge.svc.plus/acp-server/*)
printf '{"jsonrpc":"2.0","result":{"providers":["ok"]}}\n'
;;
https://xworkmate-bridge.svc.plus/gateway/openclaw)
printf '{"jsonrpc":"2.0","result":{"providers":["ok"]}}\n'
;;
https://xworkmate-bridge.svc.plus/acp/rpc)
if [[ "${data}" == *'"providerId":"codex"'* ]]; then
printf '{"jsonrpc":"2.0","result":{"success":true,"providerId":"codex","capabilities":{"providers":["codex"]}}}\n'
@ -194,12 +206,12 @@ run_validate_capture() {
FAKE_CURL_SCENARIO="${scenario}" \
FAKE_CURL_STATE_DIR="${RUN_STATE_DIR}" \
BRIDGE_SERVER_URL="https://xworkmate-bridge.svc.plus" \
OPENCLAW_URL="wss://openclaw.svc.plus" \
CODEX_RPC_URL="https://xworkmate-bridge.svc.plus/acp-server/codex/acp/rpc" \
OPENCODE_RPC_URL="https://xworkmate-bridge.svc.plus/acp-server/opencode/acp/rpc" \
GEMINI_RPC_URL="https://xworkmate-bridge.svc.plus/acp-server/gemini/acp/rpc" \
HERMES_RPC_URL="https://xworkmate-bridge.svc.plus/acp-server/hermes/acp/rpc" \
INTERNAL_SERVICE_TOKEN="test-token" \
OPENCLAW_URL="https://xworkmate-bridge.svc.plus/gateway/openclaw" \
CODEX_RPC_URL="https://xworkmate-bridge.svc.plus/acp-server/codex" \
OPENCODE_RPC_URL="https://xworkmate-bridge.svc.plus/acp-server/opencode" \
GEMINI_RPC_URL="https://xworkmate-bridge.svc.plus/acp-server/gemini" \
HERMES_RPC_URL="https://xworkmate-bridge.svc.plus/acp-server/hermes" \
BRIDGE_AUTH_TOKEN="test-token" \
bash "${SCRIPT_PATH}" "${IMAGE_REF}" 2>&1
)"
RUN_STATUS=$?

View File

@ -54,26 +54,22 @@ fi
BASE_URL="$(normalize_url "${BRIDGE_SERVER_URL:-${2:-https://xworkmate-bridge.svc.plus}}")"
OPENCLAW_BASE_URL="$(normalize_url "${OPENCLAW_URL:-${3:-${BASE_URL}/gateway/openclaw}}")"
CODEX_BASE_URL="$(normalize_url "${CODEX_RPC_URL:-${4:-${BASE_URL}/acp-server/codex/acp/rpc}}")"
OPENCODE_BASE_URL="$(normalize_url "${OPENCODE_RPC_URL:-${5:-${BASE_URL}/acp-server/opencode/acp/rpc}}")"
GEMINI_BASE_URL="$(normalize_url "${GEMINI_RPC_URL:-${6:-${BASE_URL}/acp-server/gemini/acp/rpc}}")"
HERMES_BASE_URL="$(normalize_url "${HERMES_RPC_URL:-${7:-${BASE_URL}/acp-server/hermes/acp/rpc}}")"
AUTH_TOKEN="${BRIDGE_AUTH_TOKEN:-${INTERNAL_SERVICE_TOKEN:-${7:-}}}"
CODEX_BASE_URL="$(normalize_url "${CODEX_RPC_URL:-${4:-${BASE_URL}/acp-server/codex}}")"
OPENCODE_BASE_URL="$(normalize_url "${OPENCODE_RPC_URL:-${5:-${BASE_URL}/acp-server/opencode}}")"
GEMINI_BASE_URL="$(normalize_url "${GEMINI_RPC_URL:-${6:-${BASE_URL}/acp-server/gemini}}")"
HERMES_BASE_URL="$(normalize_url "${HERMES_RPC_URL:-${7:-${BASE_URL}/acp-server/hermes}}")"
AUTH_TOKEN="${BRIDGE_AUTH_TOKEN:?BRIDGE_AUTH_TOKEN is required}"
ensure_rpc_path() {
append_rpc_path() {
local url="$1"
if [[ "${url}" == */acp/rpc ]]; then
printf '%s\n' "${url}"
else
printf '%s/acp/rpc\n' "${url%/}"
fi
printf '%s/acp/rpc\n' "${url%/}"
}
OPENCLAW_HTTP_PROBE_URL="$(websocket_probe_url "${OPENCLAW_BASE_URL}")"
CODEX_RPC_ENDPOINT="$(ensure_rpc_path "${CODEX_BASE_URL}")"
OPENCODE_RPC_ENDPOINT="$(ensure_rpc_path "${OPENCODE_BASE_URL}")"
GEMINI_RPC_ENDPOINT="$(ensure_rpc_path "${GEMINI_BASE_URL}")"
HERMES_RPC_ENDPOINT="$(ensure_rpc_path "${HERMES_BASE_URL}")"
CODEX_RPC_ENDPOINT="$(append_rpc_path "${CODEX_BASE_URL}")"
OPENCODE_RPC_ENDPOINT="$(append_rpc_path "${OPENCODE_BASE_URL}")"
GEMINI_RPC_ENDPOINT="$(append_rpc_path "${GEMINI_BASE_URL}")"
HERMES_RPC_ENDPOINT="$(append_rpc_path "${HERMES_BASE_URL}")"
fast_http_curl_common=(
--silent