Chat & WebSocket runtime have no TLS-bypass escape hatch — PR #20676 only fixed login (enterprise MITM still blocked)
Summary
PR #20676 (commit 9e90552, 2026-05-02) added SSL_CERT_FILE / CODEX_CA_CERTIFICATE detection to the login flow's reqwest client (codex-client/src/custom_ca.rs::build_reqwest_client_with_env). This partially addressed #6849.
The chat runtime and WebSocket transport are still broken behind any TLS-inspecting proxy whose MITM CA is rejected by rustls (modern signature/key policy):
- WebSocket (
tokio-tungstenitewithrustls-tls-native-rootsfeature) is hardcoded to rustls — it has no env-var / config / flag escape hatch. - HTTPS fallback (chat path) also appears to use rustls without honoring the login-flow CA logic.
Net effect: codex login succeeds; codex chat dies on every prompt with invalid peer certificate: BadSignature and stream disconnected before completion.
Environment
- codex-cli 0.132.0 (also reproduced on 0.130, 0.131, 0.133)
- Windows 11 (x86_64)
- Hospital LAN with two-tier SSL inspection:
- "AI hosts" (chatgpt.com,
*.openai.azure.com,api.anthropic.com, Bedrock, Vertex etc.) → MITM withCN=GlobalTrust Root CA(2002 self-signed, SHA1WithRSAEncryption, RSA-1024) - "Legacy" hosts (api.openai.com, github.com) → MITM with
CN=FG200ETK18915851, O=Fortinet(SHA256, RSA-2048) - Result:
api.openai.comworks in codex (FortiGate SHA256 chain passes rustls).chatgpt.com(ChatGPT auth path) and Azure / Anthropic / etc. all fail.
Reproduction
codex doctor
...
⚠ websocket Responses WebSocket failed; HTTPS fallback may still work
endpoint wss://chatgpt.com/backend-api/<redacted>
handshake transport error network error: invalid peer certificate: BadSignature
In chat:
⚠ Falling back from WebSockets to HTTPS transport.
stream disconnected before completion: invalid peer certificate: BadSignature
■ stream disconnected before completion: error sending request for url (https://chatgpt.com/backend-api/codex/responses)
Evidence — every modern TLS library rejects this same chain
Same cert chain, three different rejection paths (proves no single workaround works):
| TLS stack | Verdict | Reason |
|---|---|---|
| rustls (codex) | ❌ BadSignature | RFC 9155 — SHA1 signatures hard-deprecated |
| schannel (curl.exe on Windows) | ❌ CRYPT_E_NO_REVOCATION_CHECK (0x80092012) | Hospital MITM CA has no reachable CRL/OCSP endpoint |
| OpenSSL 3.x | ❌ verify error num=66 EE certificate key too weak + num=67 CA certificate key too weak | Security level 2 rejects RSA-1024 |
| Node 22 OpenSSL with NODE_TLS_REJECT_UNAUTHORIZED=0 | ✅ Works | Validation bypassed — only path that succeeds |
The Node escape hatch (NODE_TLS_REJECT_UNAUTHORIZED=0) is the only reason claude-code and other Node-based clients work in this environment. As #6849 comment put it bluntly: "I can easily use claude simply with NODE_TLS_REJECT_UNAUTHORIZED=0, whereas with codex I'm simply blocked".
What PR #20676 doesn't cover
The login flow's build_reqwest_client_with_env only wires the custom-CA path for codex-client (auth/login). But:
- The runtime chat client (whatever builds
reqwest::Clientforchatgpt.com/backend-api/codex/responsescalls) does not readSSL_CERT_FILE/CODEX_CA_CERTIFICATEthe same way. With those env vars set per #20676, login succeeds but chat still rejects. - The WebSocket transport uses
tokio-tungstenitewith therustls-tls-native-rootsfeature —Cargo.tomldeclares this with no opt-out and the wss connect path has no config hook for anaccept-invalid-certsflag or alternate TLS backend.
Even if a user sets SSL_CERT_FILE to a bundle containing the corporate MITM root, rustls still rejects because the chain itself uses SHA1 / RSA-1024 / unreachable revocation — adding the CA to the bundle doesn't override those algorithm policies. This is a class of corporate MITM (FortiGate, certain Trend Micro / Palo Alto deployments etc.) where the root cert was generated decades ago and can't be cycled by IT in any short timeframe.
Requested change — provide an explicit escape hatch
At least one of the following so enterprise users aren't locked out:
CODEX_INSECURE_TLS=1env var that maps toreqwest::ClientBuilder::danger_accept_invalid_certs(true)for every codex reqwest client (auth, chat, telemetry) and totokio_tungstenite::Connector::NativeTlsor equivalent for the WebSocket transport. Loud warning on startup when enabled, like Node logs onNODE_TLS_REJECT_UNAUTHORIZED=0.- A
--tls-backend=nativeflag / config that switches reqwest + tungstenite tonative-tls(schannel / Security.framework / OpenSSL) instead of rustls. schannel and OpenSSL legacy mode accept SHA1; native-tls path was sufficient for codex ≤ 0.118 (when the dep tree had this fallback) and there's no policy reason it should be impossible now. - Extend PR #20676's
build_reqwest_client_with_envso the sameSSL_CERT_FILE/CODEX_CA_CERTIFICATEdetection is honored by the runtime chat reqwest client and is allowed to also disablerustls's signature-algorithm policy when the user explicitly opts in (e.g.CODEX_CA_CERTIFICATE_ALLOW_LEGACY=1).
A trust-store escape hatch behind an explicit env var matches what every other major HTTP client offers (NODE_TLS_REJECT_UNAUTHORIZED, curl -k, git -c http.sslVerify=false, pip --trusted-host). The current "no escape hatch" stance effectively locks out healthcare / financial / government users who cannot change their corporate MITM.
Workarounds users currently rely on
- Run codex from outside the corporate network (home WiFi / cellular tether) — useless for daily work
- Tailscale / VPN exit nodes — adds latency + corporate policy concerns
- Use a different agent (Claude Code via Anthropic SDK in Node, OpenCode, aider) — works fine
- Switch to OpenAI Platform API key +
api.openai.cominstead of ChatGPT auth +chatgpt.com— works if the corporate MITM happens to use a stronger cert chain for that host (FortiGate SHA256 in our case), but unreliable in general
Related
- #6849 — login-only version of this issue, fixed by PR #20676 for the auth path
- PR #20676 / commit 9e90552 — partial fix (login only)
codex-rs/codex-client/src/custom_ca.rs::build_reqwest_client_with_env— the existing pattern that should be extended
Happy to provide additional probe data (cert dumps, doctor output, hospital network probe matrix across 18 LLM hosts) if it helps.
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