Codex has four usage data paths (OAuth API, web dashboard, CLI RPC, CLI PTY) plus a local cost-usage relay. The OAuth API is the default app source when credentials are available; web access is optional for dashboard extras.
Data sources + fallback order
App default selection (debug menu disabled)
- OAuth API (auth.json credentials).
- CLI RPC, with CLI PTY fallback when needed.
- If “Access OpenAI via web” is enabled, dashboard extras load in parallel and the source label becomes
primary + openai-web.
CLI default selection (--source auto)
- OpenAI web dashboard (when available).
- Codex CLI RPC, with CLI PTY fallback when needed.
OAuth API (preferred for the app)
- Reads OAuth tokens from
~/.codex/auth.json(or$CODEX_HOME/auth.json). - Refreshes access tokens when
last_refreshis older than 8 days. - Calls
GET https://chatgpt.com/backend-api/wham/usage(default) withAuthorization: Bearer <token>. - Pro accounts can report only the weekly cap (seen 2026-10):
primary_windowis the 7-day window andsecondary_windowisnull. Runic keeps the window’s 7-day length and titles it Weekly instead of Session, with no 5h row — the same way codex’s own/statusnames limits by duration.
OpenAI web dashboard (optional)
- Preferences → Usage → “Access OpenAI via web”.
- URL:
https://chatgpt.com/codex/settings/usage. - Uses an off-screen
WKWebViewwith a per-accountWKWebsiteDataStore.- Store key: deterministic UUID from the normalized email.
- WebKit store can hold multiple accounts concurrently.
- Cookie import (when WebKit store has no matching session or login required):
- Safari:
~/Library/Cookies/Cookies.binarycookies - Chrome/Chromium forks:
~/Library/Application Support/Google/Chrome/*/Cookies - Firefox:
~/Library/Application Support/Firefox/Profiles/*/cookies.sqlite
- Domains loaded:
chatgpt.com,openai.com. - No cookie-name filter; we import all matching domain cookies.
- Safari:
- Account match:
- Signed-in email extracted from
client-bootstrapJSON in HTML (or__NEXT_DATA__). - If Codex email is known and does not match, the web path is rejected.
- Signed-in email extracted from
- Web scrape payload (via
OpenAIDashboardScrapeScript+OpenAIDashboardParser):- Rate limits (5h + weekly) parsed from body text.
- Credits remaining parsed from body text.
- Code review remaining (%).
- Usage breakdown chart (Recharts bar data + legend colors).
- Credits usage history table rows.
- Credits purchase URL (best-effort).
- Errors surfaced:
- Login required or Cloudflare interstitial.
Codex CLI RPC (default CLI fallback)
- Launches local RPC server:
codex -s read-only -a untrusted app-server. - JSON-RPC over stdin/stdout:
initialize(client name/version)account/readaccount/rateLimits/read
- Provides:
- Usage windows (primary + secondary) with reset timestamps.
- Credits snapshot (balance, hasCredits, unlimited).
- Account identity (email + plan type) when available.
Codex CLI PTY fallback (/status)
- Runs
codexin a PTY viaTTYCommandRunner. - Sends
/status, parses the rendered screen:Credits:line5h limitline → percent + reset textWeekly limitline → percent + reset text
- Retry once with a larger terminal size on parse failure.
- Detects update prompts and surfaces a “CLI update needed” error.
Account identity resolution (for web matching)
- Latest Codex usage snapshot (from RPC, if available).
~/.codex/auth.json(JWT claims: email + plan).- OpenAI dashboard signed-in email (cached).
- Last imported browser cookie email (cached).
Credits
- Web dashboard fills credits only when OAuth/CLI do not provide them.
- Credits returned with the primary usage response populate the app directly; a separate CLI probe runs only when that refresh has no balance.
- When no balance can be retrieved, the menu shows 0 with an unavailable note. A reported zero has no unavailable note, and web fetch errors stay out of the credits section.
- CLI RPC:
account/rateLimits/read→ credits balance. - CLI PTY fallback: parse
Credits:from/status.
Cost usage relay
- Live source files:
- Today’s
~/.codex/sessions/YYYY/MM/DD/*.jsonl - Or today’s
$CODEX_HOME/sessions/...files ifCODEX_HOMEis set.
- Today’s
- Historical source:
- Runic-owned event relay:
~/Library/Application Support/Runic/relay/codex-events.jsonl - Daily materialized cache:
~/Library/Application Support/Runic/ledger-cache/codex-daily.json - One-time seed from legacy scanner cache:
~/Library/Caches/Runic/cost-usage/codex-v1.json
- Runic-owned event relay:
- Refresh rule:
- Provider JSONLs before today are not reopened during normal refresh.
runic cost --rebuild --provider codexis the explicit repair path that reopens the 30-day Codex JSONL window.- Today’s Codex JSONLs are normalized into relay events with source fingerprints and scan watermarks.
- Daily totals are materialized from the newest relay snapshot for each day, then merged into the 30-day token/cost view.
- Legacy cache days with implausible token/request totals are quarantined before any cache seeding.
- Parser:
- Parses today’s
event_msgtoken_count entries andturn_contextmodel markers. - Computes input/cached/output token deltas and per-model cost.
- Parses today’s
Key files
- Web:
Sources/RunicCore/OpenAIWeb/* - CLI RPC + PTY:
Sources/RunicCore/UsageFetcher.swift,Sources/RunicCore/Providers/Codex/CodexStatusProbe.swift - Cost usage relay:
Sources/RunicCore/CostUsageFetcher.swift,Sources/RunicCore/UsageLedger/CodexUsageLogSource.swift,Sources/RunicCore/UsageLedger/LedgerCache.swift
Read the source ↗ Checked against Runic 2.13.5 · 9730f91