Files
esh-pfi-infrastructure/services/grok-token-broker

grok-token-broker

Holds a refreshable xAI session credential so a gateway can serve the Grok Build coding plan — without touching the credential the operator's grok CLI depends on.

seed            copy the CLI credential into the broker's own store (read-once)
probe-rotation  MEASURE whether refresh rotates AND invalidates  <- gate
token --raw     emit a valid access token, refreshing if near expiry
refresh         refresh now
status          report state, touching nothing

The problem

The coding plan (https://cli-chat-proxy.grok.com/v1, serving grok-4.6, 500k context) authenticates with an OIDC session token — api_key: null, env_key: null in the CLI's own model cache — that expires roughly every six hours. LiteLLM and every other gateway here hold a static credential, so a naive alias works until the session lapses and then fails closed.

⛔ Why the loop is disarmed until measured

https://auth.x.ai/oauth2/token supports the refresh grant. But a refresh may rotate the refresh token, and many providers invalidate the old one server-side the moment a new one issues. The grok CLI holds its refresh token in ~/.grok/auth.json.

Not writing to that file is necessary and not sufficient. If xAI rotates-and-invalidates, a broker refreshing the same credential kills the CLI login anyway, server-side. heid's groa_http_dispatch.py refuses to refresh at all for exactly this reason — correct, absent an answer. This broker exists to get the answer, then act on it.

So probe-rotation is a gate, not a diagnostic: refresh and token-near-expiry both refuse until a verdict exists and says safe.

verdict meaning
non-rotating same refresh token returned. Coexistence safe. Armed.
rotating-old-still-valid rotation happens, old token still works. Coexistence safe. Armed.
rotating-and-invalidating the CLI login is already dead. Broker must not share this credential — it needs its own login. Stays disarmed.

⚠ The probe spends one refresh, and there is no way to ask the question without spending it. If the answer is the bad one, the CLI is broken at that moment and needs an interactive grok re-login. Hence the required --i-accept-this-may-end-the-cli-session flag.

⚠⚠ AND THE BLAST RADIUS IS BOTH GRÓA TRANSPORTS, NOT ONE — verified, 2026-09-16. An earlier draft of this file implied only the HTTP path was exposed. Wrong: heid/scripts/groa_dispatch.py invokes the grok CLI directly (argv = ["grok", "-p", prompt, ...]), and the CLI authenticates from the same ~/.grok/auth.json. The bwrap in the process table is the CLI's own Landlock sandbox, not something Heid wraps around it. So the read-jail transport and the direct-endpoint transport share one session: one reaches it through the CLI, the other reads the file the CLI wrote. An invalidating probe takes Gróa down on every transport until a human re-logs in — not "the HTTP path degrades". Caught by heid, confirmed here against their source.

Current state — ⛔ SHELVED, deliberately, 2026-09-16

Seeded, committed, disarmed, and with no consumer. Do NOT arm probe-rotation. This is a finished resting place, not a half-built tool — the gate is working exactly as designed and the thing it was gating for went away.

Operator ruling the same day, relayed by heid: "keep the jail stop the a/b." Heid dispatches Gróa through the read jail; groa_http_dispatch.py is kept as a documented fallback with no scheduled use. Nothing in the fleet is asking for a renewable xAI session.

So the trade inverted while the tool was being built:

the RISK did not shrink -- an invalidating probe still reaches BOTH Gróa transports through the shared ~/.grok/auth.json session, taking the arm down until an interactive re-login the PAYOFF went to zero -- it buys token renewal on a transport nobody dispatches

⭐ If something later needs a renewable xAI session, the argument reopens on its own merits and this is sitting here ready. That is the whole reason it was left seeded rather than torn out. Re-read the probe warning below before arming it; none of that risk expired.

Scope openid profile email offline_access grok-cli:access api:access, client b1a00492-…. Probe verdict: UNMEASURED, and correctly so.

⚠ One question this never answered, and it is a billing one: the jail reaches the coding plan already paid for; the HTTP path reaches the metered API, whose responses carry a cost_in_usd_ticks field. Whether that bills on top of the plan was never measured and was not part of the ruling. It is one look at the xAI billing console, which this fleet holds no credential for.

For a consumer

TOKEN=$(broker.py token --raw)
curl https://cli-chat-proxy.grok.com/v1/... -H "Authorization: Bearer $TOKEN"

⚠ The coding plan speaks the Responses API (api_backend: "responses"), not /chat/completions. That is a second, independent obstacle to a LiteLLM alias and this broker does not solve it — it solves the credential half only.

Invariants

  • Never writes ~/.grok/auth.json. Reads it once, on seed.
  • Never prints a token except under token --raw.
  • Never logs a token value — expiry, scope and subject only.
  • Store is ~/.config/grok-token-broker/ at 0700, credential at 0600.