Define the security boundary
Enable Hardened Runtime
Adopt runtime protections for distributed code and grant exceptions only for behavior the application can justify and test.
12 minute lesson
The Hardened Runtime is a set of protections your app opts into at signing time. It blocks code injection, loading of unsigned or tampered libraries, and debugger attachment in shipped builds. You’re telling macOS: this process should never be modified at runtime — enforce that.
You need it. Notarization requires the Hardened Runtime on every executable in a Developer ID distribution. No hardened runtime, no notarization, no smooth installs.
In Xcode, add the Hardened Runtime capability under Signing & Capabilities for the app target and every helper or bundled tool target. If you sign manually in a script, it’s the --options runtime flag:
codesign --force --options runtime \
--sign "Developer ID Application: Flavio Copes (ABCDE12345)" \
Notes.app
Verify it took. The flags line must include runtime:
codesign -d --verbose=2 Notes.app
Look for flags=0x10000(runtime) in the output.
The runtime has escape hatches: exceptions for just-in-time compilation, unsigned executable memory, debugging, and library validation. Some apps genuinely need one — an app embedding a JavaScript engine with JIT, for example. But every exception switches off one specific protection, so treat them like sandbox entitlements: none by default, each one justified by a feature you can name.
When a third-party dependency crashes under the hardened runtime, investigate before reaching for an exception. Often the real problem is a badly signed framework, and the right fix is re-signing or updating the dependency, not disabling library validation for your whole app.
And check that debug entitlements are gone. com.apple.security.get-task-allow must not survive into a shipping build.
The failure you’ll actually meet: a scripted build that signs without --options runtime. Everything works locally. Then notarytool returns Invalid, and the log says the executable does not have the hardened runtime enabled. Recognize that message, add the flag, re-sign, resubmit.
Lesson completed