diff --git a/Coder-Desktop/Coder-Desktop/Coder_DesktopApp.swift b/Coder-Desktop/Coder-Desktop/Coder_DesktopApp.swift
index 307e0797..35aed082 100644
--- a/Coder-Desktop/Coder-Desktop/Coder_DesktopApp.swift
+++ b/Coder-Desktop/Coder-Desktop/Coder_DesktopApp.swift
@@ -25,6 +25,7 @@ struct DesktopApp: App {
             SettingsView<CoderVPNService>()
                 .environmentObject(appDelegate.vpn)
                 .environmentObject(appDelegate.state)
+                .environmentObject(appDelegate.helper)
         }
         .windowResizability(.contentSize)
         Window("Coder File Sync", id: Windows.fileSync.rawValue) {
@@ -45,10 +46,12 @@ class AppDelegate: NSObject, NSApplicationDelegate {
     let fileSyncDaemon: MutagenDaemon
     let urlHandler: URLHandler
     let notifDelegate: NotifDelegate
+    let helper: HelperService
 
     override init() {
         notifDelegate = NotifDelegate()
         vpn = CoderVPNService()
+        helper = HelperService()
         let state = AppState(onChange: vpn.configureTunnelProviderProtocol)
         vpn.onStart = {
             // We don't need this to have finished before the VPN actually starts
diff --git a/Coder-Desktop/Coder-Desktop/HelperService.swift b/Coder-Desktop/Coder-Desktop/HelperService.swift
new file mode 100644
index 00000000..17bdc72a
--- /dev/null
+++ b/Coder-Desktop/Coder-Desktop/HelperService.swift
@@ -0,0 +1,117 @@
+import os
+import ServiceManagement
+
+// Whilst the GUI app installs the helper, the System Extension communicates
+// with it over XPC
+@MainActor
+class HelperService: ObservableObject {
+    private let logger = Logger(subsystem: Bundle.main.bundleIdentifier!, category: "HelperService")
+    let plistName = "com.coder.Coder-Desktop.Helper.plist"
+    @Published var state: HelperState = .uninstalled {
+        didSet {
+            logger.info("helper daemon state set: \(self.state.description, privacy: .public)")
+        }
+    }
+
+    init() {
+        update()
+    }
+
+    func update() {
+        let daemon = SMAppService.daemon(plistName: plistName)
+        state = HelperState(status: daemon.status)
+    }
+
+    func install() {
+        let daemon = SMAppService.daemon(plistName: plistName)
+        do {
+            try daemon.register()
+        } catch let error as NSError {
+            self.state = .failed(.init(error: error))
+        } catch {
+            state = .failed(.unknown(error.localizedDescription))
+        }
+        state = HelperState(status: daemon.status)
+    }
+
+    func uninstall() {
+        let daemon = SMAppService.daemon(plistName: plistName)
+        do {
+            try daemon.unregister()
+        } catch let error as NSError {
+            self.state = .failed(.init(error: error))
+        } catch {
+            state = .failed(.unknown(error.localizedDescription))
+        }
+        state = HelperState(status: daemon.status)
+    }
+}
+
+enum HelperState: Equatable {
+    case uninstalled
+    case installed
+    case requiresApproval
+    case failed(HelperError)
+
+    var description: String {
+        switch self {
+        case .uninstalled:
+            "Uninstalled"
+        case .installed:
+            "Installed"
+        case .requiresApproval:
+            "Requires Approval"
+        case let .failed(error):
+            "Failed: \(error.localizedDescription)"
+        }
+    }
+
+    init(status: SMAppService.Status) {
+        self = switch status {
+        case .notRegistered:
+            .uninstalled
+        case .enabled:
+            .installed
+        case .requiresApproval:
+            .requiresApproval
+        case .notFound:
+            // `Not found`` is the initial state, if `register` has never been called
+            .uninstalled
+        @unknown default:
+            .failed(.unknown("Unknown status: \(status)"))
+        }
+    }
+}
+
+enum HelperError: Error, Equatable {
+    case alreadyRegistered
+    case launchDeniedByUser
+    case invalidSignature
+    case unknown(String)
+
+    init(error: NSError) {
+        self = switch error.code {
+        case kSMErrorAlreadyRegistered:
+            .alreadyRegistered
+        case kSMErrorLaunchDeniedByUser:
+            .launchDeniedByUser
+        case kSMErrorInvalidSignature:
+            .invalidSignature
+        default:
+            .unknown(error.localizedDescription)
+        }
+    }
+
+    var localizedDescription: String {
+        switch self {
+        case .alreadyRegistered:
+            "Already registered"
+        case .launchDeniedByUser:
+            "Launch denied by user"
+        case .invalidSignature:
+            "Invalid signature"
+        case let .unknown(message):
+            message
+        }
+    }
+}
diff --git a/Coder-Desktop/Coder-Desktop/Views/Settings/ExperimentalTab.swift b/Coder-Desktop/Coder-Desktop/Views/Settings/ExperimentalTab.swift
new file mode 100644
index 00000000..838f4587
--- /dev/null
+++ b/Coder-Desktop/Coder-Desktop/Views/Settings/ExperimentalTab.swift
@@ -0,0 +1,10 @@
+import LaunchAtLogin
+import SwiftUI
+
+struct ExperimentalTab: View {
+    var body: some View {
+        Form {
+            HelperSection()
+        }.formStyle(.grouped)
+    }
+}
diff --git a/Coder-Desktop/Coder-Desktop/Views/Settings/HelperSection.swift b/Coder-Desktop/Coder-Desktop/Views/Settings/HelperSection.swift
new file mode 100644
index 00000000..66fdc534
--- /dev/null
+++ b/Coder-Desktop/Coder-Desktop/Views/Settings/HelperSection.swift
@@ -0,0 +1,82 @@
+import LaunchAtLogin
+import ServiceManagement
+import SwiftUI
+
+struct HelperSection: View {
+    var body: some View {
+        Section {
+            HelperButton()
+            Text("""
+            Coder Connect executes a dynamic library downloaded from the Coder deployment.
+            Administrator privileges are required when executing a copy of this library for the first time.
+            Without this helper, these are granted by the user entering their password.
+            With this helper, this is done automatically.
+            This is useful if the Coder deployment updates frequently.
+
+            Coder Desktop will not execute code unless it has been signed by Coder.
+            """)
+            .font(.subheadline)
+            .foregroundColor(.secondary)
+        }
+    }
+}
+
+struct HelperButton: View {
+    @EnvironmentObject var helperService: HelperService
+
+    var buttonText: String {
+        switch helperService.state {
+        case .uninstalled, .failed:
+            "Install"
+        case .installed:
+            "Uninstall"
+        case .requiresApproval:
+            "Open Settings"
+        }
+    }
+
+    var buttonDescription: String {
+        switch helperService.state {
+        case .uninstalled, .installed:
+            ""
+        case .requiresApproval:
+            "Requires approval"
+        case let .failed(err):
+            err.localizedDescription
+        }
+    }
+
+    func buttonAction() {
+        switch helperService.state {
+        case .uninstalled, .failed:
+            helperService.install()
+            if helperService.state == .requiresApproval {
+                SMAppService.openSystemSettingsLoginItems()
+            }
+        case .installed:
+            helperService.uninstall()
+        case .requiresApproval:
+            SMAppService.openSystemSettingsLoginItems()
+        }
+    }
+
+    var body: some View {
+        HStack {
+            Text("Privileged Helper")
+            Spacer()
+            Text(buttonDescription)
+                .foregroundColor(.secondary)
+            Button(action: buttonAction) {
+                Text(buttonText)
+            }
+        }.onReceive(NotificationCenter.default.publisher(for: NSApplication.didBecomeActiveNotification)) { _ in
+            helperService.update()
+        }.onAppear {
+            helperService.update()
+        }
+    }
+}
+
+#Preview {
+    HelperSection().environmentObject(HelperService())
+}
diff --git a/Coder-Desktop/Coder-Desktop/Views/Settings/Settings.swift b/Coder-Desktop/Coder-Desktop/Views/Settings/Settings.swift
index 8aac9a0c..170d171b 100644
--- a/Coder-Desktop/Coder-Desktop/Views/Settings/Settings.swift
+++ b/Coder-Desktop/Coder-Desktop/Views/Settings/Settings.swift
@@ -13,6 +13,11 @@ struct SettingsView<VPN: VPNService>: View {
                 .tabItem {
                     Label("Network", systemImage: "dot.radiowaves.left.and.right")
                 }.tag(SettingsTab.network)
+            ExperimentalTab()
+                .tabItem {
+                    Label("Experimental", systemImage: "gearshape.2")
+                }.tag(SettingsTab.experimental)
+
         }.frame(width: 600)
             .frame(maxHeight: 500)
             .scrollContentBackground(.hidden)
@@ -23,4 +28,5 @@ struct SettingsView<VPN: VPNService>: View {
 enum SettingsTab: Int {
     case general
     case network
+    case experimental
 }
diff --git a/Coder-Desktop/Coder-Desktop/XPCInterface.swift b/Coder-Desktop/Coder-Desktop/XPCInterface.swift
index 43c6f09b..e21be86f 100644
--- a/Coder-Desktop/Coder-Desktop/XPCInterface.swift
+++ b/Coder-Desktop/Coder-Desktop/XPCInterface.swift
@@ -14,9 +14,9 @@ import VPNLib
     }
 
     func connect() {
-        logger.debug("xpc connect called")
+        logger.debug("VPN xpc connect called")
         guard xpc == nil else {
-            logger.debug("xpc already exists")
+            logger.debug("VPN xpc already exists")
             return
         }
         let networkExtDict = Bundle.main.object(forInfoDictionaryKey: "NetworkExtension") as? [String: Any]
@@ -34,14 +34,14 @@ import VPNLib
         xpcConn.exportedObject = self
         xpcConn.invalidationHandler = { [logger] in
             Task { @MainActor in
-                logger.error("XPC connection invalidated.")
+                logger.error("VPN XPC connection invalidated.")
                 self.xpc = nil
                 self.connect()
             }
         }
         xpcConn.interruptionHandler = { [logger] in
             Task { @MainActor in
-                logger.error("XPC connection interrupted.")
+                logger.error("VPN XPC connection interrupted.")
                 self.xpc = nil
                 self.connect()
             }
diff --git a/Coder-Desktop/Coder-DesktopHelper/HelperXPCProtocol.swift b/Coder-Desktop/Coder-DesktopHelper/HelperXPCProtocol.swift
new file mode 100644
index 00000000..5ffed59a
--- /dev/null
+++ b/Coder-Desktop/Coder-DesktopHelper/HelperXPCProtocol.swift
@@ -0,0 +1,5 @@
+import Foundation
+
+@objc protocol HelperXPCProtocol {
+    func removeQuarantine(path: String, withReply reply: @escaping (Int32, String) -> Void)
+}
diff --git a/Coder-Desktop/Coder-DesktopHelper/com.coder.Coder-Desktop.Helper.plist b/Coder-Desktop/Coder-DesktopHelper/com.coder.Coder-Desktop.Helper.plist
new file mode 100644
index 00000000..c00eed40
--- /dev/null
+++ b/Coder-Desktop/Coder-DesktopHelper/com.coder.Coder-Desktop.Helper.plist
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>Label</key>
+	<string>com.coder.Coder-Desktop.Helper</string>
+	<key>BundleProgram</key>
+	<string>Contents/MacOS/com.coder.Coder-Desktop.Helper</string>
+	<key>MachServices</key>
+	<dict>
+		<!-- $(TeamIdentifierPrefix) isn't populated here, so this value is hardcoded -->
+		<key>4399GN35BJ.com.coder.Coder-Desktop.Helper</key>
+		<true/>
+	</dict>
+	<key>AssociatedBundleIdentifiers</key>
+	<array>
+		<string>com.coder.Coder-Desktop</string>
+	</array>
+</dict>
+</plist>
diff --git a/Coder-Desktop/Coder-DesktopHelper/main.swift b/Coder-Desktop/Coder-DesktopHelper/main.swift
new file mode 100644
index 00000000..0e94af21
--- /dev/null
+++ b/Coder-Desktop/Coder-DesktopHelper/main.swift
@@ -0,0 +1,72 @@
+import Foundation
+import os
+
+class HelperToolDelegate: NSObject, NSXPCListenerDelegate, HelperXPCProtocol {
+    private var logger = Logger(subsystem: Bundle.main.bundleIdentifier!, category: "HelperToolDelegate")
+
+    override init() {
+        super.init()
+    }
+
+    func listener(_: NSXPCListener, shouldAcceptNewConnection newConnection: NSXPCConnection) -> Bool {
+        newConnection.exportedInterface = NSXPCInterface(with: HelperXPCProtocol.self)
+        newConnection.exportedObject = self
+        newConnection.invalidationHandler = { [weak self] in
+            self?.logger.info("Helper XPC connection invalidated")
+        }
+        newConnection.interruptionHandler = { [weak self] in
+            self?.logger.debug("Helper XPC connection interrupted")
+        }
+        logger.info("new active connection")
+        newConnection.resume()
+        return true
+    }
+
+    func removeQuarantine(path: String, withReply reply: @escaping (Int32, String) -> Void) {
+        guard isCoderDesktopDylib(at: path) else {
+            reply(1, "Path is not to a Coder Desktop dylib: \(path)")
+            return
+        }
+
+        let task = Process()
+        let pipe = Pipe()
+
+        task.standardOutput = pipe
+        task.standardError = pipe
+        task.arguments = ["-d", "com.apple.quarantine", path]
+        task.executableURL = URL(fileURLWithPath: "/usr/bin/xattr")
+
+        do {
+            try task.run()
+        } catch {
+            reply(1, "Failed to start command: \(error)")
+            return
+        }
+
+        let data = pipe.fileHandleForReading.readDataToEndOfFile()
+        let output = String(data: data, encoding: .utf8) ?? ""
+
+        task.waitUntilExit()
+        reply(task.terminationStatus, output)
+    }
+}
+
+func isCoderDesktopDylib(at rawPath: String) -> Bool {
+    let url = URL(fileURLWithPath: rawPath)
+        .standardizedFileURL
+        .resolvingSymlinksInPath()
+
+    // *Must* be within the Coder Desktop System Extension sandbox
+    let requiredPrefix = ["/", "var", "root", "Library", "Containers",
+                          "com.coder.Coder-Desktop.VPN"]
+    guard url.pathComponents.starts(with: requiredPrefix) else { return false }
+    guard url.pathExtension.lowercased() == "dylib" else { return false }
+    guard FileManager.default.fileExists(atPath: url.path) else { return false }
+    return true
+}
+
+let delegate = HelperToolDelegate()
+let listener = NSXPCListener(machServiceName: "4399GN35BJ.com.coder.Coder-Desktop.Helper")
+listener.delegate = delegate
+listener.resume()
+RunLoop.main.run()
diff --git a/Coder-Desktop/VPN/AppXPCListener.swift b/Coder-Desktop/VPN/AppXPCListener.swift
new file mode 100644
index 00000000..3d77f01e
--- /dev/null
+++ b/Coder-Desktop/VPN/AppXPCListener.swift
@@ -0,0 +1,43 @@
+import Foundation
+import NetworkExtension
+import os
+import VPNLib
+
+final class AppXPCListener: NSObject, NSXPCListenerDelegate, @unchecked Sendable {
+    let vpnXPCInterface = XPCInterface()
+    private var activeConnection: NSXPCConnection?
+    private var connMutex: NSLock = .init()
+
+    var conn: VPNXPCClientCallbackProtocol? {
+        connMutex.lock()
+        defer { connMutex.unlock() }
+
+        let conn = activeConnection?.remoteObjectProxy as? VPNXPCClientCallbackProtocol
+        return conn
+    }
+
+    func setActiveConnection(_ connection: NSXPCConnection?) {
+        connMutex.lock()
+        defer { connMutex.unlock() }
+        activeConnection = connection
+    }
+
+    func listener(_: NSXPCListener, shouldAcceptNewConnection newConnection: NSXPCConnection) -> Bool {
+        newConnection.exportedInterface = NSXPCInterface(with: VPNXPCProtocol.self)
+        newConnection.exportedObject = vpnXPCInterface
+        newConnection.remoteObjectInterface = NSXPCInterface(with: VPNXPCClientCallbackProtocol.self)
+        newConnection.invalidationHandler = { [weak self] in
+            logger.info("active connection dead")
+            self?.setActiveConnection(nil)
+        }
+        newConnection.interruptionHandler = { [weak self] in
+            logger.debug("connection interrupted")
+            self?.setActiveConnection(nil)
+        }
+        logger.info("new active connection")
+        setActiveConnection(newConnection)
+
+        newConnection.resume()
+        return true
+    }
+}
diff --git a/Coder-Desktop/VPN/HelperXPCSpeaker.swift b/Coder-Desktop/VPN/HelperXPCSpeaker.swift
new file mode 100644
index 00000000..77de1f3a
--- /dev/null
+++ b/Coder-Desktop/VPN/HelperXPCSpeaker.swift
@@ -0,0 +1,55 @@
+import Foundation
+import os
+
+final class HelperXPCSpeaker: @unchecked Sendable {
+    private var logger = Logger(subsystem: Bundle.main.bundleIdentifier!, category: "HelperXPCSpeaker")
+    private var connection: NSXPCConnection?
+
+    func tryRemoveQuarantine(path: String) async -> Bool {
+        let conn = connect()
+        return await withCheckedContinuation { continuation in
+            guard let proxy = conn.remoteObjectProxyWithErrorHandler({ err in
+                self.logger.error("Failed to connect to HelperXPC \(err)")
+                continuation.resume(returning: false)
+            }) as? HelperXPCProtocol else {
+                self.logger.error("Failed to get proxy for HelperXPC")
+                continuation.resume(returning: false)
+                return
+            }
+            proxy.removeQuarantine(path: path) { status, output in
+                if status == 0 {
+                    self.logger.info("Successfully removed quarantine for \(path)")
+                    continuation.resume(returning: true)
+                } else {
+                    self.logger.error("Failed to remove quarantine for \(path): \(output)")
+                    continuation.resume(returning: false)
+                }
+            }
+        }
+    }
+
+    private func connect() -> NSXPCConnection {
+        if let connection = self.connection {
+            return connection
+        }
+
+        // Though basically undocumented, System Extensions can communicate with
+        // LaunchDaemons over XPC if the machServiceName used is prefixed with
+        // the team identifier.
+        // https://developer.apple.com/forums/thread/654466
+        let connection = NSXPCConnection(
+            machServiceName: "4399GN35BJ.com.coder.Coder-Desktop.Helper",
+            options: .privileged
+        )
+        connection.remoteObjectInterface = NSXPCInterface(with: HelperXPCProtocol.self)
+        connection.invalidationHandler = { [weak self] in
+            self?.connection = nil
+        }
+        connection.interruptionHandler = { [weak self] in
+            self?.connection = nil
+        }
+        connection.resume()
+        self.connection = connection
+        return connection
+    }
+}
diff --git a/Coder-Desktop/VPN/Manager.swift b/Coder-Desktop/VPN/Manager.swift
index b9573810..bc441acd 100644
--- a/Coder-Desktop/VPN/Manager.swift
+++ b/Coder-Desktop/VPN/Manager.swift
@@ -312,7 +312,14 @@ private func removeQuarantine(_ dest: URL) async throws(ManagerError) {
     let file = NSURL(fileURLWithPath: dest.path)
     try? file.getResourceValue(&flag, forKey: kCFURLQuarantinePropertiesKey as URLResourceKey)
     if flag != nil {
+        // Try the privileged helper first (it may not even be registered)
+        if await globalHelperXPCSpeaker.tryRemoveQuarantine(path: dest.path) {
+            // Success!
+            return
+        }
+        // Then try the app
         guard let conn = globalXPCListenerDelegate.conn else {
+            // If neither are available, we can't execute the dylib
             throw .noApp
         }
         // Wait for unsandboxed app to accept our file
diff --git a/Coder-Desktop/VPN/main.swift b/Coder-Desktop/VPN/main.swift
index 708c2e0c..bf6c371a 100644
--- a/Coder-Desktop/VPN/main.swift
+++ b/Coder-Desktop/VPN/main.swift
@@ -5,45 +5,6 @@ import VPNLib
 
 let logger = Logger(subsystem: Bundle.main.bundleIdentifier!, category: "provider")
 
-final class XPCListenerDelegate: NSObject, NSXPCListenerDelegate, @unchecked Sendable {
-    let vpnXPCInterface = XPCInterface()
-    private var activeConnection: NSXPCConnection?
-    private var connMutex: NSLock = .init()
-
-    var conn: VPNXPCClientCallbackProtocol? {
-        connMutex.lock()
-        defer { connMutex.unlock() }
-
-        let conn = activeConnection?.remoteObjectProxy as? VPNXPCClientCallbackProtocol
-        return conn
-    }
-
-    func setActiveConnection(_ connection: NSXPCConnection?) {
-        connMutex.lock()
-        defer { connMutex.unlock() }
-        activeConnection = connection
-    }
-
-    func listener(_: NSXPCListener, shouldAcceptNewConnection newConnection: NSXPCConnection) -> Bool {
-        newConnection.exportedInterface = NSXPCInterface(with: VPNXPCProtocol.self)
-        newConnection.exportedObject = vpnXPCInterface
-        newConnection.remoteObjectInterface = NSXPCInterface(with: VPNXPCClientCallbackProtocol.self)
-        newConnection.invalidationHandler = { [weak self] in
-            logger.info("active connection dead")
-            self?.setActiveConnection(nil)
-        }
-        newConnection.interruptionHandler = { [weak self] in
-            logger.debug("connection interrupted")
-            self?.setActiveConnection(nil)
-        }
-        logger.info("new active connection")
-        setActiveConnection(newConnection)
-
-        newConnection.resume()
-        return true
-    }
-}
-
 guard
     let netExt = Bundle.main.object(forInfoDictionaryKey: "NetworkExtension") as? [String: Any],
     let serviceName = netExt["NEMachServiceName"] as? String
@@ -57,9 +18,11 @@ autoreleasepool {
     NEProvider.startSystemExtensionMode()
 }
 
-let globalXPCListenerDelegate = XPCListenerDelegate()
+let globalXPCListenerDelegate = AppXPCListener()
 let xpcListener = NSXPCListener(machServiceName: serviceName)
 xpcListener.delegate = globalXPCListenerDelegate
 xpcListener.resume()
 
+let globalHelperXPCSpeaker = HelperXPCSpeaker()
+
 dispatchMain()
diff --git a/Coder-Desktop/VPNLib/FileSync/FileSyncDaemon.swift b/Coder-Desktop/VPNLib/FileSync/FileSyncDaemon.swift
index 98807e3a..d4b36065 100644
--- a/Coder-Desktop/VPNLib/FileSync/FileSyncDaemon.swift
+++ b/Coder-Desktop/VPNLib/FileSync/FileSyncDaemon.swift
@@ -32,7 +32,7 @@ public class MutagenDaemon: FileSyncDaemon {
 
     @Published public var state: DaemonState = .stopped {
         didSet {
-            logger.info("daemon state set: \(self.state.description, privacy: .public)")
+            logger.info("mutagen daemon state set: \(self.state.description, privacy: .public)")
             if case .failed = state {
                 Task {
                     try? await cleanupGRPC()
diff --git a/Coder-Desktop/project.yml b/Coder-Desktop/project.yml
index f2c96fac..9455a44a 100644
--- a/Coder-Desktop/project.yml
+++ b/Coder-Desktop/project.yml
@@ -139,6 +139,13 @@ targets:
       - path: Coder-Desktop
       - path: Resources
         buildPhase: resources
+      - path: Coder-DesktopHelper/com.coder.Coder-Desktop.Helper.plist
+        attributes:
+          - CodeSignOnCopy
+        buildPhase:
+          copyFiles:
+            destination: wrapper
+            subpath: Contents/Library/LaunchDaemons
     entitlements:
       path: Coder-Desktop/Coder-Desktop.entitlements
       properties:
@@ -185,6 +192,11 @@ targets:
         embed: false # Loaded from SE bundle
       - target: VPN
         embed: without-signing # Embed without signing.
+      - target: Coder-DesktopHelper
+        embed: true
+        codeSign: true
+        copy:
+          destination: executables
       - package: FluidMenuBarExtra
       - package: KeychainAccess
       - package: LaunchAtLogin
@@ -235,6 +247,7 @@ targets:
     platform: macOS
     sources:
       - path: VPN
+      - path: Coder-DesktopHelper/HelperXPCProtocol.swift
     entitlements:
       path: VPN/VPN.entitlements
       properties:
@@ -347,3 +360,15 @@ targets:
       base:
         TEST_HOST: "$(BUILT_PRODUCTS_DIR)/Coder Desktop.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Coder Desktop"
         PRODUCT_BUNDLE_IDENTIFIER: com.coder.Coder-Desktop.CoderSDKTests
+
+  Coder-DesktopHelper:
+    type: tool
+    platform: macOS
+    sources: Coder-DesktopHelper
+    settings:
+      base:
+        ENABLE_HARDENED_RUNTIME: YES
+        PRODUCT_BUNDLE_IDENTIFIER: "com.coder.Coder-Desktop.Helper"
+        PRODUCT_MODULE_NAME: "$(PRODUCT_NAME:c99extidentifier)"
+        PRODUCT_NAME: "$(PRODUCT_BUNDLE_IDENTIFIER)"
+        SKIP_INSTALL: YES
\ No newline at end of file