Files
datadunia 84689b208e docs: add ESP32 and iOS agent architecture reference
- reference/esp32-agent/: DESIGN, HARDWARE, API_COMPAT, ARCHITECTURE, README

- reference/ios-agent/: DESIGN, API_COMPAT, UI_DESIGN, ARCHITECTURE, README

- Both mirror Android agent architecture, UI design, and heartbeat flow

- Architecture-only (no code) — avoids submodule conflicts

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-07-06 14:20:10 +07:00

14 KiB

iOS Agent — Technical Design Document

Version: 1.0.0 Date: 2026-07-06 Status: Draft Mirror: Android Agent (apps/android-agent/)

1. Executive Summary

NexusGuard iOS Agent is an iOS-based WireGuard client for mobile devices. It mirrors the Android agent's architecture, UI design, and heartbeat flow, using iOS-native APIs (NetworkExtension, CryptoKit, Keychain).

2. Goals & Non-Goals

Goals

  • iOS-based WireGuard tunnel (NetworkExtension)
  • Auto-provisioning via HTTP (same API as Android/ESP32)
  • Heartbeat monitoring with config sync (30s interval)
  • Keychain-based config storage (hardware-backed encryption)
  • SwiftUI UI matching Android agent design
  • Background operation via NetworkExtension

Non-Goals

  • gRPC support (not needed for mobile)
  • Port forwarding (userspace agent only)
  • Jailbroken device support
  • macOS/iPadOS support (iPhone only for v1)

3. Architecture

3.1 System Architecture

┌─────────────────────────────────────────────────────────┐
│                    iOS (SwiftUI)                         │
├─────────────────────────────────────────────────────────┤
│  App Layer                                              │
│  ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐  │
│  │ContentView│ │SettingsView│ │LogView  │ │PortView │  │
│  └────┬─────┘ └────┬─────┘ └────┬─────┘ └────┬─────┘  │
│       │             │            │             │         │
│  ┌────┴─────────────┴────────────┴─────────────┴────┐  │
│  │              AgentManager                        │  │
│  │     (orchestrates all components)                │  │
│  └──────────────────────────────────────────────────┘  │
├─────────────────────────────────────────────────────────┤
│  Service Layer                                          │
│  ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐  │
│  │Provisioning│ │Heartbeat│ │TunnelMgr │ │ConfigMgr│  │
│  └────┬─────┘ └────┬─────┘ └────┬─────┘ └────┬─────┘  │
│       │             │            │             │         │
│  ┌────┴─────────────┴────────────┴─────────────┴────┐  │
│  │              CryptoManager                       │  │
│  │           AES-256-GCM (CryptoKit)                │  │
│  └──────────────────────────────────────────────────┘  │
├─────────────────────────────────────────────────────────┤
│  NetworkExtension Layer                                 │
│  ┌──────────────────────────────────────────────────┐  │
│  │         PacketTunnelProvider                     │  │
│  │    (WireGuard tunnel via wireguard-apple)        │  │
│  └──────────────────────────────────────────────────┘  │
├─────────────────────────────────────────────────────────┤
│  Storage Layer                                          │
│  ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐  │
│  │ Keychain │ │ UserDefaults│ │ CoreData│ │ FileMgr │  │
│  └──────────┘ └──────────┘ └──────────┘ └──────────┘  │
└─────────────────────────────────────────────────────────┘

3.2 Task Architecture

iOS Threads/Tasks
├── Main Thread           # UI updates
├── AgentManager          # Orchestration (async/await)
├── HeartbeatTask         # HTTP heartbeat (30s interval)
├── TunnelManager         # WireGuard tunnel management
└── BackgroundTask        # BGAppRefreshTask (background refresh)

3.3 State Machine

┌─────────────┐
│   BOOT      │
└──────┬──────┘
       │
       ▼
┌─────────────┐
│   APP_INIT  │ ◄──────────────────────────────┐
└──────┬──────┘                                 │
       │ loaded                                 │
       ▼                                        │
┌─────────────┐     fail        ┌─────────────┐│
│ PROVISION   │───────────────►│ APP_RETRY   ││
└──────┬──────┘                └──────┬──────┘│
       │ success                       │       │
       ▼                               └───────┘
┌─────────────┐
│ TUNNEL_UP   │
└──────┬──────┘
       │
       ▼
┌─────────────┐
│ HEARTBEAT   │ ◄─── 30s interval
└──────┬──────┘
       │ config_changed
       ▼
┌─────────────┐
│ TUNNEL_REBUILD │
└──────┬──────┘
       │
       └──► HEARTBEAT

4. Module Design

4.1 AgentManager

Responsibilities:

  • Orchestrate all agent components
  • Manage agent lifecycle (start/stop)
  • Handle state transitions

Key Properties:

class AgentManager: ObservableObject {
    @Published var state: AgentState = .disconnected
    @Published var tunnelInfo: TunnelInfo?
    @Published var logs: [LogEntry] = []
    
    private let provisioning: ProvisioningService
    private let heartbeat: HeartbeatService
    private let tunnel: TunnelManager
    private let config: KeychainStorage
}

Key Methods:

func startAgent() async
func stopAgent()
func rebuildTunnel(with config: WireGuardConfig) async

4.2 PacketTunnelProvider

Responsibilities:

  • Handle NetworkExtension tunnel lifecycle
  • Start/stop WireGuard tunnel
  • Report tunnel status

Key Methods:

override func startTunnel(options: [String: NSObject]?, completionHandler: @escaping (Error?) -> Void)
override func stopTunnel(with reason: NEProviderStopReason, completionHandler: @escaping () -> Void)
override func handleAppMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)?)

4.3 TunnelManager

