Auto-committed by release workflow so users behind GitHub-Releases-page filtering can download via the in-repo releases/ folder. The GitHub Release page itself still has the canonical versioned artifacts; this folder is the fallback path for users who can only reach the static source tree (Code → Download ZIP).
@bankbunk reported (#460) that on a 1 Gbps VPS, raw MP4 streams in Full
mode died with `batch JSON parse error: EOF while parsing a string at
line 1 column 52428685` minutes into playback. Root cause: drain_now
took the entire per-session read buffer in one shot. On high-bandwidth
VPS the reader task fills the buffer with tens of MiB between polls;
the resulting batch response (raw + base64 1.33× + JSON envelope)
exceeded Apps Script's ~50 MiB hard cap; Apps Script truncated mid-base64;
the client's serde_json parse hit EOF and the stream tore.
Fix: drain_now now returns at most TCP_DRAIN_MAX_BYTES (16 MiB) per call
and leaves the tail in the buffer for the next poll. EOF is held back
until the buffer is fully drained so partial drains don't tear the
session prematurely. Three regression tests cover the cap, the under-cap
pass-through, and the EOF-holdback case (33 tunnel-node tests passing).
@bankbunk's wondershaper rate-limit workaround (40 Mbps cap on the VPS
interface) is no longer necessary — high-bandwidth VPS users can run at
line rate again.
Mirror of ~/.claude/skills/mhrv-rs-maintainer/ — SKILL.md plus eight reference
files plus assets. Cloud-scheduled agents clone the repo fresh on each fire
and have no access to the maintainer's local home directory; embedding the
skill in docs/maintainer/ lets them read the same canonical context as the
local maintainer and produce replies indistinguishable from a local DOPR
session.
The local copy at ~/.claude/skills/mhrv-rs-maintainer/ remains the source of
truth; this directory mirrors it.
Auto-committed by release workflow so users behind GitHub-Releases-page filtering can download via the in-repo releases/ folder. The GitHub Release page itself still has the canonical versioned artifacts; this folder is the fallback path for users who can only reach the static source tree (Code → Download ZIP).
Adaptive batch coalescing from @yyoyoian-pixel based on field testing in Iran.
Replace fixed 8ms batch coalesce with adaptive 40ms-step / 1000ms-max scheme. Apps Script adds ~1.5s overhead per HTTP call — packing more ops into each batch means fewer total calls. Field testing showed P75 RTT 6.2s → 3.0s, fast (<3s) batches 61% → 74-85%.
Both values configurable via config.json (coalesce_step_ms, coalesce_max_ms) and Android UI Advanced sliders (10-500ms / 100-2000ms).
Note: desktop UI's to_config() needs follow-up to round-trip the new fields. Filing immediately as a separate commit so v1.8.4 can ship both PRs together.
Tunnel-node stability fix from @yyoyoian-pixel based on field testing in Iran.
- LONGPOLL_DEADLINE 5s → 15s: persistent connections (Telegram XMPP :5222, Google Push :5228) stay alive instead of forcing re-handshakes every 5s
- Replace fixed 30ms straggler settle with adaptive 40ms-step / 500ms-max — packs more session responses into each batch, breaks early when all ready
Local verification: tunnel-node 30/30 tests pass, main crate 160/160 tests pass, both build clean.
Replace the fixed 8ms batch coalesce window with an adaptive scheme:
after each new op arrives, wait another 40ms (step) for more ops.
The timer resets on every arrival, up to 1000ms (max) from the first
op. Both values are configurable via config JSON (coalesce_step_ms,
coalesce_max_ms) and two new sliders in the Android Advanced section.
Why this helps: Apps Script adds ~1.5s overhead per HTTP call. The
previous 8ms window barely caught any concurrent ops — most batches
carried just 1 op. With 40ms/1000ms, batches average 2-3 ops,
reducing total Apps Script calls for the same workload.
Tested on device in Iran:
Before: P75=6.2s, 61% fast (<3s), ~1 op/batch
After: P75=3.0s, 74-85% fast, ~2-3 ops/batch
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Apps like Telegram maintain persistent XMPP connections (:5222) and
Google Push uses :5228 — both rely on long-lived sessions with
periodic heartbeats. At the previous 5s long-poll deadline, the
tunnel-node returned empty responses frequently enough that Telegram
interpreted it as connection instability and rotated sessions. Each
reconnect costs a full TLS handshake (~4s through Apps Script),
causing visible video/voice interruptions and buffering.
Raising the long-poll deadline to 15s keeps these persistent
connections alive: the tunnel-node holds the response open until
server data actually arrives (push notification, chat message, media
chunk) rather than returning empty every 5s. Tested on censored
networks in Iran where users reported smoother Telegram video
playback and fewer session resets.
The straggler settle is now adaptive (40ms steps, 500ms max): after
the first session in a batch gets data, keep checking every 40ms
whether neighboring sessions also have data. Break early when all
sessions are ready — no fixed 500ms wait when data is already there.
On high-latency relays where each Apps Script call costs ~1.5s
overhead, packing more session responses into one batch saves quota
and reduces total round-trips.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Auto-committed by release workflow so users behind GitHub-Releases-page filtering can download via the in-repo releases/ folder. The GitHub Release page itself still has the canonical versioned artifacts; this folder is the fallback path for users who can only reach the static source tree (Code → Download ZIP).
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/
CleanBrowsing/dns.sb/dns0.eu/AliDNS/doh.pub/Mullvad. Saves the ~2s
UrlFetchApp roundtrip per name without losing privacy (DoH is
already encrypted). Default on; users can opt out via tunnel_doh: true
or extend the list via bypass_doh_hosts.
- #438 by @dazzling-no-more: H1 container keepalive + 431 oversized-
headers + clearer port-collision message. Cherry-picks from upstream
Python (Apr 23-26 window). Keepalive prevents Apps Script V8 cold
starts (visible as YouTube stalls after pause); 431 replaces silent
socket drops on >64 KB headers (which caused browser retry loops).
Routes browser DoH lookups (Cloudflare, Google, Quad9, AdGuard, NextDNS, OpenDNS, dns.sb, dns0.eu, AliDNS, doh.pub, Mullvad) around the Apps Script tunnel via plain TCP. By @dazzling-no-more.
DNS-over-HTTPS is already encrypted; tunneling it adds 2s UrlFetchApp roundtrip per name without privacy benefit. New `bypass_doh_hosts` config (default true) lets users opt out. Gated to TCP/443 — private DoH on :8443 should use `passthrough_hosts`.
Local verification: cargo build clean, cargo test --lib 160/160 passing (+6 new matches_doh_host tests).
Auto-committed by release workflow so users behind GitHub-Releases-page filtering can download via the in-repo releases/ folder. The GitHub Release page itself still has the canonical versioned artifacts; this folder is the fallback path for users who can only reach the static source tree (Code → Download ZIP).
- src/bin/ui.rs: install_ui_tracing now takes config_level. Filter
precedence is RUST_LOG > config.log_level > info,hyper=warn. The
filter is wrapped in a reload::Layer; Save reinstalls it via
apply_log_level so users don't need to restart for a level change.
Fixes#401 (w0l4i) — config.log_level was previously dead on the
UI binary even though the CLI honored it via init_logging.
- src/tunnel_client.rs: v1.8.1 asserted "AUTH_KEY mismatch" on the
Apps Script placeholder body, but #404 (w0l4i) showed mixed
success/failure on the same script_id, which rules that out. The
body is also returned for Apps Script execution timeout, quota
tear, internal hiccup, and ISP-side truncation. Error message now
enumerates all four candidates and points to DIAGNOSTIC_MODE for
disambiguation.
Auto-committed by release workflow so users behind GitHub-Releases-page filtering can download via the in-repo releases/ folder. The GitHub Release page itself still has the canonical versioned artifacts; this folder is the fallback path for users who can only reach the static source tree (Code → Download ZIP).
Three small, ship-able-now changes from the past day's issue triage:
1. Client-side detection of the v1.8.0 bad-auth decoy HTML
(#404 w0l4i, #310 sina-b4hrm)
When mhrv-rs gets back the decoy HTML body that v1.8.0's Code.gs/
CodeFull.gs/tunnel-node return on bad AUTH_KEY, the client now
string-matches the body's distinctive "The script completed but
did not return anything" sentinel and emits an explicit ERROR
line naming AUTH_KEY mismatch as the likely cause + walking the
user through "redeploy as new version" + the DIAGNOSTIC_MODE
escape hatch — instead of the previous cryptic "WARN batch
failed: bad response: no json in batch response: <!DOCTYPE...".
Saves users hours of debugging. Reported pattern hits everyone
who edits Code.gs's AUTH_KEY without redeploying as a new version
(Apps Script doesn't auto-pick-up that change).
2. script_id in every batch-failure log (#404 w0l4i)
Previously WARN batch-failed lines didn't say which deployment
failed. In multi-deployment setups (5–10 deployments where
some have stale AUTH_KEY), users couldn't identify the culprit
without the per-deployment curl probe loop.
All four failure paths in tunnel_client::fire_batch — timeout,
bad response, decoy detection, missing-response-in-batch — now
include the script_id short prefix: `batch failed (script
AKfycbz4): ...`. Combined with #1 above, this is the first
reliable diagnostic for the "1 of 8 deployments has bad
AUTH_KEY" pattern.
3. New disable_padding config flag (#391 EBRAHIM-AM)
Default false (padding active = stronger DPI defense). For
users on heavily-throttled ISPs where v1.8.0's ~25% bandwidth
overhead from random padding compounds with the throttle and
pushes borderline-working batches into timeouts, setting
`"disable_padding": true` in config.json recovers headroom at
the cost of losing length-distribution DPI defense.
Don't flip on speculatively — only enable if you've measured
actual throughput improvement on your specific ISP path. For
users where Apps Script outbound flows freely, padding is free
defense.
Tested:
- cargo build --release --bin mhrv-rs: clean
- cargo build --release --bin mhrv-rs-ui --features ui: clean
- cargo test --release --lib: 154 passed
- UI FormState round-trips disable_padding through save/load
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two related changes to the main-channel cross-post (one message per
release that points at the files channel):
1. Post-link now uses the public-username form `t.me/mhrv_rs/<msg_id>`
instead of the private `t.me/c/<chat_id>/<msg_id>`. The latter only
resolves for channel members; the former works for everyone, so
recipients seeing the main-channel announcement can click through
to a specific release post even if they're not yet subscribed.
Wired via the existing `FILES_CHANNEL_USERNAME` workflow env var,
now defaulting to `mhrv_rs` (the channel's public username) if the
`vars.FILES_CHANNEL_USERNAME` repo variable is unset. Override per
repo if the channel is renamed.
2. Channel-join links rendered in the body of the main-channel post,
below the post-link:
لینک کانال:
https://t.me/mhrv_rs
و یا: https://t.me/+R1OyoHX2boA1ZDgx
Two forms cover the cases where one or the other is filtered:
- `t.me/mhrv_rs` — public username form, prettier, surfaces in
Telegram search
- `t.me/+<hash>` — invite link, the only join path that works for
private/restricted channels and for users whose client doesn't
resolve public usernames cleanly
Wired via new `FILES_CHANNEL_INVITE` env var, defaulting to the
current invite hash; override via `vars.FILES_CHANNEL_INVITE` if
rotated.
Per user request — not running this against v1.8.0 retroactively,
just wiring it up for the next release.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two changes on top of last commit:
1. SHA-256 ("تایید اصالت") now in every file caption. Each artifact's
caption gets a `<code>...</code>` line with the file's SHA-256 hex
so recipients can `sha256sum <file>` after download and verify it
matches what the channel posted. Defends against modified copies
if the channel ever gets relayed through a third party.
For chunked uploads (file > 45 MB), each part shows BOTH:
- SHA-256 of that specific part (verifies the chunk downloaded
intact before bothering to reassemble)
- SHA-256 of the full reassembled file (verifies the final result
after `cat <name>.part_* > <name>`)
2. Main channel post is now a cross-link, not files.
Previously the legacy `telegram` job in release.yml posted the
universal APK + full changelog as one sendDocument + sendMessage
pair to the main announcement channel.
New behaviour: telegram-publish-files.yml's last step posts a short
message to the main channel saying "v1.8.0 released, click here
for files" with a t.me link pointing at the files channel's
announcement anchor post. Recipients land on the anchor, scroll
to find the platform-specific artifact they need.
Link format: `t.me/c/<chat_id>/<msg>` for private channels (works
for members), or `t.me/<username>/<msg>` if `FILES_CHANNEL_USERNAME`
repo variable is set (works for everyone — useful if the files
channel is later made public).
Legacy telegram job in release.yml stays in source, dormant,
gated on `vars.TELEGRAM_NOTIFY_ENABLED == 'true'` (default false).
Comment updated to note the new workflow is the canonical path.
If both are turned on at once, the main channel gets two posts
per release.
Tested manually for syntax + caption rendering — actual SHA-256 values
will appear on the next workflow_dispatch run.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
New workflow + script that posts every artifact (Android APKs, Windows
ZIP, macOS .app + CLI tarballs, Linux glibc + musl, OpenWRT, Raspbian)
to the Telegram channel as separate sendDocument calls, each with a
Persian caption naming the platform variant and a `#v<NNN>` hashtag
(e.g. `#v180`, `#v1810`, `#v200`) so users can find a specific release
later via the channel's hashtag search.
Files larger than 45 MB (the Bot API's effective ceiling after multipart
+ caption overhead) are split into byte chunks named `<name>.part_aa`,
`.part_ab`, ... and posted with reassembly instructions in the caption.
For the v1.8.0 file set everything is ≤41 MB so the split path is
defensive.
Decoupled from `release.yml` so it can be re-triggered for any past tag
via `workflow_dispatch` without rebuilding artifacts — downloads from
the GitHub Release page directly via `gh release download`. Also
auto-runs on each successful `release.yml` completion via
`workflow_run`.
Hard-codes the channel ID `-1003966234444` (one well-known channel,
auditable in source). Reuses `secrets.TELEGRAM_BOT_TOKEN` which already
has post permissions there.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Mirror of the English README with the same setup paths (Cloud Run /
Docker prebuilt / Docker source / direct binary), env-var table, and
protocol section, plus a Persian-language FAQ section answering the
specific questions users keep filing:
- bandwidth overhead (~25-30% from base64 + JSON envelope + v1.8.0
random padding)
- whether all Android apps get tunneled (yes in Tunnel mode + VpnService;
no in Proxy mode)
- realistic per-flow throughput (~50-200 KB/s, bound by Apps Script's
per-roundtrip floor; horizontal-scale via more deployments)
- whether a VPS is required for Full mode (yes; not required for
apps_script or google_only)
- which VPS to pick (Hetzner CX11 €4/mo for general use; Cloud Run
free tier specifically for Iran users hit by #313 since destination
IP stays Google-internal)
Adds an `MHRV_DIAGNOSTIC` env-var row to both the English and Persian
env-var tables — was added in v1.8.0 but never documented.
Closes#372.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Auto-committed by release workflow so users behind GitHub-Releases-page filtering can download via the in-repo releases/ folder. The GitHub Release page itself still has the canonical versioned artifacts; this folder is the fallback path for users who can only reach the static source tree (Code → Download ZIP).
Five user-visible changes shipping together. Each is independently
useful + bounded; bundled because they're all "small architectural
hardening" that benefits from one release announcement.
1. Random payload padding (#313, #365 §1)
Every outbound Apps Script JSON request now carries a `_pad` field
of uniform-random length 0..1024 bytes (base64). Defeats DPI that
fingerprints on the tight length distribution of mhrv-rs's previous
per-mode-bound packet sizes. ~25% bandwidth on a typical 2 KB batch,
negligible against Apps Script's per-call latency floor. Backward-
compatible — old `Code.gs` deployments ignore the unknown field.
Applied at all three payload-build sites: single relay, single
tunnel op, batch tunnel.
2. Active-probing decoy: GAS bad-auth → 200 HTML (#365 §3)
`Code.gs` and `CodeFull.gs` now return a benign Apps-Script-style
placeholder HTML page on bad/missing AUTH_KEY instead of the JSON
`{"e":"unauthorized"}`. To an active scanner the deployment looks
like one of the millions of forgotten public Apps Script projects
rather than an obvious API endpoint. New `DIAGNOSTIC_MODE` const
restores JSON errors during setup; default false (production-strong).
3. Active-probing decoy: tunnel-node bad-auth → 404 nginx (#365 §3)
`tunnel-node` returns an HTTP 404 with an nginx-style HTML body on
bad auth instead of `{"e":"unauthorized"}`. Active scanners cataloging
the host see "static web server, nothing tunnel-shaped here." New
`MHRV_DIAGNOSTIC=1` env var restores verbose JSON during setup.
4. Fix: Full-mode usage counter stuck at zero (#230, #362)
`today_calls` / `today_bytes` were only being incremented on the
apps_script-mode relay path. Full-mode batches go through
`tunnel_client::fire_batch` which never wired into the counter.
Now `fire_batch` calls `record_today(response_bytes)` after each
successful batch — bytes estimated from the `d` (TCP payload) and
`pkts` (UDP datagrams) sizes in the BatchTunnelResponse. Full-mode
users now see real usage numbers.
5. Fix: quota reset countdown was UTC, should be PT (#230, #362)
Apps Script's UrlFetchApp daily quota resets at midnight Pacific
Time, not UTC. We were displaying the countdown to UTC midnight,
off by 7-8h depending on DST. New `current_pt_day_key()` and
`seconds_until_pacific_midnight()` helpers with hand-rolled US DST
detection (2nd Sunday March → 1st Sunday November = PDT, else PST)
so we don't pull `chrono-tz` and a ~3 MB IANA tzdb just for one
helper. UI label "UTC day" → "PT day". Tests pin DST window
boundaries against March/November of 2024, 2026, 2027 to catch
regressions in the day-of-week math.
Tested:
- cargo test --lib: 154 passed (was 152, +2 for DST window + day-of-week)
- cargo build --release: clean
- cargo build --release --bin mhrv-rs-ui --features ui: clean (macOS arm64)
- tunnel-node cargo test: 30 passed
- Android: ./gradlew assembleDebug succeeds; APK installs + launches
on mhrv_test emulator (arm64-v8a), no UnsatisfiedLink, no crash
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Auto-committed by release workflow so users behind GitHub-Releases-page filtering can download via the in-repo releases/ folder. The GitHub Release page itself still has the canonical versioned artifacts; this folder is the fallback path for users who can only reach the static source tree (Code → Download ZIP).
v1.7.10 release run shipped no assets due to two CI failures stacked:
1. The i686-pc-windows-msvc job (added in v1.7.7 for Win7 support per
#318) failed because Rust 1.77.2 — the last stable that produces
Win7-loadable binaries — can't parse modern transitive crate
manifests (`time` 0.3.47 in this case). Pinning transitives across
the dep tree at every MSRV bump in our deps isn't sustainable, so
the target is removed from the release matrix. Win7 32-bit users
self-build per #318's instructions.
2. The `release` job hit `actions/download-artifact@v4`'s 5-retries-
exhausted error on multiple artifacts. Same flake we worked around
in #288 for `commit-releases`. The `release` and `telegram` jobs
now use `gh run download` wrapped in a 3-attempt retry loop, mirror-
ing the working pattern.
v1.7.11 is the first full release after v1.7.9; ships #337 (Apps
Script gzip-decoded range probe) and #344 (Android Paste button) that
were tagged in v1.7.10 but never published as assets.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Patch release for the changes shipped via #337 (Apps Script range
probe gzip-decoded body handling) and #344 (Android Paste button on
13+) plus a CI fix that restores the Win7 i686 binary missing from
v1.7.9 (Cargo.lock format mismatch with Rust 1.77).
The Cargo.lock version=4 (Rust 1.78+) wasn't readable by the pinned
1.77.2 toolchain on the i686 job. Workflow now regenerates the
lockfile with the pinned toolchain on that job only, leaving every
other target unaffected.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Android 13+ restricts clipboard access for background-to-foreground
transitions — the auto-detect banner never appeared on resume.
Replace with a permanent Paste button that reads clipboard on tap
(user interaction grants clipboard permission).
Also: Export button is now icon-only (share icon) to save space.
Co-authored-by: yyoyoian-pixel <279225925+yyoyoian-pixel@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Accept a synthetic first range probe when Content-Range proves the whole entity was returned, even if Apps Script decoded the body and left compressed Content-Range metadata intact. The response is then rewritten to HTTP 200 with Content-Range removed and Content-Length based on the decoded body, avoiding an unnecessary fallback full GET.
Keep strict validation for real client Range requests and later chunks. Also recognize localized Apps Script bandwidth quota errors.
Co-authored-by: freeinternet865 <free@internet865.com>
Auto-committed by release workflow so users behind GitHub-Releases-page filtering can download via the in-repo releases/ folder. The GitHub Release page itself still has the canonical versioned artifacts; this folder is the fallback path for users who can only reach the static source tree (Code → Download ZIP).
Patch release for the Win7 i686 binary fix shipped via #323.
No code changes; CI workflow change only — Cargo, Gradle, and
changelog bumps in lockstep so the release produces a fresh
i686 Windows binary built against Rust 1.77.2.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ci(release): pin i686-pc-windows-msvc to Rust 1.77.2 for Win7 compat
Fixes#318. Rust 1.78 (May 2024) raised the std MSRV for Windows from
Win7 to Win10 by switching std::time to GetSystemTimePreciseAsFileTime,
a kernel32 export that doesn't exist on Win7 SP1. Building the i686
binary with stable Rust (currently 1.86+) produces an exe that fails
to load on Win7 with "the procedure entry point
GetSystemTimePreciseAsFile could not be located in the dynamic link
library kernel32.dll" — making the whole reason we ship i686 (legacy
Win7 32-bit boxes per #272) moot.
Add a per-matrix `rust_toolchain` knob; only i686-pc-windows-msvc uses
it, pinning to 1.77.2 (last stable that supports Win7). Other targets
remain on @stable and pick up regular Rust updates.
dtolnay/rust-toolchain switches from `@stable` to `@master` because
the per-tag aliases (`@stable`, `@1.77.2`) can't be selected via a
matrix variable — `@master` accepts the toolchain string as input.
Cache key gains a toolchain suffix so the 1.77.2 cache doesn't collide
with the stable cache for the same target, and a future toolchain bump
invalidates only the affected slot.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ci(release): make i686-pc-windows-msvc continue-on-error
Companion to the Rust 1.77.2 pin: if the deps' MSRV ever moves above
1.77, the i686 target will fail to build, but we don't want it to
block the rest of the release. Mirror the mipsel-softfloat approach.
If/when this triggers, options are dropping i686 entirely or moving
to the tier-3 i686-win7-windows-msvc target.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Auto-committed by release workflow so users behind GitHub-Releases-page filtering can download via the in-repo releases/ folder. The GitHub Release page itself still has the canonical versioned artifacts; this folder is the fallback path for users who can only reach the static source tree (Code → Download ZIP).
Patch release for the auto-blacklist of timeout-saturated deployments
shipped via #319. No new features; bugfix only — cargo, gradle, and
changelog bumps in lockstep so the release workflow can ship matching
artifacts.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Auto-committed by release workflow so users behind GitHub-Releases-page filtering can download via the in-repo releases/ folder. The GitHub Release page itself still has the canonical versioned artifacts; this folder is the fallback path for users who can only reach the static source tree (Code → Download ZIP).
The v1.7.7 tag commit (6885800) only updated the changelog; the
version field edits failed earlier due to file-state-changed-mid-edit
race. Fixing forward — Cargo.toml + build.gradle.kts now show 1.7.7
properly.
Workflow will build from main HEAD on workflow_dispatch, so the
v1.7.7 release-page artifacts will have the correct internal version
even though the tag commit itself doesn't include the version bump.
- #288 (@amiralishoja): adds i686-pc-windows-msvc to the release
matrix. 32-bit Windows users get mhrv-rs-windows-i686.zip on
every release.
- #290 (@dazzling-no-more): per-deployment longpoll fallback state
with TTL-based auto-recovery. Replaces a global AtomicBool that
one degraded deployment could permanently flip. Now the aggregate
legacy gate only fires when every configured deployment is marked,
and self-corrects on TTL expiry — upgraded tunnel-nodes rejoin
the fast path automatically. 4 new tokio::test virtual-time tests.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Auto-committed by release workflow so users behind GitHub-Releases-page filtering can download via the in-repo releases/ folder. The GitHub Release page itself still has the canonical versioned artifacts; this folder is the fallback path for users who can only reach the static source tree (Code → Download ZIP).
The commit-releases job's `actions/download-artifact@v4` step has
failed twice in a row (v1.7.5 retrigger, v1.7.6) with the same
shape: ~10 artifacts download successfully, then "Unable to
download artifact(s): Artifact download failed after 5 retries" on
the 11th-13th. The 10 that complete print their SHA256 digests
cleanly; the failure is unambiguously inside actions/download-
artifact, not on our side.
Workaround: pull from `gh release download` instead. The `release`
job populated the GitHub Release page a few seconds earlier with
the same artifacts; pulling from there reads from a different
CDN (Release-page blob store) with different retry / rate-limit
characteristics. Empirically more reliable for our 13-artifact
release size.
Filtered to *.tar.gz / *.zip / *.apk so we only fetch the user-
facing artifacts (skipping anything like checksum sidecars that
softprops/action-gh-release@v2 might add later).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
v1.7.4 added googlevideo.com to SNI_REWRITE_SUFFIXES on the theory
that video chunks should bypass the Apps Script relay. Multiple
users (#275 amirabbas117, #281 mrerf) reported total YouTube
breakage on v1.7.4: SNI-rewriting googlevideo.com:443 to a GFE IP
returned TLS handshake failure / wrong-cert error.
Root cause: googlevideo.com is served by Google's separate "EVA"
edge IPs, not the regular GFE IPs that the user's `google_ip`
typically points at. The SNI-rewrite tunnel TLS handshake against
a GFE IP for googlevideo.com SNI fails because the GFE IP doesn't
hold a googlevideo.com cert.
Pre-v1.7.4 behaviour restored: video chunks fall through to the
Apps Script relay path. Slower but reliable on every GFE IP.
The other v1.7.4 youtube_via_relay carve-out fixes (ytimg.com
correctly stays on SNI rewrite, youtubei.googleapis.com correctly
goes through relay) remain intact — those were a separate
improvement and still correct.
Future: if we want direct googlevideo.com routing, it needs a
separate `eva_edge_ip` config knob — users can populate from their
own EVA scan, defaulting to "use relay" if not configured.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
v1.7.5's block_quic config field broke the UI binary build because
src/bin/ui.rs constructs Config{} explicitly and I forgot to add the
new field there. CLI binary loads from JSON via serde so it didn't
trip — only the 4 UI-building targets failed (linux-amd64-gnu,
windows-amd64, macos-amd64, macos-arm64).
block_quic is round-tripped through the form (config-only for now,
no UI control) so save doesn't drop a user-set true.