Route macOS traffic through WireGuard tunnels.
Rules you can read.

A SOCKS5/HTTP proxy that reads a plain-text config, matches each outbound connection against a list of rules, and forwards it through a named WireGuard tunnel, or directly, or drops it.

Not a VPN. The tunnel is yours; woute decides which traffic uses it.

The entire program is a single Python file. Read it before you run it.

woute connection sequence Step-by-step flow when an app opens a connection, showing the three possible outcomes based on rule match: tunnel, direct, or block. app curl, browser proxy listener SOCKS5 rule engine pattern match tunnel / target per match internet CONNECT host:port match(host) action alt tunnel host matched a tunnel or group (e.g. *.anthropic.com -> VPN) open WireGuard tunnel · handshake tunnel ready TCP to host bytes in (via tunnel) bytes out (via tunnel) alt direct host matched `direct` (e.g. 192.168.0.0/16 -> direct) TCP directly to host · no tunnel bytes in (direct) bytes out (direct) alt block host matched `block` (e.g. *doubleclick* -> block) refuse connection (no outbound connection made) All rule evaluation happens in-process. No external service. No phone-home.

I already had WireGuard tunnels. I wanted per-domain rules where the config still made sense six months later, and where the code handling my traffic was short enough to read before running it with sudo.

Most alternatives are unreadable (Clash YAML, sing-box JSON), closed source (Surge), or both. Running any of them with sudo means handing control of your network traffic to code you probably have not read. woute is a few hundred lines of Python. The routing config looks like what it does. Read it first, then run it.

For
You have WireGuard credentials and want specific traffic, not all of it, routed through a tunnel.
You want different domains or IP ranges to use different tunnels.
You want to read every line of code that handles your traffic before trusting it with sudo.
Not for
A single always-on tunnel with no per-domain rules. Use wg-quick.
Corporate VPN access. Use your employer's client.
Linux or Windows (macOS only).
git clone https://github.com/harryngai/woute
cd woute && ./install.sh

The installer checks and installs all dependencies (Homebrew, python3, wireguard-go, wireguard-tools), configures PATH, and copies woute to ~/.local/bin/.

A config file has five section types. Each [Tunnel: name] block registers a named tunnel automatically — no separate proxy declaration needed.

[General]
socks5-listen = 7890
dns           = 1.1.1.1, 1.0.0.1

[Log]
level     = warning
folder    = log

[Tunnel Group]
VPN = Taiwan, Japan

[Tunnel: Taiwan]
…WireGuard keys and endpoint…

[Rule]
*.anthropic.com  -> VPN
default          -> direct

Evaluated top-to-bottom. First match wins. The right side names a tunnel, group, or built-in action.

[Rule]
*.anthropic.com    -> VPN      # through VPN group
*doubleclick*      -> block    # drop the connection
192.168.0.0/16     -> direct   # LAN stays local
default            -> direct   # catch-all, must be last
Pattern Matches
*.example.com example.com and all subdomains
example.com only exactly example.com
*keyword* any domain containing “keyword”
1.2.3.0/24 IP addresses in CIDR range
default everything not matched above
Action Effect
direct Connect without a tunnel
block Drop the connection
name Route through the tunnel or group named name

A group holds multiple tunnels and tries them in order — first tunnel with a recent WireGuard handshake wins. To change priority, reorder members in the config and restart.

[Tunnel Group]
VPN = Taiwan, Japan

Each WireGuard peer gets a named section. Keys map directly to WireGuard config.

[Tunnel: MyTunnel]
private-key  = YOUR_PRIVATE_KEY
address      = 10.14.0.2
dns          = 1.1.1.1, 1.0.0.1
mtu          = 1280
public-key   = PEER_PUBLIC_KEY
endpoint     = wg.example.com:51820
allowed-ips  = 0.0.0.0/0
Key What it sets
private-key Your WireGuard private key
address IP assigned to your end of the tunnel; must be unique per tunnel
dns DNS servers used through this tunnel
mtu Maximum packet size; 1280 is safe for most networks
public-key The peer server's public key
endpoint Peer server address and UDP port
allowed-ips Which IPs to route through this tunnel
sudo woute woute.conf

sudo is required — WireGuard needs to create network interfaces and modify the routing table. In a terminal, a live panel shows tunnel status and recent traffic. Press q to stop. Routes are cleaned up automatically.

KeyAction
lToggle log view
tTest rules / test selected log entry
rReload config, or switch to a new config file
qQuit

When stdout is not a TTY, woute skips the panel and logs only. Set folder = log in [Log] to write woute-YYYYMMDD.log alongside the config file.

Command Description
sudo woute Start with default.conf
sudo woute <file> Start with a specific config file
sudo woute <file> -v Start with verbose logging
woute -t <host> Show which rule matches a given host