How VPN Works 0.4.0 Was Tested: 121 Tests, a Real WireGuard Peer and 14 Ways Out Blocked
Every number on vpnw.com comes from one script in the code repository, run on one Linux machine with 2 CPUs. Its output is committed next to the code, so anyone can check a figure against the run that produced it.
The script is tools/record-results.sh, and it writes to test/results. It builds the plugins and the binaries, runs every test, runs them again under Go’s race detector, measures coverage, runs nine fuzz targets, times the plugins and the WireGuard path, and tries every known way out of the sealed sandbox.
The tests aim at what VPN Works promises, more than at line counts. Here’s what that means for each promise.

A Sealed Program Can’t Get Out
A sealed program tries 16 ways around vpnw: straight to a public address, a private one and cloud metadata; UDP; a DNS lookup; a raw socket; a child process connecting on its own; two kinds of Unix socket; io_uring, which can make sockets without calling socket(); and system calls through a second table. Each is tried from inside the sandbox and, where it makes sense, from outside as a control, so a blocked result means the sandbox blocked it. 14 ran and all 14 were blocked. The 2 over IPv6 couldn’t run, because the test machine has no IPv6. That’s the next gap to close. The bypass matrix
Plugins Can’t Take the Run Down
The plugin tests use small plugins built to misbehave. One panics on its second event. One spins in an endless loop. One writes to the console without asking for the permission. One falls behind by sleeping on every event. One exports a hook with the wrong signature. One refuses connections with a reason full of terminal escape codes.
Each is checked for what should happen. The one that writes without permission is refused before any of its code runs. The panic and the endless loop stop the plugin and nothing else, and after the loop, a check makes sure Go’s garbage collector can still run, because that’s how an earlier design failed. The slow one loses events but never slows the program. The escape codes come out as harmless question marks. A Guard that breaks refuses every connection after it.
WireGuard Works, and Fails Safely
The tests run a real WireGuard peer, in user space, the same way vpnw runs its end. Inside its tunnel it has a web server that says who’s asking, and a DNS server that knows a few names. The real vpnw binary reaches the web server by name through the tunnel, and the peer sees the tunnel’s address, not the machine’s. The DNS server sees the lookup, so names went through the tunnel.
Then the failures. A key the peer doesn’t know: the run stops before the program starts. An address outside the tunnel’s AllowedIPs: refused with that reason. deny_private against the tunnel’s own private addresses: refused, and nothing reaches the peer. A config with a PostUp shell hook: refused. A config whose DNS server the tunnel would drop: refused at the start.
The Numbers
| What | Result |
|---|---|
| Test functions | 121 of 121 passed |
| Under the race detector | no failures |
| Statements covered, unit tests and the real binary together | 82.5% |
| Fuzz targets: config, policy rules and decisions, broker, proxy URLs, exit answers, WireGuard configs, plugin manifests, signatures | 9, all clean |
| A connection opened through a WireGuard tunnel | 0.26 ms |
| Data through a WireGuard tunnel, both ends on the same machine | 70 MB/s |
| A Guard plugin’s decision | 0.15 ms |
| One event through the Trace plugin | 0.44 ms |
| Starting a plugin, compiled code from the cache | 63 ms |
What It Doesn’t Show
One machine is one machine. The figures come from a 2-CPU Linux VM, and the WireGuard peer ran on it too, so the throughput is what two ends of the tunnel manage on two CPUs, not what a real network will give. No IPv6. And a test suite, however pointed, isn’t an outside security review, which the project still wants before it calls anything 1.0.
Before each release, an independent review read the code that changed, looking for ways to break it. For 0.3.0 it found a plugin that could crash vpnw with a malformed export, a sleep that ran past a Guard’s time budget, and a burst of connections that could switch a Guard off; for 0.4.0, a handshake timeout too short to survive one lost packet, and a quiet fallback that could have sent DNS outside the tunnel. All were fixed, with tests, before the release went out.