Disclosure up front: I made this, it’s a $2.99 one-time app, and I’m posting here because c/vpn is the audience that gets why “tunnel everything” is the wrong default.

Most iOS/macOS VPN clients are one big switch. Connect → every packet leaves the country. Your banking app breaks, your local services stop loading, Maps gets confused, and if you’re at home behind a regional ISP you also lose anything that depends on that. The fix everyone uses is to keep toggling the VPN on and off, which is miserable.

I wanted a client where routing is per destination by default, not all-or-nothing:

  • You bring your own server (Outline, WireGuard, Shadowsocks, or Trojan — a $5/mo VPS is enough). No commercial VPN provider in the loop.
  • The client decides destination-by-destination what tunnels vs. stays direct, driven by region profiles.
  • Profiles use sing-box’s binary .srs rule-sets built from SagerNet’s sing-geosite + sing-geoip. A US↔JP profile keeps JP domestic destinations direct so they work on the local network, and routes the rest via your server. US↔CN profile does the inverse. Profiles are editable, you can layer your own rules on top.
  • Runs inside an NEPacketTunnelProvider extension on Apple’s Network Extension framework, so the system handles the tunnel lifecycle natively (no “please don’t kill my background process” tricks).
  • I run zero backend. No account, no email, no telemetry. Configs sync across iPhone/iPad/Mac via iCloud (end-to-end encrypted if you have Advanced Data Protection on).

Universal Purchase across iPhone/iPad/Mac (native menu-bar app on macOS). $2.99 once, no subscription.

Happy to get into the weeds on rule-set authoring, why I went sing-box instead of rolling routing in Swift, or the app↔extension IPC.