macOS 15 (Sequoia): Endpoint Security client runs by hand, but LaunchDaemon fails with TCC “Full Disk Access” denial on unmanaged Macs

  • Platforms: macOS 15.x (Sequoia), Intel-Based
  • App type: Endpoint Security (ES) client, notarized Developer ID app + LaunchDaemon
  • Goal: Boot-time ES client that runs on any Mac (managed or unmanaged)

Summary

Our ES client launches and functions when started manually (terminal), but when loaded as a LaunchDaemon it fails to initialize the ES connection with:

(libEndpointSecurity.dylib) Failed to open service: 0xe00002d8: Caller lacks TCC authorization for Full Disk Access

We can’t find a supported way to grant Full Disk Access (SystemPolicyAllFiles) to a system daemon on unmanaged Macs (no MDM). Local installation of a PPPC (TCC) profile is rejected as “must originate from a user-approved MDM server.”

We’re seeking confirmation: Is MDM now the only supported path for a boot-time ES daemon that requires FDA? If so, what’s Apple’s recommended approach for unmanaged Macs?

Environment & Artifacts

Binary (path placeholder): /Library/Application Support/<VENDOR>/<PRODUCT>/App/<AppName>.app/Contents/MacOS/<es-binary>

  • Universal (x86_64 + arm64)
  • Notarized, hardened runtime; Developer ID Team <TEAM_ID>
  • Entitlements include:
    • com.apple.developer.endpoint-security.client (present)

Daemon plist (simplified; placeholders used):

<?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>com.example.esd</string>
  <key>Program</key>
  <string>/Library/Application Support/<VENDOR>/<PRODUCT>/Platform/<daemon-exec></string>
  <key>WorkingDirectory</key>
  <string>/Library/Application Support/<VENDOR>/<PRODUCT>/Platform</string>
  <key>RunAtLoad</key><true/>
  <key>KeepAlive</key><true/>
</dict></plist>

Designated requirement (abridged & masked):

identifier "<BUNDLE_ID>" and anchor apple generic and certificate 1[...] and
certificate leaf[...] and certificate leaf[subject.OU] = "<TEAM_ID>"

What works

  • Launching the ES client manually (interactive shell) succeeds; ES events flow.
  • Signature, notarization, entitlements, Gatekeeper: all OK.

What fails (daemon)

  • launchctl print system/<label> shows it starts, but Console logs:
    • (libEndpointSecurity.dylib) Failed to open service: 0xe00002d8:Caller lacks TCC authorization for Full Disk Access

  • System TCC DB shows ES consent rows but no allow for TCCServiceSystemPolicyAllFiles for the daemon binary.
  • Installing a PPPC mobileconfig locally (system scope) is blocked as “must originate from a user-approved MDM server.”

Repro (minimal)

  1. Install app bundle + LaunchDaemon plist above (placeholders).
  2. Verify entitlements & notarization:

codesign -dvvv --entitlements :- "<path-to-es-binary>"

spctl --assess --type execute -vv "<path-to-es-binary>"

  1. Start daemon & watch logs:

sudo launchctl bootstrap system "/Library/LaunchDaemons/<your-daemon>.plist"

log stream --style compact --predicate 'process == "<es-binary>" OR subsystem == "com.apple.TCC"' --info

  1. Observe FDA denial message only in daemon context.
  2. Attempt to add FDA via PPPC profile (system scope) → rejected unless installed by user-approved MDM.

Questions for Apple

  1. On macOS 14/15, is Full Disk Access for system daemons strictly MDM-only via PPPC (i.e., not installable locally)?
  2. Under what conditions would libEndpointSecurity report a Full Disk Access denial at client initialization, given ES consent is distinct from FDA?
  3. For unmanaged Macs needing boot-time ES processing, does Apple recommend a split: root LaunchDaemon (ES subscription; no protected file I/O) + per-user LaunchAgent (user-granted FDA) via XPC for on-demand disk access?
  4. Would moving ES connection code into a System Extension change FDA requirements for unmanaged devices, or is FDA still governed by PPPC/MDM?
  5. If behavior changed across releases, can Apple confirm the intended policy so vendors can document MDM requirements vs. unmanaged install paths?

