Initial commit: NetworkCity — network traffic as a neon city
A native macOS network monitor that renders live traffic as a top-down neon city: your Mac is downtown, remote orgs are glowing districts, and traffic is cars of light driving the roads. Architecture: - NetworkCityCore: swappable data layer behind a ConnectionSource protocol (nettop-backed today), plus pure/tested logic — traffic diffing, org classification (reverse-DNS + CIDR), traffic classes, hub-and-spoke expansion policy, label anti-overlap, and latency→distance layout. - NetworkCityApp: SwiftUI + SpriteKit city — auto-expanding districts, protocol-coloured cars, click-to-inspect panel, and latency-as-distance (districts glide to their measured RTT). - nettop-probe: CLI proof of the data layer. 44 tests passing. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,93 @@
|
||||
import Foundation
|
||||
|
||||
/// Transport flavour as reported by `nettop` (the trailing 4/6 is the IP version).
|
||||
public enum NetProtocol: String, Sendable, Hashable {
|
||||
case tcp4, tcp6, udp4, udp6, quic4, quic6, other
|
||||
|
||||
public init(_ raw: String) {
|
||||
self = NetProtocol(rawValue: raw) ?? .other
|
||||
}
|
||||
}
|
||||
|
||||
/// One end of a connection. `port` is nil for a wildcard (`*`) — i.e. a listener.
|
||||
public struct Endpoint: Sendable, Hashable {
|
||||
public let host: String
|
||||
public let port: Int?
|
||||
|
||||
public init(host: String, port: Int?) {
|
||||
self.host = host
|
||||
self.port = port
|
||||
}
|
||||
|
||||
public var isWildcard: Bool { host == "*" || host.isEmpty }
|
||||
|
||||
public var display: String {
|
||||
guard let port else { return host }
|
||||
return "\(host):\(port)"
|
||||
}
|
||||
}
|
||||
|
||||
/// A single connection sampled from `nettop`. Byte counts are cumulative
|
||||
/// (lifetime of the connection), so traffic *rates* come from diffing snapshots.
|
||||
public struct Connection: Sendable, Hashable {
|
||||
public let proto: NetProtocol
|
||||
public let local: Endpoint
|
||||
public let remote: Endpoint
|
||||
public let bytesIn: UInt64
|
||||
public let bytesOut: UInt64
|
||||
public let processName: String
|
||||
public let pid: Int32
|
||||
|
||||
public init(
|
||||
proto: NetProtocol,
|
||||
local: Endpoint,
|
||||
remote: Endpoint,
|
||||
bytesIn: UInt64,
|
||||
bytesOut: UInt64,
|
||||
processName: String,
|
||||
pid: Int32
|
||||
) {
|
||||
self.proto = proto
|
||||
self.local = local
|
||||
self.remote = remote
|
||||
self.bytesIn = bytesIn
|
||||
self.bytesOut = bytesOut
|
||||
self.processName = processName
|
||||
self.pid = pid
|
||||
}
|
||||
|
||||
/// Stable identity for diffing across snapshots (ignores byte counts).
|
||||
public var key: String {
|
||||
"\(proto.rawValue) \(local.display)<->\(remote.display)"
|
||||
}
|
||||
|
||||
/// True when the remote is a real off-box destination — the only kind that
|
||||
/// becomes a "building" in the city. Filters listeners, loopback, link-local.
|
||||
public var isExternal: Bool {
|
||||
let h = remote.host
|
||||
if remote.isWildcard { return false }
|
||||
if h == "::1" || h.hasPrefix("127.") { return false } // loopback
|
||||
if h.hasPrefix("fe80") || h == "::" { return false } // link-local / unspecified
|
||||
return true
|
||||
}
|
||||
|
||||
/// True when the remote is on the local network (a "neighbourhood" district).
|
||||
public var isPrivateLAN: Bool {
|
||||
let h = remote.host
|
||||
return h.hasPrefix("10.")
|
||||
|| h.hasPrefix("192.168.")
|
||||
|| h.hasPrefix("169.254.")
|
||||
|| (h.hasPrefix("172.") && (16...31).contains(Int(h.split(separator: ".").dropFirst().first ?? "") ?? -1))
|
||||
}
|
||||
}
|
||||
|
||||
/// All connections observed at one instant.
|
||||
public struct ConnectionSnapshot: Sendable {
|
||||
public let timestamp: Date
|
||||
public let connections: [Connection]
|
||||
|
||||
public init(timestamp: Date, connections: [Connection]) {
|
||||
self.timestamp = timestamp
|
||||
self.connections = connections
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user