Define the security boundary
Read the final entitlements
Inspect the entitlements embedded in the release artifact instead of assuming the Xcode project editor represents the shipped result.
12 minute lesson
Entitlements are the capabilities granted to your signed code: sandbox on or off, network access, file access, Keychain groups, automation. Xcode shows you what you asked for. The built app is what users actually get, and the two can differ.
Build settings, configurations, and the export step all influence the final entitlement set. So the habit to build is: inspect the artifact, not the project.
codesign -d --entitlements :- Notes.app
The :- writes the embedded entitlements to standard output. Read the list slowly. Every entry should map to a feature you can name. Entitlements left over from an abandoned experiment are pure attack surface — delete them from the project and rebuild.
Watch for temporary exception entitlements in particular. They exist for migration scenarios, and a final build carrying broad exceptions is a design smell.
One entitlement deserves special attention: com.apple.security.get-task-allow. Debug builds carry it so the debugger can attach. A distributed build must not. Check for it explicitly:
codesign -d --entitlements :- Notes.app | grep get-task-allow
No output is the answer you want. If it’s there, you exported a debug-configured build — and notarization will reject it, with exactly that entitlement named in the log.
Nested code has its own entitlements. Embedded frameworks and helper executables are signed separately, so inspect them too:
codesign -d --entitlements :- \
Notes.app/Contents/Frameworks/Sparkle.framework
A helper with broader capabilities than the main app undermines the boundary you designed.
Finish with a one-line rule: for every entitlement in the shipped artifact, you can say which feature needs it and why. If you can’t, remove it.
Lesson completed