8.0 KiB
8.0 KiB
Antigravity Proxy
OpenAI-compatible proxy that intercepts and relays requests to Google's Antigravity language server, impersonating the real Electron webview.
Quick Start
# Build
cargo build --release
# Run (language server must be running)
RUST_LOG=info ./target/release/antigravity-proxy
# Custom port
RUST_LOG=info ./target/release/antigravity-proxy --port 9000
Default port: 8741
Endpoints
| Method | Path | Description |
|---|---|---|
POST |
/v1/responses |
Responses API (primary) — supports stream: true/false |
POST |
/v1/chat/completions |
Chat Completions API (OpenAI compat shim) |
GET |
/v1/models |
List available models |
GET |
/v1/sessions |
List active sessions |
DELETE |
/v1/sessions/:id |
Delete a session |
POST |
/v1/token |
Set OAuth token at runtime |
GET |
/v1/usage |
MITM-intercepted token usage stats |
GET |
/v1/quota |
LS quota — credits, per-model rate limits, reset timers |
GET |
/health |
Health check |
Available Models
| Name | Label |
|---|---|
opus-4.6 |
Claude Opus 4.6 (Thinking) — default |
opus-4.5 |
Claude Opus 4.5 (Thinking) |
gemini-3-pro-high |
Gemini 3 Pro (High) |
gemini-3-pro |
Gemini 3 Pro (Low) |
gemini-3-flash |
Gemini 3 Flash |
Example: Responses API
Sync
curl -s http://localhost:8741/v1/responses \
-H "Content-Type: application/json" \
-d '{
"model": "gemini-3-flash",
"input": "Say hello in exactly 3 words",
"stream": false,
"timeout": 60
}' | jq .
Streaming
curl -N http://localhost:8741/v1/responses \
-H "Content-Type: application/json" \
-d '{
"model": "gemini-3-flash",
"input": "Say hello in exactly 3 words",
"stream": true,
"timeout": 60
}'
Multi-turn (session reuse)
# First message
curl -s http://localhost:8741/v1/responses \
-H "Content-Type: application/json" \
-d '{
"model": "gemini-3-flash",
"input": "What is 2+2?",
"conversation": "my-session-1",
"stream": false
}' | jq .
# Follow-up in same cascade
curl -s http://localhost:8741/v1/responses \
-H "Content-Type: application/json" \
-d '{
"model": "gemini-3-flash",
"input": "Now multiply that by 10",
"conversation": "my-session-1",
"stream": false
}' | jq .
Authentication
The proxy needs an OAuth token. Three ways to provide it:
- Environment variable:
export ANTIGRAVITY_OAUTH_TOKEN=ya29.xxx - Token file:
echo 'ya29.xxx' > ~/.config/antigravity-proxy-token - Runtime API:
curl -X POST http://localhost:8741/v1/token -d '{"token":"ya29.xxx"}'
Stealth Features
- TLS fingerprint: BoringSSL with Chrome 142 JA3/JA4 + H2 fingerprint via
wreq - Protobuf: Hand-rolled encoder producing byte-exact match to real webview traffic
- Warmup: Mimics real webview startup RPC calls
- Heartbeat: Periodic keep-alive matching real webview lifecycle
- Jitter: Randomized polling intervals to avoid automation fingerprint
- Session reuse: Cascades are reused for multi-turn, matching real webview behavior
- Version detection: Auto-detects Antigravity/Chrome/Electron versions from installed app
MITM Proxy
Built-in TLS-intercepting proxy captures real token usage from LS ↔ Google/Anthropic traffic. Disabled with --no-mitm.
Setup
# 1. Start proxy (generates CA cert automatically)
RUST_LOG=info ./target/release/antigravity-proxy
# 2. Install wrapper (patches LS binary to route through MITM)
sudo ./scripts/mitm-wrapper.sh install
# 3. Restart Antigravity — done!
# Check status
./scripts/mitm-wrapper.sh status
# Uninstall
sudo ./scripts/mitm-wrapper.sh uninstall
Usage Stats
curl -s http://localhost:8741/v1/usage | jq .
Standalone Language Server
Launch an isolated LS instance for experimentation:
# Basic test (starts, checks quota, exits)
./scripts/standalone-ls.sh
# Foreground mode (stays alive)
./scripts/standalone-ls.sh --fg
# With MITM traffic interception
./scripts/standalone-ls.sh --mitm
# Capture a clean traffic snapshot
./scripts/standalone-ls.sh --snapshot
# Snapshot with custom prompt
./scripts/standalone-ls.sh --snapshot --prompt "Explain quantum computing"
The standalone LS shares the main Antigravity app's OAuth (via its extension server) but has its own port, data directory, and cascades.
Traffic Snapshots
The --snapshot flag captures all HTTP/2 traffic and formats it into a clean, color-coded report:
══════════════════════════════════════════════════════════════════════
STANDALONE LS TRAFFIC SNAPSHOT
══════════════════════════════════════════════════════════════════════
▸ Outbound Connections
→ antigravity-unleash.goog (Feature Flags)
→ play.googleapis.com (Telemetry)
══════════════════════════════════════════════════════════════════════
antigravity-unleash.goog — Feature Flags
══════════════════════════════════════════════════════════════════════
→ POST /api/client/register
authorization: *:production.e4455...
unleash-appname: codeium-language-server
Body (561 bytes, JSON):
{"appName":"codeium-language-server","instanceId":"..."}
Architecture
graph LR
A[Your App<br/>OpenAI SDK] -->|HTTP| B[Proxy<br/>:8741]
B -->|gRPC| C[Language<br/>Server]
C -->|HTTPS| D[Google /<br/>Anthropic]
E[MITM Proxy<br/>:8742] -.->|intercept| D
C -.->|routed via| E
Project Structure
src/
├── main.rs # Entry point, CLI args, lifecycle
├── backend.rs # LS discovery and RPC communication
├── constants.rs # Version detection + stealth constants
├── proto.rs # Hand-rolled protobuf encoder
├── quota.rs # LS quota polling and caching
├── session.rs # Multi-turn session management
├── warmup.rs # Startup warmup (mimics real webview)
├── api/
│ └── mod.rs # Axum API server + route handlers
└── mitm/
├── mod.rs # MITM module root
├── ca.rs # Dynamic CA cert generation
├── proxy.rs # TLS-intercepting proxy server
├── intercept.rs # API response parser (usage extraction)
└── store.rs # Token usage aggregation store
scripts/
├── mitm-wrapper.sh # Install/uninstall MITM wrapper on LS binary
├── standalone-ls.sh # Launch isolated LS instance
└── parse-snapshot.py # HTTP/2 traffic snapshot parser
CLI Flags
antigravity-proxy [OPTIONS]
Options:
--port <PORT> API server port (default: 8741)
--no-mitm Disable MITM proxy
--mitm-port <PORT> Override MITM proxy port (default: auto)
License
Private. Do not distribute.