What we’ve tried

  • Verified signature, notarization, hardened runtime, ES entitlement present.
  • Confirmed context difference: manual run OK; daemon fails.
  • Inspected system TCC: ES consent rows present; no FDA allow for daemon.
  • Tried installing system-scoped PPPC locally → blocked as “must originate from a user-approved MDM server.”
  • Considered LaunchAgent-only, but ES needs root; evaluating daemon+agent split to keep ES in root and put FDA-gated work in user space.

What we need

  • A definitive statement on the supported way to grant FDA to a system daemon on macOS 14/15.
  • If MDM PPPC is required, we’ll ship “daemon mode requires MDM” and provide a daemon+agent fallback for unmanaged devices.
  • If a compliant non-MDM path exists for daemon FDA on unmanaged Macs, please share exact steps.

Thanks! Happy to provide additional logs privately if helpful.

Answered by DTS Engineer in 862859022

Our ES client launches and functions when started manually (terminal).

FYI, this is almost certainly because you previously granted Terminal.app FDA, which your client then inherited. Terminal.app does not have any unique/privileged access to the file system.

We can’t find a supported way to grant Full Disk Access (SystemPolicyAllFiles) to a system daemon on unmanaged Macs (no MDM).

The supported way to do this is for the user to grant your daemon FDA through System Setting-> Privacy & Security-> Full Disk Access.

You may have already tried this and found that the interface wouldn't let you select this bare executable:

/Library/Application Support/<VENDOR>/<PRODUCT>/Platform/<daemon-exec>

However, the direct solution to that is simply embed your daemon executable inside an app bundle, as described here. However, this would also be a great time to adopt SMAppService, which would change some of the requirements/behavior here.

Covering some specifics:

  1. On macOS 14/15, is Full Disk Access for system daemons strictly MDM-only via PPPC (i.e., not installable locally)?

No, not at all. As described above, you may need to package your daemon inside an app bundle, but system daemons can be granted FDA.

  1. Under what conditions would libEndpointSecurity report a Full Disk Access denial at client initialization, given ES consent is distinct from FDA?

All? Using the ES API requires FDA. Given that privacy, security, and general disruption the ES API can create, requiring FDA was the simplest way to ensure the user wanted the ES client to "work".

  1. For unmanaged Macs needing boot-time ES processing, does Apple recommend a split: root LaunchDaemon (ES subscription; no protected file I/O) + per-user LaunchAgent (user-granted FDA) via XPC for on-demand disk access?

No, this won't work. As noted above, the ES API requires FDA.

  1. Would moving ES connection code into a System Extension change FDA requirements for unmanaged devices, or is FDA still governed by PPPC/MDM?

Sort of, but only indirectly. Using a system extension means that your embedded inside an app and your system extension can then inherit the FDA grant given to your app. However, the biggest reason to use a system extension is that it allows you to use NSEndpointSecurityEarlyBoot (from "man EndpointSecurity"):

...
The EndpointSecurity (ES) subsystem is a set of functionality to expose
security-relevant system events to applications (ES clients).  ES
clients can either be standalone applications/executables or installed
as system extensions.

If the ES client is a system extension, the following optional keys can
be set in the bundle's Info.plist:

NSEndpointSecurityEarlyBoot
	  Type: Boolean
	
	If set to TRUE, the ES subsystem will hold up all mounts and third
	party executions (anything that is not a platform binary) until all
	early boot ES extensions make their first subscription.

However, I think adopting SMAppService would also allow a daemon to inherit FDA from its parent app.

> 5. If behavior changed across releases, can Apple confirm the intended policy so vendors can document MDM requirements vs. unmanaged install paths?

FDA has "always" worked the way.

A definitive statement on the supported way to grant FDA to a system daemon on macOS 14/15.

