Release 1.5.0 — the exit chain - #19
Merged
Merged
Conversation
Cloudflare WARP is explicit that it does not change your country: it egresses near you and geolocates the exit address to your region. A user in Iran connects successfully and still looks like they are in Iran, and no setting in Aether can alter that, because the decision is Cloudflare's. So add a hop after the tunnel. mihomo's dialer-proxy dials a node *through* another proxy, which puts every node behind MASQUE and makes the exit address the node's rather than Cloudflare's. Verified end to end before any of this was written: a real subscription moved the visible country from TH to JP, and the tunnel logged the node being dialled through it. The order is the whole design. Reversed, traffic would leave via Cloudflare again and nothing would change. Two consequences fall out of getting it right: the node is dialled from inside the tunnel, so local filtering sees an ordinary Cloudflare connection and never the node's address or SNI; and a node blocked from this network is still reachable, because it is reached from Cloudflare's network instead. dialer-proxy is set per provider rather than per node, so a subscription of any size arrives without us parsing or rewriting a single entry -- and mihomo converts pasted vless, vmess, trojan, ss and hysteria2 URIs itself, so there is no parser here at all. The chain is a dependent of the connected state rather than a peer process: it starts once the tunnel is up and stops whenever the proxy is restored. It also starts *before* the system proxy is pointed anywhere, because which listener to point at is the question the chain answers -- the other order would aim the machine at the tunnel while mihomo loaded a subscription, sending traffic out with the wrong exit address at the moment the user believes the opposite. Three things the config must not get wrong, each covered by a test: every source dials through the tunnel, nothing may take a direct route, and DNS resolves inside the chain -- a query that escapes names the destination even when the traffic does not. The control API is loopback-only behind a secret regenerated every run. Without both, any web page the user opened could reroute their traffic. No interface yet, and the chain is off unless configured. The installer grows from 6.2 to 18.8 MB.
The nodes were reachable from the backend but there was no way to see or choose one. This is that screen: subscriptions, pasted configs, and every node with the delay it last recorded. The measurement is the point. mihomo sends each probe down that node's own dialer-proxy, so a figure means the node works from behind the tunnel and a failure means it does not -- "can this config be chained" and "how fast is it" are one question with one answer, which is what the dashboard shows against each row. Subscription links are shown host-only. A link is a credential, anyone holding it can use those nodes, and this screen turns up in screenshots and support threads. Settings search reaches the section, and ties in it now break on the order the entries are written in rather than alphabetically. Searching a section name matches everything in that section equally, and the useful answer is the control the section exists for -- listed first -- rather than whichever label happened to start with the earliest letter. Searching "chain" returned "Configs pasted by hand" before this.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
Cloudflare WARP is explicit that it does not change your country. It egresses near you and geolocates the exit address to your region, so a user in Iran connects successfully and still looks like they are in Iran. No setting in Aether can alter that, because the decision is Cloudflare's.
This adds the hop that can.
Verified before a line was written
Everything below was proven by running mihomo against a real subscription, not read from documentation:
Four things that documentation would not have told us, each of which cost a wrong turn:
/proxies; they answer only under their own provider/proxies/{name}/delayreturns 404 for them — the path is/providers/proxies/{p}/{n}/healthcheckwireguard://entry in the subscription is skipped rather than failing the whole providerThe order is the design
Reversed, traffic leaves via Cloudflare again and nothing changes. Getting it right has two consequences worth having: the node is dialled from inside the tunnel, so local filtering sees an ordinary Cloudflare connection and never the node's address or SNI; and a node blocked from this network is still reachable, because it is reached from Cloudflare's network instead.
dialer-proxyis set per provider, so a subscription of any size arrives without rewriting a single entry.What the dashboard shows
Every measurement travels the tunnel, so one number answers both questions the feature exists for: does this config work from behind the tunnel, and how fast is it. A failure against a row is the answer, not an error.
Subscription links are shown host-only — a link is a credential, and this screen appears in screenshots and support threads.
Three things the config must not get wrong
Each has a test, because each is silent when broken:
dialer-proxyreaches nodes directly and changes nothingDIRECTrule puts that traffic on the local networkThe control API is loopback-only behind a secret regenerated every run. Without both, any web page the user opened could reroute their traffic.
Ordering that matters
The chain starts before the system proxy is pointed anywhere, because which listener to point at is the question the chain answers. The other order aims the machine at the tunnel while mihomo loads a subscription — traffic leaving with the wrong exit address at the moment the user believes the opposite.
It stops whenever the proxy is restored: it exists only to carry a live tunnel's traffic.
Cost
The installer grows 6.2 → 18.8 MB (measured, not estimated). mihomo is pinned to
v1.19.30and staged as a sidecar for all five architectures.GPL-3.0, shipped as a separate process — the licence text and a source pointer need to reach the Licences page before this is released publicly.
Verification
50 backend tests, 33 frontend,
tsc --noEmitclean, Windows bundle built and the sidecar confirmed beside the binary. The dashboard was driven end to end in the browser: search reaches it, the toggle explains itself, and a pasted subscription is stored with its token hidden.Not in this one
The chain is off unless configured, so nothing changes for existing users. Selecting a node from the Simple screen, and showing the node's country on the exit card, are follow-ups.