Skip to content

v1.7.1

Latest

Choose a tag to compare

@github-actions github-actions released this 04 Aug 10:23
742db26

v1.7.1 — cooler, lighter, and it stops dropping your videos

Measured on the same device, same config, before and after 👇

v1.6.5 v1.7.1
🔥 Full TLS handshakes 28.1 /min 1.5 /min −95%
🎬 Media connections falling back to raw TCP 14.8% 0.0% fixed
🧠 Relay buffers @ 200 connections 12.5 MiB 3.1 MiB −75%
✅ Tests 69 181 +112

⚡ Your device runs cooler now

The proxy used to do a full TLS handshake on every single connection. On a phone or a router, that key exchange was the biggest source of CPU load — and heat.

The cause: the TLS config was rebuilt per connection, and rustls keeps session-resumption tickets inside that config. A config that lives for one connection can never resume anything.

One tester's 72-minute log: 958 handshakes, 958 × No cached session, zero resumptions.

Now the config is built once and shared, so connections to the same DC or Cloudflare domain resume instead of starting over.

Result: 88 of 96 handshakes resumed in a follow-up run on the same device.


🎬 Videos and stickers no longer fall through to raw TCP

Media connections try the kws{N}-1 DNS record first. That record is optional and most people don't create it — so media spent its first attempt on a name that doesn't resolve, and the working record it fell back to was then skipped as "already tried".

        non-media  →  kws2.domain  ✗   kws2-1.domain  ✗   kws2.domain  ← 2 tries
        media      →  kws2-1.domain ✗  kws2.domain    ✗                ← 1 try  😞

Media got half the attempts of everything else. In one log, 9 of 10 fallbacks to raw TCP were media — and each one burns a full --tcp-fallback-timeout before your client gives up and retries.

Both orderings now get the same two attempts.

Result: media failover to TCP went 14.8% → 0.0%.


🧠 Lighter on memory

This proxy runs on routers. Everything allocated per connection gets multiplied by every client you have.

  • Relay buffers: 64 KiB → 16 KiB per direction — the same cap the FakeTLS bridge always used. Costs a few more read calls, nothing else.
  • Message splitter no longer copies a second full-size buffer on every read, no longer clones on flush, and hands back memory grabbed by a big media packet once traffic settles.
  • Routing no longer builds and throws away a HashMap for a single DC lookup.

🐛 Fixes

FakeTLS sessions could die silently. With --listen-faketls-domain, a client TLS record larger than the read buffer looked exactly like the client hanging up — the session ended with nothing in the log. Fixed.

Session byte counts were lying to you. Whichever direction was still running when the other finished got cancelled, taking its counter with it. In one 137-session log, 136 sessions reported one direction as exactly 0.0B. Both directions now report as they go.


🔍 Logs tell you who hung up

- DC2m WS session closed: ↑2.2KB  ↓65.3KB  0.3s
+ DC2m WS session closed by upstream: ↑2.2KB  ↓65.3KB  0.3s
+ DC2m WS session closed by client:   ↑34.9KB ↓3.1MB   48.5s

A session that moves no data looks identical whichever side closed it — and "your client walked away while we were connecting" versus "Telegram dropped us" call for completely different fixes.


🧱 Under the hood — the refactor behind all of this

The connection handler was ~840 lines holding two near-identical fallback ladders. Routing is now separated from bridging: one function walks the ladder and returns a connected upstream, so the client's socket halves move into exactly one bridge call.

That removed:

  • 3 copies of the Cloudflare-proxy ladder
  • 2 copies each of the Worker and upstream-MTProto ladders
  • 5 duplicated bridge call sites

Also collapsed: four hand-rolled cooldown maps into one type, two byte-identical relay bridges into one, and the MTProto secret layout (dd/ee prefix handling) out of four open-coded copies. The file-descriptor budget logic moved out of main.rs into its own testable module.

Tests went 69 → 181, reorganised one file per subsystem, with new coverage for the inbound FakeTLS handshake end-to-end, the Cloudflare fallback orderings, TLS resumption, secret parsing, and splitter buffer reuse.

Dependencies updated within semver.


📦 Upgrading

Nothing to change. No flags added, removed or renamed. No configuration behaves differently.

ℹ️ --buf-kb / TG_BUF_KB is accepted but has no effect, and hasn't for some time. It's now documented as such instead of being silently dead.


⚠️ Known issue

#81 tracks reports that some videos still need a few attempts to start on Cloudflare-only setups. Every proxy-side cause found so far is fixed here, and failure metrics now match or beat the Python upstream — but the symptom isn't fully gone and the remaining difference isn't pinned down yet. Logs welcome.


💚 Thanks

@Dum4G — for the sustained testing, the strace captures, and the upstream comparison runs. Three of the four fixes in this release exist because of those logs, and two of them proved my first diagnosis wrong.

The Anthropic team — for the Max 20x subscription through the Claude for Open Source Program.