Originator Persona Hardening Job Spec #1

Open
opened 2026-06-27 10:50:25 +00:00 by mika · 1 comment
Owner

Originator Persona Hardening Job Spec

Goal

Prevent upstream token revocation by making OAuth minting personas and upstream request personas congruent, and by removing downstream client identity fields that can reveal a persona mismatch.

The chosen canonical persona is:

codex_chatgpt_desktop

Background

codex-lb proxies Codex/Responses traffic to ChatGPT upstream using stored OAuth credentials. Those credentials are minted through codex-lb's OAuth flow. The current default OAuth authorize URL includes:

originator=codex_chatgpt_desktop

However, proxied upstream requests can preserve downstream client identity headers. For example, Codex CLI can send:

originator: codex_cli_rs
user-agent: codex-tui/0.142.3 (...)

OpenCode may not send an explicit originator, but its traffic can still expose an OpenCode-looking User-Agent or x-openai-client-* fields while using OAuth tokens minted under the desktop persona.

This creates a suspicious envelope:

OAuth token minted as:     codex_chatgpt_desktop
Upstream request sent as:  codex_cli_rs / opencode / other client persona
Same bearer token:         yes

Live incident evidence showed usage refresh was only the detector, not the revocation source. Affected accounts had successful usage refreshes shortly before upstream Codex WebSocket traffic, then 401 token_revoked shortly after:

c1b93771: usage OK 08:03:15 -> OpenCode HTTP bridge upstream WS traffic 08:03:34-08:04:18 -> token_revoked 08:04:19
4737cbcd: usage OK 08:10:39 -> Codex CLI WS traffic 08:11:08/08:11:15 -> token_revoked 08:11:41

Important nuance: OpenCode's external /v1/responses path is not necessarily pure upstream HTTP. codex-lb's HTTP responses session bridge opens/reuses an upstream /backend-api/codex/responses WebSocket internally, so both OpenCode and Codex CLI can exercise the upstream native Codex WebSocket endpoint.

Agreed Principles

  1. OAuth minting and request personas must be congruent.
Mint token with persona P.
Use token only with persona P.
Do not forward downstream client persona Q.
  1. Extra fields that identify a mismatched downstream client should be omitted or normalized.

  2. Use codex_chatgpt_desktop as the canonical persona for this repo/default deployment.

  3. Do not silently change the persona for existing tokens based on downstream traffic.

  4. If originator configurability remains, it should affect token minting and the matching upstream request persona together. Existing tokens should keep their minted persona if persisted per account.

Target Upstream Persona Envelope

For upstream ChatGPT/Codex requests that use stored account OAuth tokens, send a minimal, coherent envelope. Example:

Authorization: Bearer <account-access-token>
chatgpt-account-id: <account-id>
originator: codex_chatgpt_desktop
openai-beta: responses_websockets=2026-02-06

Do not forward downstream identity fields that contradict the canonical persona, such as:

originator: codex_cli_rs
user-agent: opencode/...
user-agent: codex-tui/...
x-openai-client-user-agent: ...
x-openai-client-version: ...
x-openai-client-id: ...
x-openai-client-os: ...
x-openai-client-arch: ...

If a User-Agent is required upstream, stamp a stable first-party-compatible value that matches codex_chatgpt_desktop; otherwise omit it.

Required Workflow

This is a behavior change. Follow the repo's OpenSpec-first workflow before coding.

  1. Create an OpenSpec change under openspec/changes/<slug>/.
  2. Update relevant delta specs with normative MUST/SHALL requirements.
  3. Put rationale and operational notes in context.md or change notes, not docs/.
  4. Implement code and tests.
  5. Run openspec validate --specs and targeted tests.
  6. Do not edit CHANGELOG.md.

Suggested change slug:

harden-originator-persona-envelope

Relevant Existing Specs

  • openspec/specs/outbound-http-clients/spec.md
    • Currently requires browser OAuth authorize requests to include configurable originator.
    • Current default is codex_chatgpt_desktop.
  • openspec/specs/responses-api-compat/spec.md
    • Covers upstream Responses transport strategy and native Codex websocket behavior.
  • openspec/specs/responses-api-compat/ops.md
    • Has native Codex websocket capture/diagnostic context.

