Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down
27 changes: 27 additions & 0 deletions LaunchAgents/com.evansenter.tailscale.plist
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.evansenter.tailscale</string>

<!-- Open the Standalone app in the background at login. `open` exits once
the app is launched (or already running), so no KeepAlive. -->
<key>ProgramArguments</key>
<array>
<string>/usr/bin/open</string>
<string>-g</string>
<string>-a</string>
<string>/Applications/Tailscale.app</string>
</array>

<key>RunAtLoad</key>
<true/>

<key>StandardOutPath</key>
<string>__HOME__/.local/log/tailscale.out</string>

<key>StandardErrorPath</key>
<string>__HOME__/.local/log/tailscale.err</string>
</dict>
</plist>
11 changes: 11 additions & 0 deletions bootstrap.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Suggestion] The Standalone Tailscale app has its own "Launch at login" setting (Settings → General), which registers a login item. If that setting is ever switched on, the app gets launched twice at login. That is harmless, because open on an app that is already running does nothing. Still, a one-line comment on why the repo uses its own agent instead of that toggle would help. My guess is that the agent is version-controlled and gets restored by ./bootstrap.sh, while the toggle lives in app preferences and can be lost on reinstall. Writing that down would stop someone from later removing the agent as redundant.

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.
Expand Down
Loading