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"

I changed this thread’s title because the previous title (Note: This failure occurs even when running on the same machine that performed the build, signing, and notarization steps.) won’t help other folks find the thread.

I don’t see any mention of a provision profile in your post. The com.apple.developer.endpoint-security.client entitlement you’re trying to claim is restricted, that is, your claim must be authorised by a profile. Without that, your claim to the entitlement is rejected and the trusted execution system blocks your execution.

See TN3125 Inside Code Signing: Provisioning Profiles for an in-depth explanation of provisioning profiles.

If you were building an ES system extension then Xcode would take care of this for you. However, you’re building a standalone executable, presumable one that you eventually plan to run as a launchd daemon. In that case, you need to package your daemon in an app-like structure. Signing a daemon with a restricted entitlement explains this in depth.

There’s one gotcha here: The system ties your code to your profile via its App ID, located in the com.apple.application-identifier entitlement. It’s also a good idea to include com.apple.developer.team-identifier. So, when you go to sign your program, your entitlements claims include those two and the com.apple.developer.endpoint-security.client entitlement that you actually need.

This app-like structure gives you a place to store your provisioning profile. The next step is to sign your product. Again, if you were using Xcode then this would be easy, but you’re not, so you’ll have to do this manually. For advice on that, see:

Finally, let’s cover some random points. First, you wrote:

Is it possible that Apple only assigned the development Endpoint Security entitlement … ?

That’s definitely possible, although I checked your teams and AFAICT that didn’t happen in this case. However, there’s a way to check this. See my Finding a Capability’s Distribution Restrictions post.

Second, you’re signing your product with your Developer ID signing identity. That’s correct if you plan to distribute it widely. However, I recommend that you restrict your use of Developer ID to distribution. For day-to-day development, use an Apple Development signing identity.

And take care with your Developer ID signing identities. These are precious; you want to avoid either losing or leaking the associated private keys. For specific advice, see The Care and Feeding of Developer ID.


I think this’ll be enough to get you going, but please reply here if you have follow-up questions.

Share and Enjoy

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

After learning that Endpoint Security (ES) clients must be packaged in an “app-like” structure to use a provisioning profile, I followed Apple’s technical note and Quinn’s advice:

Steps Taken

  • App Bundle Creation
  • Created a bundle named FidelisEvents.app, with standard macOS .app layout.
  • Placed my universal Mach-O (x86_64/arm64) binary at FidelisEvents.app/Contents/MacOS/fidelisevents.
  • Added an Info.plist at FidelisEvents.app/Contents/Info.plist with the correct CFBundleIdentifier and metadata (matching the provisioning profile/App ID).

Provisioning Profile

  • Embedded the provisioning profile at FidelisEvents.app/Contents/embedded.provisionprofile.
  • Profile type: Developer ID Application for macOS.
  • App ID and team identifier match the bundle and entitlements.

Profile entitlements explicitly include:

  • com.apple.application-identifier
  • com.apple.developer.team-identifier
  • com.apple.developer.endpoint-security.client

Confirmed by extracting and inspecting the embedded profile.

Entitlements

  • Used an entitlements plist at codesign time, containing:
    • com.apple.application-identifier
    • com.apple.developer.endpoint-security.client (set to true)
    • com.apple.developer.team-identifier

(Also set sandbox and get-task-allow to false, per guidance)

Entitlements and Info.plist values exactly match the provisioning profile.

Signing and Notarization

  • Signed the entire .app bundle with our Developer ID Application certificate, referencing the entitlements file.
  • Submitted the app for notarization (xcrun notarytool submit), and stapled the notarization ticket to the bundle.

Verified codesign and notarization:

  • codesign --verify --deep --strict --verbose=4 reports "valid on disk, satisfies its Designated Requirement."
  • spctl -a -t exec -vv reports "accepted (Notarized Developer ID)".

Profile is visible, properly embedded, and shows required entitlements.

Testing Execution

  • Moved the bundle to /Applications and other trusted locations.
  • Removed com.apple.quarantine xattrs, just in case.
  • Rebooted to clear any signature cache.

Result

  • On launch (with or without sudo), the process is immediately killed with a “Code Signature Invalid” crash.

Console and log output show:

taskgated-helper: ... Unsatisfied entitlements: com.apple.developer.team-identifier, com.apple.developer.endpoint-security.client
... Error Domain=AppleMobileFileIntegrityError Code=-413 "No matching profile found"

All signature and notarization checks are successful; the only runtime issue is a failure to recognize the provisioning profile as authorizing the required restricted entitlements.

