Apparency

Frequently Asked Questions

Looking for something specific? The User Guide and FAQ are indexed within the app — open Apparency, click on the Help menu (Command-?), and type in the Search field. Results listed under Help Topics link directly to the appropriate User Guide section or FAQ entry here.

Installing Apparency

What versions of macOS does Apparency support?

Apparency supports:

If you're using an older version of macOS, you can download an old version of Apparency as follows:

We make these older versions available for those who need them, but we have not tested them since they were current, and can't make any claims about how well they might work.

Ensuring that Apparency works properly on even a single version of macOS takes a great deal of work, especially for our one-person company. Since we are not willing to claim support for a macOS version that we have not tested, and since Apple releases a new macOS version every year, we must periodically drop support for older versions.

Does Apparency automatically check for updates?

Yes, once every 2 weeks — or less, if you use it less often — Apparency will download a small file from our website to get the current version number. If a newer version is available, you'll see an Update Available button on the right side of the app's title bar: available update indication

Click on Update Available to open the Apparency download page, where you can get the latest version.

If you want to change the frequency with which Apparency checks for updates, or turn off automatic checking entirely, use Apparency > Preferences > Update: update pref pane

Apparency never automatically downloads or installs the actual updated app. You make the decision about when or if to download it, and where and how to install it.

Understanding Apparency

Why should I care about these details of an app?

You shouldn't need to care about these details. And maybe you don't need to care about them — that is clearly Apple's position on the matter.

But if you do care about them, shouldn't it be easier to get the information?

Apparency is primarily for people who already know that they wanted something like this. If it doesn't sound like something you want, we won't try to convince you. After all, we make the same $0 whether you use it or not.

“But isn't [security feature X] just more Apple security theater?”

Maybe. Maybe not. But what is theater without an audience? ¯\_(ツ)_/¯

Seriously, we don't claim to be authorities on this. There is no shortage of other people who have opinions on the matter. Apparency simply provides visibility into the mechanisms that exist. You can decide for yourself how much trust to put in them.

“Won't [some command in Terminal] give me the same information?”

Yes, absolutely. There's nothing that Apparency shows that isn't available through some Terminal command or another.

For example, the information about the code signature, entitlements and such can be discovered using the codesign(1) command. But you have to wrangle codesign's arcane command line options, and run it multiple times different ways to get all of the relevant information. If you want Gatekeeper information, you also need to use the spctl(1) command. And if you want to actually examine the signing certificate, you need to grok codesign's --extract-certificates option, hunt down the (buried) Certificate Assistant app, click through its certificate evaluation flow, and drag the extracted certificates into it.

Apparency is simply aiming to make this all easier and more convenient.

Is Apparency “sandboxed”? What entitlements does it have?

Yes, Apparency is sandboxed. In fact, you can easily see this using Apparency itself.

To quickly open Apparency using Apparency, use Cmd-Option-O. You can see that App Sandbox is enabled.

If you use Component > Show Entitlements, you'll see that the top-level Apparency app has the following entitlements:

Entitlements for the Apparency application
Entitlement KeyValueReason
com.apple.security.files.user-selected.read-only YES This allows Apparency to see the app that you tell it to open — even if it's in a place that is not otherwise accessible to sandboxed apps, such as inside your home folder.
com.apple.security.application-groups $(TeamIdentifierPrefix)com.mothersruin.Apparency.SharedPrefs This allows Apparency to access a group container that it shares with its Quick Look Preview extension, both of which are sandboxed. Currently, this group container is used only for preferences that are shared between the app and preview extension; see here for example.
com.apple.security.temporary-exception.mach-lookup.global-name com.apple.security.syspolicy This allows Apparency to talk to the /usr/libexec/syspolicyd daemon, which is required to successfully check an app's notarization status. (Apparency uses the macOS Security.framework APIs to check for notarization, but that framework's sandbox configuration isn't complete.)
com.apple.security.temporary-exception.files.absolute-path.read-only /usr/libexec/ This allows Apparency to open executables in the system /usr/libexec directory, when selected from the File > Open System Component dialog. Executables started by macOS launchd jobs tend to live in this location, which is not readable to sandboxed apps by default.
com.apple.security.automation.apple-events YES These allow Apparency to send AppleScript (Apple Events) to the Hopper Disassembler, which is required for the Component > Analyze Executable Using Hopper Disassembler command.
com.apple.security.temporary-exception.apple-events com.cryptic-apps.hopper-web-4
com.apple.security.temporary-exception.files.home-relative-path.read-only /Applications/ This allows Apparency to compile AppleScript for the Hopper Disassembler, even when that app is installed in the per-user Applications folder. The above entitlements allow us to send Apple Events, but if our process can't read Hopper's scripting dictionary, our AppleScript won't compile.
/Library/LaunchAgents/ This allows Apparency to read per-user launchd agent definitions, so that it can show these under Component > Show Launch Information > Launchd Job Definitions when the referenced executable is examined.

Within Apparency, there are two other components that are also sandboxed, and thus have explicit entitlements.

The ApparencyPreviewExtension.appex component provides the Quick Look Preview to the Finder, and has the following entitlements:

Entitlements for the ApparencyPreviewExtension.appex component
Entitlement KeyValueReason
com.apple.security.files.user-selected.read-only YES As above, this allows the Quick Look extension to see the app to preview.
com.apple.security.application-groups $(TeamIdentifierPrefix)com.mothersruin.Apparency.SharedPrefs As above, this allows the Quick Look extension to access a group container that it shares with the app.
com.apple.security.temporary-exception.mach-lookup.global-name com.apple.security.syspolicy As above, this allows the Quick Look extension to check an app's notarization status.
com.apple.windowmanager.dragserver These entitlements work around a macOS bug (reported to Apple as FB13681128) that would prevent dragging from the Apparency Quick Look preview, in the Finder's Preview pane. The drag would start, but could not be dropped anywhere, because the sandbox was preventing the drag-initiating process (our Quick Look Preview extension) from communicating with the macOS drag-and-drop infrastructure.

This bug actually impacts any preview using the Quick Look Preview extension mechanism — which was supposed to replace the Quick Look preview generator mechanism that Apple deprecated in macOS 10.15 (Catalina). But the bug is not that noticeable, because almost everyone — including and especially Apple — continues to use the deprecated mechanism. [The deprecated mechanism is disabled in macOS 15 (Sequoia) betas, but only for third-party generators.]

com.apple.ensemble.dragserver
com.apple.security.temporary-exception.mach-lookup.local-name com.apple.coredrag
com.apple.security.temporary-exception.sbpl (allow lsopen) On macOS 11 (Big Sur) or later, this allows the Open With Apparency button to work. That button does the equivalent of opening a custom URL, which is handled by the Apparency app. The sandboxing of Quick Look Preview extensions on Big Sur is such that, by default, this operation — opening a URL via LaunchServices — is not allowed.

Finally, under the MRSFoundation.framework component, you'll find a com.mothersruin.MRSFoundation.UpdateCheckingService.xpc XPC service. This is the component that performs the periodic check for updates, and is entitled as follows:

Entitlements for the com.mothersruin.MRSFoundation.UpdateCheckingService.xpc component
Entitlement KeyValueReason
com.apple.security.network.client YES This allows the XPC service to make an outgoing network connection, in order to fetch this file, which contains the information about the current version of Apparency available for download.

Why the name “Apparency” anyway?

It's a portmanteau of “app” and “transparency.”

Yeah, we know, but naming things is hard. At least we didn't give it a name that makes people think it's malware, like last time.

Using the Component Browser and Info Pane

What do the different Notarization statuses mean?

In the info pane, the Notarization status is described with one of the following:

Notarization StatusDescription
Granted The app or component was submitted to Apple's notarization service and approved for distribution.
Granted — Stapled Ticket The app or component was submitted to Apple's notarization service and approved for distribution, and a notarization ticket was “stapled” to the app. A notarization ticket is cryptographically signed by Apple, and stapling it to the app allows Gatekeeper to verify the notarization, even when your computer isn't connected to the internet. (However, stapling the ticket to the app is not strictly required. Sometimes, the ticket is stapled to the disk image that contains the app: this serves the same purpose but doesn't leave behind a stapled ticket that Apparency will find. If neither is stapled, macOS will query Apple's servers as needed to check for notarization.)

If a ticket is stapled to the app, you can use Component > Show Notarization Ticket to see details, including the notarization time and the Apple certificate that signed the ticket.

