TUNNELBLICK // ztunnel PORT REPORT

Every feature enumerated from the Tunnelblick feature surface, each hand-assessed against the real source. Status is conservative: a capability whose engine works but whose privileged helper or GUI is incomplete is PARTIAL, not FULL.
145 features tracked · 6 out-of-scope · 0 overclaim flag(s) · generated 2026-07-01

⚠ Read this first β€” what this number is, and isn't

This is a code-verified coverage map, not a vibe. Status lives in scripts/port_features.tsv and is set by hand, conservatively: FULL only when behaviour AND the GUI match Tunnelblick; PARTIAL when the engine runs the capability but the privileged helper or UI is incomplete; MISSING when neither runtime nor UI exists. The generator does not grant status from grep β€” it only audits each FULL/PARTIAL claim against the source and stamps OVERCLAIM on any row whose evidence code is gone. Coverage weights FULL=1, PARTIAL=0.5. The big gaps are real and named: the privileged helper (utun/tun, routing, DNS), live OpenVPN process supervision, the WireGuard userspace data path, Keychain credential storage, on-demand connect and network monitoring.

Coverage: 82.4%

🟒 full 94 · 🟑 partial 51 · πŸ”΄ missing 0 · weighted 82.4% of 145 tracked features
82.4%weighted coverage
94full
51partial
0missing
6out-of-scope
0overclaim flags

Every Tunnelblick feature

