- Add proxyctl CLI script for systemd service management - Add systemd user service file for background operation - Fix standalone LS kill: properly track real LS PID via pgrep and use sudo kill for cross-user cleanup on shutdown - Remove deprecated scripts (dns-redirect, iptables-redirect, mitm-wrapper, standalone-ls, parse-snapshot) - Disable tool stripping in MITM for tool call investigation - Update GEMINI.md with CLI tools documentation
8.1 KiB
Antigravity Rust Proxy
OpenAI-compatible proxy that intercepts and relays requests to Google's Antigravity language server, impersonating the real Electron webview.
Quick Start
# First-time setup (creates user + iptables for MITM)
sudo ./scripts/mitm-redirect.sh install
# Start as daemon (builds if needed)
proxyctl start
# Or run directly
RUST_LOG=info ./target/release/antigravity-proxy
Default port: 8741
CLI Tools
proxyctl — Daemon Manager
Symlinked to ~/.local/bin/proxyctl for global access. Manages the proxy as a systemd user service.
| Command | Description |
|---|---|
proxyctl start |
Start the proxy daemon |
proxyctl stop |
Stop the proxy daemon |
proxyctl restart |
Rebuild + restart |
proxyctl rebuild |
Build release binary only |
proxyctl status |
Service status + quota + usage |
proxyctl logs [N] |
Tail last N lines (default 30) + follow |
proxyctl logs-all |
Full log dump (no follow) |
proxyctl test [msg] |
Quick test request (gemini-3-flash) |
proxyctl health |
Health check |
mitm-redirect.sh — MITM Setup
One-time setup script for UID-scoped iptables traffic redirection.
sudo ./scripts/mitm-redirect.sh install # create user + iptables rule
sudo ./scripts/mitm-redirect.sh uninstall # remove user + iptables rule
sudo ./scripts/mitm-redirect.sh status # check current state
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 |
Development & Testing
- Dev/testing model:
gemini-3-flash— use this for all development, debugging, and iterative testing - Production model:
opus-4.6— use sparingly for real-world validation only (has quota limit) - See
docs/ls-binary-analysis.mdfor full reverse-engineered model catalog and proto enum mappings
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)
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"}'
Version Detection
Version strings (Antigravity, Chrome, Electron, Client) are auto-detected at startup from the installed Antigravity app:
product.json→ app version + client/IDE version- Binary → Chrome + Electron versions via
strings
Falls back to hardcoded values if the app isn't installed. No manual updates needed when Antigravity updates.
Standalone LS
By default, the proxy spawns its own Language Server instance for full isolation:
- Discovers the main LS config (
extension_server_port,csrf_token) from the running Antigravity app - Spawns a standalone LS binary on a random port
- Builds init metadata protobuf (model config,
detect_and_use_proxy=ENABLED) - If MITM is active, spawns as
antigravity-lsuser for UID-scoped traffic interception - Kills the child on proxy shutdown
Disable with --no-standalone to attach to the real LS instead.
Module: src/standalone.rs
Stealth Features
- TLS fingerprint: BoringSSL with Chrome JA3/JA4 + H2 fingerprint via
wreq(version auto-detected) - 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
- Reactive streaming:
StreamCascadeReactiveUpdatesfor real-time state diffs (polling fallback) - Jitter: Randomized intervals to avoid automation fingerprint
- Session reuse: Cascades reused for multi-turn, matching real webview behavior
- MITM proxy: TLS-intercepting proxy for real token usage capture
MITM Proxy
Built-in MITM proxy intercepts LS ↔ Google API traffic to capture real token usage (input, output, thinking tokens). Enabled by default with the standalone LS. Disable with --no-mitm.
How It Works
Client → Proxy (8741) → Standalone LS (as antigravity-ls user)
↓ (port 443 traffic)
iptables REDIRECT (UID-scoped)
↓
MITM Proxy (8742)
↓ (TLS decrypt + parse SSE)
Google API (daily-cloudcode-pa.googleapis.com)
Setup
# One-time setup (creates user + iptables rule)
sudo ./scripts/mitm-redirect.sh install
# Run proxy (standalone LS + MITM are both on by default)
RUST_LOG=info ./target/release/antigravity-proxy
# Check intercepted usage
curl -s http://localhost:8741/v1/usage | jq .
# Cleanup
sudo ./scripts/mitm-redirect.sh uninstall
Details
- UID-scoped iptables: Only the standalone LS's traffic is intercepted (no side effects)
- Combined CA bundle: System CAs + MITM CA →
/tmp/antigravity-mitm-combined-ca.pem - Google SSE parsing: Extracts
promptTokenCount,candidatesTokenCount,thoughtsTokenCount - Init metadata: Protobuf field 34
detect_and_use_proxyset to ENABLED (1) - See
docs/mitm-interception-status.mdfor full technical details - See
docs/ls-binary-analysis.mdfor proto enum mappings and model IDs
CLI Flags
--no-mitm: Disable MITM proxy entirely--no-standalone: Attach to existing LS instead of spawning standalone--mitm-port <PORT>: Override MITM proxy port (default: auto-assign)--port <PORT>: Override proxy listen port (default: 8741)