Anyone building mobile VPN apps faces an unavoidable fundamental decision: How do I implement the tunnel protocol without having to load kernel modules directly on restrictive systems (like iOS or unrooted Android)? The answer is almost always: userspace networking.
The developers of Mullvad had been relying on wireguard-go — the official userspace implementation in Go. It worked, but it took a toll. Over 85 percent of crashes in the Android app came directly from this library.
Now they're pulling the plug. The Go implementation is being removed entirely. The successor is called GotaTun and is written in Rust.
FFI Bridges and the Scheduler Clash
Mullvad's client app is mostly written in Rust, but the actual tunnel logic lived in Go. The inevitable bridge between them was cgo or the Foreign Function Interface (FFI).
In systems programming, this is a massive bottleneck. Go uses its own M:N scheduler (Goroutines) and manages tiny, dynamically growing stacks. When Rust calls a Go function via C-FFI, two worlds collide: An FFI call forces Go to set up a clean C thread context, hard-copy parameters, and switch stacks. This costs precious CPU cycles and massive latency per call.
Even more severe is debugging: When the operating system (say, the Android OOM killer under memory pressure) throttles resources, the construct behaves like a black box. Clean stack traces across the FFI boundary? Forget it. Mullvad tried for a long time with patches (like pull requests #6727 and #7728) to prop up this bridge. The maintenance burden eventually became disproportionate.
GotaTun: Zero-Copy and the BoringTun Fork
The protocol itself remains untouched: It's still WireGuard (based on the Noise Protocol Framework with ChaCha20Poly1305 for AEAD encryption). GotaTun is "just" a new userspace implementation and a hard fork of Cloudflare's BoringTun.
The architectural shift to Rust completely solves the FFI problem, since the app now compiles from a single codebase. But for the network stack, a completely different feature is crucial: zero-copy memory management.
When IP packets are read from the virtual TUN interface and encrypted for transport, Go often did this under multiple allocations of new byte slices. Rust's borrow checker, on the other hand, allows memory buffers (via &mut [u8]) to be safely passed through the entire architecture without copying data in RAM. Combined with the removal of the garbage collector (no more "stop-the-world" pauses), this drastically reduces CPU load.
Telemetry from the Android rollout in November confirms the theory: The crash rate dropped from 0.40 percent to a remarkable 0.01 percent. In parallel, battery life of mobile clients improved noticeably.
DAITA: Why the Garbage Collector Gets in the Way
Since mid-2024, Mullvad has been using DAITA (Defence Against Intelligent Traffic Analysis). The idea: Even with encrypted traffic, ISPs can guess which website you're visiting based on packet sizes and timing patterns (fingerprinting). DAITA sabotages this by inflating real packets with padding and steadily injecting dummy traffic.
Implementing such deterministic timing in an asynchronous Go runtime is a nightmare. The garbage collector can intervene unpredictably at any time and shift the timing of dummy packets slightly. That's often enough to enable statistical analysis again. In Rust (GotaTun), background traffic can be sent with millisecond precision — deterministically and without GC jitter.
The Cloudflare Dependency as a Risk
Technically, the move is brilliant. Cloudflare has tuned BoringTun for extreme throughput and low latency for its own WARP service. Mullvad takes this stable foundation and augments it with its own privacy features (like DAITA).
Nevertheless, a strategic aftertaste remains. BoringTun is licensed under the permissive BSD-3-Clause, but the roadmap is dictated by Cloudflare — a corporation with fundamentally different economic interests than Mullvad. Through the hard fork, Mullvad does buy itself independence, but takes on massive long-term maintenance burden as soon as fundamental upstream improvements need to be ported.
This is a technological residual risk that needs to be watched closely in the coming months.