CF-Workers-TGProxy README

The project's README from github.com/ToiCF/CF-Workers-TGProxy. As one markdown file: README.md.

CF-Workers-TGProxy

Cloudflare Workers 的 Telegram Web/MTProto WebSocket 代理中继(KWS Lanes)。

telegram_webproxy.js 是一个单文件 Cloudflare Workers 实现:它扮演 Telegram 官方 Web Proxy(td web-proxy)后端,把客户端(Desktop / Android)在隔离 webview 里发起的 WebSocket lanes,直接转接到 Telegram Web K 自己的 KWS 通道(kws{dc}[-1].web.telegram.org/apiws)。

它不再依赖一套独立的 MTProxy / middle proxy 后端:Worker 在本地解开客户端 MTProto 传输层的 obfuscation,再用自己生成的随机密钥重新封一层,拨 Telegram 官方的 Web K 入口。内层 MTProto 载荷始终由客户端端到端加密,Worker 不接触、也不做 mtp 层加解密。

核心能力

当前代码主线包含:

代码主路径

flowchart LR
  TG["Telegram Desktop / Android"] -->|webview 加载 web-proxy 页面| P["Worker 页面 (nonce CSP)"]
  P -->|postMessage 端口| L["lanes: 每条流一条 WebSocket"]
  L -->|"tproxy-lane-v1.token.sid"| WS["/api/v1/ws"]
  WS --> S["mkSession → mkStream"]
  S -->|"解 obfuscation (AES-CTR)"| RK["mkHead 重新封层"]
  RK -->|"dial wss://kwsN[-1].web.telegram.org/apiws"| KWS["Telegram Web K / KWS"]
webview 页面
  → 建立 /api/v1/session(POST,带 bootstrap)
  → 拿到 session token + X-Carrier-Mode: websocket-lanes
  → 按流开 lane:/api/v1/ws + 子协议 tproxy-lane-v1.<token>.<sid>
  → Worker: 解 obfuscation → 重新封头 → 拨 KWS → 中继

为什么从官方 MTProto 转向 KWS 转换

Telegram 官方的 web-proxy 模型(tproxy)需要一套独立后端:客户端连到本地/自建的服务,再经它转发到远端 MTProxy,链路里多一跳、多一套需要自己运维的加解密中转。

本实现把这一整套换掉:

维度 经 middle/MTProxy 的官方 web-proxy 路径 本实现(KWS 转换)
后端 需自建 MTProxy / middle proxy 无,Worker 即后端
传输面加密 代理侧承担 mtp 层与 obfuscation 只落一层 obfuscation(AES-CTR)重签
出站目标 自建中转服务器 Telegram 官方 kws*.web.telegram.org
内层 MTProto 载荷 端到端(客户端) 端到端(客户端,Worker 不接触)

也就是说,转向 KWS 的关键点是:Worker 只处理传输层,不处理 mtp 层。客户端 MTProto 载荷原样透传,Worker 只对 64 字节 obfuscation 头做一次解、一次重签,再走 Telegram Web K 用的同一入口,边缘可达性与线路质量都直接吃官方域名。

性能理论上提升多少倍

以下为基于当前代码路径的理论模型(标记 [推断]),不是本仓库的实测基准。

综合到瓶颈场景,理论提升从 约 2x 起(仅算加解密 + 去跳),小包风暴场景在汇聚部分叠加后可更高。[推断]

设计重点

1. 多 lane 直接替代 Durable Object

公开的 Workers web-proxy 实现大多用 Durable Object 来维持"单条长连接 / 单份状态"。DO 是单线程且需要路由到固定实例,上限和排队都集中在那个对象上。

本实现改为按流拆 lane:

TG 客户端本身就有多流、并且会自动分流,所以 lanes 让每条流各自背压、各自排队,而不是把整条会话压进一个单线程对象。这正是"不需要 DO、TG 侧也感觉不到单连接限流"的设计依据。

2. 小包汇聚

上下行各自把连续小块先收进一颗薄核,再尽量并成更少的实际写入:

上传: collect -> bundle -> peer.write()
下载: collect -> bundle -> dnPack 门控 -> ws.send()

目的都是削减高频小 frame 带来的固定调度成本,而不是再造一层重型队列。

3. 鉴权与租约

4. 页面隔离

5. 禁用 WebSocket 压缩协商

握手响应里显式把 Sec-WebSocket-Extensions 置空。relay 主线传的是原始 TCP 字节流(内部多为 TLS/HTTP2 等高熵数据),在 Worker 这层再做 WS 压缩换不来稳定收益,反而抬高热路径 CPU,因此按负优化处理。

