From b333965cdb41c07d8ab82cfb94590d6e008facd2 Mon Sep 17 00:00:00 2001
From: Ethan Dickson <ethan@coder.com>
Date: Thu, 16 Jan 2025 18:34:02 +1100
Subject: [PATCH 1/4] feat: support user-supplied literal headers

---
 .../Coder Desktop.xcodeproj/project.pbxproj   | 174 ++++++++++++++----
 .../xcshareddata/swiftpm/Package.resolved     |  24 ++-
 Coder Desktop/Coder Desktop/About.swift       |  10 +-
 .../Coder Desktop/Coder_DesktopApp.swift      |  23 ++-
 .../{Session.swift => State.swift}            |  46 +++++
 .../Coder Desktop/Views/LoginForm.swift       |   3 +-
 .../Views/Settings/GeneralTab.swift           |  16 ++
 .../Views/Settings/LiteralHeaderModal.swift   |  45 +++++
 .../Settings/LiteralHeadersSection.swift      |  71 +++++++
 .../Views/Settings/NetworkTab.swift           |  14 ++
 .../Views/Settings/Settings.swift             |  26 +++
 Coder Desktop/Coder Desktop/Views/Util.swift  |   1 +
 .../Coder Desktop/Views/VPNMenu.swift         |   8 +
 Coder Desktop/Coder Desktop/Windows.swift     |  10 +-
 .../LiteralHeadersSettingTests.swift          |  48 +++++
 .../Coder DesktopTests/LoginFormTests.swift   |   9 +-
 Makefile                                      |   3 +-
 17 files changed, 467 insertions(+), 64 deletions(-)
 rename Coder Desktop/Coder Desktop/{Session.swift => State.swift} (70%)
 create mode 100644 Coder Desktop/Coder Desktop/Views/Settings/GeneralTab.swift
 create mode 100644 Coder Desktop/Coder Desktop/Views/Settings/LiteralHeaderModal.swift
 create mode 100644 Coder Desktop/Coder Desktop/Views/Settings/LiteralHeadersSection.swift
 create mode 100644 Coder Desktop/Coder Desktop/Views/Settings/NetworkTab.swift
 create mode 100644 Coder Desktop/Coder Desktop/Views/Settings/Settings.swift
 create mode 100644 Coder Desktop/Coder DesktopTests/LiteralHeadersSettingTests.swift

diff --git a/Coder Desktop/Coder Desktop.xcodeproj/project.pbxproj b/Coder Desktop/Coder Desktop.xcodeproj/project.pbxproj
index 6a267239..1f36fec3 100644
--- a/Coder Desktop/Coder Desktop.xcodeproj/project.pbxproj	
+++ b/Coder Desktop/Coder Desktop.xcodeproj/project.pbxproj	
@@ -9,6 +9,7 @@
 /* Begin PBXBuildFile section */
 		961679332CFF117300B2B6DF /* NetworkExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 961679322CFF117300B2B6DF /* NetworkExtension.framework */; };
 		9616793D2CFF117300B2B6DF /* com.coder.Coder-Desktop.VPN.systemextension in Embed System Extensions */ = {isa = PBXBuildFile; fileRef = 961679302CFF117300B2B6DF /* com.coder.Coder-Desktop.VPN.systemextension */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
+		AA2C690F2D34F6920059AFAF /* LaunchAtLogin in Frameworks */ = {isa = PBXBuildFile; productRef = AA2C690E2D34F6920059AFAF /* LaunchAtLogin */; };
 		AA3B3DA92D2D23860099996A /* VPNLib.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AA3B3DA12D2D23860099996A /* VPNLib.framework */; };
 		AA3B3DBF2D2D23AB0099996A /* SwiftProtobuf in Frameworks */ = {isa = PBXBuildFile; productRef = AA3B3DBE2D2D23AB0099996A /* SwiftProtobuf */; };
 		AA3B3DC12D2D23AB0099996A /* SwiftProtobufPluginLibrary in Frameworks */ = {isa = PBXBuildFile; productRef = AA3B3DC02D2D23AB0099996A /* SwiftProtobufPluginLibrary */; };
@@ -24,6 +25,7 @@
 		AA8BC3392D0060A900E1ABAA /* ViewInspector in Frameworks */ = {isa = PBXBuildFile; productRef = AA8BC3382D0060A900E1ABAA /* ViewInspector */; };
 		AA8BC33F2D0061F200E1ABAA /* FluidMenuBarExtra in Frameworks */ = {isa = PBXBuildFile; productRef = AA8BC33E2D0061F200E1ABAA /* FluidMenuBarExtra */; };
 		AA8BC4CF2D00A4B700E1ABAA /* KeychainAccess in Frameworks */ = {isa = PBXBuildFile; productRef = AA8BC4CE2D00A4B700E1ABAA /* KeychainAccess */; };
+		AA8EECF72D3A22320049DD09 /* SettingsAccess in Frameworks */ = {isa = PBXBuildFile; productRef = AA8EECF62D3A22320049DD09 /* SettingsAccess */; };
 /* End PBXBuildFile section */
 
 /* Begin PBXContainerItemProxy section */
