Skip to content

Commit 26b10e0

Browse files
chaodu-agent超渡法師thepagent
authored
feat: add xai-proxy — OAuth PKCE sidecar for xAI SuperGrok (#841)
* feat: add xai-proxy — OAuth PKCE sidecar for xAI SuperGrok Lightweight Rust binary that authenticates with xAI via browser OAuth (PKCE, borrowing Grok CLI's public client ID) and proxies OpenAI-compatible requests to api.x.ai/v1 with the subscription token injected. Allows any OpenAI-compatible coding agent (Claude Code, OpenCode, Codex CLI, etc.) to use SuperGrok subscription quota instead of per-token API credits. * ci: add workflow for xai-proxy (check, clippy, test, build) * feat(xai-proxy): add device-code login for headless environments Adds `login-device` subcommand that uses RFC 8628 device authorization grant. Works in K8s exec, ECS exec, SSH — no browser or port-forward needed. Usage: xai-proxy login-device # prints verification URL + user code # poll until user approves on any device * fix(xai-proxy): install rustls crypto provider + add XAI_PROXY_TOKEN_PATH env var - Add rustls as direct dep and call install_default() in main() to fix 'Could not automatically determine CryptoProvider' panic on serve - Support XAI_PROXY_TOKEN_PATH env var for custom token file location (useful for K8s PVC persistence at e.g. /home/agent/.openab/xai-proxy/tokens.json) - Add Dockerfile for container builds * docs(xai-proxy): update README with architecture diagram, docker, k8s sidecar * docs: add xai-proxy deployment guide --------- Co-authored-by: 超渡法師 <[email protected]> Co-authored-by: chaodu-agent <[email protected]> Co-authored-by: thepagent <[email protected]>
1 parent ffcc8e8 commit 26b10e0

8 files changed

Lines changed: 3299 additions & 0 deletions

File tree

.github/workflows/ci-xai-proxy.yml

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
name: CI (xai-proxy)
2+
3+
on:
4+
pull_request:
5+
paths:
6+
- "xai-proxy/**"
7+
push:
8+
branches: [main]
9+
paths:
10+
- "xai-proxy/**"
11+
12+
env:
13+
CARGO_TERM_COLOR: always
14+
15+
jobs:
16+
check:
17+
runs-on: ubuntu-latest
18+
defaults:
19+
run:
20+
working-directory: xai-proxy
21+
steps:
22+
- uses: actions/checkout@v6
23+
24+
- uses: dtolnay/rust-toolchain@stable
25+
with:
26+
components: clippy
27+
28+
- uses: Swatinem/rust-cache@v2
29+
with:
30+
workspaces: xai-proxy
31+
32+
- name: cargo check
33+
run: cargo check
34+
35+
- name: cargo clippy
36+
run: cargo clippy -- -D warnings
37+
38+
- name: cargo test
39+
run: cargo test
40+
41+
- name: cargo build (release)
42+
run: cargo build --release

docs/xai-proxy.md

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
# xAI Proxy (SuperGrok Sidecar)
2+
3+
xai-proxy is a lightweight Rust sidecar that lets any OpenAI-compatible agent use your **SuperGrok subscription** instead of per-token API credits. It authenticates via OAuth and proxies requests to `api.x.ai/v1`.
4+
5+
## Architecture
6+
7+
```
8+
┌─ Kubernetes Pod ──────────────────────────────────────────────┐
9+
│ │
10+
│ openab → opencode acp │
11+
│ │ POST /v1/chat/completions │
12+
│ ▼ │
13+
│ xai-proxy :9090 │
14+
│ • Injects OAuth Bearer token │
15+
│ • Auto-refreshes 120s before expiry │
16+
│ │ │
17+
│ PVC: /home/agent/.openab/xai-proxy/tokens.json │
18+
└───────────────┼───────────────────────────────────────────────┘
19+
20+
https://api.x.ai/v1 (SuperGrok)
21+
```
22+
23+
## Prerequisites
24+
25+
- Active SuperGrok subscription (any tier)
26+
- A machine with browser access (or SSH tunnel) for initial login
27+
28+
## Helm Install
29+
30+
```bash
31+
# 1. Login locally to get tokens
32+
xai-proxy login-device
33+
34+
# 2. Create K8s secret from token file
35+
kubectl create secret generic xai-proxy-tokens \
36+
--from-file=tokens.json=$HOME/.xai-proxy/tokens.json
37+
38+
# 3. Deploy with opencode + xai-proxy sidecar
39+
helm install openab openab/openab \
40+
--set agents.kiro.enabled=false \
41+
--set agents.mybot.discord.botToken="$DISCORD_BOT_TOKEN" \
42+
--set agents.mybot.discord.allowAllChannels=true \
43+
--set agents.mybot.command=opencode \
44+
--set-json 'agents.mybot.args=["acp"]' \
45+
--set agents.mybot.image=ghcr.io/openabdev/openab-opencode \
46+
--set-json 'agents.mybot.extraVolumes=[{"name":"xai-tokens-src","secret":{"secretName":"xai-proxy-tokens"}},{"name":"opencode-config","configMap":{"name":"opencode-xai-config"}},{"name":"opencode-auth","configMap":{"name":"opencode-xai-auth"}}]' \
47+
--set-json 'agents.mybot.extraVolumeMounts=[{"name":"opencode-config","mountPath":"/home/agent/opencode.json","subPath":"opencode.json"},{"name":"opencode-config","mountPath":"/home/agent/.config/opencode/opencode.json","subPath":"opencode.json"},{"name":"opencode-auth","mountPath":"/home/agent/.local/share/opencode/auth.json","subPath":"auth.json"}]' \
48+
--set-json 'agents.mybot.extraInitContainers=[{"name":"copy-tokens","image":"busybox","command":["sh","-c","if [ ! -f /dest/.openab/xai-proxy/tokens.json ]; then mkdir -p /dest/.openab/xai-proxy && cp /src/tokens.json /dest/.openab/xai-proxy/tokens.json; fi"],"volumeMounts":[{"name":"xai-tokens-src","mountPath":"/src","readOnly":true},{"name":"data","mountPath":"/dest"}]}]' \
49+
--set-json 'agents.mybot.extraContainers=[{"name":"xai-proxy","image":"xai-proxy:latest","args":["serve","--bind","0.0.0.0"],"env":[{"name":"XAI_PROXY_TOKEN_PATH","value":"/home/agent/.openab/xai-proxy/tokens.json"}],"ports":[{"containerPort":9090}],"volumeMounts":[{"name":"data","mountPath":"/home/agent"}]}]'
50+
```
51+
52+
## OpenCode Configuration
53+
54+
Create a ConfigMap for the opencode provider config:
55+
56+
```bash
57+
kubectl create configmap opencode-xai-config --from-file=opencode.json=- <<'EOF'
58+
{
59+
"$schema": "https://opencode.ai/config.json",
60+
"model": "xai/grok-4.3",
61+
"provider": {
62+
"xai": {
63+
"npm": "@ai-sdk/openai-compatible",
64+
"name": "xAI (SuperGrok)",
65+
"options": {
66+
"baseURL": "http://localhost:9090/v1",
67+
"apiKey": "dummy"
68+
},
69+
"models": {
70+
"grok-4.3": { "name": "Grok 4.3" }
71+
}
72+
}
73+
}
74+
}
75+
EOF
76+
77+
kubectl create configmap opencode-xai-auth --from-file=auth.json=- <<'EOF'
78+
{ "xai": "dummy" }
79+
EOF
80+
```
81+
82+
## Authentication
83+
84+
### Device-code flow (recommended for headless)
85+
86+
```bash
87+
xai-proxy login-device
88+
```
89+
90+
Prints a URL and code. Open the URL in any browser, enter the code, and authorize.
91+
92+
### Browser OAuth (local machine)
93+
94+
```bash
95+
xai-proxy login
96+
```
97+
98+
Opens your browser to `auth.x.ai`. Sign in and authorize. Callback is received on `127.0.0.1:56121`.
99+
100+
### Token refresh
101+
102+
xai-proxy auto-refreshes the OAuth token 120 seconds before expiry. The refreshed token is written back to the token file (persisted on PVC across pod restarts).
103+
104+
## Environment Variables
105+
106+
| Variable | Default | Description |
107+
|----------|---------|-------------|
108+
| `XAI_PROXY_TOKEN_PATH` | `~/.xai-proxy/tokens.json` | Custom token file path |
109+
| `RUST_LOG` | `xai_proxy=info` | Log level |
110+
111+
## Token Persistence
112+
113+
The init container seeds the token from the K8s secret on first boot only. After that, xai-proxy reads and writes the token directly on the PVC. This means:
114+
115+
- Token refreshes survive pod restarts
116+
- The K8s secret is only needed for initial bootstrap
117+
- To force a token reset, delete `/home/agent/.openab/xai-proxy/tokens.json` from the PVC
118+
119+
## Standalone Usage (no K8s)
120+
121+
```bash
122+
# Build
123+
cargo build --release
124+
125+
# Login
126+
./target/release/xai-proxy login-device
127+
128+
# Serve
129+
./target/release/xai-proxy serve --port 9090
130+
131+
# Use with any OpenAI-compatible client
132+
export OPENAI_BASE_URL=http://127.0.0.1:9090/v1
133+
export OPENAI_API_KEY=dummy
134+
opencode
135+
```
136+
137+
## Limitations
138+
139+
- **codex-acp** and **claude-agent-acp** require their own proprietary auth and won't use `OPENAI_BASE_URL` — use opencode or hermes-acp instead
140+
- Browser OAuth (`xai-proxy login`) requires Cloudflare to not block your IP — use `login-device` if blocked

xai-proxy/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/target

0 commit comments

Comments
 (0)