I haven’t come across any official documentation regarding the limit on the number of Network Extensions macOS can run. However, I did see some discussions suggesting that Apple might restrict this to 5 extensions in macOS Tahoe.
Is there any official confirmation on this?
System Extensions
RSS for tagInstall and manage user space code that extends the capabilities of macOS using System Extensions.
Posts under System Extensions tag
78 Posts
Selecting any option will automatically load the page
Post
Replies
Boosts
Views
Activity
Hello, I am facing a validation error when uploading a macOS app with a Network Extension. The Error: Invalid system extension. The system extension at “[T] TEXT.app/Contents/Library/SystemExtensions/company_name.network-extension.systemextension” resides in an unexpected location. The Problem: Validation fails only when the app's Product Name contains square brackets: [T] TEXT. If I remove the brackets from the Product Name, validation passes. What I've tried: Setting Product Name to TEXT (without brackets) and CFBundleDisplayName to [T] TEXT. Cleaning Derived Data and rebuilding the archive. Verified that the extension is physically located at Contents/Library/SystemExtensions/. It seems the Apple validation tool fails to parse the bundle path correctly when it contains characters like [ or ]. Question: How can I keep the app name with brackets for the user (in System Settings and Menu Bar) while ensuring the Network Extension passes validation? Is there a way to escape these characters or a specific Info.plist configuration to satisfy the validator?"
Topic:
App Store Distribution & Marketing
SubTopic:
App Store Connect
Tags:
App Store Connect
Network Extension
System Extensions
Hello, I am facing a validation error when uploading a macOS app with a Network Extension. The Error: Invalid system extension. The system extension at “[T] TEXT.app/Contents/Library/SystemExtensions/company_name.network-extension.systemextension” resides in an unexpected location. The Problem: Validation fails only when the app's Product Name contains square brackets: [T] TEXT. If I remove the brackets from the Product Name, validation passes. What I've tried: Setting Product Name to TEXT (without brackets) and CFBundleDisplayName to [T] TEXT. Cleaning Derived Data and rebuilding the archive. Verified that the extension is physically located at Contents/Library/SystemExtensions/. It seems the Apple validation tool fails to parse the bundle path correctly when it contains characters like [ or ]. Question: How can I keep the app name with brackets for the user (in System Settings and Menu Bar) while ensuring the Network Extension passes validation? Is there a way to escape these characters or a specific Info.plist configuration to satisfy the validator?"
Topic:
App Store Distribution & Marketing
SubTopic:
App Store Connect
Tags:
App Store Connect
Network Extension
System Extensions
Hello,
I am getting the following errors when building a Mac Camera Extension with web sockets. I am using URLSessionWebsocketTask as my web socket library. I built a test program for my code and in there I can see my web sockets are working properly, but when I run it from the System Extension I get the following errors. The socket opens for two - three messages then crashes. I couldnt find any documentation online for the following errors
CMIOExtensionProvider.m:1975:-[CMIOExtensionProvider removeProviderContext:]_block_invoke Unregistered provider context <CMIOExtensionProviderContext: ->, don't be surprised if things go badly
CMIOExtensionProviderContext.m:64:-[CMIOExtensionProviderContext initWithConnection:]_block_invoke [391] received Connection invalid``
Topic:
Media Technologies
SubTopic:
Photos & Camera
Tags:
System Extensions
wwdc2022-10022
Core Media
Hello Apple Developer Support,
I’m developing a virtual camera using the CMIOExtensionDevice / CMIOExtensionStreamSource APIs on macOS. While the virtual camera appears in system settings and apps like Zoom and Google Meet, the video output exhibits the following issues:
Jittering frames: The first frame sometimes appears correctly, but subsequent frames flicker or jitter.
Solid color fill: Eventually, the camera feed fills entirely with a solid accent color (e.g., blue), rather than the intended video content.
Console logs: Repeated messages appear in Console.app:
Invalid display 0x00000000
Setup details:
The virtual camera is created using CMIOExtensionDevice and CMIOExtensionStream.
Video frames are rendered from NSImage/CGImage using CGContext and copied into CVPixelBuffers.
Frame delivery is controlled by a DispatchSourceTimer at 60 FPS.
macOS version: 26.2
Xcode version: 26.1
Observations:
The Invalid display 0x00000000 logs suggest that CGContext drawing or NSImage operations are failing in headless mode (i.e., there is no real display attached to the virtual camera).
Using CIContext with .useSoftwareRenderer = true appears to mitigate some flicker, but not entirely.
Questions / Requests:
Is it expected that CoreMediaIO virtual cameras cannot reliably render CGImage / NSImage frames offscreen?
Are there recommended APIs or approaches to render virtual camera frames fully headless to avoid display-dependent jitter?
Is there any documentation or sample code from Apple showing stable video output from a virtual camera extension that does not rely on a physical display?
Any guidance or examples would be greatly appreciated. This issue prevents the virtual camera from being used reliably in standard video apps.
Thank you,
Savvy
Topic:
Media Technologies
SubTopic:
Photos & Camera
Tags:
System Extensions
Core Media
Continuity Camera
Hello Apple Developer Support,
I’m developing a virtual camera using the CMIOExtensionDevice / CMIOExtensionStreamSource APIs on macOS. While the virtual camera appears in system settings and apps like Zoom and Google Meet, the video output exhibits the following issues:
Jittering frames: The first frame sometimes appears correctly, but subsequent frames flicker or jitter.
Solid color fill: Eventually, the camera feed fills entirely with a solid accent color (e.g., blue), rather than the intended video content.
Console logs: Repeated messages appear in Console.app:
Invalid display 0x00000000
Setup details:
The virtual camera is created using CMIOExtensionDevice and CMIOExtensionStream.
Video frames are rendered from NSImage/CGImage using CGContext and copied into CVPixelBuffers.
Frame delivery is controlled by a DispatchSourceTimer at 60 FPS.
macOS version: [Your macOS version here]
Xcode version: [Your Xcode version here]
Observations:
The Invalid display 0x00000000 logs suggest that CGContext drawing or NSImage operations are failing in headless mode (i.e., there is no real display attached to the virtual camera).
Using CIContext with .useSoftwareRenderer = true appears to mitigate some flicker, but not entirely.
Questions / Requests:
Is it expected that CoreMediaIO virtual cameras cannot reliably render CGImage / NSImage frames offscreen?
Are there recommended APIs or approaches to render virtual camera frames fully headless to avoid display-dependent jitter?
Is there any documentation or sample code from Apple showing stable video output from a virtual camera extension that does not rely on a physical display?
Any guidance or examples would be greatly appreciated. This issue prevents the virtual camera from being used reliably in standard video apps.
Thank you,
Savvy
Topic:
Media Technologies
SubTopic:
Photos & Camera
Tags:
System Extensions
Core Media
Continuity Camera
Feedback ticket ID: FB21797397
Summary
When using posix_spawn() with posix_spawnattr_set_uid_np() to spawn a child process with a different UID, the eslogger incorrectly reports a setuid event as an event originating from the parent process instead of the child process.
Steps to Reproduce
Create a binary that do the following:
Configure posix_spawnattr_t that set the process UIDs to some other user ID (I'll use 501 in this example).
Uses posix_spawn() to spawn a child process
Run eslogger with the event types setuid, fork, exec
Execute the binary as root process using sudo or from root owned shell
Terminate the launched eslogger
Observe the process field in the setuid event
Expected behavior
The eslogger will report events indicating a process launch and uid changes so the child process is set to 501. i.e.:
fork
setuid - Done by child process
exec
Actual behavior
The process field in the setuid event is reported as the parent process (that called posix_spawn) - indicating UID change to the parent process.
Attachments
I'm attaching source code for a small project with a 2 binaries:
I'll add the source code for the project at the end of the file + attach filtered eslogger JSONs
One that runs the descirbed posix_spawn flow
One that produces the exact same sequence of events by doing different operation and reaching a different process state:
Parent calls fork()
Parent process calls setuid(501)
Child process calls exec()
Why this is problematic
Both binaries in my attachment do different operations, achieving different process state (1 is parent with UID=0 and child with UID=501 while the other is parent UID=501 and child UID=0), but report the same sequence of events.
Code
#include <cstdio>
#include <spawn.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <string.h>
// environ contains the current environment variables
extern char **environ;
extern "C" {
int posix_spawnattr_set_uid_np(posix_spawnattr_t *attr, uid_t uid);
int posix_spawnattr_set_gid_np(posix_spawnattr_t *attr, gid_t gid);
}
int main() {
pid_t pid;
int status;
posix_spawnattr_t attr;
// 1. Define the executable path and arguments
const char *path = "/bin/sleep";
char *const argv[] = {(char *)"sleep", (char *)"1", NULL};
// 2. Initialize spawn attributes
if ((status = posix_spawnattr_init(&attr)) != 0) {
fprintf(stderr, "posix_spawnattr_init: %s\n", strerror(status));
return EXIT_FAILURE;
}
// 3. Set the UID for the child process (e.g., UID 501)
// Note: Parent must be root to change to a different user
uid_t target_uid = 501;
if ((status = posix_spawnattr_set_uid_np(&attr, target_uid)) != 0) {
fprintf(stderr, "posix_spawnattr_set_uid_np: %s\n", strerror(status));
posix_spawnattr_destroy(&attr);
return EXIT_FAILURE;
}
// 4. Spawn the process
printf("Spawning /bin/sleep 1 as UID %d...\n", target_uid);
status = posix_spawn(&pid, path, NULL, &attr, argv, environ);
if (status == 0) {
printf("Successfully spawned child with PID: %d\n", pid);
// Wait for the child to finish (will take 63 seconds)
if (waitpid(pid, &status, 0) != -1) {
printf("Child process exited with status %d\n", WEXITSTATUS(status));
} else {
perror("waitpid");
}
} else {
fprintf(stderr, "posix_spawn: %s\n", strerror(status));
}
// 5. Clean up
posix_spawnattr_destroy(&attr);
return (status == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
}
#include <cstdio>
#include <cstdlib>
#include <unistd.h>
#include <sys/wait.h>
#include <errno.h>
#include <string.h>
// This program demonstrates fork + setuid + exec behavior for ES framework bug report
// 1. Parent forks
// 2. Parent does setuid(501)
// 3. Child waits with sleep syscall
// 4. Child performs exec
int main() {
printf("Parent PID: %d, UID: %d, EUID: %d\n", getpid(), getuid(), geteuid());
pid_t pid = fork();
if (pid < 0) {
// Fork failed
perror("fork");
return EXIT_FAILURE;
}
if (pid == 0) {
// Child process
printf("Child PID: %d, UID: %d, EUID: %d\n", getpid(), getuid(), geteuid());
// Child waits for a bit with sleep syscall
printf("Child sleeping for 2 seconds...\n");
sleep(2);
// Child performs exec
printf("Child executing child_exec...\n");
// Get the path to child_exec (same directory as this executable)
char *const argv[] = {(char *)"/bin/sleep", (char *)"2", NULL};
// Try to exec child_exec from current directory first
execv("/bin/sleep", argv);
// If exec fails
perror("execv");
return EXIT_FAILURE;
} else {
// Parent process
printf("Parent forked child with PID: %d\n", pid);
// Parent does setuid(501)
printf("Parent calling setuid(501)...\n");
if (setuid(501) != 0) {
perror("setuid");
// Continue anyway to observe behavior
}
printf("Parent after setuid - UID: %d, EUID: %d\n", getuid(), geteuid());
// Wait for child to finish
int status;
if (waitpid(pid, &status, 0) != -1) {
if (WIFEXITED(status)) {
printf("Child exited with status %d\n", WEXITSTATUS(status));
} else if (WIFSIGNALED(status)) {
printf("Child killed by signal %d\n", WTERMSIG(status));
}
} else {
perror("waitpid");
}
}
return EXIT_SUCCESS;
}
posix_spawn.json
fork_exec.json
Network is not working when over 50MB size file upload smb using NEFilterDataProvider in macOS
The event received through NEFilterDataProvider is returned immediately without doing any other work.
override func handleNewFlow(_ flow: NEFilterFlow) -> NEFilterNewFlowVerdict {
guard let socketFlow = flow as? NEFilterSocketFlow,
let auditToken = socketFlow.sourceAppAuditToken,
let remoteEndpoint = socketFlow.remoteEndpoint as? NWHostEndpoint,
let localEndpoint = socketFlow.localEndpoint as? NWHostEndpoint else {
return .allow()
}
return .filterDataVerdict(withFilterInbound: true, peekInboundBytes: Int.max, filterOutbound: true, peekOutboundBytes: Int.max)
}
override func handleInboundData(from flow: NEFilterFlow, readBytesStartOffset offset: Int, readBytes: Data) -> NEFilterDataVerdict {
guard let socketFlow = flow as? NEFilterSocketFlow,
let auditToken = socketFlow.sourceAppAuditToken,
let remoteEndpoint = socketFlow.remoteEndpoint as? NWHostEndpoint,
let localEndpoint = socketFlow.localEndpoint as? NWHostEndpoint else {
return .allow()
}
return NEFilterDataVerdict(passBytes: readBytes.count, peekBytes: Int.max)
}
override func handleOutboundData(from flow: NEFilterFlow, readBytesStartOffset offset: Int, readBytes: Data) -> NEFilterDataVerdict {
guard let socketFlow = flow as? NEFilterSocketFlow,
let auditToken = socketFlow.sourceAppAuditToken,
let remoteEndpoint = socketFlow.remoteEndpoint as? NWHostEndpoint,
let localEndpoint = socketFlow.localEndpoint as? NWHostEndpoint else {
return .allow()
}
return NEFilterDataVerdict(passBytes: readBytes.count, peekBytes: Int.max)
}
override func handleInboundDataComplete(for flow: NEFilterFlow) -> NEFilterDataVerdict {
guard let socketFlow = flow as? NEFilterSocketFlow,
let auditToken = socketFlow.sourceAppAuditToken,
let remoteEndpoint = socketFlow.remoteEndpoint as? NWHostEndpoint,
let localEndpoint = socketFlow.localEndpoint as? NWHostEndpoint else {
return .allow()
}
return .allow()
}
override func handleOutboundDataComplete(for flow: NEFilterFlow) -> NEFilterDataVerdict {
guard let socketFlow = flow as? NEFilterSocketFlow,
let auditToken = socketFlow.sourceAppAuditToken,
let remoteEndpoint = socketFlow.remoteEndpoint as? NWHostEndpoint,
let localEndpoint = socketFlow.localEndpoint as? NWHostEndpoint else {
return .allow()
}
return .allow()
}
how can i fix it?
I’m building a macOS app with a DNS Proxy system extension for Developer ID + notarization, deployed via MDM, and Xcode fails the Developer ID Release build with a provisioning profile mismatch for com.apple.developer.networking.networkextension.
Environment
macOS: Sequoia (15.7.2)
Xcode: 26.2
Distribution: Developer ID + notarization, deployed via MDM
Host bundle ID: com.mydns.agent.MyDNSMacProxy
DNS Proxy system extension bundle ID: com.mydns.agent.MyDNSMacProxy.dnsProxy
Host entitlements (Release):
File: MyDNSMacProxy/MyDNSMacProxyRelease.entitlements:
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.application-identifier</key>
<string>B234657989.com.mydns.agent.MyDNSMacProxy</string>
<key>com.apple.developer.networking.networkextension</key>
<array>
<string>dns-proxy</string>
</array>
<key>com.apple.developer.system-extension.install</key>
<true/>
<key>com.apple.developer.team-identifier</key>
<string>B234657989</string>
<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.application-groups</key>
<array>
<string>group.com.mydns.MyDNSmac</string>
</array>
<key>keychain-access-groups</key>
<array>
<string>B234657989.*</string>
</array>
</dict>
</plist>
xcodebuild -showBuildSettings -scheme MyDNSMacProxy -configuration Release :
PROVISIONING_PROFILE_SPECIFIER = main MyDNSMacProxy5
CODE_SIGN_IDENTITY = Developer ID Application
Host Developer ID profile
main_MyDNSMacProxy5.provisionprofile (via security cms -D):
"Entitlements" => {
"com.apple.application-identifier" => "B234657989.com.mydns.agent.MyDNSMacProxy"
"com.apple.developer.team-identifier" => "B234657989"
"com.apple.security.application-groups" => [ "group.com.mydns.MyDNSmac", ..., "B234657989.*" ]
"keychain-access-groups" => [ "B234657989.*" ]
"com.apple.developer.system-extension.install" => 1
"com.apple.developer.networking.networkextension" => [
"packet-tunnel-provider-systemextension",
"app-proxy-provider-systemextension",
"content-filter-provider-systemextension",
"dns-proxy-systemextension",
"dns-settings",
"relay",
"url-filter-provider",
"hotspot-provider"
]
}
So:
App ID, team ID, keychain and system‑extension.install match.
The profile’s com.apple.developer.networking.networkextension is a superset of what I request in the host entitlements (dns-proxy only).
System extension (for context)
DNS Proxy system extension target:
NSExtensionPointIdentifier = com.apple.dns-proxy
NetworkExtension → NEProviderClasses → com.apple.networkextension.dns-proxy → my provider class
Entitlements: com.apple.developer.networking.networkextension = ["dns-proxy-systemextension"]
This target uses a separate Developer ID profile and builds successfully.
Xcode error
Release build of the host fails with:
…MyDNSMacProxy.xcodeproj: error: Provisioning profile "main MyDNSMacProxy5" doesn't match the entitlements file's value for the com.apple.developer.networking.networkextension entitlement. (in target 'MyDNSMacProxy' from project 'MyDNSMacProxy')
Xcode UI also says:
Entitlements: 6 Included, 1 Missing Includes com.apple.developer.team-identifier, com.apple.application-identifier, keychain-access-groups, com.apple.developer.system-extension.install, and com.apple.security.application-groups. Doesn’t match entitlements file value for com.apple.developer.networking.networkextension.
Because of this, the app bundle isn’t produced and I can’t inspect the final signed entitlements.
Questions:
For com.apple.developer.networking.networkextension, should Xcode accept a subset of values in the entitlements (here just dns-proxy) as long as that value is allowed by the Developer ID profile, or does it currently require a stricter match?
Is the following configuration valid for Developer ID + MDM with a DNS Proxy system extension:
Host entitlements: ["dns-proxy"]
System extension entitlements: ["dns-proxy-systemextension"]
Host profile’s NE array includes the DNS Proxy system extension types.
If this is a known limitation or bug in how Xcode validates NE entitlements for Developer ID, is there a recommended workaround?
Thanks for any guidance.
Topic:
App & System Services
SubTopic:
Networking
Tags:
Network Extension
System Extensions
Code Signing
Developer ID
Apple is encouraging VPN apps on macOS to transition to Network Extension APIs, if they haven't done so yet, see:
TN3165: Packet Filter is not API
WWDC25: Filter and tunnel network traffic with NetworkExtension
Using Network Extension is fine for VPN apps that are distributed via the Mac App Store. Users get one pop-up requesting permission to add VPN configurations and that's it.
However, VPN apps that are distributed outside of the App Store (using Developer ID) cannot use Network Extension in the same way, such apps need to install a System Extension first (see TN3134: Network Extension provider deployment).
Installing a System Extension is a very poor user experience. There is a pop-up informing about a system extension, which the user has to manually enable. The main button is "OK", which only dismisses the pop-up and in such case there is little chance that the user will be able to find the correct place to enable the extension. The other button in that pop-up navigates to the correct screen in System Settings, where the user has to enable a toggle. Then there is a password prompt. Then the user has to close the System Settings and return to the app.
This whole dance is not necessary for VPN apps on the Mac App Store, because they work with "app extensions" rather than "system extensions".
As a developer of a VPN app that is distributed outside of the App Store, my options are:
Implement VPN functionality in an alternative way, without Network Extension. This is discouraged by Apple.
Use a System Extension with Network Extension. This is going to discourage my users.
I have submitted feedback to Apple: FB19631390.
But I wonder, why did Apple create this difference in the first place? Is there a chance that they will either improve the System Extension installation process or even allow "app extensions" outside of the Mac App Store?
Topic:
App & System Services
SubTopic:
Networking
Tags:
Extensions
Network Extension
System Extensions
Developer ID
Hello,
We are implementing a Transparent Proxy using NETransparentProxyProvider and configuring NETransparentProxyNetworkSettings with NENetworkRule.
Currently, NENetworkRule requires:
NENetworkRule(
destinationHost: NWHostEndpoint(hostname: String, port: String),
protocol: .TCP / .UDP / .any
)
NWHostEndpoint.port accepts only a single port value (as a String) or an empty string for all ports.
At present, we are creating a separate NENetworkRule for each port in the range (ex for range 49152–65535 approximately 16,384 rules). After deploying this configuration, we observe the following behavior:
nesessionmanager starts consuming very high CPU (near 100%)
The system becomes unresponsive
The device eventually hangs and restarts automatically
The behavior resembles a kernel panic scenario
This strongly suggests that creating thousands of NENetworkRule entries may not be a supported or scalable approach.
Questions:
Is there any officially supported way to specify a port range in NENetworkRule?
Is creating thousands of rules (one per port) considered acceptable or supported?
Is the recommended design to intercept broadly (e.g., port = "") and filter port ranges inside handleNewTCPFlow / handleNewUDPFlow instead?
Are there documented system limits for the number of NENetworkRule entries allowed in NETransparentProxyNetworkSettings?
I am learning about endpoint security and other system extensions, while I was handling ES_EVENT_TYPE_AUTH_IOKIT_OPEN event I realized that I cannot auth deny any bluetooth events. I tried to deny any open or execute events related to com.apple.bluetoothd but it did not work. I searched google and found out that I can use CoreBluetooth to control bluetooth. But when I get connected to bluetooth keyboard or mouse, didConnectPeripheral dose not get called or when I call [central cancelPeripheralConnection:peripheral] disconnection never happens.
Is there any recommendation for handling or controlling events related to bluetooth connection?
Topic:
App & System Services
SubTopic:
Core OS
Tags:
System Extensions
Endpoint Security
Core Bluetooth
My application installs a system extension.
When I try to remove the app from the Applications folder (cmd + backspace) I get an error message:
"The operation can’t be completed right now because another operation is in progress, such as moving or copying an item or emptying the Bin."
According to systemextensionsctl the extension state is "terminating for uninstall but still running".
I can see an error in the console logs:
kernelmanagerd Failed to terminate dext com.my.driver-dk, error: Kernel request failed: (os/kern) invalid address (1)
sysextd a category delegate declined to terminate extension with identifier: com.my.driver-dk
sysextd failed to terminate extension with identifier: com.my.driver-dk: Optional(Error Domain=kernelmanagerd.KMError Code=38 "(null)")
Issue occurs with macOS 13 - works fine with macOS 12 and macOS 11
What is the problem here?
Have there been any changes in macOS in that regard?
I'm currently observing a problem similar to this thread https://developer.apple.com/forums/thread/737334
The difference is that this is happening after updating a system extension.
Basically same error, sysextd complains it can not check that the system extension is notarized: macOS Error 3 + Error code=-67050.
I think macOS (Sequoia 15.3.2 or 15.7.2 if it matters) is wrong in this case for the following reasons:
when using spctl assess -t install, the system extension is reported to be correctly notarized.
when restarting the Mac, the updated system extension is correctly checked and staged.
if I run spctl assess before sysextd tries to check the system extension, it works.
I'm currently thinking of 2 reasons why the check does not work:
sysextd is somehow trying to work with a cached assessment that has become invalid after the system extension was updated.
macOS needs way more time between the update of the files and the request to update the staged extension. I tried adding a 5-second delay. This does not seem to work or at least reliably.
I tried just touching the system extension, no positive result. Unfortunately, in macOS Sequoia, it is not possible anymore to reset-default using spctl and see if it solves the issue, at least the next time the update is performed.
[Q] Is there some magic operation that would help macOS correctly check the notarization of an updated system extension?
Hi,
I’m implementing a macOS DNS Proxy as a system extension and running into a persistent activation error:
OSSystemExtensionErrorDomain error 9 (validationFailed)
with the message:
extension category returned error
This happens both on an MDM‑managed Mac and on a completely clean Mac (no MDM, fresh install).
Setup
macOS: 15.x (clean machine, no MDM)
Xcode: 16.x
Team ID: AAAAAAA111 (test)
Host app bundle ID: com.example.agent.NetShieldProxy
DNS Proxy system extension bundle ID: com.example.agent.NetShieldProxy.dnsProxy
The DNS Proxy is implemented as a NetworkExtension system extension, not an app extension.
Host app entitlements
From codesign -d --entitlements :- /Applications/NetShieldProxy.app:
xml
com.apple.application-identifier
AAAAAAA111.com.example.agent.NetShieldProxy
<key>com.apple.developer.system-extension.install</key>
<true/>
<key>com.apple.developer.team-identifier</key>
<string>AAAAAAA111</string>
<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.application-groups</key>
<array>
<string>group.com.example.NetShieldmac</string>
</array>
<key>com.apple.security.files.user-selected.read-only</key>
<true/>
xml
com.apple.application-identifier
AAAAAAA111.com.example.agent.NetShieldProxy.dnsProxy
<key>com.apple.developer.networking.networkextension</key>
<array>
<string>dns-proxy-systemextension</string>
</array>
<key>com.apple.developer.team-identifier</key>
<string>AAAAAAA111</string>
<key>com.apple.security.application-groups</key>
<array>
<string>group.com.example.NetShieldmac</string>
<string>group.example.NetShieldmac</string>
<string>group.example.agent.enterprise.macos</string>
<string>group.example.com.NetShieldmac</string>
</array>
DNS Proxy system extension Info.plist
On the clean Mac, from:
bash
plutil -p "/Applications/NetShieldProxy.app/Contents/Library/SystemExtensions/com.example.agent.NetShieldProxy.dnsProxy.systemextension/Contents/Info.plist"
I get:
json
{
"CFBundleExecutable" => "com.example.agent.NetShieldProxy.dnsProxy",
"CFBundleIdentifier" => "com.example.agent.NetShieldProxy.dnsProxy",
"CFBundleName" => "com.example.agent.NetShieldProxy.dnsProxy",
"CFBundlePackageType" => "SYSX",
"CFBundleShortVersionString" => "1.0.1.8",
"CFBundleSupportedPlatforms" => [ "MacOSX" ],
"CFBundleVersion" => "0.1.1",
"LSMinimumSystemVersion" => "13.5",
"NSExtension" => {
"NSExtensionPointIdentifier" => "com.apple.dns-proxy",
"NSExtensionPrincipalClass" => "com_example_agent_NetShieldProxy_dnsProxy.DNSProxyProvider"
},
"NSSystemExtensionUsageDescription" => "SYSTEM_EXTENSION_USAGE_DESCRIPTION"
}
The DNSProxyProvider class inherits from NEDNSProxyProvider and is built in the system extension target.
Activation code
In the host app, I use:
swift
import SystemExtensions
final class SystemExtensionActivator: NSObject, OSSystemExtensionRequestDelegate {
private let extensionIdentifier = "com.example.agent.NetShieldProxy.dnsProxy"
func activate(completion: @escaping (Bool) -> Void) {
let request = OSSystemExtensionRequest.activationRequest(
forExtensionWithIdentifier: extensionIdentifier,
queue: .main
)
request.delegate = self
OSSystemExtensionManager.shared.submitRequest(request)
}
func request(_ request: OSSystemExtensionRequest,
didFailWithError error: Error) {
let nsError = error as NSError
print("Activation failed:", nsError)
}
func request(_ request: OSSystemExtensionRequest,
didFinishWithResult result: OSSystemExtensionRequest.Result) {
print("Result:", result.rawValue)
}
}
Runtime behavior on a clean Mac (no MDM)
config.plist is created under /Library/Application Support/NetShield (via a root shell script).
A daemon runs, contacts our backend, and writes /Library/Application Support/NetShield/state.plist with a valid dnsToken and other fields.
The app NetShieldProxy.app is installed via a notarized, stapled Developer ID .pkg.
The extension bundle is present at:
/Applications/NetShieldProxy.app/Contents/Library/SystemExtensions/com.example.agent.NetShieldProxy.dnsProxy.systemextension.
When I press Activate DNS Proxy in the UI, I see in the unified log:
text
NetShieldProxy: [com.example.agent:SystemExtensionActivator] Requesting activation for system extension: com.example.agent.NetShieldProxy.dnsProxy
NetShieldProxy: [com.example.agent:SystemExtensionActivator] SystemExtensionActivator - activation failed: extension category returned error (domain=OSSystemExtensionErrorDomain code=9)
NetShieldProxy: [com.example.agent:SystemExtensionActivator] SystemExtensionActivator - OSSystemExtensionError code enum: 9
NetShieldProxy: [com.example.agent:SystemExtensionActivator] SystemExtensionActivator - validationFailed
And:
bash
systemextensionsctl list
-> 0 extension(s)
There is no prompt in Privacy & Security on this clean Mac.
Question
Given:
The extension is packaged as a system extension (CFBundlePackageType = SYSX) with NSExtensionPointIdentifier = "com.apple.dns-proxy".
Host and extension share the same Team ID and Developer ID Application cert.
Entitlements on the target machine match the provisioning profile and Apple’s docs for DNS Proxy system extensions (dns-proxy-systemextension).
This is happening on a clean Mac with no MDM profiles at all.
What are the likely reasons for OSSystemExtensionErrorDomain error 9 (validationFailed) with "extension category returned error" in this DNS Proxy system extension scenario?
Is there any additional configuration required for DNS Proxy system extensions (beyond entitlements and Info.plist) that could trigger this category-level validation failure?
Any guidance or examples of a working DNS Proxy system extension configuration (host entitlements + extension Info.plist + entitlements) would be greatly appreciated.
Thanks!
We have an application which is written in Swift, which activates Transparent Proxy network extension. Our Transparent Proxy module is a system extension, which is exposing an app proxy provider interface (We are using NETransparentProxyProvider class and in extension’s Info.plist we use com.apple.networkextension.app-proxy key.)
Sometimes we have observed that on starting the transparent proxy extension, the startProxy overridden delegate method(override func startProxy(options: [String : Any]?, completionHandler: @escaping (Error?) -> Void) {) in our AppProxyProvider class which is derived from NETransparentProxyProvider being called more than once for the same extension(pid). This leads to Over-resume of an object crash as we invoke setTunnelNetworkSettings inside the startProxy delegate.
As this is controlled by the system, we are not sure why this delegate being called more than once sometimes even if we call the start TransparentProxy only once.
Note that, this issue is seen only in Mac OS Tahoe.
Apple Feedback ticket: FB21464147 (startProxy delegate for NETransparentProxyProvider class is being called twice for the same pid extension sometimes)
Hi everyone,
Has anyone seen notarization behave like this?
We have one specific app (let’s call it App A) with a Network Extension system extension. Whenever we submit App A for notarization:
• Its submission stays “In Progress” indefinitely
• The provisioning profile for its system extension becomes Invalid on its own
• All our other apps suddenly fail notarization
• And the whole team immediately gets:
StatusCode 7000 – “Team is not yet configured for notarization.”
Apple Support restored notarization once(Case 102738171569), and we confirmed other apps notarize fine — until we submit App A again, which instantly triggers the same team-wide block. This cycle has repeated twice.
We verified:
• Hardened runtime
• Proper system extension signing
• No private API usage
• No get-task-allow
• No ATS violations
What’s confusing is that this doesn’t look like a normal notarization rejection. Normal failures don’t invalidate provisioning profiles or disable notarization for the entire team. It feels more like an automated security heuristic or misclassification.
My questions:
1. Can a single app or system extension trigger an automated team-wide notarization disable?
2. Can an entitlement or NE configuration issue cause StatusCode 7000 instead of a standard rejection?
3. If this could be a false positive, is there a specific team at Apple who can manually review/clear it?
Any insight would be greatly appreciated.
Hi all,
I have a working macOS (Intel) system extension app that currently uses only a Content Filter (NEFilterDataProvider). I need to capture/log HTTP and HTTPS traffic in plain text, and I understand NETransparentProxyProvider is the right extension type for that.
For HTTPS I will need TLS inspection / a MITM proxy — I’m new to that and unsure how complex it will be.
For DNS data (in plain text), can I use the same extension, or do I need a separate extension type such as NEPacketTunnelProvider, NEFilterPacketProvider, or NEDNSProxyProvider?
Current architecture:
Two Xcode targets: MainApp and a SystemExtension target.
The SystemExtension target contains multiple network extension types.
MainApp ↔ SystemExtension communicate via a bidirectional NSXPC connection.
I can already enable two extensions (Content Filter and TransparentProxy). With the NETransparentProxy, I still need to implement HTTPS capture.
Questions I’d appreciate help with:
Can NETransparentProxy capture the DNS fields I need (dns_hostname, dns_query_type, dns_response_code, dns_answer_number, etc.), or do I need an additional extension type to capture DNS in plain text?
If a separate extension is required, is it possible or problematic to include that extension type (Packet Tunnel / DNS Proxy / etc.) in the same SystemExtension Xcode target as the TransparentProxy?
Any recommended resources or guidance on TLS inspection / MITM proxy setup for capturing HTTPS logs?
There are multiple DNS transport types — am I correct that capturing DNS over UDP (port 53) is not necessarily sufficient? Which DNS types should I plan to handle?
I’ve read that TransparentProxy and other extension types (e.g., Packet Tunnel) cannot coexist in the same Xcode target. Is that true?
Best approach for delivering logs from multiple extensions to the main app (is it feasible)? Or what’s the best way to capture logs so an external/independent process (or C/C++ daemon) can consume them?
Required data to capture (not limited to):
All HTTP/HTTPS (request, body, URL, response, etc.)
DNS fields: dns_hostname, dns_query_type, dns_response_code, dns_answer_number, and other DNS data — all in plain text.
I’ve read various resources but remain unclear which extension(s) to use and whether multiple extension types can be combined in one Xcode target. Please ask if you need more details.
Thank you.
Topic:
App & System Services
SubTopic:
Networking
Tags:
Swift
Frameworks
Network Extension
System Extensions
Hi everyone,
I’m developing an Electron application on macOS and I’m trying to register and activate a macOS System Extension, but I’m running into startup and entitlement issues.
🔧 What I’m trying to build
• An Electron app packaged with electron-builder
• Signed with Developer ID Application
• Notarized using @electron/notarize
• A macOS System Extension is already built and signed
• The System Extension provides a virtual camera
• I wrote a Swift helper that:
• Registers / activates the virtual camera
• Calls OSSystemExtensionManager
• This Swift code is compiled into a .node native addon
• The .node module is loaded and called from Electron (Node.js) to trigger system extension registration
❗ The problem
When I add the following entitlement:
com.apple.developer.system-extension.install
the application fails to launch at all on macOS.
Without this entitlement:
• The app launches normally
• But system extension activation fails with:
Error Domain=OSSystemExtensionErrorDomain
Code=2
Missing entitlement com.apple.developer.system-extension.install
With this entitlement:
• The app does not launch
• No UI is shown
• macOS blocks execution silently
🤔 My questions
1. Is it valid for an Electron app’s main executable to have com.apple.developer.system-extension.install?
2. Does Apple require a separate helper / launcher app to install system extensions instead of the Electron main app?
3. Are there any Electron-specific limitations when working with macOS System Extensions?
4. Is there a known working example of Electron + macOS System Extension?
5. Do I need a specific provisioning profile or App ID capability beyond Developer ID + notarization?
This is just an FYI in case someone else runs into this problem.
This afternoon (12 Dec 2025), I updated to macOS 26.2 and lost my network.
The System Settings' Wi-Fi light was green and said it was connected, but traceroute showed "No route to host".
I turned Wi-Fi on & off.
I rebooted the Mac.
I rebooted the eero network.
I switched to tethering to my iPhone.
I switched to physical ethernet cable.
Nothing worked.
Then I remembered I had a beta of an app with a network system extension that was distributed through TestFlight.
I deleted the app, and networking came right back.
I had this same problem ~2 years ago. Same story:
app with network system extension + TestFlight + macOS update = lost network.
(My TestFlight build might have expired, but I'm not certain)
I don't know if anyone else has had this problem, but I thought I'd share this in case it helps.