The upstream tier order can now be pinned separately for media and non-media
connections.
Why
Every client handshake already says whether it wants a media DC (negative DC
index) — where Telegram clients put photo/video/file transfers — or not. The
ladder was the same for both classes anyway, so there was no way to say
"control traffic direct over TCP from this network's residential IP, media
through Cloudflare" — which matters when keeping the control channel on the
residential IP is what keeps the account off the anti-spam radar (bulk
transfers can then ride any tunnel), or when only one class of upstream works
on your network.
Added
--pinned-upstream <TIERS>/TG_PINNED_UPSTREAM— pin the tier order.
Applies to media connections too: media must not quietly slip back to the
default ladder and undo the egress decision the operator made with the
single flag.--pinned-media-upstream <TIERS>/TG_PINNED_MEDIA_UPSTREAM— override
the pin for media connections only.
TIERS is a comma-separated list of ws, cfworker, cfproxy, mtproto,
tcp, tried in the order given. Examples:
# Media transfers over raw TCP only; everything else keeps the default ladder
tg-ws-proxy --pinned-media-upstream tcp
# Control traffic pinned to upstream MTProto proxies, media to the CF proxy
tg-ws-proxy --pinned-upstream mtproto --pinned-media-upstream cfproxy
# Both classes over Tor via a SOCKS outbound proxy — the pins only pick
# tiers; the outbound proxy applies to every tier alike
tg-ws-proxy --outbound-proxy socks5h://127.0.0.1:9050 \
--pinned-upstream tcp --pinned-media-upstream wsSemantics:
- The listed order is the fallback chain: a failing tier hands over to the
next one listed, each with the same pool, cooldown and fronting behavior it
has in the default ladder.--cf-priorityis ignored for a pinned class;
unpinned classes keep the default order. - A pin naming
cfworker,cfproxyormtprotowith nothing configured
behind it refuses to start, instead of dropping that class's connections
one by one at runtime.wsis judged per connection — DCs without a
--dc-iptarget skip it with a log line. - When every listed tier fails, the connection is dropped: tiers left out of
the pin are not quietly re-added as fallbacks. That is deliberate — a pin
exists to keep a class on a particular path. List more tiers (ortcp
last) if you want fallback breadth instead. - Per-tier failure cooldowns stay a "prefer to step over" hint, never a gate
on the last tier that can connect: the Worker and upstream-MTProto rungs
dial through their cooldown there, mirroring the default ladder whose
terminal TCP rung always attempts. Without this, a single-tier pin would
turn one transient failure into a cooldown-length outage — nothing else
would dial, so nothing could clear the cooldown early. tcpis terminal (the raw-TCP connect happens in the bridge), so tiers
listed after it are never reached — startup warns about such a pin.- Pool warm-up is skipped for a class pinned without
ws.
The split is per connection, not per packet: the proxy cannot see inside the
MTProto session, so "media" means "a connection to a media DC" — which is
where Telegram clients put bulk transfers anyway.
Changed
--cf-priority/TG_CF_PRIORITYare no longer documented: the pin covers
the same need (cfworker,cfproxy,ws,mtproto,tcp). The flag still parses and
routes identically — internally it expands into that pin, minus any tier not
configured, so it stays a no-op where it always was — and is kept for
already-deployed LuCI UCI options and systemd units, so nothing breaks on
upgrade. The expansion always appendswsandtcpunconditionally, so it
terminates intcpfor every configuration: a--cf-prioritydeployment
keeps its old fall-through to raw TCP rather than inheriting the pin's
drop-on-exhaustion, and the same goes for the cooldown skip —tcphas no
cooldown, so the last rung of an expanded pin always attempts.
Cost
None by default: with neither flag set the routing code is byte-for-byte the
previous ladder. Pinned classes give up the automatic fallback breadth they
did not ask for — that is the feature, but it is also a foot-gun if you pin a
class to a single tier that later stops working.