A direct VPN connection can still be the wrong path
Two SparkUp paths were both direct. One moved about 42 Mbps; the other managed roughly 1 Mbps. Connection type alone was not a useful quality score.
Two connections between the same phone and node were both labelled direct. The IPv4 path moved about 42 Mbps. The IPv6 path managed roughly 1 Mbps.
If our path selector had ranked them only as direct versus relay, those two results would have looked equal. They were not remotely equal to the person holding the phone.
This became one of the harder parts of SparkUp QoS work. Establishing a path answers “can packets cross right now?” It does not answer whether the path will remain useful, whether one direction is failing, or whether a relay would actually feel faster.
Why direct is not the same as good
SparkUp tries to connect a client to a sharing node directly through NAT. In one field setup behind carrier-grade NAT, coordinated IPv4 hole punching connected repeatedly in about 681 to 731 ms. Seven of eight valid runs completed the full packet measurement, and all eight established the direct path.
That was useful proof. It did not prove that every direct candidate was worth keeping. A later production trace selected a clean IPv4 server-reflexive pair and reached the WireGuard connected state about 147 ms after ICE selected it. A slower case selected an IPv6 host pair, spent about 12 seconds connecting and then delivered the much weaker result.
ICE is designed to discover valid candidate pairs and nominate one. It prefers routes according to candidate priorities, including direct routes over relays. A successful nomination means connectivity worked during the checks. We infer from that, and from our traces, that long-term tunnel quality needs a separate decision layer.
The word “direct” hides too much: address family, carrier policy, NAT behaviour, radio changes and the route beyond the first hop. Even candidate lists can contain noise from private and VPN interfaces. We saw addresses from a TUN alongside the fresh public candidates needed for the real punch.
Why an unstable mobile VPN is hard to detect early
A dead connection is easy. An aging one isn't. WireGuard normally stays quiet when there is no traffic, cellular networks lose packets as part of ordinary operation, and Android may move between radio conditions without giving the tunnel a clean before-and-after boundary.
We deliberately set the existing stale-handshake threshold to 150 seconds. That sounds painfully slow, but it avoids declaring routine 4G or 5G loss a failure. Make the threshold aggressive and the app starts tearing down healthy paths. Keep it conservative and a real one-way failure feels frozen before recovery starts.
Delay variation makes this worse. Averages smooth away the exact events a real-time connection notices. RFC 8868 points out that infrequent delay outliers can materially affect adaptive systems, even when the middle of the distribution looks fine. A p50 latency chart can stay calm while a call glitches or a page waits on one unlucky packet.
We added an observe-only “handshake aging” signal first. It records that a direct path may be degrading but does not switch traffic. That restraint is intentional. We need field evidence that the warning predicts a real failure before spending battery and relay capacity on proactive recovery.
Should a VPN switch to a relay before direct fails?
Sometimes. The proposed next step is a warm relay probe: establish enough of the fallback path to know it works, without attaching a second reader to the device tunnel or sending user traffic through two planes. If direct then dies, the existing coordinated switch can use a path that has already been checked.
The idea reduces recovery time, but it has a cost. A probe wakes radios, holds resources and can create relay allocations that never carry traffic. If it fires whenever a mobile handshake gets old, it replaces occasional stalls with constant battery drain.
So the proposed guardrails are strict: one warm probe at a time, a cooldown between attempts and a hard lifetime for any held probe. A healthy handshake clears the warning and tears the probe down. We have implemented the warning and its decision mapping; the active warm probe remains design work until measurements justify it.
Remember what happened on this network
Fast recovery during one session is only half the problem. Without memory, the next connection repeats the same twenty-second experiment on a network where direct has already failed several times.
SparkUp now keeps a small client-local stability record. It is keyed by a coarse network class, the peer node and the direct mode. It does not store an SSID or raw IP address. Repeated recent failures shorten the next direct attempt to a few seconds before the existing TURN fallback begins. A successful direct connection resets the failure streak.
Direct still gets a chance. We don't permanently mark a network bad because carrier routing, NAT mappings and Wi-Fi conditions change. The score is a hint about how long to wait, not permission to skip connectivity checks.
This is less dramatic than an AI path optimiser, and more useful. It turns a repeated known failure into a shorter wait while keeping the decision on the device. The part we are least sure about is the network fingerprint. “Cellular” is deliberately low-risk for privacy, but it groups together conditions that may behave very differently.
What belongs in a VPN quality score?
RFC 9439 treats delay, delay variation, loss, available bandwidth and throughput as distinct metrics. Our field work adds history: how long did this path stay healthy, did it end with a clean disconnect or a one-way death, and did the same network fail again?
| Signal | What it tells us | What it cannot tell us alone |
|---|---|---|
| ICE connected | A candidate pair passed connectivity checks | Future stability or useful throughput |
| Handshake age | The encrypted path may be going quiet | Whether silence is normal idleness |
| Throughput | How much data arrived during a test | Latency spikes or one-way failure |
| Delay variation | How uneven packet timing is | Whether a relay would be better |
| Path history | This network and peer failed or stayed stable before | What changed since the last session |
We do not collapse those into a public score today. Weighting them depends on the job. A video call will trade some bandwidth for consistent delay. A backup can tolerate delay and wants sustained throughput. A banking login mostly needs a stable route for a short period.
What this changes for SparkUp users
The product should not advertise “direct” as the victory condition. The useful promise is narrower: try the efficient path, verify that encrypted traffic really crosses it, remember repeated failures and move to a working fallback without making the user diagnose NAT.
If your own direct path is stable, a relay adds latency and should stay out of the way. If mobile connectivity keeps changing underneath you, a relay may be the better path even though the diagram looks less elegant. We would rather make that trade honestly than show a green “direct” badge over a 1 Mbps connection.
SparkUp is in early access on Android and Linux. Path stability scoring is implemented locally; proactive relay warming is still being measured before it is allowed to affect live traffic. Join the early-access list if that trade-off is useful to you.