ChapterFeatureStatusNotes / gap
ConfigurationsImport OpenVPN .ovpn configFULLdirective parse: remote/proto/dev/auth-user-pass + inline cert blocks
ConfigurationsImport Tunnelblick .tblk bundlePARTIALrecursive .ovpn/.conf locate (root or Contents/Resources/) + Info.plist CFBundleName naming; resolving bundled credential files into the live launch still pends the connect path
ConfigurationsImport WireGuard .confFULLsuperset of Tunnelblick: typed Interface/Peer parse with 32-byte key validation
ConfigurationsBackend auto-detectionFULL[Interface]/[Peer] => WireGuard, else OpenVPN
ConfigurationsConfiguration listFULLlist saved configs with parsed summary
ConfigurationsRemove configurationFULLdelete config + drop its session credential
ConfigurationsRename configurationFULLrename a saved config by id; persisted
ConfigurationsAdd config from pasted textFULLimport modal: paste .ovpn/.conf + name
ConfigurationsPer-config settings (route-all/DNS/reconnect/autoconnect)PARTIALthe four common toggles modeled + persisted; Tunnelblick's full per-config matrix (dozens of advanced flags) not ported
ConfigurationsDuplicate / share configurationFULLclone a saved config (raw + parsed fields + options) under a fresh id/name
ConnectionConnectPARTIALunder 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
ConnectionDisconnectPARTIALteardown transition; process/tunnel teardown lands with the backend
ConnectionConnection status setFULLdisconnected/connecting/connected/reconnecting/disconnecting/error + live status dots
ConnectionLifecycle state machine (legal transitions)FULLenforced transitions; double-connect / bad-disconnect rejected
ConnectionConnection logFULLbounded per-connection log + vpn.logs + GUI viewer
ConnectionByte counts / throughput readoutPARTIALcumulative stats + rate sampler + GUI readout; live source needs a running backend
ConnectionAuto-connect on launchFULLengine computes the launch set (configs flagged autoconnect, gated by the global setting); the host connects each on startup
ConnectionAuto-reconnect on dropPARTIALoption + Reconnecting status modeled; reconnect loop not wired
ConnectionMultiple simultaneous connectionsPARTIALmanager tracks per-config connections; concurrent live tunnels need the backends
ConnectionConnect on demand (when needed)PARTIALoption modeled/persisted per config; traffic-triggered activation needs the platform network monitor (phased with the helper)
ConnectionReconnect on network changePARTIALdefault-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
OpenVPNDrive the system openvpn binaryPARTIALargv 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
OpenVPNManagement interface protocolFULLSTATE/BYTECOUNT/PASSWORD/LOG parser + live socket session (connect, setup commands, read loop) behind net; loopback-tested
OpenVPNState mappingFULLmanagement STATE tokens -> ConnectionStatus
OpenVPNauth-user-pass credentialsFULLprompt detection + live management auth flow: answers >PASSWORD:Need 'Auth' with username/password management commands from the session cred store; loopback-tested
OpenVPNInline cert/key blocksFULL<ca>/<cert>/<key>/<tls-auth> detected during parse; launched verbatim from saved text
OpenVPNtls-auth / tls-cryptFULLcontrol-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
WireGuardWireGuard config model + key validationFULLtyped Interface/Peer; 32-byte base64 key validation
WireGuardUserspace data path (tunnel)PARTIALboringtun 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
PlatformPrivileged operation planFULLordered NetOp set (create iface/assign addr/route/DNS) built from a config
PlatformVirtual interface creation (utun/tun)PARTIALLinux 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
PlatformRouting (default gateway / route-all)PARTIALreal `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
PlatformDNS handling while connectedPARTIALmacOS `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)
PlatformPrivileged helper (SMAppService / polkit)PARTIALdirect-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
PlatformCross-platform abstraction (macOS + Linux)FULLhost detection + both backends apply real `ifconfig`/`route`/`networksetup` (macOS, verified) and `ip` (Linux) commands when root; needs_privilege only when not root
CredentialsUsername/password storePARTIALsession-only in-memory store + creds.set/clear commands; no GUI prompt
CredentialsKeychain / Secret Service storagePARTIALOS 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)
CredentialsPassphrase-protected keysFULLencrypted-key passphrase stored per config (creds.set passphrase arg) and answered on the >PASSWORD:Need 'Private Key' management prompt; loopback-tested
AppConnection list GUIFULLsidebar with live status dots + selection
AppConnect/disconnect actionFULLstate-aware action button per connection
AppStatus / throughput gridFULLstatus, local/server IP, received/sent
AppLog viewerFULLlive per-connection log pane
AppPreferencesPARTIALapp settings modeled + persisted; full preferences window not built
AppCommand paletteFULLthe view offers its commands to the appShell's shared ⌘K palette (setPaletteItems); the shell owns the palette chrome
AppEmbedded terminalFULLshared zpwr-embed-terminal panel
AppProfile persistenceFULLconfigs + settings saved as JSON
AppConnect/disconnect notificationsPARTIALTauri 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
ProtocolsBackend auto-detection (all formats)FULLURI scheme + plist + JSON + INI + directive sniff across the full protocol universe (unit-tested); GUI renders the detected protocol badge
ProtocolsSupported-protocol catalogFULLengine feature.matrix enumerates every protocol + feature; GUI "Protocols" modal lists them with launch-kind
ProtocolsIKEv2/IPsecPARTIALmodeled + .mobileconfig parse; live tunnel handed to the OS VPN stack (NEVPN/strongSwan) β€” pending the helper
ProtocolsL2TP/IPsecPARTIALmodeled + detected; bring-up needs the OS IPsec stack / external daemon
ProtocolsPPTP (legacy)PARTIALrecognized for migration; insecure protocol, data path needs pppd
ProtocolsSSTPPARTIALmodeled + detected; PPP-over-TLS data path pending
ProtocolsOpenConnect (Cisco AnyConnect / Juniper)PARTIALmodeled; driven by the openconnect binary (managed-process launch kind) β€” supervision pending
ProtocolsPalo Alto GlobalProtectPARTIALmodeled; openconnect --protocol=gp launch pending
ProtocolsShadowsocks (ss:// SIP002)PARTIALSIP002 + legacy ss:// parse (method+endpoint, unit-tested); AEAD data path pending
ProtocolsV2Ray/Xray (VMess/VLESS)PARTIALvmess:// base64-JSON + vless:// URI parse (endpoint/transport, unit-tested); data path pending
ProtocolsTrojanPARTIALtrojan:// URI parse (endpoint+sni, unit-tested); TLS data path pending
ProtocolsSoftEtherPARTIALmodeled; native client launch pending
ProtocolsTailscale (WireGuard mesh)PARTIALmodeled; coordinated bring-up via tailscaled pending
ProtocolsZeroTier overlayPARTIALmodeled; coordinated bring-up via zerotier-one pending
ProtocolsApple .mobileconfig (IKEv2/L2TP) parsePARTIALplist VPN payload parse (server/user, unit-tested)
ProtocolsConfig parse preview (any format)PARTIALengine parses arbitrary text -> backend+label+fields; import-modal live preview not yet wired
ProtocolsOpenVPN cipher / data-ciphersFULLcipher + data-ciphers directive parsed into the config summary (unit-tested)
SecurityKill switch (fail-closed firewall)PARTIALpf/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
SecurityDNS leak protectionPARTIALoff-tunnel :53 block in the plan + per-config toggle; enforcement needs the helper
SecurityIPv6 leak protectionPARTIALIPv6 block in the plan + toggle; enforcement needs the helper
SecurityAllow LAN while lockedPARTIALRFC-1918 carve-outs in the kill-switch plan + toggle
SecurityAd/tracker/malware blockingPARTIALper-config toggle + global blocklist setting; sinkhole resolver needs the DNS data path
SecurityGlobal kill switch + always-onPARTIALglobal settings + GUI toggles; enforcement needs the helper
RoutingSplit tunneling (CIDR include/exclude)PARTIALmode + cidrs modeled/persisted + feed the kill-switch excludes; route-table enforcement needs the helper
RoutingApp-based split tunnelingPARTIALper-app include/exclude list modeled; per-app marks/cgroup need the helper
RoutingCustom MTUPARTIALper-config MTU modeled/persisted; applied by the data path/helper
MultihopMulti-hop / double VPNPARTIALentry-hop config id modeled/persisted; nested bring-up needs both tunnels live
DNSCustom DNS servers (DoH/DoT)PARTIALper-config resolver override modeled/persisted; applied via the DNS op/helper
ObfuscationPluggable transport / obfuscationPARTIALobfs4/scramble/ws/reality selector modeled/persisted; transport wrap needs an external binary
ServerServer catalog (add/remove/list)PARTIALtyped ServerEntry CRUD persisted in the profile; browse GUI panel not yet built
ServerFavoritesPARTIALfavorite toggle persisted; pure favorites() filter (unit-tested); GUI panel pending
ServerLatency ping + fastest-serverPARTIALTCP-connect latency probe + fastest_first ranking (unit-tested); live probe needs the net feature + GUI
ServerCustom server entryPARTIALtyped endpoint w/ geo/tags/latency; persisted
TrustAuto-connect on untrusted Wi-FiPARTIALpolicy + trusted_networks list modeled/persisted; SSID read needs the helper
TransportPreferred protocol selectionPARTIALpreference modeled/persisted + GUI setting; multi-protocol server negotiation pending
AppPrivacy & security toggles UIFULLper-config kill-switch / DNS-leak / IPv6-leak / LAN / ad-block toggles via zgui-core toggle group, persisted
AppKill-switch firewall plan viewerFULLGUI renders the computed pf/nft ruleset in a zgui-core modal
RoutingCIDR math: contains / overlap (v4+v6)FULLpure IPv4+IPv6 CIDR containment/overlap test (net.cidr_contains), the foundation of split-tunnel/AllowedIPs routing; unit-tested
RoutingSubnet calculator (network/broadcast/mask/hosts)FULLfull ipcalc surface β€” network, broadcast, netmask, wildcard, usable host range + count (net.subnet_info); beyond any VPN client; unit-tested
RoutingCIDR aggregation (minimal route set)FULLcollapse a CIDR list into the minimal equivalent (drop contained + merge siblings), net.cidr_aggregate; route-table optimization beyond WireGuard AllowedIPs handling; unit-tested
RoutingSplit-tunnel route computation (CIDR subtraction)FULLexact 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)
RoutingPer-destination route decisionFULLdecide tunnel-vs-direct for a destination IP under a config's split policy (split.evaluate); an original per-destination probe; unit-tested
WireGuardWireGuard key generation (X25519)FULLpure-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
SecurityConfig security linterFULLlints 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
ConfigurationsConfig diff (field-level)FULLstructured field-by-field diff of two saved configs (config.diff); an original; unit-tested
DNSDNS resolver classification (plain/DoH/DoT/DoQ/DNSCrypt)FULLtyped classification of a resolver spec incl. bracketed IPv6 + ports (dns.parse_server); unit-tested
DNSDNS leak analysisFULLper-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
MultihopMulti-hop cascade resolution (arbitrary depth)FULLresolve 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)
ObfuscationObfuscation transport catalog + compatibilityFULLauthoritative pluggable-transport catalog + per-backend compatibility check (obfs.catalog); unit-tested. Running a transport needs its external binary (existing partial Obfuscation row)
ServerRecommended-server scoring (latency/load/geo)FULLcomposite 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
WireGuardValidate + normalize/export configFULLwireguard::parse + to_conf round-trip (config.wireguard_format) + import-modal "Validate WG" button that canonicalizes a pasted config; beyond Tunnelblick (OpenVPN-only)
ConfigurationsValidate + normalize OpenVPN profileFULLcanonicalize 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
ConfigurationsPortable profile export / importFULLversioned 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
RoutingRange → minimal CIDR coverFULLdecompose 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
RoutingIP address RFC classificationFULLcoarse 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
RoutingTunnel MTU / TCP MSS calculatorFULLeffective 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
WireGuardAllowedIPs builder (default minus excludes)FULLcompute 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
DNSAd/tracker blocklist matchFULLsubdomain-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
RoutingApp-based split-tunnel decisionFULLper-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
ConnectionLifecycle transition pre-flightFULLpure 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
TrustWi-Fi trust connection policyFULLdecide 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
ConnectionSession summary (human transfer + uptime)FULLhuman-readable per-session transfer totals + uptime (stats.session_summary, human_bytes/human_duration); the connection-info panel every client shows; unit-tested
UsageData budget accountingFULLcumulative 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
RoutingRoute-table conflict detectionFULLflag 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
RoutingTunnel coverage / leak-surface analysisFULLresidual 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
RoutingSubnet split (VLSM)FULLsplit 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
RoutingIPv6 ULA prefix builder (RFC 4193)FULLbuild 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
RoutingPath-MTU discovery (binary search)FULLconverge 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
ServerConnection-quality scoring (jitter/loss)FULLmean/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
ServerQuality-based server rankingFULLrank servers best-first by the jitter/loss-aware quality score (servers.rank_quality); ztunnel original beyond a single latency sort; unit-tested
ConfigurationsOpenVPN inline block extractionFULLextract + 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
RoutingAllowed-apps policy (app-based VPN control)FULLbulk 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
SecurityKill switch in chosen packet-filter syntaxFULLrender a config's lockdown ruleset as pf / nft / iptables on demand (vpn.killswitch_syntax); emit legacy iptables even off the computed platform; unit-tested
MultihopCascade quality validation (diversity)FULLflag 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
DNSEncrypted-DNS bootstrap analysisFULLflag 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
ConnectionConnection-log analysis / outcomeFULLclassify 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
WireGuardNoise_IKpsk2 message wire layoutFULLthe 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
WireGuardHandshake timer/rekey scheduleFULLderive 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
WireGuardAllowedIPs overlap/dedup minimizerFULLcollapse 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
OpenVPNStatic-key / tls-crypt-v2 parseFULLdecode + 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
ProtocolsIKEv2/IPsec proposal model + validatorFULLmodel 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
ProtocolsSOCKS5/HTTP proxy-over-tunnel configFULLvalidate 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
SecurityPort-forward / DNAT rule generationFULLrender 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
RoutingTCP-MSS / MTU clamp rule generationFULLcompute 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
RoutingNAT64 / DNS64 prefix synthesis (RFC 6052)FULLembed/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
RoutingIPv6 SLAAC EUI-64 / privacy classificationFULLbuild 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
ConnectionAuto-reconnect backoff schedulerFULLdeterministic 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
UsageBandwidth accounting rollups + projectionFULLroll 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
ServerNamed server-selection strategiesFULLpick 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
ConfigurationsOpenVPN <-> WireGuard config migrationFULLtranslate 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
DNSDNS query wire-format + DoH/DoT request buildFULLencode 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
WireGuardCookie / MAC DoS-mitigation modelFULLmodel 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
OpenVPNPUSH_REPLY parseFULLparse 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
ProtocolsIKEv2 traffic-selector narrowing (RFC 7296)FULLnarrow 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
DNSSplit-horizon resolver routingFULLpick 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
SecurityConfig secret redactionFULLproduce 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
UsageUptime / SLA accountingFULLclamp+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
ServerHealth-probe / failover policyFULLmark 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

Out of scope (not counted)

FeatureReason
Menu-bar status item / agent app
Sparkle auto-update
Deploying forced configurations (admin)
AppleScript support
Tunnelblick VPN (proprietary protocol)
Appearance / icon-set themes
Generated by scripts/gen_port_report.sh from scripts/port_features.tsv. Regenerate after any port work: scripts/gen_port_report.sh --write.