当前配置

变量 意义 默认值
dnPack 下行聚合上限 32 * 1024
upPack 上行合包目标 20 * 1024
dnMs 下行 quiet-window 1(ms)
lane 上限 单会话并发 lane 数 128
laneMax / laneItems 单 lane 字节 / 条数上限 8MB / 1024
qMax / iMax 整会话字节 / 条数上限 32MB / 16384
bootstrap TTL 一次性接入 token 2 分钟
session TTL 会话 token 5 分钟
KWS 端点 Telegram Web K 出站 kws{1..5}[-1].web.telegram.org/apiws

密钥配置

绑定 意义 要求
SECRET 代理密钥:AES-CTR 传输密钥派生 + ?bridge= 能力签名 32 位 hex,或 dd / ee / ef 前缀 + 32 位 hex(共 34);字符集 0-9a-f;大小写不敏感,建议小写
BOOT bootstrap token:签发 bootstrap / session,并内联进下发页面 仅要求非空,建议 ≥ 16 字符随机串

生成命令:

目标 命令 输出
SECRET openssl rand -hex 16 32 位 hex
SECRET(带前缀) printf 'dd%s\n' "$(openssl rand -hex 16)" 34 位 hex
BOOT openssl rand -hex 24 48 位 hex

openssl rand 走系统 CSPRNG,是真随机。任一绑定缺失,mkCfg 直接抛错,/health 会返回 500 而不是 {"ok":true}。

路由

路径 方法 说明
/ GET / HEAD 校验 bridge 签名后下发 web-proxy 页面
/health GET {"ok":true}
/api/v1/session POST 建会话,返回 session token
/api/v1/session DELETE 注销会话
/api/v1/ws GET lane WebSocket(需 Upgrade 与正确子协议)

文件

文件 说明
telegram_webproxy.js Worker 主实现:页面 / session / lane / obfuscation / KWS 中继

访问优化(CNAME / SaaS 优选)

相关链接

Stargazers over time

Stargazers over time

CF-Workers-TGProxy (ToiCF/CF-Workers-TGProxy)

censorship-circumvention
Searching Database…

What is this?A single-file Cloudflare Workers script that acts as Telegram's own Web Proxy (td web-proxy) backend, relaying the WebSocket lanes opened by Telegram Desktop/Android's embedded webview straight into Telegram's own Web K (KWS) channel.
Is this a full MTProxy server, or something different?It is not a traditional MTProxy/middle-proxy backend you have to run yourself; it only unwraps and re-signs the client's MTProto transport-layer obfuscation and forwards the connection directly to Telegram's official KWS WebSocket endpoint.
How do I get it running?Deploy the single file 'telegramwebproxy.js' as a Cloudflare Worker and configure two secret bindings, SECRET (an AES-CTR key, 32 hex chars) and BOOT (a bootstrap signing token); without both, the health check fails.
Do I need my own domain or Cloudflare account for this?You need a Cloudflare Workers account to deploy it, and the README recommends pointing your own domain via CNAME or using Cloudflare SaaS custom hostnames for better routing quality to Telegram's edge.
Are the performance claims actually measured benchmarks?No, the stated speedups (roughly 2x and up) are explicitly labeled as theoretical inferences based on a related micro-benchmark (GrainTCP), not measurements of this repository itself.
Why use multiple WebSocket lanes instead of a Durable Object?Durable Objects are single-threaded and pin a whole session to one instance, creating a bottleneck. This project instead gives each Telegram stream its own WebSocket lane (up to 128 per session), so each lane queues and backpressures independently inside the same isolate.
How does the bootstrap/session token auth flow work?The page issues a one-time nonce plus a short-lived bootstrap token (2 minutes); the client exchanges it via POST /api/v1/session for a session token (5 minutes) tied to a per-session lease that tracks active lanes, and DELETE /api/v1/session revokes it immediately.
Why switch from the official MTProxy model to KWS routing?Dialing straight into Telegram's own Web K (KWS) endpoint removes an extra relay hop and the need to run a separate MTProxy/middle-proxy backend, cutting per-packet crypto work to a single obfuscation re-sign instead of a full mtp-layer relay.
How does it handle bursts of tiny frames?Small outgoing and incoming frames get coalesced before being written: up to 20KB per upstream batch and up to 32KB downstream with a 1ms quiet-window flush, which cuts the fixed per-frame scheduling cost.
Creator
ToiCF
Size
<0.1 MB
Stars
907
Updated
CommitOct 11
Age
9d
Commits · 90 Days