A server-side middleware for automating Encrypted Client Hello (ECH) updates for Exclave and V2Ray/Xray-based clients.
Project URL: https://github.com/AndrewWangDev/ECH_Auto_Patcher
Related Project URL: https://github.com/dyhkwong/Exclave
Hosting services behind Cloudflare offers significant protection. To further harden privacy and prevent censorship, enabling ECH (Encrypted Client Hello) is essential. Without ECH, the SNI (Server Name Indication) remains in plaintext during the TLS handshake, allowing firewalls (like the GFW) to snoop on and block connections based on the domain name.
However, implementing ECH reliably on mobile clients (specifically Exclave For Android, which utilizes V2Ray/Xray cores) presents a dilemma:
- Key Rotation: To ensure forward secrecy, Cloudflare rotates its ECH Configs (Public Keys) frequently (often hourly).
- Static Configuration Requirement: Exclave supports advanced transport protocols (VMess + WebSocket + mTLS + Mux + XUDP). To enable these features simultaneously, the client relies on a rigid binary configuration (Protobuf) or specific JSON structure.
- The Conflict: Exclave does not natively support "Auto-Fetch ECH" logic when using these complex transport configurations. Users are forced to manually look up the ECH key and hardcode it into the client. When Cloudflare rotates the key an hour later, the connection fails.
There are two common methods attempted by the community, both of which are suboptimal for this specific use case.
The client is configured to resolve the ECH keys dynamically via a DoH server before establishing the connection.
Why it's problematic:
- Latency Overhead (The "Cold Start" Problem):
- Standard TLS 1.3: TCP Handshake (1 RTT) + TLS Handshake (1 RTT) = 2 RTT.
- With Client-Side DoH:
- Connect to DoH Server: TCP (1 RTT) + TLS (1 RTT).
- DNS Query/Response: 1 RTT.
- Total: 5+ RTTs before the first byte of real data is sent.
- Real-world impact: On a cellular network with 50ms latency, this adds 250ms+ delay to every new connection. Since DNS TTL for ECH is short, this penalty is paid frequently.
- Availability: Public DoH endpoints (e.g., 1.1.1.1) are frequently blocked or throttled in restrictive regions (e.g., Mainland China). Self-hosting DoH relays adds unnecessary complexity.
The user provides the destination IP (or domain) but leaves the ECH Config field empty, hoping the core will resolve it automatically.
Why it's problematic:
- Core Incompatibility: Not all V2Ray/Xray implementations support this logic, especially when combined with complex transports (e.g., mTLS + WebSocket).
- Exclave Limitation: Exclave's import parser often fails to generate a valid configuration when advanced fields are mixed with an empty ECH field, resulting in connection failures or ECH being disabled entirely.
Instead of relying on the client to fetch keys, we shift the burden to the Server (VPS).
This project runs a lightweight Python daemon that:
- Polls: Queries Cloudflare's DNS for the fresh ECH Config.
- Patches: Uses a Fixed-Length Binary Replacement technique to inject the new key into your Exclave backup file (Protobuf) directly.
- Serves: Exposes the always-fresh configuration via a local subscription link.
The following diagram illustrates how the server-side patching mechanism ensures zero-latency connections for the client.
| Feature | Client-Side DoH (Method A) | Empty ECH Config (Method B) | ECH Auto-Patcher (This Project) |
|---|---|---|---|
| Connection Latency | High (Requires DNS RTT) | Medium (Core Dependent) | Zero Overhead (Key is pre-loaded) |
| Reliability | Low (DoH Blocking) | Low (Client Compatibility) | High (Fetched by VPS) |
| mTLS/Mux Support | Varies | Often Fails | Full Support |
| Client Battery | Higher Drain | Normal | Lowest Drain |
| Setup | Complex Routing | Simple | One-time Server Setup |
- Configure your node in Exclave with all desired settings:
- Protocol: VMess / VLESS
- Transport: WebSocket + TLS
- Security: mTLS (Client Certificates), Mux, XUDP
- In the ECH Config field, enter a Placeholder String consisting of 1000 'a' characters.
- Why? This reserves physical space in the binary file for the script to inject the variable-length ECH key later.
- Export the backup to your clipboard.
- Clone this repository:
git clone [https://github.com/AndrewWangDev/ECH_Auto_Patcher.git](https://github.com/AndrewWangDev/ECH_Auto_Patcher.git) cd ECH_Auto_Patcher - Install dependencies:
pip3 install -r requirements.txt
- Edit
secure_sub.py:RAW_BACKUP_URI: Paste your exported Exclave string here.REAL_DOMAIN: Replace with your actual domain (e.g.,api.yourdomain.com).SECRET_PATH: Define a unique path for your subscription (e.g.,/sub/my-secret-uuid).
- Run the service:
nohup python3 secure_sub.py > sub_log.txt 2>&1 &
- Add the subscription link to Exclave:
http://<YOUR_VPS_IP>:25500/sub/<YOUR_SECRET_PATH> - Important: Go to Exclave settings and enable "Update via Proxy".
- Security Note: This ensures the subscription update (which contains your mTLS keys) is transmitted over your existing encrypted proxy tunnel, never via plaintext HTTP.
To prevent unauthorized scanning of your subscription port, it is highly recommended to configure iptables on your VPS to only allow access from localhost (for the proxy loopback) and your specific public IP.
# Only allow local proxy to access the subscription service
iptables -A INPUT -s 127.0.0.1 -p tcp --dport 25500 -j ACCEPT
iptables -A INPUT -p tcp --dport 25500 -j DROPContributions are welcome! If you have ideas for supporting other clients or improving the patching logic, please open an issue or submit a Pull Request.
This project is licensed under the MIT License - see the LICENSE file for details.

