macOS Kernel & System Extensions · Lesson 6 · reference

Lifecycle, Debugging & Teardown

Update it, diagnose why it won't load, and remove it cleanly. The troubleshooting table is the part you'll come back to.

🎯
By the end you'll know how to ship a new version, read the exact error when activation fails, and fully uninstall the extension — keeping this lesson as your debugging reference.

1Updating: the replacement flow

You don't "reinstall." You bump CFBundleVersion, rebuild, and submit another activation request for the same bundle ID. The OS notices a version it already has and calls your delegate:

func request(_ r: OSSystemExtensionRequest, actionForReplacingExtension old: OSSystemExtensionProperties, withExtension new: OSSystemExtensionProperties) -> ReplacementAction { return .replace // take the new build (return .cancel to keep the old) }

During development the running provider is torn down (stopFilter with reason .noNetworkAvailable/.userInitiated) and the new one started. No reboot — the user-space store just swaps the bundle.

2Teardown

Three levels, least to most aggressive:

▶ graceful — your app asks$ /Applications/NetFilterDemo.app/Contents/MacOS/NetFilterDemo deactivate # submits OSSystemExtensionRequest.deactivationRequest → state goes "terminated" ▶ also turn off the filter config$ # in System Settings ▸ Network, remove the "NetFilterDemo" content filter ▶ nuclear — wipe ALL third-party sysexts (SIP off only)$ systemextensionsctl reset
Two pieces to remove, not one

A content filter has both a system extension (remove via deactivation / System Settings ▸ Login Items & Extensions) and a filter configuration (remove via System Settings ▸ Network ▸ Filters, or NEFilterManager.removeFromPreferences). Pull both or you'll leave a half-configured filter behind.

3The failure table — keep this

SymptomCauseFix
Killed: 9
(exit 137)
Restricted entitlement not authorized by a provisioning profile (AMFI kill).Embed a matching embedded.provisionprofile; ensure entitlements ⊆ profile. (Lesson 5 §2.)
code=2OSSystemExtensionErrorDomain: app missing com.apple.developer.system-extension.install.Add that entitlement to app.entitlements, re-sign.
code=1"unknown/unsupported" — bundle ID mismatch, ext not embedded at the right path, or Info.plist malformed.Verify the .systemextension is under Contents/Library/SystemExtensions/ and its CFBundleIdentifier matches the request.
code=8"codeSignatureInvalid" — entitlements don't match the profile, or signing chain broken.Re-create the profile with the exact capability; sign inside-out; check codesign -vvv --deep.
waiting for
user
Activation succeeded but no one approved it.System Settings ▸ Login Items & Extensions ▸ Network Extensions ▸ toggle on.
cannot allow
outside /Applications
SIP on requires the host app in /Applications.cp -R the app into /Applications (or use developer mode with SIP off).
developer
blocked
systemextensionsctl developer on refused — SIP is enabled.Use the provisioning path, or disable SIP from 1TR first.
handleNewFlow
never fires
Extension activated but content filtering not enabled, or rules don't route to the provider.Confirm NEFilterManager.isEnabled = true saved (2nd approval); use defaultAction: .filterData.

4Where the logs are

▶ your extension's own logs$ sudo log stream --predicate 'subsystem == "com.example.netfilterdemo.filter"' ▶ the activation broker (why it rejected you)$ log show --last 5m --predicate 'process == "sysextd"' --info ▶ AMFI / code-signing kills$ log show --last 5m --predicate 'sender == "AppleMobileFileIntegrity"' ▶ a crash left a report here$ ls -t ~/Library/Logs/DiagnosticReports/ | head
Debugging mindset

systemextensionsctl list tells you the state; sysextd logs tell you why a transition failed; your os_log subsystem tells you what the provider is doing. Check them in that order.

5Flashcards

How do you ship v2 of an extension?

Bump CFBundleVersion, rebuild, submit another activation request; implement actionForReplacingExtension → .replace. No reboot.

What two things must you remove to fully uninstall a content filter?

The system extension (deactivation / Login Items & Extensions) and the filter configuration (Network ▸ Filters / removeFromPreferences).

You get code=8 on activation. First suspect?

Code-signature/entitlement vs. provisioning-profile mismatch. Regenerate the profile with the right capability and re-sign.

Which tool tells you why sysextd rejected an activation?

log show/stream --predicate 'process == "sysextd"'.

💬 Ask your teacher. Hit an error code not in the table? Paste it. Want a one-shot uninstall.sh for the demo, or to wire deactivation into a menu-bar app? Ask. Next we leave the build behind and explore the family security tools love: say "Lesson 7".