core

package
v0.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 9, 2026 License: MIT Imports: 8 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func IsLocalMAC added in v0.2.0

func IsLocalMAC(mac net.HardwareAddr, localMACs []net.HardwareAddr) bool

classFromVendor maps distinctive OUI vendors to a likely class. Ambiguous vendors (e.g. Apple, Samsung — could be phone, PC or TV) stay Unknown so a port fingerprint or nothing decides instead of a coin flip. IsLocalMAC reports whether mac belongs to one of this machine's own interfaces (e.g. Ethernet and Wi-Fi both connected at once show up as two separate devices in a scan; this recognizes the second one as "us" too, since its IP won't match Network.Self).

func Mbps

func Mbps(bytes int64, d time.Duration) float64

Mbps converts a byte count and a duration into megabits per second.

Types

type AccessPoint

type AccessPoint struct {
	SSID          string
	Channel       int
	SignalPercent int
}

AccessPoint is a single visible wireless network.

type Alias

type Alias struct {
	MAC  net.HardwareAddr
	Name string
}

Alias is a user-defined nickname bound to a device's MAC address. Keying on the MAC (not the IP) keeps the label stable across DHCP lease changes.

type AliasLookup

type AliasLookup interface {
	Alias(mac net.HardwareAddr) string
}

AliasLookup returns a user-defined nickname for a MAC, or "" if none is set.

type BandwidthResult

type BandwidthResult struct {
	DownloadMbps float64
	UploadMbps   float64
}

BandwidthResult holds a speed-test outcome in megabits per second.

type CounterReader

type CounterReader interface {
	Counters() (NetCounters, error)
}

CounterReader reads the active interface's cumulative byte counters.

type Device

type Device struct {
	IP        net.IP           // IPv4 address on the local subnet
	MAC       net.HardwareAddr // Layer-2 address (from ARP)
	Alias     string           // User-defined nickname, keyed by MAC; empty if unset
	Hostname  string           // Reverse-DNS name, empty if unresolved
	Vendor    string           // Manufacturer, resolved from the MAC's OUI prefix
	Class     DeviceClass      // Best-effort guess at what kind of device this is
	RTT       time.Duration    // ICMP round-trip time; 0 with Reachable false if no reply
	Reachable bool             // Answered an ICMP echo during enrichment
	Online    bool             // Answered the active probe during this scan
	Ports     []int            // Open TCP ports found by the classification probe; nil if not probed
}

Device is a host discovered on the local network.

It is a pure domain type: no I/O, no OS calls. Adapters produce it, the TUI consumes it.

type DeviceClass

type DeviceClass int

DeviceClass is a coarse, best-effort guess at what a host is.

const (
	ClassUnknown    DeviceClass = iota
	ClassThisDevice             // the machine running netwp
	ClassRouter                 // the default gateway
	ClassComputer               // PC / server / SBC
	ClassMobile                 // phone / tablet
	ClassMedia                  // TV / streaming stick / speaker
	ClassPrinter
	ClassIoT // smart home / embedded
)

func Classify

func Classify(d Device, gateway, self net.IP, openPorts []int, localMACs []net.HardwareAddr) DeviceClass

Classify guesses a device's class from the signals a scan can gather: whether it is us, whether it is the gateway, which TCP ports answered, and its vendor.

ponytail: pure heuristic, deliberately conservative — identity signals (self, gateway) win, then port fingerprints, then a vendor-keyword fallback. Wrong guesses fall back to Unknown rather than asserting nonsense.

func (DeviceClass) String

func (c DeviceClass) String() string

type Discovery

type Discovery struct {
	// contains filtered or unexported fields
}

Discovery is the device-discovery use case. It orchestrates a scan and then enriches each result with hostname, vendor, class and round-trip time. It depends only on ports, so it is fully testable with fakes.

func NewDiscovery

func NewDiscovery(scanner Scanner, names HostResolver, vendors VendorLookup, prober Prober, aliases AliasLookup, pinger Pinger) *Discovery

