- Remove debug header dump from MITM proxy (was temp debugging code) - Suppress dead_code warnings for intentional OpenAI compat fields - Rewrite README with styled mermaid architecture diagrams, full feature listing, usage examples, and CLI reference - Update endpoint-gap-analysis: images implemented, audio only stretch - Update mitm-interception-status: add request modification and error capture components - Update standalone-ls-todo: add new endpoints to test results - Zero compiler warnings
5.9 KiB
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
-
UID-scoped iptables (
scripts/mitm-redirect.sh)- Creates
antigravity-lssystem user - iptables rule: redirect UID's port-443 → MITM port
- Only the standalone LS is affected — no side effects on other software
- Creates
-
Combined CA bundle (
src/standalone.rs)- Go's
SSL_CERT_FILEreplaces (not appends) the system trust store - Proxy concatenates system CAs + MITM CA →
/tmp/antigravity-mitm-combined-ca.pem - Set as
SSL_CERT_FILEon the standalone LS process
- Go's
-
sudo -uspawning (src/standalone.rs)- If
antigravity-lsuser exists, LS is spawned viasudo -n -u antigravity-ls - Env vars passed via
/usr/bin/env KEY=VALUEargs - Falls back to current user if the dedicated user doesn't exist
- If
-
Google SSE parser (
src/mitm/intercept.rs)- Parses
data: {"response": {"usageMetadata": {...}}}events - Extracts
promptTokenCount,candidatesTokenCount,thoughtsTokenCount - Handles both Google and Anthropic SSE formats
- Parses
-
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
-
Request modification (
src/mitm/modify.rs)- Strips LS system instructions down to
<identity>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
functionResponseparts - Enables Google Search grounding when requested
- Updates
Content-Lengthheader after body modification
- Strips LS system instructions down to
-
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
MitmStorefor 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.Configthat does NOT read fromSSL_CERT_FILEor 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_DIRis set to/dev/nullto force exclusive use ofSSL_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
# 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
# Remove iptables rule + user
sudo ./scripts/mitm-redirect.sh uninstall