Signing a daemon with the Endpoint Security entitlement

Note: This failure occurs even when running on the same machine that performed the build, signing, and notarization steps.

We are developing a command-line Endpoint Security (ES) client for macOS, distributed to customers as part of an enterprise security suite. We have a valid Apple Developer Team ID (redacted for privacy) and have requested and received the Endpoint Security entitlement for our account.

What We’ve Done

  • Built a universal (x86_64/arm64) CLI ES client using Xcode on macOS Sonoma.
  • Signed with a Developer ID Application certificate (matching our Team ID).
  • Applied the entitlement: com.apple.developer.endpoint-security.client.
  • Notarized the binary via notarytool after receiving Apple’s confirmation that the entitlement was “assigned to our account.”
  • Distributed and unzipped the notarized ZIP (with com.apple.quarantine xattr intact).

What Happens:

When we run the binary (as root, via sudo) on any test Mac—including the original build/notarization machine—the process is killed immediately at launch. Kernel log (log stream --predicate 'eventMessage CONTAINS "AMFI"' --info) shows:

AMFI: code signature validation failed.
AMFI: bailing out because of restricted entitlements.
AMFI: When validating /path/to/fidelisevents:
  Code has restricted entitlements, but the validation of its code signature failed.
Unsatisfied Entitlements:

What We’ve Verified:

  • codesign -dvvv --entitlements 😶 ./fidelisevents shows the correct entitlement, team identifier, and certificate.
  • xattr ./fidelisevents shows both com.apple.provenance and com.apple.quarantine.
  • spctl -a -vv ./fidelisevents returns:
rejected (the code is valid but does not seem to be an app)
origin=Developer ID Application: [REDACTED]

The process is killed even if run on the same Mac where build/sign/notarization occurred.

Other Details The entitlement approval email from Apple simply says it is “assigned to your account” and does not mention “production” or “distribution.”

We have rebuilt, re-signed, and re-notarized after receiving the email.

This occurs on both Apple Silicon and Intel Macs, with recent macOS versions (Sonoma, Ventura).

Question

  • Is it possible that Apple only assigned the development Endpoint Security entitlement, and not the production entitlement required for distributing/running notarized ES clients outside of development?
  • Is there any way to verify the level of entitlement (dev vs. production) associated with our Team ID?
  • What additional steps, if any, are needed to enable the production entitlement so that our binaries can run on customer endpoints without being killed by AMFI?

Any advice, experience, or official documentation about production ES entitlement rollout, approval, or troubleshooting would be greatly appreciated!

Thanks in advance!

Answered by DTS Engineer in 848867022
I have sent you a PM on the DTS support request with a link to download the file

Got it. Thanks!

The issue here is a Developer ID certificate mismatch. Recall from TN3125 Inside Code Signing: Provisioning Profiles that your provisioning profile ties together the who, what, where, when, and how your code can run. Everything in your profile looks fine except the who. It seems your account has two Developer ID Application certificates, and your ‘app’ is signed with one but your profile authorises the other.

Contrast this:

% codesign -d --extract-certificates YourApp.app
…
% openssl x509 -inform der -in codesign0 -text
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number: 6277427490450603824 (0x571de70b17947f30)
…

with this:

% security cms -D -i YourApp.app/Contents/embedded.provisionprofile -o profile.plist
% plutil -extract DeveloperCertificates.0 raw -o - profile.plist | base64 -D > profile.cer
% openssl x509 -inform der -in profile.cer -text 
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            20:5f:6c:c2:6f:97:0d:10:5e:22:f9:d1:54:20:28:71
…

In X.509, different serial numbers means different certificates.


A while back I filed a bug (r. 136954554) about the inability of syspolicy_check to diagnose this issue. By an astonishing coincidence, an engineer working on syspolicy_check pinged me about reproducing that problem just last week. I’m gonna use this thread to illustrate how this is not just theoretical.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"


plutil -p ./com.fidelisevents.DaemonInAppsClothing.plist
{
  "Label" => "com.fidelisevents.DaemonInAppsClothing"
  "ProgramArguments" => [
    0 => "/Library/Application Support/DaemonInAppsClothing/DaemonInAppsClothing.app/Contents/MacOS/DaemonInAppsClothing"
  ]
}

I did add full disk access, I didn't ever get the background item for Login and extensions, but maybe because Fidelis Securit LLC is alread listed. Regardless, I try to load it: 

 sudo launchctl load /Library/LaunchDaemons/com.fidelisevents.DaemonInAppsClothing.plist


Looking at the logs:
log show --predicate 'eventMessage CONTAINS "QQQ"' --last 5m
Filtering the log data using "composedMessage CONTAINS "QQQ""
Skipping info and debug messages, pass --info and/or --debug to include.
Wall Clock adjustment detected - results might be strange while using --end
Timestamp                       Thread     Type        Activity             PID    TTL
--------------------------------------------------------------------------------------------------------------------
Log      - Default:          0, Info:                0, Debug:             0, Error:          0, Fault:          0
Activity - Create:           0, Transition:          0, Actions:           0
darrelburns@LT-FC008087 ~ % log show --predicate 'process == "DaemonInAppsClothing"' --last 5m

Filtering the log data using "process == "DaemonInAppsClothing""
Skipping info and debug messages, pass --info and/or --debug to include.
Wall Clock adjustment detected - results might be strange while using --end
Timestamp                       Thread     Type        Activity             PID    TTL
--------------------------------------------------------------------------------------------------------------------
Log      - Default:          0, Info:                0, Debug:             0, Error:          0, Fault:          0
Activity - Create:           0, Transition:          0, Actions:           0

