Tawa docs

Codex

Protect a Codex CLI session without changing any code — point it at a local relay instead of the OpenAI Responses API.

tawa-codex-proxy is the Codex CLI counterpart to the Claude Code relay: a standalone process that sits between Codex CLI and OpenAI, so you get scanning without editing Codex itself or writing any integration code.

Built, not yet proven against a real Codex CLI session

This relay is implemented and tested against the documented Responses API wire protocol (cross-checked against a third-party Codex proxy's own reverse-engineered event types), the same way the Claude Code relay was before it was verified — but it hasn't yet been run end-to-end against a real codex binary the way that one has. Treat the design as high-confidence, not proven. This page will be updated once that verification pass happens.

Codex CLI reads OPENAI_BASE_URL once at startup and routes every request through whatever's listening there. Point that at tawa-codex-proxy instead of api.openai.com directly, and every request is scanned before it goes anywhere near OpenAI:

                  ┌─▶ your Tawa account's hosted
                  │   detection (request scan)
                  │
Codex CLI ──OPENAI_BASE_URL──▶ tawa-codex-proxy ──▶ api.openai.com
                  │                                       │
                  │              (streamed response)  ◀───┘
                  └─▶ your Tawa account's hosted
                      detection (action-call scan + model's own
                      reply, before either reaches Codex CLI)

What gets scanned

Both directions of every POST /v1/responses are inspected.

Inbound, before it reaches OpenAI: the relay looks at the newest content in the input array — Codex CLI resends the whole array every turn (it doesn't use previous_response_id), but only appends new items since its last request, so scanning just those keeps cost flat instead of growing with the conversation.

  • If it's a fresh human turn, it's scanned as user_prompt.
  • If it's one or more function_call_output items being fed back into context, they're scanned as tool_output. Codex can request several tool calls in parallel, so this is a trailing run of new output items, not just the last one — the generalization of the same rule the Claude Code relay uses for a single tool result.

Outbound, before Codex CLI (or you) can act on it: two kinds of output item are buffered and scanned — everything else (reasoning items, response-level lifecycle events) streams through live, unbuffered.

  • An action item — a function_call (the confirmed, common shape: MCP tools and Codex's own shell/patch actions both go through this today) or any other non-text item type — is scanned before Codex CLI can act on it. Rather than hardcode a parser per item type, every item type other than message and reasoning is gated the same generic way: buffered between its response.output_item.added and response.output_item.done events (every item type has both, regardless of its own delta shape) and scanned from whatever the final item JSON contains. This is deliberately conservative — a future action-shaped item type this proxy has never seen still gets gated, not silently forwarded.
  • The model's own visible reply — any message item — is scanned too, on by default, in the same streaming checkpoints as the Claude Code relay uses for its text output.

Any direction: a BLOCK, HARD_BLOCK, or SANDBOX verdict stops it — a plain OpenAI-shaped 400 error on the request side, or a synthetic mid-stream error SSE event on the response side (the Responses API's own documented, in-band mechanism for a stream that already returned 200 OK to fail partway through) that ends the stream before the gated item ever reaches the client. Every other verdict forwards through by default, same policy as the Claude Code relay. A REVIEW the gateway has already collapsed to BLOCK for the app's content-type setting is a block here. See Configuration for how that's decided.

Scope today

What's covered:

  • Every user prompt and every tool output read back into context, including parallel tool-call turns.
  • Every action item (function_call today; any future item type generically), scanned before Codex CLI can act on it.
  • The model's own visible reply, scanned before it reaches Codex CLI (on by default, toggleable).

What isn't built yet:

  • Not yet run against a real Codex CLI session. See the callout above — this is the single most important gap before relying on this for anything real.
  • No session correlation. The Claude Code relay forwards Claude Code's own session-id header through to Tawa so related scans can be correlated in Tawa's event stream/audit trail. There's no confirmed equivalent header for Codex CLI yet, so this relay doesn't attempt one rather than guess at a name that might not exist.
  • Per-user identifiers and Policy Studio directives aren't wired in — same gap as the Claude Code relay.

Get started

On this page