# 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` ```sh sudo tsunagi up ``` In another terminal, the control commands talk to it over the system control socket and do **not** need `sudo`: ```sh tsunagi status tsunagi join ``` 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`](cy.hexor.tsunagi.plist). ```sh # Put the binary where the plist expects it (edit the plist for another path). sudo install -m 0755 target/release/tsunagi /usr/local/bin/tsunagi # 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: ```sh 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 tsunagi 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 **`utun` interface**, configured with `ifconfig` (addresses, MTU, up) and a subnet `route` for the overlay range. The kernel assigns the `utunN` name; the agent adopts whatever it is given. A `utun` is torn down with the process, so a crash never leaves one behind. - `/etc/resolver/` files pointing the system resolver at the overlay DNS server for each network's zone, with a `port` so 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.255` **route** through the overlay interface. The matching inbound-UDP firewall allowance is **not** implemented yet (it needs a `pf` anchor); `tsunagi status` reports 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.