@@ -229,7 +231,9 @@
 			files = (
 				AA3B40A42D2FC8560099996A /* CoderSDK.framework in Frameworks */,
 				AA8BC4CF2D00A4B700E1ABAA /* KeychainAccess in Frameworks */,
+				AA2C690F2D34F6920059AFAF /* LaunchAtLogin in Frameworks */,
 				AA8BC33F2D0061F200E1ABAA /* FluidMenuBarExtra in Frameworks */,
+				AA8EECF72D3A22320049DD09 /* SettingsAccess in Frameworks */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -368,7 +372,7 @@
 			buildRules = (
 			);
 			dependencies = (
-				AA8BC33C2D0060E700E1ABAA /* PBXTargetDependency */,
+				AA2C698C2D354A800059AFAF /* PBXTargetDependency */,
 				9616793C2CFF117300B2B6DF /* PBXTargetDependency */,
 				AA3B40A32D2FC8560099996A /* PBXTargetDependency */,
 			);
@@ -379,6 +383,8 @@
 			packageProductDependencies = (
 				AA8BC33E2D0061F200E1ABAA /* FluidMenuBarExtra */,
 				AA8BC4CE2D00A4B700E1ABAA /* KeychainAccess */,
+				AA2C690E2D34F6920059AFAF /* LaunchAtLogin */,
+				AA8EECF62D3A22320049DD09 /* SettingsAccess */,
 			);
 			productName = "Coder Desktop";
 			productReference = 961678FC2CFF100D00B2B6DF /* Coder Desktop.app */;
@@ -395,6 +401,7 @@
 			buildRules = (
 			);
 			dependencies = (
+				AA2C698E2D354A840059AFAF /* PBXTargetDependency */,
 				961679112CFF100E00B2B6DF /* PBXTargetDependency */,
 				AA3B40BA2D2FDA5C0099996A /* PBXTargetDependency */,
 			);
@@ -421,6 +428,7 @@
 			buildRules = (
 			);
 			dependencies = (
+				AA2C69902D354A880059AFAF /* PBXTargetDependency */,
 				9616791B2CFF100E00B2B6DF /* PBXTargetDependency */,
 			);
 			fileSystemSynchronizedGroups = (
@@ -445,6 +453,7 @@
 			buildRules = (
 			);
 			dependencies = (
+				AA2C69922D354A8B0059AFAF /* PBXTargetDependency */,
 				AA3B3DD02D2D249F0099996A /* PBXTargetDependency */,
 			);
 			fileSystemSynchronizedGroups = (
@@ -469,6 +478,7 @@
 			buildRules = (
 			);
 			dependencies = (
+				AA2C69942D354A8E0059AFAF /* PBXTargetDependency */,
 				AA3B40C32D2FE7760099996A /* PBXTargetDependency */,
 			);
 			fileSystemSynchronizedGroups = (
@@ -494,6 +504,7 @@
 			buildRules = (
 			);
 			dependencies = (
+				AA2C69962D354A910059AFAF /* PBXTargetDependency */,
 				AA3B3DAB2D2D23860099996A /* PBXTargetDependency */,
 				AA3B3DAD2D2D23860099996A /* PBXTargetDependency */,
 			);
@@ -520,6 +531,7 @@
 			buildRules = (
 			);
 			dependencies = (
+				AA2C69982D354A940059AFAF /* PBXTargetDependency */,
 			);
 			fileSystemSynchronizedGroups = (
 				AA3B40922D2FC8560099996A /* CoderSDK */,
@@ -542,6 +554,7 @@
 			buildRules = (
 			);
 			dependencies = (
+				AA2C699A2D354A970059AFAF /* PBXTargetDependency */,
 				AA3B409B2D2FC8560099996A /* PBXTargetDependency */,
 				AA3B409D2D2FC8560099996A /* PBXTargetDependency */,
 			);
@@ -607,11 +620,13 @@
 			minimizedProjectReferenceProxies = 1;
 			packageReferences = (
 				AA8BC3372D00609700E1ABAA /* XCRemoteSwiftPackageReference "ViewInspector" */,
-				AA8BC33A2D0060C500E1ABAA /* XCRemoteSwiftPackageReference "SwiftLintPlugins" */,
 				AA8BC33D2D0061F200E1ABAA /* XCRemoteSwiftPackageReference "fluid-menu-bar-extra" */,
 				AA8BC4CD2D00A4B700E1ABAA /* XCRemoteSwiftPackageReference "KeychainAccess" */,
 				961679512CFF207900B2B6DF /* XCRemoteSwiftPackageReference "swift-protobuf" */,
 				AA3B3E8A2D2E0FE10099996A /* XCRemoteSwiftPackageReference "Mocker" */,
+				AA2C690D2D34F6920059AFAF /* XCRemoteSwiftPackageReference "LaunchAtLogin-modern" */,
+				AA2C698A2D354A600059AFAF /* XCRemoteSwiftPackageReference "SwiftLintPlugins" */,
+				AA8EECF52D3A22320049DD09 /* XCRemoteSwiftPackageReference "SettingsAccess" */,
 			);
 			preferredProjectObjectVersion = 77;
 			productRefGroup = 961678FD2CFF100D00B2B6DF /* Products */;
@@ -764,6 +779,38 @@
 			target = 9616792F2CFF117300B2B6DF /* VPN */;
 			targetProxy = 9616793B2CFF117300B2B6DF /* PBXContainerItemProxy */;
 		};
+		AA2C698C2D354A800059AFAF /* PBXTargetDependency */ = {
+			isa = PBXTargetDependency;
+			productRef = AA2C698B2D354A800059AFAF /* SwiftLintBuildToolPlugin */;
+		};
+		AA2C698E2D354A840059AFAF /* PBXTargetDependency */ = {
+			isa = PBXTargetDependency;
+			productRef = AA2C698D2D354A840059AFAF /* SwiftLintBuildToolPlugin */;
+		};
+		AA2C69902D354A880059AFAF /* PBXTargetDependency */ = {
+			isa = PBXTargetDependency;
+			productRef = AA2C698F2D354A880059AFAF /* SwiftLintBuildToolPlugin */;
+		};
+		AA2C69922D354A8B0059AFAF /* PBXTargetDependency */ = {
+			isa = PBXTargetDependency;
+			productRef = AA2C69912D354A8B0059AFAF /* SwiftLintBuildToolPlugin */;
+		};
+		AA2C69942D354A8E0059AFAF /* PBXTargetDependency */ = {
+			isa = PBXTargetDependency;
+			productRef = AA2C69932D354A8E0059AFAF /* SwiftLintBuildToolPlugin */;
+		};
+		AA2C69962D354A910059AFAF /* PBXTargetDependency */ = {
+			isa = PBXTargetDependency;
+			productRef = AA2C69952D354A910059AFAF /* SwiftLintBuildToolPlugin */;
+		};
+		AA2C69982D354A940059AFAF /* PBXTargetDependency */ = {
+			isa = PBXTargetDependency;
+			productRef = AA2C69972D354A940059AFAF /* SwiftLintBuildToolPlugin */;
+		};
+		AA2C699A2D354A970059AFAF /* PBXTargetDependency */ = {
+			isa = PBXTargetDependency;
+			productRef = AA2C69992D354A970059AFAF /* SwiftLintBuildToolPlugin */;
+		};
 		AA3B3DAB2D2D23860099996A /* PBXTargetDependency */ = {
 			isa = PBXTargetDependency;
 			target = AA3B3DA02D2D23860099996A /* VPNLib */;
@@ -804,10 +851,6 @@
 			target = AA3B40902D2FC8560099996A /* CoderSDK */;
 			targetProxy = AA3B40C22D2FE7760099996A /* PBXContainerItemProxy */;
 		};
-		AA8BC33C2D0060E700E1ABAA /* PBXTargetDependency */ = {
-			isa = PBXTargetDependency;
-			productRef = AA8BC33B2D0060E700E1ABAA /* SwiftLintBuildToolPlugin */;
-		};
 /* End PBXTargetDependency section */
 
 /* Begin XCBuildConfiguration section */
@@ -955,7 +998,7 @@
 					"$(inherited)",
 					"@executable_path/../Frameworks",
 				);
-				MACOSX_DEPLOYMENT_TARGET = 14.6;
+				MACOSX_DEPLOYMENT_TARGET = 14.0;
 				MARKETING_VERSION = 1.0;
 				PRODUCT_BUNDLE_IDENTIFIER = "com.coder.Coder-Desktop";
 				PRODUCT_NAME = "$(TARGET_NAME)";
@@ -987,7 +1030,7 @@
 					"$(inherited)",
 					"@executable_path/../Frameworks",
 				);
-				MACOSX_DEPLOYMENT_TARGET = 14.6;
+				MACOSX_DEPLOYMENT_TARGET = 14.0;
 				MARKETING_VERSION = 1.0;
 				PRODUCT_BUNDLE_IDENTIFIER = "com.coder.Coder-Desktop";
 				PRODUCT_NAME = "$(TARGET_NAME)";
@@ -1006,7 +1049,7 @@
 				DEAD_CODE_STRIPPING = YES;
 				DEVELOPMENT_TEAM = 4399GN35BJ;
 				GENERATE_INFOPLIST_FILE = YES;
-				MACOSX_DEPLOYMENT_TARGET = 14.6;
+				MACOSX_DEPLOYMENT_TARGET = 14.0;
 				MARKETING_VERSION = 1.0;
 				PRODUCT_BUNDLE_IDENTIFIER = "com.coder.Coder-DesktopTests";
 				PRODUCT_NAME = "$(TARGET_NAME)";
@@ -1025,7 +1068,7 @@
 				DEAD_CODE_STRIPPING = YES;
 				DEVELOPMENT_TEAM = 4399GN35BJ;
 				GENERATE_INFOPLIST_FILE = YES;
-				MACOSX_DEPLOYMENT_TARGET = 14.6;
+				MACOSX_DEPLOYMENT_TARGET = 14.0;
 				MARKETING_VERSION = 1.0;
 				PRODUCT_BUNDLE_IDENTIFIER = "com.coder.Coder-DesktopTests";
 				PRODUCT_NAME = "$(TARGET_NAME)";
@@ -1043,7 +1086,7 @@
 				DEAD_CODE_STRIPPING = YES;
 				DEVELOPMENT_TEAM = 4399GN35BJ;
 				GENERATE_INFOPLIST_FILE = YES;
-				MACOSX_DEPLOYMENT_TARGET = 14.6;
+				MACOSX_DEPLOYMENT_TARGET = 14.0;
 				MARKETING_VERSION = 1.0;
 				PRODUCT_BUNDLE_IDENTIFIER = "com.coder.Coder-DesktopUITests";
 				PRODUCT_NAME = "$(TARGET_NAME)";
@@ -1061,7 +1104,7 @@
 				DEAD_CODE_STRIPPING = YES;
 				DEVELOPMENT_TEAM = 4399GN35BJ;
 				GENERATE_INFOPLIST_FILE = YES;
-				MACOSX_DEPLOYMENT_TARGET = 14.6;
+				MACOSX_DEPLOYMENT_TARGET = 14.0;
 				MARKETING_VERSION = 1.0;
 				PRODUCT_BUNDLE_IDENTIFIER = "com.coder.Coder-DesktopUITests";
 				PRODUCT_NAME = "$(TARGET_NAME)";
@@ -1091,7 +1134,7 @@
 					"@executable_path/../Frameworks",
 					"@executable_path/../../../../Frameworks",
 				);
-				MACOSX_DEPLOYMENT_TARGET = 14.6;
+				MACOSX_DEPLOYMENT_TARGET = 14.0;
 				MARKETING_VERSION = 1.0;
 				PRODUCT_BUNDLE_IDENTIFIER = "com.coder.Coder-Desktop.VPN";
 				PRODUCT_MODULE_NAME = "$(PRODUCT_NAME:c99extidentifier)";
@@ -1123,7 +1166,7 @@
 					"@executable_path/../Frameworks",
 					"@executable_path/../../../../Frameworks",
 				);