Key Details

  • The provisioning profile was freshly created as Developer ID Application for this App ID, with Endpoint Security checked.
  • All values (App ID, team ID, entitlements, Info.plist) match.
  • The app is not modified after signing and notarization.
  • The problem occurs on both Apple Silicon and Intel Macs, on macOS Sonoma and Ventura.

Question

Has anyone successfully deployed a non-system-extension Endpoint Security client using a Developer ID Application and a provisioning profile in an “app-like” bundle? Is there a further step required to authorize these entitlements for production use, or is this a platform limitation for non-system-extensions even with all credentials and notarization in order?

Has anyone successfully deployed a non-system-extension Endpoint Security client … in an “app-like” bundle?

Yes. I suspect that the majority of ES clients are deployed that way [1].

(Also set sandbox and get-task-allow to false, per guidance)

Don’t do that. If an entitlement defaults to false, don’t add it with a false value. Rather, just omit that entitlement entirely [2].

Still, I don’t think that’s the cause of this problem.

If you point syspolicy_check at your ‘app’, what does it report?

Note that I have a cluster of forums posts that explain how to debug issues like this, starting with Resolving Trusted Execution Problems. syspolicy_check is always my first step.

Share and Enjoy

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

[1] Maybe not in product count, but definitely in ‘seat’ count.