None detected The app or component does not appear to be notarized. This might be caused by an inability to connect to Apple's servers. However, a network connection is required only the first time that macOS checks for notarization of a given app, and then only if the notarization is not “stapled” to the downloaded copy. So even in the absence of Internet connectivity, it is likely the app is not actually notarized.
Not applicable The app or component is signed by Apple directly, and therefore is not required nor expected to be notarized. This typically applies to apps installed with macOS, downloaded from the Apple web site, or installed from the Mac App Store.
Not evaluated Apparency didn't try to determine the notarization status of the component. This is usually because the component was found in a de facto code place and is not independently signed; in this case, the notarization status of the containing component applies to this signed resource as well.
Conflicting signatures The app or component supports multiple processor architectures, which have different notarization statuses. See more below.
Verifying signature Apparency is still verifying the signature on the app or component, and can't evaluate the notarization status until this is complete.
Multiple values Multiple apps or components are selected in the browser, and these have different notarization statuses. Select a single component to see its notarization status.

What do the different Gatekeeper statuses mean?

In the info pane, the Gatekeeper status is described with one of the following:

Gatekeeper StatusDescription
Apple System The app or component was signed by Apple, and is always allowed to run. This applies to apps pre-installed with macOS and to some software downloaded from the Apple website — including most developer tools.
Mac App Store The app or component was signed by Apple for distribution via the Mac App Store, and is always allowed to run.
Notarized Developer ID The app or component was signed with an Apple-issued Developer ID certificate, and subsequently approved for distribution by Apple's notarization service. The code will be allowed to run by default, unless disabled via System Settings, under Privacy & Security > Security > Allow applications from (or on macOS 12 or earlier, in System Preferences under Security & Privacy > General). Note that “identified developers” here refers to Developer ID certificates. The system policy might also be changed through the spctl(8) tool.
Developer ID The app or component was signed with an Apple-issued Developer ID certificate, but does not appear to be notarized. However, macOS will allow this code to run, because (a) the developer began distributing apps under this Developer ID prior to the release of macOS 10.14.5 (April 8, 2019), and (b) the app or component has a verified signing time no later than June 1, 2019. [macOS 10.15 (Catalina) and later enforce both of these requirements on unnotarized code, while macOS 10.14 (Mojave) enforces only the first.]
Can't evaluate Apparency can't evaluate the Gatekeeper status because the signature itself is not valid, as shown by the Signed By identity (and documented below). Since the identity of the signing certificate can't be relied upon, it doesn't make sense to evaluate that identity against any Gatekeeper policy.
Not evaluated Apparency didn't evaluate the app or component against the Gatekeeper policies, because macOS doesn't rely on Gatekeeper for this type of component; see more about these known Gatekeeper exceptions below.

Apparency will also skip Gatekeeper evaluation if the component was found in a de facto code place and is not independently signed; in this case, the Gatekeeper status of the containing component applies to this signed resource as well.

Rejected The app or component was signed with a certificate that is not trusted by Gatekeeper (or perhaps not even by macOS; see below). This might be case the if the component was signed with a third-party certificate (which would be uncommon) or perhaps with an Apple-issued certificate that is not of the Developer ID variety (such as an App Store distribution certificate, which is only supposed to be used for submission to Apple, but is sometimes mistakenly used elsewhere).
Unnotarized Developer ID The app or component was signed with an Apple-issued Developer ID certificate, but does not appear to be notarized. Since the exceptions described above for the Developer ID status are not met, macOS will not allow this code to run (unless it is found to be notarized when Gatekeeper runs).
Conflicting signatures The app or component supports multiple processor architectures, which have different Gatekeeper statuses. See more below.
Verifying signature Apparency is still verifying the signature on the app or component, and can't evaluate the Gatekeeper status until this is complete.
Multiple values Multiple apps or components are selected in the browser, and these have different Gatekeeper statuses. Select a single component to see its Gatekeeper status.

For more information about how Apparency determines the Gatekeeper status, see below.

What do the different Signed By identities mean?

In the info pane, the Signed By identity will resemble one of the following:

Signed ByDescription
Software Signing The app or component was signed by Apple. This Apple certificate is used to sign the components of macOS itself (including those delivered through Software Update), as well as some software distributed through the Apple website, including most developer tools.
Apple Mac OS Application Signing The app or component was signed by Apple, prior to making it available on the Mac App Store. This applies to all apps from the store, whether made by Apple or by other developers. (All apps submitted to the Mac App Store are re-signed with this certificate before being made available for sale.)
Johnny Appleseed (123DE678IJ) The app or component was signed with the named certificate. The exact format varies with the type of certificate and the issuer. If it's an Apple-issued Developer ID certificate, there will usually be a parenthesized string of numbers and letters at the end, as in this example: this is the team identifier that Apple assigned to that developer's account. Click Show Code Signature to see details about the signing certificate.
No signature The app or component was not signed at all. This is often the case with older apps, or with developers who aren't particularly excited about paying Apple US$99 per year for the privilege of shipping software for macOS.
Ad-hoc signature The app or component was signed without using a certificate. This scheme gives the component a unique code signing digest (or cdhash), which macOS can use to detect changes. For example, ad-hoc signatures are used for otherwise-unsigned code on Apple Silicon (see below), and for “Web Apps” on macOS 14 (Sonoma). However, it's important to note that ad-hoc signatures don't tell you anything about the developer's identity.
Linker (Ad-hoc) The component was automatically signed by the macOS linker. This behavior was introduced in macOS 11 (Big Sur), to fulfill the Apple Silicon requirement that all executables have some sort of code signature — and thus have a code signing digest (or cdhash). This linker signature is a form of ad-hoc signature (and so doesn't tell you anything about the developer's identity); it is usually applied only to the Apple Silicon architecture (with the Intel architecture being left unsigned). This automatic signing is useful only for standalone Mach-O executables, since it doesn't seal any of the bundle resources that would be covered by a codesign(1)-generated signature (even an ad-hoc one).
Expired certificate The app or component was signed with a certificate that is now expired, and the signature doesn't have a verified signing time that predates the expiration. See more on macOS handling of expired certificates below.
Untrusted certificate The app or component was signed with a certificate that is not trusted by macOS. That is, the certificate was issued by a certificate authority that is not included in the macOS “system anchors.” This is unusual, especially since most modern apps are signed with certificates that were issued by Apple's own certificate authority (which is obviously trusted by macOS). See more on untrusted certificates and code-signing below.
Can't verify signature The app or component appears to have been corrupted or tampered with since being signed. As such, the identity implied by the signing certificate is not reliable. This could be a result of the app being compromised at the point of download. Or it could simply be the result of errors occurring during signing, or in a software update process — the latter is depressingly common, even and especially for built-in macOS apps. (For iOS apps installed on Apple Silicon Macs, read more below.)
Bundle resources modified One or more files inside the component's bundle were modified since the component was signed. These bundle resources live in specific places within the component (usually the Contents/Resources directory) and are protected by the code signature. If any of these files are changed or deleted — or if an unexpected file is added — this will be detected when the signature is verified. For details on which files were modified, use the Bundle Resources inspector.
Missing or inaccessible bundle The component is the main executable of a bundle (e.g. X.app/Contents/MacOS/X), but the enclosing bundle is either missing, or Apparency can't access it. Apparency can read only the file or folder you select in the File > Open dialog, not the folders above it: this is how the App Sandbox works (unless the app is in the shared Applications folder, which all apps can read). If Apparency can't read the bundle resources (such as the Info.plist file), it can't verify the code signature. You will likely get more useful information by opening the enclosing bundle instead.

Another reason you might see this error is if you're using Suspicious Package to Quick Look an executable within a macOS Installer package. If you preview the executable directly, Suspicious Package will export only that file. Using Quick Look on the enclosing bundle will probably yield more complete information.

Skipped verification due to size The app or component is so large that Apparency suspects it would take an unusually long time to verify, and so did not try to do so automatically: see more on this below.
Conflicting signatures The app or component supports multiple processor architectures, which were signed with different certificates. See more below.
Failed to read signature A low-level error occurred in the macOS security subsystem while attempting to read the signature. If this occurs, it probably indicates corruption of the app that fundamentally damaged the signature, and is unlikely to be recoverable.
Removed to DYLD shared cache The component is part of macOS itself, and the signature was removed when the Mach-O binary was moved into the DYLD shared cache — an optimization used in macOS 11 (Big Sur) and later.
Resource in containing bundle The component was found in a de facto code place and is not independently signed, but is likely included in the signed resources of the containing component (such as the top-level app). You can use the Bundle Resources inspector on that containing component to verify that the de facto component's files are signed. As a shortcut, click Show Code Signature and then Show Bundle Resources to open the containing component's inspector.
Verifying signature Apparency is still verifying the signature on the app or component, and can't report the signing identity until this is complete.
Multiple values Multiple apps or components are selected in the browser, and these have different signing identities. Select a single component to see its signing identity.

