Skip to main content

Why SwiftNIO Instead of URLSession

URLSession is a high-level HTTP client. Rockxy needs a low-level TCP server that can accept connections, parse HTTP, perform MITM TLS interception via CONNECT tunnels, and forward traffic — all things that require direct socket control. SwiftNIO provides the event-driven, non-blocking I/O foundation that makes this possible in pure Swift.

Why NSTableView for the Request List

SwiftUI List cannot handle 100k+ rows with virtual scrolling. The request list uses NSTableView wrapped in NSViewRepresentable for O(1) scrolling performance regardless of traffic volume. Cell reuse keeps memory constant even at high row counts.

Why a Privileged Helper Daemon

macOS requires admin authentication for each networksetup call. The helper tool (SMAppService.daemon()) runs as root and validates callers via certificate-chain comparison, eliminating repeated password prompts while maintaining security through defense-in-depth.

Actor-Based Concurrency Model

The proxy server, session managers, and certificate manager are all Swift actors. This eliminates data races without manual locking. The coordinator bridges actor-isolated state to @MainActor for SwiftUI consumption via batched updates (every 100ms).

Three Isolation Domains

IsolationTypesBridge Pattern
Swift actorProxyServer, CertificateManager, RuleEngine, TrafficSessionManager, LogCaptureEngine, SessionStore, InMemorySessionBuffer, InMemoryLogBuffer, ScriptPluginManagerNIO to actor: eventLoop.makeFutureWithTask { await ... }
@MainActor @ObservableMainContentCoordinator, HelperManager, HelperConnection, SSLProxyingManager, BypassProxyManager, AllowListManager, AppSettingsManager, BreakpointManagerActor to MainActor: Task { @MainActor in ... }
NIO event loop (@unchecked Sendable)HTTPProxyHandler, HTTPSProxyRelayHandler, TLSInterceptHandler, UpstreamResponseHandler, WebSocketFrameHandlerMainActor to NIO reads: nonisolated methods with NSLock snapshot

Plugin Sandbox

JavaScript plugins run in JavaScriptCore with a controlled bridge API ($rockxy). Each script execution has a 5-second timeout. Plugins can inspect and modify requests but cannot access the filesystem or network directly.

Performance Design

TechniqueWhat It Solves
NSTableView virtual scrolling100k+ rows with cell reuse, O(1) scroll performance
Ring buffer evictionAt 50k transactions, oldest 10% moved to SQLite or discarded
Body offloadingResponse/request bodies >1MB stored on disk, loaded on-demand
Batched UI updatesProxy transactions batched every 100ms or 50 items before UI delivery
O(1) string lengthNSString.length instead of String.count for large bodies
Log buffer cap100k entries in-memory, overflow to SQLite
Concurrent NIO threadsSystem.coreCount event loop threads

Storage Architecture

DataMechanismLocation
User preferencesUserDefaultsAppSettingsStorage
Active sessionsIn-memory ring buffer (50k)InMemorySessionBuffer
Saved sessionsSQLiteSessionStore
Root CA private keymacOS KeychainKeychainHelper
RulesJSON fileRuleStore
Large bodies (>1MB)Disk files~/Library/Application Support/com.amunx.rockxy/bodies/ or ~/Library/Application Support/com.amunx.rockxy.community/bodies/ depending on the active build
Log entriesSQLiteSessionStore (log_entries table)
Proxy backupPlist (0o600)/Library/Application Support/com.amunx.Rockxy/proxy-backup.plist
PluginsJS files + manifest~/Library/Application Support/Rockxy/Plugins/

CI/CD Pipeline

GitHub Actions workflow (manual dispatch with optional channel parameter) — canonical file: .github/workflows/build.yml.
  1. Lintswiftlint lint --strict on macOS 14
  2. Build — parallel arm64 and x86_64 release builds with Xcode 16
  3. Artifacts — uploads signed build artifacts for distribution

Next Steps

Architecture

Proxy engine, actor model, coordinator pattern, and data flow

Security

Security boundaries, XPC trust, and certificate model