Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
103 changes: 103 additions & 0 deletions docs/refarch/sidecar-proxy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
# Reference Architecture: OAuth Sidecar Proxy

> **Note:** For xAI/Grok models, OpenCode ≥1.15.0 supports native xAI OAuth
> (browser + device-code). The sidecar proxy is no longer required for OpenCode
> deployments. See [docs/xai-proxy.md](../xai-proxy.md) for the recommended
> approach.

This document describes the **sidecar proxy pattern** used by `xai-proxy` to
provide OAuth-authenticated access to xAI's SuperGrok API for any
OpenAI-compatible agent.

## When to use this pattern

- Running agents that **don't** have built-in xAI OAuth (e.g. Hermes, custom agents)
- Centralizing token management across multiple containers in a pod
- Needing a single OAuth session shared by several processes

## Architecture

```
┌─ Kubernetes Pod ──────────────────────────────────────────────┐
│ │
│ agent container (any OpenAI-compatible client) │
│ │ POST /v1/chat/completions │
│ │ (no auth header needed) │
│ ▼ │
│ xai-proxy :9090 │
│ • Reads OAuth token from PVC │
│ • Injects Authorization: Bearer header │
│ • Auto-refreshes 120s before expiry │
│ │ │
│ PVC: /home/agent/.openab/xai-proxy/tokens.json │
└───────────────┼───────────────────────────────────────────────┘
https://api.x.ai/v1 (SuperGrok)
```

## How it works

1. **One-time login** — Run `xai-proxy login-device` on any machine with a
browser. This performs OAuth PKCE device-code flow against xAI and writes
tokens to `~/.xai-proxy/tokens.json`.

2. **Token seeding** — An init container copies the token from a K8s Secret
into the PVC on first boot.

3. **Proxy** — `xai-proxy serve` listens on `:9090`, reads the token file,
injects the Bearer header into every upstream request to `api.x.ai/v1`.

4. **Auto-refresh** — The proxy refreshes the OAuth token 120s before expiry
and writes it back to the PVC (survives pod restarts).

## Helm deployment

```bash
# 1. Login locally
xai-proxy login-device

# 2. Create K8s secret
kubectl create secret generic xai-proxy-tokens \
--from-file=tokens.json=$HOME/.xai-proxy/tokens.json

# 3. Deploy with sidecar
helm install openab openab/openab \
--set agents.mybot.command=opencode \
--set-json 'agents.mybot.args=["acp"]' \
--set agents.mybot.image=ghcr.io/openabdev/openab-opencode \
--set-json 'agents.mybot.extraContainers=[{"name":"xai-proxy","image":"ghcr.io/openabdev/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"}]}]' \
--set-json 'agents.mybot.extraInitContainers=[{"name":"copy-tokens","image":"busybox","command":["sh","-c","mkdir -p /dest/.openab/xai-proxy && cp /src/tokens.json /dest/.openab/xai-proxy/tokens.json"],"volumeMounts":[{"name":"xai-tokens-src","mountPath":"/src","readOnly":true},{"name":"data","mountPath":"/dest"}]}]' \
--set-json 'agents.mybot.extraVolumes=[{"name":"xai-tokens-src","secret":{"secretName":"xai-proxy-tokens"}}]'
```

The agent's `opencode.json` points to the local proxy:

```json
{
"provider": {
"xai": {
"npm": "@ai-sdk/openai-compatible",
"options": { "baseURL": "http://localhost:9090/v1", "apiKey": "dummy" },
"models": { "grok-4.3": { "name": "Grok 4.3" } }
}
}
}
```

## Environment variables

| Variable | Default | Description |
|----------|---------|-------------|
| `XAI_PROXY_TOKEN_PATH` | `~/.xai-proxy/tokens.json` | Token file location |
| `RUST_LOG` | `xai_proxy=info` | Log verbosity |

## Limitations

- Only useful for agents without native xAI OAuth support
- Browser OAuth (`xai-proxy login`) may be blocked by Cloudflare — prefer `login-device`
- `codex-acp` and `claude-agent-acp` use proprietary auth and cannot use this proxy

## See also

- [xai-proxy source](../../xai-proxy/) — Rust implementation
- [docs/xai-proxy.md](../xai-proxy.md) — Quick-start guide (now recommends native OAuth)
147 changes: 42 additions & 105 deletions docs/xai-proxy.md
Original file line number Diff line number Diff line change
@@ -1,140 +1,77 @@
# xAI Proxy (SuperGrok Sidecar)
# xAI / SuperGrok Integration

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`.
## Recommended: Native xAI OAuth (OpenCode ≥1.15.0)

## Architecture
OpenCode now has **built-in xAI OAuth support** — no sidecar proxy needed.

```
┌─ Kubernetes Pod ──────────────────────────────────────────────┐
│ │
│ openab → opencode acp │
│ │ POST /v1/chat/completions │
│ ▼ │
│ xai-proxy :9090 │
│ • Injects OAuth Bearer token │
│ • Auto-refreshes 120s before expiry │
│ │ │
│ PVC: /home/agent/.openab/xai-proxy/tokens.json │
└───────────────┼───────────────────────────────────────────────┘
https://api.x.ai/v1 (SuperGrok)
```
### Setup

## Prerequisites
1. Use `Dockerfile.opencode` with OpenCode ≥1.15.0 (which includes native xAI OAuth).
2. Run `/connect` inside OpenCode and select **xAI Grok OAuth (Headless / Remote / VPS)**.
3. Approve the device-code on any browser.
4. Select your model with `/models` (e.g. `grok-4.3`).

- Active SuperGrok subscription (any tier)
- A machine with browser access (or SSH tunnel) for initial login
OpenCode handles token storage and auto-refresh internally.

## Helm Install
### Helm deployment (native OAuth)

```bash
# 1. Login locally to get tokens
xai-proxy login-device
# 1. Run opencode interactively once to complete device-code login,
# then copy the auth file:
kubectl cp <pod>:/home/node/.local/share/opencode/auth.json ./auth.json