How does Apparency determine the required macOS version?

macOS has a few different mechanisms for an app to declare the oldest version of macOS that it supports:

For quick reference, Apparency tries to reconcile all of these sources into a single macOS version, and shows this as the Requires version on the info pane. It does this by choosing the oldest of all the Info.plist specifications, or if there are none (such as for a standalone Mach-O executable), it chooses the oldest of the deployment targets for all architectures. Whether this makes sense depends on how you intend to use this information.

If you need more detail, all of the individual bits of information are still available via Apparency:

For iOS apps installed on Apple Silicon Macs, there is instead a minimum iOS version requirement, which is (presumably) checked against the version of the iOS frameworks bundled with that version of macOS. Apparency uses the MinimumOSVersion key from the Info.plist, as well as the Mach-O LC_BUILD_VERSION load commands, to determine what to show for the Requires version.

How does Apparency determine Notarization status, and can it be incorrect?

To determine the Notarization and Gatekeeper status, Apparency queries the macOS code signing API, which in turn relies upon the system cache of notarized code signing digests. (This system cache is stored in the SQLite database at /var/db/SystemPolicyConfiguration/Tickets, and is managed by the /usr/libexec/syspolicyd daemon.)

There are a few different ways that a code signing digest (or cdhash) gets into this macOS-maintained database:

Before version 2.0, Apparency might have reported incorrect Notarization or Gatekeeper status if none of the above cache updates had occurred yet. This could happen with an app that was not stapled (nor delivered on a stapled disk image) and that hadn't been launched yet — resulting in a misleading Unnotarized Developer ID status in Apparency.

Since it is quite reasonable to want accurate notarization information before launching an app, starting in version 2.0, Apparency explicitly requests that macOS look for an updated notarization ticket on Apple's servers before it evaluates the Notarization and Gatekeeper status (and before any other code signature verifications). This should result in more accurate information, regardless of how the app was deployed. It should also reflect any possible revocation, which might've occurred after the app was stapled or installed.

Since macOS appears to fetch notarization tickets fairly often on its own accord, we don't believe that this Apparency behavior should have any meaningful performance impact. Of course, it does trigger some additional network traffic (assuming that the Apple servers are reachable). If you want to disable this behavior, you can turn off Apparency > Preferences > General > Request Developer ID notarization ticket from Apple.
Notarization applies only to Developer ID-signed apps. It is not used or required for apps that are installed through the App Store, nor for apps that are built into macOS.

Why would Apparency tell me an app was “downloaded” when it clearly wasn't?

When you download a file in your web browser, macOS records how and where it was downloaded. If the file is an app, this tells macOS to activate Gatekeeper when the app is first opened. (If the file contains an app, as in the case of a disk image or zip file, the download information gets copied along with the app. So even after you've dragged the app to your Applications folder, macOS still knows that the app was downloaded.) Usually, the result of Gatekeeper is an alert, saying that the app was “downloaded from the Internet,” and asking you if you still want to open it.

In between when you download a file, and when you click OK in that Gatekeeper alert, macOS considers the file to be in quarantine (this terminology was more whimsical before 2020). Once you've allowed Gatekeeper to open the app, it is no longer considered quarantined, but there may still be enough information left behind for Apparency to detect when it was downloaded and by what app (i.e. your web browser). Apparency shows this information in the info pane; you can click on the download date once to see the downloading app name, and a second time to see the kind of download (e.g. from the Internet).

This is all probably what you'd expect “downloaded” to mean. But macOS also applies the quarantine mechanism elsewhere. Most significantly, when an app uses the App Sandbox, all files saved by that app are considered quarantined. For example, say that you receive an email with a zip file attached, and that zip file contains an app. When you ask the Apple Mail app to save that attachment, the zip file gets quarantined: as a result, opening the zip file and then the app inside will activate Gatekeeper. This happens because Apple Mail uses the App Sandbox, and not because of any special knowledge about the source of the files being saved. (Contrast this with Safari or Chrome, which explicitly tell macOS to quarantine a downloaded file, because it came from the Internet.)

Of course, for an app like Apple Mail, almost every file it saves was essentially downloaded from the Internet. Other sandboxed apps might not be downloading anything, but the files they save will still be quarantined — for example, simply save an image in the Preview app and it will be quarantined. This is because macOS does not rely on the (sandboxed) app to decide if content is potentially unsafe, but instead quarantines pretty much everything, and leaves it to Gatekeeper to decide what should be checked and how. (Obviously, Gatekeeper is going to check a quarantined app, but even some kinds of documents can be unsafe and will be subject to Gatekeeper checks.)

So, if an app that you open with Apparency was quarantined — even if it wasn't strictly “downloaded” in the traditional sense — you might still see a Downloaded date in the info pane. Apparency can detect that the quarantine came from a sandboxed app, but it doesn't know whether there was any actual download involved. If you click on the date, Apparency will show the app that saved it, and if you click again, from App Sandbox (save or open).

Sometimes, simply opening a file, without ever changing or saving it, will cause a quarantine to be added.

When you use the standard File > Open dialog from a sandboxed app, macOS gives the app permission to open and/or save that file, depending on the app's entitlements. But, when the Resume feature of macOS saves the open windows of the app, it saves the app's permissions in a way that does not preclude changing the file, and this triggers a quarantine on the file. [More precisely, in a document-based app, AppKit creates a security-scoped bookmark for each open document URL, and if the URL is user-writable, it omits the NSURLBookmarkCreationSecurityScopeAllowOnlyReadAccess option — even if the app has declared its read-only intent via both the com.apple.security.files.user-selected.read-only=YES entitlement and the CFBundleTypeRole=Viewer document type declaration. This creation of a read-write bookmark triggers a quarantine on the file, at least in the absence of the com.apple.security.files.user-selected.executable=YES entitlement.]

This surprising behavior applies to Apparency itself: if you open a standalone executable with Apparency, you might find that Apparency claims that it was downloaded by Apparency itself! This is simply because Apparency opened it, even though it never changes or saves anything. This can be baffling, but it only happens when you open a standalone executable (not an app or other bundle); we don't try to fudge or hide it, because you should be able to trust that Apparency is showing you the actual quarantine information.

What are “code signing options” and how can I view them?

The macOS code signing mechanism has a number of additional options that can be applied to a component. These flags change the way that macOS handles certain aspects of the code signature when the app is used.

For example, there's a Library Validation option that restricts the app to using only those dynamic libraries or frameworks provided by macOS, or those signed by the same developer. And there is a Kill option that tells macOS that it should terminate the app if the code signature should become invalid while open.

To our knowledge, the only Apple documentation of these options are in the codesign(1) man page (see the Option Flags section), and in the Security/CSCommon.h header (see the SecCodeSignatureFlags enumeration). The latter contains some options not mentioned in the former.

Apparency doesn't show these options by default, because they are not terribly common, and (quite frankly) because we don't understand the nuances of them ourselves, and can't really provide any guidance on interpreting them! However, if you want to see them in Apparency, you can enable them using Apparency > Preferences > General > Show code signature options for selected component. With this enabled, Apparency will show the options at the bottom of the info pane. (Note that Apparency won't show the Runtime or Adhoc options here, though, since these are already represented elsewhere in the info pane — the first under Hardening and the second under Signed By.)

What does “In DYLD Shared Cache” mean?

The DYLD shared cache is an optimization that has been in use since Mac OS X 10.6 (Snow Leopard).

Most every app that runs on your Mac uses the standard system libraries and frameworks (such as AppKit), and those standard libraries in turn use hundreds more Apple-private libraries. Finding, loading and preparing every one of those libraries and frameworks can delay app startup significantly, resulting in lots of “bouncing” time in the Dock.