Relevant Code

  • app/core/config/settings.py
    • oauth_originator: str = "codex_chatgpt_desktop"
    • upstream_stream_transport: "auto"
    • HTTP bridge enabled by default.
  • app/core/clients/oauth.py
    • build_authorization_url() adds originator to authorize URLs.
  • app/core/auth/refresh.py
    • Refresh token requests use settings.oauth_client_id; no originator is currently stamped.
  • app/core/clients/proxy_websocket.py
    • filter_inbound_websocket_headers() and _build_upstream_websocket_headers() preserve many downstream headers and add auth/account headers.
    • _ensure_responses_websocket_beta_header() adds openai-beta: responses_websockets=2026-02-06.
  • app/core/clients/proxy.py
    • filter_inbound_headers() and _build_upstream_websocket_headers() for direct streaming paths.
    • _NATIVE_CODEX_ORIGINATORS includes codex_chatgpt_desktop, codex_cli_rs, etc.
  • app/modules/proxy/_service/websocket/mixin.py
    • External /backend-api/codex/responses WebSocket proxy path.
    • Opens upstream WebSocket with filtered downstream headers.
  • app/modules/proxy/_service/http_bridge/mixin.py
    • HTTP bridge path creates/reuses upstream WebSocket sessions.
    • _create_http_bridge_session() calls _open_upstream_websocket_with_budget() with filtered client headers.
  • app/modules/proxy/_service/http_bridge/streaming.py
    • /v1/responses HTTP path can route through the HTTP bridge.
  • tests/unit/test_oauth_client.py
    • Existing tests assert default and overridden OAuth originator.
  • tests/unit/test_proxy_utils.py
    • Existing tests assert native Codex headers are forwarded today.
  • tests/integration/test_proxy_responses.py
    • Existing integration test test_proxy_responses_forwards_native_codex_headers currently expects native header preservation; this likely needs revision.

Implementation Tasks

1. Define Persona Sanitization Policy

Add a central helper for outbound account-authenticated ChatGPT/Codex requests. The helper should:

  • Drop downstream identity headers that can conflict with the account persona.
  • Stamp originator: codex_chatgpt_desktop for account-authenticated upstream Codex requests.
  • Preserve required protocol headers such as openai-beta for WebSocket support.
  • Preserve request tracing headers only if safe, such as x-request-id / request-id.
  • Preserve chatgpt-account-id only as injected from the selected account, not downstream.
  • Preserve Authorization only as injected from the selected account token, not downstream.

Candidate helper names:

sanitize_upstream_codex_persona_headers(...)
build_account_persona_headers(...)

Keep the implementation small and centralized so HTTP bridge, direct WebSocket, and direct stream paths cannot diverge.

2. Apply Policy to Upstream WebSocket Handshakes

Update upstream WebSocket header construction in both clients:

  • app/core/clients/proxy_websocket.py::_build_upstream_websocket_headers
  • app/core/clients/proxy.py::_build_upstream_websocket_headers

Required behavior:

  • Downstream originator MUST NOT be forwarded.
  • Upstream originator MUST be codex_chatgpt_desktop for stored account-token requests.
  • Downstream user-agent MUST NOT be forwarded unless normalized to the canonical persona.
  • Downstream x-openai-client-* identity fields MUST NOT be forwarded unless normalized to the canonical persona.
  • Downstream x-codex-* fields that are request semantics, such as x-codex-turn-state, may be preserved if needed for session continuity.
  • Downstream x-codex-* fields that identify a client/product should be reviewed and dropped if they conflict.

3. Apply Policy to Upstream HTTP/SSE Responses Calls

Audit app/core/clients/proxy.py::_build_upstream_headers().

Even if the immediate incident centers on upstream WebSockets, HTTP/SSE account-token calls should not expose a contradictory downstream persona either.

Required behavior:

  • Same account persona congruence as WebSocket.
  • Preserve only semantically required headers.

4. Consider Per-Account Originator Persistence

Preferred robust design:

  • Add oauth_originator or equivalent account metadata persisted at OAuth mint/import time.
  • For newly minted accounts, store the originator used by build_authorization_url().
  • For legacy accounts with no stored value, default to codex_chatgpt_desktop.
  • Upstream account-authenticated requests use the account's stored originator.

