---
type: research
title: Attestation Flow — DeviceCheck, Challenge, e o Hierarchy com PoW Fallback
description: The complete integrity mechanism of the desktop app — DeviceCheck native addon, challenge flow, and the if/else hierarchy with PoW fallback for non-Apple platforms
tags: [openai, attestation, devicecheck, pow, sentinel, protocol, hierarchy, fallback]
timestamp: 2026-08-25
---

# Attestation Flow — DeviceCheck, Challenge, and the PoW Hierarchy

Part of [openai-research](openai-research.md). Sources: runtime eval in the main process (bezetacil v4 harness), real captured traces, and main process code.

## The if/else hierarchy

The desktop app has a **boolean gate** that determines which integrity mechanism is used:

```
vPr() = isDeviceCheckSupported() && appVersion !== '0.0.0'
       (both checked via window.electronBridge in the renderer)

if (vPr()) {
  → ATTESTATION FLOW (macOS arm64 with DeviceCheck addon)
    GET /ios/attestation_challenge + x-sentinel-dc
    → app_attest_challenge in the conversation body
} else {
  → SENTINEL FLOW (PoW fallback for non-Apple platforms)
    POST /sentinel/chat-requirements/prepare
    → OpenAI-Sentinel-* headers (PoW FNV-1a on desktop, SHA3-512 on web)
}
```

The desktop on macOS takes the attestation path. The sentinel/PoW path exists for platforms where Apple's DeviceCheck API is unavailable.

## The DeviceCheck native addon

**File**: `/Applications/ChatGPT.app/Contents/Resources/native/devicecheck.node` (Mach-O 64-bit bundle, arm64; N-API C++ — `napi_register_module_v1`, `Napi::AsyncWorker` pattern; links `/System/Library/Frameworks/DeviceCheck.framework`)

**API**: single export `generateToken()` returning a Promise:

```json
{
  "supported": true,
  "tokenBase64": "AgAAAK9Pf1MC0r1SMXZiiu+iiG…",  // 2980 chars
  "latencyMs": 17.02
}
```

- It is a thin wrapper over macOS's **DCDevice** (DeviceCheck framework; the macOS 15+ DeviceCheck/AppAttest surface) — the token is an opaque device/app attestation (Apple server-validated with the developer's credentials).
- **NO `com.apple.developer.devicecheck` entitlement exists** — verified on the addon AND the app (`codesign -d --entitlements`): the app carries `com.apple.developer.team-identifier: 2DC432GLL2`, `aps-environment: production`, application-groups (`2DC432GLL2.com.openai.codex.notifications`, `2DC432GLL2.com.openai.sky.CUAService`), allow-jit/allow-unsigned-executable-memory, audio/camera/network/calendars, keychain-access-groups (`2DC432GLL2.*`, `2DC432GLL2.com.openai.shared`).
- **Standalone loading verified**: `require()` from a plain Node process (outside the app) → `{supported: true, tokenBase64: "AgAA…"}` — **no code-signature chain validation in the addon** (earlier claim disproven: the loader does not check the caller; macOS DCDevice provides the attestation regardless).
- Implication for flow A — UPDATED 2026-08-27: the addon is self-sufficient (no re-signing/entitlements), BUT the **plain-Node token produced 403 at the challenge** (2026-08-26, first run) and the documented acceptance continues only for tokens generated in the **app context**; the validated A1 path generates the token in the **main process** (the bezetacil `devicecheck` trigger) — see [flow-definitions](flow-definitions.md) / [flow-a-manual-step-by-step](flow-a-manual-step-by-step.md). The server-side validation is keyed to the developer's Team/bundle; acceptance of an out-of-bundle token was NOT observed (one 403 observed).

## The challenge flow (step by step)

```
1. Generate DeviceCheck token:
   addon.generateToken() → {supported: true, tokenBase64: "AgAA…", latencyMs: 17}
   (Apple hardware attestation, ~17ms)

2. GET /backend-api/ios/attestation_challenge
   Headers:
     Authorization: Bearer <codex access token>
     chatgpt-account-id: <account id from JWT>
     OAI-Language: en-US
     originator: Codex Desktop
     oai-did: <device uuid>
     x-sentinel-dc: {"token": "<DeviceCheck tokenBase64>"}
   → 200 {"attestation_challenge": "gAAAAAB…"}

3. POST /f/conversation with the challenge in the body:
   "app_attest_challenge": "gAAAAAB…"
```

