Skip to content
Open
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
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
name: CI (xai-proxy)
name: CI (openab-auth-proxy)

on:
pull_request:
paths:
- "xai-proxy/**"
- "openab-auth-proxy/**"
push:
branches: [main]
paths:
- "xai-proxy/**"
- "openab-auth-proxy/**"

env:
CARGO_TERM_COLOR: always
Expand All @@ -17,7 +17,7 @@ jobs:
runs-on: ubuntu-latest
defaults:
run:
working-directory: xai-proxy
working-directory: openab-auth-proxy
steps:
- uses: actions/checkout@v6

Expand All @@ -27,7 +27,7 @@ jobs:

- uses: Swatinem/rust-cache@v2
with:
workspaces: xai-proxy
workspaces: openab-auth-proxy

- name: cargo check
run: cargo check
Expand Down
84 changes: 84 additions & 0 deletions docs/refarch/sidecar-proxy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
# Reference Architecture: OAuth Sidecar Proxy

> **Note:** For xAI/Grok models, OpenCode ≥1.15.0 supports native xAI OAuth.
> 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** implemented by
`openab-auth-proxy` — a generic OAuth proxy that injects Bearer tokens into
upstream API requests.

## When to use this pattern

- Agents **without** built-in OAuth (Hermes, custom agents)
- Centralizing token management across multiple containers in a pod
- Proxying to any OAuth-protected API (not just xAI)

## Architecture

```
┌─ Kubernetes Pod ──────────────────────────────────────────────┐
│ │
│ agent container (any OpenAI-compatible client) │
│ │ POST /v1/chat/completions │
│ │ (no auth header needed) │
│ ▼ │
│ openab-auth-proxy :9090 │
│ • Reads OAuth token from disk │
│ • Injects Authorization: Bearer header │
│ • Auto-refreshes 120s before expiry │
│ │ │
│ Token: ~/.openab-auth-proxy/<provider>/tokens.json │
└───────────────┼───────────────────────────────────────────────┘
upstream API (configured via TOML or xAI default)
```

## Configuration

Without a config file, `openab-auth-proxy` defaults to xAI/SuperGrok.

For other providers, create `auth-proxy.toml`:

```toml
[provider]
name = "my-provider"
discovery_url = "https://auth.example.com/.well-known/openid-configuration"
client_id = "my-client-id"
scopes = "openid offline_access api:access"
upstream_base_url = "https://api.example.com"
redirect_port = 8080
```

## Helm deployment (xAI example)

```bash
# 1. Login locally
openab-auth-proxy login-device

# 2. Create K8s secret
kubectl create secret generic auth-proxy-tokens \
--from-file=tokens.json=$HOME/.openab-auth-proxy/xai/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":"auth-proxy","image":"ghcr.io/openabdev/openab-auth-proxy:latest","args":["serve","--bind","0.0.0.0"],"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-auth-proxy/xai && cp /src/tokens.json /dest/.openab-auth-proxy/xai/tokens.json"],"volumeMounts":[{"name":"tokens-src","mountPath":"/src","readOnly":true},{"name":"data","mountPath":"/dest"}]}]' \
--set-json 'agents.mybot.extraVolumes=[{"name":"tokens-src","secret":{"secretName":"auth-proxy-tokens"}}]'
```

## Environment variables

| Variable | Default | Description |
|----------|---------|-------------|
| `AUTH_PROXY_TOKEN_PATH` | `~/.openab-auth-proxy/<provider>/tokens.json` | Token file location |
| `XAI_PROXY_TOKEN_PATH` | (legacy alias) | Backward-compatible |
| `RUST_LOG` | `openab_auth_proxy=info` | Log verbosity |

## See also

- [openab-auth-proxy source](../../openab-auth-proxy/) — Rust implementation
- [docs/xai-proxy.md](../xai-proxy.md) — xAI-specific quick-start
151 changes: 27 additions & 124 deletions docs/xai-proxy.md
Original file line number Diff line number Diff line change
@@ -1,140 +1,43 @@
# 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)
```
1. Run `/connect` inside OpenCode → select **xAI Grok OAuth (Headless / Remote / VPS)**
2. Approve the device-code on any browser
3. Select your model with `/models` (e.g. `grok-4.3`)

## Prerequisites
OpenCode handles token storage and auto-refresh internally.

- Active SuperGrok subscription (any tier)
- A machine with browser access (or SSH tunnel) for initial login
---

## Helm Install
## Alternative: openab-auth-proxy sidecar

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

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

# 3. Deploy with opencode + xai-proxy sidecar
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"}]}]'
```

## OpenCode Configuration

Create a ConfigMap for the opencode provider config:
For agents **without** native xAI OAuth (Hermes, custom agents), use
`openab-auth-proxy` — a generic OAuth sidecar that defaults to xAI.

