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 — the same remedy heid's exit-3 path already names. Run it when a broken CLI
login is a two-minute annoyance, not mid-panel. Hence the required
--i-accept-this-may-end-the-cli-session flag.
Current state
Seeded on nh3-dev 2026-09-16. Scope openid profile email offline_access grok-cli:access api:access, client b1a00492-…. Probe NOT yet run — it is the operator's call when to
spend it.
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, onseed. - 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.