215 lines
8.2 KiB
Markdown
215 lines
8.2 KiB
Markdown
# th3ist: OpenAI-compatible gateway for t3.chat
|
|||
|
|
|
||
|
|
`th3ist` is a zero-dependency, standalone Go proxy gateway that exposes an OpenAI-compatible HTTP interface (`/v1/chat/completions` and `/v1/models`) backed by the `t3.chat` API.
|
||
|
|
|
||
|
|
## Features
|
||
|
|
|
||
|
|
- **Standard OpenAI interface**: Serves `/v1/chat/completions` and `/v1/models` (with `/chat/completions` and `/models` aliases).
|
||
|
|
- **Dual-engine architecture**:
|
||
|
|
- **Browser bridge (`-auto-capture`)**: Routes upstream requests directly through a private, stealth headless Chromium session over CDP. This ensures a 100% genuine Chrome TLS handshake (JA3/JA4), completely bypassing Vercel Security Checkpoints and WAF blocks.
|
||
|
|
- **Direct HTTP client**: Fallback mode for high-throughput environments where valid Vercel clearance cookies and tokens are supplied directly.
|
||
|
|
- **Private & anti-fingerprinting stealth**:
|
||
|
|
- **100% isolated session**: Launches in `--incognito` mode with a temporary ephemeral user profile and `--disable-extensions`, completely segregated from any existing Chromium/Chrome windows, history, extensions, or sessions.
|
||
|
|
- **Zero port collisions**: Binds to a dynamically allocated ephemeral loopback port for CDP commands.
|
||
|
|
- **Stealth & anti-detection**:
|
||
|
|
- Disables Blink automation features (`--disable-blink-features=AutomationControlled`).
|
||
|
|
- Masks `navigator.webdriver` to `undefined` via `Page.addScriptToEvaluateOnNewDocument`.
|
||
|
|
- Normalizes `window.chrome`, `navigator.languages`, and `navigator.plugins`.
|
||
|
|
- Sets desktop viewport (`--window-size=1920,1080`) and custom User-Agent.
|
||
|
|
- Strips telemetry, domain reliability, crash reporting, and sync.
|
||
|
|
- **Dynamic token ingestion API**: Provides `GET /v1/token` and `POST /v1/token` to inspect or hot-reload single-use hCaptcha tokens and cookies on the fly without restarting the server.
|
||
|
|
- **Fast fail & clear error propagation**: Non-transient errors (such as `captcha_failed` or `invalid_params`) return immediately (<300ms) without wasting time in exponential backoff retry loops.
|
||
|
|
- **Streaming & non-streaming**: Full support for Server-Sent Events (`stream: true`) and standard JSON responses (`stream: false`).
|
||
|
|
- **Reasoning content separation**: Parses `<think>...</think>` tags and upstream reasoning chunks into `reasoning_content` deltas / message fields.
|
||
|
|
- **Function / tool calling translation**: Injects tool schemas into system instructions, maps multi-turn tool calling history, and parses model tool invocations into standard OpenAI `tool_calls`.
|
||
|
|
- **Zero external dependencies**: Implemented using pure Go standard library.
|
||
|
|
|
||
|
|
## Building
|
||
|
|
|
||
|
|
```bash
|
||
|
|
make build
|
||
|
|
```
|
||
|
|
|
||
|
|
Binary will be produced at `bin/th3ist`.
|
||
|
|
|
||
|
|
## Running
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# Run with Browser Bridge active (recommended for bypassing Vercel TLS checkpoint)
|
||
|
|
./bin/th3ist -auto-capture
|
||
|
|
|
||
|
|
# Run on a custom port with static defaults
|
||
|
|
./bin/th3ist -port 9000 -default-model gemini-3.5-flash-lite
|
||
|
|
```
|
||
|
|
|
||
|
|
### CLI flags
|
||
|
|
|
||
|
|
| Flag | Description | Default |
|
||
|
|
|------|-------------|---------|
|
||
|
|
| `-auto-capture` | Enable private browser bridge (bypasses Vercel TLS & mints fresh tokens) | `false` |
|
||
|
|
| `-xvfb` | Automatically spawn and manage an isolated virtual X server (`Xvfb`) for complete isolation from tiling window managers | `false` |
|
||
|
|
| `-display` | Custom X11 DISPLAY to attach Chromium to (e.g. `:99` for an existing Xvfb / Xephyr / Xnest session) | *(auto / `$DISPLAY`)* |
|
||
|
|
| `-headless` | Force strict headless mode `--headless=new` (defaults to offscreen window when display is present) | `false` |
|
||
|
|
| `-browser-bin` | Custom path to Chromium/Google Chrome binary | *(auto-detected)* |
|
||
|
|
| `-port` | Port to listen on | `8080` |
|
||
|
|
| `-endpoint` | Upstream T3 chat endpoint | `https://t3.chat/api/chat` |
|
||
|
|
| `-default-model` | Default model identifier | `gemini-3.5-flash-lite` |
|
||
|
|
| `-cookie` | Cookie string to pass upstream | *(Captured from req.sh)* |
|
||
|
|
| `-hcaptcha-token` | hCaptcha token to pass upstream | *(Captured from req.sh)* |
|
||
|
|
| `-deployment-id` | `x-deployment-id` header value | `dpl_2DEEf25udk9uuFz7LwnraJnmTYoE` |
|
||
|
|
| `-client-context` | `x-client-context` header value | Base64 client context string |
|
||
|
|
| `-user-agent` / `-ua` | User-Agent string sent upstream | Google Chrome 133.0 |
|
||
|
|
|
||
|
|
### Tiled window managers & virtual display isolation
|
||
|
|
|
||
|
|
On tiling window managers (e.g., i3, bspwm, sway, dwm, awesome, hyprland, xmonad), any window created on the main `$DISPLAY` may be caught and tiled into the current workspace.
|
||
|
|
|
||
|
|
To prevent any windows from appearing on your desktop:
|
||
|
|
|
||
|
|
1. **Option A: Auto-managed Xvfb (`-xvfb`)**:
|
||
|
|
Install `xorg-server-xvfb` (or `Xfbdev`):
|
||
|
|
- **Void Linux**: `sudo xbps-install -S xorg-server-xvfb`
|
||
|
|
- **Debian / Ubuntu**: `sudo apt install xvfb`
|
||
|
|
- **Arch Linux**: `sudo pacman -S xorg-server-xvfb`
|
||
|
|
|
||
|
|
Then run `th3ist` with `-xvfb`:
|
||
|
|
```bash
|
||
|
|
./bin/th3ist -auto-capture -xvfb -port 9000
|
||
|
|
```
|
||
|
|
`th3ist` will automatically allocate an isolated virtual display (e.g. `:99`), launch `Xvfb`, attach Chromium to it, and cleanly terminate `Xvfb` on exit.
|
||
|
|
|
||
|
|
2. **Option B: Manual virtual X server (`Xvfb`, `Xephyr`, or `Xnest`)**:
|
||
|
|
```bash
|
||
|
|
# Start virtual framebuffer in background
|
||
|
|
Xvfb :99 -screen 0 1280x800x24 -ac &
|
||
|
|
|
||
|
|
# Run th3ist attached to display :99
|
||
|
|
./bin/th3ist -auto-capture -display :99 -port 9000
|
||
|
|
```
|
||
|
|
|
||
|
|
3. **Option C: Tiling window manager rules (`--class=th3ist_hidden`)**:
|
||
|
|
Chromium is launched with `--class=th3ist_hidden` and `--app=https://t3.chat`. You can add a floating/hidden rule to your window manager config:
|
||
|
|
- **i3/sway**: `for_window [class="th3ist_hidden"] floating enable, move scratchpad`
|
||
|
|
- **bspwm**: `bspc rule -a th3ist_hidden state=floating hidden=on`
|
||
|
|
|
||
|
|
### Token ingestion API
|
||
|
|
|
||
|
|
`t3.chat` protects `/api/chat` with single-use hCaptcha tokens. You can inspect or hot-reload tokens live:
|
||
|
|
|
||
|
|
- **Inspect current credentials**:
|
||
|
|
```bash
|
||
|
|
curl http://localhost:8080/v1/token
|
||
|
|
```
|
||
|
|
- **Push fresh hCaptcha token**:
|
||
|
|
```bash
|
||
|
|
curl -X POST http://localhost:8080/v1/token \
|
||
|
|
-H "Content-Type: application/json" \
|
||
|
|
-d '{
|
||
|
|
"hcaptchaToken": "<new-unconsumed-hcaptcha-token>",
|
||
|
|
"cookie": "<optional-new-cookie-string>"
|
||
|
|
}'
|
||
|
|
```
|
||
|
|
|
||
|
|
### Per-request header overrides
|
||
|
|
|
||
|
|
You can also override credentials per request:
|
||
|
|
- `X-Hcaptcha-Token`: supply a fresh hCaptcha token for a single request.
|
||
|
|
- `X-Cookie` / `Cookie` / `Authorization: Bearer <cookie-string>`: override upstream cookies.
|
||
|
|
- `X-Deployment-Id`: override `x-deployment-id`.
|
||
|
|
- `X-Client-Context`: override `x-client-context`.
|
||
|
|
- `X-User-Agent`: override upstream User-Agent.
|
||
|
|
|
||
|
|
## Usage examples
|
||
|
|
|
||
|
|
### 1. List models
|
||
|
|
|
||
|
|
```bash
|
||
|
|
curl http://localhost:8080/v1/models
|
||
|
|
```
|
||
|
|
|
||
|
|
### 2. Chat completion (non-streaming)
|
||
|
|
|
||
|
|
```bash
|
||
|
|
curl http://localhost:8080/v1/chat/completions \
|
||
|
|
-H "Content-Type: application/json" \
|
||
|
|
-d '{
|
||
|
|
"model": "gemini-3.5-flash-lite",
|
||
|
|
"messages": [
|
||
|
|
{"role": "user", "content": "Hello, how are you?"}
|
||
|
|
]
|
||
|
|
}'
|
||
|
|
```
|
||
|
|
|
||
|
|
### 3. Chat completion (streaming)
|
||
|
|
|
||
|
|
```bash
|
||
|
|
curl http://localhost:8080/v1/chat/completions \
|
||
|
|
-H "Content-Type: application/json" \
|
||
|
|
-d '{
|
||
|
|
"model": "gemini-3.5-flash-lite",
|
||
|
|
"messages": [
|
||
|
|
{"role": "user", "content": "Tell me a short joke."}
|
||
|
|
],
|
||
|
|
"stream": true
|
||
|
|
}'
|
||
|
|
```
|
||
|
|
|
||
|
|
### 4. Tool / function calling
|
||
|
|
|
||
|
|
```bash
|
||
|
|
curl http://localhost:8080/v1/chat/completions \
|
||
|
|
-H "Content-Type: application/json" \
|
||
|
|
-d '{
|
||
|
|
"model": "gemini-3.5-flash-lite",
|
||
|
|
"messages": [
|
||
|
|
{"role": "user", "content": "What is the weather in Tokyo?"}
|
||
|
|
],
|
||
|
|
"tools": [
|
||
|
|
{
|
||
|
|
"type": "function",
|
||
|
|
"function": {
|
||
|
|
"name": "get_weather",
|
||
|
|
"description": "Get current weather for a city",
|
||
|
|
"parameters": {
|
||
|
|
"type": "object",
|
||
|
|
"properties": {
|
||
|
|
"location": {"type": "string", "description": "City name"}
|
||
|
|
},
|
||
|
|
"required": ["location"]
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
]
|
||
|
|
}'
|
||
|
|
```
|
||
|
|
|
||
|
|
### 5. Using with official OpenAI Python SDK
|
||
|
|
|
||
|
|
```python
|
||
|
|
from openai import OpenAI
|
||
|
|
|
||
|
|
client = OpenAI(
|
||
|
|
base_url="http://localhost:8080/v1",
|
||
|
|
api_key="not-needed"
|
||
|
|
)
|
||
|
|
|
||
|
|
response = client.chat.completions.create(
|
||
|
|
model="gemini-3.5-flash-lite",
|
||
|
|
messages=[
|
||
|
|
{"role": "user", "content": "Write a quick hello world in Go"}
|
||
|
|
],
|
||
|
|
stream=True
|
||
|
|
)
|
||
|
|
|
||
|
|
for chunk in response:
|
||
|
|
if chunk.choices[0].delta.content:
|
||
|
|
print(chunk.choices[0].delta.content, end="", flush=True)
|
||
|
|
print()
|
||
|
|
```
|
||
|
|
|
||
|
|
## Testing
|
||
|
|
|
||
|
|
```bash
|
||
|
|
make test
|
||
|
|
```
|