The tray's Quit handler already boots out com.wireguard.vpn-tray, but install-tray.sh had retired that launchd job and relied on the fleet agent to nohup it — which never ran the tray reliably at boot (no GUI session yet). Restore the LaunchAgent (same pattern as com.lilith.mac-sync): RunAtLoad starts it at login in the GUI session, KeepAlive relaunches on crash. ensure_tray() now defers to launchd when the agent is installed (Popen path kept as fallback). Removes the dead standalone plist. |
||
|---|---|---|
| .. | ||
| icons | ||
| lilith_tray | ||
| .gitignore | ||
| com.natalie.wg-quick-wg1.plist | ||
| generate_icons.py | ||
| install-tray.sh | ||
| README.md | ||
| requirements.txt | ||
| vpn-toggle.applescript | ||
| vpn-tray | ||
| vpn_tray.py | ||
wireguard-vpn-tray
macOS menu-bar app showing live WireGuard mesh (wg1) status as a colored
hexagon icon plus a Status menu item.
- 🟢 green — tunnel up and the mesh hub (
10.9.0.1) is reachable - 🟡 yellow — tunnel up but the hub is not yet reachable (connecting)
- 🔴 red — no tunnel interface present
Built on the in-house lilith_tray framework (vendored here under
lilith_tray/) via its macOS rumps backend.
Interface detection
macOS assigns the WireGuard utun device dynamically — the number changes
between boots. The app therefore identifies the tunnel by the address it
carries (the utun with a 10.9.0.x inet address), never by a hardcoded
name. The tray icon and the Status label are both derived from a single
status computation in poll_status(), so they can never disagree.
Layout
| Path | Purpose |
|---|---|
vpn_tray.py |
the tray app |
vpn-tray |
launcher — activates the venv and runs the app |
lilith_tray/ |
vendored tray framework |
icons/ |
colored status icons |
generate_icons.py |
regenerates the icon set |
vpn-toggle.applescript |
standalone connect/disconnect applet |
com.wireguard.vpn-tray.plist |
launchd agent for the tray |
com.natalie.wg-quick-wg1.plist |
launchd agent bringing wg1 up at boot |
requirements.txt |
Python dependencies |
The bundled launchd plists carry absolute paths for the current install
(~/.wireguard/); adjust them if the app is installed elsewhere.
Setup
python3 -m venv .venv
.venv/bin/pip install -r requirements.txt
Install the launchd agent:
cp com.wireguard.vpn-tray.plist ~/Library/LaunchAgents/
launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.wireguard.vpn-tray.plist
Secrets
WireGuard keys (*.key) and the tunnel config (wg1.conf) are intentionally
not in this repo — see .gitignore. They live in ~/.wireguard/.