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 |
--link-ip <IP> |
auto-detected | IP shown in the tg:// link (see Router deployment) |
--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, TG_LINK_IP).
Examples
# Standard run (random secret, DC 2 + 4)
# Custom port and extra DCs
# Router deployment: listen on all interfaces, let all LAN devices use the proxy
# 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.
Router deployment
Run the proxy on your router with --host 0.0.0.0 so it accepts connections
from all LAN devices:
When --host 0.0.0.0 is used, the proxy auto-detects the router's LAN IP
address and uses it in the generated tg:// link, so you can share the same
link with every device on your network.
If auto-detection picks the wrong interface, override it explicitly:
Note: The default
--host 127.0.0.1only accepts connections from the machine running the proxy. Other devices on the network will not be able to connect unless you change this to0.0.0.0(or the router's LAN IP).
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