I try to run it: 

% /Library/Application\ Support/DaemonInAppsClothing/DaemonInAppsClothing.app/Contents/MacOS/DaemonInAppsClothing
2025-07-27 10:57:00.499 DaemonInAppsClothing[25436:973284] entitlements: {
    "com.apple.application-identifier" = "J4WGF5B6KZ.com.fidelissecurity.DaemonInAppsClothing";
    "com.apple.developer.endpoint-security.client" = 1;
    "com.apple.developer.team-identifier" = J4WGF5B6KZ;
    "com.apple.security.get-task-allow" = 1;
}
2025-07-27 10:57:00.499 DaemonInAppsClothing[25436:973284] QQQ err: ERR_NOT_PRIVILEGED




Did you try to start your daemon? See step 13 in my example.

launchd uses an on-demand architecture, so your daemon won’t start unless there’s some demand for it. With an ES client you typically synthesise demand via the KeepAlive property, but I don’t use that in my tests because manual starting is easier to understand.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Yes, please see my latest responses. I did start up the demon as you say and also try running it manually. I get nothing in the logs as a Daemon

Yes, please see my latest responses.

You mean this post? If so, I see a launchctl load in there but no launchctl start. Those are very different things.

Anyway, if you attempted to start the daemon and got no logging, one of two things happened:

  • The daemon failed to start at all.
  • The daemon failed before you got to your log point.

I recommend that you rule out the second by creating a ‘first light’ log point, one that runs before any code that could possibly fail.

The next step then depends on whether you see that log entry or not. If you do, it’s likely that the daemon crashed and you can look for a crash report. And, failing that, you can march your log point forward until things stop working.

If you don’t see the first light log point, I recommend that you look for a crash report. That’s a very common culprit for this. If there’s no crash report, you can deploy launchctl commands to see what’s going on. For example, if I remove my Test791996 app from /Library/Application Support/Test791996 and then try to start my job, I see this:

% sudo launchctl print system/com.example.apple-samplecode.Test791996
system/com.example.apple-samplecode.Test791996 = {
    …
	last exit code = 78: EX_CONFIG
    …
}

And if you want to dig deeper, check out /var/log/com.apple.xpc.launchd/launchd.log.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Thank you! So you are right, I was not "start"ing the daemon, just loading it. I am also able to manually run it, but it required full-disk-access to the Terminal app! We also are creating a FAT binary to run on both Intel and Apple Silicon, so I will test that next.

By the way, the examples are seriously lacking in detail - even your example. It took a long time to reproduce simply because there are missing steps. For example, the creating a new application DOES NOT create an Info.plist. That took a while to figure out. Telling someone to delete a key that shows up in that file doesn't really make any sense, particularly when they are not called the same thing in the Info tab in the project settings. Another thing is that once I added the ES code, it would not build because you also need to link the library for it. There were a few other things as well - such as telling me to delete main.storybook, or other things that actually had no extension showing in the project file list.

Finally, my main confusion left is that the sample applications work fine, but in order to do it, I'm creating an application developer cert and profile (well Xcode is), and that is requiring me to register my machine. I am worried I will get back into the same boat after moving my code into the test app, then trying to create a distributable ES client that will run anywhere.

I think you are telling me that we don't use a Developer ID certificate for that, but instead a distribution certificate? I know it's frustating, but I am still not able to figure this out from the llinked docs and would like a quick summary please.

the creating a new application create an Info.plist.

Right. Xcode changed the way it handles Info.plist files, only creating one in your project if the app using non-standard keys.

Telling someone to delete a key that shows up in that file

I reviewed the latest Signing a daemon with a restricted entitlement and I don’t see where it says that. It does say:

Switch to the Info tab and delete all the app-specific items

but that’s not referring to the Info.plist file directly but rather the Info tab of the target editor.

Having said that, there is one gnarly problem with the Signing a daemon with a restricted entitlement instructions, namely that it dosen’t recommend clearing the Enable Debug Dylib Support build setting. With that setting enabled you end up with a structure like this:

quinn@Mac Debug % find Test791996.app 
Test791996.app
Test791996.app/Contents
…
Test791996.app/Contents/MacOS
Test791996.app/Contents/MacOS/Test791996
Test791996.app/Contents/MacOS/Test791996.debug.dylib
Test791996.app/Contents/MacOS/__preview.dylib
…

That isn’t actually fatal, but it’s also not useful and the doc should be updated to account for it. I’ve filed a bug about that (r. 157201269).

I think you are telling me that we don't use a Developer ID certificate for that, but instead a distribution certificate?

No.

Apple Distribution signing identities are used when you submit to the App Store. The App Store doesn’t support ES clients, so that’s irrelevant to you.

For direct distribution you need to sign with a Developer ID signing identity.

I generally recommend that you use an Apple Development signing identity for day-to-day development. Then, for an ES client, you need to sign (or re-sign) your product with Developer ID during the distribution process.

There’s a lot of info about this in:

If you use Xcode then the standard distribution workflow is to create an Xcode archive (.xcarchive) using Product > Archive and then use the Xcode organiser to export that archive for your distribution channel. Both steps can be automated with xcodebuild.

If you’re not using Xcode then you need to do this manually. The docs should help, but if you get stuck you can run through this process using an Xcode test project, look at what it does, and then replicate that in your real product’s build system.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Signing a daemon with the Endpoint Security entitlement
 
 
Q