Making it explicit, FDA can be granted to a LaunchDaemon through System Settings by:

  1. Constructing the LaunchDaemon as a bundled application, then granting FDA to that "app". This works on all system versions.

  2. Embedding the LaunchDaemon into a parent application and then granting the parent app FDA. This is how FDA is granted to system extensions, but it may also work for other embedding executables, particularly if the embedded executable has also been built as an app bundle (like #1).

__
Kevin Elliott
DTS Engineer, CoreOS/Hardware

Our ES client launches and functions when started manually (terminal).

FYI, this is almost certainly because you previously granted Terminal.app FDA, which your client then inherited. Terminal.app does not have any unique/privileged access to the file system.

We can’t find a supported way to grant Full Disk Access (SystemPolicyAllFiles) to a system daemon on unmanaged Macs (no MDM).

The supported way to do this is for the user to grant your daemon FDA through System Setting-> Privacy & Security-> Full Disk Access.

You may have already tried this and found that the interface wouldn't let you select this bare executable:

/Library/Application Support/<VENDOR>/<PRODUCT>/Platform/<daemon-exec>

However, the direct solution to that is simply embed your daemon executable inside an app bundle, as described here. However, this would also be a great time to adopt SMAppService, which would change some of the requirements/behavior here.

Covering some specifics:

  1. On macOS 14/15, is Full Disk Access for system daemons strictly MDM-only via PPPC (i.e., not installable locally)?

No, not at all. As described above, you may need to package your daemon inside an app bundle, but system daemons can be granted FDA.

  1. Under what conditions would libEndpointSecurity report a Full Disk Access denial at client initialization, given ES consent is distinct from FDA?

All? Using the ES API requires FDA. Given that privacy, security, and general disruption the ES API can create, requiring FDA was the simplest way to ensure the user wanted the ES client to "work".

  1. For unmanaged Macs needing boot-time ES processing, does Apple recommend a split: root LaunchDaemon (ES subscription; no protected file I/O) + per-user LaunchAgent (user-granted FDA) via XPC for on-demand disk access?

No, this won't work. As noted above, the ES API requires FDA.

  1. Would moving ES connection code into a System Extension change FDA requirements for unmanaged devices, or is FDA still governed by PPPC/MDM?

Sort of, but only indirectly. Using a system extension means that your embedded inside an app and your system extension can then inherit the FDA grant given to your app. However, the biggest reason to use a system extension is that it allows you to use NSEndpointSecurityEarlyBoot (from "man EndpointSecurity"):

...
The EndpointSecurity (ES) subsystem is a set of functionality to expose
security-relevant system events to applications (ES clients).  ES
clients can either be standalone applications/executables or installed
as system extensions.

If the ES client is a system extension, the following optional keys can
be set in the bundle's Info.plist:

NSEndpointSecurityEarlyBoot
	  Type: Boolean
	
	If set to TRUE, the ES subsystem will hold up all mounts and third
	party executions (anything that is not a platform binary) until all
	early boot ES extensions make their first subscription.

However, I think adopting SMAppService would also allow a daemon to inherit FDA from its parent app.

> 5. If behavior changed across releases, can Apple confirm the intended policy so vendors can document MDM requirements vs. unmanaged install paths?

FDA has "always" worked the way.

A definitive statement on the supported way to grant FDA to a system daemon on macOS 14/15.

Making it explicit, FDA can be granted to a LaunchDaemon through System Settings by:

  1. Constructing the LaunchDaemon as a bundled application, then granting FDA to that "app". This works on all system versions.

  2. Embedding the LaunchDaemon into a parent application and then granting the parent app FDA. This is how FDA is granted to system extensions, but it may also work for other embedding executables, particularly if the embedded executable has also been built as an app bundle (like #1).

__
Kevin Elliott
DTS Engineer, CoreOS/Hardware

Hi Kevin — thanks for the detailed reply.

Quick confirmations

We’re already shipping the ES daemon as an app-bundled executable (signed, hardened, notarized).

FDA is being granted through System Settings → Privacy & Security → Full Disk Access to the app bundle (per your #1), not to a bare exe.

ES entitlement is present; Gatekeeper/SPCTL and codesign checks are clean.

What we’re actually hitting (repro matrix)

Apple Silicon (M-series) – macOS 15.6: Works. FDA toggles on and persists. ES daemon runs fine at boot.

Intel – macOS ≤ 15.5: Works.

Intel – macOS 15.6 ONLY: Broken.

In Full Disk Access, turning the toggle On either immediately flips back Off, or appears On but flips Off after navigating away and back.

When it “looks” On, the ES daemon still behaves as if FDA is not granted.

This behavior is consistent across multiple Intel machines and fresh user profiles.

Extra notes about launch

The daemon is launched by launchd (system domain) as usual. Our installer (run by another LaunchDaemon’s install.sh) registers it with launchctl ….

The same flow works on Apple Silicon and on Intel with earlier OS versions. Only Intel + 15.6 shows the FDA UI state failing to persist.

What we’ve checked so far

sqlite3 "/Library/Application Support/com.apple.TCC/TCC.db" shows either no allow row for kTCCServiceSystemPolicyAllFiles or a transient state that disappears after the UI flips back.

log show --style compact --predicate 'subsystem == "com.apple.TCC" || process == "tccd" || process == "System Settings"' --last 30m shows no obvious entitlement or code requirement mismatch.

Removing/re-adding the app, resetting with tccutil reset All, rebooting, etc., does not change the Intel-15.6 outcome.

Questions

Is there a known Intel-only regression in 15.6 where FDA toggles for app-bundled daemons fail to persist?

Does launching the daemon immediately after install (triggered by a separate LaunchDaemon running an install.sh) interact with TCC/FDA persistence on Intel 15.6 specifically?

You mentioned SMAppService inheritance—does FDA persistence differ on Intel 15.6 if the daemon is installed/registered via SMAppService vs a LaunchDaemon plist? If that path is recommended, we can prototype it.

If there are any required Info.plist keys for the bundle that affect FDA persistence (e.g., LSBackgroundOnly, specific CFBundlePackageType), please confirm.

Intel – macOS 15.6 ONLY: Broken.

Have you tested this on:

  • Multiple Intel machines.

and/or

  • A "clean" system reinstall, either by erasing the machine or by using a VM.

  • Have you tested this on 15.7.1 (the current release)?

Shifting to here:

Is there a known Intel-only regression in 15.6 where FDA toggles for app-bundled daemons fail to persist?

I'm not aware of any such issue and I'd expect a "general" FDA failure to cause significant problems. Having it be “Intel only" is also quite odd, as everything that manages FDA state is at a high enough level of the system that the CPU architecture shouldn't matter. However, what I this is more likely is that there might be issues on a specific machine which would prevent FDA from "sticking".

Lastly, have you filed a bug on this and, if so, what's the bug number?

Does launching the daemon immediately after install (triggered by a separate LaunchDaemon running an install.sh) interact with TCC/FDA persistence on Intel 15.6 specifically?

Note inherently, though how it's launched might matter. Is this through launchctl or is the daemon directly executing it?

You mentioned SMAppService inheritance—does FDA persistence differ on Intel 15.6 if the daemon is installed/registered via SMAppService vs a LaunchDaemon plist? If that path is recommended, we can prototype it.

Nothing about SMAppService should directly change any of this, however, SMAppService is the "modern" architecture and does involve very different code paths. So, while I can't think of any reason it would "fix" whatever is going on, I also wouldn't be surprised if the issue didn't occur after you switched to SMAppService.

If there are any required Info.plist keys for the bundle that affect FDA persistence (e.g., LSBackgroundOnly, specific CFBundlePackageType), please confirm.

No, that shouldn't matter. I'd include the standard "set" that's included with any app package, but that's simply a case of complying with the system's "expected" architecture behavior, not because I'd expect it to affect FDA.

One "trick" here is you include a ProgramArguments configuration in your launchd plist, you can actually use that to differentiate when you're being launched by launchd (as a daemon) from other launch types (like the user double-clicking). If you're willing to do the work, you can actually provide entirely separate logic flows, one that launches as a daemon and the other which actually presents a UI to the user telling them that this "won't work".

__
Kevin Elliott
DTS Engineer, CoreOS/Hardware

macOS 15 (Sequoia): Endpoint Security client runs by hand, but LaunchDaemon fails with TCC “Full Disk Access” denial on unmanaged Macs
 
 
Q