Changelog¶
4.13.0¶
- Wildcard certificate support in fake-TLS (#44). Operators with a wildcard cert like
*.example.comserved from a concrete vhost (e.g.proxy.example.com) couldn't make fake-TLS work — SNI lookup did literal string compare against*.example.com, every real ClientHello missed the match, and the connection landed on nginx's default vhost serving an unrelated cert (the failure mode also reported against mtg as 9seconds/mtg#394). Configure-D '*.example.com:proxy.example.com:443'(orEE_DOMAIN=*.example.com,EE_BACKEND=proxy.example.com:443) and teleproxy now matches any single-label subdomain against the pattern per RFC 6125:proxy.example.commatches,example.com(apex) anda.b.example.com(multi-label) do not. Startup fingerprinting probes the backend hostname, so the captured ServerHello parameters reflect the real cert vhost. Covered bymake test-wildcard-cert. - Document direct-mode limitations (#79). The Direct-to-DC Mode page now spells out what
--directtrades away by skipping Telegram's middle-end: media on non-Premium accounts may not load, sponsored channels aren't delivered, and Telegram voice/video calls aren't carried by any MTProto proxy regardless of mode. Answers the recurring symptom in #60. CONFIG_DOWNLOAD_PROXYenv var for the proxy-multi.conf download (#61). Hosts that can't reachcore.telegram.orgdirectly can now route the config refresh through an outbound HTTP or SOCKS proxy. Accepts any URLcurl -xunderstands (http://,https://,socks5://,socks5h://, with optionaluser:pass@). Falls back toSOCKS5_PROXYwhen unset, so users who already proxy DC traffic don't need to set anything new. Applies to the initial fetch instart.shand the 6-hour cron refresh.- Split SNI domain from camouflage backend (#62). The fake-TLS feature used to require
EE_DOMAINto be both the SNI hostname and the backend connect target — operators worked around it by editing/etc/hosts. NewEE_BACKENDenv var separates the two:EE_DOMAINkeeps a clean public SNI name likecloudflare.com, whileEE_BACKENDpoints at the actual backend (127.0.0.1:8443,[::1]:8443, orunix:/run/nginx.sock). Reality-style. Available as TOML too:domain = [{ name = "cloudflare.com", backend = "127.0.0.1:8443" }]. The legacyEE_DOMAIN=host:portanddomain = "..."strings still work unchanged. - Fix pre-handshake socket accumulation (#63). Scanners and probes that opened a TCP connection but never sent the obfs2 header used to sit on the proxy until OS-level TCP keepalive killed them roughly two hours later, inflating
total_connections(Prometheusteleproxy_active_connections) far above the authenticated user count. The proxy already schedules a 10-second handshake alarm, but the handler used to no-op when fake-TLS wasn't configured. It now drops the unhandshaked socket. Most visible under random-padding (dd) mode, where there's no SNI gate to filter scanners early. Newmake test-handshake-timeoutregression test asserts the counter returns to baseline within 15 seconds. - Memory-handling robustness and exhaustive cppcheck in CI (#84, #85, #82, #78). Tightened allocation/free paths in
src/common/common-stats.cand a few buffer sites that could leak or double-free under memory pressure.make lintnow runscppcheck --check-level=exhaustiveagainst a source-built cppcheck 2.20 (Ubuntu's 2.13 silently misses OOM checks), and CI fails on new findings — so future OOM-path regressions surface before merge instead of after.
4.12.2¶
Build hygiene release. No runtime changes.
- Clang x86_64 build support (#68).
make CC=clangnow compiles cleanly on Linux x86_64 —src/common/crc32.cpreviously relied on GCC-only__builtin_ia32_*names for SSE/PCLMUL ops; clang gets_mm_*intrinsic shims for the missing ones. The shim block is gated to__x86_64__/__i386__so Apple Silicon clang isn't affected. - Clang x86_64 CI job (#72). New
build-clangjob in the test matrix runsmake CC=clangso future regressions in the clang build path surface in CI instead of in third-party packaging.
4.12.1¶
Hotfix for log spam introduced by the 4.12.0 unique-IPs counter rework.
- Fix
WARNING: IP tracking table full for secret 0flooding docker logs (#71). The 4.12.0 fix for #70 made every secret populate a fixed-size 256-entry per-IP table; on a busy public proxy with nomax_ipsorrate_limitconfigured, the table fills as soon as ~256 distinct source IPs have connected and every subsequent unique client logs a warning. Plain secrets now bypass the precise table entirely and feedteleproxy_secret_unique_ipsfrom a per-secret Bloom filter — bounded memory, no overflow, ~0.5% FPR at 10K IPs. - Throttle the table-full warning to once per minute per slot for limit-bearing secrets where
max_ipslegitimately exceeds the table size. The message now also includes the secret label and a hint to raisemax_ipsor check the configuration. - The macro
SECRET_MAX_TRACKED_IPSis now overridable at build time (make EXTRA_CFLAGS="-DSECRET_MAX_TRACKED_IPS=N"), used by the newmake test-table-fullregression test.
4.12.0¶
Bug fixes for Docker deployments and per-secret metrics.
- Fix
teleproxy_secret_unique_ipsalways reporting 0 (#70). The counter only ever incremented for secrets that hadmax_ipsorrate_limitconfigured — a stale guard insideip_track_connect()that broke the metric for every plain secret. Removed the guard so the IP-tracking table populates for all secrets. Also clarified the metric type:teleproxy_secret_unique_ipsiscounter(cumulative count of distinct source IPs since startup), notgauge. - Clarified
teleproxy_secret_bytes_received_total/_sent_totalHELP text (#70). The labels are technically correct ("received from clients" = uploads, "sent to clients" = downloads) but easy to misread from a client perspective. The HELP lines now explicitly call out the direction. Note: these counters increment in direct mode only — relay-mode aggregation is a separate gap, tracked for a follow-up. - Fix Docker
SECRET=hex:label,hex:labelwriting the entire string as the TOML key (#67). The numbered-secret path (SECRET_LABEL_N) was already correct; the comma-separated path now mirrors it and writes a separatelabel = ...line. - New
EXTERNAL_PORTenv var (#66) for advertising a different port in the connection link than the internal listen port — needed when Docker maps-p 4443:443. Also added a matchingexternal_portTOML option, consumed by both the operator-printed connection URL and the/linkHTML page (QR codes).
4.11.0¶
SOCKS5 upstream support in the check command (#57) and a new Cloudflare Spectrum deployment guide (#55).
teleproxy checknow routes DC probes through the configured SOCKS5 proxy — new--socks5 URLCLI flag, also reads from thesocks5field in TOML config. Useful for environments where the proxy itself sits behind a SOCKS5 upstream.- Handle buffer allocation failures gracefully instead of crashing (#58). Under memory pressure,
rwm_allocand related functions could return NULL and trigger a segfault. Now they fail the connection cleanly. - Fix PROXY protocol metrics always reporting 0 in multi-worker mode (#53).
teleproxy_proxy_protocol_connections_totalandteleproxy_proxy_protocol_errors_totalwere not aggregated from worker processes — the master read its own zero copy. Both the text stats and Prometheus endpoints now report correct values. - New deployment guide for running behind Cloudflare Spectrum.
4.10.0¶
Graceful connection draining on secret removal (#45) and a brand-new signed RPM repository (#21).
- Zero-downtime secret rotation — removing a secret from
config.tomland sending SIGHUP no longer drops the in-flight connections that were authenticated under it. The slot enters a "draining" state: new connections matching that secret are rejected, but existing ones keep working until they close on their own ordrain_timeout_secs(default 300,0= infinite) elapses. Re-adding a draining secret revives the same slot — counters and IP tracking carry over. - New TOML option
drain_timeout_secs(reloadable). Pinned-SCLI secrets are immutable across SIGHUP and never drain. - New stats:
secret_<lbl>_draining,secret_<lbl>_drain_age_seconds,secret_<lbl>_rejected_draining,secret_<lbl>_drain_forced. Same fields exposed in Prometheus asteleproxy_secret_draining,teleproxy_secret_drain_age_seconds,teleproxy_secret_rejected_draining_total,teleproxy_secret_drain_forced_total. - Slot capacity expanded internally: 16 active secrets at a time as before, plus up to 16 additional draining slots.
- Fixes a latent bug where the per-secret connection counter could go negative if a TLS connection was closed between the TLS handshake and the obfs2 init.
- Signed dnf repository at https://teleproxy.github.io/repo/ serving RHEL 9, RHEL 10, AlmaLinux, Rocky Linux, and Fedora 41/42 on x86_64 and aarch64. Install on any of these distros with
dnf install https://teleproxy.github.io/repo/teleproxy-release-latest.noarch.rpm && dnf install teleproxy && systemctl enable --now teleproxy. Packages are signed with an RSA 4096 / SHA-512 key (RHEL 9 rpm-sequoia compatible). The first install generates a random secret in/etc/teleproxy/config.toml; subsequentdnf upgraderuns swap the binary and never touch user-edited config;dnf removeleaves the config file in place. Built automatically on each tag from the existing static linux binaries via nfpm, driven byrepository_dispatchfrom the release workflow into the new teleproxy/repo repository.
4.9.0¶
PROXY protocol v1/v2 listener support (#50) and per-IP top-N Prometheus metrics (#46).
- PROXY protocol — accept HAProxy PROXY protocol v1 (text) and v2 (binary) headers on client listeners. Required when running behind a load balancer (HAProxy, nginx, AWS NLB) that injects client IP. Enable with
--proxy-protocol(CLI),proxy_protocol = true(TOML), orPROXY_PROTOCOL=true(Docker). - Auto-detects v1 and v2 headers, extracts real client IP, re-checks IP ACLs against the real address.
- v2 LOCAL command accepted for load balancer health check probes.
- New Prometheus metrics:
teleproxy_proxy_protocol_connections_total,teleproxy_proxy_protocol_errors_total. - Per-IP top-N metrics — opt-in
top_ips_per_secret = N(TOML) orTOP_IPS_PER_SECRET=N(Docker) exposes the top-N heaviest client IPs per secret in/metrics. Three new families:teleproxy_secret_ip_connections,teleproxy_secret_ip_bytes_received_total,teleproxy_secret_ip_bytes_sent_total. Sorted by total bytes; cap at 32 per secret to keep Prometheus cardinality bounded. Default 0 (disabled, zero overhead). Useful for diagnosing the "proxy works for 5 minutes then stops" complaint pattern alongside the Grafana dashboard. - Fix auto-generated secret not written to TOML config —
start.shnow correctly stores the generated secret in the TOML config. - Documentation: complete SEO overhaul with per-page meta descriptions, OpenGraph tags, JSON-LD structured data, and robots.txt.
- Translations: Russian documentation now at 100% coverage, Farsi and Vietnamese expanded to 38%.
- TON wallet added as a donation option alongside Tribute.
4.8.0¶
DC health probes (#47).
- DC latency probes - periodic TCP handshake measurement to all 5 Telegram DCs, exposed as Prometheus histograms (
teleproxy_dc_latency_seconds), failure counters, and last-latency gauges. Helps operators diagnose slow downloads and pick optimal DC routing. - Disabled by default. Enable with
--dc-probe-interval 30(CLI),dc_probe_interval = 30(TOML), orDC_PROBE_INTERVAL=30(Docker env). - Probes run in the master process only. Completion is tracked via non-blocking poll to preserve sub-millisecond accuracy.
- Text stats endpoint includes per-DC latency, average, count, and failure fields.
4.7.0¶
Per-secret quotas, unique-IP limits, and expiration (#26).
- Data quota — cap total bytes transferred per secret; active connections are closed and new ones rejected when exhausted. Configurable in bytes or human-readable sizes (
quota = "10G") - Unique IP limit — cap how many distinct client IPs can use a secret simultaneously (
max_ips = 5). Additional connections from an already-connected IP are always allowed - Secret expiration — auto-disable a secret after a timestamp (
expires = 2025-12-31T23:59:59Z). Existing connections continue; only new ones are rejected - Per-reason rejection counters in Prometheus and plain-text stats (
rejected_quota,rejected_ips,rejected_expired) - Docker env vars:
SECRET_QUOTA_N,SECRET_MAX_IPS_N,SECRET_EXPIRES_N - SOCKS5 upstream proxy support (#22)
- One-click cloud deploy page
- Documentation: install/upgrade instructions, SOCKS5 docs, Observatory link
4.6.0¶
DPI resistance and operational improvements.
- ServerHello size variation widened from ±1 to ±32 bytes, mimicking the natural variation in certificate chain and session ticket sizes seen from real TLS servers
- ServerHello fragmentation: ServerHello and CCS+AppData are now sent as separate TCP segments, defeating DPI that pattern-matches the full handshake response in a single packet
- Docker healthcheck respects custom
STATS_PORT— previously hardcoded to 8888, now uses${STATS_PORT:-8888}(#38) install.shsupports multiple secrets via comma-separatedSECRETor numberedSECRET_Nvariables/linkendpoint serves connection links as HTML pages with scannable QR codes
New documentation: DPI Resistance — covers server-side mitigations, recommended setup, and client-side bypass tools.
Client-side detection
The primary detection vector for MTProxy fake-TLS in Russia is the Telegram client's TLS fingerprint, which cannot be fixed server-side. Telegram Desktop fixed several fingerprint artifacts; keep clients updated. For affected networks, client-side bypass tools like zapret and GoodbyeDPI can help.
4.5.0¶
QR codes for connection links.
teleproxy linksubcommand prints a proxy URL and renders a scannable QR code in the terminal using UTF-8 half-block characters- Docker
start.shandinstall.shnow display QR codes automatically at startup — point a phone camera at the screen to connect - Vendored nayuki/QR-Code-generator (MIT) for zero-dependency QR rendering on any platform
- E2E tests decode the rendered QR output with pyzbar and verify it matches the expected URL
- Documentation: new "Connection Links" page (en + ru)
4.4.0¶
teleproxy checkdiagnostic subcommand — validates configuration and tests connectivity before accepting clients. Checks DC reachability, NTP clock drift, fake-TLS domain probe, and SNI/DNS mismatch. Exit 0/1/2 for pass/fail/bad-args.
4.3.0¶
Direct mode connection resilience.
- IPv6 auto-detection: probe at startup, enable without
-6if reachable - Multiple addresses per DC with synchronous failover on connect failure
- Connection retry with exponential backoff (200ms–800ms, 3 attempts)
--dc-override dc_id:host:portto add or replace DC addresses (repeatable). Docker:DC_OVERRIDE=2:1.2.3.4:443,2:5.6.7.8:443- New stat:
direct_dc_retries/teleproxy_direct_dc_retries_total
4.2.1¶
- Fix aarch64 build: remove unused x86-only
sys/io.hinclude - Add native ARM64 glibc build to CI (catches platform-specific issues masked by Alpine/musl)
4.2.0¶
--stats-allow-net CIDRflag to extend stats endpoint access beyond RFC1918 ranges (repeatable). Docker:STATS_ALLOW_NET=100.64.0.0/10,fd00::/8
4.1.0¶
MTProto transport protocol compliance improvements.
- Detect and log transport error codes (-404, -429, etc.) from DCs in direct mode
- Detect transport error codes in medium mode client parse path
- Track quick ACK packets with
teleproxy_quickack_packets_totalcounter - Track transport errors with
teleproxy_transport_errors_totalcounter
4.0.0¶
Rebrand to Teleproxy. Binary renamed from mtproto-proxy to teleproxy.
- Binary name:
teleproxy(wasmtproto-proxy) - Prometheus metrics prefix:
teleproxy_(wasmtproxy_) - Docker user/paths:
/opt/teleproxy/(was/opt/mtproxy/) - Environment variables:
TELEPROXY_*(oldMTPROXY_*still accepted with deprecation warning) - Docker image includes backward-compat symlink
mtproto-proxy -> teleproxy - CLI flags and behavior unchanged