WireGuard tunnels for the Omarchy bar: a status icon with a panel that lists every WireGuard profile NetworkManager knows, switches between them with one click, graphs live traffic and verifies the exit IP.
Profiles are found automatically. To add one:
nmcli connection import type wireguard file my-gateway.conf- Every NetworkManager WireGuard profile, discovered automatically.
- One tunnel at a time by default: bringing one up takes the others down first, because two full-tunnel gateways break each other's routing.
- A default tunnel for the power switch, the icon's right-click and the IPC
oncall. Star any row to choose it. - A graph of the last minute of download and upload for the active tunnel, with the current rates and totals.
- Exit-IP check against the profile's peer endpoint: "verified" when the public address is the gateway, "unexpected" when it is not, "not checked" when the endpoint is a host name.
- A notification after every connect, switch, disconnect or failure. Connect and switch report the exit IP and flag a mismatch. Can be turned off.
- Panel keys and IPC calls for every action.
omarchy plugin add https://github.com/selfcrypto/omarchy-wireguard.git --enableVerify:
omarchy plugin list
omarchy plugin validate ~/.config/omarchy/plugins/io.github.selfcrypto.wireguardUpdate:
omarchy plugin update io.github.selfcrypto.wireguardRemove:
omarchy plugin remove io.github.selfcrypto.wireguard- Left click on the bar icon opens the panel.
- Right click connects the default tunnel, or disconnects.
- Middle click refreshes.
- In the panel: click a row to switch to it (or disconnect it), click the
star to make it the default. Keys:
ttoggle,1–9pick a tunnel (the default is always1),dmake the connected tunnel the default,rrefresh and re-check the exit IP.
Settings live on the widget's entry in ~/.config/omarchy/shell.json and are
set with omarchy bar set:
omarchy bar set io.github.selfcrypto.wireguard defaultTunnel my-gateway
omarchy bar set io.github.selfcrypto.wireguard exclusive false --json
omarchy bar set io.github.selfcrypto.wireguard notifications false --json
omarchy bar set io.github.selfcrypto.wireguard refreshIntervalSec 5| Key | Meaning | Default |
|---|---|---|
defaultTunnel |
NetworkManager connection name the switch / right-click / on bring up |
first profile |
exclusive |
bring every other tunnel down before bringing one up | true |
notifications |
notification after every connect, switch, disconnect or failure | true |
refreshIntervalSec |
status poll interval, 2–60 s | 3 |
omarchy-shell io.github.selfcrypto.wireguard status # "gateway" or "disconnected"
omarchy-shell io.github.selfcrypto.wireguard list # JSON: tunnels, active, default
omarchy-shell io.github.selfcrypto.wireguard on | off # default tunnel up / everything down
omarchy-shell io.github.selfcrypto.wireguard up <name>
omarchy-shell io.github.selfcrypto.wireguard down <name>
omarchy-shell io.github.selfcrypto.wireguard setDefault <name>
omarchy-shell io.github.selfcrypto.wireguard toggle # the panel- NetworkManager with WireGuard profiles (
nmcli);curlfor the exit-IP check;shandtimeout(coreutils). All ship with Omarchy. - Runs entirely as the user: NetworkManager's default policy lets the active local session bring its connections up and down, so no polkit prompt.
- One external request: the exit IP comes from
https://1.1.1.1/cdn-cgi/trace(Cloudflare), only while the panel is open or a tunnel changes. - The exit-IP verdict is only given when the profile's endpoint is an IP literal; a hostname endpoint shows the address tagged "not checked".
Every external command goes through wg-helper.sh, one process per
operation, as timeout -k 3 <deadline> sh wg-helper.sh <command> args...:
- Deadlines: each operation has a wall-clock limit that covers its whole
process group (
timeoutruns the helper as a group leader and signals the group, then kills it). Discovery 10 s, byte counters 5 s, exit IP 10 s, a switch 40 s plus 10 s per extra tunnel to bring down, toasts 15 s. - Fixed argv: tunnel names travel as arguments, never inside a script, and only names returned by the last discovery are passed on.
- Bounded, validated output: discovery lists at most 32 profiles, clips
names to 256 bytes and endpoints to 253, accepts interface names only when
they satisfy the kernel's own rules (1–15 bytes, no
/,:or whitespace) before touching/sys/class/net, and emits JSON. The widget re-validates every field it parses. - Exit IP: HTTPS only, no redirects, 3 s connect and 6 s total deadline,
4 KiB response cap in
curland again in the reader; the value is used only when the trace carries exactly oneip=line holding a canonical IPv4 address. - Text from outside (names, endpoints, the exit IP) is shown as plain text, capped and elided. IPC replies have the same limits.
Start with CLAUDE.md: the map of the code, the current state and the release steps.
MIT. See LICENSE.
