Three orthogonal cache-correctness wins on `Code.gs`'s spreadsheet response cache. All compound: more URLs become cacheable, the cache stops being poisoned by transient outages, and 4xx storms cost zero quota. **1. Gzip body before base64 storage** (new `Z` column, base64(gzip(rawBytes)) when worthwhile, skipped when already encoded or under 256 bytes). 3-5× compression on text bodies — many ~100-150 KB responses now fit under the 35 KB cell ceiling that previously rejected them. `_getFromCache` decompresses on hit and re-encodes for the wire — relay protocol's `b` field stays `base64(rawBytes)`, no client change. **2. 5xx never enters the cache.** A flapping upstream that returned 503 once was previously pinned for 24h, breaking the URL for every subsequent client. `status >= 500` now early-returns the live response without writing. **3. Negative caching for persistent 4xx** (404/410/451 → 5-minute TTL when upstream is silent on Cache-Control). Long enough to absorb favicon / telemetry / dev-tools-probe storms; short enough that transient 404s self-heal. Origin-stated max-age still wins when present. **Schema migration**: cache sheet grows 7 → 8 columns (added `Z` flag). Existing 7-column rows read back with `Z = undefined` (falsy → falls through the not-gzipped branch) — fully backward-compatible, no user action required. **Verified locally** on top of v1.9.18 / main: - `node --check Code.gs` clean - `cargo build --bins --lib` clean - `cargo test --lib --release`: 208/208 (no client-side change so this is a sanity check only) Behavior changes flagged in the PR body for users relying on the old "everything cached for 24h" default — the 5xx-never-cached fix is intentional, the 4xx 5-min default is overrideable via origin Cache-Control. Squash-merging. Reviewed via Anthropic Claude. Co-Authored-By: dazzling-no-more <noreply@github.com> Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Apps Script source
Three deploy-ready Apps Script files live here. They all speak the same {k, m, u, h, b, ct, r} wire protocol with mhrv-rs, so the client just points its script_id at whichever deployment you want — no mode change required.
Variants and origins
-
Code.gs— standard relay. Verbatim mirror of upstream. Apps Script does the outbound fetch itself. This is the default choice for most users.- Upstream: https://github.com/masterking32/MasterHttpRelayVPN/blob/python_testing/apps_script/Code.gs
- Credit: @masterking32. We do not modify this file.
- The mirror lives here so that (a) users on networks where
raw.githubusercontent.comis unreachable can still deploy from agit clone/ ZIP, and (b) we have a snapshot to diff against if upstream changes silently break the informal relay protocol.
-
CodeFull.gs— superset ofCode.gsthat additionally proxies raw-TCP / UDP viatunnel-node(used bymode: "full"). Maintained in this repo — written for this Rust port and not present upstream. Deploy this if you want full-tunnel mode; details in the file's header comment. -
Code.cfw.gs— Apps Script becomes a thin auth+forward layer; the actual outbound fetch happens on a Cloudflare Worker you also deploy (assets/cloudflare/). Derivative work — not unmodified upstream. The pattern of forwarding through a Cloudflare Worker came from denuitt1/mhr-cfw; this file inherits hardening fromCode.gs(decoy-on-bad-auth, fail-closed sentinels) and adds chunked batch forwarding (Promise.allon the Worker side,ceil(N/40)GAS calls per batch) that the upstreammhr-cfwdoes not have. Faster per-call latency, worse YouTube long-form, no fix for Cloudflare anti-bot. Readassets/cloudflare/README.mdbefore choosing this one.
What you must edit before deploying
For every variant: change AUTH_KEY from its placeholder to a strong secret, and use that same string in your mhrv-rs config's auth_key. Code.cfw.gs additionally requires setting WORKER_URL to your deployed Cloudflare Worker URL; CodeFull.gs additionally requires TUNNEL_SERVER_URL and TUNNEL_AUTH_KEY for the tunnel-node leg.