Server-side (Apps Script) fixes — users replace their Code.gs with assets/apps_script/Code.gs (or CodeFull.gs for full mode) and Manage deployments → ✏️ → New version → Deploy:
- Removed duplicate doGet in Code.gs (HtmlService one was overriding ContentService one due to JS hoisting → every GET to /exec returned a goog.script.init iframe instead of the placeholder HTML)
- CodeFull.gs doGet switched from HtmlService to ContentService (same reason)
- SKIP_HEADERS now strips X-Forwarded-* / Forwarded / Via family — second line of defense to v1.2.9's client-side stripping (#104), in case a misconfigured upstream proxy adds these
- _doBatch fallback when UrlFetchApp.fetchAll() throws as a whole — per-item fetch on safe methods so one bad URL no longer poisons the entire batch (port from masterking32@3094288)
Client-side (Rust) defense-in-depth:
- parse_relay_json now unwraps goog.script.init("...userHtml...") if any deployment returns the iframe-wrapped form (legacy Code.gs, or a redirect that GETs doGet). New extract_apps_script_user_html + decode_js_string_escapes helpers. Tested against a real deployment's doGet response.
Docs:
- README rewritten as short bilingual landing page (English + Persian RTL) targeting normal users; advanced reference moved to docs/guide.md + docs/guide.fa.md.
Tests: 3 new regression tests. 176 lib + 33 tunnel-node tests passing.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Five user-visible changes shipping together. Each is independently
useful + bounded; bundled because they're all "small architectural
hardening" that benefits from one release announcement.
1. Random payload padding (#313, #365 §1)
Every outbound Apps Script JSON request now carries a `_pad` field
of uniform-random length 0..1024 bytes (base64). Defeats DPI that
fingerprints on the tight length distribution of mhrv-rs's previous
per-mode-bound packet sizes. ~25% bandwidth on a typical 2 KB batch,
negligible against Apps Script's per-call latency floor. Backward-
compatible — old `Code.gs` deployments ignore the unknown field.
Applied at all three payload-build sites: single relay, single
tunnel op, batch tunnel.
2. Active-probing decoy: GAS bad-auth → 200 HTML (#365 §3)
`Code.gs` and `CodeFull.gs` now return a benign Apps-Script-style
placeholder HTML page on bad/missing AUTH_KEY instead of the JSON
`{"e":"unauthorized"}`. To an active scanner the deployment looks
like one of the millions of forgotten public Apps Script projects
rather than an obvious API endpoint. New `DIAGNOSTIC_MODE` const
restores JSON errors during setup; default false (production-strong).
3. Active-probing decoy: tunnel-node bad-auth → 404 nginx (#365 §3)
`tunnel-node` returns an HTTP 404 with an nginx-style HTML body on
bad auth instead of `{"e":"unauthorized"}`. Active scanners cataloging
the host see "static web server, nothing tunnel-shaped here." New
`MHRV_DIAGNOSTIC=1` env var restores verbose JSON during setup.
4. Fix: Full-mode usage counter stuck at zero (#230, #362)
`today_calls` / `today_bytes` were only being incremented on the
apps_script-mode relay path. Full-mode batches go through
`tunnel_client::fire_batch` which never wired into the counter.
Now `fire_batch` calls `record_today(response_bytes)` after each
successful batch — bytes estimated from the `d` (TCP payload) and
`pkts` (UDP datagrams) sizes in the BatchTunnelResponse. Full-mode
users now see real usage numbers.
5. Fix: quota reset countdown was UTC, should be PT (#230, #362)
Apps Script's UrlFetchApp daily quota resets at midnight Pacific
Time, not UTC. We were displaying the countdown to UTC midnight,
off by 7-8h depending on DST. New `current_pt_day_key()` and
`seconds_until_pacific_midnight()` helpers with hand-rolled US DST
detection (2nd Sunday March → 1st Sunday November = PDT, else PST)
so we don't pull `chrono-tz` and a ~3 MB IANA tzdb just for one
helper. UI label "UTC day" → "PT day". Tests pin DST window
boundaries against March/November of 2024, 2026, 2027 to catch
regressions in the day-of-week math.
Tested:
- cargo test --lib: 154 passed (was 152, +2 for DST window + day-of-week)
- cargo build --release: clean
- cargo build --release --bin mhrv-rs-ui --features ui: clean (macOS arm64)
- tunnel-node cargo test: 30 passed
- Android: ./gradlew assembleDebug succeeds; APK installs + launches
on mhrv_test emulator (arm64-v8a), no UnsatisfiedLink, no crash
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two reasons to pin a copy in the repo:
1. Users on networks where raw.githubusercontent.com is intermittent
can still get the deploy-ready file via a repo ZIP / clone.
2. The Apps Script relay protocol between mhrv-rs and Code.gs is
informal — upstream changes can silently break us. Keeping a
snapshot lets future-us diff against what we tested against
when diagnosing protocol-drift bugs.
Fetched verbatim from:
https://raw.githubusercontent.com/masterking32/MasterHttpRelayVPN/refs/heads/python_testing/apps_script/Code.gs
Credit stays with @masterking32. The assets/apps_script/README.md
next to it calls out that we don't modify this file — users deploy
it as-is into their own Google Apps Script project.
Updated the Setup Guide link in both the English and Persian
sections so offline / restricted-network users have a fallback path.