All notable changes to this project will be documented in this file.
--stats-allow-net CIDRflag to extend stats endpoint access beyond RFC1918 ranges (repeatable, e.g.--stats-allow-net 100.64.0.0/10for Tailscale/WireGuard). Docker:STATS_ALLOW_NET=100.64.0.0/10,fd00::/8(#4)
- DC table port field silently truncated — struct initializers put port 443 into the
ipv6[0]field (unsigned char, truncated to 187) instead of theportfield, introduced when IPv6 addresses were added. Switched to designated initializers - Use-after-free in buffer chunk cleanup —
free_block_queuewas accessed afterfree(C)infree_msg_buffers_chunk_internal. The priormemsetmasked the bug (pointer was already zeroed), but it was still undefined behavior. Found by the new-WerrorCI job
-WerrorCI build check — compiles with warnings-as-errors on every push to catch issues like the DC table overflow before release. The main Makefile keeps-Wallwithout-Werrorfor end-user build compatibility- Scheduled weekly fuzz campaign — runs each fuzz target for 30 minutes with corpus persistence across runs, incremental coverage growth, and auto-filed GitHub issues on crash (#58)
- README translations: Russian, Farsi, Vietnamese
- Stickers and icons failed to load through DRS-enabled proxies — DRS inter-record delays restarted after every 1-second idle gap, so small files (stickers, icons, avatars) always fell within the slow-start delay window. Added a separate delay counter with a 30-second reset threshold: short idle gaps between sticker loads no longer restart delays, while extended idle periods (>30s) still re-establish DPI camouflage (#70)
- Per-secret connection counter went negative in ME relay mode (e.g.
secret_secret_0_connections: -136). The increment fired at connection acceptance before the handshake identified the secret, so it was always skipped. Moved the increment to after the handshake succeeds
- IPv6 DC addresses for direct-to-DC mode (from tdesktop source)
- Warning when IPv6 ME relay addresses detected in config (broken server-side since ~2023)
- DRS delays still throttled sustained downloads — delays are now limited to slow-start phases only (first ~60 records, ~140KB). Once the connection reaches phase 3 (max-size TLS records), all delays are skipped. The DRS counter resets after 1s of inactivity, so new responses still get realistic slow-start timing. This fully resolves media download degradation reported in #70
- Media download E2E tests — CI now downloads pre-saved files (1 MB, 20 MB, 100 MB) through both obfuscated2 and fake-TLS proxy modes, verifying data integrity and throughput. Catches DRS delay regressions that throttle large transfers
- DRS bulk transfer delay bounds test in Docker test suite
- Large file downloads degraded by DRS inter-record delays — delays are now automatically skipped during bulk transfers when the output buffer exceeds one max-size TLS record (16 KB). Small responses still receive Weibull-distributed delays for anti-fingerprinting. This is more realistic than always-delay approaches (e.g. mtg): real HTTPS servers burst bulk content at TCP speed and only vary timing between responses. New stat:
drs_delays_skipped/mtproxy_drs_delays_skipped_total(#70)
- Weibull inter-record delays — automatically enabled for all TLS connections. Inserts Weibull-distributed delays (k=0.378, λ=1.732ms) between TLS records, making inter-record timing match real HTTPS servers. Combined with DRS record sizing, proxy traffic is now statistically indistinguishable from real HTTPS at both the packet-size and timing levels. No configuration needed — activates automatically with
-D(#61, #62) - New stats:
drs_delays_enabled,drs_delays_applied,drs_weibull_k,drs_weibull_lambda(plain and Prometheus) - E2E test suite for DRS delays (
make test-drs-delays)
- Per-secret connection limits — cap concurrent connections per secret to prevent a leaked or widely-shared secret from consuming all proxy resources. Syntax:
-S secret:label:1000. Fake-TLS connections are proxied to the domain on rejection (indistinguishable from a normal website); obfuscated2 connections are silently dropped. Docker:SECRET_LIMIT_Nenv vars. New stats:secret_<label>_limit,secret_<label>_rejected, and Prometheus equivalents (#66)
- Static binary releases — pre-built
mtproto-proxybinaries for Linux amd64 and arm64, statically linked against musl libc. Zero dependencies — download and run (#65) - Secret labels — name your secrets with
label:hexsyntax for human-readable per-secret metrics in Prometheus (mtproxy_secret_connections{label="office"}) and stats endpoint (#60) - CI: AddressSanitizer (ASan) job catches heap overflows and use-after-free at build time
- CI: direct-mode E2E tests with real Telethon session (
get_me()through proxy)
- Direct mode: client transport tag (e.g.
0xdddddddd) now correctly propagated to DC obfuscated2 init — previously hardcoded, breaking all direct-mode connections (#64) - Direct mode: obfuscated2 init to DC written as raw bytes to post-crypto buffer, preventing double encryption
- Dynamic Record Sizing (DRS) for TLS transport — auto-activates on all TLS connections, no flag needed. Record sizes mimic real HTTPS servers (1450→4096→16144 bytes with ±100 noise), making proxy traffic statistically indistinguishable from real HTTPS (#50)
- E2E test for TLS data-after-handshake burst (validates direct+TLS race condition fix)
- Standalone DRS E2E test script (
tests/test_drs_e2e.py) for production verification with Telethon - Crash diagnostics: libunwind-based stack traces on Alpine/musl (opt-in via
DEBUG_TOOLS=1build arg)
- Heap buffer overflow in
mtfront_pre_loop—CONN_INFO()was used on a listening connection job, writingwindow_clampat offset 512 into an 80-byte allocation (432 bytes out of bounds). Present in upstream since the initial commit (May 2018), affects all deployments using workers (-M 1+). Manifested as intermittent SIGSEGV depending on allocator page alignment - Direct mode: race condition where client data was relayed before obfuscated2 init, causing DC rejection
- Direct mode: missing
check_conn_functionsforct_direct_clientcaused crash on TLS+direct connections
Consolidates changes from v3.0.17 through v3.0.22.
- Direct-to-DC mode (
--direct/DIRECT_MODE) — bypass ME relays, connect straight to Telegram DCs - Prometheus-compatible
/metricsendpoint - ARM64 (aarch64) Docker images and native CI
- IP blocklist/allowlist (
--ip-blocklist,--ip-allowlist) with SIGHUP reload - Docker: Alpine base (~8 MB), multiple secrets, startup connection links
- Fuzz testing (libFuzzer + ASan/UBSan) for TLS and HTTP parsers
- E2E tests for anti-detection fallback scenarios
- Static analysis: cppcheck and CodeQL in CI
- Idle CPU usage reduced to 0% when no clients connected
- Stats port binds to
0.0.0.0(Docker port mapping works) - Clean Prometheus output (no engine stats prefix)
- Constant-time HMAC comparison (prevents timing side-channel)
- Replay window tightened from 10 min to 2 min
- Fuzz testing for protocol parsers — libFuzzer harnesses with ASan + UBSan for TLS ClientHello/ServerHello and HTTP request parsing. Extracted pure parsing logic into standalone modules (
net/net-tls-parse.{c,h},net/net-http-parse.{c,h}) so harnesses link without the full engine. CI runs each target for 60 seconds on every push/PR (#51) - E2E tests for anti-detection fallback scenarios: unknown SNI forwarding, duplicate
client_randomreplay rejection, and standard browser TLS passthrough (#45) - Documented anti-detection properties of TCP Splitting mode — all validation failures are transparently forwarded to the backend
- Direct-to-DC mode (
--direct/DIRECT_MODE=true) — bypass Telegram middle-end relay servers and connect straight to data centers. Eliminates one network hop, removes dependency onproxy-multi.conf/proxy-secret, and simplifies deployment. Incompatible with-P(proxy tag) (#53)
- Stats port (
-p) now binds to0.0.0.0instead of127.0.0.1— Docker port mapping (-p 8888:8888) works for accessing stats from the host (#35) - Prometheus
/metricsendpoint no longer prepends non-Prometheus engine stats
- IP blocklist/allowlist support (
--ip-blocklist,--ip-allowlist) — CIDR-based access control for client connections. Files support IPv4/IPv6 notation with comments, reloaded onSIGHUP. Prometheus metricmtproxy_ip_acl_rejected_totaltracks rejections. Docker support viaIP_BLOCKLIST/IP_ALLOWLISTenvironment variables (#46) - CI: static analysis with cppcheck and CodeQL. Fixed undefined behavior (signed left-shifts), potential memory leak in
parse_option_internal(), guarded memcpy with NULL checks (#52)
- Reduce idle CPU usage from 1-2% to 0% — the precise cron job (1ms interval) now stops when no clients are connected and resumes instantly on first accept. In multi-worker mode, the master process is unaffected (always monitors children) (#34)
- E2E tests: provide a default test secret so all TLS handshake tests pass without requiring
MTPROXY_SECRETenv var
- ARM64 (aarch64) support — Docker images now build for both
linux/amd64andlinux/arm64, enabling native deployment on Raspberry Pi, Oracle Cloud ARM, Apple Silicon, and other ARM64 platforms (#48) - CI: ARM64 test job on native GitHub ARM runner
- Makefile:
docker-image-arm64anddocker-run-help-arm64targets
- CRC32/CRC32C uses table-based software fallback on ARM64 (functionally correct; hardware-accelerated ARMv8 CRC can be added later)
- Dockerfile no longer hardcodes
--platform=linux/amd64
- Prometheus-compatible
/metricsendpoint on the stats port — returns counters and gauges in exposition format for scraping (#47) - Docker: support multiple secrets via comma-separated
SECRETor numberedSECRET_1..SECRET_16environment variables (#54) - Docker: print ready-to-share
https://t.me/proxyconnection links at startup (#55) - Docker: switch to Alpine Linux — image size reduced from ~150MB to ~8MB (#49)
- Use constant-time comparison (
CRYPTO_memcmp) for HMAC validation, preventing timing side-channel attacks - Tighten timestamp replay window from 10 minutes to 2 minutes (matching telemt)
- Critical: v3.0.15 broke fake-TLS for backends that send multiple encrypted records — TDLib (all official Telegram clients) only reads the first record and the HMAC mismatch caused connection failures. Now emits a single record whose size equals the total of all backend records, giving a realistic encrypted payload size without breaking any client (#42)
- Auto-detect external IP for Docker NAT — proxy now discovers its public IP automatically, fixing silent connection failures when
EXTERNAL_IPwas not set - Added
--allow-skip-dhflag (upstream default) for faster DC handshakes - E2E tests now use TelethonFakeTLS for real fake-TLS handshake verification
Fake-TLS emulation now replays all encrypted Application Data records from the backend— BROKEN: emitting multiple records is incompatible with TDLib's single-record ServerHello parser. Use v3.0.16 instead
- Automatic daily refresh of
proxy-multi.confvia cron — prevents proxy from becoming unavailable when Telegram rotates DC server addresses (#41) HEALTHCHECKinstruction in Dockerfile — health monitoring now works with plaindocker run, not just Docker Compose
-D host:portsupport for custom TLS backend — proxy MTProto traffic to your own web server instead of a remote domain- Custom TLS backend setup guide in README
--aes-pwdflag documentation in README (#38, fixes #36)
proxy-secretbaked into Docker image at build time — eliminates runtime download failures and speeds up container startup- Download resilience for
proxy-multi.confwith better retry logic and cached fallback
VERSION_STRwas hardcoded asmtproxy-3.0.5regardless of actual release — now injected from git tags at build time (#37)
- Stats endpoint (
--http-stats) now accessible from all RFC 1918 private networks, not just loopback (#35). Fixes stats being unreachable from Docker host via bridge network.
- LOCAL_IP detection in Docker for RouterOS containers where
/etc/hostscontains empty lines (#31)
EE_DOMAINenvironment variable for Docker to enable EE mode (Fake-TLS) (#30)
- Docker startup failure when
SECRETnot provided (#21):- Added
vim-commonpackage to providexxdfor automatic secret generation - Secret is now auto-generated if not provided via environment variable
- Added
- Container "cannot raise open file limit" error:
- Added
-cflag withMAX_CONNECTIONSenv var (default: 60000) - Added
ulimitsconfiguration to docker-compose files
- Added
- CI testing workflow with GitHub Actions
- Simplified test suite (HTTP stats + MTProto port connectivity)
TESTING.mddocumentation- Docker Quick Start section in README - run with zero configuration
EXTERNAL_IPenvironment variable for NAT support in Docker- Explicit
--platform linux/amd64in Dockerfile for Apple Silicon compatibility
- Simplified test suite - removed Telethon dependency for faster, more reliable CI
- Updated Docker documentation with clearer examples
- Fixed high CPU usage (Issue #100):
- Optimized
epoll_waittimeout innet/net-events.cto be dynamic based on pending timers. - Corrected
epoll_timeouthandling inengine/engine.candmtproto/mtproto-proxy.c.
- Optimized
- Fixed Docker startup issue (Issue #21):
- Added
vim-commontoDockerfileto providexxdfor secret generation.
- Added
- Added comprehensive test suite:
- Added
tests/directory with Python-based tests usingtelethon. - Added
make testtarget for running tests in Docker. - Added
TESTING.mddocumentation. - Added GitHub Actions workflow for automated testing.
- Added
- Build fixes:
- Added missing headers (
<x86intrin.h>) inengine/engine-rpc.h. - Suppressed array-bounds warnings for specific files.
- Added missing headers (
-
Added IPv6 usage documentation to
README.md:- How to enable IPv6 with
-6and use-H <port> - Client guidance (prefer hostname with AAAA record; IPv6 literal notes)
- Quick checks and troubleshooting (sysctl, firewall, V6ONLY)
- Systemd IPv6 example
- Docker IPv6 considerations
- How to enable IPv6 with
-
Code fixes and improvements:
jobs/jobs.c: safer signal handler logging usingsnprintfand bounded writecommon/proc-stat.c: correct parsing of/proc/<pid>/statby readingcommas(%[^)])net/net-events.c: correct IPv4 prefix-length print and IPv6 netmask bit scannet/net-http-server.c/net-http-server.h: fix HTTP date formatting to exact RFC 7231 form, useHTTP_DATE_LENandsnprintf
-
Build and tooling:
Makefile: improve host arch detection and optional 32/64-bit flags; add Docker-based test targets; tidy linker flagsDockerfile: consistent multi-stage alias casing (AS builder).gitignore: add IDE files (.idea/)