Skip to content

Commit 75f8b75

Browse files
authored
feat: Add architecture specification for macOS Wireless Auto-Switch utility (#18)
1 parent 2e73183 commit 75f8b75

File tree

1 file changed

+357
-0
lines changed

1 file changed

+357
-0
lines changed
Lines changed: 357 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,357 @@
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

Comments
 (0)