Minimal acceptable design for this change if avoiding migration scope:

  • Use settings.oauth_originator, currently codex_chatgpt_desktop, consistently for both OAuth minting and upstream request stamping.
  • Document that changing CODEX_LB_OAUTH_ORIGINATOR requires reauthenticating accounts minted under the old persona.

The preferred design is safer long-term, but the minimal design may be acceptable if the project wants a smaller fix. If choosing minimal, include clear OpenSpec/context notes about the operational constraint.

5. Revisit Existing Tests That Require Header Preservation

Some existing tests assert forwarding native Codex headers exactly. Those expectations are now unsafe.

Update them to assert:

  • Request semantics are preserved.
  • Downstream client persona fields are not forwarded.
  • Canonical persona is stamped.

For example, a test that sends:

headers = {
    "originator": "codex_cli_rs",
    "user-agent": "codex-tui/0.142.3",
    "x-openai-client-version": "0.142.3",
    "session_id": "sid-native",
    "x-codex-turn-state": "turn_123",
}

Should assert upstream sees:

headers["originator"] == "codex_chatgpt_desktop"
headers["Authorization"] == "Bearer <selected-token>"
headers["chatgpt-account-id"] == "<selected-account-id>"
headers["openai-beta"] includes "responses_websockets=2026-02-06"
"codex_cli_rs" not in serialized headers
"opencode" not in serialized headers

And still preserves required continuity headers if applicable:

headers["session_id"] == "sid-native"  # if still required upstream
headers["x-codex-turn-state"] == "turn_123"  # if still required upstream

6. Add Regression Coverage

Add targeted unit/integration tests for:

  • OAuth authorize URL still defaults to originator=codex_chatgpt_desktop.
  • Upstream WebSocket handshakes from Codex CLI downstream traffic normalize originator to codex_chatgpt_desktop.
  • Upstream WebSocket handshakes from OpenCode/downstream HTTP bridge traffic do not forward OpenCode-looking User-Agent or x-openai-client-* identity fields.
  • Direct HTTP/SSE upstream requests do not forward contradictory persona headers.
  • Account/token auth headers continue to be injected from selected account, not downstream.
  • Required WebSocket beta header is still present.

Suggested existing test files to extend:

  • tests/unit/test_proxy_utils.py
  • tests/unit/test_oauth_client.py
  • tests/integration/test_proxy_responses.py

Acceptance Criteria

Implementation is ready when:

  • OAuth minting persona remains codex_chatgpt_desktop by default.
  • Account-authenticated upstream Codex/Responses requests do not forward downstream originator values.
  • Account-authenticated upstream Codex/Responses requests stamp originator=codex_chatgpt_desktop or the account's persisted equivalent.
  • Downstream client identity fields that could reveal opencode, codex_cli_rs, or other non-canonical personas are omitted or normalized.
  • The HTTP bridge and direct WebSocket paths share the same persona policy.
  • Existing routing, account selection, sticky affinity, and previous-response continuity tests still pass or are intentionally updated.
  • openspec validate --specs passes.
  • Targeted pytest suite passes.

Non-Goals

  • Do not solve stale bridge/session reuse after reauth_required in this change unless it is already scoped into the chosen OpenSpec change. That is a separate containment bug.
  • Do not change the global default persona away from codex_chatgpt_desktop.
  • Do not add changelog entries manually.
  • Do not retain backward-compatible forwarding of client persona headers unless there is a documented, tested reason.

Operational Notes

After this fix, operators should not mix tokens minted with one persona and outbound traffic stamped with another persona.

If per-account originator persistence is not implemented, changing CODEX_LB_OAUTH_ORIGINATOR should be treated as requiring account reauthentication. Otherwise old accounts may have tokens minted under the previous persona while new outbound traffic uses the new configured persona.

For debugging future incidents, enable upstream summary logging or conversation archive only in a controlled environment because payloads and headers may contain sensitive data.

Suggested Verification Commands

Adjust test selection based on actual changes, but likely commands are:

