macOS enrollment plist | AI Agent Security
The macOS pkg takes no install arguments, so a software distribution tool has no terminal in which to run pathfinder-setup.sh. Instead, write an enrollment property list before the pkg runs, and the installer's postinstall completes enrollment itself.
This is the contract the BeyondTrust EPM Package Manager uses. On the EPM path it is written for you and you never see it. This page is for your own distribution tooling, such as Jamf Pro, Intune, Iru (formerly Kandji), or Munki. The other option on that path, an MDM post-install script, is in Mac manual install.
Auto-enrollment removes the manual enroll step only. The configuration profile is still required. A Mac without it enrolls, and its managed coding hooks still install and report sessions, but its two system extensions, Pathfinder Enforcement and Pathfinder Network Filter, stay unapproved and the transparent proxy is not configured. Endpoint Security process and file collection and HTTPS inspection never come up. The two are independent prerequisites, and a managed fleet needs both. See Distribute the configuration profile.
The file contract
Write the plist to:
/var/lib/pathfinder/enrollment.plistThe spelling is double-L, matching enrollment.json (the credentials file the agent writes after a successful enrollment). A single-L enrolment.plist is not read. If postinstall finds no enrollment.plist but finds a similar filename in that directory, it logs a warning naming the file it found, so the mismatch is easy to diagnose.
The parent directory must exist with mode 0700, owned by root:wheel, before anything else is written there:
install -d -o root -g wheel -m 0700 /var/lib/pathfinderThe file itself must be owned by root:wheel, have mode 0600 (no group or other permissions at all), and not be a symbolic link:
install -m 0600 -o root -g wheel <your-generated-file> /var/lib/pathfinder/enrollment.plistA file that fails any of these checks is refused, not repaired. The plist carries a plaintext deployment key, and a file that other users can write to would let an unprivileged local user point a fresh enrollment at a backend they control. The installer never changes the file's permissions or owner. It logs the refusal and leaves the file in place for inspection.
Both XML and binary plist formats are accepted.
Schema
| Key | Type | Required | Notes |
|---|---|---|---|
SchemaVersion | integer | yes | Must be 1, as a real <integer>1</integer>. A <string>1</string> is refused even though it prints the same value. |
BackendUrl | string | yes | Must be an absolute https:// URL. A trailing slash is removed for you. |
DeploymentKey | string | yes | A deployment key from Settings > Deployment. |
EmitAllowEvents | boolean | no | For diagnostics only: high-volume telemetry for allowed actions. Omit it; no production deployment needs it. |
Unknown keys are ignored, so you can add your own bookkeeping fields without breaking anything.
Example
<?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>SchemaVersion</key>
<integer>1</integer>
<key>BackendUrl</key>
<string>https://your-console.example.com</string>
<key>DeploymentKey</key>
<string>PF-EXAMPLE-DEPLOYMENT-KEY</string>
</dict>
</plist>Check the file before you deploy
These commands test your writer without installing anything. plutil -extract exits with an error when the key is missing or the file does not parse, so each line is a basic presence check:
# Well-formed plist
plutil -lint /var/lib/pathfinder/enrollment.plist
# Required keys present and readable
plutil -extract SchemaVersion raw -o - /var/lib/pathfinder/enrollment.plist # expect: 1
plutil -extract BackendUrl raw -o - /var/lib/pathfinder/enrollment.plist # expect: https://...
plutil -extract DeploymentKey raw -o - /var/lib/pathfinder/enrollment.plist
# SchemaVersion must be an integer, not a string (the installer refuses <string>1</string>)
plutil -type SchemaVersion /var/lib/pathfinder/enrollment.plist # expect: integer
# Ownership and permissions: expect "-rw------- root wheel"
stat -f '%Sp %Su %Sg' /var/lib/pathfinder/enrollment.plistThese check presence, type, and permissions, not every rule the installer enforces. For example, the installer also requires BackendUrl to be an absolute https:// URL. A plist that passes here can still be refused, so the install log is the final word.
What happens on install
As its last step, postinstall runs pathfinder-autoenroll.sh:
| Situation | Outcome |
|---|---|
| No file present | Nothing happens. This is the common case for every Mac that does not use this path. |
| Valid file, Mac not enrolled | The file is passed to pathfinder-setup.sh without prompts, then deleted once that succeeds. |
| Valid file, Mac already enrolled | Enrollment is skipped so the existing credentials are not overwritten. The file is still deleted, because its key is no longer needed and leaving it would repeat the same skip on every future install. |
Invalid file, such as bad ownership or permissions, a symbolic link, a malformed plist, missing or wrongly typed keys, or a BackendUrl that is not https | The file is kept and the install continues. Fix the file and run the installer again, or enroll by hand. |
| The setup script fails, such as when the backend is unreachable at install time | The file is kept, so the next installer run tries again. |
The pkg install itself never fails or rolls back because of this file: a bad or missing enrollment plist affects enrollment only.
Important"Succeeds" means configured, not enrolled. A successful hand-off means the agent is configured and its LaunchDaemon has started. The connection to the backend happens afterward, inside the agent, and can still fail on its own, for example because of a bad key or an unreachable backend. Confirm that the Mac appears in Visibility > Endpoints rather than treating a clean install log as proof of enrollment.
CONFIGUREDis the success state and means exactly that: configuration written, agent started, backend connection still to come.
ImportantDelivering the file again does not move an enrolled Mac. The already-enrolled check is unconditional: it does not compare the new file's
BackendUrlwith the backend the Mac is enrolled with. Delivering a file that names a different backend or a new key to an enrolled Mac succeeds, logsSKIPPED: host is already enrolled, deletes the file, and leaves the Mac exactly where it was.This is deliberate. Moving an enrolled endpoint between tenants or backends is not supported, because enrolling again from an installer would create a second agent identity and orphan the first. To move a Mac, unenroll it first.
Logs
Every outcome is logged:
log show --predicate 'eventMessage CONTAINS "pathfinder-autoenroll"' --last 1hEach line starts with one of NEAR-MISS FILENAME, REFUSED, SKIPPED, ENROLLING, CONFIGURED, FAILED, or UNEXPECTED ERROR, so you can search for the outcome.
The deployment key never appears in full. Keys of 12 characters or more are logged as a 7-character prefix followed by ..., and shorter keys are logged as <redacted>, because a prefix would amount to the whole key.
Why https is required
A BackendUrl that uses http:// is refused: the file is kept, the reason is logged, and the install continues without enrolling. The deployment key is sent to the backend on enrollment, and all later agent telemetry travels the same connection. Over plain HTTP, both are readable by anyone on the network path, and nothing proves to the Mac that the backend is genuine, so an impostor could answer the enrollment.
If your backend sits behind a reverse proxy or load balancer that terminates TLS, point BackendUrl at the https:// address that clients use, not the plaintext address behind it.
Managed hooks on this path
Managed enrollment also installs machine-wide coding-agent hooks for all four supported assistants. To reinstall them later, or to install one assistant's hooks again after its local configuration changed:
sudo pathfinder-agent hook install --provider claudeSubstitute codex, cursor, or copilot as needed, or use --all-providers for all four. See Coding hooks: install and verify.
For fleets where MDM must own the hook configuration files, set managed_autoinstall = false under [hooks] in /etc/pathfinder/agent.toml so the agent never writes them, then deploy the generated payloads from Coding hooks MDM payloads (macOS).
--no-managed-hooksis the Windows form.pathfinder-agent enroll --no-managed-hookswrites the same setting, but macOS enrolls throughpathfinder-setup.sh, which has no such option. On macOS, set theagent.tomlkey above, or deliver anagent.tomlthat already carries it.
Updated about 2 hours ago