VPN Works 0.5.0: Ledger Seals Each Run's Record and Names the Line Anyone Changed
VPN Works 0.5.0 brings Ledger back, as plugins. One seals every event of a run into a file as it happens. The other checks that file and names the line anyone changed.
After an incident, the record of where a program connected is the evidence. A plain log file can be edited by anyone who reaches it, and a careful edit leaves no mark. Ledger makes every edit leave one:
vpnw guard --plugin ledger --policy agent.toml -- ./my-agent
vpnw verify ~/.local/state/vpnw/ledger/r-3f9a1c.ledger

Sealed as It Happens
Each event goes into the ledger the moment it arrives, exactly as vpnw recorded it, with a SHA-256 hash that covers the line before it. Checkpoints every 100 events and an end line are links in the same chain. Change one byte, remove a line, add one, swap two, and the chain breaks at that line.
Ledger also copies each checkpoint into the run’s trace and prints the last hash when the run ends. Those copies, kept somewhere else, catch the one edit a chain alone can’t: a whole ledger written again from scratch.
A run killed halfway leaves a ledger that stops where the run did. Writes go straight to the file, so everything before that point is there, and still checks out.
Checked Line by Line
vpnw verify answers intact, CHANGED with the file and the line, or cut short when the ledger ends without its end line. The tests made 22 kinds of edit to a ledger, and 21 were reported as changes. 18 were named at the line where they were made. The other 3 cut off the end or wrote the whole ledger again, and the copies kept elsewhere caught them. The 22nd, removing only the end line with nothing kept elsewhere, looks the same as a run that was killed, and verify says it was cut short. With the trace beside it, that edit is a change too.
Before release, an independent review tried to slip edits past verify, and found four that got through: bytes added after the end line, a checkpoint removed, one added, and one removed with the rest renumbered. They’re fixed, mostly by making checkpoints and the end line links in the chain, and each is now a case in the tests. The review also caught one edit blamed on the wrong line, and lost events marked too early; both are fixed too.
A Permission Every Plugin Gets
Ledger has no way in that other plugins don’t. It writes its file through file.write, a new permission any plugin can ask for: vpnw creates one new file for it, and the plugin can only add to it. It can’t read the file, move around in it, or reach any other.
The checker uses another addition every Advisor can use: it’s handed the lines of its files exactly as they are on disk. And an Observer that falls behind the run is now told where events were lost, so Ledger marks the gap in its chain instead of skipping it.
Measured
| What | 0.5.0 |
|---|---|
| Kinds of edit to a ledger | 22, all reported: 21 as changes (18 named at their line), 1 as a run cut short |
| One event sealed by the Ledger plugin | 0.13 ms |
| Test functions | 144 of 144 passed |
| Ways out of the sealed sandbox | 17 of 17 blocked, IPv6 included |
| Statements covered | 83.1% |
| Fuzz targets | 11, all clean |
How it was measured: test/results
What It Doesn’t Do Yet
The 0.2.0 Ledger signed its checkpoints with Ed25519. This one doesn’t: signing needs a key a plugin can use without reading it, and the plugin interface has no such thing yet. And vpnw limits the network, not files, so the program being watched could rewrite its own ledger while it runs. Keep the last hash where it can’t reach.