func (*Discovery) Run

func (d *Discovery) Run(ctx context.Context, target Network) ([]Device, error)

Run scans the target network and returns the enriched, classified devices.

type Event

type Event struct {
	Kind   EventKind
	Device Device
	At     time.Time
}

Event is a presence change emitted by the Tracker.

type EventKind

type EventKind int

EventKind classifies a presence change.

const (
	Joined EventKind = iota // appeared for the first time, or came back online
	Left                    // went offline after the grace period elapsed
)

type HostResolver

type HostResolver interface {
	Hostname(ip net.IP) string
}

HostResolver turns an IP into a hostname (reverse DNS). Cross-platform.

type InterfaceConfigurator

type InterfaceConfigurator interface {
	SetStatic(cfg StaticConfig) error
	SetDHCP() error
}

InterfaceConfigurator applies an IPv4 configuration change to the active interface. Implementations require elevated/admin privileges.

type InterfaceInfo

type InterfaceInfo struct {
	Name       string
	MAC        net.HardwareAddr
	IP         net.IP
	CIDR       *net.IPNet
	Gateway    net.IP
	DNSServers []net.IP
	DHCP       bool // true if the address was assigned by DHCP, false if static
}

InterfaceInfo describes the active network interface's IP configuration.

type InterfaceInspector

type InterfaceInspector interface {
	Inspect() (InterfaceInfo, error)
}

InterfaceInspector reads the active network interface's IP configuration.

type NetCounters

type NetCounters struct {
	RxBytes uint64
	TxBytes uint64
}

NetCounters is a point-in-time reading of an interface's byte totals.

type Network

type Network struct {
	Self      net.IP             // Our address on this network
	CIDR      *net.IPNet         // The subnet (address + mask)
	Gateway   net.IP             // Default gateway (the router), nil if undetermined
	LocalMACs []net.HardwareAddr // MACs of all of this machine's own interfaces, not just Self
}

Network is the target subnet of a scan: our own address plus its CIDR.

Value object — no behaviour beyond deriving the set of scannable hosts.

func (Network) Hosts

func (n Network) Hosts() []net.IP

Hosts returns every usable IPv4 host address in the subnet, excluding the network and broadcast addresses.

ponytail: IPv4 only. A /24 yields 254 hosts; huge subnets (/16 = 65k) mean a long scan — cap or chunk upstream if that becomes a problem.

type Pinger

type Pinger interface {
	Ping(ip net.IP, timeout time.Duration) (rtt time.Duration, ok bool)
}

Pinger measures ICMP round-trip time to a host. ok is false on timeout or error (host unreachable), in which case the duration is meaningless.

type Prober

type Prober interface {
	OpenPorts(ctx context.Context, ip net.IP) []int
}

Prober reports which of a small set of well-known TCP ports a host accepts connections on — the "detailed scan" used to refine device classification.

type Rate

type Rate struct {
	DownBps float64 // bytes per second, download
	UpBps   float64 // bytes per second, upload
	TotalRx uint64  // bytes received since the meter started
	TotalTx uint64  // bytes sent since the meter started
}

Rate is a throughput reading derived from two counter samples.

type RateMeter

type RateMeter struct {
	// contains filtered or unexported fields
}

