Rust QUIC/TUN VPN for one Mac client and one small Oracle Ubuntu server.
cargo run -p litevpn-keygen -- --out-dir config --server-name litevpn.local
cp config/server.example.toml config/server.toml
cp config/client.example.toml config/client.tomlCopy these to the server:
scp -i ~/.ssh/your_oci_key \
config/server.toml config/server.crt config/server.key config/client.token \
ubuntu@YOUR_SERVER_IP:/tmp/Install them on the server:
ssh -i ~/.ssh/your_oci_key ubuntu@YOUR_SERVER_IP
sudo mkdir -p /etc/litevpn
sudo install -m 0644 /tmp/server.toml /etc/litevpn/server.toml
sudo install -m 0644 /tmp/server.crt /etc/litevpn/server.crt
sudo install -m 0600 /tmp/server.key /etc/litevpn/server.key
sudo install -m 0600 /tmp/client.token /etc/litevpn/client.tokencargo build --release
scripts/build-server.shHOST=ubuntu@YOUR_SERVER_IP KEY=~/.ssh/your_oci_key scripts/install-server.shOracle Cloud Security List or NSG must allow the selected UDP port:
source: 0.0.0.0/0
protocol: UDP
destination port: 443
After the first install, deploy only a rebuilt server binary:
HOST=ubuntu@YOUR_SERVER_IP KEY=~/.ssh/your_oci_key scripts/deploy-server.sh./target/release/litevpn-client --config config/client.toml --probe --connect-timeout-secs 10 --connect-retries 3
sudo ./target/release/litevpn-client --config config/client.toml --connect-retries 3Use --no-routes to test the tunnel without changing macOS routes.
On macOS the client removes stale LiteVPN split-default routes before connecting. If a previous run was killed and the next probe/client run times out, clean them manually:
sudo ./target/release/litevpn-client --config config/client.toml --cleanup-routesIf the probe times out while the server service is active, open 443/udp in the Oracle Cloud Security List or NSG for the instance subnet.
--connect-retries rebuilds the local QUIC endpoint for each attempt. This is
useful after route cleanup, Wi-Fi path changes, or transient OCI path loss.
./target/release/litevpn-client --config config/client.toml --bench download --bench-duration-secs 10 --bench-target-mbps 36 --bench-payload-bytes 1300 --bench-runs 3
./target/release/litevpn-client --config config/client.toml --bench upload --bench-duration-secs 10 --bench-target-mbps 13 --bench-payload-bytes 1300 --bench-runs 3Repeated benchmark output includes local send/receive aggregate stats and parsed server-side aggregate stats. Upload server Mbps uses measured_elapsed_ms, excluding the extra drain window.
scripts/bench-sweep.sh reports both the clean candidate and the delivery-ok candidate with the highest server-observed average Mbps. For DATAGRAM benchmarks, delivery-ok means payload delivery checks passed; clean additionally requires zero client/server QUIC loss and congestion events. For stream diagnostics, delivery-ok means local and server bytes match; clean additionally requires zero client/server QUIC loss and congestion events, so retransmission-heavy runs are not mistaken for the safest target.
datagram_backlog_packets caps queued QUIC DATAGRAMs that have not reached Quinn's transmit stats yet. 64 is the selected default for this path; 0 disables the cap.
vpn_transport = "datagram" is the selected VPN data plane. vpn_transport = "stream" enables the experimental reliable QUIC stream packet mode; it is useful for diagnostics and may improve delivery under loss, but can introduce head-of-line blocking.
Switch the client and server transport mode together:
MODE=stream HOST=ubuntu@YOUR_SERVER_IP KEY=~/.ssh/your_oci_key scripts/set-vpn-transport.sh
MODE=datagram HOST=ubuntu@YOUR_SERVER_IP KEY=~/.ssh/your_oci_key scripts/set-vpn-transport.shBy default this also applies the tested pacing presets: DATAGRAM uses client
13 Mbps and server 36 Mbps; stream uses client 40 Mbps and server
36 Mbps. Set APPLY_PRESETS=0 to change only vpn_transport.
For an interactive macOS TUN smoke test, use the wrapper below. It switches the
client and server, starts the local client with sudo, and restores DATAGRAM
when the client exits:
MODE=stream HOST=ubuntu@YOUR_SERVER_IP KEY=~/.ssh/your_oci_key scripts/run-tun-smoke.shWireGuard is an open-source VPN protocol and implementation. In this project it is not the LiteVPN data plane; it is a practical baseline and fallback for the same MacBook, same OCI Osaka server, and same network path. Comparing against it answers: "what can this fixed hardware and route do with a mature VPN?"
WireGuard baseline files are generated under config/wireguard/, which is
ignored by git because it contains private keys.
Install and configure the same OCI server as a WireGuard baseline:
HOST=ubuntu@YOUR_SERVER_IP KEY=~/.ssh/your_oci_key scripts/setup-wireguard-baseline.shThat script:
- generates a server keypair and a client keypair under ignored
config/wireguard/ - writes the local macOS client config to
config/wireguard/wg0.conf - installs
/etc/wireguard/wg0.confon the OCI server - uses tunnel network
10.77.0.0/24 - gives the server
10.77.0.1and the Mac client10.77.0.2 - listens on UDP
443, the same public port LiteVPN uses - enables Linux IPv4 forwarding and NAT masquerade through
ens3 - inserts WireGuard forwarding rules before the server's catch-all
REJECT
Because LiteVPN and WireGuard share UDP 443, only one server mode can be up at
a time.
Run either VPN mode from macOS:
HOST=ubuntu@YOUR_SERVER_IP KEY=~/.ssh/your_oci_key scripts/run-vpn-mode.sh --mode wireguard
HOST=ubuntu@YOUR_SERVER_IP KEY=~/.ssh/your_oci_key scripts/run-vpn-mode.sh --mode litevpn
MODE=wireguard HOST=ubuntu@YOUR_SERVER_IP KEY=~/.ssh/your_oci_key scripts/run-vpn-mode.sh
MODE=litevpn HOST=ubuntu@YOUR_SERVER_IP KEY=~/.ssh/your_oci_key scripts/run-vpn-mode.sh
HOST=ubuntu@YOUR_SERVER_IP KEY=~/.ssh/your_oci_key scripts/run-vpn-mode.sh --mode offMODE=wireguard stops the remote LiteVPN service, starts remote wg0, then
starts local wg-quick and restores LiteVPN when the script exits.
MODE=litevpn stops remote wg0, starts the LiteVPN service, then starts the
local LiteVPN client.
MODE=off stops local WireGuard/LiteVPN state, stops remote wg0, and restores
the remote LiteVPN service.
Both modes check local sudo before changing the remote server state.
After starting a mode, verify the public exit IP:
curl ifconfig.meIt should show the OCI public IP when full-tunnel routing is active.
With either VPN already running, compare tunnel throughput:
MODE=wireguard HOST=ubuntu@YOUR_SERVER_IP KEY=~/.ssh/your_oci_key scripts/bench-vpn-throughput.sh
MODE=litevpn HOST=ubuntu@YOUR_SERVER_IP KEY=~/.ssh/your_oci_key scripts/bench-vpn-throughput.shOr run both modes sequentially with one command:
scripts/compare-vpn-modes.sh --preflight
HOST=ubuntu@YOUR_SERVER_IP KEY=~/.ssh/your_oci_key scripts/compare-vpn-modes.sh --mode wireguard --mode litevpn
HOST=ubuntu@YOUR_SERVER_IP KEY=~/.ssh/your_oci_key scripts/compare-vpn-modes.sh--preflight checks local tools, ignored WireGuard config, remote WireGuard
tools/config, and the remote LiteVPN service without asking for local sudo.
To keep each VPN mode up while measuring Fast.com in Chrome, add --fastcom:
HOST=ubuntu@YOUR_SERVER_IP KEY=~/.ssh/your_oci_key scripts/compare-vpn-modes.sh --fastcomComparison logs are written under bench-results/vpn-compare-*.
Each run also writes per-mode upload.json, download.json, ping.txt, and
a combined summary.csv for quick WireGuard vs LiteVPN comparison.
With FASTCOM_PAUSE=1, per-mode Fast.com note templates are written as
fastcom.md beside the iperf logs.
Generate a Markdown comparison report and recommendation from the latest run:
scripts/summarize-vpn-comparison.shSweep WireGuard MTU candidates before choosing the WireGuard baseline:
MTUS="1280 1380 1420" HOST=ubuntu@YOUR_SERVER_IP KEY=~/.ssh/your_oci_key scripts/sweep-wireguard-mtu.shServer runtime/network snapshot:
HOST=ubuntu@YOUR_SERVER_IP KEY=~/.ssh/your_oci_key scripts/server-snapshot.shSelected stability benchmark with before/after server snapshots:
HOST=ubuntu@YOUR_SERVER_IP KEY=~/.ssh/your_oci_key scripts/bench-selected.shLogs are written under bench-results/, which is intentionally ignored by git.
The performance rationale and next experiment ranking are in
PERFORMANCE_THEORY.md.
The short version: WireGuard is faster because it is a small purpose-built VPN data plane with mature kernel/network-stack integration. LiteVPN currently sends TUN packets through a Rust userspace TUN loop and Quinn/QUIC DATAGRAM or stream transport, with extra copies, task wakeups, pacing, backlog accounting, and loss/latency tradeoffs. LiteVPN is the experimental implementation; WireGuard is the baseline to chase, not code we wrote.
Target sweep for comparing candidate pacing limits:
DIRECTION=download TARGETS="30 34 38 40" scripts/bench-sweep.sh
DIRECTION=upload TARGETS="10 12 13" scripts/bench-sweep.sh
DIRECTION=stream-upload TARGETS="13 20 40" scripts/bench-sweep.sh
DIRECTION=stream-download TARGETS="36 50" scripts/bench-sweep.sh
DIRECTION=stream-packet-upload TARGETS="20 40 60" scripts/bench-sweep.sh
DIRECTION=stream-packet-download TARGETS="36 40 50" scripts/bench-sweep.sh