macOSIru / Kandji

Iru deployment

Iru/Kandji deploys the same universal AgentKeeper PKG used by Jamf and Intune. The PKG reconciles seven local IDE/CLI surfaces; Cowork and MCP Gateway remain separate deployments.

Universal signed PKG · Custom App

Iru/Kandji deploys the same universal AgentKeeper PKG used by Jamf and Intune. The PKG reconciles seven local IDE/CLI surfaces; Cowork and MCP Gateway remain separate deployments.

Current package versions and checksums are published with the release artifacts. See Download artifacts.

Before deployment, subscribe to installer updates.

1. Create the AgentKeeper profile

Create the macOS Enterprise Installer token first. Select Iru / Kandji and use the same deployment ID in the token and profile. Copy the token when it appears. It is a secret and must not go in the readable profile, logs, screenshots, or support messages.

The token is reusable for Macs in this scoped deployment until it expires or is revoked. Keep it only in the restricted Kandji Library Item script. Rotate it immediately after exposure and revoke it when the deployment ends.

This deployment uses two artifacts: the computer-level com.agentkeeper.runtime profile carries api_url, org_id, mdm_provider, and mdm_deployment_id, and the Custom App's Post-Install Script carries the enrollment token over standard input.

In Library > Add Library Item > Custom Profile, upload the following as AgentKeeper.mobileconfig. Edit only the <string> value that follows each key. The <key> names are fixed; renaming one removes that setting and leaves the profile incomplete.

Key (do not edit)Value in the <string> below it
api_urlYour AgentKeeper app origin
org_idReplace YOUR_AGENTKEEPER_ORG_ID with your AgentKeeper organization ID
mdm_providerKeep kandji
mdm_deployment_idReplace YOUR_AGENTKEEPER_DEPLOYMENT_ID with the deployment ID entered on the token

The deployment ID in the profile must match the token. A placeholder left in place is a real value that does not match, and enrollment is rejected.

<?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>PayloadContent</key>
  <array>
    <dict>
      <key>PayloadType</key>
      <string>com.apple.ManagedClient.preferences</string>
      <key>PayloadIdentifier</key>
      <string>com.agentkeeper.runtime.preferences.kandji</string>
      <key>PayloadUUID</key>
      <string>2D7B26D5-FE8E-4BB4-A172-936F3F9235C5</string>
      <key>PayloadVersion</key>
      <integer>1</integer>
      <key>PayloadEnabled</key>
      <true/>
      <key>PayloadContent</key>
      <dict>
        <key>com.agentkeeper.runtime</key>
        <dict>
          <key>Forced</key>
          <array>
            <dict>
              <key>mcx_preference_settings</key>
              <dict>
                <key>api_url</key>
                <string>https://agentkeeper.dev</string>
                <key>org_id</key>
                <string>YOUR_AGENTKEEPER_ORG_ID</string>
                <key>mdm_provider</key>
                <string>kandji</string>
                <key>mdm_deployment_id</key>
                <string>YOUR_AGENTKEEPER_DEPLOYMENT_ID</string>
              </dict>
            </dict>
          </array>
        </dict>
      </dict>
    </dict>
  </array>
  <key>PayloadDisplayName</key>
  <string>AgentKeeper Runtime</string>
  <key>PayloadIdentifier</key>
  <string>com.agentkeeper.runtime.kandji</string>
  <key>PayloadOrganization</key>
  <string>AgentKeeper</string>
  <key>PayloadScope</key>
  <string>System</string>
  <key>PayloadType</key>
  <string>Configuration</string>
  <key>PayloadUUID</key>
  <string>8F0C81DF-86D2-40A9-AF4C-4A6A3CD21777</string>
  <key>PayloadVersion</key>
  <integer>1</integer>
</dict>
</plist>

Assign the profile before the app. An existing Kandji Assigned User can remain for inventory or device ownership; AgentKeeper neither requires nor reads it. Do not put the Assigned User value, a local account name, or a username variable in the AgentKeeper profile. This is a device-scoped deployment, and machine enrollment can complete at the login window. Confirm the profile is installed before assigning the Custom App:

#!/bin/bash
set -euo pipefail

PROFILE="/Library/Managed Preferences/com.agentkeeper.runtime.plist"
if [[ ! -f "$PROFILE" || -L "$PROFILE" ]]; then
  echo "AgentKeeper profile is missing or invalid" >&2
  exit 1
fi
for key in api_url org_id mdm_provider mdm_deployment_id; do
  sudo /usr/bin/plutil -extract "$key" raw -o - "$PROFILE" 2>/dev/null \
    | /usr/bin/grep -q '[^[:space:]]' || exit 1
