LaunchAgents & LaunchDaemons

Sanctum manages a set of macOS LaunchAgents (user-level) and LaunchDaemons (root-level) that form the boot chain for the haus intelligence platform. Each plist is rendered from templates at ~/.sanctum/templates/launchagents/ by the generate-plists.sh script, which pulls values from instance.yaml and tokens from the macOS Keychain.
You might wonder why these aren’t written in a friendlier format. The answer is that Apple chose XML for process management configuration in 2005 and has been politely pretending that was fine ever since. The template system exists so you never have to touch raw plist XML. You’re welcome.
Boot Chain Overview
Section titled “Boot Chain Overview”LaunchAgents are loaded at user login. The ordering below reflects the logical dependency chain — launchd does not guarantee ordering, but RunAtLoad: true ensures all agents start promptly after login.
In practice, everything starts within seconds of each other and sorts itself out. It’s less of a chain and more of a stampede in roughly the right direction.
LaunchAgents
Section titled “LaunchAgents”Every Sanctum-managed user-level agent has its own paragraph in the LaunchAgent Catalog — grouped by role (Core Infrastructure, AI & Voice, Network & Tunnels, System & Maintenance), each with port, KeepAlive setting, purpose, and the specific 3 AM incident that earned the paragraph. Twenty-three plists is a lot to scroll past on the way to a config tweak, so the catalog gets its own room.
The labels all live in ~/Library/LaunchAgents/, all follow the
com.sanctum.* namespace, all render from templates at
~/.sanctum/templates/launchagents/, and all load at user-session
start via launchctl bootstrap gui/$(id -u). The convention is the
only thing standing between the haus and twenty-three little XML
islands all doing roughly the same thing slightly differently.
LaunchDaemons
Section titled “LaunchDaemons”Everything above runs as your user. Everything below runs as root. There is exactly one daemon in this section, and it exists because of a number: 80. The lowest-numbered privilege escalation in the history of haus automation.
com.sanctum.dench-proxy
Section titled “com.sanctum.dench-proxy”| Property | Value |
|---|---|
| Label | com.sanctum.dench-proxy |
| Purpose | Reverse proxy from port 80 to port 1977 for the Holocron chat interface |
| Required Service | gateway |
| KeepAlive | Yes |
| RunAtLoad | Yes |
| Runs as | root |
This is a LaunchDaemon (not a LaunchAgent) because binding to port 80 requires root privileges. It enables http://holocron/ access from the LAN without specifying a port.
Plist location: /Library/LaunchDaemons/com.sanctum.dench-proxy.plist
The entire reason this runs as root is so family members can type holocron into a browser instead of holocron:1977. Usability has a cost. That cost is sudo.
Plist Generation
Section titled “Plist Generation”All plists are rendered from Mustache-style templates using values from instance.yaml and tokens from the macOS Keychain:
# Preview what would be generated (dry run)~/.sanctum/generate-plists.sh --dry-run
# Generate and install all plists for enabled services~/.sanctum/generate-plists.shThe generator:
- Reads each template from
~/.sanctum/templates/launchagents/ - Checks if the corresponding service is enabled in
instance.yaml - Expands
{{PLACEHOLDER}}tokens with config values - Pulls secrets from the macOS Keychain using the configured
keychain_account - Writes the rendered plist to
~/Library/LaunchAgents/(or/Library/LaunchDaemons/for daemons)
Managing Agents
Section titled “Managing Agents”Load or unload agents using launchctl:
# Load an agentlaunchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.sanctum.watchdog.plist
# Unload an agentlaunchctl bootout gui/$(id -u) ~/Library/LaunchAgents/com.sanctum.watchdog.plist
# Check if an agent is runninglaunchctl print gui/$(id -u)/com.sanctum.watchdog