-				MACOSX_DEPLOYMENT_TARGET = 14.6;
+				MACOSX_DEPLOYMENT_TARGET = 14.0;
 				MARKETING_VERSION = 1.0;
 				PRODUCT_BUNDLE_IDENTIFIER = "com.coder.Coder-Desktop.VPN";
 				PRODUCT_MODULE_NAME = "$(PRODUCT_NAME:c99extidentifier)";
@@ -1157,7 +1200,7 @@
 					"@executable_path/../Frameworks",
 					"@loader_path/Frameworks",
 				);
-				MACOSX_DEPLOYMENT_TARGET = 14.6;
+				MACOSX_DEPLOYMENT_TARGET = 14.0;
 				MARKETING_VERSION = 1.0;
 				MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++";
 				MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20";
@@ -1194,7 +1237,7 @@
 					"@executable_path/../Frameworks",
 					"@loader_path/Frameworks",
 				);
-				MACOSX_DEPLOYMENT_TARGET = 14.6;
+				MACOSX_DEPLOYMENT_TARGET = 14.0;
 				MARKETING_VERSION = 1.0;
 				MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++";
 				MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20";
@@ -1216,7 +1259,7 @@
 				CURRENT_PROJECT_VERSION = 1;
 				DEVELOPMENT_TEAM = 4399GN35BJ;
 				GENERATE_INFOPLIST_FILE = YES;
-				MACOSX_DEPLOYMENT_TARGET = 14.6;
+				MACOSX_DEPLOYMENT_TARGET = 14.0;
 				MARKETING_VERSION = 1.0;
 				PRODUCT_BUNDLE_IDENTIFIER = "com.coder.Coder-Desktop.VPNLibTests";
 				PRODUCT_NAME = "$(TARGET_NAME)";
@@ -1233,7 +1276,7 @@
 				CURRENT_PROJECT_VERSION = 1;
 				DEVELOPMENT_TEAM = 4399GN35BJ;
 				GENERATE_INFOPLIST_FILE = YES;
-				MACOSX_DEPLOYMENT_TARGET = 14.6;
+				MACOSX_DEPLOYMENT_TARGET = 14.0;
 				MARKETING_VERSION = 1.0;
 				PRODUCT_BUNDLE_IDENTIFIER = "com.coder.Coder-Desktop.VPNLibTests";
 				PRODUCT_NAME = "$(TARGET_NAME)";
@@ -1265,7 +1308,7 @@
 					"@executable_path/../Frameworks",
 					"@loader_path/Frameworks",
 				);
-				MACOSX_DEPLOYMENT_TARGET = 14.6;
+				MACOSX_DEPLOYMENT_TARGET = 14.0;
 				MARKETING_VERSION = 1.0;
 				MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++";
 				MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20";
@@ -1302,7 +1345,7 @@
 					"@executable_path/../Frameworks",
 					"@loader_path/Frameworks",
 				);
-				MACOSX_DEPLOYMENT_TARGET = 14.6;
+				MACOSX_DEPLOYMENT_TARGET = 14.0;
 				MARKETING_VERSION = 1.0;
 				MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++";
 				MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20";
@@ -1324,7 +1367,7 @@
 				CURRENT_PROJECT_VERSION = 1;
 				DEVELOPMENT_TEAM = 4399GN35BJ;
 				GENERATE_INFOPLIST_FILE = YES;
-				MACOSX_DEPLOYMENT_TARGET = 14.6;
+				MACOSX_DEPLOYMENT_TARGET = 14.0;
 				MARKETING_VERSION = 1.0;
 				PRODUCT_BUNDLE_IDENTIFIER = "com.coder.Coder-Desktop.CoderSDKTests";
 				PRODUCT_NAME = "$(TARGET_NAME)";
@@ -1341,7 +1384,7 @@
 				CURRENT_PROJECT_VERSION = 1;
 				DEVELOPMENT_TEAM = 4399GN35BJ;
 				GENERATE_INFOPLIST_FILE = YES;
-				MACOSX_DEPLOYMENT_TARGET = 14.6;
+				MACOSX_DEPLOYMENT_TARGET = 14.0;
 				MARKETING_VERSION = 1.0;
 				PRODUCT_BUNDLE_IDENTIFIER = "com.coder.Coder-Desktop.CoderSDKTests";
 				PRODUCT_NAME = "$(TARGET_NAME)";
@@ -1446,6 +1489,22 @@
 				version = 1.28.2;
 			};
 		};
+		AA2C690D2D34F6920059AFAF /* XCRemoteSwiftPackageReference "LaunchAtLogin-modern" */ = {
+			isa = XCRemoteSwiftPackageReference;
+			repositoryURL = "https://github.com/sindresorhus/LaunchAtLogin-modern";
+			requirement = {
+				kind = exactVersion;
+				version = 1.1.0;
+			};
+		};
+		AA2C698A2D354A600059AFAF /* XCRemoteSwiftPackageReference "SwiftLintPlugins" */ = {
+			isa = XCRemoteSwiftPackageReference;
+			repositoryURL = "https://github.com/SimplyDanny/SwiftLintPlugins";
+			requirement = {
+				kind = upToNextMajorVersion;
+				minimumVersion = 0.58.0;
+			};
+		};
 		AA3B3E8A2D2E0FE10099996A /* XCRemoteSwiftPackageReference "Mocker" */ = {
 			isa = XCRemoteSwiftPackageReference;
 			repositoryURL = "https://github.com/WeTransfer/Mocker";
@@ -1462,14 +1521,6 @@
 				minimumVersion = 0.10.0;
 			};
 		};
