CLI Reference

This page documents the command-line tools used to manage, monitor, and maintain a Sanctum instance. Every command here has been typed in anger at least once during a production incident. They work.
sanctumctl
Section titled “sanctumctl”sanctumctl is the checked-in operator surface for bootstrapping, rendering, verifying, and doctoring a Sanctum instance. Use it first. Drop to the raw scripts only when you are debugging a specific subsystem.
python3 tools/sanctumctl.py <subcommand>bootstrap
Section titled “bootstrap”Creates the local ~/.sanctum scaffold from the checked-in example configuration.
# Preview onlypython3 tools/sanctumctl.py bootstrap
# Write the scaffoldpython3 tools/sanctumctl.py bootstrap --write| Flag | Description |
|---|---|
--home <path> | Target an alternate home directory, useful for test runs |
--write | Materialize the scaffold instead of printing the plan |
--force | Recopy instance.yaml and .node_id even if they already exist |
render
Section titled “render”Checks or syncs the canonical generated artifacts:
- checked-in workspace manifests
- runtime manifests in
~/.sanctum/services - runtime agent capabilities
- runtime calibration byproducts
# Verify generated surfaces are currentpython3 tools/sanctumctl.py render --checkverify
Section titled “verify”Runs the audited Sanctum proof wall.
python3 tools/sanctumctl.py verify
# Print the exact audit plan without executing itpython3 tools/sanctumctl.py verify --dry-runToday that executes the workspace audit, runtime audit, and system end-to-end suites directly, so the operator entry point and the test wall stay coupled.
doctor
Section titled “doctor”Runs render checks, the feature matrix audit, and optionally the full verification wall.
# Fast local confidence checkpython3 tools/sanctumctl.py doctor --quick
# Full doctor passpython3 tools/sanctumctl.py doctorThe quick pass now includes:
- generated runtime surface checks
- agent markdown drift checks
- a live MBP calibration audit
- the feature matrix audit
- Kitchen Loop source validation
It is the closest thing Sanctum has to a morning roll call. If it fails, something real drifted.
sync-agents
Section titled “sync-agents”Installs the local helper, pushes canonical markdown and helper scripts, then audits the reachable fanout targets.
# Apply the full sync and audit passpython3 tools/sanctumctl.py sync-agents
# Preview the exact steps firstpython3 tools/sanctumctl.py sync-agents --dry-runThe sync writes machine-readable state to:
~/.sanctum/state/agent-markdown-sync.jsonOn the Mac Mini, this command treats the local machine as the authority and fans out to the VM and MBP when they are reachable. On the MBP, the installed helper runs in fanout-target-only mode when ~/.sanctum/instance.yaml is absent, which is a polite way of saying “thank you for receiving the update; please do not start a theological schism.”
stability
Section titled “stability”Manages the one-week soak gate before Phase 2 resumes.
# Start the seven-day stabilization windowpython3 tools/sanctumctl.py stability start
# Check how much time is leftpython3 tools/sanctumctl.py stability status
# Run the final exit gate after the window maturespython3 tools/sanctumctl.py stability checkThe policy is checked into sanctum/stability_window.yaml and currently requires:
sanctumctl.py doctor --quicksanctumctl.py verifypnpm buildinsanctum-docs
Gateway Management
Section titled “Gateway Management”The OpenClaw gateway is the core agent runtime — the brain stem of the whole operation. Always use the openclaw CLI to manage it. Never use raw launchctl commands for the gateway.
Start the Gateway
Section titled “Start the Gateway”openclaw gateway startLoads the gateway LaunchAgent and starts the agent runtime on the configured port (default 1977).
Stop the Gateway
Section titled “Stop the Gateway”openclaw gateway stopGracefully shuts down the gateway, cleans up state files and port locks, then unloads the LaunchAgent.
Restart Pattern
Section titled “Restart Pattern”# Macopenclaw gateway stopopenclaw gateway start
# VMsystemctl --user restart openclaw-gateway.serviceThere is no restart wrapper on purpose. Stop, then start — two commands so you always know which half failed.
Agent Commands
Section titled “Agent Commands”Send a Message to an Agent
Section titled “Send a Message to an Agent”openclaw agent --agent <agent_name> --message "<message>"| Flag | Description |
|---|---|
--agent | Agent identifier: main (Yoda), windu, quigon, cilghal, mundi, or jocasta |
--message | The message to deliver to the agent |
# Send a message to the main agent (Yoda)openclaw agent --agent main --message "Run the evening briefing"
# Send a message to the security agentopenclaw agent --agent windu --message "Generate the weekly security report"Yes, you’re sending text messages to named AI agents running on a Mac Mini. This is your life now.
Cross-Node Agent Messaging
Section titled “Cross-Node Agent Messaging”For sending messages between Mac and VM agents, use the council bridge SSH pattern:
ssh ubuntu@10.0.0.10 \ '/home/ubuntu/.npm-global/bin/openclaw agent --agent main --message "Hello from Jocasta"'ssh neo@10.0.0.1 \ 'PATH=/Users/neo/.local/share/fnm/node-versions/v22.22.0/installation/bin:/opt/homebrew/bin:$PATH \ openclaw agent --agent main --message "Hello from Yoda"'launchctl Patterns
Section titled “launchctl Patterns”macOS uses launchctl to manage LaunchAgents and LaunchDaemons. Sanctum uses the modern bootstrap/bootout subcommands, because Apple deprecated the old load/unload commands and then kept them working anyway, which is the most Apple thing imaginable.
Load a LaunchAgent
Section titled “Load a LaunchAgent”launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/<label>.plist# Example: load the council MLX serverlaunchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.sanctum.mlx.plistUnload a LaunchAgent
Section titled “Unload a LaunchAgent”launchctl bootout gui/$(id -u) ~/Library/LaunchAgents/<label>.plist# Example: unload the council MLX serverlaunchctl bootout gui/$(id -u) ~/Library/LaunchAgents/com.sanctum.mlx.plistCheck Agent Status
Section titled “Check Agent Status”launchctl print gui/$(id -u)/<label># Example: check if the council MLX server is runninglaunchctl print gui/$(id -u)/com.sanctum.mlxLoad a LaunchDaemon
Section titled “Load a LaunchDaemon”LaunchDaemons require sudo and use the system domain:
sudo launchctl bootstrap system /Library/LaunchDaemons/<label>.plistsudo launchctl bootout system /Library/LaunchDaemons/<label>.plistgenerate-plists.sh
Section titled “generate-plists.sh”Renders LaunchAgent plist files from templates using values from instance.yaml and the macOS Keychain. The bridge between your clean YAML config and the XML format that Apple chose specifically to test human patience.
~/.sanctum/generate-plists.sh [--dry-run]| Flag | Description |
|---|---|
--dry-run | Show what would be generated without writing any files |
What It Does
Section titled “What It Does”- Reads templates from
~/.sanctum/templates/launchagents/ - Checks each template’s corresponding service
enabledflag ininstance.yaml - Skips disabled services
- Expands
{{PLACEHOLDER}}tokens with config values - Retrieves secrets from the macOS Keychain using the configured
keychain_account - Writes rendered plists to
~/Library/LaunchAgents/(user agents only — LaunchDaemons are installed by a separate,sudo-gated path)
# Preview changes~/.sanctum/generate-plists.sh --dry-run
# Generate and install~/.sanctum/generate-plists.shrun-all.sh (Test Suite)
Section titled “run-all.sh (Test Suite)”The master test runner. It does not probe ports itself — it runs every other suite and tallies the results.
~/.sanctum/tests/run-all.sh [--quiet]It runs five families of suites, in order:
| Family | What it covers |
|---|---|
| Config | config.sh and config.ts library parity (same instance.yaml, two languages) |
| Infrastructure | LaunchAgent validation, instance.yaml schema, cron jobs, model scout |
| Connections | HA (localhost / LAN / Tailscale / Cloudflare), VM SSH, health ingester, tunnel, keychain |
| Skills | per-skill test scripts under openclaw-skills/ |
| Dashboards | Command Center and Health Center vitest suites |
Bash suites and vitest suites get their pass/fail/skip counts parsed and rolled up into one summary:
╔══════════════════════════════════════════╗║ FINAL SUMMARY ║╚══════════════════════════════════════════╝
Suites: 9 pass, 1 fail (of 10) Tests: 142 pass, 0 fail, 3 skip (of 145)
Failed suites: - Connections
Status: FAILURES DETECTEDThat one failed suite is usually Connections, and it is usually the external drive — HA can’t see the media library because the drive isn’t plugged in. It’s always the external drive.
Watchdog
Section titled “Watchdog”The watchdog is no longer a shell script on a timer — it is a long-running Rust daemon (sanctum-watchdog, built into the unified sanctumd binary) managed by the com.sanctum.watchdog LaunchAgent with KeepAlive, so launchd respawns it if it ever dies. It monitors all enabled services and attempts auto-healing via service-doctor. It’s the night shift security guard of your infrastructure — mostly bored, occasionally essential, and now on the payroll full-time instead of clocking in every ten minutes.
It exposes a status API on 127.0.0.1:2187 and logs to ~/.openclaw/logs/watchdog-rust.log.
Behavior
Section titled “Behavior”- Iterates through all services with
enabled: trueininstance.yaml - Checks each service’s health (port check, process check, or custom probe)
- If a service is unhealthy, invokes
service-doctorto attempt recovery - Logs all results to
~/.openclaw/logs/watchdog-rust.log - Sends a notification via
sanctum_notifyif any service required healing
Check Current Health
Section titled “Check Current Health”The daemon answers over its API rather than being re-run by hand:
curl -s http://127.0.0.1:2187/health | jq .sanctum-backup.sh
Section titled “sanctum-backup.sh”Backs up the Sanctum configuration and critical state via restic — content-addressed, client-side encrypted, deduplicated. It replaced an rsync-to-iCloud scheme that ate 356 GB of SSD by re-copying everything in full every single day. Dedup is not a luxury here; it’s the difference between a backup and a slow disk-full incident.
~/Backups/sanctum-backup.shIt writes to two restic repositories, both encrypted with a passphrase stored in the Keychain (sanctum-backup-key):
| Repo | Role |
|---|---|
/Volumes/T9/sanctum-restic | Primary — fast local disk, full history |
rclone:gdrive-sanctum:sanctum-restic | Cloud — offsite, encrypted before it leaves the Mac |
Each run only ships changed blocks (typical delta: tens of MB), then enforces GFS retention with restic forget --prune (7 daily / 4 weekly / 12 monthly). On full success it beats an off-box dead-man’s-switch on GitHub, so an overdue backup pages even if this Mac, launchd, and Force Flow are all dead. There is no --destination flag — the two repos are the design, not a default to override.
The full operational playbook lives in Backup & Restore.
sanctum-restore.sh
Section titled “sanctum-restore.sh”The undo button for your entire haus infrastructure.
~/Backups/sanctum-restore.sh [YYYY-MM-DD]| Argument | Description |
|---|---|
YYYY-MM-DD | Which dated snapshot to restore. Omit it to take the latest. |
# Restore the most recent snapshot~/Backups/sanctum-restore.sh
# Restore a specific day~/Backups/sanctum-restore.sh 2026-03-19The script previews the source, prompts before it overwrites anything, then restores the configs in place.
Quick Reference
Section titled “Quick Reference”| Command | Purpose |
|---|---|
openclaw gateway start | Start the Mac gateway |
openclaw gateway stop | Stop the Mac gateway |
openclaw agent --agent main --message "..." | Send a message to an agent |
~/.sanctum/generate-plists.sh | Regenerate all LaunchAgent plists |
~/.sanctum/generate-plists.sh --dry-run | Preview plist generation |
~/.sanctum/tests/run-all.sh | Run the full test suite |
curl -s http://127.0.0.1:2187/health | Query the watchdog daemon’s health |
~/Backups/sanctum-backup.sh | Run the restic backup |
~/Backups/sanctum-restore.sh [YYYY-MM-DD] | Restore from a dated snapshot |
launchctl bootstrap gui/$(id -u) <plist> | Load a LaunchAgent |
launchctl bootout gui/$(id -u) <plist> | Unload a LaunchAgent |
launchctl print gui/$(id -u)/<label> | Check agent status |
systemctl --user restart openclaw-gateway.service | Restart VM gateway |