done
if [[ "$(sudo /usr/bin/plutil -extract mdm_provider raw -o - "$PROFILE")" != "kandji" ]]; then
  echo "AgentKeeper profile has the wrong MDM provider" >&2
  exit 1
fi
if sudo /usr/bin/plutil -extract enrollment_token raw -o - "$PROFILE" >/dev/null 2>&1; then
  echo "Remove enrollment_token from the AgentKeeper profile" >&2
  exit 1
fi
echo "AgentKeeper device profile ready"

Sharing one Blueprint does not prove delivery order. Never add enrollment_token to the profile.

2. Add the Custom App

  1. Verify the PKG against checksums.txt, its Developer ID Installer signature, stapler, and Gatekeeper.
  2. In the Library, add a Custom App.
  3. Under Installation, choose Installer Package and upload the PKG.
  4. Use Install once per device.
  5. Add this Post-Install Script, replacing the token placeholder:
#!/bin/bash
set -euo pipefail
set +x

AGENTKEEPER_ENROLLMENT_TOKEN='PASTE_ENTERPRISE_INSTALLER_TOKEN'
trap 'unset AGENTKEEPER_ENROLLMENT_TOKEN' EXIT

BIN="/Library/Application Support/AgentKeeper/bin/agentkeeper"
REMEDIATE="/Library/Application Support/AgentKeeper/helpers/agentkeeper-macos-remediate.sh"

if [[ "$(id -u)" -ne 0 || ! -x "$BIN" || ! -x "$REMEDIATE" ]]; then
  echo "AgentKeeper PKG is not ready for enrollment" >&2
  exit 1
fi

printf '%s' "$AGENTKEEPER_ENROLLMENT_TOKEN" | "$BIN" \
  stage-managed-enrollment \
  --token-stdin \
  --program-data "/Library/Application Support/AgentKeeper" \
  --install-dir "/Library/Application Support/AgentKeeper"
unset AGENTKEEPER_ENROLLMENT_TOKEN

"$REMEDIATE"
  1. Assign the Library Item to the target Blueprint or Assignment Map.

Record the AgentKeeper version and SHA-256 in the notes. Do not put the PKG in an Audit or Remediation Script, and do not build a fleet package from the source ZIP. The Custom App installs the package; its Post-Install Script stages the token over standard input and starts enrollment.

If you add Audit and Remediation scripts, keep them thin. Audit the package receipt, the LaunchDaemon, and the installed status helper. Remediate with the package-matched repair helper, after token staging. A remediation script that installs anything has become a second installer, and the two will drift.

3. Verify the deployment

pkgutil --pkg-info dev.agentkeeper.runtime
sudo launchctl print system/com.agentkeeper.runtime
sudo "/Library/Application Support/AgentKeeper/helpers/agentkeeper-macos-status.sh"

Status must show enrollment_state="enrolled", managed_profile_state="ready", hook_matrix.config_json=true, and credential_state="valid". Then restart one selected AI agent and confirm one live Activity event in AgentKeeper.

Run that action in a session on the Mac itself, such as the Claude Code CLI or another selected local agent. The PKG reconciles local IDE and CLI hooks, so this check covers agent activity that executes on the Mac. A Claude session running in the cloud is outside its scope.

The universal PKG does not install Cowork, managed browser extensions, or MCP Gateway. Before reporting missing MCP or connector coverage, confirm Claude Desktop is signed in to the enterprise workspace that received those separate deployments, not a personal account.

The runtime enrolls the machine before login without a per-device user mapping. After a regular non-root user signs in, it resolves the active console account through macOS Directory Services and reconciles that account automatically. It repeats this after a user switch and never scans /Users. Before sign-in, targeting_mode="active_console_user" with no active_console_user and maintenance_state="waiting_for_user" is expected.

An Audit Script may report package, LaunchDaemon, and status-helper state. A Remediation Script may call the installed repair helper, agentkeeper-macos-remediate.sh. Neither should download another installer or rewrite customer hook files.

Troubleshooting

SymptomFix
Custom App is installed, but no Workstation appearsCheck enrollment_state and managed_profile_state. Correct missing or invalid profile metadata. If enrollment_state=managed_token_missing, rerun the Custom App so its Post-Install Script stages the token. The service checks changes each minute; network failures retry with backoff.
Enrolled before login, but no user hooks appearSign in with a regular account, then wait for the one-minute maintenance tick or run the installed remediation helper. Do not add a username to the profile.
Audit keeps reinstalling AgentKeeperRemove installer logic from the script; keep installation in the Custom App.

Kandji reruns a Custom App at the next agent check-in when its Post-Install Script fails. Do not replace the package with a separate installer script.

See Kandji's Custom Apps guide, Validate deployment, and Uninstall and rollback.