Instead, the work of finding and preparing all of those libraries is done once, and stored as the DYLD shared cache. When an app starts, it only needs to load the shared cache (along with any non-system frameworks it uses, of course). Moreover, since every other process is also loading that cache, the actual loading work (an mmap(2) call) happens only for the first process; the loaded cache is then simply vended to every subsequent process. This not only speeds up app launch time, but also decreases overall system memory usage, because every process is sharing the same copy of the system libraries (via virtual memory) rather than loading another copy.

Through macOS 10.15 (Catalina), the DYLD shared cache was rebuilt whenever new system libraries or frameworks were installed. That is, when Software Update installed new versions of any of the libraries in the cache, it would run the update_dyld_shared_cache(1) tool afterward.

However, in macOS 11 (Big Sur), Apple introduced the signed system volume, wherein the macOS system volume (made read-only in Catalina) becomes an Apple-signed snapshot that can't be modified at all — except by Software Update, which is essentially now installing an updated snapshot of the system.

With this new model, the libraries and frameworks in the DYLD shared cache are fixed precisely by the version of the system snapshot. This allows the work of building the shared cache to be moved from the installer to the building of macOS itself. That is, Apple delivers the DYLD shared cache as part of the signed system snapshot, and there's no need to run update_dyld_shared_cache on your Mac.

Which finally brings us back to what any of this has to do with Apparency! Because Apple is delivering the DYLD shared cache, they've decided that there is no longer any reason to deliver the actual libraries that went into that cache. From a user standpoint, they are indeed redundant. Unfortunately, from a development and debugging standpoint, this is quite painful, because it makes it much more difficult to analyze or otherwise reverse-engineer Apple's frameworks — which has become increasingly necessary as Apple's developer documentation has gotten progressively more useless.

Anyway, in Big Sur or later, if you examine a standard system framework, say AppKit.framework, you will find there is no actual Mach-O binary inside it (e.g. AppKit.framework/AppKit). Apparency can still open the framework, but the info pane will show the Executable as being simply In DYLD shared cache, instead of showing the usual processor architecture information. You can still use Component > Show Executable Information (Cmd-Shift-X), since Apparency retrieves the Mach-O binary information from the shared cache. This at least allows you to see the linked libraries and other parameters.

You will also notice that the code signature information is missing: Apparency shows the Signed By as Removed to DYLD shared cache. That's because the code signature normally lives inside the Mach-O binary (at least for frameworks and libraries), and that signature was removed in the process of building the DYLD shared cache. Essentially, the signature on the framework has been deleted — presumably, Apple is okay with this because the entire system snapshot is cryptographically signed, so the identity of the frameworks inside that snapshot is not in question.

If you look at the framework in the Finder or Terminal, you might notice there is still a _CodeSignature/CodeResources file, but this is a useless artifact of the former code signature. This file usually contains digests of the resource files provided by the framework — which of course still remain — but nothing will actually use this file, because the code directory that would normally reference it (by digest) is no longer present in the Mach-O binary.

