feat(core): send attestation for ChatGPT compaction and realtime setup

This commit is contained in:
Jiaming Zhang
2026-04-30 07:10:52 -07:00
parent 2fa3e50ef8
commit 273e5d754e
5 changed files with 142 additions and 5 deletions
+32 -4
View File
@@ -118,6 +118,7 @@ use codex_login::auth_env_telemetry::AuthEnvTelemetry;
use codex_login::auth_env_telemetry::collect_auth_env_telemetry;
use codex_model_provider::SharedModelProvider;
use codex_model_provider::create_model_provider;
use codex_model_provider_info::CHATGPT_CODEX_BASE_URL;
#[cfg(test)]
use codex_model_provider_info::DEFAULT_WEBSOCKET_CONNECT_TIMEOUT_MS;
use codex_model_provider_info::ModelProviderInfo;
@@ -424,6 +425,8 @@ impl ModelClient {
return Ok(Vec::new());
}
let client_setup = self.current_client_setup().await?;
let should_send_attestation =
should_send_attestation(&client_setup.api_provider, AttestationPurpose::Compaction);
let transport = ReqwestTransport::new(build_reqwest_client());
let request_telemetry = Self::build_request_telemetry(
session_telemetry,
@@ -477,6 +480,9 @@ impl ModelClient {
extra_headers.extend(build_conversation_headers(Some(
self.state.conversation_id.to_string(),
)));
if should_send_attestation && let Some(header_value) = macos_devicecheck_header() {
extra_headers.insert(X_OAI_ATTESTATION_HEADER, header_value);
}
let trace_attempt = compaction_trace.start_attempt(&payload);
let result = client
.compact_input(&payload, extra_headers)
@@ -495,10 +501,18 @@ impl ModelClient {
// Create the media call over HTTP first, then retain matching auth so realtime can attach
// the server-side control WebSocket to the call id from that HTTP response.
let client_setup = self.current_client_setup().await?;
let should_send_attestation = should_send_attestation(
&client_setup.api_provider,
AttestationPurpose::RealtimeWebrtcCallSetup,
);
let mut sideband_headers = extra_headers.clone();
sideband_headers.extend(sideband_websocket_auth_headers(
client_setup.api_auth.as_ref(),
));
let mut extra_headers = extra_headers;
if should_send_attestation && let Some(header_value) = macos_devicecheck_header() {
extra_headers.insert(X_OAI_ATTESTATION_HEADER, header_value);
}
let transport = ReqwestTransport::new(build_reqwest_client());
let response =
ApiRealtimeCallClient::new(transport, client_setup.api_provider, client_setup.api_auth)
@@ -1627,18 +1641,32 @@ fn build_responses_headers(
headers
}
fn provider_base_url_is_chatgpt_codex(provider: &codex_api::Provider) -> bool {
provider
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum AttestationPurpose {
Response,
Compaction,
RealtimeWebrtcCallSetup,
}
fn should_send_attestation(provider: &codex_api::Provider, purpose: AttestationPurpose) -> bool {
let provider_is_chatgpt_codex = provider
.base_url
.trim_end_matches('/')
.eq_ignore_ascii_case("https://chatgpt.com/backend-api/codex")
.eq_ignore_ascii_case(CHATGPT_CODEX_BASE_URL);
provider_is_chatgpt_codex
&& matches!(
purpose,
AttestationPurpose::Response
| AttestationPurpose::Compaction
| AttestationPurpose::RealtimeWebrtcCallSetup
)
}
fn extend_devicecheck_header_for_responses_endpoint(
headers: &mut ApiHeaderMap,
provider: &codex_api::Provider,
) {
if provider_base_url_is_chatgpt_codex(provider)
if should_send_attestation(provider, AttestationPurpose::Response)
&& let Some(header_value) = macos_devicecheck_header()
{
headers.insert(X_OAI_ATTESTATION_HEADER, header_value);
+44
View File
@@ -1,3 +1,5 @@
use super::AttestationPurpose;
use super::AttestationPurpose;
use super::AuthRequestTelemetryContext;
use super::ModelClient;
use super::PendingUnauthorizedRetry;
@@ -8,7 +10,9 @@ use super::X_CODEX_TURN_METADATA_HEADER;
use super::X_CODEX_WINDOW_ID_HEADER;
use super::X_OPENAI_SUBAGENT_HEADER;
use codex_api::ApiError;
use codex_api::Provider as ApiProvider;
use codex_api::ResponseEvent;
use codex_api::RetryConfig as ApiRetryConfig;
use codex_app_server_protocol::AuthMode;
use codex_model_provider::BearerAuthProvider;
use codex_model_provider_info::WireApi;
@@ -383,3 +387,43 @@ fn auth_request_telemetry_context_tracks_attached_auth_and_retry_phase() {
assert_eq!(auth_context.recovery_mode, Some("managed"));
assert_eq!(auth_context.recovery_phase, Some("refresh_token"));
}
fn api_provider(base_url: &str) -> ApiProvider {
ApiProvider {
name: "test".to_string(),
base_url: base_url.to_string(),
query_params: None,
headers: http::HeaderMap::new(),
retry: ApiRetryConfig {
max_attempts: 1,
base_delay: Duration::from_millis(1),
retry_429: false,
retry_5xx: true,
retry_transport: true,
},
stream_idle_timeout: Duration::from_secs(1),
}
}
#[test]
fn should_send_attestation_for_allowed_chatgpt_codex_purposes() {
let provider = api_provider("https://chatgpt.com/backend-api/codex/");
for purpose in [
AttestationPurpose::Response,
AttestationPurpose::Compaction,
AttestationPurpose::RealtimeWebrtcCallSetup,
] {
assert!(super::should_send_attestation(&provider, purpose));
}
}
#[test]
fn should_not_send_attestation_for_non_chatgpt_codex_provider() {
let provider = api_provider("https://api.openai.com/v1");
assert!(!super::should_send_attestation(
&provider,
AttestationPurpose::Response,
));
}
+2 -1
View File
@@ -34,6 +34,7 @@ const MAX_REQUEST_MAX_RETRIES: u64 = 100;
const OPENAI_PROVIDER_NAME: &str = "OpenAI";
pub const OPENAI_PROVIDER_ID: &str = "openai";
pub const CHATGPT_CODEX_BASE_URL: &str = "https://chatgpt.com/backend-api/codex";
const AMAZON_BEDROCK_PROVIDER_NAME: &str = "Amazon Bedrock";
pub const AMAZON_BEDROCK_PROVIDER_ID: &str = "amazon-bedrock";
pub const AMAZON_BEDROCK_DEFAULT_BASE_URL: &str =
@@ -234,7 +235,7 @@ impl ModelProviderInfo {
auth_mode,
Some(AuthMode::Chatgpt | AuthMode::ChatgptAuthTokens | AuthMode::AgentIdentity)
) {
"https://chatgpt.com/backend-api/codex"
CHATGPT_CODEX_BASE_URL
} else {
"https://api.openai.com/v1"
};