Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.runlayer.com/llms.txt

Use this file to discover all available pages before exploring further.

When to use this

Run this cleanup if your fleet was previously deployed with the script-based Detect path (an MDM Script + the runlayer-scan PPPC profile) and you are migrating to the new macOS .pkg flow (see the per-MDM guides linked from Detect). The two paths use different binary identifiers, paths, and PPPC pins — leaving the old artifacts in place causes:
  • Duplicate scans (per-user com.runlayer.scan LaunchAgent + system-wide com.runlayer.aiwatch LaunchAgent both fire).
  • Dangling TCC grants for /usr/local/bin/runlayer-scan that survive even after the script is unscoped.
  • Confusing log output split across /var/log/runlayer/ai_watch_detect.log (old) and the new macOS unified log under process == "aiwatch".

What gets left behind

The script-based deployment installs and configures the following on each enrolled device:
ArtifactPath / Identifier
Wrapper binary/usr/local/bin/runlayer-scan (ad-hoc signed, identifier com.runlayer.scan)
Per-user LaunchAgent~/Library/LaunchAgents/com.runlayer.scan.plist
Runlayer CLI (uv tool)~/.local/share/uv/tools/runlayer
MDM org API key~/.runlayer/config.yaml — only the org_api_keys.ai_watch_mdm entry
Logs/var/log/runlayer/ai_watch_detect.log (fallback /tmp/runlayer-ai_watch_detect.log), plus /tmp/runlayer-scan.{stdout,stderr}.log
MDM-side scriptOne Script per MDM (Jamf, SimpleMDM, Mosyle, Kandji, …)
MDM-side PPPC profilecom.runlayer.scan.pppc (pins Full Disk Access to /usr/local/bin/runlayer-scan)

Order of operations

  1. Unscope the old MDM artifacts (Script + old PPPC profile) so devices stop receiving the script-based deployment.
  2. Push a one-time per-device cleanup script (below) to remove leftover binaries, LaunchAgent, and logs.
  3. Deploy the new .pkg via the per-MDM guide for your platform — see Detect for the index of MDM-specific guides.
Steps 1 and 2 can be staged: keep step 1 active for at least one MDM check-in cycle to let scheduled scans drain before step 2 runs.

MDM-side cleanup

For each MDM, remove or unscope the two script-based artifacts. Names below match the artifacts created in the original setup guides.
  1. Computers → Policies → open the “Deploy AI Watch Detect” Policy → set Scope to none (or delete the Policy).
  2. Settings → Computer Management → Scripts → delete the “AI Watch Detect” Script.
  3. Computers → Configuration Profiles → delete the uploaded Runlayer PPPC profile (com.runlayer.scan.pppc).

Per-device cleanup script

Push the script below as a one-time Custom Script to every device that previously ran the script-based deployment. It is idempotent — safe to re-run, and a no-op on devices that never had the old deployment.
#!/bin/bash
set -u

CONSOLE_USER=$(stat -f %Su /dev/console 2>/dev/null || echo "")
CONSOLE_UID=$(stat -f %u /dev/console 2>/dev/null || echo "")
USER_HOME=$(dscl . -read "/Users/$CONSOLE_USER" NFSHomeDirectory 2>/dev/null | awk '{print $2}')

if [ -n "$CONSOLE_UID" ] && [ "$CONSOLE_UID" != "0" ]; then
    /bin/launchctl bootout "gui/${CONSOLE_UID}/com.runlayer.scan" 2>/dev/null || true
fi

# Remove only the MDM org API key entry from ~/.runlayer/config.yaml.
# Other entries (e.g. a developer's personal `runlayer login` credentials)
# are preserved. Run before deleting the uv tool install below so the
# `runlayer` CLI is still available.
if [ -n "$CONSOLE_USER" ] && [ -n "$USER_HOME" ]; then
    /bin/launchctl asuser "$CONSOLE_UID" /usr/bin/sudo -u "$CONSOLE_USER" \
        "$USER_HOME/.local/bin/uvx" runlayer org-api-key remove ai_watch_mdm 2>/dev/null || true
fi

if [ -n "$USER_HOME" ]; then
    rm -f "$USER_HOME/Library/LaunchAgents/com.runlayer.scan.plist"
    rm -rf "$USER_HOME/.local/share/uv/tools/runlayer"
fi

rm -f /usr/local/bin/runlayer-scan
rm -rf /var/log/runlayer
rm -f /tmp/runlayer-ai_watch_detect.log
rm -f /tmp/runlayer-scan.stdout.log
rm -f /tmp/runlayer-scan.stderr.log

echo "Script-based AI Watch Detect cleanup complete."
exit 0
The script intentionally leaves the following in place:
  • uv itself (/usr/local/bin/uv, /usr/local/bin/uvx) — general-purpose tooling that may be in use for other workflows. Only the runlayer uv-tool install is removed.
  • ~/.runlayer/config.yaml — only the org_api_keys.ai_watch_mdm entry is removed. Other host entries and any personal runlayer login credentials are kept. The file is not deleted, even if it ends up empty.
  • OS Keychain entries — the script-based deployment never wrote to the keychain. Any runlayer keychain entries belong to an interactive runlayer login session and are left untouched.

Verification

After the cleanup script runs and the new .pkg is deployed, confirm there is exactly one Runlayer scan agent on each device:
# 1. Old wrapper gone
test ! -e /usr/local/bin/runlayer-scan && echo "old wrapper removed"

# 2. Old per-user LaunchAgent gone
launchctl print "gui/$(id -u)/com.runlayer.scan" 2>&1 | grep -q "Could not find service" && \
  echo "old LaunchAgent removed"

# 3. New system LaunchAgent loaded
launchctl print "gui/$(id -u)/com.runlayer.aiwatch" >/dev/null && \
  echo "new LaunchAgent loaded"

# 4. Only the new PPPC pin is active (com.runlayer.aiwatch, signed by Anysource Inc.)
sudo log show --predicate 'subsystem == "com.apple.TCC"' --last 1h | \
  grep -E "com\.runlayer\.(scan|aiwatch)"
A clean fleet shows TCC entries only for com.runlayer.aiwatch. Any remaining com.runlayer.scan entries indicate a device that didn’t receive the cleanup script — re-target it and re-run.

Rollback

If you need to revert to the script-based deployment temporarily, re-scope the original Script and PPPC profile in your MDM. The new .pkg and the old wrapper can coexist for short periods (they use distinct identifiers and paths), but scheduled scans will then run twice per cycle — use only as a short-term safety net during migration.