gofi - Go UniFi Controller Client
Programmatic control of Ubiquiti UniFi UDM Pro devices. This repository is two things:
- Command-line program — a unified tool (
gofi) with subcommands for managing fixed IPs, listing clients, inspecting networks, and more. - A Go module (SDK) — a type-safe, concurrent-safe client library you can import into your own programs.
If you just want to get work done from the shell, start with Part 1: Command-line programs. If you're writing Go code against a UniFi controller, jump to Part 2: Using gofi in your Go program.
Part 1: Command-line programs
Step 1 — Get a UniFi API key (recommended)
gofi supports two ways to authenticate. A cloud API key used through Ubiquiti's Site Manager connector is the recommended one: it works even when the controller isn't directly reachable on your LAN, needs no session cookies, and is the preferred authentication method.
Create the key:
- Sign in at unifi.ui.com using a Site Admin or Owner account. A key inherits the permissions of the account that created it.
- Go to your profile icon → API → Create API Key.
- Grant it the UniFi Applications → Network scope.
Cloud keys (from unifi.ui.com) are a different credential from console-issued keys (from the console's own UI). Connector access requires the cloud key.
Find your console ID (the connector needs it):
export UNIFI_API_KEY=... # the key you just created curl -s -H "X-API-KEY: $UNIFI_API_KEY" https://api.ui.com/v1/hosts
Export both values so the programs can find them:
export UNIFI_API_KEY=... export UNIFI_CONSOLE_ID=...
That's it — every request now goes to https://api.ui.com, which forwards it to your
console. No direct network route to the UDM is required.
Alternative — local username/password
The cloud API key above is the intended path — it's what most of gofi's own testing and
day-to-day use goes through. If the controller isn't reachable at api.ui.com at all
(an isolated network, no internet route) or you'd rather not create a cloud key, a local
username/password path exists as a secondary, less-tested alternative. See
docs/alternate-local-api.md for the full setup,
including TLS/self-signed-certificate handling. If you're not sure which to use, use
the cloud API key above.
Step 2 — Build and install the programs
make install # build the utilities and install them to ~/bin make utilities # or just build them into ./bin make examples # build the example programs into ./bin/examples
make install puts gofi on your PATH (override the destination with make install INSTALL_DIR=/somewhere/else).
Step 3 — Shell completion (optional)
gofi completion <shell> doesn't configure anything by itself — it prints a completion
script to stdout. What you do with that output determines whether completion is
temporary (this shell session only) or permanent (every new shell).
Try it first, without installing anything:
source <(gofi completion bash) # bash gofi completion zsh | source # zsh (as a one-off; see below for the real setup)
Completion works for this shell session only; open a new terminal and it's gone. Use this to confirm completion behaves the way you want before wiring it in permanently.
Install permanently:
bash
Requires the bash-completion package (apt install bash-completion, brew install bash-completion, etc.) — without it, sourced completion scripts are silently ignored.
System-wide (needs root, affects every user):
gofi completion bash | sudo tee /etc/bash_completion.d/gofi > /dev/null
Per-user, no root required:
mkdir -p ~/.local/share/bash-completion/completions gofi completion bash > ~/.local/share/bash-completion/completions/gofi
Restart your shell, or source the file directly, to pick it up.
zsh
Pick a directory already on your fpath (check with echo $fpath), or add one:
mkdir -p ~/.zsh/completions gofi completion zsh > ~/.zsh/completions/_gofi
Then, in ~/.zshrc, before the compinit line:
fpath=(~/.zsh/completions $fpath) autoload -Uz compinit && compinit
If you use a framework (oh-my-zsh, prezto), drop _gofi into its custom completions
directory instead (e.g. ~/.oh-my-zsh/custom/completions/) and skip the fpath edit.
Restart your shell after making this change — zsh only rebuilds its completion cache
on compinit.
fish
gofi completion fish > ~/.config/fish/completions/gofi.fishPicked up automatically in new fish sessions — no restart-triggering config edit needed.
powershell
gofi completion powershell | Out-String | Invoke-Expression
To make this permanent, add that line to your PowerShell profile ($PROFILE).
Once installed, gofi <TAB> lists areas, gofi ips <TAB> lists actions, and
gofi ips add --<TAB> lists flags. Flag values (target names, MAC addresses, sites)
don't complete — only the command and flag names themselves.
The gofi commands
One binary, gofi <area> <action>:
| Area | What it manages |
|---|---|
ips |
Fixed IP + DNS reservations, in ISC DHCP host-declaration format |
dns |
Local DNS records, independent of ips |
network |
Networks (VLANs): subnet, DHCP pool, DNS servers (read-only) |
clients |
Currently-connected stations, with offline OUI vendor lookup |
users |
Known-client identity records, connected or not |
profile |
Capture networks + WLANs + fixed IPs as JSON, apply one back |
config |
gofi's own configuration file (acts on your machine, not a controller) |
gofi network list gofi ips add --name nas --mac aa:bb:cc:dd:ee:01 --ip 192.168.1.13 gofi profile export > bench.json
docs/gofi-user-guide.md is the full reference — every
area, every action, every flag, with the reasoning behind how the command tree is
shaped.
Part 2: Using gofi in your Go program (SDK)
The gofi module gives you type-safe, concurrent-safe access to UniFi Network Application
endpoints (v1, v2, REST, and WebSocket) — the same library the gofi CLI is built on.
go get github.com/unifi-go/gofi
package main import ( "context" "fmt" "log" "os" "github.com/unifi-go/gofi/src" ) func main() { client, err := gofi.New(&gofi.Config{}, gofi.WithAPIKey(os.Getenv("UNIFI_API_KEY")), gofi.WithConnector(os.Getenv("UNIFI_CONSOLE_ID"))) if err != nil { log.Fatal(err) } ctx := context.Background() if err := client.Connect(ctx); err != nil { log.Fatal(err) } defer client.Disconnect(ctx) devices, err := client.Devices().List(ctx, "default") if err != nil { log.Fatal(err) } fmt.Printf("Found %d devices\n", len(devices)) for _, device := range devices { fmt.Printf("- %s (%s)\n", device.Name, device.Model) } }
docs/api-guide.md is the full SDK reference — every service,
common operations for devices/networks/WLANs/clients/firewall/events, error handling,
testing with the mock server, retry/timeout options, and local-auth setup.
Development
make test # Run all tests make coverage # Generate coverage report make lint # Run linter make build # Build the module and utilities make examples # Build all examples to bin/examples/ make utilities # Build all utilities to bin/ make install # Install utilities to ~/bin make all # Run lint, test, and build
Requirements
- Go 1.22 or later
- UniFi UDM Pro with Network Application 10.x+
- Admin access to the controller
Compatibility
Tested with UniFi OS 4.x and 5.x, Network Application 10.x, on UDM Pro, UDM SE, and UDR.
Documentation
- gofi user guide — every CLI area, action, and flag
- SDK / API guide — the full Go library reference and example programs
- Alternate local API — local username/password auth (secondary path)
- Design — architecture details
- GoDoc — API reference
Contributing
Contributions are welcome. Please ensure all tests pass (make test), code passes linting
(make lint), new features include tests, and changes maintain backward compatibility.
License
MIT License.
Acknowledgments
- Inspired by paultyng/go-unifi (Terraform provider patterns)
- Type patterns from unpoller/unifi (FlexInt/FlexBool)
- API patterns from thib3113/unifi-client (TypeScript)
