7.7 KiB
Architecture
Module: forge.lthn.ai/core/go-netops
Overview
go-netops is a thin Go wrapper around the unpoller/unifi SDK that provides a simplified interface for querying UniFi network controllers. It handles authentication, TLS configuration, multi-site filtering, and config resolution from file, environment, and CLI flags.
Package Structure
go-netops/
├── unifi/
│ ├── client.go # Client struct and constructor (New)
│ ├── clients.go # Connected client queries with filtering
│ ├── config.go # Config resolution + persistence (NewFromConfig, ResolveConfig, SaveConfig)
│ ├── devices.go # Infrastructure device queries (UAP, USW, USG, UDM, UXG)
│ ├── networks.go # Network configuration queries (VLANs, DHCP, firewall zones)
│ ├── routes.go # Gateway routing table queries
│ ├── sites.go # Site listing
│ ├── client_test.go # Client creation tests
│ └── config_test.go # Config resolution and persistence tests
├── go.mod
└── go.sum
All exported types live in the unifi package under the unifi/ subdirectory.
Key Types
Client
The central type. Wraps the unpoller SDK client with config-based authentication.
type Client struct {
api *uf.Unifi
url string
}
func New(url, user, pass, apikey string, insecure bool) (*Client, error)
func NewFromConfig(flagURL, flagUser, flagPass, flagAPIKey string, flagInsecure *bool) (*Client, error)
func (c *Client) API() *uf.Unifi
func (c *Client) URL() string
Two constructors are provided:
New— direct credentials, useful for tests and scripts.NewFromConfig— three-tier config resolution (file, env, flags), used by CLI commands.
ClientFilter
Controls which connected clients are returned from a query.
type ClientFilter struct {
Site string // Filter by site name (empty = all sites)
Wired bool // Show only wired clients
Wireless bool // Show only wireless clients
}
func (c *Client) GetClients(filter ClientFilter) ([]*uf.Client, error)
DeviceInfo
Flat representation of any UniFi infrastructure device, abstracting over the SDK's separate UAP/USW/USG/UDM/UXG containers.
type DeviceInfo struct {
Name string
IP string
Mac string
Model string
Version string
Type string // uap, usw, usg, udm, uxg
Status int // 1 = online
}
func (c *Client) GetDevices(siteName string) (*uf.Devices, error)
func (c *Client) GetDeviceList(siteName, deviceType string) ([]DeviceInfo, error)
GetDevicesreturns the raw SDK container (for callers that need full detail).GetDeviceListreturns a flat, uniform slice with optional type filtering.
NetworkConf
Network configuration entry covering VLANs, DHCP, DNS, firewall zones, and WAN settings.
type NetworkConf struct {
ID string `json:"_id"`
Name string `json:"name"`
Purpose string `json:"purpose"` // wan, corporate, remote-user-vpn
IPSubnet string `json:"ip_subnet"` // CIDR
VLAN int `json:"vlan"`
VLANEnabled bool `json:"vlan_enabled"`
Enabled bool `json:"enabled"`
NetworkGroup string `json:"networkgroup"` // LAN, WAN, WAN2
NetworkIsolationEnabled bool `json:"network_isolation_enabled"`
InternetAccessEnabled bool `json:"internet_access_enabled"`
IsNAT bool `json:"is_nat"`
DHCPEnabled bool `json:"dhcpd_enabled"`
DHCPStart string `json:"dhcpd_start"`
DHCPStop string `json:"dhcpd_stop"`
DHCPDNS1 string `json:"dhcpd_dns_1"`
DHCPDNS2 string `json:"dhcpd_dns_2"`
DHCPDNSEnabled bool `json:"dhcpd_dns_enabled"`
MDNSEnabled bool `json:"mdns_enabled"`
FirewallZoneID string `json:"firewall_zone_id"`
GatewayType string `json:"gateway_type"`
VPNType string `json:"vpn_type"`
WANType string `json:"wan_type"` // pppoe, dhcp, static
WANNetworkGroup string `json:"wan_networkgroup"`
}
func (c *Client) GetNetworks(siteName string) ([]NetworkConf, error)
Uses the raw controller REST API (/api/s/{site}/rest/networkconf) since the
unpoller SDK does not wrap this endpoint.
Route
Gateway routing table entry with prefix, next-hop, interface, type, and metrics.
type Route struct {
Network string `json:"pfx"` // CIDR prefix
NextHop string `json:"nh"` // Next-hop address or interface
Interface string `json:"intf"` // Interface name (e.g. "br0", "eth4")
Type string `json:"type"` // S=static, C=connected, K=kernel, B=bgp, O=ospf
Distance int `json:"distance"` // Administrative distance
Metric int `json:"metric"`
Uptime int `json:"uptime"` // Seconds
Selected bool `json:"fib"` // In forwarding table
}
func (c *Client) GetRoutes(siteName string) ([]Route, error)
func RouteTypeName(code string) string
Also uses the raw controller API (/api/s/{site}/stat/routing).
Design Decisions
Thin Wrapper
The package deliberately stays thin. It delegates transport, session management, and cookie handling to the unpoller SDK, adding only:
- Config resolution — three-tier priority (config file, env vars, CLI flags).
- TLS flexibility —
insecureflag for self-signed certificates on home lab controllers. - Flat types —
DeviceInfoandNetworkConfflatten the SDK's nested/typed structures into uniform slices for easier consumption by CLI commands and templates. - Raw API access —
GetNetworksandGetRoutescall controller endpoints the SDK does not wrap, parsing the JSON directly.
Config Resolution Order
All credential resolution follows the same three-tier pattern used across the core framework:
- Config file —
~/.core/config.yaml(keys:unifi.url,unifi.user,unifi.pass,unifi.apikey,unifi.insecure) - Environment variables —
UNIFI_URL,UNIFI_USER,UNIFI_PASS,UNIFI_APIKEY,UNIFI_INSECURE - CLI flags — highest priority, override everything
Site Filtering
Most query methods accept a siteName parameter. An empty string queries all sites.
The getSitesForFilter helper resolves site names and returns an error for unknown sites,
preventing silent data omission.
TLS Configuration
The client enforces TLS 1.2 as the minimum version regardless of the insecure flag.
The insecure flag only controls certificate verification, not protocol version.
API Patterns
Constructor Pattern
// Direct credentials
client, err := unifi.New("https://10.69.1.1", "admin", "pass", "", true)
// Config-resolved (CLI usage)
client, err := unifi.NewFromConfig(flagURL, flagUser, flagPass, flagAPIKey, &flagInsecure)
Query Pattern
All query methods follow the same shape: accept optional filters, return typed slices, wrap errors with log.E.
clients, err := client.GetClients(unifi.ClientFilter{Site: "default", Wireless: true})
devices, err := client.GetDeviceList("default", "uap")
networks, err := client.GetNetworks("default")
routes, err := client.GetRoutes("default")
sites, err := client.GetSites()
Config Persistence
// Save credentials
err := unifi.SaveConfig("https://10.69.1.1", "admin", "pass", "", nil)
// Resolve credentials (without creating a client)
url, user, pass, apikey, insecure, err := unifi.ResolveConfig("", "", "", "", nil)