mirror of
https://github.com/therealaleph/MasterHttpRelayVPN-RUST.git
synced 2026-05-17 21:24:48 +03:00
docs(maintainer): add maintainer knowledge base for contributors and automated agents
This commit is contained in:
@@ -0,0 +1,18 @@
|
|||||||
|
# Maintainer knowledge base
|
||||||
|
|
||||||
|
Project-internal knowledge base for triaging issues, reviewing PRs, cutting releases, and writing user-facing replies in the project's voice. Treat this as canonical context for any maintenance work — local or automated.
|
||||||
|
|
||||||
|
## Read order
|
||||||
|
|
||||||
|
Start with `SKILL.md` for orientation, conventions, and pointers. Then read references lazily as relevant to the current task:
|
||||||
|
|
||||||
|
- `references/architecture.md` — apps_script vs Full mode, MITM CA, tunnel-node, AUTH_KEY/TUNNEL_AUTH_KEY/DIAGNOSTIC_MODE, SNI rewriting, Apps Script's hidden constraints
|
||||||
|
- `references/issue-patterns.md` — recurring user issue patterns with diagnostic procedures and canonical reply structures
|
||||||
|
- `references/diagnostic-taxonomy.md` — six candidate causes for the placeholder body, DIAGNOSTIC_MODE disambiguator
|
||||||
|
- `references/workflow-conventions.md` — reply marker, Persian/English match rule, changelog format, commit messages, close reasons
|
||||||
|
- `references/release-workflow.md` — Cargo.toml → tag → Telegram pipeline
|
||||||
|
- `references/contributors.md` — core contributor roles + their substantive PRs
|
||||||
|
- `references/roadmap.md` — current and upcoming release batches
|
||||||
|
- `references/persian-templates.md` — adaptable Persian reply templates and standardized phrasings
|
||||||
|
- `assets/changelog-template.md` — starter template for a new `docs/changelog/vX.Y.Z.md`
|
||||||
|
- `assets/reply-marker.md` — the standard reply footer
|
||||||
@@ -0,0 +1,114 @@
|
|||||||
|
# mhrv-rs maintenance
|
||||||
|
|
||||||
|
This document encodes the project context, recurring patterns, and conventions needed to ship code, triage issues, and respond to users effectively. It is the entry point to the broader knowledge base in `references/`.
|
||||||
|
|
||||||
|
## Why this matters
|
||||||
|
|
||||||
|
mhrv-rs is **infrastructure for circumvention**. The bulk of the userbase is in Iran — under one of the world's heaviest internet censorship regimes — using this tool to reach YouTube, Wikipedia, Telegram, GitHub, news sites, banking, and (critically) to communicate with family abroad. A non-trivial fraction of users are in Russia, China, Belarus, and other censored networks, but Iran dominates the issue tracker.
|
||||||
|
|
||||||
|
The architecture's importance is the architecture itself: by routing traffic through Google Apps Script, the user's ISP only sees encrypted HTTPS to Google IPs (`216.239.38.120` etc.) — the exact same fingerprint as `www.google.com`. ISPs that block conventional VPNs are forced to either let mhrv-rs through or break Google access for the entire country. This asymmetry is what makes the project work, and it shapes every architectural decision.
|
||||||
|
|
||||||
|
When responding to a Persian-language issue, the responder is often the only English-speaking maintainer the reporter has access to. Be clear, generous, and specific. When shipping a release, you're shipping it to people for whom the alternative is not "use a different tool" but "lose internet access". This drives the project's bias toward shipping over polish, toward backwards-compatible defaults, and toward documenting workarounds even when the proper fix is months away.
|
||||||
|
|
||||||
|
## Working directory and conventions
|
||||||
|
|
||||||
|
This is a standard Rust project. `cd` into your local clone before running git/gh/cargo commands, or use absolute paths. Reply markdown files for `gh issue comment --body-file` are conventionally written to a temporary file (e.g., `/tmp/...`) before posting, to avoid HEREDOC quoting issues with backticks and `$()` substitutions.
|
||||||
|
|
||||||
|
## Reference files (read as needed)
|
||||||
|
|
||||||
|
This knowledge base is structured for progressive disclosure. The body below covers conventions and reflexes; the reference files have the deep context for specific tasks. Read them lazily — only the ones relevant to what you're doing.
|
||||||
|
|
||||||
|
- **`references/architecture.md`** — Read when explaining the system to a user, debugging unfamiliar log patterns, or making an architectural decision. Covers domain fronting, apps_script vs Full mode, MITM CA, tunnel-node, the AUTH_KEY/TUNNEL_AUTH_KEY/DIAGNOSTIC_MODE distinction, SNI rewriting, and `google_ip` rotation.
|
||||||
|
- **`references/issue-patterns.md`** — Read when triaging a new issue. Catalogs the most common user-reported issue patterns with diagnostic procedures and canonical reply structures.
|
||||||
|
- **`references/diagnostic-taxonomy.md`** — Read when a user shows a failure log with `no json in batch response` or HTML body. The six candidate causes for the placeholder body, what each looks like, and how `DIAGNOSTIC_MODE=true` disambiguates them.
|
||||||
|
- **`references/workflow-conventions.md`** — Read when writing a reply, changelog, or commit message. Reply marker, Persian-vs-English language convention, changelog format, semver discipline.
|
||||||
|
- **`references/release-workflow.md`** — Read when cutting a release. Cargo.toml bump → changelog → commit → tag → push, then auto-fired CI handles the rest (release builds + Telegram channel publishing).
|
||||||
|
- **`references/contributors.md`** — Read when interacting with named contributors. Each top contributor has a domain they specialize in.
|
||||||
|
- **`references/roadmap.md`** — Read when categorizing a feature request. Current and upcoming release batches.
|
||||||
|
- **`references/persian-templates.md`** — Read when writing a Persian reply. Common phrases and full-paragraph templates for the most-repeated Persian-language situations.
|
||||||
|
- **`assets/changelog-template.md`** — Use as the starting template when creating a new `docs/changelog/vX.Y.Z.md` file.
|
||||||
|
- **`assets/reply-marker.md`** — The exact reply footer to append to every issue/PR comment.
|
||||||
|
|
||||||
|
## Conventions to internalize
|
||||||
|
|
||||||
|
These show up so frequently they should be memorized rather than looked up each time.
|
||||||
|
|
||||||
|
### The reply marker
|
||||||
|
|
||||||
|
Every substantive issue or PR comment ends with this exact footer (with a literal `---` HR before it):
|
||||||
|
|
||||||
|
```
|
||||||
|
---
|
||||||
|
<sub>[reply via Anthropic Claude | reviewed by @therealaleph]</sub>
|
||||||
|
```
|
||||||
|
|
||||||
|
This is non-negotiable. Users in this community recognize the marker. It signals that the reply was drafted by Claude and reviewed by the maintainer before posting. Don't omit it, don't paraphrase it, don't translate "reviewed by" into Persian.
|
||||||
|
|
||||||
|
### Persian or English: match the user
|
||||||
|
|
||||||
|
If the user wrote in Persian, reply in Persian. If they wrote in English, reply in English. If they mixed (common), match the dominant language. Never assume Iranian users want English — many are more comfortable in Persian and the message lands better in their language.
|
||||||
|
|
||||||
|
Code blocks, command examples, technical terms (`AUTH_KEY`, `script_id`, `parallel_concurrency`), URLs, and the reply marker always stay in their original Latin form. Don't translate them.
|
||||||
|
|
||||||
|
### Public artifact tone
|
||||||
|
|
||||||
|
Anything that goes into the public repo — issue replies, PR comments, commit messages, PR descriptions, changelogs — is full prose, written warmly and clearly. Persian or English, adjust to the user. Iranian users especially read carefully and brevity reads as cold or dismissive in this context. Use full sentences. Explain reasoning. Be patient.
|
||||||
|
|
||||||
|
### Semver discipline
|
||||||
|
|
||||||
|
The project uses `vX.Y.Z` strictly:
|
||||||
|
- **X (major)** — currently `1`. Bump only on a true ABI/protocol break with the Apps Script side.
|
||||||
|
- **Y (minor)** — feature batch. Bump when shipping a coherent set of features (e.g. v1.7 → v1.8).
|
||||||
|
- **Z (patch)** — small fix or single-feature addition that doesn't justify a minor bump. Most releases are patch bumps.
|
||||||
|
|
||||||
|
Patch releases (v1.8.1, v1.8.2, v1.8.3) ship continuously — every time something user-visible lands. Don't sit on completed work; releases are cheap and Iranian users who ask "when's the fix shipping?" deserve "in the next 30 minutes" not "next week". The release CI is fast (~30 min from tag push to Telegram publish).
|
||||||
|
|
||||||
|
### Persian-then-English changelog
|
||||||
|
|
||||||
|
Every changelog file in `docs/changelog/vX.Y.Z.md` follows this exact format:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
<!-- see docs/changelog/v1.1.0.md for the file format: Persian, then `---`, then English. -->
|
||||||
|
• [bullet 1 in Persian]
|
||||||
|
• [bullet 2 in Persian]
|
||||||
|
---
|
||||||
|
• [same bullet 1 in English]
|
||||||
|
• [same bullet 2 in English]
|
||||||
|
```
|
||||||
|
|
||||||
|
Persian comes first because the userbase is majority-Persian. The English version is for international contributors and the public repo audience. Both versions cover the same content but are written natively in each language — not machine-translated.
|
||||||
|
|
||||||
|
### When to close issues
|
||||||
|
|
||||||
|
Close immediately:
|
||||||
|
- **Resolved** — user confirmed fix works (`gh issue close N --reason completed`)
|
||||||
|
- **Duplicate** — point to canonical thread (`gh issue close N --reason "not planned"`)
|
||||||
|
- **Architectural limit** — feature can't be implemented due to Apps Script restrictions (close with explanation, mark as `not planned`)
|
||||||
|
|
||||||
|
Keep open:
|
||||||
|
- **Tracking** — issue serves as canonical reference for a roadmap item (e.g., #313 for ISP throttle, #300 for SABR cliff, #420 for dual-VPS docs)
|
||||||
|
- **Awaiting user verification** — a fix/workaround was posted, waiting for user to confirm
|
||||||
|
- **Active diagnostic** — back-and-forth with user gathering data
|
||||||
|
|
||||||
|
When closing as duplicate, always include the canonical issue number in the close comment so future readers can navigate.
|
||||||
|
|
||||||
|
## DOPR (Daily Open PR + Issue Triage) cycle
|
||||||
|
|
||||||
|
For "do DOPR", "check issues", "issues, prs", or similar requests, the workflow is:
|
||||||
|
|
||||||
|
1. **List open PRs**: `gh pr list --state open --limit 20`
|
||||||
|
2. **List recently-updated issues**: `gh issue list --state open --limit 30 --search "sort:updated-desc"`
|
||||||
|
3. **For each PR**: review the diff, check CI, decide merge/comment/decline. New PRs from new accounts that look like supply-chain-pattern (typosquat, "update requirements.txt" with weird deps, rebrand-and-replace) get declined politely. Substantive code from known contributors generally gets merged after a local `cargo test --lib` + build. See `references/contributors.md` for who's known.
|
||||||
|
4. **For each issue updated since last DOPR**: read the latest comments. If there's a new user message, reply substantively (not just "thanks, will look into it"). If there's user confirmation that a fix worked, close the issue. If you've been waiting on user data and they haven't responded for several days, the issue can stay open or be closed with "Closing for now; reopen if it's still happening." (use judgment).
|
||||||
|
5. **If anything user-visible landed**: cut a patch release. Don't batch up 5 PRs into one big release — ship one at a time.
|
||||||
|
6. **For each new substantive issue**: write a real reply. Default to writing it in a temp file (e.g., `/tmp/r-<issue>-<topic>.md`) and posting via `gh issue comment N --body-file ...` (avoids HEREDOC quoting hell with backticks and `$()`).
|
||||||
|
|
||||||
|
DOPR replies should not be templated. Use the issue-patterns reference to recognize the situation, then write a reply that addresses _this user's specific report_ — their log lines, their config, their setup. Templated replies are easy to spot and erode trust.
|
||||||
|
|
||||||
|
## Operational guardrails
|
||||||
|
|
||||||
|
- **Don't merge PRs without local verification** — `git fetch && gh pr checkout N && cargo test --lib && cargo build --release`. CI doesn't run tests on PRs in this repo (only the release-drafter), so local verification is the real gate.
|
||||||
|
- **Don't push to `main` while a release is mid-flight** — `release.yml` auto-fires on tag push and races with subsequent commits. Wait for the release CI to complete before merging more PRs.
|
||||||
|
- **Don't skip the `--reason` flag on `gh issue close`** — `completed` for resolved, `not planned` for duplicates and architectural limits.
|
||||||
|
- **Don't update `docs/changelog/` for already-released versions** — the file is the historical record of what shipped. New work goes into a new file for the next version.
|
||||||
|
- **Don't share AUTH_KEYs, TUNNEL_AUTH_KEYs, or deployment IDs** that a user posted in an issue. They might think they obfuscated them, but if they didn't, don't quote them back. If you need to reference them, use `YOUR_AUTH_KEY` / `<deployment_id>` placeholders.
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
<!-- see docs/changelog/v1.1.0.md for the file format: Persian, then `---`, then English. -->
|
||||||
|
• [توضیح bullet اول به فارسی، با link به issue/PR — مثال: ([#NNN](https://github.com/therealaleph/MasterHttpRelayVPN-RUST/issues/NNN), PR [#MMM](https://github.com/therealaleph/MasterHttpRelayVPN-RUST/pull/MMM) از @contributor)]: شرح آنچه تغییر کرده + چرا اهمیت داره. اطلاعات معماری مرتبط، مقادیر default، و escape hatchها برای کاربرانی که میخواهن behavior قدیم رو نگه دارن
|
||||||
|
• [bullet دوم — همین structure: تغییر + چرا + escape hatch]
|
||||||
|
• [اگر breaking change وجود داره: **شکستگی سازگاری**: شرح breaking + migration steps]
|
||||||
|
---
|
||||||
|
• [bullet 1 in English ([#NNN](https://github.com/therealaleph/MasterHttpRelayVPN-RUST/issues/NNN), PR [#MMM](https://github.com/therealaleph/MasterHttpRelayVPN-RUST/pull/MMM) by @contributor): description of what changed and why it matters. Architectural context, default values, and escape hatch for users who want to preserve old behavior]
|
||||||
|
• [bullet 2 in English with the same structure: change + why + escape hatch]
|
||||||
|
• [if breaking change: **Breaking change**: description + migration steps]
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
---
|
||||||
|
<sub>[reply via Anthropic Claude | reviewed by @therealaleph]</sub>
|
||||||
@@ -0,0 +1,122 @@
|
|||||||
|
# Architecture
|
||||||
|
|
||||||
|
## What mhrv-rs is
|
||||||
|
|
||||||
|
mhrv-rs is the Rust port of [`masterking32/MasterHttpRelayVPN`](https://github.com/masterking32/MasterHttpRelayVPN) (Python). It's an HTTP proxy that runs locally on the user's machine (Windows / macOS / Linux / Android, with OpenWRT and Raspbian builds for sidecars) and bridges browser/app traffic out through Google Apps Script.
|
||||||
|
|
||||||
|
The architectural unlock: from the user's ISP perspective, all traffic looks like normal HTTPS to a Google IP. ISPs that censor by SNI / domain / TLS-fingerprint can't block the relay without breaking Google access for their entire customer base. ISPs that censor by destination IP can't block it either, because the destinations are Google data centers.
|
||||||
|
|
||||||
|
Apps Script's `UrlFetchApp.fetch()` is the workhorse — it's a Google-blessed API for outbound HTTPS, and Google effectively runs an open proxy to the rest of the internet on every Apps Script user's behalf.
|
||||||
|
|
||||||
|
## Two operating modes
|
||||||
|
|
||||||
|
### apps_script mode (default)
|
||||||
|
|
||||||
|
```
|
||||||
|
client app → mhrv-rs HTTP/SOCKS5 listener →
|
||||||
|
MITM (intercepts HTTPS, signs with local CA) →
|
||||||
|
POST batch to Apps Script Web App →
|
||||||
|
Apps Script's UrlFetchApp.fetch() → upstream destination →
|
||||||
|
Apps Script returns body → mhrv-rs returns to client
|
||||||
|
```
|
||||||
|
|
||||||
|
- **Code.gs** (in `assets/apps_script/Code.gs`) is the script the user deploys to their own Google account at `script.google.com`. Each deployment gets a `script_id` like `AKfycbz1abc...`.
|
||||||
|
- The MITM layer signs HTTPS leaf certs on the fly using a CA installed in the user's trust store. This lets mhrv-rs read the plaintext request, batch it through Apps Script, and return the response to the client.
|
||||||
|
- All upstream protocols are HTTP/HTTPS. **No UDP, no MTProto, no QUIC, no WebRTC.** Apps Script can't carry them.
|
||||||
|
- Per-Apps-Script-account quota: ~20,000 UrlFetchApp calls/day, 30 concurrent, 6-min per-invocation cap, 30s soft response cliff.
|
||||||
|
|
||||||
|
### Full mode
|
||||||
|
|
||||||
|
```
|
||||||
|
client app → mhrv-rs SOCKS5 →
|
||||||
|
signal/control via Apps Script (small JSON RPC) →
|
||||||
|
Apps Script calls into tunnel-node container on user's VPS →
|
||||||
|
tunnel-node opens TCP socket to upstream →
|
||||||
|
bytes flow through tunnel-node ↔ Apps Script ↔ mhrv-rs ↔ client
|
||||||
|
```
|
||||||
|
|
||||||
|
- **CodeFull.gs** (in `assets/apps_script/CodeFull.gs`) is a different Apps Script — replaces Code.gs's local-fetch with calls to a tunnel-node container.
|
||||||
|
- **tunnel-node** is a small axum-based Rust HTTP server (in `tunnel-node/`) that the user runs on their own VPS via Docker. Image: `ghcr.io/therealaleph/mhrv-tunnel-node:latest`.
|
||||||
|
- The bytes flow through the actual TCP tunnel between tunnel-node and the upstream server — Apps Script only handles the **signaling** for tunnel session lifecycle. This means Apps Script's 30s response cap doesn't apply to long-running connections (no SABR cliff). Bigger uploads/downloads work.
|
||||||
|
- Trade-off: requires a VPS ($3-5/month from Hetzner/Contabo/OVH/Parspack), more setup steps, three places to keep AUTH_KEYs in sync.
|
||||||
|
- The VPS does NOT need to be reachable from Iran directly. Apps Script (running in Google's data center) is the one that talks to the VPS, so the user's ISP only sees the user-to-Apps-Script leg, which is Google IPs.
|
||||||
|
|
||||||
|
## The three secrets
|
||||||
|
|
||||||
|
These are the constant source of user confusion. Get the names right:
|
||||||
|
|
||||||
|
| Secret | Lives where | Must match | Notes |
|
||||||
|
|--------|-------------|------------|-------|
|
||||||
|
| `AUTH_KEY` (or `auth_key` in mhrv-rs config.json) | mhrv-rs `config.json` ↔ `Code.gs`/`CodeFull.gs` | Both ends | Per-deployment user secret; protects against random people hitting the user's deployment URL. Editing it in Code.gs without **redeploying as a new version** in Apps Script is the single most common user mistake. |
|
||||||
|
| `TUNNEL_AUTH_KEY` | `CodeFull.gs` ↔ tunnel-node container env var | Both ends | Full mode only. Env var name is **literally `TUNNEL_AUTH_KEY`** — uppercase, with underscores, exact string. Several users have written `MHRV_AUTH_KEY` (wrong) or `Tunnel` (wrong); the env var is case-sensitive in Linux/Docker and any deviation falls back to the default `changeme`. |
|
||||||
|
| `DIAGNOSTIC_MODE` | `Code.gs` and `CodeFull.gs` (constant at top) | n/a — local toggle | When `false` (default), the script returns a benign HTML decoy (`"The script completed but did not return anything"`) for bad-auth requests, mimicking Apps Script's own placeholder. When `true`, returns explicit JSON `{"e":"unauthorized"}`. The decoy mode is anti-active-probing defense (#357 pattern); diagnostic mode is for setup. |
|
||||||
|
|
||||||
|
## Apps Script's hidden constraints
|
||||||
|
|
||||||
|
These are constraints Google enforces on Apps Script's `UrlFetchApp.fetch()` that shape what mhrv-rs can and can't do:
|
||||||
|
|
||||||
|
1. **Self-loop restriction** — `UrlFetchApp.fetch()` blocks calls to `*.google.com`, `*.googleapis.com`, `*.gstatic.com`, `*.googleusercontent.com`. **Google services are unreachable through apps_script mode by design.** Includes `gmail.com`, `meet.google.com`, `colab.research.google.com`, `drive.google.com`, `script.google.com` itself (ironic — you can't proxy your way to manage your own deployment). Workaround for users with VPS: dual-routing in xray (route Google direct from VPS, everything else through mhrv-rs). Without VPS, no workaround — point users at #420.
|
||||||
|
2. **30-second response cliff** — Apps Script Web Apps have a soft cap of 30s on the response. Long downloads or video streams (YouTube SABR, large file downloads >50 MB through MITM) get truncated. Tracked as #300 (SABR cliff). v1.9.0 xmux roadmap aims to mitigate by splitting across deployments.
|
||||||
|
3. **6-minute per-invocation cap** — hard limit. After this, `UrlFetchApp.fetch()` throws and Apps Script kills the request.
|
||||||
|
4. **30 concurrent executions per Apps Script account** — affects users who put the same `script_id` under heavy load. Lower `parallel_concurrency` in mhrv-rs config to avoid hitting this.
|
||||||
|
5. **Daily quota: 20,000 UrlFetchApp calls per Google account** — resets at 00:00 UTC. Multi-deployment rotation across multiple Google accounts is the workaround.
|
||||||
|
6. **Per-100s rolling soft quota** — undocumented but consistently observed. When tripped, returns the placeholder body (one of the 6 candidate causes for the placeholder; see `diagnostic-taxonomy.md`).
|
||||||
|
7. **Localized error pages** — Apps Script returns its placeholder body in the locale of the deploying account or origin IP. For Iranian users, this means a Persian HTML page. v1.8.3 detection now distinguishes this case.
|
||||||
|
|
||||||
|
## The MITM CA
|
||||||
|
|
||||||
|
To intercept HTTPS in apps_script mode, mhrv-rs runs a per-machine CA:
|
||||||
|
|
||||||
|
- Generated on first run, stored at `<data_dir>/ca/ca.crt` and `ca.key`.
|
||||||
|
- Installed into the user's OS trust store via the `cert_installer` module.
|
||||||
|
- On Windows: user-trust store via `certutil -addstore`.
|
||||||
|
- On macOS: login keychain via `security`.
|
||||||
|
- On Linux: distro-specific (NSS for Firefox, system bundle for Chrome/curl).
|
||||||
|
- **On Android**: only the **user trust store**, not system. Most apps (YouTube, Gmail, Telegram, Instagram, banking) only trust the system store, so they don't see mhrv-rs. Chrome/Firefox/Edge browsers explicitly opt in to user trust and DO use mhrv-rs. This is the Android user-trust-store gotcha that drives much of the Android UX confusion. Workaround for power users: root + Magisk + MagiskTrustUserCerts module migrates user CA to system.
|
||||||
|
|
||||||
|
The `--remove-cert` CLI flag tears down the CA cleanly (uninstall from trust store + delete files). PR #121 from `dazzling-no-more` added this; lives in `src/main.rs` `remove_cert` flow.
|
||||||
|
|
||||||
|
## SNI rewriting + google_ip rotation
|
||||||
|
|
||||||
|
The TLS handshake between mhrv-rs and Apps Script does:
|
||||||
|
|
||||||
|
- **TCP connect** to `google_ip` (default `216.239.38.120` — a Google edge IP)
|
||||||
|
- **TLS SNI** = `www.google.com` (rewritten — this is what the ISP sees in cleartext)
|
||||||
|
- **HTTP Host header** = `script.google.com` (the real destination, hidden inside the encrypted tunnel)
|
||||||
|
|
||||||
|
Iran ISPs occasionally filter specific Google IPs (#313 pattern). When this happens, the user can rotate `google_ip` to another IP from `DEFAULT_GOOGLE_SNI_POOL` (the 12-entry list in `src/domain_fronter.rs`). `mhrv-rs scan-ips` is a diagnostic command that probes Google IPs from the user's network and reports which ones complete TLS handshakes.
|
||||||
|
|
||||||
|
`scan_config.json` (separate from main `config.json`) is the input for `mhrv-rs scan-ips` — users sometimes confuse the two and put the scan config where the main config should be. See `issue-patterns.md`.
|
||||||
|
|
||||||
|
## v1.8.0 anti-fingerprinting features
|
||||||
|
|
||||||
|
- **Random padding** (`_pad` field, 0-1024 bytes uniform random, base64) — defeats DPI length-distribution fingerprinting. Users on heavily-throttled ISPs can disable with `disable_padding: true` (~25% bandwidth savings) — landed in v1.8.1.
|
||||||
|
- **Auto-blacklist deployments** that timeout repeatedly (#319) — round-robin pool actively excludes failing deployments for a cooldown period. Tunable strike threshold queued for v1.8.x.
|
||||||
|
- **Decoy responses** for bad-auth requests — see `DIAGNOSTIC_MODE` above.
|
||||||
|
- **Active-probing defense** — random benign body on `doGet` requests so a probe to the deployment URL doesn't reveal that it's a relay.
|
||||||
|
|
||||||
|
## v1.8.3 features (just shipped)
|
||||||
|
|
||||||
|
- **DoH bypass** — DNS-over-HTTPS to Cloudflare/Google/Quad9/AdGuard/etc. routes around the Apps Script tunnel via plain TCP/443. Saves ~2s per DNS lookup. Default on; opt out with `tunnel_doh: true`.
|
||||||
|
- **H1 container keepalive** — 240s ping to prevent Apps Script V8 cold-start stalls. Visible win for YouTube playback after pause.
|
||||||
|
- **64 KB header cap with HTTP 431** — replaces silent socket drops that caused browser retry loops on oversized headers.
|
||||||
|
- **Spreadsheet-backed response cache** in Code.gs (opt-in via `CACHE_SPREADSHEET_ID`) — TTL-aware, Vary-aware, circular-buffer for O(1) writes. Reduces UrlFetchApp quota consumption.
|
||||||
|
|
||||||
|
## Key files in the repo
|
||||||
|
|
||||||
|
- `src/main.rs` — CLI binary entry point. `init_logging()` reads `config.log_level`. `Cmd::Test`, `Cmd::ScanIps`, etc. as subcommands.
|
||||||
|
- `src/bin/ui.rs` — UI binary entry (Windows + Android via JNI). Shares lib code via `mhrv_rs::*`. The `install_ui_tracing` function (post-v1.8.2) reads `RUST_LOG > config.log_level > info,hyper=warn`.
|
||||||
|
- `src/lib.rs` — re-exports for the lib + Android JNI shim.
|
||||||
|
- `src/domain_fronter.rs` — the SNI-rewrite TLS dialer + the `DomainFronter` orchestrator. `DEFAULT_GOOGLE_SNI_POOL` lives here.
|
||||||
|
- `src/proxy_server.rs` — HTTP/SOCKS5 listeners, dispatch logic, DoH bypass, MITM mode entry.
|
||||||
|
- `src/tunnel_client.rs` — Full mode batch client. Decoy detection + script_id-in-logs added v1.8.1; softer 6-cause message v1.8.3.
|
||||||
|
- `src/mitm/` — MITM cert manager.
|
||||||
|
- `src/cert_installer/` — per-OS trust store installation logic.
|
||||||
|
- `src/config.rs` — `Config` struct + JSON serde. Default values, validation.
|
||||||
|
- `assets/apps_script/Code.gs` and `CodeFull.gs` — server-side scripts. Edit these and tell users to redeploy as new version in Apps Script.
|
||||||
|
- `tunnel-node/` — separate Rust crate for the Full-mode VPS container. README + README.fa.md (Persian translation).
|
||||||
|
- `android/app/src/main/java/com/therealaleph/mhrv/` — Android Kotlin glue. `MhrvVpnService.kt` is the VPNService that calls into Rust via JNI. `ConfigStore.kt` is the form/preferences round-trip.
|
||||||
|
- `docs/changelog/` — versioned changelog files. Format: Persian, then `---`, then English.
|
||||||
|
- `.github/workflows/release.yml` — release CI: builds for all platforms, attaches to GitHub release.
|
||||||
|
- `.github/workflows/telegram-publish-files.yml` — fires on `workflow_run` of release.yml; posts each file individually to the Telegram channel `-1003966234444` with Persian captions, SHA-256 in caption, and a cross-link from the main channel.
|
||||||
|
- `.github/scripts/telegram_publish_files.py` — stdlib-only Python script that does the actual Telegram posting (no `requests` dep so it works in minimal CI runners).
|
||||||
@@ -0,0 +1,126 @@
|
|||||||
|
# Contributor ecosystem
|
||||||
|
|
||||||
|
The project's substantive contributors fall into a few specialty domains. Knowing who-does-what lets you tag the right reviewer, weight feedback appropriately, and route new design decisions to the people most likely to have informed opinions.
|
||||||
|
|
||||||
|
## Project owner
|
||||||
|
|
||||||
|
### @therealaleph
|
||||||
|
|
||||||
|
Maintainer. Final authority on architectural decisions, release timing, what merges. Persian/English bilingual. Replies that go through Claude carry the marker `[reply via Anthropic Claude | reviewed by @therealaleph]` and are reviewed before posting.
|
||||||
|
|
||||||
|
## Core community contributors
|
||||||
|
|
||||||
|
These are the contributors whose substantive PRs and reports have shaped the project's roadmap. When designing features that touch their domain, tag them for review.
|
||||||
|
|
||||||
|
### @w0l4i
|
||||||
|
|
||||||
|
**Domain**: deep diagnostic feedback, architectural insight, persistence on hard bugs.
|
||||||
|
|
||||||
|
**Notable contributions**:
|
||||||
|
- Drove the v1.8.1 → v1.8.2 → v1.8.3 evolution of decoy detection. Reported the false-positive in v1.8.1 that led to the 4-cause taxonomy (then 5-cause, then 6-cause).
|
||||||
|
- Reported the Persian-localized quota body case (#404) after multiple iterations through wrong hypotheses (third-party relay → Iranian VPS appliance → Hetzner DE → Apps Script account locale).
|
||||||
|
- Suggested the v1.8.x "per-deployment auto-throttle" feature (AIMD style) with detailed rationale.
|
||||||
|
- Suggested the v1.9.0 xmux roadmap items: byte-range slipstreaming across deployments, MTU/packet-size optimization, per-deployment burst limits.
|
||||||
|
- Drove the v1.8.x DNS architecture redesign by pointing out that Iranian DNS providers (Shecan, 403) perform DNS hijacking and poisoning — they cannot be trusted as privacy-preserving alternatives (see #449).
|
||||||
|
|
||||||
|
**How to engage**:
|
||||||
|
- Reports are detailed and self-correct fast as data comes in
|
||||||
|
- Setups tend to be advanced (multiple deployments, Hetzner VPS, Full mode)
|
||||||
|
- Tag as a core reviewer for v1.9.0 xmux design issue when filed
|
||||||
|
- Communication: English
|
||||||
|
|
||||||
|
### @2bemoji
|
||||||
|
|
||||||
|
**Domain**: roadmap design discussions, particularly for QUIC blocking and DNS optimization.
|
||||||
|
|
||||||
|
**Notable contributions**:
|
||||||
|
- Drove the design of `block_quic` 3-state UI toggle (off / drop / reject with ICMP unreachable for instant Happy Eyeballs failover) in #361 / #377
|
||||||
|
- Surfaced the mobile-accessibility framing for `block_quic` (config-only is "Linux desktop only" for users who can't easily edit Android's `/data/data/...` config)
|
||||||
|
|
||||||
|
**How to engage**:
|
||||||
|
- Tag for Android UI batch decisions, especially anything touching QUIC / DNS / network-layer toggles
|
||||||
|
- Tag for v1.9.0 xmux design as a core reviewer
|
||||||
|
- Communication: English
|
||||||
|
|
||||||
|
### @ipvsami / Sam Ashouri
|
||||||
|
|
||||||
|
**Domain**: advanced Full mode setups, dual-VPS topologies, account suspension reports.
|
||||||
|
|
||||||
|
**Notable contributions**:
|
||||||
|
- Reported the Iranian-VPS xray entry topology in #420 (Iranian VPS as xray entry, German VPS as tunnel-node exit) — drove the dual-routing-xray design discussion
|
||||||
|
- Reported the Google account flag pattern in #421 (phone-less new accounts, "action required" notifications, Workspace landing HTML on flagged deployments) — drove the v1.8.x detection for the 6th cause in the diagnostic taxonomy
|
||||||
|
|
||||||
|
**How to engage**:
|
||||||
|
- Comfortable with VPS / xray / network routing; explanations can assume that level
|
||||||
|
- Tag for v1.9.0 xmux design as a core reviewer
|
||||||
|
- Communication: English
|
||||||
|
|
||||||
|
### @dazzling-no-more
|
||||||
|
|
||||||
|
**Domain**: code contributor — substantive Rust PRs.
|
||||||
|
|
||||||
|
**Notable contributions**:
|
||||||
|
- PR #121 (`--remove-cert` flag for clean CA teardown)
|
||||||
|
- PR #359 (Google Drive queue tunnel mode — community-testing, awaiting cleanup confirmation)
|
||||||
|
- PR #438 (H1 container keepalive + 431 oversized headers + clearer port-collision message — merged in v1.8.3)
|
||||||
|
- PR #439 (DoH bypass for Cloudflare/Google/Quad9/etc. on TCP/443 — merged in v1.8.3)
|
||||||
|
- PR #446 (tunnel-node long-poll raised to 15s, adaptive straggler settle — merged in v1.8.4)
|
||||||
|
|
||||||
|
**How to engage**:
|
||||||
|
- PRs tend to be self-contained with tests and clean diffs
|
||||||
|
- Address review feedback substantively — they iterate based on reviewer comments
|
||||||
|
- Tag for v1.9.0 xmux design as a core reviewer (could potentially contribute the implementation)
|
||||||
|
- Communication: English
|
||||||
|
|
||||||
|
### @euvel
|
||||||
|
|
||||||
|
**Domain**: code contributor — Apps Script (Code.gs) features.
|
||||||
|
|
||||||
|
**Notable contributions**:
|
||||||
|
- Designed the spreadsheet-backed response cache (#400 design discussion → PR #443 implementation)
|
||||||
|
- All 5 review suggestions from the design discussion implemented in PR #443: TTL-aware caching, 35 KB body-size gate, header rewriting on hit, circular buffer for O(1) writes, Vary-aware compound cache keys
|
||||||
|
|
||||||
|
**How to engage**:
|
||||||
|
- Apps Script JavaScript expertise; consider tagging for any future Code.gs changes
|
||||||
|
- Communication: English
|
||||||
|
|
||||||
|
## Adjacent projects
|
||||||
|
|
||||||
|
### @masterking32
|
||||||
|
|
||||||
|
Original Python project (`masterking32/MasterHttpRelayVPN`). mhrv-rs is the Rust port; the project periodically cherry-picks stability/feature commits from masterking32. PR #438 in v1.8.3 was a batch of three such cherry-picks. Not a direct contributor here, but the project's design parent.
|
||||||
|
|
||||||
|
### @denuitt1
|
||||||
|
|
||||||
|
Maintainer of `denuitt1/mhr-cfw` — Cloudflare Workers backend that aims to be Apps Script-compatible. Independent project, not officially endorsed. Tracked in #380 / #393 for compatibility audit. Not a direct contributor here.
|
||||||
|
|
||||||
|
### @g3ntrix, @mehrad-mz
|
||||||
|
|
||||||
|
Authors of forks/branches on the Python project that occasionally have valuable commits to cherry-pick (see #430 for the audit list).
|
||||||
|
|
||||||
|
## Tagging conventions
|
||||||
|
|
||||||
|
When tagging in a comment:
|
||||||
|
|
||||||
|
- Reviewer requests: "@dazzling-no-more — would you mind reviewing this approach?"
|
||||||
|
- Cross-references: "see [#404](https://github.com/therealaleph/MasterHttpRelayVPN-RUST/issues/404) where @w0l4i described this"
|
||||||
|
- Recognition: "this drove the design — thanks @euvel for the detailed initial proposal"
|
||||||
|
- For v1.9.0 xmux design issue specifically (when it's filed): tag @w0l4i, @2bemoji, @ipvsami, @dazzling-no-more, @euvel as core reviewers
|
||||||
|
|
||||||
|
Don't ping people gratuitously; each ping should have a clear ask or recognition.
|
||||||
|
|
||||||
|
## Project history context
|
||||||
|
|
||||||
|
The project predates this repo as `masterking32/MasterHttpRelayVPN` (Python). The Rust port was started for performance + cross-platform binary distribution. Apps Script protocol stayed compatible across both, and we periodically cherry-pick from upstream Python. v1.7.x represented the initial port stabilization; v1.8.x is the "DPI evasion + diagnostics + community-contribution batch"; v1.9.0 will be the xmux flagship.
|
||||||
|
|
||||||
|
Canonical "long" issues for context:
|
||||||
|
- #313 — Iran ISP throttle, primary tracking issue
|
||||||
|
- #300 — SABR cliff, primary tracking for video streaming limit
|
||||||
|
- #310 — VPS setup help, primary tracking for setup questions
|
||||||
|
- #333 — VPS / Full mode / Iranian-network workarounds
|
||||||
|
- #420 — dual-VPS topology, primary tracking for advanced Full mode
|
||||||
|
- #382 — Cloudflare error patterns
|
||||||
|
- #325 — community-shared deployment workflow
|
||||||
|
- #361 / #377 — Android UI batch + QUIC blocking design
|
||||||
|
- #369 — v1.9.0 xmux design (RFC, not yet filed as the formal design issue)
|
||||||
|
- #449 — DNS architecture redesign (post-Shecan correction)
|
||||||
@@ -0,0 +1,161 @@
|
|||||||
|
# Diagnostic taxonomy: the placeholder body
|
||||||
|
|
||||||
|
## What this is
|
||||||
|
|
||||||
|
Multiple distinct conditions cause Apps Script (or our own scripts on Apps Script) to return an HTML body that mhrv-rs's batch parser sees as `bad response: no json in batch response: <body prefix>`. Through user reports and iteration we've narrowed the body strings to **6 candidate causes**. Distinguishing them requires both client-side detection (string-match on body content) and server-side disambiguation (`DIAGNOSTIC_MODE` flag in Code.gs).
|
||||||
|
|
||||||
|
This taxonomy is the post-mortem evolution of v1.8.0 → v1.8.1 → v1.8.2 → v1.8.3 detection. v1.8.1 falsely asserted "AUTH_KEY mismatch" on body match; v1.8.2 softened to enumerate 4 candidates; v1.8.3 added the Persian-localized cause and the Workspace landing HTML cause for account-flagged deployments — bringing the count to 6.
|
||||||
|
|
||||||
|
## The 6 candidate causes
|
||||||
|
|
||||||
|
### 1. AUTH_KEY mismatch (intentional decoy)
|
||||||
|
|
||||||
|
**Body**:
|
||||||
|
```html
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head><title>Web App</title></head>
|
||||||
|
<body><p>The script completed but did not return anything.</p></body>
|
||||||
|
</html>
|
||||||
|
```
|
||||||
|
|
||||||
|
**Source**: Our `Code.gs` / `CodeFull.gs` returns this when `request.k !== AUTH_KEY` and `DIAGNOSTIC_MODE = false`. It mimics Apps Script's stock placeholder for empty-return scripts.
|
||||||
|
|
||||||
|
**Trigger**: User edited AUTH_KEY in Apps Script editor but didn't redeploy as new version, OR user has different AUTH_KEY in `config.json` than in `Code.gs`, OR user is using Code.gs deployment ID with `mode: full` (which expects CodeFull.gs).
|
||||||
|
|
||||||
|
**Disambiguator**: Set `DIAGNOSTIC_MODE = true` in Code.gs / CodeFull.gs + redeploy as new version. Then this case returns `{"e":"unauthorized"}` (explicit JSON) instead of the HTML. The other 5 cases are independent of DIAGNOSTIC_MODE and still return their natural body.
|
||||||
|
|
||||||
|
**Fix**: Align AUTH_KEY values + redeploy as new version.
|
||||||
|
|
||||||
|
### 2. Apps Script execution timeout
|
||||||
|
|
||||||
|
**Body**: same `"The script completed but did not return anything"` HTML, but emitted by Apps Script itself (not our script) when the execution exceeded the per-invocation cap.
|
||||||
|
|
||||||
|
**Source**: Apps Script's runtime kills the script after 6-min hard cap or 30s soft cap on Web App responses, then serves the placeholder body.
|
||||||
|
|
||||||
|
**Trigger**: Slow upstream destination, large response payload, network stall mid-fetch.
|
||||||
|
|
||||||
|
**Disambiguator**: With `DIAGNOSTIC_MODE = true`, AUTH_KEY mismatch (cause 1) goes away; if the placeholder body still appears for some batches, it's likely cause 2/3/4/5/6.
|
||||||
|
|
||||||
|
**Fix**: Lower `parallel_concurrency` in `config.json`, retry, accept some intermittent failures.
|
||||||
|
|
||||||
|
### 3. Apps Script soft-quota tear
|
||||||
|
|
||||||
|
**Body**: same placeholder HTML. Sometimes a different short HTML page mentioning Apps Script's quota system.
|
||||||
|
|
||||||
|
**Source**: Apps Script's per-100s rolling soft quota or per-account daily quota hit. Apps Script kills the request mid-execution.
|
||||||
|
|
||||||
|
**Trigger**: Account-aggregate UrlFetchApp throughput exceeded per-100s threshold (~30 concurrent or so). Common with multi-device single-deployment users during page load events (browsers fire 50+ requests in a burst).
|
||||||
|
|
||||||
|
**Disambiguator**: Same as 2 — DIAGNOSTIC_MODE rules out AUTH_KEY but doesn't distinguish 2 from 3 from 4. Check the per-script_id error rate over a few minutes — if a deployment has 30%+ failure rate during peak browser activity but works fine when idle, it's quota-related (3 or possibly 5).
|
||||||
|
|
||||||
|
**Fix**: Lower `parallel_concurrency`, add more deployments to `script_ids` rotation, distribute deployments across multiple Google accounts.
|
||||||
|
|
||||||
|
### 4. Iran ISP-side response truncation
|
||||||
|
|
||||||
|
**Body**: typically truncated mid-stream — the body that arrives at mhrv-rs is missing the trailing JSON envelope. The early bytes look like a valid Apps Script response prefix but the request was cut by an ISP-side TCP RST mid-flight.
|
||||||
|
|
||||||
|
**Source**: Iran's ISP infrastructure (especially TCI/مخابرات) actively RST-injects on TLS connections to specific Google IPs (the #313 pattern).
|
||||||
|
|
||||||
|
**Trigger**: Network-conditional. Active throttle periods (sometimes hours, sometimes days). Worse on certain Google IPs. Worse on certain Iranian ISPs.
|
||||||
|
|
||||||
|
**Disambiguator**: Direct curl test from the user's network (see `issue-patterns.md` Pattern 3). If curl-to-Apps-Script also gets timeouts/RST, confirmed ISP-side. The HTML body in this case is partial/truncated — sometimes just `<!DOCT...` rather than the full placeholder.
|
||||||
|
|
||||||
|
**Fix**: Workarounds in Pattern 3 — `disable_padding`, rotate `google_ip`, switch network, multi-deployment, Full mode + VPS.
|
||||||
|
|
||||||
|
### 5. Apps Script Persian-localized soft-quota body
|
||||||
|
|
||||||
|
**Body**:
|
||||||
|
```html
|
||||||
|
<html lang="fa" dir="rtl">
|
||||||
|
<head>
|
||||||
|
<meta name="description" content="پردازش کلمه وب، ارائهها و صفحات گسترده">
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
May also include phrases like `از سهمیه پهنای باند مجاز فراتر رفتهاید` ("you exceeded the allowed bandwidth quota") and `مقدار انتقال داده را کمتر کنید` ("reduce data transfer volume").
|
||||||
|
|
||||||
|
**Source**: Apps Script itself. Apps Script localizes its system error pages based on the deploying Google account's locale (fa-IR for Persian) and/or the request-origin IP.
|
||||||
|
|
||||||
|
**Trigger**: Account is Persian-locale (common for Iranian users) AND hit a quota threshold (cause 3) OR an internal Google-side hiccup.
|
||||||
|
|
||||||
|
**Disambiguator**: With `DIAGNOSTIC_MODE = true`, cause 1 returns explicit JSON; if Persian HTML still appears, it's not our script — it's Apps Script's own response.
|
||||||
|
|
||||||
|
**Important**: w0l4i's case in #404 traced through several wrong hypotheses before landing here:
|
||||||
|
- Initially diagnosed as AUTH_KEY mismatch → no, mixed success/failure on same `script_id`
|
||||||
|
- Then diagnosed as third-party relay (`g.workstream.ir` looks Iranian) → no, w0l4i clarified it's his own tunnel
|
||||||
|
- Then diagnosed as Iranian VPS provider appliance → no, Hetzner Nuremberg
|
||||||
|
- Final landing: Apps Script's own Persian-localized quota response based on Google account locale
|
||||||
|
|
||||||
|
This iteration is documented because the false starts are instructive — don't lock in on the first hypothesis.
|
||||||
|
|
||||||
|
**Fix**: Same as cause 3 (it's a quota issue presenting as Persian HTML).
|
||||||
|
|
||||||
|
### 6. Workspace landing HTML for account-flagged deployments
|
||||||
|
|
||||||
|
**Body**:
|
||||||
|
```html
|
||||||
|
<html lang="fa" dir="rtl">
|
||||||
|
<head>
|
||||||
|
<meta name="description" content="پردازش کلمه وب، ارائهها و صفحات گسترده"...
|
||||||
|
<title>...</title>
|
||||||
|
```
|
||||||
|
|
||||||
|
The body is Google Workspace's landing page (the description "Word web processing, presentations, and spreadsheets" is the standard tagline for Google Docs/Sheets/Slides). It's served by Apps Script when the deployment owner's Google account is in a flagged state (post-warning, pre-suspension).
|
||||||
|
|
||||||
|
**Source**: Apps Script refuses to execute the deployed script when the owning account is restricted, and serves the Workspace landing page as a "log in" prompt instead.
|
||||||
|
|
||||||
|
**Trigger**: Account is in stage 1b or stage 2 of the suspension progression (see `issue-patterns.md` Pattern 8). Often correlates with phone-less new accounts that ignored the "action required" prompt.
|
||||||
|
|
||||||
|
**Disambiguator**: Owner of the deployment can log in to Google → see if there are pending warnings or restrictions. If yes → fix the account (add phone) or rotate the deployment to a healthier account.
|
||||||
|
|
||||||
|
**Fix**: Account-side, not config-side. Add phone verification, OR move to a different deployment owner via #325 workflow.
|
||||||
|
|
||||||
|
## v1.8.3 detection logic
|
||||||
|
|
||||||
|
```rust
|
||||||
|
// In src/tunnel_client.rs around line 893+
|
||||||
|
if err_msg.contains("The script completed but did not return anything") {
|
||||||
|
tracing::error!(
|
||||||
|
"batch failed (script {}): got the v1.8.0 decoy/placeholder body — \
|
||||||
|
could be (1) AUTH_KEY mismatch (run a direct curl probe against \
|
||||||
|
the deployment to verify), (2) Apps Script execution timeout or \
|
||||||
|
per-100s quota tear (try lowering parallel_concurrency), \
|
||||||
|
(3) Apps Script internal hiccup (transient, retry next batch), \
|
||||||
|
or (4) ISP-side response truncation (#313 pattern, try a \
|
||||||
|
different google_ip). To distinguish (1) from the rest: set \
|
||||||
|
DIAGNOSTIC_MODE=true at the top of Code.gs + redeploy as new \
|
||||||
|
version — only AUTH_KEY mismatch returns this body in diagnostic \
|
||||||
|
mode.",
|
||||||
|
sid_short
|
||||||
|
);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This is the v1.8.2 string. v1.8.3 adds detection for the Persian quota body and the Workspace landing HTML as separate paths.
|
||||||
|
|
||||||
|
## When responding to users showing this log
|
||||||
|
|
||||||
|
The right response shape is:
|
||||||
|
|
||||||
|
1. **Acknowledge** the log line they pasted
|
||||||
|
2. **Enumerate** the 6 (or 4-5 in older versions) candidate causes briefly
|
||||||
|
3. **Identify the most likely** for their specific case using context clues:
|
||||||
|
- Single-deployment user, fresh setup → likely cause 1 (AUTH_KEY)
|
||||||
|
- Mixed success/failure on same script_id → NOT cause 1 (AUTH_KEY would fail 100%)
|
||||||
|
- "Worked yesterday, broken today" → likely cause 4 (ISP throttle) or cause 8 (account flag in progression)
|
||||||
|
- High concurrency / many devices on one deployment → likely cause 3 (quota) or cause 5 (Persian quota variant)
|
||||||
|
- Persian HTML body → cause 5 or 6
|
||||||
|
- Hetzner/Iranian VPS Full-mode user → check if VPS is actually Iranian (provider appliance is real for Iranian VPS only)
|
||||||
|
4. **Give the disambiguator**: DIAGNOSTIC_MODE flip + redeploy
|
||||||
|
5. **Give the immediate workaround** appropriate to the most-likely cause
|
||||||
|
|
||||||
|
Don't claim certainty before disambiguator data. v1.8.1 over-asserted; v1.8.3 explicitly enumerates because we learned to.
|
||||||
|
|
||||||
|
## What v1.8.x roadmap is doing about this
|
||||||
|
|
||||||
|
- **Per-script_id error-category counter** — surface in CLI/UI: "deployment AKfycbz1: 95% success, 4% timeout, 1% quota, 0% auth_mismatch over last 5 min". Lets users diagnose without flipping DIAGNOSTIC_MODE.
|
||||||
|
- **Distinct error categories in client logs** — separate AUTH_KEY mismatch / timeout / quota / ISP truncation / Persian quota / Workspace landing into 6 distinct error log lines. Currently merged.
|
||||||
|
- **AIMD per-deployment auto-throttle** — automatically lower `parallel_concurrency` for deployments that hit quota too often. Find the sustainable rate per deployment without manual tuning.
|
||||||
|
|
||||||
|
These are queued for v1.8.x batch (~2-4 weeks).
|
||||||
@@ -0,0 +1,327 @@
|
|||||||
|
# Issue patterns
|
||||||
|
|
||||||
|
The repo gets the same ~15 issues over and over with different wrappers. Recognizing the pattern fast is most of the maintenance job. Each section below covers: the symptoms users describe, what's actually happening, how to diagnose, and the canonical reply structure.
|
||||||
|
|
||||||
|
## Pattern 1: AUTH_KEY mismatch (the v1.8.0 decoy body)
|
||||||
|
|
||||||
|
**Symptoms**:
|
||||||
|
- `502 Relay error: bad response: no json in: <!DOCTYPE html>...The script completed but did not return anything`
|
||||||
|
- v1.8.1+ logs say `got the v1.8.0 bad-auth decoy` (now soft-language in v1.8.3)
|
||||||
|
- Issue title often "502 error", "خطای 502", "ارور relay", or "no json in batch response"
|
||||||
|
- Often combined with: "MITM mode works but Full mode doesn't" (CodeFull.gs has different AUTH_KEY than Code.gs)
|
||||||
|
|
||||||
|
**Root cause**: The `AUTH_KEY` constant in `Code.gs` (or `CodeFull.gs`) on Apps Script doesn't match the `auth_key` field in mhrv-rs `config.json`. Apps Script returns the v1.8.0 decoy HTML.
|
||||||
|
|
||||||
|
**The hidden killer**: Apps Script does NOT auto-pickup edits to deployed scripts. Editing `const AUTH_KEY = "..."` in the Apps Script editor and clicking Save does nothing for the deployed version. The user must:
|
||||||
|
|
||||||
|
1. Apps Script web editor → **Deploy → Manage Deployments**
|
||||||
|
2. Click the deployment → pencil/Edit
|
||||||
|
3. Version dropdown → **New version**
|
||||||
|
4. Click Deploy
|
||||||
|
|
||||||
|
This redeploys with the new AUTH_KEY. Most users skip this and stay on the old version.
|
||||||
|
|
||||||
|
**Diagnostic procedure**:
|
||||||
|
|
||||||
|
Tell the user to flip `DIAGNOSTIC_MODE = true` at the top of `Code.gs` / `CodeFull.gs`, redeploy as new version, and re-test:
|
||||||
|
|
||||||
|
- If they still see the same decoy body → it's NOT AUTH_KEY mismatch (one of the other 5 candidate causes — see `diagnostic-taxonomy.md`)
|
||||||
|
- If they see explicit JSON `{"e":"unauthorized"}` → confirmed AUTH_KEY mismatch; align values + redeploy as new version
|
||||||
|
|
||||||
|
**Canonical reply structure** (from #414 thread):
|
||||||
|
|
||||||
|
1. Confirm the symptom matches the v1.8.x decoy detection
|
||||||
|
2. Walk through the 6 candidate causes and explain why AUTH_KEY mismatch is most likely for their case
|
||||||
|
3. Detail the redeploy-as-new-version steps with exact UI clicks
|
||||||
|
4. Suggest the DIAGNOSTIC_MODE flip as the disambiguator
|
||||||
|
5. Close with link to `diagnostic-taxonomy.md`-equivalent context
|
||||||
|
|
||||||
|
## Pattern 2: TUNNEL_AUTH_KEY env var name confusion (Full mode)
|
||||||
|
|
||||||
|
**Symptoms**:
|
||||||
|
- User on Full mode, Docker container set up
|
||||||
|
- `docker logs mhrv-tunnel` shows `tunnel_auth_key not set, using defaults`
|
||||||
|
- Or: AUTH_KEY mismatch errors in mhrv-rs that the user "definitely" set correctly
|
||||||
|
- Often Persian-language issue (matches Iranian VPS user demographic)
|
||||||
|
|
||||||
|
**Root cause**: User typed `MHRV_AUTH_KEY` (wrong, this is what some old docs said), `Tunnel` (wrong, partial match), `tunnel_auth_key` (wrong, lowercase), `TUNNEL-AUTH-KEY` (wrong, dash instead of underscore), or skipped the env var entirely.
|
||||||
|
|
||||||
|
The literal env var name is **`TUNNEL_AUTH_KEY`** — uppercase, three underscored words.
|
||||||
|
|
||||||
|
**Diagnostic command**:
|
||||||
|
```bash
|
||||||
|
docker exec mhrv-tunnel env | grep TUNNEL_AUTH_KEY
|
||||||
|
```
|
||||||
|
|
||||||
|
Should print: `TUNNEL_AUTH_KEY=<their-secret>`. If empty, the env var wasn't set during `docker run`.
|
||||||
|
|
||||||
|
**Canonical fix**:
|
||||||
|
```bash
|
||||||
|
docker stop mhrv-tunnel
|
||||||
|
docker rm mhrv-tunnel
|
||||||
|
|
||||||
|
docker run -d --name mhrv-tunnel \
|
||||||
|
--restart unless-stopped \
|
||||||
|
-p 8443:8443 \
|
||||||
|
-e TUNNEL_AUTH_KEY="<their-real-secret>" \
|
||||||
|
ghcr.io/therealaleph/mhrv-tunnel-node:latest
|
||||||
|
```
|
||||||
|
|
||||||
|
Then in `CodeFull.gs`, `const TUNNEL_AUTH_KEY = "<their-real-secret>"` must match. Redeploy as new version.
|
||||||
|
|
||||||
|
**Related: port mismatch**. If `docker run` used `-p 8443:8080` or similar mapping, the curl test must use the external port. Check with `docker port mhrv-tunnel`.
|
||||||
|
|
||||||
|
## Pattern 3: Iran ISP throttle (#313)
|
||||||
|
|
||||||
|
**Symptoms**:
|
||||||
|
- 504 timeouts, intermittent connection drops
|
||||||
|
- "Worked yesterday, broken today"
|
||||||
|
- "Mobile data works but home Wi-Fi doesn't" (or vice versa)
|
||||||
|
- TLS handshake timeouts during SNI rotation pool tests
|
||||||
|
- All sites slow, not specific to one destination
|
||||||
|
|
||||||
|
**Root cause**: Iran's ISP infrastructure (especially TCI/مخابرات, less so MCI/همراه) actively RST-injects mid-stream into TLS connections destined for specific Google IPs. This is targeted at Apps Script outbound, not generic Google access. The throttle has plus-and-minus periods — sometimes off for hours, sometimes on for days. Was particularly aggressive starting late April 2026.
|
||||||
|
|
||||||
|
**Direct curl test** (the gold-standard diagnostic):
|
||||||
|
```bash
|
||||||
|
curl -L -X POST 'https://script.google.com/macros/s/<deployment_id>/exec' \
|
||||||
|
-H 'Content-Type: application/json' \
|
||||||
|
-d '{"k":"<auth_key>","u":"https://httpbin.org/get","m":"GET"}' \
|
||||||
|
--max-time 30 -w "\ntime: %{time_total}s\n"
|
||||||
|
```
|
||||||
|
|
||||||
|
Run 5-10 times. If majority timeout/RST → ISP throttle confirmed. If majority succeed → it's mhrv-rs path or config.
|
||||||
|
|
||||||
|
**Workarounds** (in roughly the order to try):
|
||||||
|
1. Upgrade to latest version (each release tends to add diagnostics + small mitigations)
|
||||||
|
2. `disable_padding: true` in config (~25% bandwidth savings, helps under throttle)
|
||||||
|
3. Rotate `google_ip` to a different IP from the SNI pool (some IPs filtered, others not, varies by ISP and week)
|
||||||
|
4. Switch network (mobile data often less throttled than home Wi-Fi)
|
||||||
|
5. Multiple `script_ids` in config — rotation helps when individual deployments are mid-throttle
|
||||||
|
6. Full mode + non-Iranian VPS (Hetzner/Contabo/OVH or Iranian-VPS-broker like Parspack selling German VPS)
|
||||||
|
|
||||||
|
**Don't promise a fix**. The ISP throttle is upstream of anything we can ship. Acknowledge it, list workarounds, point at #313 as the canonical thread.
|
||||||
|
|
||||||
|
## Pattern 4: Apps Script self-loop restriction (Google services blocked)
|
||||||
|
|
||||||
|
**Symptoms**:
|
||||||
|
- "cloud.google.com gives 403"
|
||||||
|
- "Can't access Gmail / Meet / Drive / Colab / Gemini"
|
||||||
|
- "google.com loads but mail.google.com doesn't"
|
||||||
|
- "YouTube video player shows error" (different — this is SABR cliff #300)
|
||||||
|
|
||||||
|
**Root cause**: Google explicitly blocks `UrlFetchApp.fetch()` calls to `*.google.com`, `*.googleapis.com`, `*.gstatic.com`, `*.googleusercontent.com`. This is hardcoded into Google's API to prevent Apps Script from being abused as an internal Google proxy. **No HTTP-relay-on-Apps-Script architecture can fix this.**
|
||||||
|
|
||||||
|
**No workaround in apps_script mode**. This is permanent.
|
||||||
|
|
||||||
|
**Workaround for users with VPS in Full mode**: dual-routing in xray. Their xray client (or v2ray, etc.) routes Google domains direct from their VPS, everything else through mhrv-rs. See #420 for the canonical thread with config snippets.
|
||||||
|
|
||||||
|
**Canonical reply**: explain the architectural limit, list the affected sites, point at #420 for the dual-VPS workaround. Close as duplicate of #420 if it's a clean duplicate.
|
||||||
|
|
||||||
|
## Pattern 5: SABR cliff (#300) — YouTube video doesn't play
|
||||||
|
|
||||||
|
**Symptoms**:
|
||||||
|
- "YouTube loads but video doesn't play"
|
||||||
|
- "This content isn't available"
|
||||||
|
- "Playback error" / "An error occurred"
|
||||||
|
- "Short videos work, long ones don't"
|
||||||
|
|
||||||
|
**Root cause**: Apps Script's 30-second response cap. YouTube's SABR streaming protocol expects long-lived response streams. After ~30s the stream gets cut by Apps Script and the video player errors out. Page HTML/JS loads fine (small, fits in window). Video stream doesn't.
|
||||||
|
|
||||||
|
**Workarounds**:
|
||||||
|
- Short videos (<1 min) often work
|
||||||
|
- Lowest quality (144p/240p) sometimes squeaks past
|
||||||
|
- YouTube web in Chrome/Firefox (browsers use user trust store on Android, YouTube app doesn't) > YouTube app
|
||||||
|
- NewPipe (Android, F-Droid) sometimes works better than official app
|
||||||
|
- Full mode + VPS (definitive — bytes flow through TCP tunnel, not Apps Script's response window)
|
||||||
|
|
||||||
|
v1.9.0 xmux roadmap aims to mitigate by splitting streams across multiple deployments. Won't fully resolve.
|
||||||
|
|
||||||
|
**Canonical reply**: explain SABR cliff, list workarounds, close as duplicate of #300 if pure duplicate.
|
||||||
|
|
||||||
|
## Pattern 6: Android user trust store
|
||||||
|
|
||||||
|
**Symptoms**:
|
||||||
|
- "Browser works but YouTube/Telegram/Instagram apps don't"
|
||||||
|
- "VPN is on but apps don't go through mhrv-rs"
|
||||||
|
- "How do I make Gmail app work?"
|
||||||
|
|
||||||
|
**Root cause**: Android has two CA trust stores — system (factory-installed CAs) and user (user-installed CAs via Settings → Security → Install certificate). Since Android 7.0 (2016), most apps default to system-only. The mhrv-rs MITM CA installs to user trust store; system trust requires root.
|
||||||
|
|
||||||
|
**Apps that work via mhrv-rs on Android**: Chrome, Firefox, Edge, Brave (browsers explicitly opt in to user trust). Most desktop-class apps that delegate to system browser.
|
||||||
|
|
||||||
|
**Apps that don't work**: YouTube app, Gmail app, Maps, Instagram, Twitter/X, banking apps, any app shipped with strict TLS pinning. They use system trust + don't see mhrv-rs.
|
||||||
|
|
||||||
|
**Workarounds**:
|
||||||
|
- Use web versions (`youtube.com` in Chrome instead of YouTube app)
|
||||||
|
- Root + Magisk + MagiskTrustUserCerts module migrates user CA to system
|
||||||
|
- Full mode + VPS (bytes don't flow through MITM, so trust isn't needed for arbitrary apps; v2ray/xray on VPS handles routing)
|
||||||
|
|
||||||
|
**Canonical reply**: explain user/system trust store distinction, list which apps work, give the three workarounds. This is FAQ-tier — should eventually be in `docs/faq/android.md`.
|
||||||
|
|
||||||
|
## Pattern 7: Cloudflare CAPTCHA / 403
|
||||||
|
|
||||||
|
**Symptoms**:
|
||||||
|
- "Most CF-protected sites block me"
|
||||||
|
- "ChatGPT shows captcha I can't solve"
|
||||||
|
- "Cloudflare checking your browser..." stuck
|
||||||
|
|
||||||
|
**Root cause**: All mhrv-rs traffic exits via Google data center IPs (Apps Script's outbound). Cloudflare's bot detection flags traffic from Google IPs to consumer-facing sites as suspicious — looks like a scraper/bot, not a person. Result: aggressive CAPTCHA, sometimes outright 403.
|
||||||
|
|
||||||
|
**Workarounds** (limited):
|
||||||
|
- Solve interactive CAPTCHA when shown — the resulting token works for hours
|
||||||
|
- Different browser fingerprints sometimes pass (Brave, Tor)
|
||||||
|
- Full mode + VPS — VPS exits with its own (residential-adjacent) IP, often not flagged
|
||||||
|
- Cloudflare WARP integration is on the v1.9.x roadmap (#309) but feasibility uncertain
|
||||||
|
|
||||||
|
**Canonical reply**: explain why (Google IP exit), list workarounds, point at #382 (canonical Cloudflare thread) and #309 (WARP roadmap).
|
||||||
|
|
||||||
|
## Pattern 8: Apps Script account suspension / phone-required
|
||||||
|
|
||||||
|
**Symptoms**:
|
||||||
|
- "Action required" notifications on Google account
|
||||||
|
- "Phone number must be added"
|
||||||
|
- Deployment intermittently returns Persian Workspace landing HTML (`<html lang="fa" dir="rtl">پردازش کلمه وب...`)
|
||||||
|
- Sometimes resolves on its own; sometimes escalates to suspension
|
||||||
|
|
||||||
|
**Root cause**: Google's anti-abuse system flags new Google accounts (especially phone-less ones) within hours of deploying automation-pattern code. The progression is: warning → soft restriction (Workspace landing HTML on UrlFetchApp calls) → full suspension.
|
||||||
|
|
||||||
|
**Workarounds**:
|
||||||
|
1. Add a phone number to the account (most reliable). Iranian phones often filtered by Google's verification; user might need a friend's foreign number, TextNow, paid SMS-receive service, or shared phone
|
||||||
|
2. Use established phone-verified accounts (own main Gmail, family/friends' main accounts) — multi-year-old accounts with normal usage history are very rarely flagged
|
||||||
|
3. Workflow #325 — community shared deployments (one user with stable account hosts the deployment, others use the deployment ID + shared AUTH_KEY)
|
||||||
|
|
||||||
|
**Risk levels** (approximate, from observed reports):
|
||||||
|
- Phone-verified personal Gmail, single deployment, light use → low risk
|
||||||
|
- Phone-verified, multiple deployments under same account → medium risk
|
||||||
|
- New no-phone account, any usage → high risk
|
||||||
|
- Old established account, single deployment → very low risk
|
||||||
|
|
||||||
|
No confirmed cases of full Google account ban (Gmail deletion, Drive loss). Suspensions are scoped to Apps Script + UrlFetchApp.
|
||||||
|
|
||||||
|
## Pattern 9: Telegram / VoIP / "app doesn't work in Full mode"
|
||||||
|
|
||||||
|
**Symptoms**:
|
||||||
|
- "Can I add Telegram support?"
|
||||||
|
- "WhatsApp/Skype voice calls don't work"
|
||||||
|
- "Need a port for Telegram"
|
||||||
|
|
||||||
|
**Root cause**: Telegram uses MTProto (custom UDP-ish protocol). WhatsApp/Skype/FaceTime voice/video use WebRTC (UDP STUN/TURN). Apps Script's `UrlFetchApp` is HTTP/HTTPS only — **cannot carry UDP or non-HTTP protocols by design.**
|
||||||
|
|
||||||
|
**Workarounds**:
|
||||||
|
- **Telegram messaging**: web.telegram.org through mhrv-rs Chrome (HTTPS, works)
|
||||||
|
- **Telegram MTProto proxy**: use a public MTProto proxy from Telegram channels (free, unreliable) or self-host on VPS
|
||||||
|
- **Voice/video calls**: only via Full mode + VPS + xray UDP-enabled routing — bytes route direct from VPS to upstream, not through Apps Script
|
||||||
|
|
||||||
|
Architectural ceiling — can't be fixed in mhrv-rs core.
|
||||||
|
|
||||||
|
## Pattern 10: Config file confusion (config.json vs scan_config.json)
|
||||||
|
|
||||||
|
**Symptoms**:
|
||||||
|
- "I followed instructions but it doesn't import the config"
|
||||||
|
- User pastes a config that has `google_ips`, `max_ips_to_scan`, `scan_batch_size`, `google_ip_validation` fields
|
||||||
|
- Says "the program doesn't pick up my config"
|
||||||
|
|
||||||
|
**Root cause**: User confused `config.json` (main runtime config — `script_ids`, `auth_key`, `google_ip`, `mode`, etc.) with `scan_config.json` (input for `mhrv-rs scan-ips` diagnostic command — Google IP discovery).
|
||||||
|
|
||||||
|
**Fix**: explain the two files, point at `config.example.json` in repo root for the right template.
|
||||||
|
|
||||||
|
Common related typos:
|
||||||
|
- `script_id` (singular) instead of `script_ids` (plural array) — mhrv-rs parses as 0 deployments and falls back
|
||||||
|
- `mode: "fullmode"` or `"full_mode"` instead of `"full"` (or `"apps_script"`)
|
||||||
|
|
||||||
|
## Pattern 11: Windows OpenGL renderer fail
|
||||||
|
|
||||||
|
**Symptoms**:
|
||||||
|
- `Error: Glutin(Error { ... NotSupported("extension to create ES context with wgl is not present") })`
|
||||||
|
- `Error: Wgpu(NoSuitableAdapterFound)`
|
||||||
|
- run.bat fails twice (Glow then wgpu fallback) and exits
|
||||||
|
|
||||||
|
**Root cause**: User's Windows lacks OpenGL 2.0+ AND lacks DX12/Vulkan-compatible GPU. Causes: old GPU (Intel HD 2500/3000-era), running in VM without GPU acceleration, RDP session, missing/corrupt graphics drivers.
|
||||||
|
|
||||||
|
**Workaround**: use the CLI binary `mhrv-rs.exe` directly. Put `config.json` in the same folder, double-click `mhrv-rs.exe`, set browser proxy to `127.0.0.1:8086`. Same functionality, no UI.
|
||||||
|
|
||||||
|
v1.8.x roadmap: improve `run.bat` to auto-fallback to CLI when both UI renderers fail.
|
||||||
|
|
||||||
|
## Pattern 12: VPS / Full mode setup questions
|
||||||
|
|
||||||
|
**Symptoms**:
|
||||||
|
- "How do I set up VPS?"
|
||||||
|
- "Does the VPS need to be reachable from Iran?"
|
||||||
|
- "Which provider should I buy?"
|
||||||
|
- "Step-by-step please"
|
||||||
|
|
||||||
|
**Canonical answer**: VPS does NOT need to be reachable from Iran (Apps Script proxies the path). Recommended providers:
|
||||||
|
|
||||||
|
- **Direct purchase from Iran**: difficult — Hetzner needs VAT ID
|
||||||
|
- **Iranian reseller**: Parspack ([parspack.com/vps](https://parspack.com/vps)), Iranserver, Hostiran sell German VPS via Iranian payment with mark-up (~20-40% over direct)
|
||||||
|
- **Outside Iran**: Hetzner Falkenstein DE, Contabo DE, OVH SYS — direct euro/dollar payment
|
||||||
|
|
||||||
|
Specs: 1 vCPU, 1 GB RAM, 25 GB SSD, 50+ Mbps unmetered → ~$3-5/month direct or ~250-500k toman/month via reseller for personal use. For 5+ devices + Instagram smooth: 2-4 GB RAM, 100 Mbps unmetered.
|
||||||
|
|
||||||
|
Setup walkthrough: see `tunnel-node/README.md` and `tunnel-node/README.fa.md` (Persian).
|
||||||
|
|
||||||
|
## Pattern 13: Iranian VPS provider bandwidth-cap appliance
|
||||||
|
|
||||||
|
**Symptoms** (rare but observed):
|
||||||
|
- Persian "exceeded bandwidth quota" HTML response from user's own tunnel-node URL
|
||||||
|
- Mixed success/failure on same `script_id`
|
||||||
|
|
||||||
|
**Root cause** (provisional — confirmed only when VPS is on Iranian provider): Iranian VPS providers enforce monthly bandwidth quotas at the upstream router/load-balancer layer. When tripped, they intercept traffic and serve a Persian quota landing page **upstream** of the user's Docker container. Container itself never sees the request during quota events.
|
||||||
|
|
||||||
|
**Note**: Several users have reported this where the VPS turned out to be at Hetzner DE (not Iranian) — in which case the Persian body is actually Apps Script's own localized soft-quota response (cause #5 in the diagnostic taxonomy). Always confirm the VPS provider before assuming.
|
||||||
|
|
||||||
|
**Workarounds**:
|
||||||
|
1. Upgrade plan if provider has a higher tier
|
||||||
|
2. Move to non-Iranian VPS (Hetzner/Contabo/OVH unmetered)
|
||||||
|
3. Client-side bandwidth optimizations: `disable_padding`, lower `parallel_concurrency`, DNS bypass (v1.8.3+)
|
||||||
|
|
||||||
|
## Pattern 14: Account locale → Persian Apps Script error pages
|
||||||
|
|
||||||
|
**Symptoms**:
|
||||||
|
- Apps Script's response body comes back as Persian HTML (Workspace landing page or quota page)
|
||||||
|
- User on Hetzner/non-Iranian VPS
|
||||||
|
- Their Google account is set to fa-IR locale OR request originates from Iranian IP through some leg
|
||||||
|
|
||||||
|
**Root cause**: Apps Script localizes its system error/placeholder pages based on the deploying account's locale and (sometimes) request-origin IP. Persian-locale account → Persian error pages. This is independent of the user's geographic location running mhrv-rs.
|
||||||
|
|
||||||
|
**Disambiguator**: `DIAGNOSTIC_MODE = true` in Code.gs. If still see Persian body → it's NOT AUTH_KEY mismatch (which gets replaced with explicit JSON in diagnostic mode). It's Apps Script's own quota/state response.
|
||||||
|
|
||||||
|
This is the "5th candidate cause" in the diagnostic taxonomy and the "6th candidate cause" if you separate "Workspace landing HTML for account-flagged deployments" from "Persian quota body for healthy deployments under quota tear".
|
||||||
|
|
||||||
|
## Pattern 15: Download large files / IDM workaround
|
||||||
|
|
||||||
|
**Symptoms**:
|
||||||
|
- "Downloads stick at 1-10 MB"
|
||||||
|
- "Need to download a 1 GB file, IDM gets partial only"
|
||||||
|
|
||||||
|
**Root cause**: 30s response cliff again. For 10 MB files at typical Apps Script throughput, 30s is enough. For 1 GB, would need 200+ seconds — hopeless.
|
||||||
|
|
||||||
|
**Workarounds**:
|
||||||
|
- IDM's multi-segment download with 5 MB segments — each segment fits inside 30s window
|
||||||
|
- Full mode + VPS — bytes flow through TCP tunnel, not constrained
|
||||||
|
- v1.8.x roadmap: range-aware splicing in Code.gs to natively support `Range:` requests
|
||||||
|
|
||||||
|
## Quick triage table
|
||||||
|
|
||||||
|
When a new issue lands, scan for these keywords to map fast:
|
||||||
|
|
||||||
|
| Keywords | Pattern |
|
||||||
|
|----------|---------|
|
||||||
|
| `502`, `decoy`, `no json in batch`, `script completed but did not return` | 1 (AUTH_KEY mismatch) |
|
||||||
|
| `tunnel_auth_key not set`, `MHRV_AUTH_KEY`, `Tunnel_Auth_Key`, `docker logs mhrv-tunnel` | 2 (TUNNEL_AUTH_KEY confusion) |
|
||||||
|
| `504`, `timeout`, `Apps Script unresponsive`, `Connection reset`, `RST`, "yesterday worked" | 3 (Iran ISP throttle #313) |
|
||||||
|
| `cloud.google.com`, `colab`, `gmail`, `meet`, `gemini`, `drive` not loading | 4 (self-loop restriction → #420) |
|
||||||
|
| `YouTube video doesn't play`, `This content isn't available`, `playback error` | 5 (SABR cliff → #300) |
|
||||||
|
| Android, `Gmail app`, `YouTube app`, `Telegram`, "browser works but apps don't" | 6 (user trust store) |
|
||||||
|
| `Cloudflare`, `captcha`, `403 Forbidden`, "checking your browser" | 7 (CF bot detection → #382) |
|
||||||
|
| `Google account`, `phone required`, `action required`, `suspension`, `Workspace landing` | 8 (account flag) |
|
||||||
|
| `Telegram support`, `WhatsApp call`, `Skype`, `voice call`, `video call` | 9 (UDP/MTProto architectural) |
|
||||||
|
| Config has `google_ips`, `scan_batch_size`, `max_ips_to_scan` | 10 (scan_config confusion) |
|
||||||
|
| `egui_glow`, `OpenGL`, `wgl`, `Wgpu(NoSuitableAdapterFound)`, `run.bat` | 11 (Windows OpenGL → CLI) |
|
||||||
|
| `VPS`, `Hetzner`, `Parspack`, `setup help`, "step by step VPS" | 12 (Full mode setup) |
|
||||||
|
| `سهمیه پهنای باند`, `bandwidth quota`, Iranian VPS provider | 13 (provider appliance) |
|
||||||
|
| Persian HTML body in error log + non-Iranian VPS | 14 (account locale) |
|
||||||
|
| `IDM`, `download stuck`, `large file`, `1 GB download` | 15 (range/cliff) |
|
||||||
|
|
||||||
|
If the issue doesn't fit any pattern, it's worth reading carefully — these are the genuine new bugs.
|
||||||
@@ -0,0 +1,439 @@
|
|||||||
|
# Persian reply templates
|
||||||
|
|
||||||
|
These are starting templates for the highest-frequency Persian-language replies. Don't use them verbatim — adapt to the specific user's log lines, config, and report. They exist to prevent re-deriving common phrasings each time and to keep the project's Persian voice consistent across replies.
|
||||||
|
|
||||||
|
The conventions throughout assume:
|
||||||
|
- Polite professional register (`میفرمایید` over `میگی`, full pronouns)
|
||||||
|
- Half-spaces (ZWNJ, ``) in compound words
|
||||||
|
- Latin-script for technical terms inline with Persian particles
|
||||||
|
- Persian numerals optional in prose (`۲۰،۰۰۰` or `20,000` both fine — match the user)
|
||||||
|
- Code blocks always in Latin
|
||||||
|
- Reply marker (Latin) at end
|
||||||
|
|
||||||
|
## Template 1: AUTH_KEY mismatch (with redeploy-as-new-version walkthrough)
|
||||||
|
|
||||||
|
For users showing the v1.8.x decoy detection log line:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
این `502` با body `The script completed but did not return anything` دقیقاً همان pattern decoy detection است که در v1.8.x اضافه شد. شش علت ممکن (per v1.8.3 taxonomy) داره ولی محتملترین برای case شما **AUTH_KEY mismatch** است.
|
||||||
|
|
||||||
|
**نکته بسیار مهم که اکثر کاربران نمیدونند:**
|
||||||
|
|
||||||
|
اگر AUTH_KEY رو در Code.gs ویرایش کردهاید **بعد از deployment اولیه**، Apps Script اتومات edit رو در deployment موجود pick-up نمیکنه. لازمه که **redeploy as new version** کنید:
|
||||||
|
|
||||||
|
1. در Apps Script web editor بازش کنید
|
||||||
|
2. Deploy → **Manage Deployments** (نه Deploy → New deployment)
|
||||||
|
3. روی **deployment موجود** کلیک کنید → پنسیل (Edit)
|
||||||
|
4. در dropdown **Version** → **New version** انتخاب کنید (نه "Head")
|
||||||
|
5. Description بنویسید (مثلاً "AUTH_KEY update")
|
||||||
|
6. **Deploy** کلیک کنید
|
||||||
|
|
||||||
|
URL deployment همون میمونه ولی الان Apps Script کد جدید با AUTH_KEY جدید رو serve میکنه.
|
||||||
|
|
||||||
|
**Diagnostic سریع برای تأیید AUTH_KEY mismatch:**
|
||||||
|
|
||||||
|
در بالای Code.gs این خط رو پیدا کنید:
|
||||||
|
|
||||||
|
`const DIAGNOSTIC_MODE = false;`
|
||||||
|
|
||||||
|
تغییر دهید به:
|
||||||
|
|
||||||
|
`const DIAGNOSTIC_MODE = true;`
|
||||||
|
|
||||||
|
سپس **redeploy as new version** کنید (مثل بالا). سپس test:
|
||||||
|
|
||||||
|
- اگر **هنوز decoy body همون** برمیگرده → علت **NOT** AUTH_KEY mismatch است (یکی از سایر ۵ علت)
|
||||||
|
- اگر **JSON `{"e":"unauthorized"}` صریح** برمیگرده → بله، AUTH_KEY mismatch — fix رو با aligning AUTH_KEY در config.json با Code.gs انجام دهید + redeploy as new version
|
||||||
|
|
||||||
|
بعد از debug کامل، DIAGNOSTIC_MODE رو به `false` برگردونید + redeploy. در production این flag رو false نگه میداریم چون decoy body anti-fingerprinting protection محسوب میشه.
|
||||||
|
|
||||||
|
نتیجه DIAGNOSTIC_MODE flip + پیغام دقیق error بعد از redeploy رو share کنید + میتونیم narrow کنیم.
|
||||||
|
|
||||||
|
---
|
||||||
|
<sub>[reply via Anthropic Claude | reviewed by @therealaleph]</sub>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Template 2: TUNNEL_AUTH_KEY exact spelling
|
||||||
|
|
||||||
|
For users showing `tunnel_auth_key not set, using defaults` in `docker logs mhrv-tunnel`:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
مشکلت یادم نرفته! `tunnel_auth_key not set, using defaults` در logها یعنی **اسم env variable هنوز اشتباه است**. میخوام دقیقتر توضیح بدم چون اسم env vars خیلی sensitive هست:
|
||||||
|
|
||||||
|
**اسم env variable باید دقیقاً این باشد** (نه چیز دیگهای، نه شبیه به این):
|
||||||
|
|
||||||
|
```
|
||||||
|
TUNNEL_AUTH_KEY
|
||||||
|
```
|
||||||
|
|
||||||
|
- **همهش حروف بزرگ**
|
||||||
|
- **با underscore (`_`) بین کلمات** — نه فاصله، نه dash
|
||||||
|
- **سه قسمت**: `TUNNEL` + `_` + `AUTH` + `_` + `KEY`
|
||||||
|
|
||||||
|
**اشتباهات رایج که `tunnel_auth_key not set` میده:**
|
||||||
|
|
||||||
|
| اشتباه | چرا کار نمیکنه |
|
||||||
|
|--------|-----------------|
|
||||||
|
| `Tunnel` یا `tunnel` (تنها) | اسم کامل نیست، tunnel-node این رو نمیخونه |
|
||||||
|
| `Tunnel_Auth_Key` یا `tunnel_auth_key` (lowercase/mixed) | env vars در Linux/Docker case-sensitive هستن |
|
||||||
|
| `TUNNEL-AUTH-KEY` (با dash) | باید underscore باشه نه dash |
|
||||||
|
| `MHRV_AUTH_KEY` | اشتباه قدیمی، tunnel-node این رو نمیخونه |
|
||||||
|
|
||||||
|
**دستور docker run درست — کپی exact:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ssh root@your-vps-ip
|
||||||
|
docker stop mhrv-tunnel
|
||||||
|
docker rm mhrv-tunnel
|
||||||
|
|
||||||
|
docker run -d --name mhrv-tunnel \
|
||||||
|
--restart unless-stopped \
|
||||||
|
-p 8443:8443 \
|
||||||
|
-e TUNNEL_AUTH_KEY="your-secret-here" \
|
||||||
|
ghcr.io/therealaleph/mhrv-tunnel-node:latest
|
||||||
|
```
|
||||||
|
|
||||||
|
بهجای `your-secret-here` همون مقداری که در CodeFull.gs گذاشتید بنویسید.
|
||||||
|
|
||||||
|
**verify بعد از start:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker exec mhrv-tunnel env | grep TUNNEL_AUTH_KEY
|
||||||
|
```
|
||||||
|
|
||||||
|
اگر خروجی این باشه:
|
||||||
|
```
|
||||||
|
TUNNEL_AUTH_KEY=your-secret-here
|
||||||
|
```
|
||||||
|
درسته. اگر هیچ خروجی نداد یا خروجی متفاوت بود، دستور `docker run` با اسم اشتباه اجرا شده.
|
||||||
|
|
||||||
|
نتیجه + خروجی `docker exec` رو share کنید + اگر همچنان مشکل بود narrow میکنیم.
|
||||||
|
|
||||||
|
---
|
||||||
|
<sub>[reply via Anthropic Claude | reviewed by @therealaleph]</sub>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Template 3: #313 ISP throttle (for "504 timeout" reports)
|
||||||
|
|
||||||
|
For users with intermittent timeouts that look like ISP throttle:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
این الگو با [#313](https://github.com/therealaleph/MasterHttpRelayVPN-RUST/issues/313) (Iran ISP throttle Apps Script outbound) match میکنه. throttle این هفته در حال پلاسی بوده — گاهی off میشه ساعتی، گاهی روزی.
|
||||||
|
|
||||||
|
**Diagnostic سریع — direct curl test:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -L -X POST 'https://script.google.com/macros/s/YOUR_DEPLOYMENT_ID/exec' \
|
||||||
|
-H 'Content-Type: application/json' \
|
||||||
|
-d '{"k":"YOUR_AUTH_KEY","u":"https://httpbin.org/get","m":"GET"}' \
|
||||||
|
--max-time 30 -w "\ntime: %{time_total}s\n"
|
||||||
|
```
|
||||||
|
|
||||||
|
اجرا کنید ۵-۱۰ بار. اگر:
|
||||||
|
|
||||||
|
- اکثرشون timeout/RST میگیرن = #313 ISP throttle (شبکه شما Apps Script رو filter میکنه)
|
||||||
|
- اکثرشون JSON برمیگردونن = مشکل از path mhrv-rs است (config، auth_key، یا غیره)
|
||||||
|
|
||||||
|
**Workaround احتمالی برای ISP throttle:**
|
||||||
|
|
||||||
|
۱. **به نسخه v1.8.3 (الان موجود) ارتقا دهید:**
|
||||||
|
- دانلود از <https://github.com/therealaleph/MasterHttpRelayVPN-RUST/releases/tag/v1.8.3> یا <https://t.me/mhrv_rs>
|
||||||
|
- شامل DoH bypass، H1 keepalive، 6-cause error detection
|
||||||
|
|
||||||
|
۲. **`disable_padding: true` در config:**
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"disable_padding": true,
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
~۲۵٪ bandwidth کمتر، در شبکههای throttle شده compounds رو کم میکنه.
|
||||||
|
|
||||||
|
۳. **`google_ip` متفاوت تست کنید** — default `216.239.38.120` ممکنه روی شبکه شما filter شده + یکی دیگه از pool reachable است. لیست pool در `src/domain_fronter.rs` `DEFAULT_GOOGLE_SNI_POOL`.
|
||||||
|
|
||||||
|
۴. **شبکه عوض کنید** — همراه/MCI کمترین throttle داره معمولاً. اگر روی Wi-Fi مخابرات هستید، با موبایل دیتا تست کنید.
|
||||||
|
|
||||||
|
۵. **چند `script_ids` داشته باشید** — اگر یک deployment quota tear گرفته یا throttle شده، rotation کار میکنه. حداقل ۳-۵ deployment.
|
||||||
|
|
||||||
|
۶. **اگر VPS دارید** — Full mode رو امتحان کنید (راهنما [tunnel-node README فارسی](https://github.com/therealaleph/MasterHttpRelayVPN-RUST/blob/main/tunnel-node/README.fa.md)). ISP throttle Apps Script outbound روی Full mode اعمال نمیشه.
|
||||||
|
|
||||||
|
نتیجه v1.8.3 + curl test + log رو share کنید + میتونیم narrow کنیم.
|
||||||
|
|
||||||
|
---
|
||||||
|
<sub>[reply via Anthropic Claude | reviewed by @therealaleph]</sub>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Template 4: VPS setup (Full mode) walkthrough
|
||||||
|
|
||||||
|
For "how do I set up VPS?" questions:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
**Q: آیا VPS باید مستقیم از Iran قابل دسترسی باشه؟**
|
||||||
|
|
||||||
|
**کوتاه: نه.** VPS لازم نیست از Iran direct reachable باشه. این مزیت architectural mhrv-rs Full mode است.
|
||||||
|
|
||||||
|
مسیر traffic:
|
||||||
|
|
||||||
|
```
|
||||||
|
Phone (Iran) → mhrv-rs client (Iran) → Apps Script (via Google IP fronting) →
|
||||||
|
Apps Script's UrlFetchApp →
|
||||||
|
VPS tunnel-node container →
|
||||||
|
upstream internet
|
||||||
|
```
|
||||||
|
|
||||||
|
دقت کنید: **مسیر از Iran به VPS از طریق Apps Script میگذره**. پس:
|
||||||
|
|
||||||
|
- Iran ISP فقط TLS traffic به Google IPها میبینه (`216.239.38.120` و سایر) — مثل HTTPS عادی به Google
|
||||||
|
- Apps Script (در Google data center، US/EU) به VPS شما call میکنه
|
||||||
|
- VPS شما فقط traffic از Google IP میگیره (Apps Script's outbound)
|
||||||
|
|
||||||
|
پس حتی اگر VPS IP از Iran ISP filter شده باشه، **مهم نیست** چون هیچ Iran connection direct به VPS نمیره.
|
||||||
|
|
||||||
|
**Setup گامبهگام:**
|
||||||
|
|
||||||
|
**۱. خرید VPS:**
|
||||||
|
|
||||||
|
- اگر میتوانید Hetzner direct: ~€۴.۵۰/ماه از Falkenstein DE — [hetzner.com/cloud](https://www.hetzner.com/cloud)
|
||||||
|
- اگر VAT ID نیست: Parspack ([parspack.com/vps](https://parspack.com/vps)) واسطهی آلمانی فروش میکنه با ~۲۵۰-۵۰۰ هزار تومان/ماه
|
||||||
|
|
||||||
|
specs توصیه شده:
|
||||||
|
- شخصی: 1 vCPU، 1 GB RAM، 25 GB SSD، 50+ Mbps unmetered
|
||||||
|
- خانوادگی (۵+ device + Instagram smooth): 2-4 GB RAM، 100 Mbps unmetered
|
||||||
|
|
||||||
|
**۲. Docker install:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ssh root@your-vps-ip
|
||||||
|
apt update && apt upgrade -y
|
||||||
|
apt install -y docker.io
|
||||||
|
systemctl enable --now docker
|
||||||
|
docker --version # verify
|
||||||
|
```
|
||||||
|
|
||||||
|
**۳. tunnel-node container run:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker run -d --name mhrv-tunnel \
|
||||||
|
--restart unless-stopped \
|
||||||
|
-p 8443:8443 \
|
||||||
|
-e TUNNEL_AUTH_KEY="your-secret-here" \
|
||||||
|
ghcr.io/therealaleph/mhrv-tunnel-node:latest
|
||||||
|
```
|
||||||
|
|
||||||
|
**اسم env var دقیقاً `TUNNEL_AUTH_KEY` ست** — uppercase، با underscore. هر deviation در default `changeme` میافته + بعداً mismatch میسازه.
|
||||||
|
|
||||||
|
برای ساخت secret تصادفی:
|
||||||
|
```bash
|
||||||
|
openssl rand -hex 32
|
||||||
|
```
|
||||||
|
|
||||||
|
**۴. firewall:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo ufw allow 8443/tcp
|
||||||
|
sudo ufw allow ssh
|
||||||
|
sudo ufw enable
|
||||||
|
```
|
||||||
|
|
||||||
|
**۵. verify direct از خود VPS:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -X POST 'http://localhost:8443/tunnel' \
|
||||||
|
-H 'Content-Type: application/json' \
|
||||||
|
-d '{"k":"YOUR_TUNNEL_SECRET","op":"connect","host":"www.google.com","port":443}' \
|
||||||
|
--max-time 10
|
||||||
|
```
|
||||||
|
|
||||||
|
باید JSON success برگرده. اگر نه، tunnel-node container start نشده.
|
||||||
|
|
||||||
|
**۶. CodeFull.gs setup:**
|
||||||
|
|
||||||
|
در [`assets/apps_script/CodeFull.gs`](https://github.com/therealaleph/MasterHttpRelayVPN-RUST/blob/main/assets/apps_script/CodeFull.gs) محتوا رو copy کنید + در script.google.com یک پروژه جدید ایجاد کنید + paste کنید.
|
||||||
|
|
||||||
|
بالای فایل تنظیم کنید:
|
||||||
|
|
||||||
|
```js
|
||||||
|
const AUTH_KEY = "your-mhrv-auth-key";
|
||||||
|
const TUNNEL_URL = "http://YOUR_VPS_IP:8443/tunnel";
|
||||||
|
const TUNNEL_AUTH_KEY = "your-tunnel-secret-here"; // match با docker run -e
|
||||||
|
```
|
||||||
|
|
||||||
|
سپس **Deploy → New deployment → Web App → Execute as: Me + Who has access: Anyone → Deploy**. URL deployment رو copy کنید + ID بخشش رو بردارید.
|
||||||
|
|
||||||
|
**۷. mhrv-rs config:**
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"mode": "full",
|
||||||
|
"auth_key": "your-mhrv-auth-key",
|
||||||
|
"script_ids": ["YOUR_DEPLOYMENT_ID"]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**`script_ids` plural با s** — این یک typo رایجه که config رو 0-deployment میکنه.
|
||||||
|
|
||||||
|
**۸. Connect + verify:**
|
||||||
|
|
||||||
|
mhrv-rs رو start کنید + log باید نشون بده:
|
||||||
|
|
||||||
|
```
|
||||||
|
INFO batch: 1 ops → AKfyc..., rtt=Xs ← good
|
||||||
|
INFO tunnel session abc1234... opened for ...:443 ← good
|
||||||
|
```
|
||||||
|
|
||||||
|
اگر `ERROR batch failed: got the v1.8.0 bad-auth decoy` میگیرید، AUTH_KEY mismatch است (gam ۶ check کنید).
|
||||||
|
|
||||||
|
اگر `Connection refused` به VPS، firewall بسته است (gam ۴ بررسی کنید).
|
||||||
|
|
||||||
|
برای فارسی-language راهنما با تصاویر [tunnel-node README فارسی](https://github.com/therealaleph/MasterHttpRelayVPN-RUST/blob/main/tunnel-node/README.fa.md) رو ببینید.
|
||||||
|
|
||||||
|
اگر در گامی fail کرد، error log + خروجی command رو share کنید + میتونیم narrow کنیم.
|
||||||
|
|
||||||
|
---
|
||||||
|
<sub>[reply via Anthropic Claude | reviewed by @therealaleph]</sub>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Template 5: Account suspension / phone-required (for "action required" reports)
|
||||||
|
|
||||||
|
For users reporting Google account flag or "action required" notifications:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
این الگو شناختهشدهست + در اساس Google's anti-abuse system فلاگ میکنه new accounts که immediately Apps Script deployment میسازن (مخصوصاً بدون phone verification).
|
||||||
|
|
||||||
|
**Stage تشخیص account flag:**
|
||||||
|
|
||||||
|
```
|
||||||
|
Stage 1: "Action required - add phone number"
|
||||||
|
↓ (phone اضافه میشه) → account stable
|
||||||
|
↓ (phone اضافه نمیشه + automation activity ادامه میده)
|
||||||
|
↓
|
||||||
|
Stage 2: "Account temporarily restricted"
|
||||||
|
↓ (Apps Script deployments شروع میکنن Workspace landing HTML برگردونن
|
||||||
|
↓ بهجای execute کردن — see #421 + cause #6 در v1.8.3 detection)
|
||||||
|
↓
|
||||||
|
Stage 3: "Account suspended" — full lockout، deployments fail
|
||||||
|
```
|
||||||
|
|
||||||
|
شما الان در Stage 1. اگر زود phone verify کنید، account stable میمونه + deployments بدون مشکل ادامه میدن.
|
||||||
|
|
||||||
|
**برای فکر شما درباره ban Google account کلی:**
|
||||||
|
|
||||||
|
در history reports این پروژه (~۵۰+ کاربر در طول سال گذشته)، **هیچ confirmed case full account ban** ندیدم. consequences scope-شده به Apps Script + UrlFetchApp quota — نه Gmail یا Drive یا سایر Google services. accounts با history regular usage (Gmail, Drive files، etc.) و age چند سال + در low-risk قرار دارند برای personal CodeFull.gs deployment.
|
||||||
|
|
||||||
|
**workarounds:**
|
||||||
|
|
||||||
|
**۱. بهترین: phone اضافه کنید.**
|
||||||
|
|
||||||
|
Iranian phone گاهی filter میشه، ولی میتوانید:
|
||||||
|
|
||||||
|
- phone یک friend/family member outside Iran استفاده کنید (SMS code رو forward کنند)
|
||||||
|
- TextNow / Google Voice (US) / paid SMS-receive services
|
||||||
|
- بعضی موارد Google یک phone رو روی چند account قبول میکنه (~۵ account per phone limit)
|
||||||
|
|
||||||
|
**۲. اگر phone نمیتوانید:**
|
||||||
|
|
||||||
|
accounts احتمالاً به Stage 2-3 progress میکنن طی روزها-تا-هفته. برای حفظ service:
|
||||||
|
|
||||||
|
- deployments جدید زیر accounts متفاوت بسازید قبل از اینکه old fail کنه
|
||||||
|
- از **community shared deployment** workflow ([#325](https://github.com/therealaleph/MasterHttpRelayVPN-RUST/issues/325)) استفاده کنید — friend با account stable deployment میسازه + ID share میکنه + AUTH_KEY مشترک
|
||||||
|
|
||||||
|
**۳. برای access به script.google.com وقتی شبکه slow:**
|
||||||
|
|
||||||
|
میتوانید از **mhrv-rs خود** برای access به script.google.com استفاده کنید. mhrv-rs's HTTP proxy به browser → CONNECT tunneling به Google عمل میکنه (نه UrlFetchApp.fetch — که Google block میکنه). browser رو با proxy `127.0.0.1:8086` تنظیم کنید + بروید script.google.com.
|
||||||
|
|
||||||
|
**Action item:**
|
||||||
|
|
||||||
|
اگر Stage 1a هستید (notification ولی deployments هنوز کار میکنن): فوراً phone verify کنید.
|
||||||
|
|
||||||
|
اگر Stage 1b هستید (deployments شروع به Workspace HTML برمیگردونن): همان، plus rotation deploymentها به accounts سالم.
|
||||||
|
|
||||||
|
---
|
||||||
|
<sub>[reply via Anthropic Claude | reviewed by @therealaleph]</sub>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Template 6: Architectural limit (Google services + UrlFetchApp self-loop)
|
||||||
|
|
||||||
|
For users asking why `cloud.google.com` / `colab` / `gmail` / `meet` / `gemini` doesn't work:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
این محدودیت **architectural** است + ربطی به config یا setup شما نداره.
|
||||||
|
|
||||||
|
**Apps Script's UrlFetchApp self-loop restriction:**
|
||||||
|
|
||||||
|
`UrlFetchApp.fetch()` Google در API hardcoded ساخته که نمیتونه به دامنههای `*.google.com` / `*.googleapis.com` / `*.gstatic.com` request بفرسته. Apps Script یا empty response میده یا 4xx/5xx error.
|
||||||
|
|
||||||
|
این محدودیت **Google ست** (نه implementation ما) + در Apps Script API documentation هم ذکر شده. هیچ HTTP-relay مبتنی بر Apps Script نمیتونه به Google services از Apps Script→Google path برسه.
|
||||||
|
|
||||||
|
**سایتهای متأثر:**
|
||||||
|
|
||||||
|
- `cloud.google.com` — Console
|
||||||
|
- `colab.research.google.com` — Colab
|
||||||
|
- `gemini.google.com` — Gemini chat
|
||||||
|
- `drive.google.com` — Drive
|
||||||
|
- `docs.google.com` / `sheets.google.com` / `slides.google.com` — Workspace
|
||||||
|
- `meet.google.com` — Meet (Web)
|
||||||
|
- `mail.google.com` — Gmail
|
||||||
|
- `script.google.com/home/usage` — Apps Script dashboard
|
||||||
|
- `*.google.com` بهطور کلی
|
||||||
|
|
||||||
|
**راهحلها:**
|
||||||
|
|
||||||
|
**۱. سایتهای alternative:**
|
||||||
|
|
||||||
|
- بهجای Drive: WebDAV / Mega / Cloudflare R2
|
||||||
|
- بهجای Colab: Kaggle Notebooks / Jupyter Lab روی VPS
|
||||||
|
- بهجای Gemini: ChatGPT (openai.com) / Claude (claude.ai) — اگر CF block نشدن، کار میکنن
|
||||||
|
- بهجای Cloud Console: SSH مستقیم یا cloud provider's CLI
|
||||||
|
|
||||||
|
**۲. Full mode + VPS:**
|
||||||
|
|
||||||
|
VPS از طرف خود به Google direct وصل میشه. در Full mode، traffic Google رو میتوانید با xray dual-routing از mhrv-rs bypass کنید. detail در [#420](https://github.com/therealaleph/MasterHttpRelayVPN-RUST/issues/420). با این setup همهی Google services از طریق VPS direct کار میکنن.
|
||||||
|
|
||||||
|
**۳. temp VPN موقت:**
|
||||||
|
|
||||||
|
برای access گاهگاهی به Google services (مثلاً برای download فایل از Drive یا setup OAuth)، یک VPN موقت ۱۰ دقیقهای استفاده کنید + سپس به mhrv-rs برمیگردید.
|
||||||
|
|
||||||
|
**نتیجه:**
|
||||||
|
|
||||||
|
اگر میخواهید سایتهای Google کار کنن با همان setup mhrv-rs که الان دارید، نیاز به Full mode + VPS + xray routing است. تا وقتی فقط apps_script mode دارید، Google services unreachable میمونن.
|
||||||
|
|
||||||
|
---
|
||||||
|
<sub>[reply via Anthropic Claude | reviewed by @therealaleph]</sub>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Common Persian phrases for inline use
|
||||||
|
|
||||||
|
When writing custom replies, these phrases come up frequently and should be standardized:
|
||||||
|
|
||||||
|
| Concept | Persian phrasing |
|
||||||
|
|---------|------------------|
|
||||||
|
| "redeploy as new version" | `redeploy as new version کنید (نه head)` |
|
||||||
|
| "exact match" | `دقیقاً match کنه` / `exact match` |
|
||||||
|
| "case-sensitive" | `case-sensitive است` |
|
||||||
|
| "ISP throttle" | `ISP throttle` (English term, transliterate not used) |
|
||||||
|
| "narrow down" | `narrow کنیم` |
|
||||||
|
| "share the log" | `log رو share کنید` |
|
||||||
|
| "thanks for the report" | `ممنون از گزارش` / `تشکر از گزارش` |
|
||||||
|
| "I owe you" / "apologies" | `معذرت میخوام بابت` |
|
||||||
|
| "for your specific case" | `برای case خاص شما` |
|
||||||
|
| "unfortunately" | `متأسفانه` |
|
||||||
|
| "the workaround is" | `workaround این هست که...` |
|
||||||
|
| "this is a known issue" | `این مشکل شناخته شده است` |
|
||||||
|
| "feature is queued" | `feature در roadmap است` |
|
||||||
|
| "we'll ship in v1.x.y" | `در v1.x.y ship میشه` |
|
||||||
|
| "configuration file" | `فایل config` |
|
||||||
|
| "command line" | `command line` / `terminal` / `ترمینال` |
|
||||||
|
| "deployment" (Apps Script) | `deployment` (transliterated `دپلوی` is not used in this project) |
|
||||||
|
| "tunnel" (Full mode) | `tunnel` |
|
||||||
|
| "browser" | `browser` / `مرورگر` |
|
||||||
|
| "system proxy" | `system proxy` |
|
||||||
|
| "page loads but X doesn't work" | `page بالا میاد ولی X کار نمیکنه` |
|
||||||
|
| "I tested with curl" | `با curl تست کردم` |
|
||||||
|
| "the bug is fixed in vX.Y.Z" | `bug در vX.Y.Z حل شده` |
|
||||||
|
| "thanks for catching this" | `ممنون از catch کردن این` |
|
||||||
|
| "let me know if it works" | `اگر کار کرد گزارش بدید` |
|
||||||
|
| "if it fails again, share the log" | `اگر دوباره fail کرد، log رو share کنید` |
|
||||||
|
|
||||||
|
These let Persian replies use English technical terms naturally without forced transliteration, which matches how Iranian developers actually talk.
|
||||||
@@ -0,0 +1,211 @@
|
|||||||
|
# Release workflow
|
||||||
|
|
||||||
|
Cutting a release is fast and low-ceremony for this project. Most releases are patch bumps that go from "decision to ship" to "Telegram channel posting" in under 30 minutes of human work + ~30 minutes of CI.
|
||||||
|
|
||||||
|
## When to cut a release
|
||||||
|
|
||||||
|
Cut a release whenever **anything user-visible** has landed since the last tag. User-visible includes:
|
||||||
|
|
||||||
|
- Bug fixes that affect runtime behavior
|
||||||
|
- New config options
|
||||||
|
- New CLI subcommands or flags
|
||||||
|
- Diagnostic improvements (better log messages, error categories)
|
||||||
|
- Apps Script script changes (Code.gs / CodeFull.gs)
|
||||||
|
- Documentation that users will read (README updates, troubleshooting docs — though these can also batch into the next release)
|
||||||
|
|
||||||
|
Don't cut for:
|
||||||
|
- Internal refactors with no behavior change
|
||||||
|
- CI/workflow file edits
|
||||||
|
- Markdown formatting fixes
|
||||||
|
- Test-only changes
|
||||||
|
|
||||||
|
When in doubt, cut. Patch releases are cheap and Iranian users actively check the Telegram channel for updates.
|
||||||
|
|
||||||
|
## The release workflow
|
||||||
|
|
||||||
|
### Step 1: Decide the version
|
||||||
|
|
||||||
|
Read the latest tag:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git describe --tags --abbrev=0
|
||||||
|
```
|
||||||
|
|
||||||
|
Then bump:
|
||||||
|
- **Patch (Z+1)** — for ~95% of releases. v1.8.2 → v1.8.3
|
||||||
|
- **Minor (Y+1)** — for a coherent feature batch shipped together. v1.7.x → v1.8.0 represented "DPI evasion + active-probing defense + full-mode usage counters" together
|
||||||
|
- **Major (X+1)** — never done in this project's history. Reserved for true protocol-incompatible changes with the Apps Script side. Don't bump major without explicit go-ahead.
|
||||||
|
|
||||||
|
### Step 2: Bump `Cargo.toml`
|
||||||
|
|
||||||
|
Edit `Cargo.toml` line 3 (`version = "X.Y.Z"`). Keep package name `mhrv-rs` unchanged. The `tunnel-node` subcrate has its own version that's independent — don't bump it unless you're shipping a tunnel-node change.
|
||||||
|
|
||||||
|
### Step 3: Build to refresh `Cargo.lock`
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo build --release 2>&1 | tail -3
|
||||||
|
```
|
||||||
|
|
||||||
|
`Cargo.lock` will pick up the new version string. Verify with:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git diff Cargo.lock | head -20
|
||||||
|
```
|
||||||
|
|
||||||
|
Should show only the `name = "mhrv-rs"` block's `version = "X.Y.Z"` change.
|
||||||
|
|
||||||
|
### Step 4: Write the changelog
|
||||||
|
|
||||||
|
Create `docs/changelog/vX.Y.Z.md` using the format in `assets/changelog-template.md`. Persian first, then `---`, then English. See `workflow-conventions.md` for format details.
|
||||||
|
|
||||||
|
When the release is shipping multiple PRs from contributors, credit each by name + handle in both halves of the changelog.
|
||||||
|
|
||||||
|
### Step 5: Run tests + final build
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo test --lib 2>&1 | tail -5
|
||||||
|
cargo build --release 2>&1 | tail -3
|
||||||
|
cargo build --bin mhrv-rs-ui --release --features ui 2>&1 | tail -3
|
||||||
|
```
|
||||||
|
|
||||||
|
All three must succeed. Test count varies by version. All passing is the gate.
|
||||||
|
|
||||||
|
If any contributor PRs were merged in this release, also verify by re-running tests after the merge — sometimes integration with main reveals issues that didn't show in the PR's CI.
|
||||||
|
|
||||||
|
### Step 6: Commit + tag + push
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add Cargo.toml Cargo.lock docs/changelog/vX.Y.Z.md
|
||||||
|
git status # sanity check
|
||||||
|
git commit -m "$(cat <<'EOF'
|
||||||
|
chore: vX.Y.Z — <short summary fitting under 75 chars>
|
||||||
|
|
||||||
|
<longer body explaining the why and the changes — see workflow-conventions.md
|
||||||
|
for format>
|
||||||
|
EOF
|
||||||
|
)"
|
||||||
|
|
||||||
|
git push origin main
|
||||||
|
git tag vX.Y.Z
|
||||||
|
git push origin vX.Y.Z
|
||||||
|
```
|
||||||
|
|
||||||
|
The `git push origin vX.Y.Z` is the trigger — release CI auto-fires on tag push.
|
||||||
|
|
||||||
|
If `git push origin main` fails with `non-fast-forward`, someone (often the auto-binary-refresh CI from a prior release) pushed in the meantime:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git pull --rebase origin main
|
||||||
|
git push origin main
|
||||||
|
git tag vX.Y.Z # if you didn't tag yet
|
||||||
|
git push origin vX.Y.Z
|
||||||
|
```
|
||||||
|
|
||||||
|
If you already tagged before the push race, the tag still works — it's pinned to your commit, and the rebase shouldn't change your commit's SHA unless there were conflicts.
|
||||||
|
|
||||||
|
### Step 7: Watch CI
|
||||||
|
|
||||||
|
```bash
|
||||||
|
gh run list --limit 3
|
||||||
|
```
|
||||||
|
|
||||||
|
Two workflows fire on tag push:
|
||||||
|
1. **`release-drafter`** — quick (~15s), updates the GitHub release draft. Always succeeds.
|
||||||
|
2. **`release`** — slow (~25-35 minutes), builds binaries for all platforms, attaches to release.
|
||||||
|
|
||||||
|
Once `release` succeeds, a third workflow auto-fires:
|
||||||
|
3. **`Telegram publish release files`** — posts each binary individually to the Telegram channel `mhrv_rs` with Persian captions, SHA-256 hashes, and a cross-link from the main channel. Takes ~1-2 minutes.
|
||||||
|
|
||||||
|
If `release` fails, common causes:
|
||||||
|
|
||||||
|
- **Cross-compile failure** — particularly on i686 / mipsel. i686 was dropped from the matrix in v1.7.11 because of MSRV churn (see #411 thread). If a new architecture starts failing, it's usually a transitive dep bumping MSRV past what the toolchain pinned for that target supports. Triage: check which architecture's job failed, look at the cargo error, decide whether to pin a dep with `cargo update --precise` or drop the architecture.
|
||||||
|
- **`actions/download-artifact@v4` flakiness** — replaced with `gh run download` + 3-attempt retry in v1.7.11. Should be stable now; if it flakes again, increase retry count.
|
||||||
|
|
||||||
|
After CI succeeds, optionally check the binary refresh:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git pull origin main
|
||||||
|
git log --oneline -3
|
||||||
|
```
|
||||||
|
|
||||||
|
You should see an auto-generated commit `chore(releases): refresh prebuilt binaries for vX.Y.Z` from the release CI bot.
|
||||||
|
|
||||||
|
### Step 8: Verify Telegram channel
|
||||||
|
|
||||||
|
The Telegram publish workflow posts to channel `mhrv_rs` (public link `https://t.me/mhrv_rs`). The channel should show:
|
||||||
|
|
||||||
|
1. An announcement post: `📦 mhrv-rs vX.Y.Z منتشر شد...` referencing the changelog file
|
||||||
|
2. ~16 individual file posts (Android APKs split by ABI, Windows ZIP, macOS arm64/amd64 dmg+tar, Linux x86_64/arm64 incl. musl, Raspbian, OpenWRT)
|
||||||
|
3. Each file caption includes Persian description (e.g., "نسخه ویندوز x86") + SHA-256 hash
|
||||||
|
4. A "main channel" post (different channel) cross-linking to the files channel post
|
||||||
|
|
||||||
|
Files larger than 50 MB get chunked into `.part_aa`, `.part_ab`, etc. via the `split` pattern in `.github/scripts/telegram_publish_files.py`.
|
||||||
|
|
||||||
|
If something didn't post, check the workflow run logs:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
gh run view <run-id> --log
|
||||||
|
```
|
||||||
|
|
||||||
|
Common cause: the auto-fire dispatch on `workflow_run` requires the parent workflow to succeed; if release.yml had a flaky download retry, the dispatch might still succeed but partial.
|
||||||
|
|
||||||
|
## Manual re-publish (rare)
|
||||||
|
|
||||||
|
If you need to re-trigger Telegram publishing for an already-released version (e.g., the workflow failed and you fixed it), use `workflow_dispatch`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
gh workflow run "Telegram publish release files" -f version=vX.Y.Z
|
||||||
|
```
|
||||||
|
|
||||||
|
The script downloads artifacts via `gh release download` (not the workflow's artifacts) so it works retroactively.
|
||||||
|
|
||||||
|
## Re-cutting a release (very rare)
|
||||||
|
|
||||||
|
If a release was tagged and pushed but turns out to be broken (e.g., bug in a merged PR you wanted to revert):
|
||||||
|
|
||||||
|
1. **Don't** delete the tag if the release is already public. Iranian users may have already pulled the binaries; a deleted tag confuses them and they think the project is gone.
|
||||||
|
2. Cut a fix immediately as the next patch (vX.Y.Z+1).
|
||||||
|
3. Optionally edit the GitHub release notes for the broken version to say "known issue, upgrade to vX.Y.Z+1".
|
||||||
|
|
||||||
|
If you tagged but didn't push yet, just delete the tag locally and re-tag after fixing:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git tag -d vX.Y.Z # local only; safe
|
||||||
|
# fix the issue, commit
|
||||||
|
git tag vX.Y.Z
|
||||||
|
git push origin vX.Y.Z
|
||||||
|
```
|
||||||
|
|
||||||
|
## Pre-release rollback
|
||||||
|
|
||||||
|
If `cargo test --lib` fails after merging PRs but before tagging:
|
||||||
|
|
||||||
|
1. Don't tag.
|
||||||
|
2. Either revert the merge commit (`git revert <merge-commit-sha>`) or fix forward (commit a new fix on main).
|
||||||
|
3. Re-run tests until green.
|
||||||
|
4. Tag.
|
||||||
|
|
||||||
|
The release CI doesn't run tests before building, so untagged-but-broken main is fine — you have time to fix before tagging.
|
||||||
|
|
||||||
|
## Coordinating with multiple PRs in flight
|
||||||
|
|
||||||
|
If two PRs are both ready to merge, the order matters:
|
||||||
|
|
||||||
|
- Merge them one at a time (not both into a single tag) **only** if they're independent
|
||||||
|
- If they touch the same files, merge them sequentially with `gh pr checkout N1 && cargo test && merge`, then `gh pr checkout N2` (which now bases on the new main; CI on the PR may show the old base, but the local checkout sees latest main) `&& cargo test && merge`
|
||||||
|
- If a merge introduces conflicts, GitHub's UI flags the PR as conflicting; resolve via `gh pr checkout N` + manual rebase + push to the PR branch
|
||||||
|
|
||||||
|
After all PRs are merged, **then** bump version, write changelog (covering all merged PRs), tag, push.
|
||||||
|
|
||||||
|
## Versioning the tunnel-node subcrate
|
||||||
|
|
||||||
|
`tunnel-node/Cargo.toml` has its own version field separate from the main crate. Bump it when:
|
||||||
|
|
||||||
|
- Changing the tunnel-node HTTP API (`/tunnel`, `/batch` endpoints)
|
||||||
|
- Changing the auth flow (`TUNNEL_AUTH_KEY` semantics)
|
||||||
|
- Changing the env var contract
|
||||||
|
- Bumping the Docker image label
|
||||||
|
|
||||||
|
For pure internal refactors of tunnel-node that don't change the surface, leave it alone — the Docker image at `ghcr.io/therealaleph/mhrv-tunnel-node:latest` continues to be the latest tag and users don't need to know an internal version bumped.
|
||||||
|
|
||||||
|
When tunnel-node version bumps, the Docker image gets re-tagged in the registry by the CI. Users running `docker pull ghcr.io/therealaleph/mhrv-tunnel-node:latest` get the new version automatically; users pinned to a specific version stay pinned.
|
||||||
@@ -0,0 +1,118 @@
|
|||||||
|
# Roadmap
|
||||||
|
|
||||||
|
This is the project's queued work, organized by release batch. Use it when:
|
||||||
|
- Categorizing a new feature request from a user (which batch does this fit?)
|
||||||
|
- Cross-referencing roadmap items in your replies ("queued for v1.8.x")
|
||||||
|
- Deciding what to ship in the current batch vs deferred
|
||||||
|
|
||||||
|
Update this file when items ship (move to "shipped") or when new items are added (cluster with similar items in the right batch).
|
||||||
|
|
||||||
|
## v1.8.x (current batch — small fixes + diagnostics + Android UI)
|
||||||
|
|
||||||
|
The v1.8.x line is a **small-and-frequent** release pattern. Each release ships one or two completed items rather than batching many. The theme is "diagnostic improvements + Android UX + Apps Script script enhancements".
|
||||||
|
|
||||||
|
### Shipped
|
||||||
|
|
||||||
|
- ✅ **v1.8.0**: Random padding (DPI evasion), auto-blacklist deployments, decoy responses, full-mode usage counters, active-probing defense, DIAGNOSTIC_MODE flag in Code.gs
|
||||||
|
- ✅ **v1.8.1**: Decoy detection client-side (with v1.8.2/v1.8.3 corrections), `script_id` in error logs, `disable_padding` config flag
|
||||||
|
- ✅ **v1.8.2**: UI binary tracing reads `config.log_level` (with reload handle for live changes), softer 4-cause decoy detection error message
|
||||||
|
- ✅ **v1.8.3**: Spreadsheet-backed response cache (Code.gs, opt-in), DoH bypass for known DoH endpoints, H1 container keepalive (240s), 64 KB header cap with HTTP 431, clearer port-collision error message
|
||||||
|
|
||||||
|
### Queued (small, can ship in next 1-3 patch releases)
|
||||||
|
|
||||||
|
- **v1.8.4 candidate items**:
|
||||||
|
- Soften decoy detection further with the 6-cause taxonomy (Persian quota body + Workspace landing HTML detection)
|
||||||
|
- Per-`script_id` rolling-window error-category counter visible in CLI/UI
|
||||||
|
- run.bat auto-fallback to CLI when both UI renderers fail (#417 / #426)
|
||||||
|
- TUNNEL_AUTH_KEY startup warning when `MHRV_AUTH_KEY` is set without `TUNNEL_AUTH_KEY` (catches the recurring #391-style env var typo)
|
||||||
|
- Range-aware splicing in Code.gs (lets large downloads work via HTTP Range requests, partial fix for #441)
|
||||||
|
|
||||||
|
### Queued (medium-effort, batch into focused release)
|
||||||
|
|
||||||
|
- **`googlevideo_ip` config field** (#300) — separate `google_ip` for googlevideo.com vs other Google domains. Some users have one IP that works for the latter but not the former. Approx 1-2 days of work.
|
||||||
|
- **DNS ad-blocking via StevenBlack/hosts** (#377) — opt-in DNS-level filtering during SOCKS5/MITM dispatch. Reduces upstream calls for ad-domains.
|
||||||
|
- **DNS caching + parallel dispatch via hickory-resolver** (#377) — replace blocking DNS with cached + parallel resolver. Substantial latency win for browser pageloads.
|
||||||
|
- **Tunable strike-counter threshold for auto-blacklist** (#391) — single-deployment users currently hit the auto-blacklist after a few transient errors and end up with no working deployment. Make threshold configurable.
|
||||||
|
- **`block_quic` 3-state UI toggle** (#361 / #377): off / drop / reject (default reject = ICMP unreachable, instant Happy Eyeballs failover). 2bemoji's design.
|
||||||
|
- **Android UI batch** (#285 / #361 / #261 / #295 / #254 / #313 / #375):
|
||||||
|
- block_quic toggle
|
||||||
|
- youtube_via_relay toggle
|
||||||
|
- listen_host editor
|
||||||
|
- passthrough_hosts editor
|
||||||
|
- Active deployment indicator
|
||||||
|
- Per-deployment quota counters
|
||||||
|
- Android disconnect crash fix (#418)
|
||||||
|
- **System proxy toggle** (#432) — Windows/macOS/Linux desktop UI: on Connect set system HTTP proxy to mhrv-rs, on Disconnect clear. With crash-rollback so a hung mhrv-rs doesn't leave system proxy stuck.
|
||||||
|
- **`script_ids_url` dynamic config** (#433) — config field pointing at an HTTPS URL that returns a JSON list of deployment IDs. mhrv-rs fetches at startup + every TTL. Lets distributors update deployment lists for many users without each editing config manually.
|
||||||
|
- **In-app updater via mhrv-rs's own proxy** (#366) — let mhrv-rs check for updates + download new binaries through its own relay (avoiding the chicken-and-egg of "I can't reach github.com to update mhrv-rs"). Defense in depth.
|
||||||
|
- **Temporal jitter** (#369 §2) — randomize timing of batch dispatches to defeat timing-correlation DPI.
|
||||||
|
- **`tls_verify` config** (#430 / masterking32 PR #26) — opt-in to skip upstream TLS verification for self-signed certs. Trade-off: opens MITM-of-MITM risk; needs careful design.
|
||||||
|
- **`request_timeout` configurable** (#430 / masterking32 PR #25) — currently hard-coded `BATCH_TIMEOUT = 30s`. Make configurable for users on slow networks who want longer timeouts.
|
||||||
|
- **CF Workers backend audit** (#380 / #393) — test mhr-cfw compatibility. If it works, document as alternative backend.
|
||||||
|
|
||||||
|
### Documentation queued
|
||||||
|
|
||||||
|
- **`docs/full-mode-google-bypass.md`** (#420) — dual-routing in xray for users with Iranian VPS xray entry topology
|
||||||
|
- **`docs/full-mode-iran-vps-setup.md`** (#420) — full step-by-step for the dual-VPS topology (Iranian xray entry + non-Iranian tunnel-node exit)
|
||||||
|
- **`docs/iran-mirrors.md`** (#422) — community-maintained Iranian CDN mirrors for users who can't reach github.com. Pending SHA-256 verification of @amintoorchi's xdevteam.liara.space mirrors.
|
||||||
|
- **`docs/win7-build.md`** (#411) — manual Cargo.lock downgrade + cargo update --precise chain for community Win7 32-bit builds. Officially unsupported since v1.7.11 but the build path works for technical users.
|
||||||
|
- **`docs/faq/android.md`** — user trust store explanation, which apps work, why Gmail/YouTube don't, root + Magisk option
|
||||||
|
- **Updates to README** — short explanation of dual-routing for Google services + xray config snippet
|
||||||
|
|
||||||
|
## v1.9.0 (headline release — xmux)
|
||||||
|
|
||||||
|
The v1.9.0 release is the **xmux** feature: stream splitting across multiple Apps Script deployments at byte-range level. Currently in design / RFC stage (#369).
|
||||||
|
|
||||||
|
### Design goals
|
||||||
|
|
||||||
|
- **Survivability under ISP RST** — when one deployment's TCP connection gets RST-injected mid-stream, other deployments continue to carry remaining byte ranges
|
||||||
|
- **Latency reduction** — small responses can hit any of N deployments first; mhrv-rs takes the first to respond
|
||||||
|
- **Bandwidth aggregation** — large downloads chunk across deployments concurrently. 5 deployments × 10 MB/s each ≈ 50 MB/s aggregate (subject to per-deployment caps)
|
||||||
|
- **SABR cliff mitigation** — long YouTube streams chunk into <30s windows across deployments; each window finishes within Apps Script's response cap, then mhrv-rs reassembles
|
||||||
|
|
||||||
|
### Open design questions
|
||||||
|
|
||||||
|
- **Reordering buffer size** — bigger = more memory; smaller = more retries on out-of-order
|
||||||
|
- **Failure recovery** — if a deployment fails mid-chunk, who picks up the half-served range?
|
||||||
|
- **Idempotency** — POST requests are tricky; current design only handles GET safely
|
||||||
|
- **State consistency** — if some chunks come from cache and some don't, ETag/Last-Modified handling needs care
|
||||||
|
- **Configurability** — when does a user want xmux on (latency-sensitive) vs off (quota-sensitive)?
|
||||||
|
|
||||||
|
### Implementation timeline
|
||||||
|
|
||||||
|
- 4-6 weeks of design + implementation
|
||||||
|
- Tag @w0l4i, @2bemoji, @ipvsami, @dazzling-no-more, @euvel as core reviewers when design issue is filed
|
||||||
|
|
||||||
|
The design issue should be filed after the v1.8.x batch settles (so the queue isn't too long).
|
||||||
|
|
||||||
|
## v1.9.x and beyond (longer-horizon)
|
||||||
|
|
||||||
|
These are committed to the project's roadmap but not actively in design. Listed for traceability when users ask "are you planning X?".
|
||||||
|
|
||||||
|
- **Cloudflare WARP integration** (#309) — outbound traffic exits via Cloudflare WARP after Apps Script. Lets sites that flag Google IPs (most CF-protected) see traffic as Cloudflare-residential. Feasibility uncertain — needs CF account + WARP wireguard interface integration.
|
||||||
|
- **TLS fingerprint randomization** (#369 §2) — randomize JA3/JA4 across deployments. Defeats CF / commercial bot detection.
|
||||||
|
- **tunnel-node UPSTREAM_SOCKS5 chain** (#333 kanan-droid) — let tunnel-node forward through a SOCKS5 upstream (e.g., another VPN). Defense in depth + IP variety.
|
||||||
|
- **Tier-3 i686-win7-windows-msvc target** (#411) — Windows 7 32-bit support via tier-3 target with `-Z build-std`. Needs nightly Rust. Roadmap v1.9.x or v2.x.
|
||||||
|
- **Web frontend / dashboard** (#384) — landing page for the project. Low priority but recurring request.
|
||||||
|
- **In-app changelog viewer** — show changelog for new version inside mhrv-rs UI when an update is available (small UX polish).
|
||||||
|
|
||||||
|
## How to use this when triaging issues
|
||||||
|
|
||||||
|
When a feature request comes in:
|
||||||
|
|
||||||
|
1. Match the request to an existing item in this list. If found, reply: "Queued for v1.8.x [or whichever batch]. ETA ~X weeks. See [#NNN](#) for the canonical thread."
|
||||||
|
2. If it's a duplicate of an existing roadmap item, close as duplicate of the canonical issue.
|
||||||
|
3. If it's a new request not on this list:
|
||||||
|
- Substantive feature: add to v1.8.x or v1.9.x list as appropriate, note the issue number, reply with the planned bucket
|
||||||
|
- Long-horizon / uncertain: add to v1.9.x and beyond, reply that it's noted but no timeline
|
||||||
|
- Architectural impossibility (UrlFetchApp self-loop, MTProto, etc.): close with explanation, link to architectural reference
|
||||||
|
|
||||||
|
## Roadmap velocity
|
||||||
|
|
||||||
|
The project ships v1.x.y patches frequently — typically 1-3 per week during active development. Minor (1.x) bumps happen every few months. v1.0 → v1.8 took ~12 months. So:
|
||||||
|
|
||||||
|
- "v1.8.x ETA" usually means "next 1-2 weeks" for small items, "next 1-2 months" for big items
|
||||||
|
- "v1.9.0 ETA" usually means "next 2-3 months"
|
||||||
|
- "v1.9.x" or "v2.x" means "no specific timeline, but committed to consider"
|
||||||
|
|
||||||
|
Be honest with users about timelines. Iranian users especially appreciate knowing whether to wait or pursue alternatives.
|
||||||
@@ -0,0 +1,174 @@
|
|||||||
|
# Workflow conventions
|
||||||
|
|
||||||
|
These are the writing conventions, formatting rules, and tone guidelines for everything that goes into the public repo or out to users. Internalize these — they're applied to every issue reply, every commit message, every changelog, every PR description.
|
||||||
|
|
||||||
|
## The reply marker
|
||||||
|
|
||||||
|
Every substantive issue or PR comment ends with this exact footer:
|
||||||
|
|
||||||
|
```
|
||||||
|
---
|
||||||
|
<sub>[reply via Anthropic Claude | reviewed by @therealaleph]</sub>
|
||||||
|
```
|
||||||
|
|
||||||
|
That's a literal Markdown horizontal rule, then the `<sub>...</sub>` line. The `[reply via Anthropic Claude | reviewed by @therealaleph]` text is verbatim — same brackets, same pipe, same case, same `@therealaleph` mention.
|
||||||
|
|
||||||
|
**Why this exists**: replies are drafted by Claude and reviewed by the maintainer before posting. The marker signals this to the user. Users in this community know this convention and rely on it.
|
||||||
|
|
||||||
|
**Don't omit it**, don't translate "reviewed by" into Persian, don't paraphrase the format. The marker stays the same regardless of whether the rest of the reply is in Persian or English.
|
||||||
|
|
||||||
|
**Where it doesn't go**: very short comments like "Dup of #423." or "Closing as resolved." or close-comments via `gh issue close --comment "..."`. The marker is for substantive replies. Trivial close comments don't need it.
|
||||||
|
|
||||||
|
## Persian or English: match the user
|
||||||
|
|
||||||
|
The repo's userbase is majority Persian-speaking. Writing in their language matters — both for clarity (technical context lands better) and for respect (assuming everyone wants English is wrong).
|
||||||
|
|
||||||
|
**Match what the user wrote**:
|
||||||
|
- User wrote in Persian → reply in Persian
|
||||||
|
- User wrote in English → reply in English
|
||||||
|
- User wrote a mix → match the dominant language; if it's roughly even, prefer Persian since most mixed-language Iranian users default to Persian for nuance and English for technical terms
|
||||||
|
|
||||||
|
**Things that always stay in original Latin form**, regardless of reply language:
|
||||||
|
- Code blocks (Rust, JSON, bash, JS — all stay as-is)
|
||||||
|
- Command-line examples (`gh issue close N`, `cargo build`, `docker run ...`)
|
||||||
|
- Technical identifiers: `AUTH_KEY`, `TUNNEL_AUTH_KEY`, `script_id`, `parallel_concurrency`, `disable_padding`, `tunnel_doh`, `bypass_doh_hosts`, `DIAGNOSTIC_MODE`, `passthrough_hosts`, `google_ip`, `mode: "full"` / `mode: "apps_script"`
|
||||||
|
- Filename references: `Code.gs`, `CodeFull.gs`, `config.json`, `tunnel-node`, `mhrv-rs.exe`, `MhrvVpnService.kt`, `domain_fronter.rs`
|
||||||
|
- URLs and links
|
||||||
|
- The reply marker
|
||||||
|
- Issue references like `#404`, `#313`
|
||||||
|
- HTTP status codes (`502`, `504`, `403`)
|
||||||
|
|
||||||
|
**Don't**:
|
||||||
|
- Translate command names or function names
|
||||||
|
- Mix Persian text into code blocks (unless user did so in their own paste)
|
||||||
|
- Use machine-translation for the Persian — write it natively
|
||||||
|
|
||||||
|
**Persian register**: write at "polite professional" level — `میفرمایید` over `میگی`, `لطفاً` (please), full pronouns when needed. Iranian Github users tend to write fairly formally; match that. Use Persian punctuation conventions: `،` (Persian comma), `؛` (Persian semicolon), `؟` (Persian question mark) — though comma in lists is acceptable as `،` or `,` per style preference.
|
||||||
|
|
||||||
|
## Public artifact tone
|
||||||
|
|
||||||
|
Anything that goes into the public repo — issue replies, PR comments, commit messages, PR descriptions, changelogs — is full prose, written warmly and clearly. Iranian users especially read carefully and brevity reads as cold or dismissive in this context. Use full sentences. Explain reasoning. Be patient.
|
||||||
|
|
||||||
|
## Changelog format
|
||||||
|
|
||||||
|
Every release has a file at `docs/changelog/vX.Y.Z.md`. The format is strict:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
<!-- see docs/changelog/v1.1.0.md for the file format: Persian, then `---`, then English. -->
|
||||||
|
• [bullet 1 in Persian, with markdown links to issue numbers]
|
||||||
|
• [bullet 2 in Persian]
|
||||||
|
• [bullet 3 in Persian]
|
||||||
|
---
|
||||||
|
• [same bullet 1 in English, written natively, not machine-translated]
|
||||||
|
• [same bullet 2 in English]
|
||||||
|
• [same bullet 3 in English]
|
||||||
|
```
|
||||||
|
|
||||||
|
Conventions:
|
||||||
|
|
||||||
|
- **Use `•` (U+2022 bullet)**, not `-` or `*`. The Persian half uses bullets because Markdown unordered lists don't render naturally with Persian RTL text in the GitHub Releases page.
|
||||||
|
- **Issue/PR links**: full GitHub URLs in markdown form: `[#404](https://github.com/therealaleph/MasterHttpRelayVPN-RUST/issues/404)`. Don't use bare `#404` in changelogs — they don't auto-link in the Persian section.
|
||||||
|
- **Same content both halves** — they cover the same bullets, in the same order. Not necessarily verbatim translation; the Persian is written for Persian readers and may use slightly different framing.
|
||||||
|
- **Length**: each bullet should describe what changed AND why it matters. "Added DoH bypass" is too thin; "DoH lookups now route around the Apps Script tunnel via plain TCP, saving the ~2s UrlFetchApp roundtrip per name without losing privacy (DoH is already encrypted)" is the right depth.
|
||||||
|
- **Credit contributors**: if a PR landed from a community contributor, say so by name + handle. Persian: `از @euvel`. English: `by @euvel`.
|
||||||
|
- **Backwards-incompatible changes**: rare for this project, but flag prominently if any. Add `**شکستگی سازگار**` / `**Breaking change**` prefix.
|
||||||
|
|
||||||
|
The starter template is at `assets/changelog-template.md`.
|
||||||
|
|
||||||
|
## Commit messages
|
||||||
|
|
||||||
|
Format:
|
||||||
|
|
||||||
|
```
|
||||||
|
<type>: vX.Y.Z — <short summary>
|
||||||
|
|
||||||
|
<longer prose body explaining the why and the changes>
|
||||||
|
|
||||||
|
[optional: bullet list of specific changes]
|
||||||
|
```
|
||||||
|
|
||||||
|
Types in regular use:
|
||||||
|
- `feat:` — new feature, user-visible (most common)
|
||||||
|
- `fix:` — bug fix
|
||||||
|
- `chore(releases):` — auto-fired CI commit refreshing prebuilt binaries
|
||||||
|
- `chore:` — version bump, dep update, etc.
|
||||||
|
- `docs:` — documentation-only changes
|
||||||
|
- `ci(workflow-name):` — workflow file changes
|
||||||
|
- `feat(area):` — feature scoped to a specific subsystem (e.g., `feat(code.gs):`, `feat(drive):`)
|
||||||
|
|
||||||
|
Example commit message:
|
||||||
|
|
||||||
|
```
|
||||||
|
feat: v1.8.3 — sheet cache + DoH bypass + H1 keepalive + 431 + clearer errors
|
||||||
|
|
||||||
|
Three substantive PRs from contributors landed for this release:
|
||||||
|
|
||||||
|
- #443 by @euvel: optional spreadsheet-backed response cache in Code.gs.
|
||||||
|
Implements all 5 review suggestions from the design discussion (#400):
|
||||||
|
TTL-aware caching, 35 KB body-size gate, header rewriting on hit,
|
||||||
|
circular buffer for O(1) writes, Vary-aware compound keys.
|
||||||
|
|
||||||
|
- #439 by @dazzling-no-more: bypass Apps Script tunnel for known DoH
|
||||||
|
endpoints on TCP/443. Cloudflare/Google/Quad9/AdGuard/NextDNS/OpenDNS/
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
Conventions:
|
||||||
|
- **Subject line under 75 chars** (GitHub truncates longer)
|
||||||
|
- **Body wrapped at ~75-80 chars** for terminal-readability
|
||||||
|
- **PR-merge commits**: when merging PRs via `gh pr merge --merge`, use `--subject` and `--body` to write the merge commit. Format is the same — type prefix, short summary, body explaining what shipped and credit.
|
||||||
|
|
||||||
|
## Issue close reasons
|
||||||
|
|
||||||
|
Always pass `--reason`:
|
||||||
|
|
||||||
|
- `--reason completed` — the user's problem was resolved (their fix worked, or our fix shipped + they confirmed). For close comments, brief acknowledgement is fine; full marker not required.
|
||||||
|
- `--reason "not planned"` — duplicate, architectural limit, won't-fix, or stale and unrecoverable. Always link to the canonical thread when closing as duplicate.
|
||||||
|
|
||||||
|
For close comments, always include the destination issue if duplicate:
|
||||||
|
|
||||||
|
```
|
||||||
|
gh issue close N --reason "not planned" --comment "Closing as duplicate of #420 — full discussion + workarounds there."
|
||||||
|
```
|
||||||
|
|
||||||
|
## File names for reply markdown
|
||||||
|
|
||||||
|
Convention: write reply markdown to a temp file (e.g., `/tmp/r-<issue>-<topic>.md`) before posting via `gh issue comment N --body-file <path>`.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
- `/tmp/r-404-quota.md` — reply to #404 about a quota observation
|
||||||
|
- `/tmp/r-414-decoy.md` — reply to #414 about the decoy body
|
||||||
|
- `/tmp/r-pr-merged.md` — generic "merged + included in vX.Y.Z" PR thank-you reply
|
||||||
|
|
||||||
|
**Why use files instead of inline `--body`**: the inline `--body` argument runs through the shell, which interprets backticks (\`code\`) and `$()` substitutions. Issue replies frequently contain bash command examples with these patterns. The file approach sidesteps the quoting hell entirely. Use it by default.
|
||||||
|
|
||||||
|
The exception is very short replies like `Dup of #423.` — those can use `--body "Dup of #423."` directly.
|
||||||
|
|
||||||
|
## Tone
|
||||||
|
|
||||||
|
- **Warm but technical**. Iranian users in particular often write apologetically ("sorry for using AI for the translation", "sorry to bother") — answer them as you'd want to be answered: with care, with technical depth, with explicit acknowledgment that their report is valuable.
|
||||||
|
- **Don't promise fixes you can't deliver**. The Iran ISP throttle is not something the project can fix; saying "we're working on it" is OK, "we'll fix it next release" is not.
|
||||||
|
- **Don't pretend certainty**. v1.8.1's over-confident "AUTH_KEY mismatch" message in the decoy detection cost trust with reporters who turned out to be hitting one of the other candidate causes. v1.8.2 + v1.8.3 are explicitly less assertive ("could be one of the following four/six causes...") because being honest about uncertainty is the better long-term move.
|
||||||
|
- **Acknowledge community contributions liberally**. When a contributor's report shaped a roadmap item, say so by name. When a PR lands, thank them in the merge commit + PR comment + changelog. The project runs on goodwill.
|
||||||
|
- **Don't apologize excessively** but do correct yourself when wrong. Iterating publicly through wrong hypotheses to a correct one is fine; doubling down on a wrong assertion is not.
|
||||||
|
|
||||||
|
## Persian translation specifics
|
||||||
|
|
||||||
|
When writing Persian replies:
|
||||||
|
|
||||||
|
- **Half-spaces (ZWNJ — ``)** in compound words: `میخواهم` (not `میخواهم` or `می خواهم`), `نمیتوانم` (not `نمیتوانم`)
|
||||||
|
- **Persian numerals**: optional but common in formal writing — `۲۰،۰۰۰` instead of `20,000`. Code/commands always Latin numerals.
|
||||||
|
- **English technical terms in Persian text**: leave them in Latin script with surrounding Persian particles. Example: `از طریق Apps Script روی Google` (not transliterated)
|
||||||
|
- **Quotation marks**: Persian uses `«...»` rather than `"..."` for prose. Code/commands use `"..."` regardless.
|
||||||
|
- **The reply marker stays in English** as established. Don't translate `reviewed by` to Persian.
|
||||||
|
|
||||||
|
## DOPR cycle structure
|
||||||
|
|
||||||
|
When triaging a batch of issues/PRs, work through them in this order:
|
||||||
|
|
||||||
|
1. **Read everything first** — list PRs, list recently-updated issues, scan headlines. Don't reply to issue 1 before knowing what issues 2-15 contain. Often there are clusters that should be addressed together (e.g., five users all hit the v1.8.0 decoy on the same day).
|
||||||
|
2. **Triage by pattern** — match each issue to a pattern from `issue-patterns.md`. Issues that match a pattern get pattern-canonical replies (with specifics drawn from the user's actual log lines). Issues that don't match a pattern get individual attention.
|
||||||
|
3. **Substantive PRs first** — if a PR has tests passing and looks mergeable, merge it. Then your subsequent issue replies can reference "shipped in vX.Y.Z" instead of "queued for next release".
|
||||||
|
4. **Reply in batches but not as templates** — write each reply to address that user's specific log lines, config quirks, or terminology. Templated replies are easy to spot and erode trust.
|
||||||
|
5. **Close cleanly** — if an issue was a duplicate, close at the end of your reply with the close-comment pointing to canonical thread. If it's awaiting user verification, leave open with last comment from you.
|
||||||
|
6. **Cut releases when work lands** — don't accumulate fixes across multiple work sessions. Each session that lands user-visible code → one tag → one release.
|
||||||
Reference in New Issue
Block a user