diff --git a/CLAUDE.md b/CLAUDE.md index 8b80aac..16845ae 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -120,7 +120,7 @@ Files in `home/` are symlinked to `~` by `bootstrap.sh`. This allows version con LaunchAgent plists live in `~/Library/LaunchAgents/`. The external service repos (event-bus, session-analytics, memory-store) have their own `make install-server`; obsidian-mcp is set up by *this* repo's bootstrap (`claude mcp add` + plist in `LaunchAgents/`, wrapper in `home/.bin/obsidian-mcp-start`, requires Obsidian with the Local REST API plugin running). Reload with `launchctl unload` + `launchctl load`. -Note: `agent-memory-store` is documented infra but is **not** provisioned by bootstrap (no `claude mcp add`, no `AGENT_MEMORY_STORE_URL` in `settings.local.json`) — it's registered out-of-band where used. The repo-managed LaunchAgents are: `com.evansenter.obsidian-mcp` (gateway only), `com.evansenter.jellyfin` (gateway only, runs the server binary from the `jellyfin` cask headless with `DOTNET_USE_POLLING_FILE_WATCHER=1` — on macOS 27 its FSEvents config watcher blocks startup forever; don't also launch Jellyfin.app, which would fight it for port 8096), `com.evansenter.sysload` (zellij CPU/RAM widget, if zellij installed), and `com.user.cargo-sweep` (periodic `cargo sweep`, if cargo installed). +Note: `agent-memory-store` is documented infra but is **not** provisioned by bootstrap (no `claude mcp add`, no `AGENT_MEMORY_STORE_URL` in `settings.local.json`) — it's registered out-of-band where used. The repo-managed LaunchAgents are: `com.evansenter.obsidian-mcp` (gateway only), `com.evansenter.jellyfin` (gateway only, runs the server binary from the `jellyfin` cask headless with `DOTNET_USE_POLLING_FILE_WATCHER=1` — on macOS 27 its FSEvents config watcher blocks startup forever; don't also launch Jellyfin.app, which would fight it for port 8096), `com.evansenter.tailscale` (gateway only, opens the Standalone Tailscale app at login — the app can't run before login and doesn't coexist with the open-source `tailscaled`, and FileVault blocks pre-login daemons anyway), `com.evansenter.sysload` (zellij CPU/RAM widget, if zellij installed), and `com.user.cargo-sweep` (periodic `cargo sweep`, if cargo installed). **Adding new LaunchAgents:** Plists go in the top-level `LaunchAgents/` directory (NOT `home/Library/LaunchAgents/`). Use `__HOME__` as a placeholder for the user's home directory — `install_launch_agent()` in bootstrap does `sed` substitution at install time. Logs go to `~/.local/log/`. Add an `install_launch_agent` call in `install_launch_agents()`, gated on the binary being present. diff --git a/LaunchAgents/com.evansenter.tailscale.plist b/LaunchAgents/com.evansenter.tailscale.plist new file mode 100644 index 0000000..2ca8faf --- /dev/null +++ b/LaunchAgents/com.evansenter.tailscale.plist @@ -0,0 +1,27 @@ + + + + + Label + com.evansenter.tailscale + + + ProgramArguments + + /usr/bin/open + -g + -a + /Applications/Tailscale.app + + + RunAtLoad + + + StandardOutPath + __HOME__/.local/log/tailscale.out + + StandardErrorPath + __HOME__/.local/log/tailscale.err + + diff --git a/bootstrap.sh b/bootstrap.sh index 750902b..da17cd1 100755 --- a/bootstrap.sh +++ b/bootstrap.sh @@ -398,6 +398,17 @@ install_launch_agents() { install_launch_agent "com.evansenter.jellyfin.plist" fi + # Install Tailscale LaunchAgent (gateway host, from the tailscale-app cask) + # Opens the Standalone app at login so the gateway's tailnet services come + # back after a reboot. The app can't run before login (only the open-source + # tailscaled can, and the two don't coexist); with FileVault on, nothing + # runs before the first unlock anyway. Overlaps the app's own "Launch at + # login" toggle on purpose: that lives in app prefs and is lost on reinstall, + # while this is tracked here and restored by bootstrap (a double `open` is a no-op). + if is_gateway_host && [[ -d /Applications/Tailscale.app ]]; then + install_launch_agent "com.evansenter.tailscale.plist" + fi + # Install sysload writer LaunchAgent (only if zellij is installed) # Caches `top` output to ~/.cache/sysload every 10s so the zjstatus # sysload widget can read it without spawning `top` per-tab-per-redraw.