tg-ws-proxy-rs
Telegram MTProto WebSocket Bridge Proxy — a Rust vibecoded port of Flowseal/tg-ws-proxy.
Listens for Telegram Desktop's MTProto connections on a local port and tunnels them through WebSocket (TLS) connections to Telegram's DC servers. Useful on networks where direct TCP traffic to Telegram is blocked or throttled.
Telegram Desktop → MTProto (TCP 1443) → tg-ws-proxy-rs → WS (TLS 443) → Telegram DC
Why Rust?
| Python original | This port | |
|---|---|---|
| Runtime | CPython required | Single static binary |
| Memory | ~30–50 MB | ~3–5 MB |
| CPU | Higher | Lower (compiled) |
| OpenWrt | Needs Python install | Just copy the binary |
| Static build | No | Yes (musl) |
Quick Start
Pre-built binaries
Download from the Releases page.
Build from source
# Debug build
# Optimised release build
# Static binary for Linux x86_64 (e.g. for Docker scratch images)
The release binary is at target/release/tg-ws-proxy (or
target/<target>/release/tg-ws-proxy for cross-compiled targets).
Cross-platform builds with cargo-zigbuild
cargo-zigbuild uses the Zig
compiler as a drop-in C cross-linker so you can build for every platform from
a single Linux or macOS host without installing any platform SDKs.
# Install cargo-zigbuild and Zig
# Add all required Rust targets in one shot
# Build for all platforms
Note: Building macOS targets (
*-apple-darwin) requires the macOS SDK (XCode Command Line Tools). On Linux you can useosxcrossto supply the SDK and then setSDKROOT/MACOSX_DEPLOYMENT_TARGETappropriately before runningcargo zigbuild.
Usage
tg-ws-proxy [OPTIONS]
| Flag | Default | Description |
|---|---|---|
--port <PORT> |
1443 |
Listen port |
--host <HOST> |
127.0.0.1 |
Listen address |
--secret <HEX> |
random | 32 hex-char MTProto secret |
--dc-ip <DC:IP> |
DC2 + DC4 | Target IP per DC (repeatable) |
--buf-kb <KB> |
256 |
Socket buffer size |
--pool-size <N> |
4 |
Pre-warmed WS connections per DC |
-q / --quiet |
off | Suppress all log output |
-v / --verbose |
off | Debug logging |
--danger-accept-invalid-certs |
off | Skip TLS verification |
Every flag has a matching environment variable (TG_PORT, TG_HOST,
TG_SECRET, TG_BUF_KB, TG_POOL_SIZE, TG_QUIET, TG_VERBOSE, TG_SKIP_TLS_VERIFY).
Examples
# Standard run (random secret, DC 2 + 4)
# Custom port and extra DCs
# Verbose logging
# All options via environment variables (useful for Docker / systemd)
TG_PORT=1443 TG_SECRET=deadbeef...
On startup the proxy prints a tg://proxy?... link you can paste into
Telegram Desktop to configure it automatically.
Telegram Desktop Setup
- Settings → Advanced → Connection type → Use custom proxy
- Add MTProto proxy:
- Server:
127.0.0.1 - Port:
1443(or your--port) - Secret: shown in the proxy startup log
- Server:
Or use the tg://proxy?... link that is printed on startup.
Cross-compilation for OpenWrt
OpenWrt uses musl libc and runs on MIPS, ARM, and ARM64 CPUs. Building a fully static Rust binary requires:
- A C cross-compiler for your target (used by
ring/aws-lc-sys) - The matching Rust target
ARM64 (aarch64) — e.g. GL.iNet MT6000, Banana Pi R4
# Install the cross toolchain (Ubuntu/Debian)
# Add the Rust target
# Uncomment the [target.aarch64-unknown-linux-musl] section in .cargo/config.toml,
# then build:
ARM (armv7) — e.g. older GL.iNet routers, some TP-Link models
# Uncomment the armv7 section in .cargo/config.toml
MIPS LE — e.g. TP-Link WR series
# Uncomment the mipsel section in .cargo/config.toml
Using cross (easier alternative)
cross uses Docker to manage toolchains:
OpenWrt procd init script
Create /etc/init.d/tg-ws-proxy:
#!/bin/sh /etc/rc.common
USE_PROCD=1
START=90
STOP=10
PROG=/usr/local/bin/tg-ws-proxy
How it works
- Telegram Desktop connects to the proxy on
127.0.0.1:1443. - The proxy reads the 64-byte MTProto obfuscation handshake, validates the secret, and extracts the target DC id and transport protocol.
- A WebSocket connection is opened to
wss://kwsN.web.telegram.org/apiws(using the DC-specific domain as TLS SNI but routing TCP to the configured IP). - The relay init packet is sent to Telegram, and bidirectional bridging begins with AES-256-CTR re-encryption (client keys <=> relay keys).
- If WebSocket is unavailable (redirect response), the proxy falls back to direct TCP on port 443.
- A small pool of pre-connected WebSocket connections is maintained per DC to reduce connection latency for subsequent clients.
Project structure
src/
main.rs — Entry point, CLI parsing, server startup, banner
config.rs — ProxyConfig struct, argument parsing, env-var aliases
crypto.rs — MTProto obfuscation: handshake parsing, relay init generation,
AES-256-CTR key derivation and cipher construction
splitter.rs — MTProto packet splitter for correct WebSocket framing
ws_client.rs — WebSocket client for Telegram DC connections (IP routing + SNI)
pool.rs — Pre-warmed WebSocket connection pool per DC
proxy.rs — Client handler, re-encryption bridge, TCP fallback logic
.cargo/
config.toml — Cross-compilation target presets (commented out)
Configuration via environment
TG_HOST=0.0.0.0
TG_PORT=1443
TG_SECRET=0123456789abcdef0123456789abcdef
TG_POOL_SIZE=4
TG_BUF_KB=256
TG_QUIET=true
TG_VERBOSE=false
License
MIT