WikifitaGitHub live67e8de5
pesquisa · openai-research/proof-of-work

Proof of Work — Sentinel PoW Mechanism (Desktop vs Web)

Baixar raw

Proof of Work — Sentinel PoW Mechanism (Desktop vs Web)

Part of openai-research. The sentinel PoW is the integrity mechanism used by the web/browser route (and by the desktop fallback when DeviceCheck is unavailable — the gate flip).

What it is, where it lives

  • The client (renderer) builds a fingerprint (array of slots, sPr, 24 slots on desktop — see sentinel-fingerprint-comparison), serializes gAAAAAC + base64(fingerprint) as the prekey body for POST /sentinel/chat-requirements/prepare.
  • The server returns requirements + a seed; the client then solves a PoW to mutate the fingerprint slots (counter and time slots — the solver increments/mutates them) and computes the final value.
  • PoW hash: desktop = FNV-1a 32-bit murmur-finalized; web route (OmniRoute reference) = SHA3-512 (100k iteration loop, target 0fffff) — mutually exclusive families per client/route.
  • The final key travels in the OpenAI-Sentinel-* headers of the protected requests.

When it runs / who invokes it

  • Web route: per typing throttle (/f/conversation/prepare fires on every keystroke, debounced — the prepare carries the growing partial text), so longer conversations → heavier continuous PoW (observed CPU increase / GPU idle in Safari = single-threaded JS solving, no GPU/WASM accelerator, no web worker).
  • Desktop (fallback): the same sentinel chain with the desktop fingerprint, originator: Codex Browser + plain Chrome UA (Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) … Chrome/151.0.0.0), one-time per prepare/first request.
  • Solver is in the renderer main thread (no dedicated worker found in the asar; the murmur2/murmur3 hash primitives are bundled). This is the CPU spike/GPU idle observation: single-thread JS.
  • The desktop main route (attestation) has no PoW — the prepare still runs on keystrokes (typing throttle) but without the sentinel chain.

The slot values baked in (desktop, real trailer)

0 screen dims (e.g. 2494)? No — real: slot 0 = screen.width + screen.height derived, slot 1 = new Date().toString(), slot 2 = performance.memory.jsHeapSizeLimit (real ~4.4GB in the flip run), slot 3 = counter (solver-mutated), slot 4 = UA, slot 5 = real webview chunk (app://-/assets/index-rLYmMQgS.js), slot 6 = <!-- PROD_BUILD_TAG_HERE --> (confirmed leaving the client, accepted by the server), 7-8 languages, 9 = time (solver-mutated), 10-12 navigator/document/window props, 13 performance.now(), 14 uuid, 15 search params, 16 hardwareConcurrency, 17 timeOrigin, 18-23 flags.

Contrast with DeviceCheck (the alternative)

Sentinel/PoWDeviceCheck attestation
Gate!vPr() (no DeviceCheck)vPr() = isDeviceCheckSupported && appVersion ≠ 0.0.0
CostCPU-bound single-thread JS, per prepare/typing~17ms native (hardware attestation via addon)
Header/bodyOpenAI-Sentinel-* + prekey px-sentinel-dc: {token} → challenge gAAAAAB… → body app_attest_challenge
Detection surfacefingerprint slots + PoW hash familyApple's DeviceCheck server validation
Mobile/web/desktopweb (SHA3-512) + desktop fallback (FNV-1a)desktop macOS arm64 only

For the standalone

The PoW is single-threaded JS on the web route — a standalone harness can solve it far faster with WebGPU/WASM (or wasi/wasix in Node), but for the desktop flows the attestation path is the better target (no PoW, ~17ms). The fallback PoW (desktop flavor: FNV-1a + real fingerprint) is reproducible with the captured slot values. performance.now()/timeOrigin should be sampled honestly — the server validates ranges (the web-vendor fixed/heap hardcodes (4294705152) are a detectable deviation).

TypeScript types (solver)

export type PoWHashFamily = 'fnv1a-32-murmur' | 'sha3-512'

export interface SolverResult { answer: string; slots: unknown[] }
export interface PoWSolver {
  /** Mutate the mutable slots (counter/time) until the hash meets the target. */
  solve(seed: string, difficulty: string): SolverResult
  /** FNV-1a 32-bit murmur-finalized (desktop) — the only family on the desktop fallback. */
  hash: (input: string) => string
}

export interface SentinelRequirements {           // POST /sentinel/chat-requirements/prepare → 200
  token?: string
  prepare_token?: string
  pow?: { seed: string; difficulty: string }       // when non-empty, the client solves and sets
}

// ── headers the solved key lands in ──────────────────────────────────────
export interface OpenAIRequestSentinelHeaders {
  'OpenAI-Sentinel-Chat-Requirements-Token'?: string
  'OpenAI-Sentinel-Chat-Requirements-Prepare-Token'?: string
  'OpenAI-Sentinel-Proof-Token'?: string           // 'gAAAAAB' + base64(seed+~S…)
}

// ── the contrast table as types ──────────────────────────────────────────
export interface IntegrityMechanism {
  gate: 'devicecheck' | 'sentinel-pow'
  cost: 'native ~17ms' | 'cpu-bound single-thread js'
  headers: { 'x-sentinel-dc'?: string; 'OpenAI-Sentinel-*'?: string }
  lifetime: 'one-time challenge' | 'per-prepare'
}

Cross-references