| Configurations | Import OpenVPN .ovpn config | FULL | directive parse: remote/proto/dev/auth-user-pass + inline cert blocks |
| Configurations | Import Tunnelblick .tblk bundle | PARTIAL | recursive .ovpn/.conf locate (root or Contents/Resources/) + Info.plist CFBundleName naming; resolving bundled credential files into the live launch still pends the connect path |
| Configurations | Import WireGuard .conf | FULL | superset of Tunnelblick: typed Interface/Peer parse with 32-byte key validation |
| Configurations | Backend auto-detection | FULL | [Interface]/[Peer] => WireGuard, else OpenVPN |
| Configurations | Configuration list | FULL | list saved configs with parsed summary |
| Configurations | Remove configuration | FULL | delete config + drop its session credential |
| Configurations | Rename configuration | FULL | rename a saved config by id; persisted |
| Configurations | Add config from pasted text | FULL | import modal: paste .ovpn/.conf + name |
| Configurations | Per-config settings (route-all/DNS/reconnect/autoconnect) | PARTIAL | the four common toggles modeled + persisted; Tunnelblick's full per-config matrix (dozens of advanced flags) not ported |
| Configurations | Duplicate / share configuration | FULL | clone a saved config (raw + parsed fields + options) under a fresh id/name |
| Connection | Connect | PARTIAL | under root a WireGuard vpn.connect now brings the real tunnel up end-to-end (utun + boringtun pump + address/route/DNS apply) β VERIFIED via examples/engine_wg (connect->connected, ifconfig shows the tunnel address, disconnect tears down). Remaining: status is set on interface-up not handshake-confirmation, OpenVPN connect needs the openvpn child + a server, and real traffic needs a live peer |
| Connection | Disconnect | PARTIAL | teardown transition; process/tunnel teardown lands with the backend |
| Connection | Connection status set | FULL | disconnected/connecting/connected/reconnecting/disconnecting/error + live status dots |
| Connection | Lifecycle state machine (legal transitions) | FULL | enforced transitions; double-connect / bad-disconnect rejected |
| Connection | Connection log | FULL | bounded per-connection log + vpn.logs + GUI viewer |
| Connection | Byte counts / throughput readout | PARTIAL | cumulative stats + rate sampler + GUI readout; live source needs a running backend |
| Connection | Auto-connect on launch | FULL | engine computes the launch set (configs flagged autoconnect, gated by the global setting); the host connects each on startup |
| Connection | Auto-reconnect on drop | PARTIAL | option + Reconnecting status modeled; reconnect loop not wired |
| Connection | Multiple simultaneous connections | PARTIAL | manager tracks per-config connections; concurrent live tunnels need the backends |
| Connection | Connect on demand (when needed) | PARTIAL | option modeled/persisted per config; traffic-triggered activation needs the platform network monitor (phased with the helper) |
| Connection | Reconnect on network change | PARTIAL | default-path monitor (route/ip-route parsers + polling NetMonitor) + vpn.network_changed re-establishes active auto-reconnect tunnels (reconnect_targets policy, unit-tested); live default-path read VERIFIED (returns en0|gateway on this host). Forcing a real network flap to test the reconnect end-to-end is not done headlessly |
| OpenVPN | Drive the system openvpn binary | PARTIAL | argv builder + real process supervisor (spawn/lifecycle/teardown, temp config, management wiring) behind net; spawn primitive lifecycle-tested with a stand-in. End-to-end (a tunnel actually coming up) still needs root via the helper, so this stays partial |
| OpenVPN | Management interface protocol | FULL | STATE/BYTECOUNT/PASSWORD/LOG parser + live socket session (connect, setup commands, read loop) behind net; loopback-tested |
| OpenVPN | State mapping | FULL | management STATE tokens -> ConnectionStatus |
| OpenVPN | auth-user-pass credentials | FULL | prompt detection + live management auth flow: answers >PASSWORD:Need 'Auth' with username/password management commands from the session cred store; loopback-tested |
| OpenVPN | Inline cert/key blocks | FULL | <ca>/<cert>/<key>/<tls-auth> detected during parse; launched verbatim from saved text |
| OpenVPN | tls-auth / tls-crypt | FULL | control-channel protection parsed + modeled (directive and inline <tls-auth>/<tls-crypt> blocks, incl. tls-crypt-v2) into ConfigFields.tls_mode; launched verbatim from saved text |
| WireGuard | WireGuard config model + key validation | FULL | typed Interface/Peer; 32-byte base64 key validation |
| WireGuard | Userspace data path (tunnel) | PARTIAL | boringtun Noise pump + real tun/utun device + UDP, three pump threads (tun<->net + timers); WIRED into Engine::connect/disconnect (wg_tunnels map) and VERIFIED end-to-end under sudo (engine_wg: connect brings up the utun + assigns the address, disconnect tears down). A completed handshake + live traffic still needs a real WireGuard peer |
| Platform | Privileged operation plan | FULL | ordered NetOp set (create iface/assign addr/route/DNS) built from a config |
| Platform | Virtual interface creation (utun/tun) | PARTIAL | Linux creates a real tun via `ip tuntap` (root-gated); macOS utun is created+held by the WireGuard data path (boringtun TunSocket) β VERIFIED under sudo (wg_smoke brings up a real utun). OpenVPN's tun is owned by the openvpn child. Linux side not yet exercised against a live tunnel |
| Platform | Routing (default gateway / route-all) | PARTIAL | real `route`/`ip route` application, root-gated; VERIFIED end-to-end under sudo (privileged_smoke adds+removes a TEST-NET route via the engine). Full default-gateway takeover in a live tunnel still pending a server |
| Platform | DNS handling while connected | PARTIAL | macOS `networksetup -setdnsservers` apply path implemented (root-gated); UNVERIFIED + the network service is currently hardcoded to Wi-Fi; Linux per-link DNS deferred (the SetDns op carries no interface yet) |
| Platform | Privileged helper (SMAppService / polkit) | PARTIAL | direct-root application: when launched as root (sudo / setuid / an SMAppService helper that hands us euid 0) apply() runs the real OS commands β VERIFIED under sudo. A packaged installer for double-click (no-sudo) launch is the remaining piece |
| Platform | Cross-platform abstraction (macOS + Linux) | FULL | host detection + both backends apply real `ifconfig`/`route`/`networksetup` (macOS, verified) and `ip` (Linux) commands when root; needs_privilege only when not root |
| Credentials | Username/password store | PARTIAL | session-only in-memory store + creds.set/clear commands; no GUI prompt |
| Credentials | Keychain / Secret Service storage | PARTIAL | OS keychain backend via keyring (macOS Keychain / Linux Secret Service / Windows), each Credential stored as one JSON secret, no plaintext on disk; installable on the engine via set_cred_store. macOS round-trip VERIFIED (keychain_store_roundtrip hits the real keychain). Linux Secret Service unverified here; default backend stays session-only (host opts in) |
| Credentials | Passphrase-protected keys | FULL | encrypted-key passphrase stored per config (creds.set passphrase arg) and answered on the >PASSWORD:Need 'Private Key' management prompt; loopback-tested |
| App | Connection list GUI | FULL | sidebar with live status dots + selection |
| App | Connect/disconnect action | FULL | state-aware action button per connection |
| App | Status / throughput grid | FULL | status, local/server IP, received/sent |
| App | Log viewer | FULL | live per-connection log pane |
| App | Preferences | PARTIAL | app settings modeled + persisted; full preferences window not built |
| App | Command palette | FULL | the view offers its commands to the appShell's shared βK palette (setPaletteItems); the shell owns the palette chrome |
| App | Embedded terminal | FULL | shared zpwr-embed-terminal panel |
| App | Profile persistence | FULL | configs + settings saved as JSON |
| App | Connect/disconnect notifications | PARTIAL | Tauri notification plugin wired (registered in main.rs + notification:default capability, cargo-checked) + the webui posts OS notifications on connect/disconnect/error transitions (guarded so the browser preview no-ops, permission requested lazily); actual notification display needs the running Tauri app to verify |
| Protocols | Backend auto-detection (all formats) | FULL | URI scheme + plist + JSON + INI + directive sniff across the full protocol universe (unit-tested); GUI renders the detected protocol badge |
| Protocols | Supported-protocol catalog | FULL | engine feature.matrix enumerates every protocol + feature; GUI "Protocols" modal lists them with launch-kind |
| Protocols | IKEv2/IPsec | PARTIAL | modeled + .mobileconfig parse; live tunnel handed to the OS VPN stack (NEVPN/strongSwan) β pending the helper |
| Protocols | L2TP/IPsec | PARTIAL | modeled + detected; bring-up needs the OS IPsec stack / external daemon |
| Protocols | PPTP (legacy) | PARTIAL | recognized for migration; insecure protocol, data path needs pppd |
| Protocols | SSTP | PARTIAL | modeled + detected; PPP-over-TLS data path pending |
| Protocols | OpenConnect (Cisco AnyConnect / Juniper) | PARTIAL | modeled; driven by the openconnect binary (managed-process launch kind) β supervision pending |
| Protocols | Palo Alto GlobalProtect | PARTIAL | modeled; openconnect --protocol=gp launch pending |
| Protocols | Shadowsocks (ss:// SIP002) | PARTIAL | SIP002 + legacy ss:// parse (method+endpoint, unit-tested); AEAD data path pending |
| Protocols | V2Ray/Xray (VMess/VLESS) | PARTIAL | vmess:// base64-JSON + vless:// URI parse (endpoint/transport, unit-tested); data path pending |
| Protocols | Trojan | PARTIAL | trojan:// URI parse (endpoint+sni, unit-tested); TLS data path pending |
| Protocols | SoftEther | PARTIAL | modeled; native client launch pending |
| Protocols | Tailscale (WireGuard mesh) | PARTIAL | modeled; coordinated bring-up via tailscaled pending |
| Protocols | ZeroTier overlay | PARTIAL | modeled; coordinated bring-up via zerotier-one pending |
| Protocols | Apple .mobileconfig (IKEv2/L2TP) parse | PARTIAL | plist VPN payload parse (server/user, unit-tested) |
| Protocols | Config parse preview (any format) | PARTIAL | engine parses arbitrary text -> backend+label+fields; import-modal live preview not yet wired |
| Protocols | OpenVPN cipher / data-ciphers | FULL | cipher + data-ciphers directive parsed into the config summary (unit-tested) |
| Security | Kill switch (fail-closed firewall) | PARTIAL | pf/nft fail-closed ruleset computed (default-deny + tunnel/server/LAN carve-outs, unit-tested) + GUI toggle + plan viewer; installing the ruleset needs the privileged helper |
| Security | DNS leak protection | PARTIAL | off-tunnel :53 block in the plan + per-config toggle; enforcement needs the helper |
| Security | IPv6 leak protection | PARTIAL | IPv6 block in the plan + toggle; enforcement needs the helper |
| Security | Allow LAN while locked | PARTIAL | RFC-1918 carve-outs in the kill-switch plan + toggle |
| Security | Ad/tracker/malware blocking | PARTIAL | per-config toggle + global blocklist setting; sinkhole resolver needs the DNS data path |
| Security | Global kill switch + always-on | PARTIAL | global settings + GUI toggles; enforcement needs the helper |
| Routing | Split tunneling (CIDR include/exclude) | PARTIAL | mode + cidrs modeled/persisted + feed the kill-switch excludes; route-table enforcement needs the helper |
| Routing | App-based split tunneling | PARTIAL | per-app include/exclude list modeled; per-app marks/cgroup need the helper |
| Routing | Custom MTU | PARTIAL | per-config MTU modeled/persisted; applied by the data path/helper |
| Multihop | Multi-hop / double VPN | PARTIAL | entry-hop config id modeled/persisted; nested bring-up needs both tunnels live |
| DNS | Custom DNS servers (DoH/DoT) | PARTIAL | per-config resolver override modeled/persisted; applied via the DNS op/helper |
| Obfuscation | Pluggable transport / obfuscation | PARTIAL | obfs4/scramble/ws/reality selector modeled/persisted; transport wrap needs an external binary |
| Server | Server catalog (add/remove/list) | PARTIAL | typed ServerEntry CRUD persisted in the profile; browse GUI panel not yet built |
| Server | Favorites | PARTIAL | favorite toggle persisted; pure favorites() filter (unit-tested); GUI panel pending |
| Server | Latency ping + fastest-server | PARTIAL | TCP-connect latency probe + fastest_first ranking (unit-tested); live probe needs the net feature + GUI |
| Server | Custom server entry | PARTIAL | typed endpoint w/ geo/tags/latency; persisted |
| Trust | Auto-connect on untrusted Wi-Fi | PARTIAL | policy + trusted_networks list modeled/persisted; SSID read needs the helper |
| Transport | Preferred protocol selection | PARTIAL | preference modeled/persisted + GUI setting; multi-protocol server negotiation pending |
| App | Privacy & security toggles UI | FULL | per-config kill-switch / DNS-leak / IPv6-leak / LAN / ad-block toggles via zgui-core toggle group, persisted |
| App | Kill-switch firewall plan viewer | FULL | GUI renders the computed pf/nft ruleset in a zgui-core modal |
| Routing | CIDR math: contains / overlap (v4+v6) | FULL | pure IPv4+IPv6 CIDR containment/overlap test (net.cidr_contains), the foundation of split-tunnel/AllowedIPs routing; unit-tested |
| Routing | Subnet calculator (network/broadcast/mask/hosts) | FULL | full ipcalc surface β network, broadcast, netmask, wildcard, usable host range + count (net.subnet_info); beyond any VPN client; unit-tested |
| Routing | CIDR aggregation (minimal route set) | FULL | collapse a CIDR list into the minimal equivalent (drop contained + merge siblings), net.cidr_aggregate; route-table optimization beyond WireGuard AllowedIPs handling; unit-tested |
| Routing | Split-tunnel route computation (CIDR subtraction) | FULL | exact base-minus-excludes cover (0.0.0.0/0 minus subnets -> minimal routes) + include-set aggregation (split.route_plan); the AllowedIPs/exclude-route calculator competitors hide; unit-tested. Installing the routes is the existing partial Split-tunneling row (needs the helper) |
| Routing | Per-destination route decision | FULL | decide tunnel-vs-direct for a destination IP under a config's split policy (split.evaluate); an original per-destination probe; unit-tested |
| WireGuard | WireGuard key generation (X25519) | FULL | pure-Rust RFC 7748 X25519 (Montgomery ladder over GF(2^255-19), no crypto dep) β wg genkey/pubkey built in (wg.genkey/wg.pubkey), validated against the RFC 7748 test vectors; superset of Tunnelblick and clients that shell out to wg |
| Security | Config security linter | FULL | lints a parsed config before connect β weak/broken cipher, missing tls-auth, DNS-leak-prone full-tunnel routing, insecure PPTP, out-of-range MTU (config.lint/config.lint_text); no mainstream VPN client lints imports; unit-tested |
| Configurations | Config diff (field-level) | FULL | structured field-by-field diff of two saved configs (config.diff); an original; unit-tested |
| DNS | DNS resolver classification (plain/DoH/DoT/DoQ/DNSCrypt) | FULL | typed classification of a resolver spec incl. bracketed IPv6 + ports (dns.parse_server); unit-tested |
| DNS | DNS leak analysis | FULL | per-resolver leak verdict comparing system resolvers to the tunnel's DNS set, public vs private off-tunnel (dns.leak_check); the DNS-leak-test competitors ship, as pure logic; unit-tested |
| Multihop | Multi-hop cascade resolution (arbitrary depth) | FULL | resolve a config's multihop_via chain to an ordered bring-up plan with cycle/missing detection (multihop.chain); beyond fixed two-hop Double VPN / Secure Core; unit-tested. Nested bring-up still needs every hop live (existing partial Multi-hop row) |
| Obfuscation | Obfuscation transport catalog + compatibility | FULL | authoritative pluggable-transport catalog + per-backend compatibility check (obfs.catalog); unit-tested. Running a transport needs its external binary (existing partial Obfuscation row) |
| Server | Recommended-server scoring (latency/load/geo) | FULL | composite load- and geo-aware server ranking with an auditable score breakdown + Haversine distance (servers.recommend); beyond a single latency sort (NordVPN/ProtonVPN recommendation); unit-tested |
| WireGuard | Validate + normalize/export config | FULL | wireguard::parse + to_conf round-trip (config.wireguard_format) + import-modal "Validate WG" button that canonicalizes a pasted config; beyond Tunnelblick (OpenVPN-only) |
| Configurations | Validate + normalize OpenVPN profile | FULL | canonicalize a pasted .ovpn (strip comments/dupes, collapse whitespace, keep inline cert blocks + every remote) via openvpn::normalize (config.openvpn_format); the OpenVPN analogue of config.wireguard_format β profile normalization OpenVPN Connect / Viscosity offer; unit-tested |
| Configurations | Portable profile export / import | FULL | versioned JSON bundle of all configs/servers/settings (profile.export) + merge-import with id de-collision (profile.import); the config backup/restore Tunnelblick/Viscosity ship; credentials never included (session-only); unit-tested |
| Routing | Range β minimal CIDR cover | FULL | decompose an inclusive IP range into the minimal covering CIDR set for v4+v6 (net.range_to_cidrs); the ipcalc/sipcalc rangeβprefixes transform that feeds route/AllowedIPs building; unit-tested |
| Routing | IP address RFC classification | FULL | coarse RFC bucket for an address β private/loopback/link-local/cgnat/documentation/multicast/unique_local/global (net.ip_classify); the vocabulary leak/split logic reasons over; unit-tested |
| Routing | Tunnel MTU / TCP MSS calculator | FULL | effective inner MTU + MSS clamp from per-backend encapsulation overhead (WireGuard/OpenVPN/IPsec) over a link MTU (net.tunnel_mtu); the "what MTU should I set" answer computed rather than guessed; unit-tested |
| WireGuard | AllowedIPs builder (default minus excludes) | FULL | compute a full-tunnel AllowedIPs set minus excluded subnets as the minimal covering route list (wg.allowed_ips); the wg-quick / Mullvad "local network sharing" carve-out, computed; unit-tested |
| DNS | Ad/tracker blocklist match | FULL | subdomain-aware blocklist match over hosts/AdGuard/*. rule forms (dns.block_match); the NordVPN CyberSec / Mullvad content-blocking / ProtonVPN NetShield filtering primitive as pure logic; unit-tested |
| Routing | App-based split-tunnel decision | FULL | per-app tunnel/direct decision under a config's include/exclude apps policy (split.evaluate_app); complements per-IP split.evaluate β the app-based split Mullvad/ProtonVPN/Windscribe ship; unit-tested |
| Connection | Lifecycle transition pre-flight | FULL | pure legal-transition query for connect/disconnect from a connection's current state (vpn.can_transition); drives enable/disable of the Connect/Disconnect buttons without attempting the transition; unit-tested |
| Trust | Wi-Fi trust connection policy | FULL | decide connect/disconnect/no-change on joining an SSID from always-on + trusted-networks + auto-connect-untrusted toggles (policy.wifi_action); the "auto-connect on untrusted Wi-Fi" policy Viscosity/Windscribe/NordVPN/Tunnelblick ship, as pure logic; unit-tested. SSID read still needs the host Wi-Fi monitor |
| Connection | Session summary (human transfer + uptime) | FULL | human-readable per-session transfer totals + uptime (stats.session_summary, human_bytes/human_duration); the connection-info panel every client shows; unit-tested |
| Usage | Data budget accounting | FULL | cumulative usage vs a cap β percent used / remaining / over-budget (stats.budget); the data-limit surface metered/mobile clients ship; ztunnel original beyond Tunnelblick/OpenVPN Connect; unit-tested |
| Routing | Route-table conflict detection | FULL | flag overlapping/duplicate/shadowing routes in a set with the longest-prefix winner (route.conflicts); the route conflicts Tailscale subnet routers / WireGuard AllowedIPs can silently hide; unit-tested |
| Routing | Tunnel coverage / leak-surface analysis | FULL | residual leak surface of a tunnel route set β is it really a full tunnel and which public addresses escape (route.coverage); ztunnel original beyond Mullvad/WireGuard eyeballing; unit-tested |
| Routing | Subnet split (VLSM) | FULL | split a CIDR into equal longer-prefix subnets with capped list + true total (net.subnet_split); the ipcalc --split transform β carve a WireGuard Address block into per-peer /32s; unit-tested |
| Routing | IPv6 ULA prefix builder (RFC 4193) | FULL | build an fd00::/8 ULA /64 from a 40-bit Global ID + 16-bit Subnet ID (net.ula); the stable private-v6 addressing WireGuard/wg-quick and v6 mesh overlays use; unit-tested |
| Routing | Path-MTU discovery (binary search) | FULL | converge on the largest DF-probe payload that fits via binary search + probe count (net.pmtu_discover); the tracepath/PMTUD algorithm every WireGuard/OpenVPN deploy needs; the live probe needs the network but the search is real + unit-tested |
| Server | Connection-quality scoring (jitter/loss) | FULL | mean/jitter(stddev)/loss composite + rating from an RTT burst (servers.quality); the jitter- and loss-aware server metric NordVPN/Mullvad surface, hidden by a single ping; unit-tested |
| Server | Quality-based server ranking | FULL | rank servers best-first by the jitter/loss-aware quality score (servers.rank_quality); ztunnel original beyond a single latency sort; unit-tested |
| Configurations | OpenVPN inline block extraction | FULL | extract + validate inline <ca>/<cert>/<key>/<tls-auth>/<tls-crypt> blocks (PEM / static-key structure) via openvpn.inline_blocks; the inline-cert handling OpenVPN Connect/Viscosity do on import; structure only, no crypto verification (stated); unit-tested |
| Routing | Allowed-apps policy (app-based VPN control) | FULL | bulk allow/deny classification of app ids under an allowlist/denylist policy with prefix* wildcards (policy.app_decisions); the "split by app" Mullvad/ProtonVPN/Windscribe ship; unit-tested |
| Security | Kill switch in chosen packet-filter syntax | FULL | render a config's lockdown ruleset as pf / nft / iptables on demand (vpn.killswitch_syntax); emit legacy iptables even off the computed platform; unit-tested |
| Multihop | Cascade quality validation (diversity) | FULL | flag single-hop pseudo-cascades and hops sharing an endpoint host (multihop.validate); the geo/endpoint diversity NordVPN Double VPN / ProtonVPN Secure Core rely on; unit-tested |
| DNS | Encrypted-DNS bootstrap analysis | FULL | flag which encrypted resolvers (hostname-addressed DoH/DoT/DoQ) need a plaintext bootstrap lookup (dns.bootstrap_plan); the chicken-and-egg AdGuard/NextDNS/dnscrypt-proxy solve with pinned bootstrap IPs; unit-tested |
| Connection | Connection-log analysis / outcome | FULL | classify a raw OpenVPN log into typed events + counts and derive the run outcome β connected/auth-failed/tls-error/timeout/fatal (log.analyze); the diagnostics verdict Tunnelblick/Viscosity leave the user to eyeball; unit-tested |
| WireGuard | Noise_IKpsk2 message wire layout | FULL | the four WireGuard message types with exact field byte offsets/sizes (init 148 / resp 92 / cookie 64 / transport 16) and the Noise token sequence incl. the PSK mix point (wg.noise_layout); WireGuard's kernel handshake as inspectable data; unit-tested |
| WireGuard | Handshake timer/rekey schedule | FULL | derive rekey-after/reject-after countdown for a session from the protocol constants (REKEY_AFTER_TIME/REJECT_AFTER_TIME) + keepalive echo (wg.handshake_model); answers "when will this session rekey/expire" no client exposes; unit-tested |
| WireGuard | AllowedIPs overlap/dedup minimizer | FULL | collapse an AllowedIPs/route list to the minimal covering set (drop duplicates + covered blocks, aggregate siblings) and report every redundant entry WireGuard silently ignores (wg.allowed_ips_dedup); unit-tested |
| OpenVPN | Static-key / tls-crypt-v2 parse | FULL | decode + length-check an OpenVPN static key body: 2048-bit hex Static key V1 (secret/tls-auth/tls-crypt) vs. base64-wrapped tls-crypt-v2 client key (openvpn.static_key_parse); structure/length only, stated; beyond Tunnelblick's opaque side-files; unit-tested |
| Protocols | IKEv2/IPsec proposal model + validator | FULL | model phase-1 (IKE SA) + phase-2 (ESP) proposals, DH-group strength table, PFS/lifetime checks, render strongSwan ike=/esp= strings and reject weak ciphers/DH (ipsec.profile); the cipher suite NordVPN/ProtonVPN/macOS IKEv2 hide behind a checkbox; unit-tested |
| Protocols | SOCKS5/HTTP proxy-over-tunnel config | FULL | validate a SOCKS5/HTTP proxy endpoint and emit the OpenVPN socks-proxy/http-proxy directives incl. auth-file handling + retry (proxy.plan); the corporate/censored-egress transport Viscosity/Tunnelblick take unvalidated; unit-tested |
| Security | Port-forward / DNAT rule generation | FULL | render port-forward DNAT rules for pf/nft/iptables with dest-IP/port validation (firewall.port_forward); the forwarded-port DNAT Mullvad/PIA advertise but leave to the user; unit-tested |
| Routing | TCP-MSS / MTU clamp rule generation | FULL | compute the TCP MSS clamp (MTU-40 v4 / -60 v6) and render SYN-clamp rules for pf/nft/iptables (firewall.mss_clamp); fixes the small-MTU TCP black-hole OpenVPN's mssfix only half-solves; unit-tested |
| Routing | NAT64 / DNS64 prefix synthesis (RFC 6052) | FULL | embed/extract an IPv4 in an IPv6 NAT64 prefix for all valid prefix lengths, skipping the reserved u-octet, incl. the well-known 64:ff9b::/96 (net.nat64); how a DNS64 resolver builds a synthetic AAAA; ztunnel original β no consumer client models 464XLAT; unit-tested |
| Routing | IPv6 SLAAC EUI-64 / privacy classification | FULL | build a modified-EUI-64 SLAAC address from a MAC (insert ff:fe, flip U/L bit) and classify an address as MAC-derived vs opaque/privacy (net.ipv6_eui64); flags trackable MAC-derived addresses for the leak auditor; unit-tested |
| Connection | Auto-reconnect backoff scheduler | FULL | deterministic exponential-backoff reconnect schedule with cap, decorrelated seeded jitter, and give-up bound (vpn.reconnect_schedule); makes the invisible retry loop every client bakes in an inspectable timeline; unit-tested |
| Usage | Bandwidth accounting rollups + projection | FULL | roll per-interval usage into hour/day/week buckets and project end-of-period usage / cap-hit day from the run rate (stats.rollup); the "data used per day / you'll hit your cap on day N" metered-plan surface; ztunnel original; unit-tested |
| Server | Named server-selection strategies | FULL | pick the best server under a named strategy β lowest-latency / nearest-geo / least-loaded / balanced / prefer-favorite β each with the deciding metric + reason (servers.select_strategy); the "connection preference" toggle NordVPN/ProtonVPN expose, made auditable; unit-tested |
| Configurations | OpenVPN <-> WireGuard config migration | FULL | translate endpoint/DNS/routes/keepalive across protocols and produce an explicit untranslatable list (TCP transport, cert auth, cipher, pushed addresses, wg port) with key placeholders (config.migrate); no client offers a migration path; unit-tested |
| DNS | DNS query wire-format + DoH/DoT request build | FULL | encode an RFC 1035 DNS query message (header + QNAME labels + QTYPE + QCLASS) and wrap it for DoH (RFC 8484 base64url GET + application/dns-message POST) and DoT (RFC 7858 2-byte length prefix) β dns.query_wire; the exact bytes a leak test / bootstrap probe puts on the wire, no client exposes this; unit-tested |
| WireGuard | Cookie / MAC DoS-mitigation model | FULL | model WireGuard's mac1/mac2 keying (LABEL_MAC1/LABEL_COOKIE, 16-byte MACs, XChaCha20Poly1305 cookie reply, 120s cookie refresh) and the under-load accept/reject decision β process vs send_cookie_reply vs drop (wg.cookie_model/wg.cookie_decision); the Β§5.4 rate-limit logic every implementation buries; unit-tested |
| OpenVPN | PUSH_REPLY parse | FULL | parse an OpenVPN PUSH_REPLY control message into structured pushed settings β routes, dhcp-option DNS/DOMAIN, ifconfig, redirect-gateway flags, ping/ping-restart, negotiated cipher, with unmodeled directives preserved (openvpn.push_reply); the server-pushed config no client surfaces; unit-tested |
| Protocols | IKEv2 traffic-selector narrowing (RFC 7296) | FULL | narrow a peer's proposed IKEv2 traffic selector against local policy β intersect address range (re-expressed as minimal CIDRs) β© port range β© protocol, returning TS_UNACCEPTABLE on an empty intersection (ipsec.narrow_ts); the Β§2.9 negotiation strongSwan/macOS hide; unit-tested |
| DNS | Split-horizon resolver routing | FULL | pick the resolver for a query name by longest domain-suffix match with a catch-all default, plus a compiler that flags missing default / duplicate / redundant (shadowed) rules (dns.split_horizon); the dnsmasq --server=/domain/ip and systemd-resolved routing-domain logic as pure data; unit-tested |
| Security | Config secret redaction | FULL | produce a shareable copy of an OpenVPN/WireGuard config with all key material masked β WireGuard PrivateKey/PresharedKey, inline <key>/<tls-crypt>/<tls-auth>/<secret> blocks, inline auth-user-pass creds β each replaced by a length + non-reversible FNV fingerprint so two redacted configs compare (config.redact); the safe-to-attach-to-a-bug-report transform; unit-tested |
| Usage | Uptime / SLA accounting | FULL | clamp+merge outage intervals over an observation window and compute downtime/uptime, availability, outage count, longest outage, MTBF/MTTR, SLA-met vs a target, and the error budget + budget remaining (stats.uptime_sla); the availability report managed always-on clients track; ztunnel original; unit-tested |
| Server | Health-probe / failover policy | FULL | mark each candidate healthy/unhealthy against latency/loss/staleness thresholds, score the healthy ones and order best-first into an active pick + failover chain, with a reason per unhealthy server (servers.failover_plan); the health-probe/failover logic a managed always-on client runs, as testable data; unit-tested |