solo enterprise agentgateway · entra jwt · xai openai dialect
Dedicated gateway for Grok Build (xAI CLI / BYOK custom models). Strict JWT on the data plane against Microsoft Entra ID, then xAI via the Vault-synced API key. Grok Build talks the Responses API (or chat completions) and allows plain HTTP off loopback — LAN NodePort is the primary lab path; the loopback proxy is optional.
Grok Build authenticates with an Entra ID token as Bearer
(GROK_AGW_TOKEN), the gateway validates it Strict against Entra JWKS,
then injects the xAI key upstream. Model is not pinned —
Grok Build sends it per request.
ID token → optional loopback proxy → grok-build-gateway JWT Strict → xai-grok-build → api.x.ai /v1/responses
Path is / on this dedicated Gateway so Grok Build can call
{base}/v1/responses. The kagent xAI path stays on
xai-grok-gateway at :31397/grok
(grok-4.5 pinned — do not steal it). Sibling Codex path:
codex.html.
Bare-metal NodePort on worker 172.16.10.155. Grok Build
allows plain HTTP off loopback, so the LAN URL is valid.
Live Service ports: HTTP 80:31918/TCP, HTTPS 443:31987/TCP.
Grok Build base URL: http://172.16.10.155:31918/v1
(the /v1 suffix is required). Optional loopback:
python3
scripts/grok-build-lab-proxy.py
(default GROK_BUILD_GW_NODEPORT=31918)
→ http://127.0.0.1:18791/v1.
| Thing | Value |
|---|---|
| Service | grok-build-gateway · ns agentgateway-system |
| Grok Build base URL (LAN) | http://172.16.10.155:31918/v1 |
| Grok Build base URL (loopback, optional) | http://127.0.0.1:18791/v1 · via scripts/grok-build-lab-proxy.py |
| Responses path | {base}/responses → …/v1/responses |
| Models path | {base}/models → …/v1/models · set GROK_MODELS_BASE_URL to the same base_url |
| HTTP NodePort | 172.16.10.155:31918 · Service 80:31918/TCP |
| HTTPS NodePort (optional / advanced) | https://172.16.10.155:31987/v1 · TLS Terminate · Secret solo-ui-tls |
If ports ever change, re-confirm with
kubectl --context maniak-goose -n agentgateway-system get svc grok-build-gateway
(HTTP from port 80, HTTPS from port 443).
Anonymous calls must 401/403; a valid Entra ID token reaches xAI.
Public client already registered — do not recreate.
| Field | Value |
|---|---|
| App name | agw-grok-build |
| Tenant ID | 8635e970-2205-4189-bc77-77519ff5064f |
| Client ID | 36fb017c-5b4b-4c26-9bd7-7af25793178b |
| 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.
GROK_AGW_TOKEN must be an Entra ID token for
agw-grok-build (gateway JWT aud = client ID). Grok Build sends it as
Authorization: Bearer. Access tokens fail.
~/.grok/config.toml[model.agw] model = "grok-4.5" base_url = "http://172.16.10.155:31918/v1" name = "Grok via agentgateway" env_key = "GROK_AGW_TOKEN" api_backend = "responses" supports_backend_search = false [models] default = "agw"
Loopback variant: base_url = "http://127.0.0.1:18791/v1".
supports_backend_search must be false on this BYOK
gateway — xAI server-side search tools will not exist through AgentGateway.
Optional: export GROK_MODELS_BASE_URL="http://172.16.10.155:31918/v1"
so the client lists models from {base}/models (this gateway maps
/v1/models → Models).
api_backend may be responses (recommended),
chat_completions (/v1/chat/completions), or
messages (Passthrough). Prefer responses — same wire as Codex.
Device-code against the public client (no secret). Copy id_token,
not access_token.
TENANT=8635e970-2205-4189-bc77-77519ff5064f
CLIENT=36fb017c-5b4b-4c26-9bd7-7af25793178b
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}"
Backend maps /v1/models → Models. Set
GROK_MODELS_BASE_URL to the same base_url if Grok Build
lists models from {base}/models.
Dedicated Gateway + PathPrefix /. The grok-4.5
/grok route on xai-grok-gateway (:31397) is unchanged.
Gateway aud = agw-grok-build client ID.
Access tokens (az account get-access-token) have a different audience story and will fail Strict JWT.
Grok Build env_key = GROK_AGW_TOKEN.
base_url must end in /v1Grok Build posts {base}/v1/responses. A URL without the suffix 404s.
Do not point Grok Build at :31397/grok.
supports_backend_search = falsexAI server-side search tools will not exist through this BYOK AgentGateway path.
Leave supports_backend_search = false on [model.agw].
/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 | grok-build-gateway | config/gateway/grok-build-gateway.yaml |
| HTTPRoute | grok-build | config/routes/grok-build-route.yaml |
| AgentgatewayBackend | xai-grok-build | config/backends/xai-grok-build.yaml |
| AgentgatewayPolicy | grok-build-jwt-auth | config/policies/grok-build-jwt-auth.yaml |
| tracing targetRef | Gateway grok-build-gateway | config/policies/tracing.yaml |
| Lab proxy script | — | scripts/grok-build-lab-proxy.py |
| Markdown deep-dive | — | docs/grok-build-gateway.md |
AgentgatewayBackend/entra-jwks ·
Secret/xai-secret (+ ExternalSecret → Vault) ·
Secret/solo-ui-tls (HTTPS terminate; same ns as Gateway) ·
EnterpriseAgentgatewayParameters/agentgateway-proxy-params (cost catalog) ·
Entra app agw-grok-build
Live GitOps YAML for this dedicated gateway (Argo app
agentgateway-config). Copy a resource below, or open the GitHub
blob. There is no first-party Grok Build client page — start at the
LLM clients
index, then the
xAI provider
and Solo
openai-compatible (xAI)
host/path table. xAI speaks the
OpenAI dialect.
Does not steal /grok on xai-grok-gateway
(:31397).
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: grok-build-gateway
namespace: agentgateway-system
spec:
gatewayClassName: enterprise-agentgateway
# Load the shared model cost catalog so this gateway's LLM traffic is priced
# in USD for Cost Management. Without this, spans on this gateway compute
# CostUsd=0 regardless of catalog contents (only gateways with a parametersRef
# price requests). Same params object as agentgateway-proxy / codex.
infrastructure:
parametersRef:
group: enterpriseagentgateway.solo.io
kind: EnterpriseAgentgatewayParameters
name: agentgateway-proxy-params
listeners:
- name: http
protocol: HTTP
port: 80
allowedRoutes:
namespaces:
from: All
- name: https
protocol: HTTPS
port: 443
tls:
mode: Terminate
certificateRefs:
- name: solo-ui-tls
kind: Secret
allowedRoutes:
namespaces:
from: All
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: grok-build
namespace: agentgateway-system
spec:
parentRefs:
- name: grok-build-gateway
namespace: agentgateway-system
rules:
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- name: xai-grok-build
namespace: agentgateway-system
group: agentgateway.dev
kind: AgentgatewayBackend
apiVersion: agentgateway.dev/v1alpha1
kind: AgentgatewayBackend
metadata:
name: xai-grok-build
namespace: agentgateway-system
labels:
app.kubernetes.io/component: grok-build
app.kubernetes.io/part-of: entra-id
spec:
ai:
provider:
openai: {}
host: api.x.ai
port: 443
pathPrefix: /v1
policies:
ai:
routes:
"/v1/responses": Responses
"/v1/chat/completions": Completions
"/v1/models": Models
"*": Passthrough
auth:
secretRef:
name: xai-secret
tls:
sni: api.x.ai
apiVersion: agentgateway.dev/v1alpha1
kind: AgentgatewayPolicy
metadata:
name: grok-build-jwt-auth
namespace: agentgateway-system
labels:
app.kubernetes.io/component: grok-build
app.kubernetes.io/part-of: entra-id
spec:
targetRefs:
- group: gateway.networking.k8s.io
kind: HTTPRoute
name: grok-build
traffic:
jwtAuthentication:
mode: Strict
providers:
- issuer: https://login.microsoftonline.com/8635e970-2205-4189-bc77-77519ff5064f/v2.0
audiences:
- "36fb017c-5b4b-4c26-9bd7-7af25793178b"
jwks:
remote:
backendRef:
name: entra-jwks
kind: AgentgatewayBackend
group: agentgateway.dev
port: 443
jwksPath: /8635e970-2205-4189-bc77-77519ff5064f/discovery/v2.0/keys
cacheDuration: 5m
Copyable AgentGateway GitOps YAML, official AgentGateway docs (LLM clients index, xAI provider, Solo openai-compatible + OpenAI dialect), lab page, repo, proxy script, and Grok Build / xAI docs.
scripts/grok-build-lab-proxy.pydocs/grok-build-gateway.mdconfig/gateway/grok-build-gateway.yamlconfig/routes/grok-build-route.yamlconfig/backends/xai-grok-build.yamlconfig/policies/grok-build-jwt-auth.yaml