Elio Darras

Technical note · Published on 25 September 2026

A WireGuard tunnel from Flutter

A VPN for the people close to me, a Swift network extension, consent before any advertising, and a kill switch announced as "coming soon" rather than faked.

I wanted to test something, and I wanted the people close to me to have a VPN that's simple to use. The result is an iOS and Android client in Flutter, with a native Swift network extension that brings up a WireGuard tunnel. Ten countries to choose from, free servers, and an AdMob banner on the home screen, the ad-funded free VPN model.

Consent comes before anything else. Until the user has accepted the service, the no-logs policy and the advertising, the ad SDK is not initialised. That's the app's compliance mechanism, not one more screen.

Home shows the tunnel's real state, not a guess. The interface listens to the state stream emitted by native code, so if iOS cuts the VPN from Settings, the app sees it.

A 20-second watchdog closes the tunnel and shows an error if the connection doesn't complete, and the button cancels the attempt in progress. That was the fix for a real regression. The button disabled during the transition had closed the only way out of a connection that never returned.

On the Swift side, the extension parses the wg-quick configuration by hand with a state machine, validates the Curve25519 keys, the address ranges and the endpoints, with about twenty typed error cases, then hands it to WireGuardKit. The wireguard-apple repository is embedded as a local Swift package, and an Xcode build phase compiles the Go library.

The kill switch is deliberately disabled and announced as "coming soon", rather than pretending to a protection that doesn't exist. The project isn't published, and what's missing is stated as is, test AdMob identifiers to replace, tracking declarations to add for Apple's review, and shared free-account keys, which mean one abuse takes the service down for everyone.