# 2. Create K8s secret from token file
kubectl create secret generic xai-proxy-tokens \
--from-file=tokens.json=$HOME/.xai-proxy/tokens.json
# 2. Create secret from the auth file
kubectl create secret generic opencode-xai-auth \
--from-file=auth.json=./auth.json

# 3. Deploy with opencode + xai-proxy sidecar
# 3. Deploy — no sidecar needed
helm install openab openab/openab \
--set agents.kiro.enabled=false \
--set agents.mybot.discord.botToken="$DISCORD_BOT_TOKEN" \
--set agents.mybot.discord.allowAllChannels=true \
--set agents.mybot.command=opencode \
--set-json 'agents.mybot.args=["acp"]' \
--set agents.mybot.image=ghcr.io/openabdev/openab-opencode \
--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"}}]' \
--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"}]' \
--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"}]}]' \
--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"}]}]'
--set-json 'agents.mybot.extraVolumes=[{"name":"xai-auth","secret":{"secretName":"opencode-xai-auth"}}]' \
--set-json 'agents.mybot.extraVolumeMounts=[{"name":"xai-auth","mountPath":"/home/node/.local/share/opencode/auth.json","subPath":"auth.json"}]'
```

## OpenCode Configuration

Create a ConfigMap for the opencode provider config:
### opencode.json

```bash
kubectl create configmap opencode-xai-config --from-file=opencode.json=- <<'EOF'
```json
{
"$schema": "https://opencode.ai/config.json",
"model": "xai/grok-4.3",
"provider": {
"xai": {
"npm": "@ai-sdk/openai-compatible",
"name": "xAI (SuperGrok)",
"options": {
"baseURL": "http://localhost:9090/v1",
"apiKey": "dummy"
},
"models": {
"grok-4.3": { "name": "Grok 4.3" }
}
}
}
"model": "xai/grok-4.3"
}
EOF

kubectl create configmap opencode-xai-auth --from-file=auth.json=- <<'EOF'
{ "xai": "dummy" }
EOF
```

## Authentication

### Device-code flow (recommended for headless)

```bash
xai-proxy login-device
```

Prints a URL and code. Open the URL in any browser, enter the code, and authorize.
No custom provider block needed — OpenCode discovers xAI natively when auth is present.

### Browser OAuth (local machine)
---

```bash
xai-proxy login
```

Opens your browser to `auth.x.ai`. Sign in and authorize. Callback is received on `127.0.0.1:56121`.

### Token refresh

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).
## Alternative: xai-proxy sidecar (legacy)

## Environment Variables
For agents **without** native xAI OAuth (Hermes, custom agents), or for
multi-container pods sharing a single OAuth session, the `xai-proxy` sidecar
is still available.

| Variable | Default | Description |
|----------|---------|-------------|
| `XAI_PROXY_TOKEN_PATH` | `~/.xai-proxy/tokens.json` | Custom token file path |
| `RUST_LOG` | `xai_proxy=info` | Log level |
See [docs/refarch/sidecar-proxy.md](refarch/sidecar-proxy.md) for the full
architecture and deployment guide.

## Token Persistence

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:

- Token refreshes survive pod restarts
- The K8s secret is only needed for initial bootstrap
- To force a token reset, delete `/home/agent/.openab/xai-proxy/tokens.json` from the PVC

## Standalone Usage (no K8s)
### Quick start

```bash
# Build
cargo build --release

# Login
./target/release/xai-proxy login-device

# Serve
./target/release/xai-proxy serve --port 9090
xai-proxy login-device # one-time device-code login
xai-proxy serve --port 9090 # start proxy

# Use with any OpenAI-compatible client
# Point any OpenAI-compatible client at the proxy
export OPENAI_BASE_URL=http://127.0.0.1:9090/v1
export OPENAI_API_KEY=dummy
opencode
```

## Limitations
## Comparison

- **codex-acp** and **claude-agent-acp** require their own proprietary auth and won't use `OPENAI_BASE_URL` — use opencode or hermes-acp instead
- Browser OAuth (`xai-proxy login`) requires Cloudflare to not block your IP — use `login-device` if blocked
| | Native OAuth | xai-proxy sidecar |
|---|---|---|
| **Requires** | OpenCode ≥1.15.0 | Any OpenAI-compatible agent |
| **Extra container** | No | Yes |
| **Token management** | Built into OpenCode | Proxy handles refresh |
| **Multi-agent sharing** | Each agent needs own auth | Single proxy serves all |
| **Complexity** | Low | Medium |
Loading