-		AA8BC33A2D0060C500E1ABAA /* XCRemoteSwiftPackageReference "SwiftLintPlugins" */ = {
-			isa = XCRemoteSwiftPackageReference;
-			repositoryURL = "https://github.com/SimplyDanny/SwiftLintPlugins";
-			requirement = {
-				kind = upToNextMajorVersion;
-				minimumVersion = 0.57.1;
-			};
-		};
 		AA8BC33D2D0061F200E1ABAA /* XCRemoteSwiftPackageReference "fluid-menu-bar-extra" */ = {
 			isa = XCRemoteSwiftPackageReference;
 			repositoryURL = "https://github.com/lfroms/fluid-menu-bar-extra";
@@ -1486,9 +1537,62 @@
 				kind = branch;
 			};
 		};
+		AA8EECF52D3A22320049DD09 /* XCRemoteSwiftPackageReference "SettingsAccess" */ = {
+			isa = XCRemoteSwiftPackageReference;
+			repositoryURL = "https://github.com/orchetect/SettingsAccess";
+			requirement = {
+				kind = upToNextMajorVersion;
+				minimumVersion = 2.1.0;
+			};
+		};
 /* End XCRemoteSwiftPackageReference section */
 
 /* Begin XCSwiftPackageProductDependency section */
+		AA2C690E2D34F6920059AFAF /* LaunchAtLogin */ = {
+			isa = XCSwiftPackageProductDependency;
+			package = AA2C690D2D34F6920059AFAF /* XCRemoteSwiftPackageReference "LaunchAtLogin-modern" */;
+			productName = LaunchAtLogin;
+		};
+		AA2C698B2D354A800059AFAF /* SwiftLintBuildToolPlugin */ = {
+			isa = XCSwiftPackageProductDependency;
+			package = AA2C698A2D354A600059AFAF /* XCRemoteSwiftPackageReference "SwiftLintPlugins" */;
+			productName = "plugin:SwiftLintBuildToolPlugin";
+		};
+		AA2C698D2D354A840059AFAF /* SwiftLintBuildToolPlugin */ = {
+			isa = XCSwiftPackageProductDependency;
+			package = AA2C698A2D354A600059AFAF /* XCRemoteSwiftPackageReference "SwiftLintPlugins" */;
+			productName = "plugin:SwiftLintBuildToolPlugin";
+		};
+		AA2C698F2D354A880059AFAF /* SwiftLintBuildToolPlugin */ = {
+			isa = XCSwiftPackageProductDependency;
+			package = AA2C698A2D354A600059AFAF /* XCRemoteSwiftPackageReference "SwiftLintPlugins" */;
+			productName = "plugin:SwiftLintBuildToolPlugin";
+		};
+		AA2C69912D354A8B0059AFAF /* SwiftLintBuildToolPlugin */ = {
+			isa = XCSwiftPackageProductDependency;
+			package = AA2C698A2D354A600059AFAF /* XCRemoteSwiftPackageReference "SwiftLintPlugins" */;
+			productName = "plugin:SwiftLintBuildToolPlugin";
+		};
+		AA2C69932D354A8E0059AFAF /* SwiftLintBuildToolPlugin */ = {
+			isa = XCSwiftPackageProductDependency;
+			package = AA2C698A2D354A600059AFAF /* XCRemoteSwiftPackageReference "SwiftLintPlugins" */;
+			productName = "plugin:SwiftLintBuildToolPlugin";
+		};
+		AA2C69952D354A910059AFAF /* SwiftLintBuildToolPlugin */ = {
+			isa = XCSwiftPackageProductDependency;
+			package = AA2C698A2D354A600059AFAF /* XCRemoteSwiftPackageReference "SwiftLintPlugins" */;
+			productName = "plugin:SwiftLintBuildToolPlugin";
+		};
+		AA2C69972D354A940059AFAF /* SwiftLintBuildToolPlugin */ = {
+			isa = XCSwiftPackageProductDependency;
+			package = AA2C698A2D354A600059AFAF /* XCRemoteSwiftPackageReference "SwiftLintPlugins" */;
+			productName = "plugin:SwiftLintBuildToolPlugin";
+		};
+		AA2C69992D354A970059AFAF /* SwiftLintBuildToolPlugin */ = {
+			isa = XCSwiftPackageProductDependency;
+			package = AA2C698A2D354A600059AFAF /* XCRemoteSwiftPackageReference "SwiftLintPlugins" */;
+			productName = "plugin:SwiftLintBuildToolPlugin";
+		};
 		AA3B3DBE2D2D23AB0099996A /* SwiftProtobuf */ = {
 			isa = XCSwiftPackageProductDependency;
 			package = 961679512CFF207900B2B6DF /* XCRemoteSwiftPackageReference "swift-protobuf" */;
@@ -1519,11 +1623,6 @@
 			package = AA8BC3372D00609700E1ABAA /* XCRemoteSwiftPackageReference "ViewInspector" */;
 			productName = ViewInspector;
 		};
-		AA8BC33B2D0060E700E1ABAA /* SwiftLintBuildToolPlugin */ = {
-			isa = XCSwiftPackageProductDependency;
-			package = AA8BC33A2D0060C500E1ABAA /* XCRemoteSwiftPackageReference "SwiftLintPlugins" */;
-			productName = "plugin:SwiftLintBuildToolPlugin";
-		};
 		AA8BC33E2D0061F200E1ABAA /* FluidMenuBarExtra */ = {
 			isa = XCSwiftPackageProductDependency;
 			package = AA8BC33D2D0061F200E1ABAA /* XCRemoteSwiftPackageReference "fluid-menu-bar-extra" */;
@@ -1534,6 +1633,11 @@
 			package = AA8BC4CD2D00A4B700E1ABAA /* XCRemoteSwiftPackageReference "KeychainAccess" */;
 			productName = KeychainAccess;
 		};
+		AA8EECF62D3A22320049DD09 /* SettingsAccess */ = {
+			isa = XCSwiftPackageProductDependency;
+			package = AA8EECF52D3A22320049DD09 /* XCRemoteSwiftPackageReference "SettingsAccess" */;
+			productName = SettingsAccess;
+		};
 /* End XCSwiftPackageProductDependency section */
 	};
 	rootObject = 961678F42CFF100D00B2B6DF /* Project object */;
diff --git a/Coder Desktop/Coder Desktop.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Coder Desktop/Coder Desktop.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
index 5a69cd28..c362f1fb 100644
--- a/Coder Desktop/Coder Desktop.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved	
+++ b/Coder Desktop/Coder Desktop.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved	
@@ -1,5 +1,5 @@
 {
-  "originHash" : "ec40e522ec1a2416e8e8f5cbe97424ab3e4a614e6ef453c10ea28e84e88b6771",
+  "originHash" : "b52ef58779afac669f0b78fbf402855ebb45d016ab69ee39b5470c9442c12823",
   "pins" : [
     {
       "identity" : "fluid-menu-bar-extra",
@@ -18,6 +18,15 @@
         "revision" : "e0c7eebc5a4465a3c4680764f26b7a61f567cdaf"
       }
     },
+    {
+      "identity" : "launchatlogin-modern",
+      "kind" : "remoteSourceControl",
+      "location" : "https://github.com/sindresorhus/LaunchAtLogin-modern",
+      "state" : {
+        "revision" : "a04ec1c363be3627734f6dad757d82f5d4fa8fcc",
+        "version" : "1.1.0"
+      }
+    },
     {
       "identity" : "mocker",
       "kind" : "remoteSourceControl",
@@ -27,6 +36,15 @@
         "version" : "3.0.2"
       }
     },
