- Add .cursorrules for Cursor AI - Add CLAUDE.md for Claude Code - Add AGENTS.md for universal AI agent context - Add .github/copilot-instructions.md for GitHub Copilot These files provide comprehensive context about the UnPoller project architecture, coding standards, and development patterns to help AI coding assistants understand and work with the codebase effectively.
7.8 KiB
UnPoller - AI Agent Context
This file provides comprehensive context for AI coding assistants working on the UnPoller project.
Project Identity
Name: UnPoller (UniFi Poller)
Language: Go 1.25.5+
Purpose: Collect metrics and events from UniFi network controllers and export to monitoring backends
Repository: https://github.com/unpoller/unpoller
Documentation: https://unpoller.com
Architecture Overview
UnPoller uses a plugin-based architecture with a generic core that provides input/output interfaces. The core library (pkg/poller) has no knowledge of UniFi or specific backends - it's a generic plugin system.
Core Components
-
Plugin System (
pkg/poller/):- Generic input/output interfaces
- Automatic plugin discovery via blank imports
- Configuration management (TOML/JSON/YAML + env vars)
- Metrics and events aggregation
-
Input Plugins:
pkg/inputunifi/- Collects data from UniFi controllers
-
Output Plugins:
pkg/influxunifi/- InfluxDB (v1 and v2)pkg/promunifi/- Prometheuspkg/lokiunifi/- Lokipkg/datadogunifi/- DataDogpkg/mysqlunifi/- MySQL
-
Web Server (
pkg/webserver/):- Health checks
- Metrics endpoint
- Plugin information
Code Organization
Directory Structure
unpoller/
├── main.go # Entry point, loads plugins
├── pkg/
│ ├── poller/ # Core plugin system (generic)
│ │ ├── config.go # Configuration structures
│ │ ├── inputs.go # Input plugin interface
│ │ ├── outputs.go # Output plugin interface
│ │ ├── start.go # Application startup
│ │ └── commands.go # CLI commands
│ ├── inputunifi/ # UniFi input plugin
│ ├── influxunifi/ # InfluxDB output
│ ├── promunifi/ # Prometheus output
│ ├── lokiunifi/ # Loki output
│ ├── datadogunifi/ # DataDog output
│ ├── mysqlunifi/ # MySQL output
│ └── webserver/ # Web server
├── examples/ # Configuration examples
├── init/ # Init scripts (systemd, docker, etc.)
└── scripts/ # Build scripts
Plugin Interface Pattern
Input Plugin:
type Input interface {
GetMetrics() (*Metrics, error)
GetEvents() (*Events, error)
}
Output Plugin:
type Output interface {
WriteMetrics(*Metrics) error
WriteEvents(*Events) error
}
Registration:
func init() {
poller.RegisterInput(&MyInput{})
// or
poller.RegisterOutput(&MyOutput{})
}
Coding Standards
Go Style
- Formatting: Standard
gofmt - Linting:
.golangci.yamlconfiguration- Enabled:
nlreturn,revive,tagalign,testpackage,wsl_v5
- Enabled:
- Imports: Use
//nolint:gciwhen import ordering needs exception
Naming Conventions
- Packages:
lowercase, single word - Exported:
PascalCase - Unexported:
camelCase - Constants:
PascalCasewith descriptive names - Errors: Always
err, checked immediately
Error Handling
// ✅ Good
if err != nil {
return fmt.Errorf("context: %w", err)
}
// ❌ Bad
_ = functionThatReturnsError()
Configuration Pattern
All configuration structs must include:
type Config struct {
Field string `json:"field" toml:"field" xml:"field" yaml:"field"`
}
- Environment variables use
UP_prefix (defined inENVConfigPrefix) - Automatic unmarshaling via
golift.io/cnfgandgolift.io/cnfgfile
Testing
- Tests in
_testpackages (enforced bytestpackagelinter) - Use
github.com/stretchr/testifyfor assertions - Integration tests:
integration_test.gowithintegration_test_expectations.yaml
Key Dependencies
Core Libraries
github.com/unpoller/unifi/v5- UniFi API client (local:/Users/briangates/unifi)golift.io/cnfg- Configuration managementgolift.io/cnfgfile- Config file parsinggithub.com/spf13/pflag- CLI flagsgithub.com/gorilla/mux- HTTP router
Output Backends
- InfluxDB:
github.com/influxdata/influxdb1-client(v1),github.com/influxdata/influxdb-client-go/v2(v2) - Prometheus:
github.com/prometheus/client_golang - DataDog:
github.com/DataDog/datadog-go/v5 - Loki: Custom HTTP implementation
Data Structures
Metrics
type Metrics struct {
TS time.Time
Sites []any
Clients []any
SitesDPI []any
ClientsDPI []any
Devices []any
RogueAPs []any
SpeedTests []any
CountryTraffic []any
}
Events
type Events struct {
Logs []any
}
Device Types
UniFi devices supported:
- UAP - Access Points (
uap.go) - USG - Security Gateways (
usg.go) - USW - Switches (
usw.go) - UDM - Dream Machines (
udm.go) - UXG - Next-Gen Gateways (
uxg.go) - UBB - Building Bridges (
ubb.go) - UCI - Industrial devices (
uci.go) - PDU - Power Distribution Units (
pdu.go)
Each output plugin has device-specific reporting functions following the pattern:
func (r *Report) UAP(uaps []*unifi.UAP) {
// Convert and export UAP data
}
Common Development Tasks
Adding a New Output Plugin
- Create package in
pkg/myoutput/ - Implement
Outputinterface - Register in
init()function - Add blank import to
main.go:_ "github.com/unpoller/unpoller/pkg/myoutput" - Add configuration struct with tags
- Create
README.mdwith usage examples
Adding Device Type Support
- Add device-specific file (e.g.,
mydevice.go) - Follow pattern from existing device files
- Convert UniFi device struct to output format
- Handle errors appropriately
Modifying Configuration
- Add fields to appropriate config struct
- Include all format tags:
json,toml,xml,yaml - Update example configs in
examples/ - Document in package
README.md
Build & Deployment
- CI/CD: GitHub Actions (
.github/workflows/) - Build:
goreleaser(.goreleaser.yaml) - Platforms: Linux, macOS, Windows, FreeBSD
- Docker: Auto-built to
ghcr.io - Packages: Debian, RedHat via goreleaser
- Homebrew: Formula for macOS
Important Constraints
- Generic Core:
pkg/pollermust remain generic - no UniFi/backend knowledge - Cross-Platform: Support Windows, macOS, Linux, BSD
- Configuration: Support TOML (default), JSON, YAML, and environment variables
- Error Handling: Always check and return errors
- Context: Use
context.Contextfor cancellable operations - Timeouts: Respect timeouts and deadlines
- Logging: Use structured logging via logger interfaces
Configuration Examples
File Formats: See examples/up.conf.example, examples/up.json.example, examples/up.yaml.example
Environment Variables:
UP_INFLUX_URL=http://localhost:8086
UP_UNIFI_DEFAULT_USER=admin
UP_UNIFI_DEFAULT_PASS=password
UP_POLLER_DEBUG=true
UP_POLLER_INTERVAL=30s
When Writing Code
- Follow Existing Patterns: Look at similar plugins for examples
- Keep Core Generic: Don't add UniFi/backend-specific code to
pkg/poller - Error Handling: Check all errors, return descriptive messages
- Documentation: Document exported functions and types
- Testing: Write tests in
_testpackages - Cross-Platform: Test on multiple platforms when possible
- Performance: Consider polling intervals and data volume
- Security: Don't log passwords or sensitive data
Resources
- Documentation: https://unpoller.com
- UniFi Library: https://github.com/unpoller/unifi
- Grafana Dashboards: https://grafana.com/dashboards?search=unifi-poller
- Discord: https://golift.io/discord (#unpoller channel)