openspec validate --specs
uv run pytest tests/unit/test_oauth_client.py tests/unit/test_proxy_utils.py tests/integration/test_proxy_responses.py
# Originator Persona Hardening Job Spec ## Goal Prevent upstream token revocation by making OAuth minting personas and upstream request personas congruent, and by removing downstream client identity fields that can reveal a persona mismatch. The chosen canonical persona is: ```text codex_chatgpt_desktop ``` ## Background codex-lb proxies Codex/Responses traffic to ChatGPT upstream using stored OAuth credentials. Those credentials are minted through codex-lb's OAuth flow. The current default OAuth authorize URL includes: ```text originator=codex_chatgpt_desktop ``` However, proxied upstream requests can preserve downstream client identity headers. For example, Codex CLI can send: ```http originator: codex_cli_rs user-agent: codex-tui/0.142.3 (...) ``` OpenCode may not send an explicit `originator`, but its traffic can still expose an OpenCode-looking `User-Agent` or `x-openai-client-*` fields while using OAuth tokens minted under the desktop persona. This creates a suspicious envelope: ```text OAuth token minted as: codex_chatgpt_desktop Upstream request sent as: codex_cli_rs / opencode / other client persona Same bearer token: yes ``` Live incident evidence showed usage refresh was only the detector, not the revocation source. Affected accounts had successful usage refreshes shortly before upstream Codex WebSocket traffic, then `401 token_revoked` shortly after: ```text c1b93771: usage OK 08:03:15 -> OpenCode HTTP bridge upstream WS traffic 08:03:34-08:04:18 -> token_revoked 08:04:19 4737cbcd: usage OK 08:10:39 -> Codex CLI WS traffic 08:11:08/08:11:15 -> token_revoked 08:11:41 ``` Important nuance: OpenCode's external `/v1/responses` path is not necessarily pure upstream HTTP. codex-lb's HTTP responses session bridge opens/reuses an upstream `/backend-api/codex/responses` WebSocket internally, so both OpenCode and Codex CLI can exercise the upstream native Codex WebSocket endpoint. ## Agreed Principles 1. OAuth minting and request personas must be congruent. ```text Mint token with persona P. Use token only with persona P. Do not forward downstream client persona Q. ``` 2. Extra fields that identify a mismatched downstream client should be omitted or normalized. 3. Use `codex_chatgpt_desktop` as the canonical persona for this repo/default deployment. 4. Do not silently change the persona for existing tokens based on downstream traffic. 5. If originator configurability remains, it should affect token minting and the matching upstream request persona together. Existing tokens should keep their minted persona if persisted per account. ## Target Upstream Persona Envelope For upstream ChatGPT/Codex requests that use stored account OAuth tokens, send a minimal, coherent envelope. Example: ```http Authorization: Bearer <account-access-token> chatgpt-account-id: <account-id> originator: codex_chatgpt_desktop openai-beta: responses_websockets=2026-02-06 ``` Do not forward downstream identity fields that contradict the canonical persona, such as: ```http originator: codex_cli_rs user-agent: opencode/... user-agent: codex-tui/... x-openai-client-user-agent: ... x-openai-client-version: ... x-openai-client-id: ... x-openai-client-os: ... x-openai-client-arch: ... ``` If a `User-Agent` is required upstream, stamp a stable first-party-compatible value that matches `codex_chatgpt_desktop`; otherwise omit it. ## Required Workflow This is a behavior change. Follow the repo's OpenSpec-first workflow before coding. 1. Create an OpenSpec change under `openspec/changes/<slug>/`. 2. Update relevant delta specs with normative MUST/SHALL requirements. 3. Put rationale and operational notes in `context.md` or change notes, not `docs/`. 4. Implement code and tests. 5. Run `openspec validate --specs` and targeted tests. 6. Do not edit `CHANGELOG.md`. Suggested change slug: ```text harden-originator-persona-envelope ``` ## Relevant Existing Specs - `openspec/specs/outbound-http-clients/spec.md` - Currently requires browser OAuth authorize requests to include configurable `originator`. - Current default is `codex_chatgpt_desktop`. - `openspec/specs/responses-api-compat/spec.md` - Covers upstream Responses transport strategy and native Codex websocket behavior. - `openspec/specs/responses-api-compat/ops.md` - Has native Codex websocket capture/diagnostic context. ## Relevant Code - `app/core/config/settings.py` - `oauth_originator: str = "codex_chatgpt_desktop"` - `upstream_stream_transport: "auto"` - HTTP bridge enabled by default. - `app/core/clients/oauth.py` - `build_authorization_url()` adds `originator` to authorize URLs. - `app/core/auth/refresh.py` - Refresh token requests use `settings.oauth_client_id`; no originator is currently stamped. - `app/core/clients/proxy_websocket.py` - `filter_inbound_websocket_headers()` and `_build_upstream_websocket_headers()` preserve many downstream headers and add auth/account headers. - `_ensure_responses_websocket_beta_header()` adds `openai-beta: responses_websockets=2026-02-06`. - `app/core/clients/proxy.py` - `filter_inbound_headers()` and `_build_upstream_websocket_headers()` for direct streaming paths. - `_NATIVE_CODEX_ORIGINATORS` includes `codex_chatgpt_desktop`, `codex_cli_rs`, etc. - `app/modules/proxy/_service/websocket/mixin.py` - External `/backend-api/codex/responses` WebSocket proxy path. - Opens upstream WebSocket with filtered downstream headers. - `app/modules/proxy/_service/http_bridge/mixin.py` - HTTP bridge path creates/reuses upstream WebSocket sessions. - `_create_http_bridge_session()` calls `_open_upstream_websocket_with_budget()` with filtered client headers. - `app/modules/proxy/_service/http_bridge/streaming.py` - `/v1/responses` HTTP path can route through the HTTP bridge. - `tests/unit/test_oauth_client.py` - Existing tests assert default and overridden OAuth originator. - `tests/unit/test_proxy_utils.py` - Existing tests assert native Codex headers are forwarded today. - `tests/integration/test_proxy_responses.py` - Existing integration test `test_proxy_responses_forwards_native_codex_headers` currently expects native header preservation; this likely needs revision. ## Implementation Tasks ### 1. Define Persona Sanitization Policy Add a central helper for outbound account-authenticated ChatGPT/Codex requests. The helper should: - Drop downstream identity headers that can conflict with the account persona. - Stamp `originator: codex_chatgpt_desktop` for account-authenticated upstream Codex requests. - Preserve required protocol headers such as `openai-beta` for WebSocket support. - Preserve request tracing headers only if safe, such as `x-request-id` / `request-id`. - Preserve `chatgpt-account-id` only as injected from the selected account, not downstream. - Preserve `Authorization` only as injected from the selected account token, not downstream. Candidate helper names: ```python sanitize_upstream_codex_persona_headers(...) build_account_persona_headers(...) ``` Keep the implementation small and centralized so HTTP bridge, direct WebSocket, and direct stream paths cannot diverge. ### 2. Apply Policy to Upstream WebSocket Handshakes Update upstream WebSocket header construction in both clients: - `app/core/clients/proxy_websocket.py::_build_upstream_websocket_headers` - `app/core/clients/proxy.py::_build_upstream_websocket_headers` Required behavior: - Downstream `originator` MUST NOT be forwarded. - Upstream `originator` MUST be `codex_chatgpt_desktop` for stored account-token requests. - Downstream `user-agent` MUST NOT be forwarded unless normalized to the canonical persona. - Downstream `x-openai-client-*` identity fields MUST NOT be forwarded unless normalized to the canonical persona. - Downstream `x-codex-*` fields that are request semantics, such as `x-codex-turn-state`, may be preserved if needed for session continuity. - Downstream `x-codex-*` fields that identify a client/product should be reviewed and dropped if they conflict. ### 3. Apply Policy to Upstream HTTP/SSE Responses Calls Audit `app/core/clients/proxy.py::_build_upstream_headers()`. Even if the immediate incident centers on upstream WebSockets, HTTP/SSE account-token calls should not expose a contradictory downstream persona either. Required behavior: - Same account persona congruence as WebSocket. - Preserve only semantically required headers. ### 4. Consider Per-Account Originator Persistence Preferred robust design: - Add `oauth_originator` or equivalent account metadata persisted at OAuth mint/import time. - For newly minted accounts, store the originator used by `build_authorization_url()`. - For legacy accounts with no stored value, default to `codex_chatgpt_desktop`. - Upstream account-authenticated requests use the account's stored originator. Minimal acceptable design for this change if avoiding migration scope: - Use `settings.oauth_originator`, currently `codex_chatgpt_desktop`, consistently for both OAuth minting and upstream request stamping. - Document that changing `CODEX_LB_OAUTH_ORIGINATOR` requires reauthenticating accounts minted under the old persona. The preferred design is safer long-term, but the minimal design may be acceptable if the project wants a smaller fix. If choosing minimal, include clear OpenSpec/context notes about the operational constraint. ### 5. Revisit Existing Tests That Require Header Preservation Some existing tests assert forwarding native Codex headers exactly. Those expectations are now unsafe. Update them to assert: - Request semantics are preserved. - Downstream client persona fields are not forwarded. - Canonical persona is stamped. For example, a test that sends: ```python headers = { "originator": "codex_cli_rs", "user-agent": "codex-tui/0.142.3", "x-openai-client-version": "0.142.3", "session_id": "sid-native", "x-codex-turn-state": "turn_123", } ``` Should assert upstream sees: ```python headers["originator"] == "codex_chatgpt_desktop" headers["Authorization"] == "Bearer <selected-token>" headers["chatgpt-account-id"] == "<selected-account-id>" headers["openai-beta"] includes "responses_websockets=2026-02-06" "codex_cli_rs" not in serialized headers "opencode" not in serialized headers ``` And still preserves required continuity headers if applicable: ```python headers["session_id"] == "sid-native" # if still required upstream headers["x-codex-turn-state"] == "turn_123" # if still required upstream ``` ### 6. Add Regression Coverage Add targeted unit/integration tests for: - OAuth authorize URL still defaults to `originator=codex_chatgpt_desktop`. - Upstream WebSocket handshakes from Codex CLI downstream traffic normalize `originator` to `codex_chatgpt_desktop`. - Upstream WebSocket handshakes from OpenCode/downstream HTTP bridge traffic do not forward OpenCode-looking `User-Agent` or `x-openai-client-*` identity fields. - Direct HTTP/SSE upstream requests do not forward contradictory persona headers. - Account/token auth headers continue to be injected from selected account, not downstream. - Required WebSocket beta header is still present. Suggested existing test files to extend: - `tests/unit/test_proxy_utils.py` - `tests/unit/test_oauth_client.py` - `tests/integration/test_proxy_responses.py` ## Acceptance Criteria Implementation is ready when: - OAuth minting persona remains `codex_chatgpt_desktop` by default. - Account-authenticated upstream Codex/Responses requests do not forward downstream `originator` values. - Account-authenticated upstream Codex/Responses requests stamp `originator=codex_chatgpt_desktop` or the account's persisted equivalent. - Downstream client identity fields that could reveal `opencode`, `codex_cli_rs`, or other non-canonical personas are omitted or normalized. - The HTTP bridge and direct WebSocket paths share the same persona policy. - Existing routing, account selection, sticky affinity, and previous-response continuity tests still pass or are intentionally updated. - `openspec validate --specs` passes. - Targeted pytest suite passes. ## Non-Goals - Do not solve stale bridge/session reuse after `reauth_required` in this change unless it is already scoped into the chosen OpenSpec change. That is a separate containment bug. - Do not change the global default persona away from `codex_chatgpt_desktop`. - Do not add changelog entries manually. - Do not retain backward-compatible forwarding of client persona headers unless there is a documented, tested reason. ## Operational Notes After this fix, operators should not mix tokens minted with one persona and outbound traffic stamped with another persona. If per-account originator persistence is not implemented, changing `CODEX_LB_OAUTH_ORIGINATOR` should be treated as requiring account reauthentication. Otherwise old accounts may have tokens minted under the previous persona while new outbound traffic uses the new configured persona. For debugging future incidents, enable upstream summary logging or conversation archive only in a controlled environment because payloads and headers may contain sensitive data. ## Suggested Verification Commands Adjust test selection based on actual changes, but likely commands are: ```bash openspec validate --specs uv run pytest tests/unit/test_oauth_client.py tests/unit/test_proxy_utils.py tests/integration/test_proxy_responses.py ```
mika changed title from bug: <one-line summary of what's broken> to Originator Persona Hardening Job Spec 2026-06-27 10:56:09 +00:00
mika changed title from bug: <one-line summary of what's broken> to Originator Persona Hardening Job Spec 2026-06-27 10:56:11 +00:00
Author
Owner