+    {
+      "identity" : "settingsaccess",
+      "kind" : "remoteSourceControl",
+      "location" : "https://github.com/orchetect/SettingsAccess",
+      "state" : {
+        "revision" : "08e80c35501f273afa2f5d6f737429bbe395ff81",
+        "version" : "2.1.0"
+      }
+    },
     {
       "identity" : "swift-protobuf",
       "kind" : "remoteSourceControl",
@@ -41,8 +59,8 @@
       "kind" : "remoteSourceControl",
       "location" : "https://github.com/SimplyDanny/SwiftLintPlugins",
       "state" : {
-        "revision" : "f9731bef175c3eea3a0ca960f1be78fcc2bc7853",
-        "version" : "0.57.1"
+        "revision" : "fac0c3d3ac69b15ea5382275dbbd5e583a2e05fa",
+        "version" : "0.58.0"
       }
     },
     {
diff --git a/Coder Desktop/Coder Desktop/About.swift b/Coder Desktop/Coder Desktop/About.swift
index 4bff9d61..37711758 100644
--- a/Coder Desktop/Coder Desktop/About.swift	
+++ b/Coder Desktop/Coder Desktop/About.swift	
@@ -32,15 +32,7 @@ enum About {
 
     @MainActor
     static func open() {
-        #if compiler(>=5.9) && canImport(AppKit)
-            if #available(macOS 14, *) {
-                NSApp.activate()
-            } else {
-                NSApp.activate(ignoringOtherApps: true)
-            }
-        #else
-            NSApp.activate(ignoringOtherApps: true)
-        #endif
+        appActivate()
         NSApp.orderFrontStandardAboutPanel(options: [
             .credits: credits,
         ])
diff --git a/Coder Desktop/Coder Desktop/Coder_DesktopApp.swift b/Coder Desktop/Coder Desktop/Coder_DesktopApp.swift
index 940f5ccf..0758f38a 100644
--- a/Coder Desktop/Coder Desktop/Coder_DesktopApp.swift	
+++ b/Coder Desktop/Coder Desktop/Coder_DesktopApp.swift	
@@ -14,6 +14,11 @@ struct DesktopApp: App {
             LoginForm<PreviewSession>().environmentObject(appDelegate.session)
         }
         .windowResizability(.contentSize)
+        SwiftUI.Settings { SettingsView<PreviewVPN>()
+            .environmentObject(appDelegate.vpn)
+            .environmentObject(appDelegate.settings)
+        }
+        .windowResizability(.contentSize)
     }
 }
 
@@ -22,10 +27,12 @@ class AppDelegate: NSObject, NSApplicationDelegate {
     private var menuBarExtra: FluidMenuBarExtra?
     let vpn: PreviewVPN
     let session: PreviewSession
+    let settings: Settings
 
     override init() {
-        // TODO: Replace with real implementations
+        // TODO: Replace with real implementation
         vpn = PreviewVPN()
+        settings = Settings()
         session = PreviewSession()
     }
 
@@ -34,6 +41,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
             VPNMenu<PreviewVPN, PreviewSession>().frame(width: 256)
                 .environmentObject(self.vpn)
                 .environmentObject(self.session)
+                .environmentObject(self.settings)
         }
     }
 
@@ -49,3 +57,16 @@ class AppDelegate: NSObject, NSApplicationDelegate {
         false
     }
 }
+
+@MainActor
+func appActivate() {
+    #if compiler(>=5.9) && canImport(AppKit)
+        if #available(macOS 14, *) {
+            NSApp.activate()
+        } else {
+            NSApp.activate(ignoringOtherApps: true)
+        }
+    #else
+        NSApp.activate(ignoringOtherApps: true)
+    #endif
+}
diff --git a/Coder Desktop/Coder Desktop/Session.swift b/Coder Desktop/Coder Desktop/State.swift
similarity index 70%
rename from Coder Desktop/Coder Desktop/Session.swift
rename to Coder Desktop/Coder Desktop/State.swift
index 2e39adae..f3d70947 100644
--- a/Coder Desktop/Coder Desktop/Session.swift	
+++ b/Coder Desktop/Coder Desktop/State.swift	
@@ -1,6 +1,8 @@
+import CoderSDK
 import Foundation
 import KeychainAccess
 import NetworkExtension
+import SwiftUI
 
 protocol Session: ObservableObject {
     var hasSession: Bool { get }
@@ -89,3 +91,47 @@ class SecureSession: ObservableObject, Session {
         static let sessionToken = "sessionToken"
     }
 }
