Skip to content

service macos

Eric Busboom edited this page Sep 24, 2026 · 1 revision

Service on macOS (launchd)

LaunchDaemon (root, at boot) and LaunchAgent (per user) plists, with the launchctl commands to run them.

Service on macOS (launchd)

mbregistry install-service does not support macOS; it writes systemd files. Use one of the plists below. No drivers or udev-style rules are needed, because a normal user can open the micro:bit's serial and CMSIS-DAP interfaces.

Pick one. Both use the label org.jointheleague.mbregistry, and only one daemon may run per host.

LaunchDaemon (root) LaunchAgent (one user)
Starts at boot, no login when that user logs in
Plist /Library/LaunchDaemons/org.jointheleague.mbregistry.plist ~/Library/LaunchAgents/org.jointheleague.mbregistry.plist
Install mbtools into /opt/mbtools ~/.local/share/mbtools
Database /Library/Application Support/mbregistry/devices.db ~/Library/Application Support/mbregistry/devices.db
Local API socket /var/run/mbregistry/api.sock ~/Library/Application Support/mbregistry/api.sock
Log /Library/Logs/mbregistry.log ~/Library/Logs/mbregistry.log
launchctl target system/org.jointheleague.mbregistry gui/$(id -u)/org.jointheleague.mbregistry
Best for an unattended board host a workstation

Clients (mbregistry list, mbdeploy, ...) try the user's own socket first, then the root one, so either setup works with no flags.

Both plists run <venv>/bin/python -m mbtools.registry.cli run, the same entry point as the Linux unit. KeepAlive → SuccessfulExit=false restarts the daemon after a crash but not after a clean stop, like systemd's Restart=on-failure. ThrottleInterval spaces restarts 10 s apart.

LaunchDaemon (root, starts at boot)

Install into /opt/mbtools first (see Installing mbtools).

sudo tee /Library/LaunchDaemons/org.jointheleague.mbregistry.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>org.jointheleague.mbregistry</string>
    <key>ProgramArguments</key>
    <array>
        <string>/opt/mbtools/bin/python</string>
        <string>-m</string>
        <string>mbtools.registry.cli</string>
        <string>run</string>
    </array>
    <key>EnvironmentVariables</key>
    <dict>
        <key>PYTHONUNBUFFERED</key>
        <string>1</string>
    </dict>
    <key>WorkingDirectory</key>
    <string>/</string>
    <key>RunAtLoad</key>
    <true/>
    <key>KeepAlive</key>
    <dict>
        <key>SuccessfulExit</key>
        <false/>
    </dict>
    <key>ThrottleInterval</key>
    <integer>10</integer>
    <key>StandardOutPath</key>
    <string>/Library/Logs/mbregistry.log</string>
    <key>StandardErrorPath</key>
    <string>/Library/Logs/mbregistry.log</string>
</dict>
</plist>
EOF
sudo chown root:wheel /Library/LaunchDaemons/org.jointheleague.mbregistry.plist
sudo chmod 644        /Library/LaunchDaemons/org.jointheleague.mbregistry.plist
plutil -lint          /Library/LaunchDaemons/org.jointheleague.mbregistry.plist

sudo launchctl enable    system/org.jointheleague.mbregistry
sudo launchctl bootstrap system /Library/LaunchDaemons/org.jointheleague.mbregistry.plist
sudo launchctl kickstart -k system/org.jointheleague.mbregistry

A LaunchDaemon plist must be owned by root:wheel with mode 644. Otherwise bootstrap refuses it.

Operate it:

sudo launchctl print system/org.jointheleague.mbregistry | head -30   # state, pid, last exit code
sudo launchctl kickstart -k system/org.jointheleague.mbregistry      # restart
sudo launchctl bootout system/org.jointheleague.mbregistry           # stop and unload
sudo launchctl disable system/org.jointheleague.mbregistry           # keep it from loading at boot
tail -f /Library/Logs/mbregistry.log

LaunchAgent (one user, starts at login)

Install into ~/.local/share/mbtools first. launchd doesn't expand ~, so this heredoc writes absolute paths from $HOME:

mkdir -p ~/Library/LaunchAgents ~/Library/Logs
cat > ~/Library/LaunchAgents/org.jointheleague.mbregistry.plist <<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>org.jointheleague.mbregistry</string>
    <key>ProgramArguments</key>
    <array>
        <string>$HOME/.local/share/mbtools/bin/python</string>
        <string>-m</string>
        <string>mbtools.registry.cli</string>
        <string>run</string>
    </array>
    <key>EnvironmentVariables</key>
    <dict>
        <key>PYTHONUNBUFFERED</key>
        <string>1</string>
    </dict>
    <key>WorkingDirectory</key>
    <string>$HOME</string>
    <key>RunAtLoad</key>
    <true/>
    <key>KeepAlive</key>
    <dict>
        <key>SuccessfulExit</key>
        <false/>
    </dict>
    <key>ThrottleInterval</key>
    <integer>10</integer>
    <key>StandardOutPath</key>
    <string>$HOME/Library/Logs/mbregistry.log</string>
    <key>StandardErrorPath</key>
    <string>$HOME/Library/Logs/mbregistry.log</string>
</dict>
</plist>
EOF
plutil -lint ~/Library/LaunchAgents/org.jointheleague.mbregistry.plist

launchctl enable    gui/$(id -u)/org.jointheleague.mbregistry
launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/org.jointheleague.mbregistry.plist
launchctl kickstart -k gui/$(id -u)/org.jointheleague.mbregistry

Operate it with the same verbs as the LaunchDaemon: print, kickstart -k, bootout, disable. Use target gui/$(id -u)/org.jointheleague.mbregistry and no sudo. The log is at ~/Library/Logs/mbregistry.log.

If you installed with uv tool install, use that tool's interpreter as the first ProgramArguments entry. head -1 "$(command -v mbregistry)" prints it (the shebang line).

Flags and environment variables

  • Add flags as extra <string> elements after run, e.g. <string>--no-relay-pool</string> or <string>--peer</string><string>host-a</string>.
  • Add env vars (e.g. MBREGISTRY_TOKEN) under EnvironmentVariables.
  • After editing, run bootout, then bootstrap again.

See Command reference for every flag and variable.

Upgrade

sudo /opt/mbtools/bin/pip install --upgrade "git+https://github.com/League-Microbit/mbtools.git"
sudo launchctl kickstart -k system/org.jointheleague.mbregistry
# LaunchAgent: ~/.local/share/mbtools/bin/pip install --upgrade ...; launchctl kickstart -k gui/$(id -u)/org.jointheleague.mbregistry

Uninstall

sudo launchctl bootout system/org.jointheleague.mbregistry
sudo rm /Library/LaunchDaemons/org.jointheleague.mbregistry.plist
sudo rm -rf /opt/mbtools "/Library/Application Support/mbregistry"   # optional

macOS notes

  • Local Network privacy (macOS 15+). Programs started as a user can be blocked from the LAN until allowed in System Settings → Privacy & Security → Local Network. If a LaunchAgent finds no peers but the same command run in Terminal does, check there. A root LaunchDaemon doesn't get this prompt.
  • Application Firewall. If it is enabled, allow the interpreter. See Networking and peering.
  • The logs grow without limit. Truncate them occasionally (sudo truncate -s 0 /Library/Logs/mbregistry.log) or add a newsyslog.d entry.
  • Foreground debugging. Stop the service first (one daemon per host). Then run mbregistry run for the per-user paths, or sudo mbregistry run for the root paths.

Clone this wiki locally