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
.srsrule-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
NEPacketTunnelProviderextension 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.
App Store link: https://apps.apple.com/us/app/travelers-vpn/id6764288591