Scope update

This hardening should apply to all account-token upstream calls, not only Codex/Responses stream and websocket paths.

Current-code review against HEAD shows the original issue is still valid, with these amendments:

  • Treat every upstream request built with a selected/stored account OAuth token as in scope.
  • Centralize the policy below shared builders where possible: app/core/clients/proxy.py::_build_upstream_headers, app/core/clients/proxy.py::_build_upstream_websocket_headers, app/core/clients/proxy_websocket.py::_build_upstream_websocket_headers, and app/core/clients/proxy.py::_build_upstream_transcribe_headers.
  • The policy should cover current account-token consumers including Responses HTTP/SSE, Responses websocket, HTTP bridge upstream websocket sessions, compact, thread-goal, Codex control, transcription, and any future account-token upstream helpers.
  • Do not forward downstream originator, user-agent, x-openai-client-*, or other product/client identity fields unless explicitly normalized to the account-token persona.
  • Stamp originator from the account minted persona when persisted; otherwise use configured settings.oauth_originator with the existing default codex_chatgpt_desktop.
  • Preserve only semantic/protocol headers needed for the request, such as websocket beta, safe tracing IDs, and continuity headers like session_id or required x-codex-* turn-state fields.
  • Tests that currently expect native header preservation should be updated. Add regression coverage for the non-Responses account-token paths too if those paths can receive downstream identity headers.