## Device registration (the missing piece — runtime-verified 2026-08-27)

The main process `DeviceCheckCookieManager` (asar class `Epe`) implements the registration:

- **`registerCookie()`**: `POST /devicecheck` body `{bundle_id, device_token}` with **`credentials: 'include'`** (the Electron session cookies — the web session `__oailb`), then stores the returned `_devicecheck` cookie (name const `_devicecheck`, domain `.chatgpt.com`, secure; freshness checked against ~60s).
- **`ensureCookie()`**: reads `oai-did` from the request headers; if the did differs from the registered one or the cookie is stale → `registerCookie()`.
- **`attachToken()`**: `x-sentinel-dc: {"token": <DeviceCheck token>}` — attached to the challenge request.
- **Runtime state (verified via bezetacil)**: the Electron default session holds `_devicecheck` (`.chatgpt.com`, secure) + `__oailb` (chatgpt.com and chat.openai.com, HttpOnly — the link-session derived web session). `installation_id` in this install: `594e0bbc-3b0c-470a-833a-7943425e76ba` (file `~/.codex/installation_id`, resolved by the app-server at boot — uuid v4, regenerate-or-reuse per `codex-rs/core/src/installation_id.rs`).
- **Implication** (facts, not yet exercised): an unregistered did+token pair gets 403 on `/ios/attestation_challenge`; the registration POST carries the web-session cookies — the standalone probe flow needs the `__oailb` (link-session) + the `/devicecheck` step before the challenge.

## What the server validates

The server receives the DeviceCheck token via `x-sentinel-dc` and validates it against Apple's DeviceCheck API using the app's server-side credentials. This proves:
- The request originates from a real Apple device
- The app is the genuine ChatGPT desktop (bundle ID `com.openai.codex`)
- The device hasn't been modified to bypass attestation

The `attestation_challenge` returned is a **one-time-use token** that the server validates when the conversation request arrives. It expires quickly (observed: valid for the duration of one message send).

## The sentinel/PoW fallback (non-Apple platforms)

On Windows/Linux, `or({arch, platform})` returns `false` (requires `darwin && arm64`), so `generateToken()` returns `null` → `vPr()` is false → the renderer falls back to the sentinel flow:

- `POST /sentinel/chat-requirements/prepare` with fingerprint
- PoW solving (FNV-1a on desktop, SHA3-512 on web)
- `OpenAI-Sentinel-*` headers on the conversation request

This is the flow that OmniRoute implements (for web/cookies) and that our probes initially replicated — but it's the **fallback**, not the primary mechanism.

## The `x-sentinel-dc` header format

```json
{"token": "<Apple DeviceCheck tokenBase64>"}
```

Wrapped by `attachToken()` on the `deviceCheckCookieManager` (asar class `Epe`), which also manages a **registration cookie** (`POST /devicecheck` with `{bundle_id, device_token}`) to register the device with the backend before the first attested request.

## DeviceCheck addon standalone loading (verified again 2026-08-26, plain Node)

The addon exports exactly one function. Loading it from a **plain Node process** (no app context) works — this is the flow-A basis for a standalone:

```sh
node -e "const a=require('/Applications/ChatGPT.app/Contents/Resources/native/devicecheck.node');
a.generateToken().then(t=>console.log(t.supported, t.tokenBase64.slice(0,12)))"
# → true AgAAAK9Pf1MC…
```