+
+class Settings: ObservableObject {
+    let store: UserDefaults
+    @AppStorage(Keys.useLiteralHeaders) var useLiteralHeaders = false
+
+    @Published var literalHeaders: [LiteralHeader] {
+        didSet {
+            try? store.set(JSONEncoder().encode(literalHeaders), forKey: Keys.literalHeaders)
+        }
+    }
+
+    init(store: UserDefaults = UserDefaults.standard) {
+        self.store = store
+        _literalHeaders = Published(
+            initialValue: UserDefaults.standard.data(
+                forKey: Keys.literalHeaders
+            ).flatMap { try? JSONDecoder().decode([LiteralHeader].self, from: $0) } ?? []
+        )
+    }
+
+    enum Keys {
+        static let useLiteralHeaders = "UseLiteralHeaders"
+        static let literalHeaders = "LiteralHeaders"
+    }
+}
+
+struct LiteralHeader: Hashable, Identifiable, Equatable, Codable {
+    var header: String
+    var value: String
+    var id: String {
+        "\(header):\(value)"
+    }
+
+    init(header: String, value: String) {
+        self.header = header
+        self.value = value
+    }
+}
+
+extension LiteralHeader {
+    func toSDKHeader() -> HTTPHeader {
+        return .init(header: header, value: value)
+    }
+}
diff --git a/Coder Desktop/Coder Desktop/Views/LoginForm.swift b/Coder Desktop/Coder Desktop/Views/LoginForm.swift
index 776cee91..10fa6b0e 100644
--- a/Coder Desktop/Coder Desktop/Views/LoginForm.swift	
+++ b/Coder Desktop/Coder Desktop/Views/LoginForm.swift	
@@ -3,6 +3,7 @@ import SwiftUI
 
 struct LoginForm<S: Session>: View {
     @EnvironmentObject var session: S
+    @EnvironmentObject var settings: Settings
     @Environment(\.dismiss) private var dismiss
 
     @State private var baseAccessURL: String = ""
@@ -68,7 +69,7 @@ struct LoginForm<S: Session>: View {
         }
         loading = true
         defer { loading = false }
-        let client = Client(url: url, token: sessionToken)
+        let client = Client(url: url, token: sessionToken, headers: settings.literalHeaders.map { $0.toSDKHeader() })
         do {
             _ = try await client.user("me")
         } catch {
diff --git a/Coder Desktop/Coder Desktop/Views/Settings/GeneralTab.swift b/Coder Desktop/Coder Desktop/Views/Settings/GeneralTab.swift
new file mode 100644
index 00000000..0c1bb9e1
--- /dev/null
+++ b/Coder Desktop/Coder Desktop/Views/Settings/GeneralTab.swift	
@@ -0,0 +1,16 @@
+import LaunchAtLogin
+import SwiftUI
+
+struct GeneralTab: View {
+    var body: some View {
+        Form {
+            Section {
+                LaunchAtLogin.Toggle("Launch at Login")
+            }
+        }.formStyle(.grouped)
+    }
+}
+
+#Preview {
+    GeneralTab()
+}
diff --git a/Coder Desktop/Coder Desktop/Views/Settings/LiteralHeaderModal.swift b/Coder Desktop/Coder Desktop/Views/Settings/LiteralHeaderModal.swift
new file mode 100644
index 00000000..9e2ea2a7
--- /dev/null
+++ b/Coder Desktop/Coder Desktop/Views/Settings/LiteralHeaderModal.swift	
@@ -0,0 +1,45 @@
+import SwiftUI
+
+struct LiteralHeaderModal: View {
+    var existingHeader: LiteralHeader?
+
+    @EnvironmentObject var settings: Settings
+    @Environment(\.dismiss) private var dismiss
+
+    @State private var header: String = ""
+    @State private var value: String = ""
+
+    var body: some View {
+        VStack(spacing: 0) {
+            Form {
+                Section {
+                    TextField("Header", text: $header)
+                    TextField("Value", text: $value)
+                }
+            }.formStyle(.grouped).scrollDisabled(true).padding(.horizontal)
+            Divider()
+            HStack {
+                Spacer()
+                Button("Cancel", action: { dismiss() }).keyboardShortcut(.cancelAction)
+                Button(existingHeader == nil ? "Add" : "Save", action: submit)
+                    .keyboardShortcut(.defaultAction)
+            }.padding(20)
+        }.onAppear {
+            if let existingHeader {
+                self.header = existingHeader.header
+                self.value = existingHeader.value
+            }
+        }
+    }
+
+    func submit() {
+        defer { dismiss() }
+        if let existingHeader {
+            settings.literalHeaders.removeAll { $0 == existingHeader }
+        }
+        let newHeader = LiteralHeader(header: header, value: value)
+        if !settings.literalHeaders.contains(newHeader) {
+            settings.literalHeaders.append(newHeader)
+        }
+    }
+}
diff --git a/Coder Desktop/Coder Desktop/Views/Settings/LiteralHeadersSection.swift b/Coder Desktop/Coder Desktop/Views/Settings/LiteralHeadersSection.swift
new file mode 100644
index 00000000..aa272034
--- /dev/null
+++ b/Coder Desktop/Coder Desktop/Views/Settings/LiteralHeadersSection.swift	
@@ -0,0 +1,71 @@
+import SwiftUI
+
+struct LiteralHeadersSection<VPN: VPNService>: View {
+    @EnvironmentObject var vpn: VPN
+    @EnvironmentObject var settings: Settings
+
+    @State private var selectedHeader: LiteralHeader.ID?
+    @State private var editingHeader: LiteralHeader?
+    @State private var addingNewHeader = false
+
+    let inspection = Inspection<Self>()
+
+    var body: some View {
+        Section {
+            Toggle(isOn: settings.$useLiteralHeaders) {
+                Text("HTTP Headers")
+                Text("When enabled, these headers will be included on all outgoing HTTP requests.")
+                if vpn.state != .disabled { Text("Cannot be modified while Coder VPN is enabled.") }
+            }
+            .controlSize(.large)
+
+            Table(settings.literalHeaders, selection: $selectedHeader) {
+                TableColumn("Header", value: \.header)
+                TableColumn("Value", value: \.value)
+            }.opacity(settings.useLiteralHeaders ? 1 : 0.5)
+                .frame(minWidth: 400, minHeight: 200)
+                .padding(.bottom, 25)
+                .overlay(alignment: .bottom) {
+                    VStack(alignment: .leading, spacing: 0) {
+                        Divider()
+                        HStack(spacing: 0) {
+                            Button {
+                                addingNewHeader = true
+                            } label: {
+                                Image(systemName: "plus")
+                                    .frame(width: 24, height: 24)
+                            }
+                            Divider()
+                            Button {
+                                settings.literalHeaders.removeAll { $0.id == selectedHeader }
+                                selectedHeader = nil
+                            } label: {
+                                Image(systemName: "minus")
+                                    .frame(width: 24, height: 24)
+                            }.disabled(selectedHeader == nil)
+                        }
+                        .buttonStyle(.borderless)
+                    }
+                    .background(.primary.opacity(0.04))
+                    .fixedSize(horizontal: false, vertical: true)
+                }
+                .background(.primary.opacity(0.04))
+                .contextMenu(forSelectionType: LiteralHeader.ID.self, menu: { _ in },
+                             primaryAction: { selectedHeaders in
+                                 if let firstHeader = selectedHeaders.first {
+                                     editingHeader = settings.literalHeaders.first(where: { $0.id == firstHeader })
+                                 }
+                             })
+                .disabled(!settings.useLiteralHeaders)
+        }
+        .sheet(isPresented: $addingNewHeader) {
+            LiteralHeaderModal()
+        }
+        .sheet(item: $editingHeader) { header in
+            LiteralHeaderModal(existingHeader: header)
+        }.onTapGesture {
+            selectedHeader = nil
+        }.disabled(vpn.state != .disabled)
+        .onReceive(inspection.notice) { self.inspection.visit(self, $0) } // ViewInspector
+    }
+}
diff --git a/Coder Desktop/Coder Desktop/Views/Settings/NetworkTab.swift b/Coder Desktop/Coder Desktop/Views/Settings/NetworkTab.swift
new file mode 100644
index 00000000..5e1d7ef8
--- /dev/null
+++ b/Coder Desktop/Coder Desktop/Views/Settings/NetworkTab.swift	
@@ -0,0 +1,14 @@
+import SwiftUI
+
+struct NetworkTab<VPN: VPNService>: View {
+    var body: some View {
+        Form {
+            LiteralHeadersSection<VPN>()
+        }
+        .formStyle(.grouped)
+    }
+}
+
+#Preview {
+    NetworkTab<PreviewVPN>()
+}
diff --git a/Coder Desktop/Coder Desktop/Views/Settings/Settings.swift b/Coder Desktop/Coder Desktop/Views/Settings/Settings.swift
new file mode 100644
index 00000000..8aac9a0c
--- /dev/null
+++ b/Coder Desktop/Coder Desktop/Views/Settings/Settings.swift	
@@ -0,0 +1,26 @@
+import SwiftUI
+
+struct SettingsView<VPN: VPNService>: View {
+    @AppStorage("SettingsSelectedIndex") private var selection: SettingsTab = .general
+
+    var body: some View {
+        TabView(selection: $selection) {
+            GeneralTab()
+                .tabItem {
+                    Label("General", systemImage: "gearshape")
+                }.tag(SettingsTab.general)
+            NetworkTab<VPN>()
+                .tabItem {
+                    Label("Network", systemImage: "dot.radiowaves.left.and.right")
+                }.tag(SettingsTab.network)
+        }.frame(width: 600)
+            .frame(maxHeight: 500)
+            .scrollContentBackground(.hidden)
+            .fixedSize()
+    }
+}
+
+enum SettingsTab: Int {
+    case general
+    case network
+}
diff --git a/Coder Desktop/Coder Desktop/Views/Util.swift b/Coder Desktop/Coder Desktop/Views/Util.swift
index 96fb2467..ce61c667 100644
--- a/Coder Desktop/Coder Desktop/Views/Util.swift	
+++ b/Coder Desktop/Coder Desktop/Views/Util.swift	
@@ -1,4 +1,5 @@
 import Combine
+import SwiftUI
 
 // This is required for inspecting stateful views
 final class Inspection<V> {
diff --git a/Coder Desktop/Coder Desktop/Views/VPNMenu.swift b/Coder Desktop/Coder Desktop/Views/VPNMenu.swift
index 87889369..5c6cc170 100644
--- a/Coder Desktop/Coder Desktop/Views/VPNMenu.swift	
+++ b/Coder Desktop/Coder Desktop/Views/VPNMenu.swift	
@@ -1,3 +1,4 @@
+import SettingsAccess
 import SwiftUI
 
 struct VPNMenu<VPN: VPNService, S: Session>: View {
@@ -21,6 +22,8 @@ struct VPNMenu<VPN: VPNService, S: Session>: View {
                     )) {
                         Text("CoderVPN")
                             .frame(maxWidth: .infinity, alignment: .leading)
+                            .font(.body.bold())
+                            .foregroundColor(.primary)
                     }.toggleStyle(.switch)
                         .disabled(vpnDisabled)
                 }
