|
| 1 | +--- |
| 2 | +title: Architecture Specification - macOS Wireless Auto-Switch Utility |
| 3 | +version: 1.0 |
| 4 | +date_created: 2025-09-14 |
| 5 | +last_updated: 2025-09-14 |
| 6 | +owner: System Architecture Team |
| 7 | +tags: [architecture, macos, networking, launchd, system-utility, automation] |
| 8 | +--- |
| 9 | + |
| 10 | +# Introduction |
| 11 | + |
| 12 | +This specification defines the architecture, requirements, and implementation guidelines for the macOS Wireless Auto-Switch utility - a system service that automatically manages WiFi connectivity based on wired network adapter status. The utility provides seamless network switching without user intervention, eliminating connection conflicts between wired and wireless interfaces. |
| 13 | + |
| 14 | +## 1. Purpose & Scope |
| 15 | + |
| 16 | +### Purpose |
| 17 | +Define the complete architecture and requirements for a macOS system utility that automatically toggles WiFi connectivity based on the presence of active wired network connections. |
| 18 | + |
| 19 | +### Scope |
| 20 | +- **In Scope**: Network interface detection, WiFi state management, system service integration, installation/management tooling, macOS compatibility (Ventura 13.x, Sonoma 14.x, Sequoia 15.x) |
| 21 | +- **Out of Scope**: GUI applications, network configuration management beyond WiFi toggle, third-party network managers, cross-platform support |
| 22 | +- **Target Audience**: System administrators, developers maintaining macOS network automation tools, DevOps engineers |
| 23 | +- **Assumptions**: Administrator privileges available, standard macOS networking stack, bash shell environment |
| 24 | + |
| 25 | +## 2. Definitions |
| 26 | + |
| 27 | +- **LaunchDaemon**: macOS system service that runs with root privileges and starts automatically at boot |
| 28 | +- **NetworkSetup**: macOS command-line utility for network configuration management |
| 29 | +- **Hardware Port**: Physical network interface identifier in macOS network configuration |
| 30 | +- **Airport Power**: macOS WiFi radio state (on/off) controlled via networksetup command |
| 31 | +- **System Configuration**: macOS framework for network and system state monitoring located at `/Library/Preferences/SystemConfiguration` |
| 32 | +- **Self-Assigned Address**: IPv4 address in 169.254.x.x range assigned when DHCP fails |
| 33 | +- **Wired Interface**: Ethernet, Thunderbolt, LAN, or USB-C network adapters with active IP assignment |
| 34 | + |
| 35 | +## 3. Requirements, Constraints & Guidelines |
| 36 | + |
| 37 | +### Functional Requirements |
| 38 | +- **REQ-001**: System shall detect active wired network connections with valid IP addresses |
| 39 | +- **REQ-002**: System shall automatically disable WiFi when wired connection is active |
| 40 | +- **REQ-003**: System shall automatically enable WiFi when no wired connections are active |
| 41 | +- **REQ-004**: System shall support multiple wired adapter types (Ethernet, Thunderbolt, LAN, AX88179A) |
| 42 | +- **REQ-005**: System shall ignore loopback (127.0.0.1) and self-assigned (169.254.x.x) IP addresses |
| 43 | +- **REQ-006**: System shall respond to network configuration changes in real-time |
| 44 | +- **REQ-007**: System shall provide comprehensive installation and management tooling |
| 45 | + |
| 46 | +### Performance Requirements |
| 47 | +- **PERF-001**: Network state detection shall complete within 5 seconds |
| 48 | +- **PERF-002**: WiFi toggle operations shall complete within 10 seconds |
| 49 | +- **PERF-003**: System shall introduce maximum 10-second delay to prevent LaunchDaemon restart loops |
| 50 | + |
| 51 | +### Security Requirements |
| 52 | +- **SEC-001**: System shall require administrator privileges for installation and execution |
| 53 | +- **SEC-002**: System shall use absolute paths for all system commands |
| 54 | +- **SEC-003**: System shall validate input parameters and exit with appropriate error codes |
| 55 | +- **SEC-004**: Scripts shall be owned by root with appropriate execution permissions |
| 56 | + |
| 57 | +### Compatibility Requirements |
| 58 | +- **COMP-001**: System shall support macOS Ventura (version 22.x), Sonoma (23.x), and Sequoia (24.x) |
| 59 | +- **COMP-002**: System shall require Bash 4+ for proper array handling |
| 60 | +- **COMP-003**: System shall integrate with standard macOS networking utilities |
| 61 | + |
| 62 | +### Operational Constraints |
| 63 | +- **CON-001**: System must run with root privileges for network configuration access |
| 64 | +- **CON-002**: System files must be installed in standard macOS system directories |
| 65 | +- **CON-003**: System shall not interfere with user manual network configuration |
| 66 | +- **CON-004**: System shall provide logging integration with macOS system logs |
| 67 | + |
| 68 | +### Development Guidelines |
| 69 | +- **GUD-001**: Use explicit error handling with appropriate exit codes |
| 70 | +- **GUD-002**: Implement comprehensive logging for debugging and monitoring |
| 71 | +- **GUD-003**: Follow macOS system service best practices for LaunchDaemon configuration |
| 72 | +- **GUD-004**: Maintain backward compatibility within supported macOS versions |
| 73 | + |
| 74 | +### Architecture Patterns |
| 75 | +- **PAT-001**: Use event-driven architecture with file system monitoring for network changes |
| 76 | +- **PAT-002**: Implement idempotent operations for safe repeated execution |
| 77 | +- **PAT-003**: Separate concerns between detection logic and configuration management |
| 78 | +- **PAT-004**: Use declarative configuration for LaunchDaemon properties |
| 79 | + |
| 80 | +## 4. Interfaces & Data Contracts |
| 81 | + |
| 82 | +### Command Line Interface |
| 83 | +```bash |
| 84 | +# Installation script interface |
| 85 | +./install.sh [i|up|ui] |
| 86 | +# i = install system components |
| 87 | +# up = update existing installation |
| 88 | +# ui = uninstall system components |
| 89 | +``` |
| 90 | + |
| 91 | +### System Integration Points |
| 92 | +| Component | Interface | Purpose | |
| 93 | +|-----------|-----------|---------| |
| 94 | +| networksetup | `/usr/sbin/networksetup -listnetworkserviceorder` | Enumerate network interfaces | |
| 95 | +| networksetup | `/usr/sbin/networksetup -listallhardwareports` | Get WiFi interface identifiers | |
| 96 | +| networksetup | `/usr/sbin/networksetup -setairportpower <interface> <on\|off>` | Control WiFi state | |
| 97 | +| ifconfig | `ifconfig <interface>` | Query interface IP configuration | |
| 98 | +| logger | `logger <message>` | System log integration | |
| 99 | +| launchctl | `launchctl load/unload <plist>` | Service lifecycle management | |
| 100 | + |
| 101 | +### File System Layout |
| 102 | +``` |
| 103 | +/Library/Scripts/NetBasics/ |
| 104 | +├── wireless.sh # Core detection and toggle logic |
| 105 | +└── install.sh # Installation management script |
| 106 | +
|
| 107 | +/Library/LaunchDaemons/ |
| 108 | +└── com.computernetworkbasics.wifionoff.plist # Service configuration |
| 109 | +
|
| 110 | +/var/log/system.log # System logging destination |
| 111 | +``` |
| 112 | + |
| 113 | +### LaunchDaemon Configuration Schema |
| 114 | +```xml |
| 115 | +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> |
| 116 | +<plist version="1.0"> |
| 117 | +<dict> |
| 118 | + <key>Label</key> |
| 119 | + <string>com.computernetworkbasics.wifionoff</string> |
| 120 | + <key>ProgramArguments</key> |
| 121 | + <array> |
| 122 | + <string>/Library/Scripts/NetBasics/wireless.sh</string> |
| 123 | + </array> |
| 124 | + <key>WatchPaths</key> |
| 125 | + <array> |
| 126 | + <string>/Library/Preferences/SystemConfiguration</string> |
| 127 | + </array> |
| 128 | +</dict> |
| 129 | +</plist> |
| 130 | +``` |
| 131 | + |
| 132 | +## 5. Acceptance Criteria |
| 133 | + |
| 134 | +### Network Detection |
| 135 | +- **AC-001**: Given multiple network interfaces, When wired interface has valid IP address, Then system shall detect active wired connection |
| 136 | +- **AC-002**: Given wired interface with self-assigned IP (169.254.x.x), When evaluating connection status, Then system shall treat as inactive connection |
| 137 | +- **AC-003**: Given no wired interfaces with valid IPs, When evaluating connection status, Then system shall detect no active wired connections |
| 138 | + |
| 139 | +### WiFi State Management |
| 140 | +- **AC-004**: Given active wired connection detected, When WiFi is currently enabled, Then system shall disable WiFi and log action |
| 141 | +- **AC-005**: Given no active wired connections, When WiFi is currently disabled, Then system shall enable WiFi and log action |
| 142 | +- **AC-006**: Given WiFi state change command fails, When executing networksetup command, Then system shall exit with error code 1 |
| 143 | + |
| 144 | +### System Integration |
| 145 | +- **AC-007**: Given network configuration changes, When SystemConfiguration directory is modified, Then LaunchDaemon shall trigger script execution within 5 seconds |
| 146 | +- **AC-008**: Given script execution completes, When processing finishes, Then system shall sleep 10 seconds to prevent restart loops |
| 147 | +- **AC-009**: Given system startup, When LaunchDaemon loads, Then service shall start automatically without user intervention |
| 148 | + |
| 149 | +### Installation Process |
| 150 | +- **AC-010**: Given installation command executed, When install script runs with 'i' parameter, Then all system files shall be copied and permissions set correctly |
| 151 | +- **AC-011**: Given uninstall command executed, When install script runs with 'ui' parameter, Then all system files shall be removed and service stopped |
| 152 | +- **AC-012**: Given update command executed, When install script runs with 'up' parameter, Then existing files shall be replaced and service restarted |
| 153 | + |
| 154 | +## 6. Test Automation Strategy |
| 155 | + |
| 156 | +### Test Levels |
| 157 | +- **Unit Testing**: Shell script function validation using bash test framework |
| 158 | +- **Integration Testing**: Network interface detection with mocked system commands |
| 159 | +- **System Testing**: End-to-end validation on target macOS versions |
| 160 | +- **Compatibility Testing**: Cross-version validation on Ventura, Sonoma, Sequoia |
| 161 | + |
| 162 | +### Testing Frameworks |
| 163 | +- **Shell Testing**: Bash Automated Testing System (BATS) for script validation |
| 164 | +- **Mock Testing**: Custom mocking for networksetup and ifconfig commands |
| 165 | +- **System Testing**: GitHub Actions with macOS runners for automated validation |
| 166 | +- **Manual Testing**: Physical hardware validation with multiple adapter types |
| 167 | + |
| 168 | +### Test Data Management |
| 169 | +- **Network Mocking**: Predefined interface configurations for consistent testing |
| 170 | +- **IP Address Scenarios**: Valid, invalid, self-assigned, and loopback address sets |
| 171 | +- **Hardware Simulation**: Mock data for various adapter types and configurations |
| 172 | + |
| 173 | +### CI/CD Integration |
| 174 | +- **Automated Testing**: GitHub Actions workflow with macOS matrix builds |
| 175 | +- **Syntax Validation**: ShellCheck integration for static analysis |
| 176 | +- **Security Scanning**: Automated vulnerability assessment for shell scripts |
| 177 | +- **Documentation Validation**: Automated README and specification consistency checks |
| 178 | + |
| 179 | +### Coverage Requirements |
| 180 | +- **Script Coverage**: 100% line coverage for core wireless.sh logic |
| 181 | +- **Scenario Coverage**: All supported hardware adapter types and IP configurations |
| 182 | +- **Error Handling**: All error conditions and exit codes validated |
| 183 | +- **Integration Points**: All system command interactions tested with mocks |
| 184 | + |
| 185 | +### Performance Testing |
| 186 | +- **Response Time**: Network change detection and WiFi toggle performance measurement |
| 187 | +- **Resource Usage**: Memory and CPU utilization monitoring during operation |
| 188 | +- **Stress Testing**: Rapid network state changes and concurrent execution scenarios |
| 189 | + |
| 190 | +## 7. Rationale & Context |
| 191 | + |
| 192 | +### Architecture Decisions |
| 193 | + |
| 194 | +#### LaunchDaemon vs LaunchAgent |
| 195 | +**Decision**: Use LaunchDaemon for system-level network monitoring |
| 196 | +**Rationale**: Requires root privileges for networksetup commands and must operate regardless of user login status |
| 197 | + |
| 198 | +#### File System Monitoring vs Polling |
| 199 | +**Decision**: Monitor `/Library/Preferences/SystemConfiguration` for network changes |
| 200 | +**Rationale**: Event-driven approach provides real-time response without continuous polling overhead |
| 201 | + |
| 202 | +#### Shell Script vs Compiled Binary |
| 203 | +**Decision**: Implement core logic in Bash shell script |
| 204 | +**Rationale**: Simplifies maintenance, leverages existing macOS command-line tools, and provides transparency for security auditing |
| 205 | + |
| 206 | +#### Hardware Port Detection Strategy |
| 207 | +**Decision**: Use `networksetup -listnetworkserviceorder` with hardware port filtering |
| 208 | +**Rationale**: Provides reliable identification of physical interfaces across different macOS versions and hardware configurations |
| 209 | + |
| 210 | +### Design Trade-offs |
| 211 | + |
| 212 | +#### Performance vs Reliability |
| 213 | +- **Trade-off**: 10-second sleep delay after execution |
| 214 | +- **Rationale**: Prevents LaunchDaemon restart loops at cost of slight delay in rapid network changes |
| 215 | + |
| 216 | +#### Flexibility vs Simplicity |
| 217 | +- **Trade-off**: Hardcoded adapter type detection vs dynamic discovery |
| 218 | +- **Rationale**: Explicit adapter type list (Ethernet, LAN, Thunderbolt, AX88179A) provides predictable behavior |
| 219 | + |
| 220 | +#### Security vs Usability |
| 221 | +- **Trade-off**: Requires sudo privileges for installation |
| 222 | +- **Rationale**: System-level network control necessitates administrative access |
| 223 | + |
| 224 | +## 8. Dependencies & External Integrations |
| 225 | + |
| 226 | +### macOS System Dependencies |
| 227 | +- **SYS-001**: macOS networksetup utility - Network interface configuration and control |
| 228 | +- **SYS-002**: macOS ifconfig utility - Network interface status and IP address query |
| 229 | +- **SYS-003**: macOS launchctl utility - Service lifecycle management |
| 230 | +- **SYS-004**: macOS logger utility - System log integration |
| 231 | +- **SYS-005**: Bash shell environment - Script execution runtime |
| 232 | + |
| 233 | +### System Framework Dependencies |
| 234 | +- **FWK-001**: SystemConfiguration framework - Network state change monitoring |
| 235 | +- **FWK-002**: Airport/WiFi framework - Wireless interface control via networksetup |
| 236 | +- **FWK-003**: LaunchDaemon framework - System service execution environment |
| 237 | + |
| 238 | +### Hardware Dependencies |
| 239 | +- **HW-001**: Network interfaces - Physical Ethernet, Thunderbolt, LAN, or USB-C adapters |
| 240 | +- **HW-002**: WiFi capability - Wireless network interface for state management |
| 241 | +- **HW-003**: Administrator access - User account with sudo privileges |
| 242 | + |
| 243 | +### File System Dependencies |
| 244 | +- **FS-001**: System directories - Write access to `/Library/Scripts/` and `/Library/LaunchDaemons/` |
| 245 | +- **FS-002**: Configuration monitoring - Read access to `/Library/Preferences/SystemConfiguration` |
| 246 | +- **FS-003**: System logging - Write access to system log facilities |
| 247 | + |
| 248 | +### Network Dependencies |
| 249 | +- **NET-001**: DHCP services - For valid IP address assignment to wired interfaces |
| 250 | +- **NET-002**: Network infrastructure - Physical network connectivity for wired adapters |
| 251 | + |
| 252 | +### Version Dependencies |
| 253 | +- **VER-001**: macOS Ventura 13.x+ - Minimum supported operating system version |
| 254 | +- **VER-002**: Bash 4.0+ - Required for proper array handling and script execution |
| 255 | + |
| 256 | +## 9. Examples & Edge Cases |
| 257 | + |
| 258 | +### Basic Network Detection Logic |
| 259 | +```bash |
| 260 | +# Detect wired interfaces with valid IP addresses |
| 261 | +INTERFACES=$(networksetup -listnetworkserviceorder | \ |
| 262 | + grep "Hardware Port" | \ |
| 263 | + grep "Ethernet\|LAN\|Thunderbolt\|AX88179A" | \ |
| 264 | + awk -F ": " '{print $3}' | sed 's/)//g') |
| 265 | + |
| 266 | +for INTERFACE in $INTERFACES; do |
| 267 | + IPCHECK=$(ifconfig "$INTERFACE" | \ |
| 268 | + grep -E 'inet [0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}' | \ |
| 269 | + grep -E -v '127.0.0.1|169.254.' | \ |
| 270 | + awk '{print $2}') |
| 271 | + if [ "$IPCHECK" ]; then |
| 272 | + IPFOUND=true |
| 273 | + break |
| 274 | + fi |
| 275 | +done |
| 276 | +``` |
| 277 | + |
| 278 | +### WiFi State Management |
| 279 | +```bash |
| 280 | +# Get WiFi interface identifier |
| 281 | +WIFIINTERFACES=$(networksetup -listallhardwareports | \ |
| 282 | + tr '\n' ' ' | \ |
| 283 | + sed -e 's/Hardware Port:/\'$'\n/g' | \ |
| 284 | + grep Wi-Fi | awk '{print $3}') |
| 285 | + |
| 286 | +# Toggle WiFi based on wired connection status |
| 287 | +if [ $IPFOUND ]; then |
| 288 | + networksetup -setairportpower "$WIFIINTERFACES" off || exit 1 |
| 289 | + logger "wireless.sh: turning off wireless card ($WIFIINTERFACES)" |
| 290 | +else |
| 291 | + networksetup -setairportpower "$WIFIINTERFACES" on || exit 1 |
| 292 | + logger "wireless.sh: turning on wireless card ($WIFIINTERFACES)" |
| 293 | +fi |
| 294 | +``` |
| 295 | + |
| 296 | +### Edge Cases |
| 297 | + |
| 298 | +#### Multiple Wired Interfaces |
| 299 | +- **Scenario**: System has both Ethernet and Thunderbolt adapters connected |
| 300 | +- **Behavior**: Detection logic finds first interface with valid IP and enables wired mode |
| 301 | +- **Handling**: Loop through all interfaces, set IPFOUND=true on first valid IP |
| 302 | + |
| 303 | +#### Rapid Network Changes |
| 304 | +- **Scenario**: User frequently connects/disconnects wired adapter |
| 305 | +- **Behavior**: Each change triggers LaunchDaemon execution |
| 306 | +- **Handling**: 10-second sleep prevents rapid cycling and system instability |
| 307 | + |
| 308 | +#### No WiFi Interface |
| 309 | +- **Scenario**: System has no wireless capability (desktop Mac Pro) |
| 310 | +- **Behavior**: Script continues execution but WiFi commands fail silently |
| 311 | +- **Handling**: Check for WiFi interface existence before state changes |
| 312 | + |
| 313 | +#### Invalid IP Assignments |
| 314 | +- **Scenario**: Wired interface gets self-assigned IP (169.254.x.x) |
| 315 | +- **Behavior**: System treats as no valid wired connection |
| 316 | +- **Handling**: Explicit exclusion of 169.254.x.x range in IP detection |
| 317 | + |
| 318 | +#### Permission Failures |
| 319 | +- **Scenario**: Script runs without sufficient privileges |
| 320 | +- **Behavior**: networksetup commands fail with permission errors |
| 321 | +- **Handling**: Exit with error code 1 to signal LaunchDaemon failure |
| 322 | + |
| 323 | +## 10. Validation Criteria |
| 324 | + |
| 325 | +### Functional Validation |
| 326 | +- **VAL-001**: Script correctly identifies all supported wired adapter types on test hardware |
| 327 | +- **VAL-002**: WiFi toggle operations complete successfully across all supported macOS versions |
| 328 | +- **VAL-003**: IP address filtering excludes loopback and self-assigned addresses correctly |
| 329 | +- **VAL-004**: LaunchDaemon responds to network configuration changes within specified timeframes |
| 330 | + |
| 331 | +### Performance Validation |
| 332 | +- **VAL-005**: Network detection completes within 5-second requirement under normal conditions |
| 333 | +- **VAL-006**: System operates without memory leaks during extended operation periods |
| 334 | +- **VAL-007**: CPU utilization remains minimal during monitoring and execution cycles |
| 335 | + |
| 336 | +### Integration Validation |
| 337 | +- **VAL-008**: Installation script successfully deploys all components with correct permissions |
| 338 | +- **VAL-009**: System logging integration captures all significant events and errors |
| 339 | +- **VAL-010**: Uninstallation completely removes all system components without residue |
| 340 | + |
| 341 | +### Security Validation |
| 342 | +- **VAL-011**: All system commands use absolute paths to prevent PATH injection attacks |
| 343 | +- **VAL-012**: Script validation detects and prevents execution of malformed commands |
| 344 | +- **VAL-013**: File permissions prevent unauthorized modification of system components |
| 345 | + |
| 346 | +### Compatibility Validation |
| 347 | +- **VAL-014**: Solution operates correctly across Ventura, Sonoma, and Sequoia macOS versions |
| 348 | +- **VAL-015**: Hardware compatibility verified with various adapter types and configurations |
| 349 | +- **VAL-016**: Script handles differences in command output formats across macOS versions |
| 350 | + |
| 351 | +## 11. Related Specifications / Further Reading |
| 352 | + |
| 353 | +- [CI/CD Workflow Specification - macOS Utility Validation](spec-process-cicd-macos-utility-validation.md) |
| 354 | +- [Apple Developer Documentation - LaunchDaemon and LaunchAgent](https://developer.apple.com/library/archive/documentation/MacOSX/Conceptual/BPSystemStartup/Chapters/CreatingLaunchdJobs.html) |
| 355 | +- [macOS Network Configuration Guide](https://support.apple.com/guide/mac-help/mchlp2439/mac) |
| 356 | +- [Bash Scripting Best Practices for System Administration](https://google.github.io/styleguide/shellguide.html) |
| 357 | +- [macOS Security and Privacy Guidelines](https://support.apple.com/guide/security/welcome/web) |
0 commit comments