//! Candidate planning and provider request shaping for Codex Live. //! //! Live has its own endpoint and permission surface. Candidate selection, //! model aliases and transport policy are shared with the ordinary scheduler, //! but Responses body normalization and its WebSocket state machine never see //! a Live protocol frame. use std::net::SocketAddr; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; use std::time::Duration; use axum::http::header::{AUTHORIZATION, CONNECTION, CONTENT_TYPE, COOKIE, UPGRADE}; use axum::http::{HeaderMap, HeaderName, HeaderValue, Method}; use serde_json::json; use sha2::{Digest, Sha256}; use url::{form_urlencoded, Url}; use crate::ai_serving::{ build_standard_stream_plan_from_decision, maybe_build_pinned_stream_local_same_format_provider_decision_payload, AiExecutionDecision, AiStreamAttempt, ResponsesWebSocketPinnedCandidate, }; use crate::control::GatewayControlDecision; use crate::headers::request_origin_from_headers_and_remote_addr; use crate::privacy::RedactionSessionSlot; use crate::{AppState, GatewayError}; use super::protocol::{validate_model, LiveProtocolError}; pub(super) const LIVE_ALPHA_HEADER_VALUE: &str = "quicksilver=v2"; const CHATGPT_ACCOUNT_ID_HEADER: &str = "chatgpt-account-id"; const CHATGPT_FEDRAMP_HEADER: &str = "x-openai-fedramp"; const CHATGPT_SESSION_ID_HEADER: &str = "x-session-id"; const OFFICIAL_CHATGPT_HOST: &str = "chatgpt.com"; const LIVE_ROUTING_FINGERPRINT_DOMAIN: &[u8] = b"aether-codex-live-routing-v1"; const MAX_LIVE_MODEL_BYTES: usize = 256; #[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)] #[serde(rename_all = "snake_case")] pub(super) enum LiveAuthMode { ApiKey, ChatGptOauth, } #[derive(Debug)] pub(super) struct PlannedLiveCandidate { pub(super) execution: AiExecutionDecision, pub(super) pinned_candidate: ResponsesWebSocketPinnedCandidate, pub(super) client_model: String, pub(super) provider_model: String, pub(super) auth_mode: LiveAuthMode, /// Domain-separated digest of the stable upstream auth/account/origin /// identity. It deliberately excludes bearer tokens so an OAuth refresh /// does not invalidate an in-flight WebRTC call. pub(super) routing_fingerprint: String, } /// Cancellation-safe owner for the scheduler's distributed pool-key lease. /// Live does not enter the ordinary HTTP/Responses attempt lifecycle, so it /// must hold and release the lease explicitly for the call or socket lifetime. pub(super) struct LivePoolLeaseGuard { state: AppState, report_context: Option, renewal_task: Option>, healthy: Arc, armed: bool, } impl LivePoolLeaseGuard { pub(super) fn new(state: &AppState, candidate: &PlannedLiveCandidate) -> Self { let report_context = candidate.execution.report_context.clone(); let lease = crate::orchestration::local_execution_candidate_metadata_from_report_context( report_context.as_ref(), ) .pool_key_lease; let healthy = Arc::new(AtomicBool::new(true)); let renewal_task = lease.map(|lease| { let runtime_state = Arc::clone(&state.runtime_state); let healthy = Arc::clone(&healthy); tokio::spawn(async move { let ttl = Duration::from_millis(lease.ttl_ms); let interval = Duration::from_millis((lease.ttl_ms / 3).max(1)); loop { tokio::time::sleep(interval).await; match runtime_state.lock_renew(&lease, ttl).await { Ok(true) => {} Ok(false) | Err(_) => { healthy.store(false, Ordering::Release); return; } } } }) }); Self { state: state.clone(), report_context, renewal_task, healthy, armed: true, } } pub(super) fn is_healthy(&self) -> bool { self.healthy.load(Ordering::Acquire) } pub(super) async fn release(mut self) { if let Some(task) = self.renewal_task.take() { task.abort(); } crate::orchestration::release_pool_key_lease_from_report_context( &self.state, self.report_context.as_ref(), ) .await; self.armed = false; } } impl Drop for LivePoolLeaseGuard { fn drop(&mut self) { if let Some(task) = self.renewal_task.take() { task.abort(); } if !self.armed { return; } let state = self.state.clone(); let report_context = self.report_context.take(); if let Ok(runtime) = tokio::runtime::Handle::try_current() { runtime.spawn(async move { crate::orchestration::release_pool_key_lease_from_report_context( &state, report_context.as_ref(), ) .await; }); } } } pub(super) async fn plan_live_candidate( state: &AppState, trace_id: &str, decision: &GatewayControlDecision, headers: &HeaderMap, remote_addr: &SocketAddr, client_model: &str, pinned_candidate: Option<&ResponsesWebSocketPinnedCandidate>, ) -> Result, GatewayError> { if validate_model(client_model).is_err() || client_model.len() > MAX_LIVE_MODEL_BYTES { return Ok(None); } let parts = build_live_planning_parts(headers, remote_addr); let body = json!({"model": client_model, "input": []}); let execution = maybe_build_pinned_stream_local_same_format_provider_decision_payload( state, &parts, trace_id, decision, &body, crate::ai_serving::CODEX_LIVE_STREAM_PLAN_KIND, pinned_candidate .map(|pinned| (pinned.provider_id(), pinned.endpoint_id(), pinned.key_id())), ) .await?; let Some(mut execution) = execution else { return Ok(None); }; if execution .provider_api_format .as_deref() .map(crate::ai_serving::normalize_api_format_alias) .as_deref() != Some("codex:live") { crate::orchestration::release_pool_key_lease_from_report_context( state, execution.report_context.as_ref(), ) .await; return Ok(None); } let Some(effective_auth_type) = execution .report_context .as_ref() .and_then(|context| context.get("upstream_credential_mode")) .and_then(serde_json::Value::as_str) .map(str::trim) .filter(|value| !value.is_empty()) .map(str::to_string) else { crate::orchestration::release_pool_key_lease_from_report_context( state, execution.report_context.as_ref(), ) .await; return Ok(None); }; let provider_type = execution .provider_type .as_deref() .map(str::trim) .unwrap_or_default(); if !matches!( provider_type.to_ascii_lowercase().as_str(), "codex" | "openai" | "custom" ) { crate::orchestration::release_pool_key_lease_from_report_context( state, execution.report_context.as_ref(), ) .await; return Ok(None); } let Some(pinned_candidate) = ResponsesWebSocketPinnedCandidate::from_decision(&execution) else { crate::orchestration::release_pool_key_lease_from_report_context( state, execution.report_context.as_ref(), ) .await; return Ok(None); }; let provider_model = execution .mapped_model .as_deref() .or(execution.model_name.as_deref()) .map(str::trim) .filter(|model| validate_model(model).is_ok()) .map(str::to_string); let Some(provider_model) = provider_model else { crate::orchestration::release_pool_key_lease_from_report_context( state, execution.report_context.as_ref(), ) .await; return Ok(None); }; let Some(auth_mode) = live_auth_mode(provider_type, effective_auth_type.as_str()) else { crate::orchestration::release_pool_key_lease_from_report_context( state, execution.report_context.as_ref(), ) .await; return Ok(None); }; apply_live_headers(&mut execution.provider_request_headers, trace_id); let routing_fingerprint = match live_routing_fingerprint(&execution, effective_auth_type.as_str(), auth_mode) { Ok(fingerprint) => fingerprint, Err(_) => { crate::orchestration::release_pool_key_lease_from_report_context( state, execution.report_context.as_ref(), ) .await; return Ok(None); } }; Ok(Some(PlannedLiveCandidate { execution, pinned_candidate, client_model: client_model.to_string(), provider_model, auth_mode, routing_fingerprint, })) } pub(super) fn direct_live_websocket_url( candidate: &PlannedLiveCandidate, ) -> Result { if candidate.auth_mode == LiveAuthMode::ChatGptOauth { return Err(LiveProtocolError::OauthDirectWebSocketUnsupported); } replace_live_suffix( candidate.execution.upstream_url.as_deref(), &["live"], Some(("model", candidate.provider_model.as_str())), ) } pub(super) fn live_call_url(candidate: &PlannedLiveCandidate) -> Result { match candidate.auth_mode { LiveAuthMode::ApiKey => { replace_live_suffix(candidate.execution.upstream_url.as_deref(), &["live"], None) } LiveAuthMode::ChatGptOauth => { let source = validated_official_chatgpt_url(candidate.execution.upstream_url.as_deref())?; replace_live_suffix( Some(source.as_str()), &["realtime", "calls"], Some(("intent", "quicksilver")), ) } .and_then(|raw| { let mut url = Url::parse(raw.as_str()).map_err(|_| LiveProtocolError::InvalidUpstreamUrl)?; replace_url_query_pair(&mut url, "architecture", "avas"); Ok(url.to_string()) }), } } pub(super) fn live_sideband_url( candidate: &PlannedLiveCandidate, call_id: &str, ) -> Result { super::protocol::validate_call_id(call_id)?; match candidate.auth_mode { LiveAuthMode::ApiKey => replace_live_suffix( candidate.execution.upstream_url.as_deref(), &["live", call_id], None, ), // The Codex ChatGPT call creation endpoint returns an OpenAI Realtime // call ID. Current Codex connects its sideband to this API origin even // when call creation used the ChatGPT OAuth backend. LiveAuthMode::ChatGptOauth => { validated_official_chatgpt_url(candidate.execution.upstream_url.as_deref())?; Ok(format!("https://api.openai.com/v1/live/{call_id}")) } } } pub(super) fn apply_live_headers( headers: &mut std::collections::BTreeMap, seed: &str, ) { replace_header(headers, "openai-alpha", LIVE_ALPHA_HEADER_VALUE); let session_id = find_header(headers, "x-session-id") .or_else(|| find_header(headers, "thread-id")) .or_else(|| find_header(headers, "session-id")) .map(str::to_string) .filter(|value| !value.trim().is_empty()) .unwrap_or_else(|| seed.to_string()); replace_header(headers, "x-session-id", session_id.as_str()); } pub(super) fn build_live_stream_admission_attempt( candidate: &PlannedLiveCandidate, headers: &HeaderMap, remote_addr: &SocketAddr, upstream_url: String, ) -> Result, GatewayError> { let parts = build_live_planning_parts(headers, remote_addr); let body = json!({"model": candidate.client_model.as_str(), "input": []}); let mut execution = candidate.execution.clone(); execution.upstream_url = Some(upstream_url); execution.upstream_is_stream = true; build_standard_stream_plan_from_decision(&parts, &body, execution, false) } fn live_auth_mode(provider_type: &str, effective_auth_type: &str) -> Option { match effective_auth_type.trim().to_ascii_lowercase().as_str() { "api_key" | "bearer" => Some(LiveAuthMode::ApiKey), "oauth" if provider_type.eq_ignore_ascii_case("codex") => Some(LiveAuthMode::ChatGptOauth), _ => None, } } fn live_routing_fingerprint( decision: &AiExecutionDecision, effective_auth_type: &str, auth_mode: LiveAuthMode, ) -> Result { let raw_url = decision .upstream_url .as_deref() .ok_or(LiveProtocolError::MissingUpstreamUrl)?; let url = Url::parse(raw_url).map_err(|_| LiveProtocolError::InvalidUpstreamUrl)?; if url.host_str().is_none() || !url.username().is_empty() || url.password().is_some() || url.fragment().is_some() { return Err(LiveProtocolError::InvalidUpstreamUrl); } let path = url.path().trim_end_matches('/'); let path_family = path .strip_suffix("/live") .ok_or(LiveProtocolError::InvalidUpstreamUrl)?; if auth_mode == LiveAuthMode::ChatGptOauth { validated_official_chatgpt_url(Some(raw_url))?; } let mut hasher = Sha256::new(); hasher.update(LIVE_ROUTING_FINGERPRINT_DOMAIN); hasher.update([0]); for value in [ effective_auth_type.trim(), url.scheme(), url.host_str().unwrap_or_default(), path_family, ] { hasher.update(value.as_bytes()); hasher.update([0]); } hasher.update( url.port_or_known_default() .unwrap_or_default() .to_be_bytes(), ); hasher.update([0]); hasher.update(canonical_live_route_query(&url).as_bytes()); hasher.update([0]); let session_id = find_header( &decision.provider_request_headers, CHATGPT_SESSION_ID_HEADER, ) .map(str::trim) .filter(|value| !value.is_empty()) .ok_or(LiveProtocolError::InvalidUpstreamUrl)?; hasher.update(session_id.as_bytes()); hasher.update([0]); if auth_mode == LiveAuthMode::ChatGptOauth { for (name, required) in [ (CHATGPT_ACCOUNT_ID_HEADER, true), (CHATGPT_FEDRAMP_HEADER, false), ] { let value = find_header(&decision.provider_request_headers, name) .map(str::trim) .filter(|value| !value.is_empty()); if required && value.is_none() { return Err(LiveProtocolError::InvalidUpstreamUrl); } hasher.update(value.unwrap_or_default().as_bytes()); hasher.update([0]); } } Ok(format!("{:x}", hasher.finalize())) } fn validated_official_chatgpt_url(raw: Option<&str>) -> Result { let url = Url::parse(raw.ok_or(LiveProtocolError::MissingUpstreamUrl)?) .map_err(|_| LiveProtocolError::InvalidUpstreamUrl)?; let official = url.scheme() == "https" && url .host_str() .is_some_and(|host| host.eq_ignore_ascii_case(OFFICIAL_CHATGPT_HOST)) && url.port_or_known_default() == Some(443) && url.username().is_empty() && url.password().is_none() && url.fragment().is_none() && url.path().trim_end_matches('/').strip_suffix("/live") == Some("/backend-api/codex"); if !official { return Err(LiveProtocolError::OauthUpstreamUnsupported); } Ok(url) } fn replace_live_suffix( raw: Option<&str>, suffix: &[&str], query: Option<(&str, &str)>, ) -> Result { let mut url = Url::parse(raw.ok_or(LiveProtocolError::MissingUpstreamUrl)?) .map_err(|_| LiveProtocolError::InvalidUpstreamUrl)?; if url.host_str().is_none() || !url.username().is_empty() || url.password().is_some() || url.fragment().is_some() { return Err(LiveProtocolError::InvalidUpstreamUrl); } if url.path_segments().and_then(Iterator::last) != Some("live") { return Err(LiveProtocolError::InvalidUpstreamUrl); } { let mut path = url .path_segments_mut() .map_err(|_| LiveProtocolError::InvalidUpstreamUrl)?; path.pop_if_empty(); path.pop(); for segment in suffix { path.push(segment); } } if let Some((name, value)) = query { replace_url_query_pair(&mut url, name, value); } Ok(url.to_string()) } fn replace_url_query_pair(url: &mut Url, name: &str, value: &str) { let retained = url .query_pairs() .filter(|(candidate, _)| !candidate.eq_ignore_ascii_case(name)) .map(|(key, value)| (key.into_owned(), value.into_owned())) .collect::>(); url.set_query(None); let mut query = url.query_pairs_mut(); for (key, value) in retained { query.append_pair(key.as_str(), value.as_str()); } query.append_pair(name, value); } fn canonical_live_route_query(url: &Url) -> String { if url.query().is_none() { return String::new(); } let mut pairs = url .query_pairs() .map(|(name, value)| { let value = if live_route_query_key_is_credential(name.as_ref()) { // Bind the authentication mechanism without pinning a rotating // credential value into the WebRTC call identity. String::new() } else { value.into_owned() }; (name.into_owned(), value) }) .collect::>(); pairs.sort_unstable(); let mut serializer = form_urlencoded::Serializer::new(String::new()); for (name, value) in pairs { serializer.append_pair(name.as_str(), value.as_str()); } serializer.finish() } fn live_route_query_key_is_credential(name: &str) -> bool { matches!( name.to_ascii_lowercase().as_str(), "key" | "api_key" | "api-key" | "x-api-key" | "x-goog-api-key" | "access_token" | "authorization" | "token" | "oauth_token" | "client_secret" | "secret_key" | "signature" | "sig" ) } fn build_live_planning_parts( headers: &HeaderMap, remote_addr: &SocketAddr, ) -> http::request::Parts { let mut request = http::Request::builder() .method(Method::GET) .uri("/v1/live") .body(()) .expect("the fixed Live planning request must be valid"); *request.headers_mut() = sanitize_live_planning_headers(headers.clone()); request .headers_mut() .insert(CONTENT_TYPE, HeaderValue::from_static("application/json")); request .extensions_mut() .insert(request_origin_from_headers_and_remote_addr( headers, remote_addr, )); request .extensions_mut() .insert(RedactionSessionSlot::default()); request.into_parts().0 } fn sanitize_live_planning_headers(mut headers: HeaderMap) -> HeaderMap { let connection_names = headers .get_all(CONNECTION) .iter() .filter_map(|value| value.to_str().ok()) .flat_map(|value| value.split(',')) .filter_map(|name| HeaderName::from_bytes(name.trim().as_bytes()).ok()) .collect::>(); for name in connection_names { headers.remove(name); } for name in [AUTHORIZATION, CONNECTION, COOKIE, UPGRADE] { headers.remove(name); } for name in [ "api-key", "x-api-key", "x-goog-api-key", "proxy-authorization", "proxy-connection", "keep-alive", ] { headers.remove(name); } let websocket_headers = headers .keys() .filter(|name| name.as_str().starts_with("sec-websocket-")) .cloned() .collect::>(); for name in websocket_headers { headers.remove(name); } headers } fn find_header<'a>( headers: &'a std::collections::BTreeMap, name: &str, ) -> Option<&'a str> { headers .iter() .find(|(candidate, _)| candidate.eq_ignore_ascii_case(name)) .map(|(_, value)| value.as_str()) } fn replace_header( headers: &mut std::collections::BTreeMap, name: &str, value: &str, ) { headers.retain(|candidate, _| !candidate.eq_ignore_ascii_case(name)); headers.insert(name.to_string(), value.to_string()); } #[cfg(test)] mod tests { use std::collections::BTreeMap; use aether_provider_transport::snapshot::{ GatewayProviderTransportEndpoint, GatewayProviderTransportKey, GatewayProviderTransportProvider, GatewayProviderTransportSnapshot, }; use super::*; fn candidate(url: &str, auth_mode: LiveAuthMode) -> PlannedLiveCandidate { let execution: AiExecutionDecision = serde_json::from_value(json!({ "action": "stream", "provider_id": "provider-1", "endpoint_id": "endpoint-1", "key_id": "key-1", "upstream_url": url, "provider_type": "codex", "provider_request_headers": {"authorization": "Bearer secret"} })) .expect("decision should deserialize"); PlannedLiveCandidate { execution, pinned_candidate: ResponsesWebSocketPinnedCandidate::new( "provider-1", "endpoint-1", "key-1", ) .unwrap(), client_model: "global-model".to_string(), provider_model: "provider-model".to_string(), auth_mode, routing_fingerprint: "0".repeat(64), } } fn transport_with_auth_override( default_auth_type: &str, auth_type_by_format: Option, ) -> GatewayProviderTransportSnapshot { GatewayProviderTransportSnapshot { provider: GatewayProviderTransportProvider { id: "provider-1".to_string(), name: "codex".to_string(), provider_type: "codex".to_string(), website: None, is_active: true, keep_priority_on_conversion: false, enable_format_conversion: false, concurrent_limit: None, max_retries: None, proxy: None, request_timeout_secs: None, stream_first_byte_timeout_secs: None, config: None, }, endpoint: GatewayProviderTransportEndpoint { id: "endpoint-1".to_string(), provider_id: "provider-1".to_string(), api_format: "codex:live".to_string(), api_family: Some("codex".to_string()), endpoint_kind: Some("live".to_string()), is_active: true, base_url: "https://chatgpt.com/backend-api/codex".to_string(), header_rules: None, body_rules: None, max_retries: None, custom_path: None, config: None, format_acceptance_config: None, proxy: None, }, key: GatewayProviderTransportKey { id: "key-1".to_string(), provider_id: "provider-1".to_string(), name: "key".to_string(), auth_type: default_auth_type.to_string(), is_active: true, api_formats: Some(vec!["codex:live".to_string()]), auth_type_by_format, allow_auth_channel_mismatch_formats: None, allowed_models: None, capabilities: None, rate_multipliers: None, global_priority_by_format: None, expires_at_unix_secs: None, proxy: None, fingerprint: None, upstream_metadata: None, decrypted_api_key: "rotating-secret".to_string(), decrypted_auth_config: None, }, } } fn oauth_fingerprint_decision( token: &str, account_id: &str, fedramp: &str, session_id: &str, ) -> AiExecutionDecision { let mut decision = candidate( "https://chatgpt.com/backend-api/codex/live", LiveAuthMode::ChatGptOauth, ) .execution; decision.provider_request_headers = BTreeMap::from([ ("authorization".to_string(), format!("Bearer {token}")), ("chatgpt-account-id".to_string(), account_id.to_string()), ("x-openai-fedramp".to_string(), fedramp.to_string()), ("x-session-id".to_string(), session_id.to_string()), ]); decision } #[test] fn format_auth_override_selects_the_effective_live_auth_mode() { let overridden = transport_with_auth_override("oauth", Some(json!({"codex:live": "bearer"}))); let effective = aether_provider_transport::auth::resolve_local_auth_type_for_transport_format( &overridden, ); assert_eq!(effective, "bearer"); assert_eq!( live_auth_mode( overridden.provider.provider_type.as_str(), effective.as_str() ), Some(LiveAuthMode::ApiKey) ); let oauth = transport_with_auth_override("oauth", None); let effective = aether_provider_transport::auth::resolve_local_auth_type_for_transport_format(&oauth); assert_eq!(effective, "oauth"); assert_eq!( live_auth_mode(oauth.provider.provider_type.as_str(), effective.as_str()), Some(LiveAuthMode::ChatGptOauth) ); } #[test] fn derives_api_key_live_urls_preserves_query_and_replaces_the_mapped_model() { let mut candidate = candidate( "https://api.example.test/v1/live?api-version=2026-08-01&model=stale&MODEL=duplicate", LiveAuthMode::ApiKey, ); candidate.provider_model = "upstream/model + future".to_string(); let direct = Url::parse(direct_live_websocket_url(&candidate).unwrap().as_str()).unwrap(); assert_eq!(direct.path(), "/v1/live"); assert_eq!( direct.query_pairs().collect::>(), vec![ ("api-version".into(), "2026-08-01".into()), ("model".into(), "upstream/model + future".into()), ] ); assert_eq!( direct .query_pairs() .filter(|(name, _)| name.eq_ignore_ascii_case("model")) .count(), 1 ); assert!(!direct.as_str().contains("global-model")); assert_eq!( live_call_url(&candidate).unwrap(), "https://api.example.test/v1/live?api-version=2026-08-01&model=stale&MODEL=duplicate" ); assert_eq!( live_sideband_url(&candidate, "rtc_abc-123").unwrap(), "https://api.example.test/v1/live/rtc_abc-123?api-version=2026-08-01&model=stale&MODEL=duplicate" ); } #[test] fn derives_chatgpt_call_and_official_sideband_urls() { let candidate = candidate( "https://chatgpt.com/backend-api/codex/live?api-version=2026-08-01&intent=stale&INTENT=duplicate&architecture=stale&ARCHITECTURE=duplicate", LiveAuthMode::ChatGptOauth, ); let call = Url::parse(live_call_url(&candidate).unwrap().as_str()).unwrap(); assert_eq!(call.path(), "/backend-api/codex/realtime/calls"); assert_eq!( call.query_pairs().collect::>(), vec![ ("api-version".into(), "2026-08-01".into()), ("intent".into(), "quicksilver".into()), ("architecture".into(), "avas".into()), ] ); assert_eq!( call.query_pairs() .filter(|(name, _)| name.eq_ignore_ascii_case("intent")) .count(), 1 ); assert_eq!( call.query_pairs() .filter(|(name, _)| name.eq_ignore_ascii_case("architecture")) .count(), 1 ); assert_eq!( live_sideband_url(&candidate, "rtc_call_1").unwrap(), "https://api.openai.com/v1/live/rtc_call_1" ); assert_eq!( direct_live_websocket_url(&candidate), Err(LiveProtocolError::OauthDirectWebSocketUnsupported) ); } #[test] fn chatgpt_oauth_live_fails_closed_for_custom_backend_origins() { let candidate = candidate( "https://relay.example/backend-api/codex/live", LiveAuthMode::ChatGptOauth, ); assert_eq!( live_call_url(&candidate), Err(LiveProtocolError::OauthUpstreamUnsupported) ); assert_eq!( live_sideband_url(&candidate, "rtc_custom_backend"), Err(LiveProtocolError::OauthUpstreamUnsupported) ); assert_eq!( live_routing_fingerprint(&candidate.execution, "oauth", LiveAuthMode::ChatGptOauth), Err(LiveProtocolError::OauthUpstreamUnsupported) ); } #[test] fn routing_fingerprint_ignores_token_rotation_but_binds_oauth_identity() { let baseline = oauth_fingerprint_decision("access-token-1", "account-1", "true", "session-1"); let refreshed = oauth_fingerprint_decision("access-token-2", "account-1", "true", "session-1"); let fingerprint = live_routing_fingerprint(&baseline, "oauth", LiveAuthMode::ChatGptOauth).unwrap(); assert_eq!( live_routing_fingerprint(&refreshed, "oauth", LiveAuthMode::ChatGptOauth).unwrap(), fingerprint, "access-token refresh must not invalidate an established sideband binding" ); for changed in [ oauth_fingerprint_decision("access-token-2", "account-2", "true", "session-1"), oauth_fingerprint_decision("access-token-2", "account-1", "false", "session-1"), oauth_fingerprint_decision("access-token-2", "account-1", "true", "session-2"), ] { assert_ne!( live_routing_fingerprint(&changed, "oauth", LiveAuthMode::ChatGptOauth).unwrap(), fingerprint ); } } #[test] fn routing_fingerprint_binds_api_key_origin_without_hashing_the_token() { let mut first = candidate("https://api-a.example/v1/live", LiveAuthMode::ApiKey).execution; first.provider_request_headers.extend([ ("authorization".to_string(), "Bearer token-1".to_string()), ("x-session-id".to_string(), "session-1".to_string()), ]); let mut refreshed = first.clone(); refreshed .provider_request_headers .insert("authorization".to_string(), "Bearer token-2".to_string()); assert_eq!( live_routing_fingerprint(&first, "bearer", LiveAuthMode::ApiKey).unwrap(), live_routing_fingerprint(&refreshed, "bearer", LiveAuthMode::ApiKey).unwrap() ); let mut changed_origin = candidate("https://api-b.example/v1/live", LiveAuthMode::ApiKey).execution; changed_origin .provider_request_headers .insert("x-session-id".to_string(), "session-1".to_string()); assert_ne!( live_routing_fingerprint(&first, "bearer", LiveAuthMode::ApiKey).unwrap(), live_routing_fingerprint(&changed_origin, "bearer", LiveAuthMode::ApiKey).unwrap() ); let mut changed_session = refreshed; changed_session .provider_request_headers .insert("x-session-id".to_string(), "session-2".to_string()); assert_ne!( live_routing_fingerprint(&first, "bearer", LiveAuthMode::ApiKey).unwrap(), live_routing_fingerprint(&changed_session, "bearer", LiveAuthMode::ApiKey).unwrap() ); let missing_session = candidate("https://api-a.example/v1/live", LiveAuthMode::ApiKey).execution; assert_eq!( live_routing_fingerprint(&missing_session, "bearer", LiveAuthMode::ApiKey), Err(LiveProtocolError::InvalidUpstreamUrl) ); } #[test] fn routing_fingerprint_canonicalizes_safe_query_and_ignores_query_credentials() { let mut baseline = candidate( "https://api-a.example/v1/live?api-version=2026-08-01&deployment=primary&alt=sse&token=secret-1&key=secret-1", LiveAuthMode::ApiKey, ) .execution; baseline .provider_request_headers .insert("x-session-id".to_string(), "session-1".to_string()); let fingerprint = live_routing_fingerprint(&baseline, "bearer", LiveAuthMode::ApiKey).unwrap(); let mut reordered = candidate( "https://api-a.example/v1/live?key=secret-2&alt=sse&token=secret-2&deployment=primary&api-version=2026-08-01", LiveAuthMode::ApiKey, ) .execution; reordered .provider_request_headers .insert("x-session-id".to_string(), "session-1".to_string()); assert_eq!( live_routing_fingerprint(&reordered, "bearer", LiveAuthMode::ApiKey).unwrap(), fingerprint, "query order and query credentials must not change the stable route identity" ); let mut changed_route = candidate( "https://api-a.example/v1/live?api-version=2026-08-01&deployment=secondary&alt=sse&token=secret-2&key=secret-2", LiveAuthMode::ApiKey, ) .execution; changed_route .provider_request_headers .insert("x-session-id".to_string(), "session-1".to_string()); assert_ne!( live_routing_fingerprint(&changed_route, "bearer", LiveAuthMode::ApiKey).unwrap(), fingerprint, "a non-sensitive endpoint route query change must invalidate the binding" ); } #[test] fn live_headers_force_quicksilver_and_preserve_a_stable_session_identity() { let mut headers = BTreeMap::from([ ("OpenAI-Alpha".to_string(), "wrong".to_string()), ("thread-id".to_string(), "thread-stable".to_string()), ]); apply_live_headers(&mut headers, "trace-fallback"); assert_eq!( headers.get("openai-alpha").map(String::as_str), Some(LIVE_ALPHA_HEADER_VALUE) ); assert_eq!( headers.get("x-session-id").map(String::as_str), Some("thread-stable") ); assert_eq!( headers .keys() .filter(|name| name.eq_ignore_ascii_case("openai-alpha")) .count(), 1 ); let mut legacy_session_header = BTreeMap::from([ ("Session-Id".to_string(), "legacy-stable".to_string()), ("chatgpt-account-id".to_string(), "account-1".to_string()), ]); apply_live_headers(&mut legacy_session_header, "trace-fallback"); assert_eq!( legacy_session_header .get("x-session-id") .map(String::as_str), Some("legacy-stable") ); assert_eq!( legacy_session_header .get("chatgpt-account-id") .map(String::as_str), Some("account-1") ); } #[test] fn live_urls_reject_credentials_invalid_suffixes_and_call_ids() { let credentials = candidate("https://token@example.test/v1/live", LiveAuthMode::ApiKey); assert_eq!( direct_live_websocket_url(&credentials), Err(LiveProtocolError::InvalidUpstreamUrl) ); let wrong_suffix = candidate( "https://api.example.test/v1/chat/completions", LiveAuthMode::ApiKey, ); assert_eq!( live_call_url(&wrong_suffix), Err(LiveProtocolError::InvalidUpstreamUrl) ); assert_eq!( live_sideband_url(&wrong_suffix, "rtc/escape"), Err(LiveProtocolError::InvalidCallId) ); let fragment = candidate( "https://api.example.test/v1/live#not-sent-upstream", LiveAuthMode::ApiKey, ); assert_eq!( direct_live_websocket_url(&fragment), Err(LiveProtocolError::InvalidUpstreamUrl) ); assert_eq!( live_call_url(&fragment), Err(LiveProtocolError::InvalidUpstreamUrl) ); let mut fragment_fingerprint = fragment.execution; fragment_fingerprint .provider_request_headers .insert("x-session-id".to_string(), "session-1".to_string()); assert_eq!( live_routing_fingerprint(&fragment_fingerprint, "bearer", LiveAuthMode::ApiKey), Err(LiveProtocolError::InvalidUpstreamUrl) ); } }