Current notable references:

  • app/core/clients/proxy.py::_build_upstream_headers() also feeds compact, thread-goal, and Codex control.
  • app/core/clients/proxy.py::_build_upstream_transcribe_headers() currently forwards user-agent, x-openai-*, and x-codex-*, so it needs explicit review under the all account-token policy.
  • app/db/models.py::Account still has no persisted originator field.

This supersedes the narrower wording in the original issue where it only names Codex/Responses paths.

**Scope update** This hardening should apply to **all account-token upstream calls**, not only Codex/Responses stream and websocket paths. Current-code review against `HEAD` shows the original issue is still valid, with these amendments: - Treat every upstream request built with a selected/stored account OAuth token as in scope. - Centralize the policy below shared builders where possible: `app/core/clients/proxy.py::_build_upstream_headers`, `app/core/clients/proxy.py::_build_upstream_websocket_headers`, `app/core/clients/proxy_websocket.py::_build_upstream_websocket_headers`, and `app/core/clients/proxy.py::_build_upstream_transcribe_headers`. - The policy should cover current account-token consumers including Responses HTTP/SSE, Responses websocket, HTTP bridge upstream websocket sessions, compact, thread-goal, Codex control, transcription, and any future account-token upstream helpers. - Do not forward downstream `originator`, `user-agent`, `x-openai-client-*`, or other product/client identity fields unless explicitly normalized to the account-token persona. - Stamp `originator` from the account minted persona when persisted; otherwise use configured `settings.oauth_originator` with the existing default `codex_chatgpt_desktop`. - Preserve only semantic/protocol headers needed for the request, such as websocket beta, safe tracing IDs, and continuity headers like `session_id` or required `x-codex-*` turn-state fields. - Tests that currently expect native header preservation should be updated. Add regression coverage for the non-Responses account-token paths too if those paths can receive downstream identity headers. Current notable references: - `app/core/clients/proxy.py::_build_upstream_headers()` also feeds compact, thread-goal, and Codex control. - `app/core/clients/proxy.py::_build_upstream_transcribe_headers()` currently forwards `user-agent`, `x-openai-*`, and `x-codex-*`, so it needs explicit review under the all account-token policy. - `app/db/models.py::Account` still has no persisted originator field. This supersedes the narrower wording in the original issue where it only names Codex/Responses paths.
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
mika/codex-lb#1
No description provided.