Native macOS client for snmpproxyd with live graphing.
- macOS 14.0+ (Sonoma)
- Xcode 15+
- swift-protobuf
brew install swift-protobufmake proto- Open Xcode
- File → New → Project
- Choose "App" (macOS)
- Product Name:
SNMPProxyClient - Interface: SwiftUI
- Language: Swift
Drag all .swift files from this directory into the Xcode project.
- File → Add Package Dependencies
- Enter:
https://github.com/apple/swift-protobuf.git - Add to target: SNMPProxyClient
Press ⌘R
- Click connection status → Connect
- Enter server details and token
- Click "+" to add a target
- Enter SNMP details (host, OID, community or v3 credentials)
- Select target in sidebar to view graph
- Connect to snmpproxyd
- SNMPv2c and SNMPv3 support
- Live streaming graphs
- Multiple targets
- Rate calculation (counter → bps)
- Counter wrap detection (64-bit)
- Configurable time windows
- Request timeouts
- Proper cancellation support
┌─────────────────────────────────────────────┐
│ Views │
│ ContentView → LiveGraphView │
│ → AddTargetSheet │
│ → ConnectSheet │
└─────────────────┬───────────────────────────┘
│
┌─────────────────▼───────────────────────────┐
│ Services │
│ TimeSeriesStore (rate calculation, storage)│
└─────────────────┬───────────────────────────┘
│
┌─────────────────▼───────────────────────────┐
│ Network │
│ ProxyClient (TCP + Protobuf) │
│ WireProtocol (varint length-delimited) │
└─────────────────────────────────────────────┘
SNMPProxyClient/
├── App/
│ └── SNMPProxyClientApp.swift # Entry point
├── Models/
│ └── Proto/
│ └── snmpproxy.pb.swift # Generated
├── Network/
│ ├── ConnectionState.swift
│ ├── ProxyClient.swift # TCP client with timeout support
│ └── WireProtocol.swift # Varint framing
├── Services/
│ └── TimeSeriesStore.swift # Data management
├── Views/
│ ├── ContentView.swift
│ ├── LiveGraphView.swift # Swift Charts
│ ├── ConnectSheet.swift
│ ├── AddTargetSheet.swift
│ └── SettingsView.swift
└── Proto/
└── snmpproxy.proto
The client uses varint-prefixed length-delimited protobuf, compatible with
Go's google.golang.org/protobuf/encoding/protodelim.
┌─────────────────────────────────────────────┐
│ [varint: message length][protobuf payload] │
└─────────────────────────────────────────────┘
Varint encoding:
- Each byte: 7 data bits + 1 continuation bit (MSB)
- MSB=1: more bytes follow
- MSB=0: last byte
Example implementation in WireProtocol.swift:
static func frame(_ data: Data) -> Data {
var framed = Data()
framed.append(contentsOf: encodeVarint(UInt64(data.count)))
framed.append(data)
return framed
}
private static func encodeVarint(_ value: UInt64) -> [UInt8] {
var v = value
var result: [UInt8] = []
while v > 0x7F {
result.append(UInt8((v & 0x7F) | 0x80))
v >>= 7
}
result.append(UInt8(v))
return result
}The ProxyClient supports configurable timeouts:
let client = ProxyClient(
requestTimeout: 30.0, // Individual request timeout
connectionTimeout: 10.0 // Initial connection timeout
)The client handles various error conditions:
| Error | Description |
|---|---|
authFailed |
Authentication rejected by server |
serverError |
Server returned an error response |
unexpectedResponse |
Protocol mismatch |
notConnected |
No active connection |
timeout |
Request or connection timed out |
- No automatic reconnection (planned)
- TLS certificate validation disabled (uses
InsecureSkipVerify) - No credential storage (token must be provided each session)
The client uses a dedicated dispatch queue to avoid deadlocks with @MainActor.
If connections hang, check that the server is reachable.
Rate calculation requires at least 2 samples. Wait for the polling interval.
Ensure the target is subscribed (check sidebar selection).
MIT