[2] I coulda sworn I documented that somewhere [2]. Oh, yeah, in Hardened Runtime. Not super obvious I’ll admit, but at least it’s there (-:

fidelisevents % syspolicy_check distribution ./bin/FidelisEvents.app/Contents/MacOS/fidelisevents

App passed all pre-distribution checks and is ready for distribution.

fidelisevents % syspolicy_check distribution ./bin/FidelisEvents.app

App passed all pre-distribution checks and is ready for distribution.

And yes, I did this after removing the negative flags in the entitlement:

codesign -d --entitlements :- ./fidelisevents.app

Executable=/Users/darrelburns/devel/fidelisevents/bin/FidelisEvents.app/Contents/MacOS/fidelisevents warning: Specifying ':' in the path is deprecated and will not work in a future release <?xml version="1.0" encoding="UTF-8"?><!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "https://www.apple.com/DTDs/PropertyList-1.0.dtd"><plist version="1.0"><dict><key>com.apple.application-identifier</key><string>J4WGF5B6KZ.com.fidelisendpoint.fidelisevents</string><key>com.apple.developer.endpoint-security.client</key><true/><key>com.apple.developer.team-identifier</key><string>J4WGF5B6KZ</string></dict></plist>

Current Status: I’ve followed all documented Apple and forum guidance for deploying a non-system-extension Endpoint Security client as a Developer ID app bundle. I have:

Embedded a Developer ID Application provisioning profile with the Endpoint Security entitlement (confirmed by developer.apple.com, profile XML, and all tools).

Used codesign, spctl, and syspolicy_check distribution (macOS 14+)—all report the app is correctly signed and ready for distribution.

Removed all non-required/negative entitlements.

On first launch, the app is killed instantly, and the logs always say:

taskgated-helper: ... Unsatisfied entitlements: com.apple.developer.team-identifier, com.apple.developer.endpoint-security.client
taskgated-helper: ... Disallowing: com.fidelisendpoint.fidelisevents
amfid: ... not valid: Error Domain=AppleMobileFileIntegrityError Code=-413 "No matching profile found" ... unsatisfiedEntitlements=[
    "com.apple.developer.team-identifier",
    "com.apple.developer.endpoint-security.client"
]

I confirmed the profile is a production Developer ID Application profile, not just for development, and it’s valid through 2030.

Please verify this is correct for my use case

Hmmmm, we’re really narrowing down the scope for this problem. As this point I think I’m gonna need to see your built binary. You have a couple of options for sharing that:

  • If you’re OK with sharing it publicly [1], you can upload it to your file sharing site of choice and then post the link here [2].
  • If not, you should open a DTS code-level support request and pass it along that way. Make sure you reference this thread so that your request comes to me promptly.

Note that, either way, once I get your binary, I’ll reply here with my analysis.

Share and Enjoy

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

[1] If you’re worried about sharing your code, just build an empty version of the program. I don’t need it to actually function. A program with a main function that prints Hello Cruel World! would be fine.

[2] If you have problems posting the link, see tip 14 in Quinn’s Top Ten DevForums Tips.

Oh hey, I just went back to my code-level support request queue and noticed that you’ve already opened one. Cool. I’ll reply there shortly.

Share and Enjoy

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

I have sent you a PM on the DTS support request with a link to download the file

Accepted Answer
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"

I must ask, now that everything matches, it does sign and run, but I want to know how I can run this locally for day-to-day development

I want to know how I can run this locally for day-to-day development

My advice for day-to-day development is that you use an Apple Development signing identity. This will need the same entitlements and a corresponding profile. However, once you’ve assembled those assets, the act of actually signing your code is pretty much what you’ve done already for Developer ID.

The one tricky part here is the provisioning profile:

  • The who — You have to create a different type of profile. Start by choosing Development > macOS App Development rather than Distribution > Developer ID. The creation process then ensures you select Apple Development certificates.
  • The where — Developer ID profiles authorise you to run the code anywhere. That’s the whole reason they exist. macOS App Development profiles are limited to specific Macs. When you create the profile, you’ll be prompted to select a list of Macs and that profile will only authorise you to run your code on those Macs.

Share and Enjoy

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

I am following up because while the daemon is signed correctly now, we still get the ES_NEW_CLIENT_RESULT_ERR_NOT_PERMITTED on SIP-enabled systems:

How can a security company like ours distribute an Endpoint Security (ES) daemon to run on any Mac?

I’m starting to get the sense that it’s not possible to use a Developer ID Application certificate for a widely distributed ES daemon, and that Apple requires these to be implemented as system extensions instead. The official documentation doesn’t make this distinction very clear. Can anyone confirm the correct approach for distributing ES clients to end users?

I’m starting to get the sense that it’s not possible to use a Developer ID Application certificate for a widely distributed ES daemon …

That is incorrect. Creating a sysex is certainly easier, but we absolutely support daemon-based ES clients. And my experience is that the majority of ES products are based on a daemon rather than a sysex.

Note There are some things that are only possible to do in a sysex client. We covered that during a WWDC talk. I can’t remember which one, but they’re both really good talks so I’m gonna drop two links:

With regards ES_NEW_CLIENT_RESULT_ERR_NOT_PERMITTED, it a very specific meaning. If you search the ES headers, you’ll find this:

	/// The caller lacks Transparency, Consent, and Control (TCC) approval from the user.
	ES_NEW_CLIENT_RESULT_ERR_NOT_PERMITTED,

which is kinda vague, but also this:

 * @note The only supported way to check if an application is properly TCC authorized for Full Disk Access
 *       is to call es_new_client and handling ES_NEW_CLIENT_RESULT_ERR_NOT_PERMITTED in a way appropriate
 *       to your application.  Most applications will want to ask the user for TCC authorization when
 *       es_new_client returns ES_NEW_CLIENT_RESULT_ERR_NOT_PERMITTED.
 *       To direct the user to the Full Disk Access section in System Settings, applications can use the following URLs:
 *       `x-apple.systempreferences:com.apple.settings.PrivacySecurity.extension?Privacy_AllFiles` (macOS 13 and later)
 *       `x-apple.systempreferences:com.apple.preference.security?Privacy_AllFiles` (until macOS 12)
 *       Applications are advised to use the new URL in macOS 13 as the old one may stop working in a future release.

which is more helpful. For ES to work, your program must be granted the System Settings > Privacy & Security > Full Disk Access privilege.

There are two ways this can happen:

Share and Enjoy

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

Hi Quinn,

Unfortunately, even after giving full disk access to both fidelisevents.app, it still gets the same error ( /// The caller lacks Transparency, Consent, and Control (TCC) approval from the user. ES_NEW_CLIENT_RESULT_ERR_NOT_PERMITTED,)

I don't know what else to try now.

I’m not sure why you’re having problems with this. Lemme walk you through how I tested this today. You can review my steps to see if there’s anything obviously different. And if there isn’t, you can run through the steps yourself to see if you can repeat my experience. If so, you can then compare your primary daemon to your test daemon to see what’s different.

So, here’s what I did:

  1. Using Xcode 16.4 on macOS 15.5, I created a new project from the macOS > App template.

  2. I set it up as a daemon per the advice in Signing a daemon with a restricted entitlement. Note that the details will differ a bit but the final result will be the same. Specifically, here’s my final structure:

    % find Test791996.app
    Test791996.app
    Test791996.app/Contents
    Test791996.app/Contents/_CodeSignature
    Test791996.app/Contents/_CodeSignature/CodeResources
    Test791996.app/Contents/MacOS
    Test791996.app/Contents/MacOS/Test791996
    Test791996.app/Contents/embedded.provisionprofile
    Test791996.app/Contents/Info.plist
    Test791996.app/Contents/PkgInfo
    

    IMPORTANT To get this structure you’ll have to disable the Enable Debug Dylib Support build setting.

  3. In Signing & Capabilities, I added the Endpoint Security capability (the Signing a daemon with a restricted entitlement instructions use Custom Network Protocol, but that’s just an example).

  4. I checked the entitlements of the built ‘app’:

    % codesign -d --entitlements - Test791996.app 
    [Dict]
        [Key] com.apple.application-identifier
        [Value]
            [String] SKMME9E2Y8.com.example.apple-samplecode.Test791996
        [Key] com.apple.developer.endpoint-security.client
        [Value]
            [Bool] true
        [Key] com.apple.developer.team-identifier
        [Value]
            [String] SKMME9E2Y8
        [Key] com.apple.security.get-task-allow
        [Value]
            [Bool] true
    %
    % security cms -D -i Test791996.app/Contents/embedded.provisionprofile | plutil -p -
    {
      …
      "Entitlements" => {
        "com.apple.application-identifier" => "SKMME9E2Y8.com.example.apple-samplecode.Test791996"
        "com.apple.developer.endpoint-security.client" => 1
        "com.apple.developer.team-identifier" => "SKMME9E2Y8"
        "keychain-access-groups" => [
          0 => "SKMME9E2Y8.*"
        ]
      }
      …
      "ProvisionedDevices" => [
        …
      ]
      …
    }
    
  5. I ran that and confirmed that it runs and actually has the entitlement:

    % ./Test791996.app/Contents/MacOS/Test791996
    2025-07-22 12:07:28.952 Test791996[16586:28089394] entitlements: {
        "com.apple.application-identifier" = "SKMME9E2Y8.com.example.apple-samplecode.Test791996";
        "com.apple.developer.endpoint-security.client" = 1;
        "com.apple.developer.team-identifier" = SKMME9E2Y8;
        "com.apple.security.get-task-allow" = 1;
    }
    
  6. I then added this code to my main.swift and called it from main:

    import EndpointSecurity
    
    typealias es_client_t = OpaquePointer
    
    func endpointSecurityTest() {
        var client: es_client_t? = nil
        let err = es_new_client(&client) { _, _ in
            fatalError()
        }
        let errStr = switch err {
            case ES_NEW_CLIENT_RESULT_SUCCESS: "SUCCESS"
            case ES_NEW_CLIENT_RESULT_ERR_INVALID_ARGUMENT: "ERR_INVALID_ARGUMENT"
            case ES_NEW_CLIENT_RESULT_ERR_INTERNAL: "ERR_INTERNAL"
            case ES_NEW_CLIENT_RESULT_ERR_NOT_ENTITLED: "ERR_NOT_ENTITLED"
            case ES_NEW_CLIENT_RESULT_ERR_NOT_PERMITTED: "ERR_NOT_PERMITTED"
            case ES_NEW_CLIENT_RESULT_ERR_NOT_PRIVILEGED: "ERR_NOT_PRIVILEGED"
            case ES_NEW_CLIENT_RESULT_ERR_TOO_MANY_CLIENTS: "ERR_TOO_MANY_CLIENTS"
            default: "unknown \(err)"
            }
        NSLog("QQQ err: \(errStr)")
        if err == ES_NEW_CLIENT_RESULT_SUCCESS {
            _ = es_delete_client(client)
        }
    }
    
  7. I created /Library/Application Support/Test791996 and copied the Test791996 ‘app’ there.

  8. I deleted all other copies of the Test791996 ‘app’.

  9. I created a trivial launchd property list for my daemon:

    % plutil -p com.example.apple-samplecode.Test791996.plist 
    {
      "Label" => "com.example.apple-samplecode.Test791996"
      "ProgramArguments" => [
        0 => "/Library/Application Support/Test791996/Test791996.app/Contents/MacOS/Test791996"
      ]
    }
    
  10. And then installed it:

    % sudo cp com.example.apple-samplecode.Test791996.plist /Library/LaunchDaemons 
    % sudo launchctl load /Library/LaunchDaemons/com.example.apple-samplecode.Test791996.plist 
    
  11. This triggers a background item alert. I went to System Settings > General > Login Items & Extensions and made sure Test791996 was enabled.

    Note Because this daemon doesn’t declare its responsible code, it shows up in that list under the name from my code signing identity, Quinn Quinn.

  12. In System Settings > Privacy & Security > Full Disk Access, I clicked the add (+) button, navigated to the Test791996 app, and added it.

  13. I started the daemon:

    % sudo launchctl start com.example.apple-samplecode.Test791996                            
    

In the system log I see this:

type: default
time: 12:15:30.575316+0100
process: Test791996
message: QQQ err: SUCCESS

Victory!

ps To clean up I did this:

% sudo launchctl unload /Library/LaunchDaemons/com.example.apple-samplecode.Test791996.plist
% sudo rm /Library/LaunchDaemons/com.example.apple-samplecode.Test791996.plist 
% sudo rm -r "/Library/Application Support/Test791996"

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