Deploying to Windows devices? See Intune (Windows) — Windows uses an MSI with tenant values passed as install-command properties instead of Configuration Profiles.
Migrating from the legacy script-based Detect rollout? Run Legacy macOS Detect Cleanup before rolling out the
.pkg so the old runlayer-scan artifacts don’t conflict with com.runlayer.aiwatch.Overview
A signed, notarizedaiwatch binary installs once per device via .pkg. Tenant config — host, a single organization API key, Mode, legacy Enforcement, the Sessions capability flag, and the AutoUpdate gate — is pushed via an MDM Configuration Profile.
Capabilities
All behaviors ship in the same.pkg and shared PPPC / Login Items profiles. The mode and flags in the tenant-config profile decide what runs — update the configuration and re-push the profile, no .pkg reinstall.
If you need multiple configurations, deploy one tenant-config profile per group. The .pkg, PPPC profile, and Login Items profile are universal and can be reused unchanged across all configurations.
Project and client-config detection is always enabled after AI Watch is installed. Endpoint behavior is selected with Mode; Sessions and optional runtime discovery are controlled independently. One organization API key authenticates AI Watch scans, hooks, session events, and macOS updates; no enrollment keys are required.
- Running-process and container discovery are disabled by default. Set their macOS/Linux keys to
trueor Windows properties to1to opt in. - Monitor is the default mode.
- Sessions is enabled by default. Set
Sessions=false(macOS) orAIWATCH_SESSIONS=0(Windows) to skip the full event/session hook set.
Mode overrides the legacy Enforcement boolean when it contains a valid value. If Mode is absent or invalid, Enforcement=true maps to Enforce and Enforcement=false or absent maps to Monitor. Generated deployment artifacts still include Enforcement for older AI Watch versions: Monitor writes false, while Protect and Enforce write true. Mode-aware clients apply the exact selected mode; an older client receiving Protect falls back to Enforce rather than silently monitoring, but does not have Protect’s exact direct-MCP scanner path.
Hook installation follows the combined capability state: AI Watch installs hook configs when Mode is Protect/Enforce or Sessions is enabled. Detect-only uses Monitor with Sessions disabled; omitting Sessions uses the default enabled state and installs hooks for Monitor telemetry.
On current macOS and Windows packages, admins can change Mode, Sessions, process/container discovery, project depth, and project timeout from Settings → MDM configuration → Manage settings. Devices fetch the complete settings snapshot during the privileged hook reconcile, typically within an hour, without an MDM policy update. The tenant host, organization API key, AutoUpdate, CPU/memory limits, and browser-extension settings remain MDM-owned.
Advanced scan tuning (optional). Detect scans walk each user’s home directory to a bounded depth within a per-scan time budget, and a cooperative in-process governor keeps CPU and memory use within soft caps. The defaults fit most fleets — override them only if scans miss deeply-nested project configs (raise depth), run out of time on very large home directories (raise timeout), or you want to constrain the scan’s footprint on constrained hosts (lower the CPU / memory caps).
All are integers — macOS
<integer> in the tenant-config profile, Windows REG_DWORD via the matching MSI AIWATCH_* property. Omit the keys to keep the defaults. Values outside the range are clamped into it; non-integer or non-positive values are ignored and the default applies. The CPU / memory caps are best-effort: the governor throttles the scan to hold the CPU duty cycle (max(0.05, min(cpu_cores, MaxCpuPercent% × logical_cores)) cores) and aborts the scan if its Python-heap high-water mark exceeds the memory ceiling.
The MDM Configuration setup wizard bakes the selected Monitor / Protect / Enforce mode and Collect session data value into the downloaded profile, plus any Advanced scan settings overrides. When hand-editing a macOS profile, set Mode to monitor, protect, or enforce; pair legacy Enforcement=false with Monitor and Enforcement=true with Protect or Enforce; set other boolean flags explicitly and each scan-tuning key to an <integer>.
Prerequisites
- Devices enrolled via UAMDM (User-Approved MDM) or DEP/ADE. TCC payloads are ignored on manually-enrolled MDM.
- A single organization API key with the Shadow AI Scan role minted in Settings → Organization API keys in the Runlayer dashboard. Record the secret value (
rl_org_...). The same key authenticates scanning, enforcement, sessions, and the narrow update-target/download endpoints. - Your Runlayer tenant host URL (e.g.
https://your-instance.runlayer.com).
Apple Silicon only for now. The current release ships an
arm64 .pkg.Artifacts
The tenant profile enables
AutoUpdate by default. Set it to <false/> before deployment to opt a group out; an absent key is also treated as enabled.
Deploy the three Configuration Profiles before the
.pkg. Profiles must land in /Library/Managed Preferences/ and TCC before the bundled LaunchAgent’s first scan tick — otherwise aiwatch logs host not configured and TCC denies project-config reads until the next MDM sync.Deployment
Intune macOS custom profiles have no admin tenant variables. Intune only substitutes device-scoped tokens (
{{serialnumber}} and similar) inside custom .mobileconfig payloads — there is no way to inject admin-typed tenant values at deploy time. Use the wizard’s pre-filled tenant-config profile, or hand-edit the template and replace REPLACE_WITH_TENANT_HOST and REPLACE_WITH_ORG_API_KEY before upload.1
Prepare the tenant-config profile
Download
com.runlayer.aiwatch.config.mobileconfig pre-filled from the wizard (Settings → MDM configuration), or open the template in a text editor and replace REPLACE_WITH_TENANT_HOST (e.g. https://your-instance.runlayer.com) and REPLACE_WITH_ORG_API_KEY (the rl_org_... secret). When hand-editing Mode, pair legacy Enforcement=false with Monitor and Enforcement=true with Protect or Enforce.2
Upload the three Configuration Profiles
Intune → Devices → macOS → Configuration profiles → Create profile → Templates → Custom. Create one profile per file and upload:
com.runlayer.aiwatch.config.mobileconfig(tenant config, prepared above)com.runlayer.aiwatch.pppc.mobileconfig(as-is — pins Full Disk Access to Developer ID teamAF2M8HC7A2+ identifiercom.runlayer.aiwatch; only the signed binary satisfies the CodeRequirement)com.runlayer.aiwatch.loginitems.mobileconfig(as-is — pre-approves the bundled LaunchAgent on macOS 13+ so users don’t see “Background Item Added” notifications)
3
Upload the .pkg as a macOS app
Intune → Apps → macOS → Add. Choose macOS app (PKG) and upload
aiwatch-<version>-macos-arm64.pkg. The .pkg is self-contained (no install-time arguments) and signed/notarized, so Gatekeeper accepts it.Set the assignment to Required for the same device groups so the app installs automatically at the next check-in./Library/Managed Preferences/com.runlayer.aiwatch.plist. After deployment, change Mode, Sessions, optional process/container discovery, project depth, or project timeout from Settings → MDM configuration → Manage settings; connected devices refresh them during the hourly hook reconcile. To rotate the API key or change AutoUpdate, resource limits, identity overrides, or browser-extension settings, edit (or re-download) com.runlayer.aiwatch.config.mobileconfig and re-deploy the Custom profile — no .pkg reinstall. Keep each Apple PayloadVersion at 1.
Verification
On a test Mac after MDM sync:Common post-deploy issues
Users see 'Background Item Added' after install
Users see 'Background Item Added' after install
Confirm On macOS 13+, expect the Runlayer item to show
com.runlayer.aiwatch.loginitems.mobileconfig is scoped to the same device group as the .pkg, then force an MDM sync and check:enabled allowed visible. macOS 12 and older ignore the Login Items payload; the notification control only exists on macOS 13+.Gatekeeper blocks the package
Gatekeeper blocks the package
Verify you are deploying the official signed and notarized release artifact:Expected: a Developer ID Installer signature for Anysource Inc. and
source=Notarized Developer ID.PPPC applied but TCC still denies access
PPPC applied but TCC still denies access
The PPPC profile pins Full Disk Access to identifier Expected:
com.runlayer.aiwatch and Developer ID team AF2M8HC7A2. Confirm the installed binary matches:Authority=Developer ID Application: Anysource Inc. (AF2M8HC7A2) and Identifier=com.runlayer.aiwatch. Custom or ad-hoc signed builds will not satisfy the PPPC CodeRequirement.Customize scan schedule (optional)
The bundled LaunchAgent ships with a 15-minuteStartInterval. Most tenants don’t need to change it. To override, push the script below via your MDM’s recurring-script mechanism (Jamf Policy, Kandji Custom Script, SimpleMDM Script, etc.):
The next
.pkg upgrade resets StartInterval to the bundled default — the override script must run on a recurring schedule, or be re-applied after each upgrade.Upgrade
By default, the rootcom.runlayer.aiwatch.update LaunchDaemon checks hourly and installs the backend-selected signed + notarized .pkg. An MDM Custom App push remains a manual fallback, but the next hourly tick will restore the backend-selected target. To hold a manually pushed version, set AutoUpdate=false or align the backend rollout policy first.
The package preinstall removes only the previous /usr/local/lib/runlayer/aiwatch/ onedir tree before PackageKit deploys the new payload, so obsolete modules and version metadata cannot survive an upgrade or rollback. The separate Runlayer CLI installation is preserved. Mid-scan upgrades are safe — macOS lets you unlink an open file, and the running aiwatch keeps its mapped pages until the next LaunchAgent tick picks up the new version. Postinstall intentionally leaves an already-loaded updater running so it cannot terminate its own installer transaction; the hourly cadence remains stable.
The three Configuration Profiles (tenant config, PPPC, Login Items) are unchanged across version upgrades.