Amirhossein Hosseinpouramirhp
CV
Active macOS app · Open source

TunnelGuard

Choose the websites that skip your VPN. Everything else stays on it.

A small Mac app that lives in the menu bar. Add a website, press Start, and traffic to that site uses your normal connection while the rest of your Mac stays on the VPN. It works with the routing macOS already has, so there is no kernel extension or network extension to install.

Download for Mac Source on GitHub

Free and open source under the GPL, published under BlackSwan Development, my own label. Needs macOS 13 Ventura or later and an administrator password. The download is not notarised yet, so the first launch needs right-click, Open.

On GitHub2026 to today
TunnelGuard · Exclusion Rules · illustrative

TunnelGuard

v2.7.0

3 active

Exclusion Rules

4 domains configured · gateway 192.168.1.1

  • intranet.example.com

    office

    192.0.2.14

  • bank.example

    banking

    198.51.100.7
    198.51.100.8

  • shop.example.net

    local marketplace

    203.0.113.25

  • cdn.example.org

    CDN

    203.0.113.80

Activity log

  1. sudo route -n add 192.0.2.14 192.168.1.1
  2. sudo route -n add 198.51.100.7 192.168.1.1
  3. sudo route -n add 198.51.100.8 192.168.1.1
  4. sudo route -n add 203.0.113.25 192.168.1.1

What you are looking at

  • A rule is a website. TunnelGuard looks up every address behind it, and you can add your own.
  • Press Start and traffic to those addresses goes through your own router instead of the VPN.
  • Pause a rule without deleting it.
  • Change networks and it finds the new router and applies the rules again.

Illustrative. Example domains and documentation-only addresses; the layout follows the real app.

// in-plain-words

In plain words

When a VPN is on, all of your traffic goes through it. Some services work fine without it, and some break because of it.

Without it

Everything goes through the VPN, including the services that break because of it. The only way out is to switch the VPN off.

With TunnelGuard

The websites you list use your normal connection. Everything else stays on the VPN, and the VPN stays on.

What I keep off the tunnel

Banks, local marketplaces and a few CDNs: the services that have to stay on a normal connection.

It asks before it acts

Changing how your Mac routes traffic needs your administrator password, or a one-time grant that covers the route command and nothing else.

It can start with your Mac

The routes it adds last until a restart. Set it to open at login and it applies your rules again on its own.

// getting-started

Getting started

It needs macOS 13 Ventura or later, and an administrator account.

Full guide on the documentation site

  1. Download

    Get TunnelGuard.dmg from the latest release on GitHub.

  2. Drag it into Applications

    Open the file and drag the TunnelGuard icon onto the Applications folder beside it.

  3. Open it the first time with right-click

    The build is not notarised yet, so macOS warns about an unidentified developer. Right-click the app, choose Open, then Open again. If macOS blocks it outright, use Open Anyway in System Settings, under Privacy and Security.

  4. Grant admin access, if you like

    In Settings, Admin Access, Grant Access lets it run the route command without asking for your password each time. It is optional and limited to that command.

  5. Add a website and press Start

    Add Domain (⌘N), type the address, then Start Whitelisting (⌘⇧R). Pasted links are cleaned down to the domain.

Rules can be exported and imported as JSON, for a backup or a second Mac. TunnelGuard changes your Mac's routing table: never use it to get around security controls you are required to follow.

// why-i-built-it

Why I built it

macOS gives you no way to exclude a specific domain from a VPN tunnel. TunnelGuard does it with a native menu bar app, bypass routes, and optionally the hosts file.

Context

This started as my own problem. Working from a VPN-heavy environment, some services have to stay off the tunnel: banks, local marketplaces, a few CDNs. Apple removed the kernel extension APIs that VPN clients used for split tunneling in Big Sur, and most clients still do not offer it.

The routing approach came from a post on dev.to by vavilov2212, which is credited in the repository.

The problem

You can do this by hand with the route command. But the rules vanish on every network change, DNS still resolves through the VPN so the bypass silently fails, and nothing tells you whether it is currently working.

Constraints

Admin rights, honestly
It changes system routing state and needs administrator rights, so it has to be clear about what it is about to do.
A shared hosts file
It cannot fight with other tools that write to the hosts file.
A Mac that moves
It has to survive the machine moving between networks.

// how-it-works

How it works

One click applies every rule, one click stops. Underneath, it is the same three commands you would type by hand, done carefully.

Every address, not just one

Domains are resolved with dig and nslookup, including domains with several addresses. Manual addresses can sit beside the resolved ones.

The right way out

The local default gateway is detected and validated, with a manual override for when detection returns something that is not an address, such as link#28.

DNS off the tunnel too

Resolved addresses can go into the hosts file so lookups skip the VPN's DNS, in a block format compatible with Local and WordPress Studio, so the tools do not overwrite each other. The file is backed up before every change.

It survives moving

It reads VPN DNS state through scutil, keeps route state by parsing netstat, and re-detects the gateway and reapplies when the network changes.

Pause, not delete

A rule can be switched off and on again without losing its domain, notes or manual addresses.

Narrow permissions

Every input to a shell command is validated, Hardened Runtime is on, and the admin grant is limited to specific commands and paths.

route.shthe commands TunnelGuard runs
# 1. Find every address behind the domain
dig +short example.com A

# 2. Send those addresses through your own router, not the VPN
sudo route -n add <address> <local-gateway>

# 3. Pause or remove the rule
sudo route -n delete <address>

// releases

Version 2 was about correctness

Four public releases in March 2026, each with a DMG attached.

Users reported the menu bar freezing. It turned out to be shell commands running on the main thread, and they all moved off it. Start-up diagnostics moved to a background thread. The menu bar icon is now driven by a Combine publisher, so its state can never drift from reality. A duplicate window on dock click and a delete dialog that switched tabs were both fixed.

VersionReleasedWhat changed
1.9.06 March 2026Passwordless route commands through a limited admin grant, gateway validation, inline editing, one copy running at a time.
1.9.57 March 2026The menu bar icon and the sidebar count follow the state of the rules.
2.07 March 2026Correctness: shell commands off the main thread, background start-up checks, a Combine-driven menu bar icon, window and dialog fixes.
2.7.026 March 2026Everything since 2.0: validation on every shell input, Hardened Runtime, a narrower admin grant, network change detection, JSON import and export, keyboard shortcuts, and a hosts file that stays current while rules run.
Result

Released with a documentation site and a written walkthrough. A small audience by design: it solves one specific problem for people who have it.

// hindsight

Hindsight

Two things I would do from the first release.

SwiftSwiftUICombinemacOS routescutilnetstatdignslookuphosts file

Repository, changelog and contributing guide

branch main 6 active projects ↑ 113 releases products/tunnelguard.md Sari --:-- UTC+3:30 its@amirhp.com