@@ -50,6 +53,11 @@ struct VPNMenu<VPN: VPNService, S: Session>: View {
                     TrayDivider()
                 }
                 AuthButton<VPN, S>()
+                SettingsLink {
+                    ButtonRowView { Text("Settings") }
+                } preAction: {} postAction: {
+                    appActivate()
+                }.buttonStyle(.plain)
                 Button {
                     About.open()
                 } label: {
diff --git a/Coder Desktop/Coder Desktop/Windows.swift b/Coder Desktop/Coder Desktop/Windows.swift
index e82680cf..61ac4ef6 100644
--- a/Coder Desktop/Coder Desktop/Windows.swift	
+++ b/Coder Desktop/Coder Desktop/Windows.swift	
@@ -8,15 +8,7 @@ enum Windows: String {
 extension OpenWindowAction {
     // Type-safe wrapper for opening windows that also focuses the new window
     func callAsFunction(id: Windows) {
-        #if compiler(>=5.9) && canImport(AppKit)
-            if #available(macOS 14, *) {
-                NSApp.activate()
-            } else {
-                NSApp.activate(ignoringOtherApps: true)
-            }
-        #else
-            NSApp.activate(ignoringOtherApps: true)
-        #endif
+        appActivate()
         callAsFunction(id: id.rawValue)
         // The arranging behaviour is flakey without this
         NSApp.arrangeInFront(nil)
diff --git a/Coder Desktop/Coder DesktopTests/LiteralHeadersSettingTests.swift b/Coder Desktop/Coder DesktopTests/LiteralHeadersSettingTests.swift
new file mode 100644
index 00000000..6d68a74c
--- /dev/null
+++ b/Coder Desktop/Coder DesktopTests/LiteralHeadersSettingTests.swift	
@@ -0,0 +1,48 @@
+@testable import Coder_Desktop
+import SwiftUI
+import Testing
+import ViewInspector
+
+@MainActor
+@Suite(.timeLimit(.minutes(1)))
+struct LiteralHeadersSettingTests {
+    let vpn: MockVPNService
+    let sut: LiteralHeadersSection<MockVPNService>
+    let view: any View
+
+    init() {
+        vpn = MockVPNService()
+        sut = LiteralHeadersSection<MockVPNService>()
+        let store = UserDefaults(suiteName: #file)!
+        store.removePersistentDomain(forName: #file)
+        view = sut.environmentObject(vpn).environmentObject(Settings(store: store))
+    }
+
+    @Test
+    func testToggleDisabledWhenVPNEnabled() async throws {
+        vpn.state = .connected
+
+        try await ViewHosting.host(view) {
+            try await sut.inspection.inspect { view in
+                let toggle = try view.find(ViewType.Toggle.self)
+                #expect(toggle.isDisabled())
+                #expect(throws: Never.self) { try toggle.labelView().find(text: "HTTP Headers") }
+            }
+        }
+    }
+
+    @Test
+    func testToggleEnabledWhenVPNDisabled() async throws {
+        vpn.state = .disabled
+
+        try await ViewHosting.host(view) {
+            try await sut.inspection.inspect { view in
+                let toggle = try view.find(ViewType.Toggle.self)
+                #expect(!toggle.isDisabled())
+                #expect(throws: Never.self) { try toggle.labelView().find(text: "HTTP Headers") }
+            }
+        }
+    }
+
+    // TODO: More tests, ViewInspector cannot currently inspect Tables
+}
diff --git a/Coder Desktop/Coder DesktopTests/LoginFormTests.swift b/Coder Desktop/Coder DesktopTests/LoginFormTests.swift
index 912f4099..6ba1154a 100644
--- a/Coder Desktop/Coder DesktopTests/LoginFormTests.swift	
+++ b/Coder Desktop/Coder DesktopTests/LoginFormTests.swift	
@@ -15,7 +15,9 @@ struct LoginTests {
     init() {
         session = MockSession()
         sut = LoginForm<MockSession>()
-        view = sut.environmentObject(session)
+        let store = UserDefaults(suiteName: #file)!
+        store.removePersistentDomain(forName: #file)
+        view = sut.environmentObject(session).environmentObject(Settings(store: store))
     }
 
     @Test
@@ -70,12 +72,11 @@ struct LoginTests {
 
     @Test
     func testFailedAuthentication() async throws {
-        let login = LoginForm<MockSession>()
         let url = URL(string: "https://testFailedAuthentication.com")!
         Mock(url: url.appendingPathComponent("/api/v2/users/me"), statusCode: 401, data: [.get: Data()]).register()
 
-        try await ViewHosting.host(login.environmentObject(session)) {
-            try await login.inspection.inspect { view in
+        try await ViewHosting.host(view) {
+            try await sut.inspection.inspect { view in
                 try view.find(ViewType.TextField.self).setInput(url.absoluteString)
                 try view.find(button: "Next").tap()
                 #expect(throws: Never.self) { try view.find(text: "Session Token") }
diff --git a/Makefile b/Makefile
index b746639d..a428f067 100644
--- a/Makefile
+++ b/Makefile
@@ -21,8 +21,7 @@ test:
 		-testPlan $(SCHEME) \
 		-skipPackagePluginValidation \
 		CODE_SIGNING_REQUIRED=NO \
-		CODE_SIGNING_ALLOWED=NO \
-		| LC_ALL="en_US.UTF-8" xcpretty
+		CODE_SIGNING_ALLOWED=NO | xcbeautify
 
 lint:
 	swiftlint \

From 81754a22b13a875713a43db53c4dd0346af13301 Mon Sep 17 00:00:00 2001
From: Ethan Dickson <ethan@coder.com>
Date: Tue, 21 Jan 2025 13:13:01 +1100
Subject: [PATCH 2/4] use passed store

---
 Coder Desktop/Coder Desktop/State.swift | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/Coder Desktop/Coder Desktop/State.swift b/Coder Desktop/Coder Desktop/State.swift
index f3d70947..7a588080 100644
--- a/Coder Desktop/Coder Desktop/State.swift	
+++ b/Coder Desktop/Coder Desktop/State.swift	
@@ -93,7 +93,7 @@ class SecureSession: ObservableObject, Session {
 }
 
 class Settings: ObservableObject {
-    let store: UserDefaults
+    private let store: UserDefaults
     @AppStorage(Keys.useLiteralHeaders) var useLiteralHeaders = false
 
     @Published var literalHeaders: [LiteralHeader] {
@@ -102,10 +102,10 @@ class Settings: ObservableObject {
         }
     }
 
-    init(store: UserDefaults = UserDefaults.standard) {
+    init(store: UserDefaults = .standard) {
         self.store = store
         _literalHeaders = Published(
-            initialValue: UserDefaults.standard.data(
+            initialValue: store.data(
                 forKey: Keys.literalHeaders
             ).flatMap { try? JSONDecoder().decode([LiteralHeader].self, from: $0) } ?? []
         )

From dc6dd4f292829a70c7a3240964a9fd674619b4ef Mon Sep 17 00:00:00 2001
From: Ethan Dickson <ethan@coder.com>
Date: Wed, 22 Jan 2025 13:42:26 +1100
Subject: [PATCH 3/4] remove settingsaccess

---
 .../Coder Desktop.xcodeproj/project.pbxproj     | 17 -----------------
 .../xcshareddata/swiftpm/Package.resolved       | 11 +----------
 Coder Desktop/Coder Desktop/Views/VPNMenu.swift |  9 +++++----
 3 files changed, 6 insertions(+), 31 deletions(-)

diff --git a/Coder Desktop/Coder Desktop.xcodeproj/project.pbxproj b/Coder Desktop/Coder Desktop.xcodeproj/project.pbxproj
index 1f36fec3..e68a8c44 100644
--- a/Coder Desktop/Coder Desktop.xcodeproj/project.pbxproj	
+++ b/Coder Desktop/Coder Desktop.xcodeproj/project.pbxproj	
@@ -25,7 +25,6 @@
 		AA8BC3392D0060A900E1ABAA /* ViewInspector in Frameworks */ = {isa = PBXBuildFile; productRef = AA8BC3382D0060A900E1ABAA /* ViewInspector */; };
 		AA8BC33F2D0061F200E1ABAA /* FluidMenuBarExtra in Frameworks */ = {isa = PBXBuildFile; productRef = AA8BC33E2D0061F200E1ABAA /* FluidMenuBarExtra */; };
 		AA8BC4CF2D00A4B700E1ABAA /* KeychainAccess in Frameworks */ = {isa = PBXBuildFile; productRef = AA8BC4CE2D00A4B700E1ABAA /* KeychainAccess */; };
-		AA8EECF72D3A22320049DD09 /* SettingsAccess in Frameworks */ = {isa = PBXBuildFile; productRef = AA8EECF62D3A22320049DD09 /* SettingsAccess */; };
 /* End PBXBuildFile section */
 
 /* Begin PBXContainerItemProxy section */
@@ -233,7 +232,6 @@
 				AA8BC4CF2D00A4B700E1ABAA /* KeychainAccess in Frameworks */,
 				AA2C690F2D34F6920059AFAF /* LaunchAtLogin in Frameworks */,
 				AA8BC33F2D0061F200E1ABAA /* FluidMenuBarExtra in Frameworks */,
-				AA8EECF72D3A22320049DD09 /* SettingsAccess in Frameworks */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -384,7 +382,6 @@
 				AA8BC33E2D0061F200E1ABAA /* FluidMenuBarExtra */,
 				AA8BC4CE2D00A4B700E1ABAA /* KeychainAccess */,
 				AA2C690E2D34F6920059AFAF /* LaunchAtLogin */,
-				AA8EECF62D3A22320049DD09 /* SettingsAccess */,
 			);
 			productName = "Coder Desktop";
 			productReference = 961678FC2CFF100D00B2B6DF /* Coder Desktop.app */;
@@ -626,7 +623,6 @@
 				AA3B3E8A2D2E0FE10099996A /* XCRemoteSwiftPackageReference "Mocker" */,
 				AA2C690D2D34F6920059AFAF /* XCRemoteSwiftPackageReference "LaunchAtLogin-modern" */,
 				AA2C698A2D354A600059AFAF /* XCRemoteSwiftPackageReference "SwiftLintPlugins" */,
-				AA8EECF52D3A22320049DD09 /* XCRemoteSwiftPackageReference "SettingsAccess" */,
 			);
 			preferredProjectObjectVersion = 77;
 			productRefGroup = 961678FD2CFF100D00B2B6DF /* Products */;
@@ -1537,14 +1533,6 @@
 				kind = branch;
 			};
 		};
-		AA8EECF52D3A22320049DD09 /* XCRemoteSwiftPackageReference "SettingsAccess" */ = {
-			isa = XCRemoteSwiftPackageReference;
-			repositoryURL = "https://github.com/orchetect/SettingsAccess";
-			requirement = {
-				kind = upToNextMajorVersion;
-				minimumVersion = 2.1.0;
-			};
-		};
 /* End XCRemoteSwiftPackageReference section */
 
 /* Begin XCSwiftPackageProductDependency section */
@@ -1633,11 +1621,6 @@
 			package = AA8BC4CD2D00A4B700E1ABAA /* XCRemoteSwiftPackageReference "KeychainAccess" */;
 			productName = KeychainAccess;
 		};
-		AA8EECF62D3A22320049DD09 /* SettingsAccess */ = {
-			isa = XCSwiftPackageProductDependency;
-			package = AA8EECF52D3A22320049DD09 /* XCRemoteSwiftPackageReference "SettingsAccess" */;
-			productName = SettingsAccess;
-		};
 /* End XCSwiftPackageProductDependency section */
 	};
 	rootObject = 961678F42CFF100D00B2B6DF /* Project object */;
diff --git a/Coder Desktop/Coder Desktop.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Coder Desktop/Coder Desktop.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
index c362f1fb..37e6bc54 100644
--- a/Coder Desktop/Coder Desktop.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved	
+++ b/Coder Desktop/Coder Desktop.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved	
@@ -1,5 +1,5 @@
 {
-  "originHash" : "b52ef58779afac669f0b78fbf402855ebb45d016ab69ee39b5470c9442c12823",
+  "originHash" : "c41f63aa01c78f450e2232efbefcd30874995ad120db77fa5942062d6f813891",
   "pins" : [
     {
       "identity" : "fluid-menu-bar-extra",
@@ -36,15 +36,6 @@
         "version" : "3.0.2"
       }
     },
-    {
-      "identity" : "settingsaccess",
-      "kind" : "remoteSourceControl",
-      "location" : "https://github.com/orchetect/SettingsAccess",
-      "state" : {
-        "revision" : "08e80c35501f273afa2f5d6f737429bbe395ff81",
-        "version" : "2.1.0"
-      }
-    },
     {
       "identity" : "swift-protobuf",
       "kind" : "remoteSourceControl",
diff --git a/Coder Desktop/Coder Desktop/Views/VPNMenu.swift b/Coder Desktop/Coder Desktop/Views/VPNMenu.swift
index 5c6cc170..5ba76e06 100644
--- a/Coder Desktop/Coder Desktop/Views/VPNMenu.swift	
+++ b/Coder Desktop/Coder Desktop/Views/VPNMenu.swift	
@@ -1,9 +1,9 @@
-import SettingsAccess
 import SwiftUI
 
 struct VPNMenu<VPN: VPNService, S: Session>: View {
     @EnvironmentObject var vpn: VPN
     @EnvironmentObject var session: S
+    @Environment(\.openSettings) private var openSettings
 
     let inspection = Inspection<Self>()
 
@@ -53,10 +53,11 @@ struct VPNMenu<VPN: VPNService, S: Session>: View {
                     TrayDivider()
                 }
                 AuthButton<VPN, S>()
-                SettingsLink {
-                    ButtonRowView { Text("Settings") }
-                } preAction: {} postAction: {
+                Button {
+                    openSettings()
                     appActivate()
+                } label: {
+                    ButtonRowView { Text("Settings") }
                 }.buttonStyle(.plain)
                 Button {
                     About.open()

From a3adfc6cf69516c6b303eb2b261bca8bb6af8764 Mon Sep 17 00:00:00 2001
From: Ethan Dickson <ethan@coder.com>
Date: Wed, 22 Jan 2025 13:45:14 +1100
Subject: [PATCH 4/4] remove unused compiler directives

---
 Coder Desktop/Coder Desktop/Coder_DesktopApp.swift | 10 +---------
 1 file changed, 1 insertion(+), 9 deletions(-)

diff --git a/Coder Desktop/Coder Desktop/Coder_DesktopApp.swift b/Coder Desktop/Coder Desktop/Coder_DesktopApp.swift
index 0758f38a..c45e632a 100644
--- a/Coder Desktop/Coder Desktop/Coder_DesktopApp.swift	
+++ b/Coder Desktop/Coder Desktop/Coder_DesktopApp.swift	
@@ -60,13 +60,5 @@ class AppDelegate: NSObject, NSApplicationDelegate {
 
 @MainActor
 func appActivate() {
-    #if compiler(>=5.9) && canImport(AppKit)
-        if #available(macOS 14, *) {
-            NSApp.activate()
-        } else {
-            NSApp.activate(ignoringOtherApps: true)
-        }
-    #else
-        NSApp.activate(ignoringOtherApps: true)
-    #endif
+    NSApp.activate()
 }