macOS Launchd Agent
Install, smoke-test, and remove the macOS endpoint agent with the launchd helper path.
macOS launchd agent
This packaging path is for the macOS device agent. The control-plane API and background workers remain documented separately and are not packaged here as macOS services.
Recommended flow
- From the repository root, create a virtual environment and install dependencies:
python3 -m venv .venv
./.venv/bin/python -m pip install -r requirements-agent.txt
- Copy
agent/config.macos.example.jsontoagent/config.jsonfor a macOS-oriented starting point, or useagent/config.example.jsonif you prefer the shared cross-platform sample.
The sample config already uses __CURRENT_PYTHON__ for TPM and updater helper commands. Keep that placeholder if you want those hooks to run with the same interpreter as the launchd job. The sample config leaves attestation_private_key_path, attestation_public_key_path, and update_signing_public_key_path blank so the default preflight works before you enable signed attestation or signed update manifests.
- Validate the agent config and referenced files:
./.venv/bin/python -m agent.config --config-path agent/config.json
- Validate the agent interactively once before registering launchd:
./.venv/bin/python -m agent.agent_secure
- Preview the rendered plist locally before you touch
/Library/LaunchDaemons:
make macos-launchd-preview \
PYTHON="$(pwd)/.venv/bin/python" \
MACOS_LAUNCHD_PYTHON="$(pwd)/.venv/bin/python"
The repo shortcut defaults the install root to the current repository, reads agent/config.json, and writes rendered output to build/macos-launchd/ unless you override it.
- Render the LaunchDaemon plist into
/Library/LaunchDaemons:
sudo make macos-launchd-render \
PYTHON="$(pwd)/.venv/bin/python" \
MACOS_LAUNCHD_PYTHON="$(pwd)/.venv/bin/python" \
MACOS_LAUNCHD_OUTPUT_DIR=/Library/LaunchDaemons
- Load and start the service:
sudo launchctl bootstrap system /Library/LaunchDaemons/com.sentinelsecurex.agent.plist
sudo launchctl kickstart -k system/com.sentinelsecurex.agent
- Inspect the loaded service:
make macos-launchd-smoke \
PYTHON="$(pwd)/.venv/bin/python" \
MACOS_LAUNCHD_OUTPUT_DIR=/Library/LaunchDaemons
- Remove the service cleanly later when you need to redeploy or uninstall it:
sudo make macos-launchd-uninstall \
PYTHON="$(pwd)/.venv/bin/python" \
MACOS_LAUNCHD_OUTPUT_DIR=/Library/LaunchDaemons
Add MACOS_LAUNCHD_UNINSTALL_ARGS=--keep-plist if you want to unload the service but leave the rendered plist in place for inspection.
- If you prefer the one-step helper path, render, bootstrap, kickstart, and smoke-test in one command:
sudo ./.venv/bin/python deploy/macos/install_agent_launchd.py \
--install-root "$(pwd)" \
--python-executable "$(pwd)/.venv/bin/python" \
--config-path "$(pwd)/agent/config.json" \
--output-dir /Library/LaunchDaemons \
--bootstrap \
--kickstart \
--smoke-test
The helper runs the same agent config preflight first, writes com.sentinelsecurex.agent.plist, and can optionally bootstrap, kickstart, and smoke-test the service.
- If you prefer the raw helper without
make, render the plist first and then load it yourself:
sudo ./.venv/bin/python deploy/macos/install_agent_launchd.py \
--install-root "$(pwd)" \
--python-executable "$(pwd)/.venv/bin/python" \
--config-path "$(pwd)/agent/config.json"
sudo launchctl bootstrap system /Library/LaunchDaemons/com.sentinelsecurex.agent.plist
sudo launchctl kickstart -k system/com.sentinelsecurex.agent
- Inspect the service later with the smoke test helper:
./.venv/bin/python deploy/macos/smoke_test_agent_launchd.py \
--plist-path /Library/LaunchDaemons/com.sentinelsecurex.agent.plist
Notes
- The launchd job runs
python -u -m agent.agent_securefrom the configured install root. - Standard output and error are sent to the resolved
service_log_pathfromagent/config.json. - TPM quote collection is still environment-specific; the example collector hook remains a pluggable command, not a built-in macOS TPM integration.
- Override
MACOS_LAUNCHD_INSTALL_ROOT,MACOS_LAUNCHD_CONFIG,MACOS_LAUNCHD_PYTHON, orMACOS_LAUNCHD_LABELon themakecommands when your install root differs from the checked-out repository. make macos-launchd-uninstallwrapsdeploy/macos/uninstall_agent_launchd.pyand removes the rendered plist by default afterlaunchctl bootout.
Troubleshooting
- Re-run
./.venv/bin/python -m agent.agent_secureinteractively if the launchd job loads and exits immediately. - Inspect the configured
service_log_pathfirst; launchd sends the child process output there. - If you need to reload the job after editing the plist, boot it out and bootstrap it again:
sudo launchctl bootout system /Library/LaunchDaemons/com.sentinelsecurex.agent.plist
sudo launchctl bootstrap system /Library/LaunchDaemons/com.sentinelsecurex.agent.plist
sudo launchctl kickstart -k system/com.sentinelsecurex.agent