solo enterprise agentgateway · entra jwt · openai responses api
Dedicated gateway for OpenAI Codex (CLI + ChatGPT desktop Codex). Strict JWT on the data plane against Microsoft Entra ID, then OpenAI via the Vault-synced API key. Codex talks the Responses API and allows plain HTTP off loopback — LAN NodePort is the primary lab path; the loopback proxy is optional.
Codex authenticates with an Entra ID token as Bearer
(CODEX_AGW_TOKEN), the gateway validates it Strict against Entra JWKS,
then injects the OpenAI key upstream. Model is not pinned —
Codex sends it per request.
ID token → optional loopback proxy → codex-gateway JWT Strict → openai-codex → api.openai.com /v1/responses
Path is / on this dedicated Gateway so Codex can call
{base}/v1/responses. The kagent OpenAI path stays on
agentgateway-proxy at :30160/openai
(gpt-5.5 pinned — do not steal it). Sibling Claude Desktop path:
claude-desktop.html.
Bare-metal NodePort on worker 172.16.10.155. Codex
allows plain HTTP off loopback, so the LAN URL is valid.
Live Service ports: HTTP 80:31928/TCP, HTTPS 443:31540/TCP.
Codex base URL: http://172.16.10.155:31928/v1
(the /v1 suffix is required). Optional loopback:
python3
scripts/codex-lab-proxy.py
(default CODEX_GW_NODEPORT=31928)
→ http://127.0.0.1:18790/v1.
| Thing | Value |
|---|---|
| Service | codex-gateway · ns agentgateway-system |
| Codex base URL (LAN) | http://172.16.10.155:31928/v1 |
| Codex base URL (loopback, optional) | http://127.0.0.1:18790/v1 · via scripts/codex-lab-proxy.py |
| Responses path | {base}/responses → …/v1/responses |
| HTTP NodePort | 172.16.10.155:31928 · Service 80:31928/TCP |
| HTTPS NodePort (optional / advanced) | https://172.16.10.155:31540/v1 · TLS Terminate · Secret solo-ui-tls |
If ports ever change, re-confirm with
kubectl --context maniak-goose -n agentgateway-system get svc codex-gateway
(HTTP from port 80, HTTPS from port 443).
Anonymous calls must 401/403; a valid Entra ID token reaches OpenAI.
Codex CLI tested at 0.144.4.
Public client already registered — do not recreate.
| Field | Value |
|---|---|
| App name | agw-codex |
| Tenant ID | 8635e970-2205-4189-bc77-77519ff5064f |
| Client ID | e862e87d-d284-46a8-bf70-30ac7dba351e |
| Issuer | https://login.microsoftonline.com/8635e970-2205-4189-bc77-77519ff5064f/v2.0 |
| Platform | Mobile and desktop applications |
| Redirect URI | http://127.0.0.1/callback |
| Public client flows | Yes (no client secret · isFallbackPublicClient: true) |
base_url must end in /v1.
CODEX_AGW_TOKEN must be an Entra ID token for
agw-codex (gateway JWT aud = client ID). Codex sends it as
Authorization: Bearer. Access tokens fail.
~/.codex/config.tomlmodel_provider = "agentgateway" [model_providers.agentgateway] name = "OpenAI via agentgateway" base_url = "http://172.16.10.155:31928/v1" wire_api = "responses" env_key = "CODEX_AGW_TOKEN"
Loopback variant: base_url = "http://127.0.0.1:18790/v1".
From Solo Codex docs
plus env_key:
export AGENTGATEWAY_BASE_URL="http://172.16.10.155:31928/v1"
export CODEX_AGW_TOKEN='<entra id_token>'
codex -c 'model_provider="agentgateway"' \
-c 'model_providers.agentgateway.name="OpenAI via agentgateway"' \
-c "model_providers.agentgateway.base_url=\"${AGENTGATEWAY_BASE_URL}\"" \
-c 'model_providers.agentgateway.wire_api="responses"' \
-c 'model_providers.agentgateway.env_key="CODEX_AGW_TOKEN"' \
"Hello"
Device-code against the public client (no secret). Copy id_token,
not access_token.
TENANT=8635e970-2205-4189-bc77-77519ff5064f
CLIENT=e862e87d-d284-46a8-bf70-30ac7dba351e
curl -sS -X POST "https://login.microsoftonline.com/${TENANT}/oauth2/v2.0/devicecode" \
-d "client_id=${CLIENT}" \
-d "scope=openid profile email offline_access"
# complete user_code in a browser, then:
curl -sS -X POST "https://login.microsoftonline.com/${TENANT}/oauth2/v2.0/token" \
-d "grant_type=urn:ietf:params:oauth:grant-type:device_code" \
-d "client_id=${CLIENT}" \
-d "device_code=${DEVICE_CODE}"
Same provider block. Back up and replace the user-level config, then restart
the app — or Settings → Configuration → Open config.toml.
Export CODEX_AGW_TOKEN in the environment that launches the app.
{
"model_provider": "agentgateway",
"model_providers": {
"agentgateway": {
"name": "OpenAI via agentgateway",
"base_url": "http://172.16.10.155:31928/v1",
"wire_api": "responses",
"env_key": "CODEX_AGW_TOKEN"
}
}
}
JSON-ish equivalent of the TOML (the app file is still TOML). Profile form:
codex --profile agentgateway with
~/.codex/agentgateway.config.toml.
Codex probes /v1/models. Until
agentgateway#1462
adds a gateway-generated list, Codex may warn that model metadata is not found.
That does not block /v1/responses.
Dedicated Gateway + PathPrefix /. The gpt-5.5
/openai route on agentgateway-proxy is unchanged.
Gateway aud = agw-codex client ID.
Access tokens (az account get-access-token) have a different audience story and will fail Strict JWT.
Codex env_key = CODEX_AGW_TOKEN.
base_url must end in /v1Codex posts {base}/v1/responses. A URL without the suffix 404s.
Do not point Codex at :30160/openai.
/callbackRegister http://127.0.0.1/callback under Mobile and desktop applications.
Public client, no secret. Do not recreate the Entra app.
GitOps under config/ via Argo app agentgateway-config. Push + sync is enough. File paths link to GitHub main.
| Kind | Name | File |
|---|---|---|
| Gateway | codex-gateway | config/gateway/codex-gateway.yaml |
| HTTPRoute | codex | config/routes/codex-route.yaml |
| AgentgatewayBackend | openai-codex | config/backends/openai-codex.yaml |
| AgentgatewayPolicy | codex-jwt-auth | config/policies/codex-jwt-auth.yaml |
| tracing targetRef | Gateway codex-gateway | config/policies/tracing.yaml |
| Lab proxy script | — | scripts/codex-lab-proxy.py |
| Markdown deep-dive | — | docs/codex-gateway.md |
AgentgatewayBackend/entra-jwks ·
Secret/openai-secret (+ ExternalSecret → Vault) ·
Secret/solo-ui-tls (HTTPS terminate; same ns as Gateway) ·
EnterpriseAgentgatewayParameters/agentgateway-proxy-params (cost catalog) ·
Entra app agw-codex
Lab page, repo, proxy script, GitOps manifests, and official Codex docs.
scripts/codex-lab-proxy.pydocs/codex-gateway.mdconfig/gateway/codex-gateway.yamlconfig/routes/codex-route.yamlconfig/backends/openai-codex.yamlconfig/policies/codex-jwt-auth.yaml