github valnesfjord/tg-ws-proxy-rs v2.4.5
tg-ws-proxy-rs v2.4.5

6 hours ago

--check can now probe the proxy's own listener, not just the upstreams it is configured with:
--check-listener serves as usual, sends a real MTProto request through its own socket, and requires
Telegram's resPQ back.

Why

--check answers "will this box reach Telegram", and it answers it for every tier the config names —
CF domains, a Worker, upstream MTProto proxies. What it never covered is the listener a client
actually connects to: it can be up with a secret no client has, and the tier the routing picks can be
the one that does not work here, while every upstream probe still prints OK.

The handshake alone cannot close that gap, and the existing plain-MTProto upstream probe shows why: it
sends the 64 obfuscated bytes and never reads a reply, so a proxy that accepts the handshake and then
has nowhere to forward the connection passes the check. The Worker probe was fixed for exactly this
reason in #93 — a tunnel has to prove the far end is a DC, not merely that something answered.

Added

  • --check-listener (env TG_CHECK_LISTENER, implies --check). The process binds and serves as
    usual, probes the socket it just bound with a client-side obfuscated handshake and a real
    req_pq_multi, and requires the reply to decrypt to resPQ. It is the only check that covers the
    whole chain: the inbound handshake, the tier the routing picks and the DC.

    • The probe talks to this process's own socket, so the secret, the address and the inbound mode are
      the ones actually being served — nothing to repeat by hand, and TG_CHECK=true TG_CHECK_LISTENER=true in a container tests something instead of finding nothing listening.
    • The probe reaches the bound socket over loopback, never through an outbound proxy.
    • A listener configured with --listen-faketls-domain is reported as skipped rather than failed:
      its first byte is a TLS record, so the plain probe cannot speak to it. A run whose only probe was
      skipped exits non-zero, because nothing was verified.
    • The wait covers the connect timeouts the routing can spend before the DC answers, not just the
      handshake budget: on a blackholed network the direct WebSocket attempt alone can burn
      --ws-connect-timeout before the next tier is tried.

Notes

  • The probe checks the listener and the routing, not the tg:// link: it reaches the bound socket
    over loopback, so a wrong --link-ip, a missing port-forward or a LAN firewall rule still passes.
  • FakeTLS inbound is not covered: that needs the client half of the camouflage — ClientHello signing
    and app-data records in both directions. It is reported as skipped rather than half-implemented.
  • The msg_id in the probe's request comes from the local clock, as the protocol defines it. On a
    router whose RTC is still unsynced after boot the DC may reject it, while real clients — which use
    their own clocks — keep working.

Don't miss a new tg-ws-proxy-rs release

NewReleases is sending notifications on new releases.