RateMeter turns successive NetCounters samples into throughput. Not safe for concurrent use; drive it from one goroutine (the dashboard's update loop).

func (*RateMeter) Update

func (m *RateMeter) Update(c NetCounters, now time.Time) Rate

Update records a new sample and returns the throughput since the previous one. The first call establishes the baseline and reports zero rates.

type Scanner

type Scanner interface {
	Scan(ctx context.Context, target Network) ([]Device, error)
}

Scanner performs active discovery of hosts on a target network.

This is the central port. Implementations are platform-specific and selected at build time (Windows: SendARP; Linux: raw ARP over AF_PACKET). The core never knows which one it talks to.

type SpeedTester

type SpeedTester interface {
	Download(ctx context.Context, size int64) (time.Duration, error)
	Upload(ctx context.Context, size int64) (time.Duration, error)
}

SpeedTester transfers a fixed number of bytes and reports how long it took. Implemented by an adapter talking to a public speed-test endpoint.

type Speedtest

type Speedtest struct {
	// contains filtered or unexported fields
}

Speedtest is the bandwidth-measurement use case.

func NewSpeedtest

func NewSpeedtest(tester SpeedTester) *Speedtest

NewSpeedtest builds the use case with transfer sizes tuned for a typical home link.

ponytail: fixed sizes. Adaptive ramping (grow until the link saturates) can come later if the numbers read low on very fast or very slow connections.

func (*Speedtest) Run

Run measures download then upload throughput.

type StaticConfig

type StaticConfig struct {
	IP      net.IP
	Mask    net.IP // dotted-decimal, e.g. 255.255.255.0
	Gateway net.IP
	DNS     []net.IP
}

StaticConfig is a static IPv4 configuration to apply to an interface.

type TrackedDevice

type TrackedDevice struct {
	Device
	FirstSeen time.Time
	LastSeen  time.Time
	Online    bool
}

TrackedDevice is a device plus its presence history across scans.

type Tracker

type Tracker struct {
	// contains filtered or unexported fields
}

Tracker folds successive scans into a stable device set and reports join/leave events. Pure logic: the caller supplies the clock, so it is fully testable. Not safe for concurrent use — drive it from a single goroutine.

func NewTracker

func NewTracker(offlineAfter time.Duration) *Tracker

NewTracker returns a Tracker that marks a device offline only once it has been missing for at least offlineAfter — a grace window so a single missed scan (common on Wi-Fi) does not flap a device in and out.

func (*Tracker) Devices

func (t *Tracker) Devices() []TrackedDevice

Devices returns the tracked devices sorted by IP address.

func (*Tracker) Observe

func (t *Tracker) Observe(scanned []Device, now time.Time) []Event

Observe folds one scan result into the tracker and returns the events it produced (new arrivals, returns, and departures past the grace period).

type VendorLookup

type VendorLookup interface {
	Vendor(mac net.HardwareAddr) string
}

VendorLookup resolves a MAC address to a manufacturer via its OUI prefix.

type WiFiInfo

type WiFiInfo struct {
	Connected     bool
	SSID          string
	BSSID         string
	Band          string // e.g. "5 GHz"
	RadioType     string // e.g. "802.11ax"
	Channel       int
	SignalPercent int // 0..100 as reported by the OS
	RxRateMbps    int
	TxRateMbps    int
	Nearby        []AccessPoint // other visible APs, for interference context
}

WiFiInfo describes the active wireless connection and its radio environment. Fields are best-effort: anything the platform does not report stays zero/empty.

func (WiFiInfo) RecommendChannel

func (w WiFiInfo) RecommendChannel() int

RecommendChannel suggests the least-congested channel for the current band.

ponytail: a count-based heuristic, not an RF planner. On 2.4 GHz it scores the non-overlapping channels 1/6/11 (each overlaps its neighbours by ~4). On 5 GHz it only considers channels already in use plus the current one, so it never suggests a possibly-illegal channel for the region. Returns the current channel when it is already the clearest.

func (WiFiInfo) SameChannelCount

func (w WiFiInfo) SameChannelCount() int

SameChannelCount returns how many nearby APs share this connection's channel, a rough interference indicator.

func (WiFiInfo) SignalDBM

func (w WiFiInfo) SignalDBM() int

SignalDBM converts the OS signal percentage to an approximate dBm value using the common linear mapping Windows itself uses (0% = -100 dBm, 100% = -50 dBm).

type WiFiInspector

type WiFiInspector interface {
	WiFi() (WiFiInfo, error)
}

WiFiInspector reports the current wireless state, or an error if there is no wireless interface.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL