# MITM Traffic Interception — Status ## Status: ✅ FULLY WORKING (Standalone Mode) MITM interception is operational for the standalone LS. The proxy intercepts, decrypts, and parses all LLM API traffic with per-model token usage capture. ## 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) ``` ### Components 1. **UID-scoped iptables** (`scripts/mitm-redirect.sh`) - Creates `antigravity-ls` system user - iptables rule: redirect UID's port-443 → MITM port - Only the standalone LS is affected — no side effects on other software 2. **Combined CA bundle** (`src/standalone.rs`) - Go's `SSL_CERT_FILE` replaces (not appends) the system trust store - Proxy concatenates system CAs + MITM CA → `/tmp/antigravity-mitm-combined-ca.pem` - Set as `SSL_CERT_FILE` on the standalone LS process 3. **`sudo -u` spawning** (`src/standalone.rs`) - If `antigravity-ls` user exists, LS is spawned via `sudo -n -u antigravity-ls` - Env vars passed via `/usr/bin/env KEY=VALUE` args - Falls back to current user if the dedicated user doesn't exist 4. **Google SSE parser** (`src/mitm/intercept.rs`) - Parses `data: {"response": {"usageMetadata": {...}}}` events - Extracts `promptTokenCount`, `candidatesTokenCount`, `thoughtsTokenCount` - Handles both Google and Anthropic SSE formats 5. **Transparent proxy** (`src/mitm/proxy.rs`) - Detects iptables-redirected connections via TLS ClientHello SNI - Terminates TLS with dynamically generated certs - Forwards HTTP/1.1 requests upstream with real DNS resolution (`dig @8.8.8.8`) - Chunked response detection for fast completion 6. **Request modification** (`src/mitm/modify.rs`) - Strips LS system instructions down to `` block only - Removes stale conversation history (keeps only last user message) - Injects client tools, tool configs, generation params - Injects images as `inlineData` (base64) into user message parts - Injects tool results as `functionResponse` parts - Enables Google Search grounding when requested - Updates `Content-Length` header after body modification 7. **Upstream error capture** (`src/mitm/store.rs`) - Captures Google API error responses (HTTP 400, 429, 500, etc.) - Parses error JSON for message and status fields - Stores in `MitmStore` for immediate forwarding to client - Prevents request hangs on upstream failures ## What We Tried (Historical) ### 1. Extension Patch — `detectAndUseProxy` ✅ Still Active Patches `detectAndUseProxy=1` in the extension JS. Makes auxiliary traffic (Unleash, etc.) honor `HTTPS_PROXY`. Harmless, still applied. ### 2. MITM Wrapper (`mitm-wrapper.sh`) ⚠️ Superseded Sets env vars on the main LS process. Works for routing but the main LS's LLM client ignores `HTTPS_PROXY`. Superseded by standalone mode. ### 3. iptables REDIRECT (All Traffic) ❌ Abandoned Redirected ALL port-443 traffic. Caused redirect loops, broke other HTTPS traffic. Replaced by UID-scoped redirect. ### 4. DNS Redirect (`/etc/hosts`) ❌ Abandoned Same TLS trust issue as #3. Unnecessary with UID-scoped iptables. ### 5. Standalone LS + UID-scoped iptables ✅ WORKING Current solution. Full MITM interception with zero side effects. ## The Original Blocker (SOLVED) > The LS's Go LLM HTTP client uses a custom `tls.Config` that does NOT read > from `SSL_CERT_FILE` or the system CA store. **This turned out to be wrong.** The Go client DOES honor `SSL_CERT_FILE` when: - The env var is set BEFORE the process starts (not injected later) - The value contains a combined bundle (system CAs + custom CA) - `SSL_CERT_DIR` is set to `/dev/null` to force exclusive use of `SSL_CERT_FILE` The standalone LS gives us full control over the process environment at spawn time, which is why this approach works while the wrapper approach didn't. ## Technical Details ### API Endpoint `POST https://daily-cloudcode-pa.googleapis.com/v1internal:streamGenerateContent?alt=sse` ### SSE Response Format ``` data: {"response": {"candidates": [{"content": {"role": "model", "parts": [{"text": "..."}]}}], "usageMetadata": {"promptTokenCount": 1514, "candidatesTokenCount": 25, "totalTokenCount": 1539, "thoughtsTokenCount": 52}, "modelVersion": "gemini-3-flash"}, "traceId": "...", "metadata": {}} ``` Last event includes `"finishReason": "STOP"` in the candidate. ### Other Intercepted Endpoints | Endpoint | Type | Content | | --------------------------- | -------- | ---------------- | | `fetchUserInfo` | Protobuf | User info | | `loadCodeAssist` | Protobuf | Extension config | | `fetchAvailableModels` | Protobuf | Model catalog | | `webDocsOptions` | Protobuf | Docs config | | `streamGenerateContent` | SSE/JSON | LLM responses ✅ | | `recordCodeAssistMetrics` | Protobuf | Telemetry | | `recordTrajectoryAnalytics` | Protobuf | Telemetry | ### Model IDs | Placeholder | Model | | ----------------------- | ------------------- | | `MODEL_PLACEHOLDER_M18` | Gemini 3 Flash | | `MODEL_PLACEHOLDER_M8` | Gemini 3 Pro (High) | | `MODEL_PLACEHOLDER_M7` | Gemini 3 Pro (Low) | | `MODEL_PLACEHOLDER_M26` | Claude Opus 4.6 | | `MODEL_PLACEHOLDER_M12` | Claude Opus 4.5 | ### Setup ```bash # One-time setup (creates user + iptables rule) sudo ./scripts/mitm-redirect.sh install # Run proxy (standalone + MITM are default) RUST_LOG=info ./target/release/antigravity-proxy # Check usage curl -s http://localhost:8741/v1/usage | jq . ``` ### Cleanup ```bash # Remove iptables rule + user sudo ./scripts/mitm-redirect.sh uninstall ```