Run the bus as a service
scripts/install-unit.sh renders a launchd plist or a systemd user unit from deploy/ with absolute paths: the current node, dist/cli.js, the same BUS_DB the CLI uses, and a PATH that includes tailscale (the bind guard needs it).
Preview
bash scripts/install-unit.sh --dry-run --launchd
bash scripts/install-unit.sh --dry-run --systemd
bash scripts/install-unit.sh --print-db
These print only and write nothing.
macOS (launchd)
bash scripts/install-unit.sh --launchd
launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.catesworks.session-bus.plist
The plist has RunAtLoad and KeepAlive. Logs go under ~/.local/state/session-bus/log/ (or $XDG_STATE_HOME).
Remove:
launchctl bootout gui/$(id -u)/com.catesworks.session-bus
rm ~/Library/LaunchAgents/com.catesworks.session-bus.plist
Linux (systemd, user unit)
bash scripts/install-unit.sh --systemd
systemctl --user enable --now session-bus
The unit uses Restart=on-failure, NoNewPrivileges=true, ProtectSystem=strict and ReadWritePaths=<BUS_DB dir>.
The rendered unit is checked by the test gate, but the unit has not been run on a Linux host. systemd-analyze --user verify was skipped where tested (macOS).
Remove:
systemctl --user disable --now session-bus
rm ~/.config/systemd/user/session-bus.service
Direct tailnet bind
For the direct-bind variant, set the bind address when rendering:
BUS_HOST=$(tailscale ip -4) bash scripts/install-unit.sh --launchd
After a Node upgrade
Under nvm, the node path is versioned (.../versions/node/v24.x.y/bin/node). After an upgrade that path disappears and the unit fails. Run install-unit.sh again, reload the unit, and re-run setup-client.sh on clients for the bus-push entry.