Skip to content

feat(models): add Daybreak aliases for cybersecurity work on eligible ChatGPT models - #327

Merged
bman654 merged 8 commits into
mainfrom
feat/daybreak-alias
Oct 13, 2026
Merged

bman654 merged 8 commits into
mainfrom
feat/daybreak-alias

Conversation

@bman654

@bman654 bman654 commented Oct 13, 2026

Copy link
Copy Markdown
Owner

If your ChatGPT account has access to OpenAI's Daybreak cybersecurity programs, you can now use them through clodex on any model that offers them, not just the dedicated gpt-daybreak-blue-latest model. Add --daybreak when you save an alias:

clodex models --alias luna-blue=clodex:openai-oauth:gpt-6-luna --daybreak

Requests through luna-blue then run under Daybreak; gpt-6-luna under any other name is unchanged. clodex reads which programs each model offers your account from your ChatGPT model catalog, so you never pick a program yourself, and it refuses up front when a model doesn't offer Daybreak.

This supersedes #325 by @mLupine, which added the same request field from a hand-written per-alias value. Its wire handling and fetch wrapper are reused here; thanks for the groundwork and the live observations that shaped this design.

Why catalog-driven

The Codex catalog (chatgpt.com/backend-api/codex/models) returns available_access_programs: { cyber: [...] } per model and per account. On our account it predicted all 33 explicit model × program results we sent live: a listed program was accepted; an unlisted one got 400 (naming the right one) or 403 (model has no Daybreak). Native Codex reads the same field and picks Daybreak Blue, then Red (codex-rs/protocol/src/openai_models/access_programs.rs, codex-rs/exec/src/daybreak.rs). Using it here removes the failure modes of a hand-written value: wrong program, ineligible model, silent typo.

The change

  • Catalog: available_access_programs.cyber is parsed on refresh into CachedModel.cyberAccessPrograms (unknown programs dropped, "absent" kept distinct from "empty"), re-validated when read back from disk, and carried onto routes and server models.
  • Alias opt-in: daybreak: true in modelAliases. normalizeModelAliases rejects a non-boolean value, and treats duplicate spellings that disagree about Daybreak as conflicting.
  • Resolution (src/daybreak.ts resolveAliasDaybreak): Blue before Red, or a reason it can't be met (not an openai-oauth route, catalog has no Daybreak data, model doesn't offer it). Every alias resolver calls it: proxy mode (buildHttpProxyRoutes), the endpoint switch menu (resolveCatalogModelAliases), the explicit/print-mode endpoint launch (singleModelDaybreakAliases), and the endpoint server (createGatewayModelCatalog).
  • No silent fallback: an alias that opted in is never sent without its program. An unmet opt-in is refused with the reason, in proxy mode, both endpoint launches and both endpoint-server routes, and reported at startup and in clodex models --list. The explicit endpoint launch used to answer every unknown model id with its one model; Daybreak aliases now get their program or a refusal there, and other aliases keep the old fallback. One case is deliberately left alone: in a hand-edited config whose only valid row for a name is plain, a second malformed Daybreak row for the same name is reported and ignored, so the name stays plain in every mode.
  • Isolation: the program rides a copy of the target route or server model, never the shared one. The endpoint server's language-model cache key includes it. Without that, whichever of an alias and its plain target is requested first decides what both send (the leak feat(openai): per-alias access_programs (Daybreak) for Responses requests #325's review demonstrated).
  • Transport: only the ChatGPT/Codex OAuth WebSocket fetch is wrapped. The API-key path is untouched: there is no per-account catalog there, and Codex gates it separately.
  • CLI: --daybreak validates through the same route builder the proxy uses. It refuses ineligible models, a stale catalog (pointing to clodex providers refresh-models), non-OAuth targets, and alias names the endpoint server answers as its own model ids (those win over aliases there). --alias/--unalias combined with --json or --context now errors; it used to skip the alias change silently.

Tests

