Adds the tray application to the release: - Every target's archive now also contains the tsunagi-tray binary. On Linux it is built against an old glibc baseline (via cargo-zigbuild) rather than static musl, because a GUI must dlopen libGL/X11/Wayland at runtime and a static musl binary cannot; the agent stays static musl. macOS and Windows build the tray natively. - New GUI package variants for Linux: `tsunagi-gui` .deb and .pkg.tar.zst, built by the existing scripts with an extra tray argument. They install the agent (service, polkit, user — same as the headless packages) plus the tray binary, a desktop launcher (dist/linux/tsunagi-tray.desktop) and an icon (dist/linux/tsunagi.svg), so the GUI appears in the applications menu. The GUI package conflicts/replaces/provides the headless tsunagi so the two do not coexist. - The tray finds the system service with no configuration: the library's default control-socket path now prefers /run/tsunagi/agent.sock when present (both the tray and CLI use this), falling back to the per-user path. No control-protocol change. Builds, fmt and clippy clean on macOS; the Linux GUI cross-build and the packages are exercised only by a real CI run. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Running tsunagi on Linux
This archive holds a static tsunagi binary (musl), so it runs on any
distribution — Arch, Alpine, NixOS, old glibc systems — with no shared-library
dependencies. Alongside it are a systemd unit, a polkit rule, and an installer.
Creating the TUN interface needs CAP_NET_ADMIN, and configuring
systemd-resolved is decided by polkit. There are three ways to run it; pick one.
On Debian/Ubuntu and Arch, prefer the native package (
.deb/.pkg.tar.zst) from the release — it does all of the below for you.
Quick install — systemd service as a dedicated user
sudo ./install.sh
sudo systemctl enable --now tsunagi
The service runs as an unprivileged tsunagi user with just CAP_NET_ADMIN
and CAP_NET_BIND_SERVICE (granted by systemd), not root. systemd creates its
state (/var/lib/tsunagi), cache (/var/cache/tsunagi) and runtime
(/run/tsunagi) directories, and the polkit rule lets it configure the
resolver. Control it with sudo — the CLI finds the running service's socket at
/run/tsunagi/agent.sock with no flags:
sudo tsunagi status
sudo tsunagi join <network-name> <tsn1…secret>
As your own user — one capability, no service
sudo install -m 0755 tsunagi /usr/local/bin/tsunagi
sudo setcap cap_net_admin,cap_net_bind_service+p /usr/local/bin/tsunagi
sudo usermod -aG tsunagi "$USER" # so the resolver works; log in again after
tsunagi up
Nothing runs as root. The capability is lost on every rebuild or copy of the
binary, so re-run setcap after replacing it. The agent uses your per-user
state directory and socket, so plain tsunagi status (no sudo) reaches it.
Without touching the OS
tsunagi up --no-tun
Tunnels form and handshake between agents, but no interface, address or route is created, so traffic never reaches the operating system. Needs no privileges.
What the agent installs
Everything is tagged to its own interface, computed from local state, and reversible — removed when a network leaves, broadcast is turned off, or the agent stops:
- a TUN interface configured over netlink (addresses, MTU, up);
- while broadcast is on, a
255.255.255.255route through the interface and an inbound-UDP firewall allowance from the overlay range (iptables, tagged); - the systemd-resolved setting that sends the overlay zones' questions to the local resolver.
Nothing taken from a remote peer ever becomes a path, a command argument or an OS setting.