```bash
kubectl create configmap opencode-xai-config --from-file=opencode.json=- <<'EOF'
{
"$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" }
}
}
}
}
EOF
# Login (one-time)
openab-auth-proxy login-device

kubectl create configmap opencode-xai-auth --from-file=auth.json=- <<'EOF'
{ "xai": "dummy" }
EOF
```
# Start proxy
openab-auth-proxy serve --port 9090

## 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.

### 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).

## Environment Variables

| Variable | Default | Description |
|----------|---------|-------------|
| `XAI_PROXY_TOKEN_PATH` | `~/.xai-proxy/tokens.json` | Custom token file path |
| `RUST_LOG` | `xai_proxy=info` | Log level |

## 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)

```bash
# Build
cargo build --release

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

# Serve
./target/release/xai-proxy serve --port 9090

# 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
See [docs/refarch/sidecar-proxy.md](refarch/sidecar-proxy.md) for the full
architecture, Helm deployment, and custom provider configuration.

## 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 | openab-auth-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 |
| **Custom providers** | xAI only | Any OIDC provider via TOML config |
File renamed without changes.
7 changes: 4 additions & 3 deletions xai-proxy/Cargo.toml → openab-auth-proxy/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
[package]
name = "xai-proxy"
version = "0.1.0"
name = "openab-auth-proxy"
version = "0.2.0"
edition = "2021"
description = "Lightweight xAI OAuth proxy sidecar — PKCE login via accounts.x.ai, forwards OpenAI-compatible requests to api.x.ai/v1"
description = "Generic OAuth proxy sidecar — authenticates via OIDC/PKCE and forwards requests to any upstream API with Bearer token injection"

[dependencies]
tokio = { version = "1.44", features = ["full"] }
Expand All @@ -15,6 +15,7 @@ http-body-util = "0.1"
reqwest = { version = "0.12", features = ["json", "rustls-tls"], default-features = false }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
toml = "0.8"
clap = { version = "4", features = ["derive"] }
sha2 = "0.10"
base64 = "0.22"
Expand Down
4 changes: 2 additions & 2 deletions xai-proxy/Dockerfile → openab-auth-proxy/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@ RUN cargo build --release

FROM debian:bookworm-slim
RUN apt-get update && apt-get install -y ca-certificates && rm -rf /var/lib/apt/lists/*
COPY --from=builder /app/target/release/xai-proxy /usr/local/bin/
ENTRYPOINT ["xai-proxy"]
COPY --from=builder /app/target/release/openab-auth-proxy /usr/local/bin/
ENTRYPOINT ["openab-auth-proxy"]
CMD ["serve", "--bind", "0.0.0.0"]
81 changes: 81 additions & 0 deletions openab-auth-proxy/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
# openab-auth-proxy

Generic OAuth proxy sidecar for LLM APIs. Authenticates via OIDC (PKCE or device-code flow) and injects Bearer tokens into proxied requests.

Ships with a built-in **xAI/SuperGrok** preset. Configure any OAuth-protected API via a TOML config file.

## Quick start (xAI default)

```bash
# Login (device-code for headless, or browser PKCE)
openab-auth-proxy login-device
openab-auth-proxy login

# Start proxy
openab-auth-proxy serve --port 9090

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

## Custom provider

Create `auth-proxy.toml`:

```toml
[provider]
name = "my-provider"
discovery_url = "https://auth.example.com/.well-known/openid-configuration"
client_id = "my-client-id"
scopes = "openid offline_access api:access"
upstream_base_url = "https://api.example.com"
redirect_port = 8080
```

```bash
openab-auth-proxy -c auth-proxy.toml login-device
openab-auth-proxy -c auth-proxy.toml serve
```

## Architecture

```
┌─ Pod / Host ──────────────────────────────────────────────────┐
│ │
│ agent (any OpenAI-compatible client) │
│ │ POST /v1/chat/completions │
│ ▼ │
│ openab-auth-proxy :9090 │
│ • Reads OAuth token from disk │
│ • Injects Authorization: Bearer header │
│ • Auto-refreshes 120s before expiry │
│ │ │
│ Token: ~/.openab-auth-proxy/<provider>/tokens.json │
└───────────────┼───────────────────────────────────────────────┘
upstream API (configured via TOML)
```

## Environment variables

| Variable | Default | Description |
|----------|---------|-------------|
| `AUTH_PROXY_TOKEN_PATH` | `~/.openab-auth-proxy/<provider>/tokens.json` | Custom token file path |
| `XAI_PROXY_TOKEN_PATH` | (legacy) | Backward-compatible alias |
| `RUST_LOG` | `openab_auth_proxy=info` | Log level |

## Docker

```bash
docker build -t openab-auth-proxy .
docker run --rm -v ~/.openab-auth-proxy:/root/.openab-auth-proxy openab-auth-proxy serve --bind 0.0.0.0
```

## Presets

| Provider | Config needed? | Notes |
|----------|---------------|-------|
| xAI SuperGrok | No (built-in default) | Uses Grok CLI public OAuth client |
| Custom | Yes (`auth-proxy.toml`) | Any OIDC provider with device-code or PKCE |
Loading
Loading