Repository navigation
v1.1.0
Telegram's WEB proxy transport, and the two secret forms that reach it
The headline is a new connection type. Telegram Desktop 7.1 added a WEB proxy: the client loads a page from a hostname you control, inside its own WebView, and that page carries MTProto over a WebSocket. A network in between sees a browser visiting a website over genuine TLS — there is no obfuscated handshake to fingerprint and no FakeTLS to distinguish, because the TLS is real.
Reaching it needed the rest of this release. A WEB-capable client refuses an ee secret before it opens a socket, so serving one meant teaching the proxy the two pre-FakeTLS secret forms (dd and plain) and giving them their own listening sockets.
WEB proxy transport
mtg-proxy web-relay <config>runs the relay as a second process of the same binary, reading the same config file.--checkvalidates the[web]table and exits.mtg-proxy generate-web-key <path>writes its 32-byte token signing key.- Three-tier recognition on the public origin: a client holding a capability the relay minted gets the bridge page; an authentic-but-unusable one gets a local uncacheable 404 that is never forwarded; everything else gets your cover site, on every path — including the API ones. Nothing ships as a default cover page, because a starter page many operators installed would be a better signature than anything the protocol leaks.
- Flow control is bounded in both directions, per stream and per session, with a global backstop across the process.
mtg-proxy accessprints awebobject per secret, and/statsadvertisesweb-relayinfeaturesso a panel can tell whether the binary supports it.- contrib/web-relay/ carries the deployment: the Caddy vhost, the four hops that have to carry the client's address, and an acceptance checklist.
The three secret forms
A secret is sixteen key bytes. The ee / dd / bare-hex prefix only tells the client which door to knock on, so an existing user keeps one name, one quota bucket and one set of counters whichever form they use — nothing has to be re-issued.
- New
[[listener]]config table declares extra obfuscated2 sockets, each with its ownacceptset,publicacknowledgement,proxy-protocoland unknown-secret disposal policy. bind-tois, and stays, FakeTLS-only. One port never serves both entry paths and there is no sniffing./statsreports what each listener accepts, and per-secret modes.
Reload
The relay re-reads [secrets] on SIGHUP and swaps its profile set atomically. Only the secret set is reloadable — any other [web] change is refused whole with restart required rather than half-applied, and a reload that cannot read or validate the file keeps the running set.
Fixes worth knowing about
- FakeTLS: an unauthenticated client hello was buffered before its length was bounded.
- Config: byte sizes truncated on 32-bit builds.
- API: neither HTTP server had a header read deadline.
- Obfuscation: bytes returned alongside a read error were dropped instead of decrypted; the handshake plaintext prefix was not kept on the wire.
- Proxy: a PROXY-protocol header that would not format panicked.
- Doppelganger: noise size could exceed what a TLS record can carry.
- Middle proxy: padding was declared unconditionally; padded-intermediate padding is 0-15, not 0-3.
Supply chain
Every release now publishes a checksums file, an SPDX SBOM of the module graph, and a signed build provenance attestation covering all of it — including the checksums file itself. Verify with:
gh attestation verify <file> --repo Arman2122/mtg-proxy --signer-workflow Arman2122/mtg-proxy/.github/workflows/go.yml
Upgrading
Additive only. No config key was renamed or removed, release asset names are unchanged, and the default accept-modes is still ["ee"] — an existing deployment behaves exactly as it did on v1.0.1 until you add a [[listener]] or a [web] table.
Full Changelog: v1.0.1...v1.1.0