The command line package is now `tsunagi` and builds the `tsng` executable, which the packages, the unit, the installers and the workflows install and run. The core library package becomes `tsunagi-core`, keeping the library name `tsunagi`, so no code changes `use tsunagi::...`. Directories follow (crates/tsunagi-core, crates/tsunagi). Every command shown to a person is now `tsng ...`. Co-Authored-By: Claude <noreply@anthropic.com>
Running tsunagi on macOS
The agent needs root on macOS: creating a utun interface and writing
/etc/resolver are both root-only, and macOS has no per-capability grant like
Linux. There are two ways to run it.
Quick start — sudo
sudo tsng up
In another terminal, the control commands talk to it over the system control
socket and do not need sudo:
tsng status
tsng join <network-name> <tsn1…secret>
The agent uses fixed system paths, so the commands find it regardless of whose
shell started sudo:
| what | path |
|---|---|
| mandatory state | /var/db/tsunagi |
| disposable cache | /var/db/tsunagi/cache |
| control socket | /var/run/tsunagi/agent.sock |
Native deployment — root LaunchDaemon
For an always-on agent that starts at boot, install the bundled
cy.hexor.tsunagi.plist.
# Put the binary where the plist expects it (edit the plist for another path).
sudo install -m 0755 target/release/tsng /usr/local/bin/tsng
# Install the daemon. launchd refuses a daemon plist that is not owned by
# root:wheel or that is group/world-writable.
sudo install -m 0644 -o root -g wheel \
dist/macos/cy.hexor.tsunagi.plist \
/Library/LaunchDaemons/cy.hexor.tsunagi.plist
# Load and start it.
sudo launchctl bootstrap system /Library/LaunchDaemons/cy.hexor.tsunagi.plist
sudo launchctl enable system/cy.hexor.tsunagi
Manage it afterwards with:
sudo launchctl kickstart -k system/cy.hexor.tsunagi # restart
sudo launchctl bootout system/cy.hexor.tsunagi # stop and unload
tail -f /var/log/tsunagi.log # logs
The control commands (status, join, network, dns) work the same against
the daemon as against a sudo tsng up, over the same socket.
What the agent does to the system
Everything it installs is tagged to its own interface, computed from local state, and reversible — it is removed when a network leaves, broadcast is turned off, or the agent stops:
- a
utuninterface, configured withifconfig(addresses, MTU, up) and a subnetroutefor the overlay range. The kernel assigns theutunNname; the agent adopts whatever it is given. Autunis torn down with the process, so a crash never leaves one behind. /etc/resolver/<network>files pointing the system resolver at the overlay DNS server for each network's zone, with aportso the server stays off 53. Each file carries a marker line; the agent only ever replaces or removes files it wrote, never one you created by hand.- while broadcast is on, a
255.255.255.255route through the overlay interface. The matching inbound-UDP firewall allowance is not implemented yet (it needs apfanchor);tsng statusreports that half as incomplete and names what to allow by hand if your host firewall drops discovery replies.
Nothing taken from a remote peer ever becomes a path, a command argument or an OS setting: the interface name comes from the kernel, addresses and ranges from signed state the agent already verified.