Responsibilities:

  • Build WireGuard config from stored keys
  • Start/stop tunnel via PacketTunnelProvider
  • Monitor tunnel state
  • Track last handshake time

Key Methods:

func startTunnel(config: WireGuardConfig) async throws
func stopTunnel()
func isTunnelUp() -> Bool
func getLastHandshake() -> Date?

4.4 HeartbeatService

Responsibilities:

  • HTTP POST to /api/v1/heartbeat every 30s
  • Send device status + tunnel state
  • Receive config sync (detect changes)
  • Trigger tunnel rebuild if config changed

Key Methods:

func startHeartbeatLoop()
func stopHeartbeat()
func sendHeartbeat(config: WireGuardConfig, tunnelUp: Bool, lastHandshake: Date?) async -> WireGuardConfig?

4.5 ProvisioningService

Responsibilities:

  • HTTP POST to /api/v1/provision
  • Parse encrypted config response
  • Decrypt config via CryptoManager
  • Store config in Keychain

Key Methods:

func provision(serverUrl: String, token: String, hwid: String) async -> WireGuardConfig?
func decryptConfig(encrypted: String, hwid: String, salt: String) -> WireGuardConfig?

4.6 CryptoManager

Responsibilities:

  • Key derivation: SHA-256(hwid + salt)
  • AES-256-GCM decryption
  • Secure memory handling

Key Methods:

func deriveKey(token: String, hwid: String) -> SymmetricKey
func decrypt(encoded: String, key: SymmetricKey) -> Data?

4.7 KeychainStorage

Responsibilities:

  • Store WireGuard keys in Keychain
  • Store agent config in UserDefaults
  • Secure key access

Key Methods:

func saveConfig(_ config: AgentConfig)
func loadConfig() -> AgentConfig?
func saveWireGuardConfig(_ config: WireGuardConfig)
func loadWireGuardConfig() -> WireGuardConfig?
func clearAll()

4.8 LogStore

Responsibilities:

  • Buffer log entries in memory
  • Persist to CoreData (optional)
  • Provide log stream for UI

Key Methods:

func add(level: LogLevel, tag: String, message: String)
func clear()
func getLogs() -> [LogEntry]

5. Data Structures

5.1 WireGuardConfig

struct WireGuardConfig: Codable {
    let deviceId: String
    let privateKey: String
    let presharedKey: String
    let internalIp: String
    let serverPub: String
    let endpoint: String
    let dns: String
    let allowedIps: String
    let serverWgIp: String
    let configHash: String
    let forwardsHash: String
}

5.2 AgentConfig

struct AgentConfig: Codable {
    let serverUrl: String
    let registrationToken: String
    let isProvisioned: Bool
}

5.3 AgentState

enum AgentState: String {
    case disconnected = "disconnected"
    case connecting = "connecting"
    case connected = "connected"
    case failed = "failed"
}

5.4 LogEntry

struct LogEntry: Identifiable {
    let id = UUID()
    let timestamp: Date
    let level: LogLevel
    let tag: String
    let message: String
}

6. Error Handling

6.1 Error Types

enum AgentError: Error {
    case wifiNotConnected
    case provisioningFailed(String)
    case heartbeatFailed(String)
    case cryptoDecryptionFailed
    case configInvalid
    case tunnelInitFailed
    case keychainError(OSStatus)
}

6.2 Recovery Strategy

Error Recovery
WiFi not connected Retry 3x, then pause heartbeat
HTTP provision fail Retry 3x, then notify user
HTTP heartbeat fail Log only, continue
Crypto decrypt fail Clear config, re-provision
Tunnel init fail Retry 3x, then notify user
Keychain error Use UserDefaults fallback

7. Background Tasks

7.1 BGAppRefreshTask

iOS doesn't have boot receiver like Android. Use Background Tasks:

// Register task
BGAppRefreshTaskRequest.register(forTaskWithIdentifier: "com.nexusguard.agent.refresh", using: nil) {
    task in
    self.handleBackgroundRefresh(task: task as! BGAppRefreshTask)
}

// Schedule task
let request = BGAppRefreshTaskRequest(identifier: "com.nexusguard.agent.refresh")
request.earliestBeginDate = Date(timeIntervalSinceNow: 30 * 60) // 30 minutes
try BGTaskScheduler.shared.submit(request)

7.2 Background Modes

Add to Info.plist:

<key>UIBackgroundModes</key>
<array>
    <string>fetch</string>
    <string>processing</string>
    <string>vpn-api</string>
</array>

8. Security Considerations

8.1 Key Storage

  • WireGuard private keys stored in iOS Keychain (hardware-backed)
  • Non-sensitive config in UserDefaults
  • Never log keys or tokens

8.2 TLS

  • Server communication over HTTPS (TLS 1.2+)
  • Use URLSession with default TLS settings
  • Certificate pinning (optional, future)

8.3 Hardware ID

  • Use identifierForVendor for device identification
  • Persists across app reinstalls until device factory reset
  • Used for key derivation (HWID + salt)

9. Testing Strategy

9.1 Unit Tests

  • Crypto: Decrypt known ciphertext with known key
  • Config: Keychain read/write cycle
  • Heartbeat: Parse server response

9.2 Integration Tests

  • Provision → Tunnel → Heartbeat cycle
  • Config change detection
  • Tunnel rebuild on config change

9.3 UI Tests

  • VPN toggle interaction
  • Tab switching
  • Settings save/load

9.4 Device Tests

  • iPhone 12+ running iOS 16+
  • NetworkExtension background operation
  • Keychain access in background

10. Future Enhancements

10.1 Phase 2

  • iPadOS support
  • Widget for quick status
  • Shortcuts integration
  • Siri commands

10.2 Phase 3

  • macOS Catalyst support
  • Apple Watch companion
  • CarPlay integration

11. References