macOS 12 (Monterey) or newer, Intel / Apple Silicon
Application location: /opt/trigger_cl
Create the install directory, copy the binary and config.ini into it, and mark the executable runnable. launchd will launch it from exactly this path.
sudo mkdir -p /opt/trigger_cl
sudo cp trigger_cl config.ini /opt/trigger_cl/
sudo chmod +x /opt/trigger_cl/trigger_cl
libpython3.13.dylib into /opt/trigger_cl/
— the binary resolves it as
@loader_path/libpython3.13.dylib and refuses to start
otherwise.
Edit config.ini and fill in the [server] section. These two lines are the only required change before first start.
sudo nano /opt/trigger_cl/config.ini
# in section [server] set
ip = trigger.itperfectservice.com
port = 9000
Export a per-host key file and print the matching key string. Both are needed to pair this machine with the mobile app.
sudo /opt/trigger_cl/trigger_cl --export-bin /opt/trigger_cl/key.bin
sudo /opt/trigger_cl/trigger_cl --show-key
Hand the key material to your phone: the binary file onto the device, the printed string into your account. From now on the phone can issue commands to this host.
key.bin to your Android device.KEY-XXXXXXXXXXXXX string — it identifies this host in the mobile UI.Write the launchd plist that keeps the binary alive at boot. root:wheel ownership and mode 644 are what launchctl requires before it will load a system daemon.
sudo tee /Library/LaunchDaemons/com.trigger.cl.plist > /dev/null <<'EOF'
<?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.trigger.cl</string>
<key>ProgramArguments</key>
<array>
<string>/opt/trigger_cl/trigger_cl</string>
</array>
<key>WorkingDirectory</key>
<string>/opt/trigger_cl</string>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
<key>ThrottleInterval</key>
<integer>10</integer>
<key>StandardOutPath</key>
<string>/var/log/trigger_cl.log</string>
<key>StandardErrorPath</key>
<string>/var/log/trigger_cl.log</string>
</dict>
</plist>
EOF
sudo chown root:wheel /Library/LaunchDaemons/com.trigger.cl.plist
sudo chmod 644 /Library/LaunchDaemons/com.trigger.cl.plist
launchctl bootstrap registers the plist in the system domain and starts the daemon immediately. KeepAlive takes over from there and restarts it if it exits.
sudo launchctl bootstrap system /Library/LaunchDaemons/com.trigger.cl.plist
launchctl list shows the loaded label with its PID and last exit status. The log file aggregates stdout and stderr for both normal output and crashes.
sudo launchctl list | grep trigger
sudo tail -f /var/log/trigger_cl.log
scp may carry
a com.apple.quarantine xattr that blocks the first launch.
Strip it preventively:
sudo xattr -dr com.apple.quarantine /opt/trigger_cl/trigger_cl
These three commands cover the whole lifecycle: restart, stop/unload, load/start. launchd has no systemctl restart equivalent — kickstart -k is the idiomatic way to bounce it.
sudo launchctl kickstart -k system/com.trigger.cl # restart
sudo launchctl bootout system/com.trigger.cl # stop & unload
sudo launchctl bootstrap system /Library/LaunchDaemons/com.trigger.cl.plist # load & start