Nine tests/daybreak*.test.ts files (56 tests). They stage state through production producers wherever practical:

  • daybreak-producers: catalog response → refresh → providers.json → account selection → config.json → all resolvers, including the general-listing fallback and per-account eligibility.
  • daybreak-server-command: the real clodex server --endpoint with a real config.json, real router, provider factory, AI SDK and WebSocket transport (only ws is fake). Both request orders, Anthropic and OpenAI routes, refusal text.
  • daybreak-alias-routing: the real CONNECT + TLS MITM in proxy mode; the endpoint switch menu; the gateway in both request orders.
  • daybreak-single-model-launch: the real runClaudeCommand explicit-endpoint launch, including saved names that normalization rejects (conflicting spellings, a non-boolean flag).
  • daybreak-wire, daybreak-continuation: the response.create frame, with Responses-Lite on and off, on the first turn and on previous_response_id continuation turns.
  • daybreak-alias-command: main([...]) dispatch, every refusal (ineligible model, stale catalog, a name that is a model id or masked discovery id), flag combinations.

Mutation matrix, full-file runs (script and results available on request): 44 production mutations, one per hunk or guard, each reddens at least one test. Three adversarial reviewers added further mutations; the ones that survived are now caught by the tests above. Those covered the general-listing catalog fallback, per-account eligibility, continuation turns, the CLI's flag forwarding, the OpenAI-format server route, the guard scopes and masked ids.

pnpm typecheck && pnpm build pass. pnpm test was run on Node v24.14.1 with an isolated CLODEX_HOME and a dead HTTPS_PROXY, on a machine at load average 100–150. The only failures were timing tests in files this branch doesn't touch, and they pass on rerun (tests/http-proxy-server.test.ts 65/65 alone, on both this branch and main). CI is the clean run.

Live evidence

ChatGPT account, 2026-10-13, built branch, temporary CLODEX_HOME holding copies of the real config:

  • clodex providers refresh-models recorded daybreak_blue for gpt-6-sol, gpt-6-luna, gpt-5.6-sol/terra/luna, gpt-5.5 and two hidden models, only standard for gpt-6.1-sol and gpt-6-astra, and only daybreak_blue for gpt-daybreak-blue-latest. No model offers daybreak_red, so the Red path is unit-tested only.
  • --daybreak saved luna-blue (Daybreak Blue) and refused gpt-6.1-sol with "Daybreak isn't available for gpt-6.1-sol on this ChatGPT account".
  • Endpoint server, both request orders: luna-blue frames carried access_programs: {"cyber":"daybreak_blue"}; the plain alias and canonical id carried none. Evidence: the payload-key hashes in the --ws-diagnostics head decisions.
  • The backend enforces the field: with a catalog edited to claim gpt-6.1-sol offers Daybreak, a Daybreak alias got OpenAI's 403 "Daybreak isn't available for this model…", while the plain alias answered.
  • Proxy mode (clodex claude -- --model luna-blue -p …) answered; an inactive alias showed API Error: 400 Clodex model route 'sol-forced' is unavailable: Daybreak isn't available for gpt-6.1-sol on this ChatGPT account. ….

What we could not verify

  • Whether Daybreak Blue on these models changes refusals for real security work. Every live probe used a harmless prompt, so they prove the program is accepted and applied, not its effect on answers.
  • The API-key path and accounts entitled to daybreak_red.
  • Switching between a Daybreak alias and its plain alias mid-conversation continues the same upstream response chain, and each request carries its own alias's program. The backend accepted this live; whether OpenAI treats the program as per-request or per-conversation is not documented.
  • Eligibility comes from the saved catalog. If OpenAI withdraws access before the next refresh, requests fail with its 403, which Claude Code shows as an authentication error.

Rollback: revert the PR. Saved aliases with "daybreak": true are then ignored by older clodex (the field is unknown there) and route as plain aliases.

@bman654
bman654 merged commit c1f02eb into main Oct 13, 2026
7 checks passed
@bman654
bman654 deleted the feat/daybreak-alias branch October 13, 2026 20:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant