What the common extensions are, what languages you write them in — and how to read the three that are already running on this machine.
Mission tie-in. You want to build, install, and watch a System Extension run on this Mac, entirely from the CLI. Before you build one, you need the map: what kinds exist, what each one replaced, what language each uses, and how to see them. The fastest way in is that your Mac is already a live specimen — it's running three of them right now.
A kernel extension (kext) runs inside the kernel with full privileges — one bug is a system-wide kernel panic. A System Extension does the same jobs (filter network traffic, watch security events, drive hardware) but runs as an ordinary sandboxed user-space process. If it crashes, only that process dies — the OS keeps running.
Apple moved third-party extension code out of the kernel and into user space, so the whole class of "a vendor's driver panicked my Mac" problems goes away — and, crucially for you, System Extensions load with SIP fully enabled. No recovery reboots, no lowered security. That's why we're building one of these, not a kext.
This is your tangible win. Run it in Terminal (no sudo needed):
Here's the actual output from this Mac, annotated. Match it against what you see:
3 extension(s) --- com.apple.system_extension.network_extension enabled active teamID bundleID (version) name [state] * * W5364U7YZB io.tailscale.ipn.macsys.network-extension (1.98.5/101.98.5) Tailscale Network Extension [activated enabled] --- com.apple.system_extension.cmio enabled active teamID bundleID (version) name [state] * 2MMRE5MTB8 com.obsproject.obs-studio.mac-camera-extension (32.1.2/…) OBS Virtual Camera [activated waiting for user] --- com.apple.system_extension.endpoint_security enabled active teamID bundleID (version) name [state] * * 4HMJQ7V3SX com.swiftlydetecting.agent.securityextension (2.1.0/1) Mac Monitor Security Extension [activated enabled]
| Column | Means |
|---|---|
--- com.apple.system_extension.network_extension | The category header. Everything below it until the next --- is that family. You can see three categories here: network_extension, cmio (camera), endpoint_security. |
enabled * | The user has switched it on in System Settings → Login Items & Extensions. Notice OBS has no star here — it's installed and running but still "waiting for user" to flip the toggle. |
active * | The extension process is currently loaded and running. OBS is active even though not user-enabled — the two columns are independent. |
| teamID | The 10-character Apple Developer Team ID that code-signed it. W5364U7YZB is Tailscale. This is how macOS pins an extension to a verified publisher. |
| bundleID (version) | Reverse-DNS identifier and (marketingVersion/buildVersion). The bundle ID is what your activation request names. |
[state] | Lifecycle state. activated enabled = fully live. activated waiting for user = installed but needs the System Settings toggle. |
You just read the live extension table on your own machine and can explain why OBS shows active but not enabled. That's the same command you'll use in Lesson 4 to confirm your own extension went live.
"What are the common system extensions?" Almost everything third parties ship falls into one of four families. Here's each, what it replaced, and a real example — two of them are on your Mac.
Inspect, filter, route, or tunnel network traffic in user space. Sub-types: content filter, DNS proxy, app proxy, packet tunnel (VPNs), transparent proxy.
Replaces socket/IP filter kexts (sflt_*, ipf_*)
▸ On your Mac: Tailscale
Observe and authorize system events — process exec, file opens, mounts, signals. The backbone of EDR/antivirus/DLP tools.
Replaces KAUTH kexts (kauth_*) + the old MAC framework
▸ On your Mac: Mac Monitor
Actual device drivers in user space — USB, HID, PCI, serial, audio, networking hardware. A built driver is a .dext.
Replaces IOKit C++ kexts subclassing IOService
e.g. a vendor printer/USB-gadget driver
Virtual cameras & capture devices via Core Media I/O. Shows up under the same systemextensionsctl umbrella.
Replaces DAL plug-ins (the old .plugin camera path)
▸ On your Mac: OBS Virtual Camera
This is the deprecation story made concrete: every legacy kext category got a user-space replacement framework. (Full mapping lives in the course glossary and Apple's deprecated-kext list.)
Your direct question. The container app (the thing that ships and activates the extension) and the provider (the extension itself) can be different languages. Quick reference:
| You're writing… | Language(s) | API shape |
|---|---|---|
| Container / host app | Swift or Objective-C | SwiftUI/AppKit + OSSystemExtensionRequest |
| Network Extension provider | Swift or Objective-C | Subclass NEFilterDataProvider, NEDNSProxyProvider, etc. |
| Endpoint Security client | C (callable from C++/Obj-C/Swift) | C functions: es_new_client(), es_subscribe() |
| DriverKit driver | C++ (a restricted dialect) | IOKit-style object model, IOService subclass |
| legacy Kernel extension | C / C++ (+ a little asm) | IOKit (C++) or BSD/KPI (C), runs in-kernel |
We'll write a tiny SwiftUI container app + a Swift NEFilterDataProvider. Modern, fully Swift, no C/C++ needed — and it runs with SIP on.
A System Extension isn't a standalone install. It's an .appex bundle nested inside a normal app. The app asks the OS to activate it; the OS asks you to approve it; then a separate provider process runs.
.appex, asks sysextd to activate it, the user approves once, then the provider runs as its own user-space process — all with SIP enabled.Two separate gates decide whether you can build and run your own extension. First: does the family need a special request form that only Apple can grant? Second: even once you can sign it, how does macOS let it load?
| Family | Needs Apple's special request form? | Notes |
|---|---|---|
| Network Extension | No | The capability is self-service in the Developer portal. You can build, sign, and provision it yourself. |
| Endpoint Security | Yes | Needs com.apple.developer.endpoint-security.client — granted only via Apple's System Extensions request form. |
| DriverKit | Yes | DriverKit entitlements are Apple-granted too; and you'd want real hardware to drive. |
| Camera (CMIO) | No | Self-service, but more moving parts (virtual-device plumbing) than a filter. |
Your signature carries com.apple.developer.networking.networkextension, which is a restricted entitlement. macOS only lets a restricted-entitlement extension load through one of two doors:
① A provisioning profile that authorizes the entitlement → it loads with SIP on (the pro path; the NE capability is self-service, so no Apple wait).
② Developer mode (systemextensionsctl developer on) → which on macOS 15 requires SIP to be disabled first.
Both were proven on this machine while building this course: a from-scratch build carrying the NE entitlement with no profile is Killed: 9 at launch (that's AMFI, working as designed), and developer on refuses while SIP is enabled. Lesson 5 walks the SIP-on provisioning door.
Still a Network Extension content filter (NEFilterDataProvider): it's the only interesting family with no Apple request form, it sees every network flow (immediately observable with log stream), and your paid Developer account can provision it with SIP left on. Endpoint Security and DriverKit we cover conceptually — they're gated behind the form.
Tap an answer — you'll get instant feedback. No score saved; this is just to make the ideas stick.
Q1. Your systemextensionsctl list shows OBS with a star under active but no star under enabled. What does that mean?
active = the process is loaded; enabled = the user toggled it on. They're independent — OBS is "activated waiting for user."active means it is running; the empty enabled column just means the user hasn't flipped the System Settings toggle. State reads "activated waiting for user."Q2. You want to build something on this Mac today without filing a request form and waiting on Apple. Which family?
Q3. The big safety win of a System Extension over a kext is…
A third-party extension that does kext-style jobs (network filtering, security monitoring, drivers) but runs as a sandboxed user-space process, loading with SIP enabled.
Network Extension (VPN/filter/DNS/proxy), Endpoint Security (EDR/security events), DriverKit (hardware drivers, .dext), and Camera/CMIO (virtual cameras).
systemextensionsctl list, what's the difference between the active and enabled columns?active = the extension process is currently loaded/running. enabled = the user has switched it on in System Settings. They're independent (see OBS: active, not enabled).
As an .appex inside the container app: YourApp.app/Contents/Library/SystemExtensions/. The app activates it via OSSystemExtensionRequest.
Endpoint Security (com.apple.developer.endpoint-security.client) and DriverKit. Network Extensions can run locally with developer mode on.
All Swift: a SwiftUI container app plus a Swift NEFilterDataProvider. No C or C++ required.
① a provisioning profile authorizing the entitlement (loads with SIP on — the path properly-shipped vendor extensions like Tailscale & Mac Monitor use), or ② developer mode (systemextensionsctl developer on), which on macOS 15 requires SIP off.
systemextensionsctl developer on, create the SwiftUI app, embed an empty .appex, and call OSSystemExtensionRequest — watch the approval prompt appear.
L3 →Write the content-filter provider. Subclass NEFilterDataProvider, return verdicts on new flows, wire up Info.plist / entitlements.
L4 →Build → install → watch it filter. Build from the CLI, activate, approve, then sudo log stream to watch your extension see your live traffic. The payoff.
NEFilterDataProvider decides allow/deny, or anything from the entitlement table? Or if you'd rather aim the build at a DNS proxy or transparent proxy instead of a content filter, say so and I'll re-plan. Just tell me to start Lesson 2 when you're ready.