In any case, the CodeResources files on the signed system volume are quite small, since they simply exempt all resources from the digest check — likely because this is redundant to the signing of the snapshot. Normally, the code-signing machinery would balk about such “custom omit rules,” but it has a special exemption for the system volume. (On the other hand, if you open a component from a different system volume, you're likely to get errors, because the exemption applies only to the booted system.)

What is a de facto “code place” and how does Apparency handle it?

When you open an app (or other component), Apparency tries to find all of the meaningful components within the app — such as plug-ins that add to system functionality, frameworks that the app uses, or standalone executables that it runs (or provides for you to run from the Terminal). As a general rule, these components either have their own code signatures — and possibly, entitlements — or are types that at least could be signed.

By default, Apparency looks for components only in well-defined places, rather than examining every file and folder inside the app (which could become quite slow for large apps). Apple historically published some guidelines about where such components (or nested code) should be placed. However, these guidelines have not been updated in many years, and were never followed very closely, even by Apple itself.

As such, Apparency looks in both the “standard” code places — such as Contents/PlugIns or Contents/XPCServices — as well as other places that usually contain code — such as Contents/Library/QuickLook or Contents/Library/SystemExtensions. It doesn't look in places that mostly contain non-code items, such as Contents/Resources, because it would likely spend a lot of time examining unrelated files, like nib, image and strings files.

However, for some apps, this approach might omit some components. The Contents/Resources folder, especially, is pretty much a de facto code place, in that developers (including Apple) have put code there for years, without much consequence. (The only known consequence is that it slows down verification of the code signature, because the nested code gets “sealed” twice — once through its own code signature, and once as a resource of the containing component's code signature. This redundancy is generally noticeable only on huge apps, though.)

If you want Apparency to try harder to find components, click the Look in De Facto “Code Places” button at the bottom of the component browser: de facto hint at bottom of component browser

Apparency will then search the Contents/Resources folder, as well as any non-standard folders directly under Contents — most apps don't have these, but some use them quite extensively (Xcode being an example of the latter). The component browser will be updated to show any newly found components; of course, Apparency might not find any de facto components, in which case the component list won't change.

Sometimes, de facto components are independently signed, and even have their own Entitlements and/or Info Property Lists, which is why Apparency makes it possible to find them. This is most likely when the de facto component is a standalone Mach-O executable (or even an app bundle, such as for some helper app).

However, some de facto components don't require independent signatures. For example, a bundle that contains resources but no executable code — such as a standard macOS help bundle — has no reason to be independently signed. Apparency shows these de facto component's with a Signed By status of Resource in containing bundle. In this case, the files that make up the de facto component are usually included in the signed resources of the containing component, and thus are protected from modification by that signature — assuming, of course, that the containing component has a valid signature. You can use the Bundle Resources inspector on that containing component to see the status of the de facto component's files.

De facto components that do contain executable code are also included in the containing component's signed resources, but that doesn't preclude them from being independently signed first. Delivering code within the Resources folder results in signature redundancy, but it is not uncommon and is actually sometimes encouraged by Apple, so Apparency doesn't simply ignore this possibility.

If you know that you want Apparency to search in de facto code places at the time you are opening the app, you can specify this in the File > Open dialog: check Look for components in de facto “code places” before clicking Open. (If you don't see this checkbox, click Show Options.)

Whichever way you enable de facto code places, Apparency will remember this setting on a per-app basis, so if you open the same app again — say, using Finder > Services > Open With Apparency or File > Open Recent — it will find components the same way.

Alternatively, if you want to always search de facto code places for every app you open, you can indicate this via Apparency > Preferences > General > Always look for components in de facto “code places”.

How does Apparency determine the “Kind” of a component? What is a UTI? What is an “extended” UTI?

In macOS, the Kind of a file — such as shown by the Finder — is actually the human-readable (and localized) version of a Uniform Type Identifier (UTI). For example, an application will have a kind of “Application” in the Finder, but behind that is the UTI com.apple.application-bundle. macOS mostly infers UTIs from the file extension, which is the part of the file name that comes after the (last) period character — and which is hidden by default. So a file called abc.txt gets the UTI public.plain-text in macOS.

A UTI typically conforms to another, more general UTI. For example, the “Application” UTI (com.apple.application-bundle) conforms to the “Bundle” UTI (com.apple.bundle), which also includes all non-application bundles as well. In turn, the Bundle UTI conforms to the more general “Directory UTI” (public.directory), and so on. This scheme allows apps to generalize how they work with kind information; instead of checking for every possible kind of bundle, they can simply ask if the item's UTI conforms to com.apple.bundle.

This scheme works fairly well for “document” files — macOS has thousands of UTIs built in, and apps can also define their own UTIs (as you can see in Apparency). And macOS is pretty good about ensuring that document files maintain their file extensions, without which the UTI couldn't be determined.

Unfortunately, for system and app components, things work less well. There are a few problems:

  1. macOS doesn't define UTIs for some commonly-encountered system files and components. For example, a launchd job definition property list is just com.apple.property-list, so isn't distinguished from other property lists; and even kernel extensions didn't get their own UTI until macOS 11 (the same release where they were deprecated).
  2. Even where a UTI is defined for a system file, macOS often won't infer it. Unlike with document files, system and app components are less consistent in their use of file extensions, even when an extension is defined. Moreover, macOS will simply not look inside of a file to determine the UTI. Thus, a Mach-O executable (such as /bin/ls or Sample.app/Contents/MacOS/Sample) won't be assigned the UTI com.apple.mach-o-executable (there isn't even a file extension associated with this UTI). Likewise, a shell script that starts with #! /bin/sh won't be assigned the UTI public.shell-script, unless it also has an explicit sh extension. Instead, both files will get the UTI public.unix-executable, based simply on their executable bits being set.
  3. By convention, some components (such as XPC services or privileged helper applications) use a reverse-DNS naming convention: this can yield a file name with period characters that don't actually denote a file extension. For example, the Mach-O executable at Contents/Library/LaunchServices/com.example.LicenseHelper is seen by macOS to have the file extension LicenseHelper. Since that doesn't map to any UTI, macOS infers only the kind “Document” (and a dynamic UTI).

Hence, to show the Kind in the component browser or the info pane, Apparency first asks macOS for the UTI of that file or bundle, and also for the localized name of that UTI. (If you click the Kind in the info pane, Apparency will show that underlying UTI.) But to improve the accuracy of the kind information, Apparency goes a bit further.

First, we define a few extended UTIs for components that don't have Apple-defined ones, but that might be bundled inside of apps — such as privileged helper applications or background login applications. These UTIs always start with extended, e.g. extended.privileged-helper-tool or extended.login-item, which you'll see if you click on the Kind in the info pane.

Apparency does not declare these extended UTIs to macOS in any way, so they are not understood or used by any other app (except possibly Suspicious Package). Applications are supposed to declare new UTIs only for the file formats that they themselves define, and none of these are our formats, so we opted to not pollute the UTI namespace with these definitions. Internal to our apps, we use the extended prefix to denote these special UTIs; we don't use our own prefix (com.mothersruin) because, again, these are not our formats.

Second, Apparency tries much harder to infer a precise UTI, considering not only the file extension, but also the location of the file and the first few bytes of the file's contents. For example, if a file is called setup (with no file extension) and has its executable bit set, macOS will tell us it is simply a “Unix Executable” (UTI public.unix-executable). But Apparency will read the beginning of the file: if it starts with a #! directive, it will infer something like “Shell Script” (UTI public.shell-script) or “Python script” (UTI public.python-script). On the other hand, if the file has a Mach-O header, Apparency will infer something like “Mach-O executable” (UTI com.apple.mach-o-executable) or “Mach-O dynamic library” (UTI com.apple.mach-o-dylib).

The point of all this is simply to make the Kind shown in Apparency more useful, and also to convince macOS to choose better Quick Look previews from within Apparency. We explain this mostly because you might notice that Apparency reports a different kind than the Finder, or that the UTI behind the kind is a non-standard extended one.

Examining and Understanding Code Signatures

What does it mean when a code signature has a “verified signing time”?

All certificates — including Apple-issued Developer ID certificates — expire after some amount of time. (This is a security precaution, since the underlying technology and the attacks against it are always moving forward.) In the ideal case, developers replace certificates before they expire, and an app you download today will be signed with a still-valid certificate. But in the real world, developers don't always get ahead of expiring certificates — or you might just have an older version of an app and still want to use it after the certificate has expired.

However, an expired certificate does not necessarily make the code signature invalid. When an app is signed on macOS, the current date and time can be incorporated into the signature in a way that is independently verified by Apple. That is, instead of the app just claiming to have been signed at a specific date and time, the actual signing time is securely supplied by Apple, and bound to the code signature cryptographically. (This is known as a “trusted timestamp,” with an Apple server as the “Time Stamping Authority.”) This typically happens automatically when the developer signs the app, but it might have been disabled (see the codesign(1) man page, and the ‑‑timestamp option).

This becomes relevant when Apparency evaluates the trustworthiness of the code signature. If there is a verified signing time, macOS will evaluate the signing certificate as of that date and time. If the certificate is expired now, but was not expired (and was otherwise valid) at the time the app was signed, macOS might decide that the signature is still trusted. (There are exceptions; for example, a certificate might have been explicitly revoked, and might not be considered valid for any signature on any date.)

Even in the absence of a verified signing time, Gatekeeper is quite lax about expired certificates in code signatures. See more on this below.

Unfortunately, the way that this scenario is presented in the standard macOS certificate trust sheet is less than clear: if you examine the certificate details, you'll see that the certificate has expired, but macOS still says “this certificate is valid.” Of course, the certificate itself is no longer valid, but since it was valid at the verified signing time, the signature is nevertheless valid. However, macOS doesn't show the verified signing time anywhere, nor even note its existence.

Apparency attempts to improve matters by explicitly noting when a verified signing time is found in the code signature. When you use Component > Show Code Signature, if the code signature includes a verified signing time, it will be shown in the sheet: signature and trust dialog

If you click Show Certificate, you may still see the certificate-is-expired-but-valid confusion described above — since Apparency uses the standard macOS certificate trust sheet, and can't change this bit — but at least this gives some context to the confusion.

Why does Apparency complain that a certificate is expired when Gatekeeper doesn't seem to mind it?

If an app was signed with a certificate that has since expired — and the signature lacks a verified signing time — Apparency will show the Signed By identity as Expired certificate. However, macOS generally does not consider an expired certificate to invalidate a code signature, such as when evaluating Gatekeeper policies.

We can only speculate why this is the case. Presumably, too much would break if expired certificates invalidated a code signature, especially if it prevented otherwise-valid apps from being used. The existence of verified signing times ought to solve this problem, but in fact, these are not used consistently enough. Most third-party apps have them, but most Apple apps — and apps distributed via the App Store — do not.

In any case, Apparency shows expired certificates as such, so you can decide whether or not you care.

Why does Apparency say that a certificate is untrusted when codesign(1) doesn't complain?

If an app was signed with a certificate that is not trusted by macOS, Apparency will show the Signed By identity as Untrusted certificate. However, if you run codesign(1) with the ‑‑verify option, it might not report any error. That's because the macOS code-signing mechanism does not, by default, impose any trust requirements on the certificate used to sign an app. Features built on top of code-signing, such as Gatekeeper, can and do impose such trust requirements, but when you use codesign(1), you are interacting with the bare code-signing mechanism only.

To understand why this is the case, we have to go back in time. The macOS code-signing mechanism dates to Mac OS X 10.5 (Leopard), way back in 2007. In those early days, signed third-party apps were rare. Apple was code-signing components of macOS itself, but even those signatures were frequently broken by the Software Update process, with little ill effect. Indeed, before the introduction of Gatekeeper in Mac OS X 10.7 (Lion), Apple wasn't issuing code-signing certificates to third-party macOS developers. (Certificates issued to App Store developers, even with the advent of the Mac App Store, were never intended to be used for signing apps to be installed on macOS, but only for signing apps being submitted to App Review. App Store apps are always re-signed by Apple prior to being offered on the store.)

In those ancient times, a third-party developer could have gotten a signing certificate from some non-Apple certificate authority — any of the ones generally trusted via the macOS “system anchors” — but that would've been a lot of trouble and expense for no real benefit. In fact, the only benefit was that a code-signed app could be updated without losing access to any keychain items it created: as long as each new version of the app was signed with the same certificate as the original version of the app, it would maintain access. But even this could be achieved with a self-signed certificate, which can be created freely by a developer, and doesn't involve any third-party validation. It didn't matter that the self-signed certificate wasn't trusted by macOS; all that mattered is that the same self-signed certificate was used to sign subsequent app versions, indicating that they came from the same developer as the original.

Anyway, given that code signing was optional and uncommon for so many years after introduction, it is not so surprising that the basic code-signing mechanism doesn't enforce much actual policy — that's what features such as Gatekeeper added. As such, when you run codesign ‑‑verify, you are asking it to verify only two things:

Unless you've examined the designated requirement — such as by using codesign with the ‑‑display and ‑‑requirements options — you can't be certain that satisfies its Designated Requirement implies a trusted certificate. You can, however, ask codesign to check an additional, explicit requirement that includes trustworthiness. For example:

$ /usr/bin/codesign --verify --verbose=4 --test-requirement="=anchor trusted" /Applications/Some.app
/Applications/Some.app: valid on disk
/Applications/Some.app: satisfies its Designated Requirement
/Applications/Some.app: explicit requirement satisfied

Here, the explicit requirement says that the signing certificate must ultimately be issued by a certificate authority that is trusted by macOS. Often, this will be Apple's own certificate authority, but this requirement is more general than that. (Use anchor apple generic instead to require an Apple-issued certificate.)

Again, this is only interesting to understand what the codesign tool is actually checking — and what it isn't. It doesn't mean that macOS isn't checking the trust of code signing certificates in specific circumstances. For example, Gatekeeper rules are themselves code-signing requirements, just like the above. You can see these rules via something like:

/usr/sbin/spctl --list

In fact, these are the same requirements that Apparency uses to determine the Gatekeeper status.

Why does Apparency say that a signature is valid when codesign(1) reports the “resource envelope is obsolete (custom omit rules)”?

As described above, part of verifying a code signature is verifying that the resources inside the app (such as strings or nib files) haven't been changed since the app was signed. For a standard macOS app, there are a handful of files that are allowed to change without being flagged as an error (for example, .DS_Store or PkgInfo files). In olden times, apps could add files to this list (i.e. “custom omit rules”), but since OS X 10.9 (Mavericks), such custom exclusions from the code signature have been prohibited. This is because macOS apps are not supposed to modify themselves — they should store customizations in standard places, usually inside a Library folder.

However, Apple grants itself some exceptions to this prohibition on custom exclusions, in at least 3 cases that we're aware of:

In each of these cases, if you run codesign(1), you'll get something like:

$ /usr/bin/codesign --verify /Applications/Overcast.app
/Applications/Overcast.app: resource envelope is obsolete (custom omit rules)

Prior to version 1.4, Apparency would show Can't verify signature for these components, for the same reason. This was accurate, but obscured the more useful information about the signature. It also preempted any verification of the non-omitted resources, because the code signing machinery balks at the custom omit rules before attempting to verify what is not omitted.

To present more useful information, Apparency now disables the “custom omit rule” error on components that are signed by Apple or by the App Store (but not those signed by any other certificate, not even one issued by Apple). This avoids presenting not-very-interesting signature errors, and allows any non-omitted resources to be verified. (Admittedly, the latter probably only applies to iOS apps, since the other two cases omit all resources.)

Why does Apparency sometimes say that it skipped verification of the signature?

In order to verify the signature of an app or component, every file that makes up that component has to be read in its entirety, creating a cryptographic digest (or hash) of the complete component. This digest is then compared against the one that was produced when the component was signed by the developer. Any changes that are made to any file of the component are thus detected, because it will result in a different digest and invalidate the signature. (This is a bit of an oversimplification, but conceptually accurate anyway.)

In most cases, creating the digest takes a few seconds at most, and the signature is verified quickly. However, for extremely large apps, verification can start to take real time. For example, Xcode — weighing in at over 10 GB in size — can take several minutes to verify. (Anyone who has ever installed Xcode has surely noticed the long delay upon first launch!) You might want Apparency to do the verification anyway, if the signature is the information that you're interested in inspecting, but otherwise, it is just an annoying use of your CPU.

Apparency tries to guess when an app or component is so large that the verification will take an unusually long time. Where it believes this to be the case, Apparency will not automatically verify that component. Instead, it shows the Signed By identity as Skipped verification due to size. When this occurs:

  1. If you don't care about the signature of the component, you can ignore this entirely.
  2. If you would like to proceed with verification of the signature for just this single component, select it, click the Signature toolbar button Signature toolbar button (or use Cmd-Shift-S), and then choose Verify Signature.
  3. If you would to proceed with verification of the signatures for all components in this app, choose File > Verify All Signatures.
  4. If you would like Apparency to always verify all signatures, even where it believes that this make take a long time, go to Apparency > Preferences > General and uncheck Skip automatic signature verification for large code.

What does “Conflicting signatures” mean? Why does Apparency show a processor architecture pop-up?

The macOS code signing mechanism stores most of the information about the signature inside the executable of the app or component. This information includes the entitlements and the code signing identity — along with a web of cryptographic digests (or hashes) that uniquely identifies the compiled code, the Info.plist and the other bundle resources (such as images or strings files).

If an app supports multiple processor architectures (e.g. Intel — 64-bit and Apple Silicon — 64-bit), there will actually be a separate code signature for each architecture. This is simply how the code signing mechanism is designed. (The app can even be “thinned” to remove architectures, without invalidating the signatures on the remaining architectures.) Typically, each of these distinct signatures is functionally identical: that is, signed by the same certificate, with the same entitlements, code signing identity, and so on. (They can't be exactly identical, however, because the compiled code is always going to differ.)

To ensure that a multiple-architecture component has functionally identical code signatures, Apparency will evaluate the signature for every architecture. If everything is indeed consistent, Apparency won't show anything special: the Signed By, Gatekeeper, Entitlements and other attributes will simply show the common values extracted from every architecture.

However, in the (unlikely!) event that Apparency does find a difference between signatures, it will instead show Conflicting signatures in the applicable field of the info pane. When you use Component > Show Code Signature or Component > Show Entitlements to get more details, you will be given a choice of architecture to examine. This allows you to see how the architectures differ. Obviously, the architecture that your Mac will use is the most interesting, but a difference in signatures is unusual enough that you may want to examine everything more closely.

What do the statuses in the Bundle Resources inspector mean?

When viewing bundle resources in the Bundle Resources inspector, you'll see the following icons in the Status column.

StatusDescription
resource is valid The resource file was verified to be unchanged since the component was signed.
resource was added When the component was signed, there was no file at this location, so something or someone must've added it after the fact. Added files might be benign, but they are treated as errors because they might trigger code in the app or in macOS that would not otherwise be used.
resource was changed The resource file was changed after the component was signed. macOS detects this by comparing the SHA-256 digest of the file against the digest computed at signing time. Any change, no matter how small or (possibly) benign, will trigger this mismatch. (Because cryptographic digests are “one way” functions, there is no way to infer what specifically changed; you'd have to get the valid version of the file and compare the two directly.)
resource was deleted The resource file was deleted after the component was signed.
resource was deleted but is optional The resource file was deleted after the component was signed, but was specifically marked as being optional at signing time. This is usually the case with localized resources that, in theory, don't change the operation of the app — at least not unless you've configured macOS to use that language. This is not considered an error by macOS, probably as a legacy of the days when some users would delete unwanted localizations to save on disk space.

Note that folders show the status of everything they contain, so they might show multiple status icons. For example, a folder that contains one changed file amongst many unchanged ones will show both the resource is valid verified and resource was changed changed icons.

Examining and Understanding Launch Information

What is a “code signing identity”? What is a “code signing requirement”?

A code signing identity allows macOS to recognize a specific app from an Apple-registered developer, without regard to a particular version of that app. This is used by macOS to grant access to sensitive resources — such as keychain items or App Sandbox “containers” — in a way that won't break every time the app is updated.

In its typical form, a code signing identity — also called a “designated requirement” (DR) — consists of two pieces:

Any app that matches both of these attributes is assumed to be the “same” app, and is allowed to access the same resources. The developer can update the app with abandon, as long as the code signing identity doesn't change.

A code signing identity is an example of a code signing requirement, which is a more general mechanism for capturing requirements that a code signature must meet. In fact, Apple defines an entire domain-specific language for Code Signing Requirements. As an introduction to the language, here's an annotated version of a typical code signing identity (the one for Apparency itself):

anchor apple generic /* signing certificate was issued by Apple, trusted by Apple Root CA */
   and identifier "com.mothersruin.Apparency" /* and app has this bundle identifier */
   and ( certificate leaf[field.1.2.840.113635.100.6.1.9] /* and signing certificate is the Mac App Store one */
         or ( certificate 1[field.1.2.840.113635.100.6.2.6] /* ... OR was issued by the Developer ID intermediate CA */
              and certificate leaf[field.1.2.840.113635.100.6.1.13] /* ... for Developer ID Application use */
              and certificate leaf[subject.OU] = "936EB786NH" ) ) /* ... for the developer with this team ID */

The team ID is an identifier that Apple assigns to each registered developer; every Developer ID certificate that Apple issues to that developer will have the same team ID, and so will meet the latter part of this requirement. (You'll see the same team ID in parentheses in the Signed By identity in Apparency's info pane.) This scheme allows the developer to renew their Developer ID certificate between app updates, without breaking any existing requirements.

The alternative check for the Mac App Store certificate allows the same resources to be shared between a Mac App Store version of the app, and the same app distributed outside the store; this is part of the default code signing identity, and doesn't necessarily mean that both versions actually exist.

A code signing requirement will usually specify an identifier, which looks much like the bundle identifier. This is actually the code signing identifier, and 99% of the time, it is indeed the same as the bundle identifier. But depending on how the code was signed, it is possible for these to differ. The code signing identifier is stored separately within the code signature; the bundle identifier, if any, is stored in the Info Property List. Differences in the code signing identifier are most common with bare (unbundled) executables, since these often lack bundle identifiers (or embedded Info.plist files at all). But by default, codesign(1) sets the code signing identifier from the bundle identifier.

Within Apparency, you'll find the code signing identity in the Launch Information inspector. Here, Apparency adds syntax highlighting and comments to improve the readability of code signing requirements. For example, as shown above, a requirement often checks for the presence of a specific extension in a certificate, using a multi-part, numeric object identifier (OID) value, such as 1.2.840.113635.100.6.1.13. Where possible, Apparency adds a comment that gives the name for that OID, such as Apple Developer ID Application Signing.

How is the applicable Gatekeeper policy determined?

As described above, there are a number of possible Gatekeeper statuses, most of which refer to a policy name such as Notarized Developer ID.

When determining the Gatekeeper status, Apparency assumes that the default Gatekeeper policies are in effect. Gatekeeper policies are expressed using the code signing requirements language described above. For example, the Gatekeeper policy for Mac App Store looks like this:

anchor apple generic and certificate leaf[field.1.2.840.113635.100.6.1.9] exists /* Apple Mac App Signing (Release) */

macOS has a default set of such policy rules, which you can see using this command:

/usr/sbin/spctl --list --type execute

When “assessing” code for execution, Gatekeeper evaluates each of these requirements in turn, and the first one that matches (if any) is the applicable policy. Most of the policies are allow types, meaning that matched code is allowed to run, but there are also deny types (such as Unnotarized Developer ID), which mean that matched code is prohibited from running.

If none of the policy requirements are matched, Apparency will simply show Rejected.

If you want to see the underlying code signing requirement that produced the Gatekeeper policy name, use the Launch Information inspector.

Apparency assumes the default set of Gatekeeper policies, rather than trying to query its policy database. If you try the spctl(8) command above, you'll notice it requires authorization, even though it doesn't change anything. This — combined with the fact that customizing Gatekeeper policies is uncommon — is why we don't go beyond the defaults.
Apparency doesn't report Gatekeeper status where it isn't actually used by macOS. The status is reported as Not evaluated for the following types of components:
  • Traditional kernel extensions, which have special requirements that go beyond those for normal apps. For one, the Apple-issued Developer ID certificate must be explicitly approved for signing of kernel extensions, so an additional level of Apple review is involved. There's also an entire kernel extension user consent mode on spctl(8).
  • iOS apps on Apple Silicon Macs, which can only be installed through the App Store. Although there is a Gatekeeper policy applicable to Mac App Store apps, there has never been one for iOS App Store apps, for whatever reason. (Apparency applies this exception only if the iOS app is validly signed by the App Store, however.)
  • XCFrameworks, which are used by Xcode but are neither directly run nor dynamically linkable. The platform-specific frameworks inside are, of course, dynamically linkable, and Apparency will evaluate Gatekeeper status for those, unless they are ...
  • Components for the watchOS, tvOS or visionOS platforms, since these do not run on macOS. This exception is mainly for an XCFramework, since you're unlikely to encounter these elsewhere. (iOS components are not generally excluded, since they might run on Apple Silicon Macs, but see the exception above.)

How are launchd jobs associated with a component?

In macOS, system daemons and agents are launched and managed by launchd(8), using launchd job definition property lists installed into /System/Library/LaunchDaemons or /System/Library/LaunchAgents. The format of these plists is documented at launchd.plist(5).

Historically, in order to activate a third-party daemon or agent, the job definition plist had to be installed into /Library/LaunchDaemons or /Library/LaunchAgents, often via a macOS Installer package. (For agents — but not daemons — there is also the Library/LaunchAgents subfolder of your home folder. This subfolder can be written without special permissions — at least by non-sandboxed apps — but apps need to keep it up-to-date for every user. Nevertheless, this subfolder is used and abused far and wide.)

Over the years, macOS has acquired other methods of activating third-party daemons and agents.

Way back in Mac OS X 10.6 (Snow Leopard), the SMJobBless() API added a way to for apps to install privileged helper applications. This is essentially a launchd daemon, but delivered in the app bundle at Contents/Library/LaunchServices. After administrator authentication, the executable is copied to /Library/PrivilegedHelperTools. The executable must have an embedded launchd job definition template, which macOS uses to create a plist in /Library/LaunchDaemons.

Fast forward to macOS 13 (Ventura), where the SMAppService API introduces another way for apps to bundle and install launchd daemons or agents. In this case, the launchd job definition plists are delivered in the app bundle at Contents/Library/LaunchDaemons or Contents/Library/LaunchAgents, and point to a daemon or agent executable elsewhere in the bundle (using a new BundleProgram key). This new scheme appears to be “native” to launchd, in that the job definition plist and executable are both used directly from their place in the app bundle, and one can see the bundle information in launchctl(1) print output.

Apparency collects information about launchd job definition plists in all of these locations, and associates each component with any launchd jobs that reference that component's executable (via the Program, ProgramArguments or BundleProgram keys). In other words, these are the launchd jobs that could cause the component to be launched automatically by macOS. To see this information for the selected component, use the Launch Information inspector.

Where apps use SMJobBless() or SMAppService, Apparency gives the daemon or agent executable components a special Kind: “Privileged helper application” for the older scheme, or “Background app service daemon” or “Background app service agent” for the new one. These will also have a non-zero number of Launchd Jobs in the Provides section of the info pane.

What are launch constraints?

A launch constraint is a set of requirements, applied to a specific executable component, which must be met before macOS will allow that component to start.

Launch constraints appeared in an initial form in macOS 13 (Ventura), and were publicly described and documented beginning in macOS 14 (Sonoma).

There are two classes of launch constraint supported by macOS:

The classification of launch constraints as explicit or implicit is our terminology, so it may not match what you read elsewhere. Apple documents only the explicit form.

Every launch constraint has an associated type. This type determines which process — relative to the component being launched — must meet the requirements of the constraint:

It is always the launching of the component that defines the constraint that is gated, even though some types of constraints are requirements that other processes must meet.

In addition to the above, there is a fifth type: like a Self constraint, the Launchd Job constraint applies to the component itself, but instead of being stored with the code signature, this constraint is stored in the launchd job definition plist that references the component (under the SpawnConstraint key). This is meant to ensure that a launchd job, once installed and allowed to run, won't be tricked into running an entirely unrelated executable.

To see the launch constraints in Apparency, use the Launch Information inspector. Apparency shows whatever types of constraints are defined for that component (including Launchd Job ones). If you open a component of macOS itself, Apparency will use the constraint category (from the trust cache) to find any implicit constraint, and show this in the same way.

Although Apparency gets the constraint category from the macOS trust cache, the mapping from that category to the actual constraint isn't readily available from the running version of macOS. Instead, Apparency carries its own copy of these constraint definitions, extracted from a recent version of macOS. Depending on which version of macOS you're running — and when Apparency was last updated — it's possible for these constraint details to differ. To see the entire set of constraints that Apparency is using — and which version of macOS they were extracted from — use Window > Implicit Launch Constraints.
As of macOS 14 (Sonoma), only explicit launch constraints are documented by Apple. For our understanding of implicit launch constraints — and especially how to extract the constraint categories and read the trust cache — we are particularly indebted to Csaba Fitzl, who did the hard work of reverse-engineering this stuff and documenting it.

Examining and Understanding Entitlements

What do the different Provisioned Entitlement statuses mean?

When viewing the entitlements with Component > Show Entitlements, you may see an additional entitlements view provisioning column header column. This appears only if the selected app has a provisioning profile, and shows the provisioned status of each entitlement. The meaning of each status icon is as follows:

Provisioned Entitlement StatusDescription
entitlement is provisioned The entitlement is one that requires provisioning, and is allowed by the provisioning profile. Such entitlements are usually for an Apple service — such as iCloud or push notifications — or for a macOS feature to which Apple has decided to restrict access, such as system extensions.
entitlement is not provisioned [required for this key] The entitlement is one that requires provisioning, but it is not allowed by the provisioning profile. As a result, macOS will likely prevent the app from opening. (The macOS kernel will intentionally crash the app when it finds that it is using entitlements that are not permitted by the provisioning.)
entitlement is not provisioned [not strictly required for this key] The entitlement is not allowed by the provisioning profile, but is one of a subset of entitlements that are only quasi-restricted by Apple. These include com.apple.security.application-groups (which allows the app to share “group containers” with other apps from the same developer) and com.apple.application-identifier (which is a meta-entitlement used to identify the app and developer). Because provisioning of these is not strictly enforced, there will likely be no adverse impact on the launching or operation of the app.
provisioning is not required for this key The entitlement does not require provisioning at all. Generally, the entitlements related to App Sandbox and runtime hardening fall into this category.

What are “merged entitlements” and what do they have to do with PPPC Profiles?

Apparency allows you to select an app or other executable and show the entitlements that it requests. However, since an app might contain a large number of executable components, each which might have its own entitlements, Apparency also provides a way to see a high-level view of all entitlements requested by all components. For example, this can be useful when you are creating a Privacy Preferences Policy Control (PPPC) Profile, because it tells you (indirectly) what capabilities the app will request, and how you want macOS to prompt (or not) the end users.

If this sounds a bit hand-wavy, that's because we are not Mac admins, and have never built a PPPC Profile ourselves. We hope that this feature is useful, but if you have any feedback or suggestions in this area, please do contact us.

To get this merged view of entitlements, choose File > Show Entitlements for All Executables (Cmd-Control-A). merged entitlements view

In this view, Apparency shows every entitlement key that is requested by at least one executable component. In some cases, the keys might be all that you need, in order to see what the software wants to do. But if you need more information, Apparency also merges together all of the distinct values for a given key. If all executables use the same value (such as a Boolean YES), that will also be the merged value. But if different executables use different values (such as string identifiers), Apparency will combine these into an array (or if it's already an array, it will combine the distinct values).

The icon in the rightmost column tells you how Apparency created the merged value: either the same value icon for single entitlement value was found for all executables, or multiple values icon for merged entitlement values were merged together. If you move the pointer over the icon, a tooltip will show how many executables requested this particular entitlement.

If you're creating a PPPC Profile, you may want to allow a specific “service” for all of the executables that request a related entitlement. For example, perhaps you want to allow the AddressBook service for executables that request com.apple.security.personal-information.addressbook. If you select that entitlement, and choose Copy App Identities for PPPC Profile, Apparency will construct the array-of-dictionaries structure that a PPPC Profile uses to establish app identities and put this on the clipboard. Then you can go to your property list editor and paste directly under the Services dictionary.

Apparency doesn't understand the mapping between entitlements and PPPC services, so you will still need to infer that part. The intent here is only to reduce the tedium of gathering identifiers and code signing identities (requirements) from multiple components.

Using Apparency from Quick Look

Why don't I get an Apparency-generated Quick Look preview?

Once you've opened Apparency for the first time, macOS ought to enable its Quick Look Preview extension, so that previewing an app (or standalone executable) from the Finder will show the Apparency-generated preview.

However, if you don't get the expected preview, open System Settings and go to General > Login Items & Extensions > Quick Look and ensure that the Apparency item is turned on. (On macOS 14 or 13, go to Privacy & Security > Others > Extensions > Quick Look; on macOS 12 or earlier, open System Preferences and go to Extensions > Quick Look.)

If you still don't get an Apparency-generated preview, there might be a conflict with another app's Quick Look Preview extension. The Quick Look Extensions pane in System Settings will show which other third-party apps supply Quick Look Preview extensions, and you may able to find the conflict by temporarily turning one or more of those off.

Unfortunately, though, macOS provides no way to prioritize Quick Look Preview extensions, nor to enable or disable them for specific file Kinds (or for specific UTIs). As far as we can tell, macOS simply finds all extensions that declare support for the UTI being previewed, and if it finds more than one, chooses which one to use in an essentially arbitrary manner.

Based on some reverse-engineering, it seems that the QuickLookSupport.framework uses -[NSExtension extensionsWithMatchingAttributes:] (from Foundation.framework) to query for all plug-ins with an extension point of type com.apple.quicklook.preview. This list is similiar to what you'd get using:
/usr/bin/pluginkit --match --protocol com.apple.quicklook.preview --platform native --platform maccatalyst

Whether there's a way to influence the order of that list is unclear, but it doesn't seem to matter, because QuickLookSupport.framework proceeds to create a new list, which doesn't reflect the order of the discovered plug-ins. That is, while it does examine the plug-ins in discovery order, it stores its QLExtension model objects in a dictionary keyed by bundle identifier. That dictionary is then enumerated to find the first usable plug-in. Since the order of an NSFastEnumeration on an NSDictionary is essentially undefined, the choice is essentially arbitrary.

What if I prefer the standard Quick Look preview instead?

If you'd rather not use the Apparency-generated Quick Look preview — but you do want to keep the Apparency app itself installed — this is easy to do.

Open System Settings and go to General > Login Items & Extensions > Quick Look and turn off the Apparency item. (On macOS 14 or 13, go to Privacy & Security > Others > Extensions > Quick Look; on macOS 12 or earlier, open System Preferences and go to Extensions > Quick Look.)

How does the Apparency Quick Look Preview handle standalone script files?

Although you might use it most often for apps, Apparency also aims to provide Quick Look previews for standalone Mach-O executables, a.k.a. command-line tools. This can provide valuable information like signature and notarization status, and which processor architectures are supported.

Unfortunately, as discussed here, macOS is somewhat sloppy about the Kind of executable files, typically assigning Mach-O executables the same generic “Unix Executable” kind (UTI public.unix-executable) as script files written in some plain text language (like Python or Ruby). As such, there is no way for Apparency to offer previews for Mach-O executables without also offering it for script files.

Prior to version 2.2, Apparency would simply generate the same style of preview for a script as for a Mach-O executable. But while it's possible for a script to be signed, it's very uncommon, and so this preview tends to be mostly useless. Not least because it elides the actual text of the script file, which is probably what you really want. To be fair, even if you disabled the Apparency Quick Look Preview extension (or didn't have it installed at all), you still wouldn't see the script text, unless you'd installed another third-party extension that knows to do this.

Ideally, Apparency could delegate the script preview back to macOS to choose a more appropriate representation, but there is no way to do that. (At least, no way using the view-based Quick Look Preview extension that Apple touted as the new way forward with macOS 10.15; we're loathe to re-implement our previews yet again for the new, new way forward using data-based previews introduced with macOS 12.)

Anyway, starting in version 2.2, Apparency gets more creative. If macOS asks Apparency to preview a “Unix Executable” file that is really a script of some sort, it will make a temporary copy of the script with an explicit file extension (e.g. sample.sh or example.py) and ask macOS to generate a preview of that. Apparency then displays the resulting preview as its own. Most of the time, this will be the default macOS preview for a text file, but if you have other third-party Quick Look Preview extensions installed, it might be something more sophisticated, with syntax highlighting or the like. (This possibility is why Apparency doesn't just show the text directly, which would be simpler but might result in a preview that is less useful than you were anticipating!)

The above behavior applies only if Apparency is asked to generate the preview. As described here, if there are other third-party Quick Look Preview extensions that also declare support for “Unix Executable” files, it is impossible to predict which will be asked for the preview.

For example, the Syntax Highlight app declares itself as supporting “Unix Executable” files, and it shows them with syntax highlighting when they are actually scripts. If used on a Mach-O executable, it will show only a default preview or a hex dump.

But if both Syntax Highlight and Apparency are installed (and their Quick Look Preview extensions enabled), there's no way to guess which one will be used. If it happens to be Apparency, the above contortions should make the right thing happen for either type of file: Apparency will preview Mach-O executables itself, but delegate script files to Syntax Highlight by way of a temporary copy with an explicit file extension. On the other hand, if Syntax Highlight gets chosen, it doesn't go through any such contortions (because they are not insane, like we seem to be).

The Apparency behavior described above is a bit unorthodox, since it involves invoking a Quick Look preview from a Quick Look preview. You might notice some minor differences, such as in the default preview window size, but in general we've found it to work satisfactorily. Apparency tries to be very careful about not introducing any crazy preview loops or such, but just in case, we've added a preference to disable this “preview delegation” feature. If necessary, you can use this command in the Terminal:
defaults write "${HOME}/Library/Group Containers/936EB786NH.com.mothersruin.Apparency.SharedPrefs/Library/Preferences/936EB786NH.com.mothersruin.Apparency.SharedPrefs.plist" DisablePreviewDelegation -bool YES

Note that this requires a somewhat unusual defaults(1) incantation, since the preference is shared between the Apparency app and its Quick Look Preview extension, both of which are sandboxed.