@mehdimalekidev reported `Content Encoding Error` when ChatGPT was routed
through Apps Script + exit-node. Root cause:
1. Browser → Apps Script with `Accept-Encoding: gzip, br` (default).
2. Apps Script forwards header to exit-node.
3. exit-node calls `fetch(destination)` which **auto-decompresses** the
response body (Deno / Bun / Node `fetch()` does this by default).
4. `resp.arrayBuffer()` returns **plain decompressed bytes** but
`resp.headers` still has `Content-Encoding: gzip` from the destination.
5. exit-node forwards both — Apps Script + Rust client pass them through —
browser sees `Content-Encoding: gzip` on plain bytes → "Content Encoding
Error: invalid or unsupported form of compression".
Fix: strip `Content-Encoding` and `Content-Length` from the response
headers before returning to the relay. The Apps Script + Rust transport
layer reframes the wire body anyway, so neither header is meaningful to
forward end-to-end.
Affects ChatGPT (gzip), Claude (br), Reddit (gzip), and any other
compressed exit-node-routed destination — the fix makes them all work.
No new test (this fixes a regression that would only show in a real
fetch path; mocking auto-decompression behavior is fragile). Manual
verification: tested ChatGPT through exit-node, response renders normally
in Firefox.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@BehroozAmoozad reported the docs reference a wrapper.ts file that
doesn't exist in the repo — the README.md / README.fa.md "Your own VPS"
row pointed at a wrapper.ts the user was supposed to write themselves.
For non-JavaScript users, this was a hard blocker.
Adds assets/exit_node/wrapper.ts that:
- Imports the handler from exit_node.ts directly (so editing the PSK in
exit_node.ts is the only setup step).
- Auto-detects Deno, Bun, or Node 22+ at runtime and uses the matching
HTTP server primitive (Deno.serve / Bun.serve / node:http).
- Reads PORT, HOST, CERT_FILE, KEY_FILE from env.
- Defaults to plain HTTP on port 8443 and recommends a reverse proxy
(Caddy / nginx / Cloudflare Tunnel) for TLS termination — the path
most VPS setups already have.
- Optional standalone TLS via CERT_FILE + KEY_FILE for users without
a reverse proxy.
README.md + README.fa.md updated to link the file directly and adjust
the deno run command to include the new --allow-env / --allow-read
permissions the wrapper needs.
No code changes — assets-only.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The val.town founder asked us not to promote using their service. This
commit removes every val.town reference from the codebase and rewrites
the exit-node guides to be platform-agnostic.
Changes:
- Renamed assets/exit_node/valtown.ts → assets/exit_node/exit_node.ts.
TypeScript itself is unchanged — same web-standard Request/Response/
fetch API that runs on any serverless runtime.
- Rewrote assets/exit_node/README.md and README.fa.md to recommend
Deno Deploy as the primary host for users who want a free serverless
TS endpoint, with fly.io and your-own-VPS as alternatives. CF Workers
is explicitly called out as not-helpful (CF outbound is still on
CF's flagged IP space).
- Updated all val.town mentions in source comments (src/config.rs,
src/domain_fronter.rs, src/bin/ui.rs) to neutral wording.
- Updated config.exit-node.example.json `_comment` strings and the
example URL.
- Updated main README.md FAQ entries (Persian + English) and
docs/guide.md / docs/guide.fa.md.
- Old changelog files (v1.9.4 / v1.9.5 / v1.9.9) had val.town mentions
retroactively replaced too — same redaction principle.
- Bumped to v1.9.10 with a changelog noting the rename + Telegram
channel brief format from earlier today.
Users who already have an exit node deployed (on whichever host they
picked) don't need to change anything — the wire protocol is identical
and the renamed script is byte-identical to the old one.
Tests: 179 lib + 35 tunnel-node green.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two changes addressing user-reported issues today:
1. Exit-node feature ported from upstream masterking32@464a6e1d, with
hardening. Cloudflare-protected sites (chatgpt.com, claude.ai,
grok.com, x.com, openai.com) flag Google datacenter IPs as bots and
return Turnstile / CAPTCHA / 502 challenges. Apps Script's UrlFetchApp
exits from those IPs, so v1.9.3 surfaces these as "Relay error: json:
key must be a string..." with no apps_script-mode workaround.
Now a small TypeScript HTTP endpoint (assets/exit_node/valtown.ts)
deployed on val.town / Deno Deploy sits between Apps Script and the
destination. Chain: client → Apps Script (Google IP) → val.town
(non-Google IP) → destination. Destination sees val.town's IP, no
CF challenge.
Config:
"exit_node": {
"enabled": true,
"relay_url": "https://...web.val.run",
"psk": "<openssl rand -hex 32>",
"mode": "selective",
"hosts": ["chatgpt.com", "claude.ai", "x.com", "grok.com", "openai.com"]
}
Hardening over upstream: PSK fail-closed if still placeholder (fresh
deploy can't be open relay), loop guard (refuses fetch of own host),
explicit 503 on misconfigured. Fallback to direct Apps Script on exit
node failure (CF-affected sites fail, others keep working). Setup
docs in English + Persian at assets/exit_node/README*.md. Example
config at config.exit-node.example.json.
2. Removed the legacy `telegram` job from release.yml. With
TELEGRAM_NOTIFY_ENABLED repo var set to true, every release was
producing two duplicate APK posts on the main Telegram channel: the
old bundled-APK-on-main job AND the newer per-file files-channel
posts (telegram-publish-files.yml). Only the per-file flow is wanted.
Legacy job and its helper telegram_release_notify.py are gone.
Recoverable from git log if anyone needs the bundled pattern back.
169 mhrv-rs lib tests + 33 tunnel-node tests + UI build clean.