DEV Community

Cover image for A Rust TUI for Your UniFi Network That Actually Takes Code Review Seriously
Wes
Wes

Posted on • Originally published at wshoffner.dev

A Rust TUI for Your UniFi Network That Actually Takes Code Review Seriously

If you run UniFi gear, you manage it through a web UI. That's fine until you need to script something, automate a deployment, or check your firewall rules from an SSH session on a box that doesn't have a browser. Ubiquiti doesn't ship a CLI. The API exists but it's split across two incompatible surfaces with different auth mechanisms, different response formats, and different ideas about what an entity ID should look like. Most people who try to automate UniFi end up writing bespoke Python scripts against whichever API endpoint they needed that week. The scripts work until a firmware update moves the endpoint.

Someone decided to build the CLI that Ubiquiti didn't. And then kept going until it had a full TUI dashboard.

What Is Unifly?

Unifly is a Rust CLI and terminal dashboard for managing UniFi network infrastructure. Built by Stefanie Jane, it ships as a single binary with 27 CLI commands covering devices, clients, networks, firewall rules, NAT policies, DNS, VPN, Wi-Fi observability, and more. There's also a 10-screen ratatui TUI for real-time monitoring: device health, client connections, traffic charts, firewall policies, network topology. The whole thing speaks both UniFi API dialects (the modern Integration API and the older Session API) and reconciles data between them.

123 stars at time of writing. Eight weeks old. About 15,000 lines of Rust across two crates. The pace of development is aggressive: 71 commits in the first eight days of April alone.

The Snapshot

Project Unifly
Stars 123 at time of writing
Maintainer Solo (hyperb1iss), AI-assisted
Code health 250+ tests, pedantic clippy, forbid(unsafe), e2e suite against simulated controller
Docs Thorough CONTRIBUTING.md, detailed architecture guide, explicit ROADMAP with named gaps
Contributor UX Same-day responses, content always lands, merge mechanism varies
Worth using Yes if you have UniFi gear. The CLI alone replaces a lot of web UI clicking.

Under the Hood

The architecture is a two-crate Cargo workspace. unifly-api is the library: HTTP and WebSocket transport, a Controller facade behind an Arc, and a reactive DataStore built on DashMap and tokio::watch channels. unifly is the binary: clap-based CLI commands and ratatui TUI screens. The library is published to crates.io independently, so you could build your own tooling on top of it without pulling in the CLI.

The dual-API problem is the interesting engineering challenge. UniFi controllers expose a modern Integration API (REST, API key auth, UUIDs) and an older Session API (cookie + CSRF, hex string IDs, envelope-wrapped responses). Some data only exists on one side. Client Wi-Fi experience metrics? Session API. Firewall policy CRUD? Integration API. NAT rules? Session v2 API, a third dialect. Unifly handles all of this behind a single Controller type. You call controller.execute(Command::CreateNatPolicy(...)) and the command router figures out which API to hit, which auth to use, and which ID format to resolve.

The lint configuration tells you a lot about a project's standards. Unifly runs clippy::pedantic at deny level, clippy::unwrap_used at deny, and unsafe_code at forbid. The workspace Cargo.toml has 30+ individual clippy rule overrides, each with a clear rationale. This is not someone who pasted a default config. The test suite uses wiremock for API mocking, assert_cmd for CLI testing, and insta for snapshot tests. The e2e suite spins up a simulated UniFi controller and runs full command flows. cargo-deny enforces a license allowlist and vulnerability advisories.

One thing you notice immediately: nearly every commit has Co-Authored-By: Claude Opus 4.6 in the trailer. This is an AI-assisted codebase, and the maintainer isn't hiding it. The code quality is high regardless of how it got there. The architecture is coherent, the tests are real, the error handling uses thiserror and miette properly. AI-assisted doesn't mean AI-dumped. The commit history shows iterative problem-solving, not a single prompt that produced 15,000 lines.

What's rough? The TUI has ten screens and zero test coverage. The controller reconnect lifecycle is broken (the CancellationToken goes permanent after the first disconnect). And the ROADMAP had a few stale entries that claimed features were missing when they'd already been implemented. These are the kinds of gaps you'd expect in a project that's moving this fast with a solo maintainer.

The Contribution

The ROADMAP listed "NAT policies have no update subcommand" as a known gap. The workaround was to delete and recreate, which means re-specifying every field just to change one. NAT rules are the kind of thing you adjust frequently (new port forward, toggling a rule on and off), so this was a real workflow friction.

The fix touched 11 files across both crates. On the library side: an UpdateNatPolicyRequest struct, a new Command variant, and an apply_nat_update function that fetches the existing rule via the Session v2 API, merges only the changed fields, and PUTs it back. On the CLI side: an Update variant in the clap args with all NAT fields as optional flags plus --from-file support, validation that at least one field is provided, and conflicts_with between --name and --description (both map to the same v2 API field, which I caught during my pre-submission review). I also promoted a private ensure_session_access function from the events handler to a shared utility, and wired it into the NAT handler so users get a clean error message when their auth mode is insufficient instead of a deep transport failure.

The codebase was easy to navigate. The project's AGENTS.md (symlinked as CLAUDE.md) doubles as a comprehensive architecture guide. It documents every API quirk, every CLI pattern, every module's responsibility. When I needed to understand how the firewall update command worked (to replicate the pattern for NAT), I read the architecture doc first, then confirmed in code. The existing firewall implementation was an almost perfect blueprint. The CONTRIBUTING.md lays out the full checklist: define args, implement handler, wire dispatch, update skill docs, add tests. I followed it step by step.

PR #10 is open and waiting for review. The maintainer's track record with external PRs is good. Five previous PRs from two contributors all had their content land in main. Clean PRs against current main get proper GitHub merges. PRs that conflict with ongoing refactors get reworked by the maintainer with co-author credit. Either way, the work ships.

The Verdict

Unifly is for anyone who manages UniFi networks and wants to do it from the terminal. If you run a homelab with Ubiquiti gear, or you're an MSP managing multiple sites, or you just want to script your firewall rules instead of clicking through a web UI, this is the tool. The CLI covers enough surface area to be genuinely useful today. The TUI is a bonus for monitoring.

The trajectory is steep. Eight weeks from first commit to 27 commands, a full TUI, dual-API support, and a published crate on crates.io. The codebase is clean enough that an external contributor (me) could implement a missing feature by following the existing patterns. The maintainer responds quickly and ships contributed work. Those are the signals that matter for whether a project has legs.

What would push it further? Test coverage on the TUI screens. Fixing the reconnect lifecycle so long-running TUI sessions survive network blips. And eventually, Cloud/Site Manager API support so you can manage controllers through Ubiquiti's cloud without direct network access. The AuthCredentials::Cloud variant already exists in the code. The transport just isn't wired yet.

Go Look At This

If you have UniFi gear, try unifly. cargo install unifly or grab a binary from the releases page. Run unifly config init to connect to your controller, then unifly devices list and see what happens.

The ROADMAP has more gaps worth picking up. TUI test coverage is explicitly welcomed. The radio data parsing has untested code paths. The Cloud API transport is waiting for someone to implement it.

This is Review Bomb #13, a series where I find under-the-radar projects on GitHub, read the code, contribute something, and write it up. If you know a project that deserves more eyeballs, drop it in the comments.


This post was originally published at wshoffner.dev/blog. If you liked it, the Review Bomb series lives there too.

Top comments (0)