`Object.keys(addon)` → `["generateToken"]`. (Note: the app's own main loads it via `process.resourcesPath + '/native/' + 'devicecheck.node'` through `createRequire` in the app — same addon.)

## The platform gate (the if/else that decides attestation vs sentinel)

The main process gate is `or({arch, platform})`:

```
function or({arch = process.arch, platform = process.platform} = {})
  { return platform === 'darwin' && arch === 'arm64' }
```

Condition chain:
- `or()` false → `sr()` (the addon loader) returns null → DeviceCheck unavailable
- `ar({platform})` — platform pre-gate: `platform === 'darwin' || platform === 'win32'` (Windows can generate but the arch gate filters to darwin arm64)
- `vPr()` in the renderer — the final boolean: `isDeviceCheckSupported() === true && appVersion !== '0.0.0'`

To exercise the sentinel/PoW fallback from a Mac, the gate to patch at runtime is `vPr()` in the renderer (or `or()` in the main) — flipping either makes the app take the sentinel route.

## What the server validates (DeviceCheck token)

1. Sends the DeviceCheck token via `x-sentinel-dc: {"token": "<tokenBase64>"}`
2. The server validates it against Apple's DeviceCheck API using its own server-side credentials for the app's Team ID + bundle ID (`com.openai.codex`)
3. Returns the one-time `attestation_challenge` which the client must echo in the conversation body as `app_attest_challenge`

This is why the token is not forgeable outside an Apple device running the signed app: Apple's API rejects tokens from unregistered devices or other bundle IDs, and the server owns the server-side DeviceCheck verification secrets.

## TypeScript types (addon + manager + wire)

```ts
// ── the native addon ─────────────────────────────────────────────────────
export interface GenerateTokenResponse {
  supported: boolean
  tokenBase64: string          // base64 App-Attest (2980 chars) — 'AgAA…'
  latencyMs: number            // ~17
}
export interface DeviceCheckAddon {
  generateToken(): Promise<GenerateTokenResponse>    // single export
}

// ── the main-process gate ────────────────────────────────────────────────
export type PlatformGate = { arch?: string; platform?: string }   // defaults: process.arch/process.platform
export const gate = (o: PlatformGate): boolean => o.platform === 'darwin' && o.arch === 'arm64'
export interface ElectronBridge {
  isDeviceCheckSupported(): boolean
  // plus appVersion from SentryInitOptions ('0.0.0' → gate off)
}
export const vPr = (isSupported: boolean, appVersion: string): boolean =>
  isSupported && appVersion !== '0.0.0'

// ── the DeviceCheckCookieManager (asar class Epe) ────────────────────────
export interface DeviceCheckRegistrationBody {       // POST /devicecheck
  bundle_id: string            // 'com.openai.codex'
  device_token: string
}
export interface DeviceCheckRegistrationResponse {
  is_ok: boolean
  status_code: number
  // …(server description fields)
}
export interface EpeOptions {
  bundleIdentifier: string
  generateToken(): Promise<GenerateTokenResponse>    // the addon loader
  url: string                  // '/devicecheck' resolved against the prod API base
}
export interface EpeState {
  registration: Promise<unknown> | null
  registeredCookie: Electron.Cookie | null
  registeredDeviceId: string | null
}
export interface DeviceCheckCookieManager {
  ensureCookie(headers: Record<string, string>): Promise<void>   // compare oai-did + cookie; register if stale/unknown
  attachToken(headers: Record<string, string>): Promise<void>    // headers['x-sentinel-dc'] = JSON.stringify({token})
  registerCookie(headers: Record<string, string>): Promise<Electron.Cookie>  // POST /devicecheck (credentials:'include')
}
export const DeviceCheckCookieName = '_devicecheck'   // the registration cookie name
export const DeviceCheckCookieFreshnessMs = 60_000    // the freshness window (Tpe)

// ── the challenge wire ───────────────────────────────────────────────────
export interface XSentinelDc { token: string }         // headers['x-sentinel-dc'] = JSON string of this
export interface AttestationChallengeResponse { attestation_challenge: string }  // 'gAAAAAB…', one-time

// ── entitlements (verified) ──────────────────────────────────────────────
export interface AppEntitlements {
  'com.apple.developer.team-identifier': '2DC432GLL2'
  'com.apple.developer.aps-environment': 'production'
  'com.apple.security.application-groups': string[]
  'com.apple.security.cs.allow-jit': boolean
  'com.apple.security.cs.allow-unsigned-executable-memory': boolean
  'com.apple.security.device.audio-input'?: boolean
  'com.apple.security.device.camera'?: boolean
  'com.apple.security.files.user-selected.read-write'?: boolean
  'com.apple.security.network.client': boolean
  'com.apple.security.personal-information.calendars'?: boolean
  'keychain-access-groups': string[]
  // NO com.apple.developer.devicecheck entitlement (verified — macOS DCDevice does not require it)
}
```

## Cross-references

- [chat-submode-protocol](chat-submode-protocol.md) — where the challenge enters the conversation body
- [sentinel-fingerprint-comparison](sentinel-fingerprint-comparison.md) — the PoW fingerprint details
- [codex-flow-protocol](codex-flow-protocol.md) — the backend codex endpoints
- [chatgpt-desktop-architecture](chatgpt-desktop-architecture.md) — the app architecture
- [flow-definitions](flow-definitions.md) / [flow-a-manual-step-by-step](flow-a-manual-step-by-step.md) — the A/A1 placement and the validated steps
