After the OS trust store install, also try to add the MITM CA to all
discovered Firefox profiles via NSS certutil. Silently no-ops if:
- NSS certutil is not installed (macOS ships a different certutil; linux
needs libnss3-tools; Windows needs NSS binaries)
- No Firefox profiles exist
- Firefox is currently running (lock on cert.db)
Scans profiles in:
- macOS: ~/Library/Application Support/Firefox/Profiles
- Linux: ~/.mozilla/firefox and ~/snap/firefox/common/.mozilla/firefox
- Windows: %APPDATA%\Mozilla\Firefox\Profiles
Existing CA-install error path is unchanged; this is purely additive.
SNI-rewrite tunnels (src/proxy_server.rs):
- CONNECT to youtube.com / googlevideo.com / doubleclick / etc. now bypasses
the Apps Script relay entirely and goes direct to the Google edge IP
with SNI=front_domain.
- Accepts browser TLS with our MITM cert, opens outbound TLS to
config.google_ip with SNI=config.front_domain, bridges decrypted bytes.
- Matches Python's _do_sni_rewrite_tunnel behavior. Faster than relay for
large streams (video).
- Also respects config.hosts override map (custom IP per suffix).
gzip decode fix (src/domain_fronter.rs):
- Apps Script outer response is gzipped. Previous stub always failed,
causing 'non-utf8 json' errors. Swapped in flate2::GzDecoder.
- Verified end-to-end: HTTP and HTTPS requests through apps_script
relay succeed and return real Google IPs.
When a script returns 429, 403, or a quota/rate-limit error body,
drop it from the active rotation for 10 minutes. next_script_id
skips blacklisted IDs; if all are blacklisted, picks the one
coming off cooldown soonest.
Script IDs are masked in logs (prefix...suffix) to avoid leaking
the deployment ID even at info level.
First concurrent caller for a cache key does the relay; subsequent
callers subscribe to a broadcast channel and receive the same response.
Only applies to cacheable (GET/HEAD) requests without body.
- New cache.rs: FIFO-eviction cache with max_bytes cap
- Cacheable: GET/HEAD only, no-store/no-cache/private/Set-Cookie reject
- TTL from Cache-Control: max-age=, or heuristics by extension (css/js/fonts/images -> 1h)
- Hook in DomainFronter::relay: check cache before network, store after 2xx
- 10 new unit tests (23 total)