flexaccess.dev / ezvpn
ezvpn
The easy-setup VPN for reaching private networks.
- IP layer
- needs root
- CLI
- Linux
- macOS
- Windows
- iOS
- Access level
- IP routing — whole subnets, any protocol
- Privileges
- Root / Administrator (creates the network interface)
- Tunnel modes
- Split tunnel; full tunnel on CLI and Windows (experimental)
- Authentication
- Per-client Ed25519 keys, authorized-keys style
- Server exposure
- No public IP, no open inbound port
- Topology
- Client ↔ server only — no client-to-client, no site-to-site
how it works
One encrypted connection, dialled by identity
ezvpn creates a virtual network interface and routes IP packets — IPv4, IPv6, or both — through an encrypted QUIC connection. It runs as a split tunnel, carrying only the private prefixes you ask for; a full tunnel is available on the CLI and the Windows app but is still experimental. Clients dial the server by its stable endpoint identity, so the server needs no public IP and no open inbound port: hole punching finds a direct path through NAT, and an encrypted relay carries traffic when it can't.
There is no VPN subnet to plan, either. The server assigns client addresses dynamically, so nothing has to be kept collision-free by hand. A typical deployment is a small ezvpn server inside a private network — an AWS VPC, a homelab — that clients join temporarily to reach private resources.
Access control is a per-client Ed25519 keypair in the shared flexaccess-keys format: each client generates its own key, and the server keeps the public keys in an ssh-style authorized-keys file. No certificates, no PKI, no shared secret to rotate.
features
What ezvpn gives you
Reachability
No inbound ports
Clients dial a stable endpoint identity — no port forwarding, no dynamic DNS, and it works from behind CGNAT.
Bring your own relays
Point both ends at two or more custom relays and public infrastructure is out of the picture. A server that loses its home relay re-homes on another in place — same identity, same sockets, nothing torn down.
Waits out outages
Auto-reconnect with exponential backoff, from the very first attempt: a server that's down or not up yet is the ordinary case, not an exit. Status shows failed attempts, the last error, and when the next try is due.
Addressing
No subnet planning
Client VPN addresses are assigned dynamically by the server, so there are no static IPs to keep collision-free.
Dual stack
Run the tunnel over IPv4, IPv6, or both at once — clients receive an address in each family the server enables. IPv6-only mode works but is still experimental.
Client isolation by design
Clients reach the server's network, never each other: inter-client packets are dropped in userspace before they touch the TUN device. Nothing to firewall, no stale-IP bookkeeping.
Forwarding rules can be permanent
Reaching hosts beyond the server itself is ordinary OS routing: enable IP forwarding and add a MASQUERADE rule once. The rules are inert while the tunnel is down, so there are no PostUp / PostDown hooks to script.
Routing & DNS
Won't swallow your own LAN
A split-tunnel route that overlaps the network you're sitting on is refused at connect, and again if that network appears mid-session — on desktop and iOS alike — so the tunnel never cuts off the gateway that carries it.
The underlay stays off the tunnel
The server publishes its own transport addresses and the client pins host routes for any a routed prefix would capture, so even a full tunnel can't deadlock on itself. Running on top of another VPN? --exclude-direct-path keeps the tunnel off that overlay.
Split DNS on iOS
The iOS app can send DNS for chosen match domains through the tunnel, so private names resolve on the phone without capturing all of its DNS. On desktop and macOS, DNS stays with the OS.
Security & performance
Keys, not certificates
Each client authenticates with its own Ed25519 keypair; the server lists the public keys it accepts in an ssh-style authorized-keys file. Generate keys once with flexaccess-keys — no PKI, no shared token.
End-to-end encrypted
Traffic rides QUIC with TLS 1.3 from client to server. Relays that carry it can't decrypt it.
No tuning knobs
MTU 1280, Cubic, fixed windows: WireGuard-style protocol constants on both ends. Linux gets automatic TUN GSO offload; the macOS CLI data path was recently reworked for roughly 3× the upload and 2× the download throughput.
quick start
Up and running in four steps
The CLI flow, condensed from the README. You'll need ezvpn on both ends and flexaccess-keys on each client.
Generate keys
The server gets an identity key, which is what clients dial. Each client gets its own Ed25519 auth key.
# on the server ezvpn generate-server-key --output ./vpn-server.key # on each client flexaccess-keys generate-auth-key "alice laptop" -o client.key flexaccess-keys show-auth-key --private-key-file client.key # → ed25519-pub:… alice laptopWrite the server config
Paste each client's ed25519-pub line into ./authorized_keys, one per line, and pick a VPN network. That is the whole config.
vpn_server.tomlrole = "vpnserver" [network] network = "10.0.0.0/24" [auth] authorized_keys_file = "./authorized_keys" [iroh] secret_file = "./vpn-server.key"Start the server
It prints its endpoint ID on startup. No port to open, no IP to publish.
sudo ezvpn server start -c vpn_server.tomlTo reach hosts beyond the server itself, enable net.ipv4.ip_forward and add a MASQUERADE rule for 10.0.0.0/24 on the server's outbound interface. The rules are harmless while the tunnel is down, so they can simply stay.
Connect a client
Dial the server by its endpoint ID and route the private prefixes you need. Leave --route off to reach only the server itself.
sudo ezvpn client start \ --server-node-id <SERVER_ENDPOINT_ID> \ --auth-key-file ./client.key \ --route 192.168.1.0/24 ping 10.0.0.1 # the server's VPN addressOn a Mac, iPhone, or Windows PC, the native app does the same: generate a key in-app, paste its public half into authorized_keys, add a profile with the server's endpoint ID, connect.
install
Get the CLI
curl -sSL https://flexaccessdev.github.io/ezvpn/install.sh | sudo bashirm https://flexaccessdev.github.io/ezvpn/install.ps1 | iexThe CLI also needs wintun.dll from wintun.net next to ezvpn.exe. The Windows app bundles it.
The installer downloads the latest release binary and verifies its checksum. Prebuilt binaries cover Linux amd64/arm64, Apple Silicon macOS, and Windows amd64. Client keys are generated with flexaccess-keys. Set up flexaccess-keys
apps & repos
Pick your platform
- CLI · client and serverezvpn
The core — command-line client and server for Linux / macOS / Windows.
one-line installer, or cargo build - macOS / iOS appezvpn-apple
Native SwiftUI client with a packet-tunnel extension: dual-stack split tunnel (no full tunnel), multiple saved profiles, keys generated in-app and kept in the Keychain, split DNS on iOS, menu-bar controls on macOS. On a network change it disconnects rather than migrating the session — reconnect manually. The macOS build is a signed, notarized drag-to-Applications .dmg that needs no Apple developer account to run; iOS is built from source under your own team.
macOS: signed .dmg · iOS: build from source - Windows appezvpn-windows
Native WinUI 3 app driving the ezvpn core through its C FFI: dual-stack split tunnel, multiple profiles, Ed25519 keys kept in Windows Credential Manager, live connection-path view. Closing the window hides it to the system tray with the tunnel still up. It runs elevated, so expect a UAC prompt at launch. The MSI bundles wintun.dll; it is unsigned and scoped for personal use on x64.
unsigned .msi (x64)
good for
Where it fits
- Private cloud subnets — instances in private or egress-only VPC subnets
- Home-hosted servers behind dynamic IPs, NAT, or CGNAT
- Temporary split-tunnel access without standing VPN infrastructure
- Reaching a homelab or VPC from an iPhone or a Mac with the native app
keep in mind
Where it doesn't
A server-centred tunnel, on purpose
Every client talks only to the server gateway — never to another client — which is exactly what lets client addresses stay dynamic and conflict-free. The server and the desktop client do one thing, tunneling; firewall, NAT, and DNS stay with the OS.
Not a mesh, not site-to-site, not an anonymity tool
ezvpn gives clients the server's network and nothing else: no client-to-client traffic, and no bridging of two LANs. If you want every device to have a stable IP so peers can address each other, that's Tailscale's job; for a permanent link between two subnets, use WireGuard. And because relays can see connection metadata when they're involved, ezvpn is not for anonymity — only the payload is guaranteed private.
Pre-1.0: keep both ends on the same release
While ezvpn is in the 0.0.x series there is no backward compatibility between versions. Upgrade clients and servers together, and refresh configs from the current examples when you do. If an upgraded deployment still can't connect, regenerate the server identity key as a fallback — that changes the server's endpoint ID, so update every client profile with the new one.