HAProxy UI turns an haproxy.cfg into a graph — frontends, backends and servers as nodes — and ships the assembled configuration either to a running HAProxy through the Dataplane API, or to a git repository. The graph is the mental model; the config file stops being something you edit by hand.
Why it exists
HAProxy's configuration language is powerful and famously hard to read. The engineers who can write one fluently are scarce; everyone else learns the DSL or pays for it in incidents. HAProxy UI turns the configuration into something a team can argue about the way they argue about a network diagram, and removes the "who SSH'd in and changed the backend last week" problem by writing through a versioned, diffed, reviewed path.
Data flow
Read. The client fetches the live configuration from the Dataplane v3 API (
GET /v3/services/haproxy/configuration) and normalizes it intoCanonicalConfig— the app's own Zod-validated model, not the API's raw shape.Render.
CanonicalConfigbecomes a graph. Nodes are click-to-edit; the inspector panel focuses a node without leaving the canvas.Edit. Edits mutate a working draft. Nothing is sent yet. The pending-changes bar shows every unsaved diff, and the diff panel shows red/green against the live configuration.
Validate. Before anything leaves the machine the draft is checked — duplicate frontend and backend names, frontends with no bind line, and other typed errors surface immediately rather than as a failed apply.
Apply. The draft is assembled back into Dataplane JSON, written inside a transaction scoped by transaction id (not by a stale version), and committed. A graceful reload follows.
The core model
CanonicalConfig is the single source of truth. It deliberately does not mirror the Dataplane API shape — it is a cleaner model that happens to serialize losslessly in both directions. Three modules own the conversion in each direction:
Module | Direction | Responsibility |
|---|---|---|
| Dataplane → canonical | Flatten the API payload, drop defaults that mean “inherits from defaults”, populate |
| canonical → Dataplane | Re-inflate for the write path |
| text → canonical | Parse a pasted or GitOps |
| canonical → text | Emit the config file for GitOps |
The parse and assemble halves are inverses of each other, and that round-trip is covered by tests — see Spec coverage gate.
Two destinations, one canvas
The same draft can be written two ways, and both are first-class:
Live — versioned writes to the Dataplane API, followed by a graceful reload. For teams that own the cluster.
GitOps — the assembled
haproxy.cfgcommitted and pushed to a git remote. For teams with compliance requirements that need config changes to go through review.
See GitOps Mode.
Unmodeled directives are preserved
Not every directive in a real haproxy.cfg has a widget. Directives the app does not model — custom userlist and resolvers sections, peers, Lua loads, hand-tuned tune.* knobs — are carried verbatim through rawLines and rawSections.
This matters more than it sounds. A GitOps apply that silently dropped unmodeled directives would delete configuration somebody wrote by hand, on the theory that it was unused. It is not; it is there because it is needed.
Secret handling
In the desktop build, Rust brokers every outbound call — Dataplane API, git, OS keychain — so the webview never performs a fetch() or shells out. API tokens are resolved from the OS keychain inside Rust and never cross the webview boundary on a request. This is what answers the “is this UI exfiltrating my bearer token?” question, and it also means the desktop build has no CORS constraints at all.
See Secrets & keychain.
What ships in the box
Graph canvas with snap-to-16px grid, pinch zoom, multi-select, minimap and fit-view.
Inspectors for global, frontend, backend and server, with operator-facing help text on every field.
Paste-import of an existing
haproxy.cfgwith per-name opt-out and a change preview.Diff, validation, versioned apply, conflict detection, reject-confirm, snapshots and rollback.
Runtime stats dashboard, reload banner and GitOps sync-state badge.
Multi-instance management, OS keychain integration and signed auto-updates on desktop.
Status: phases 1–11 shipped; v1 feature-complete including the desktop build and GitOps mode.
What’s next
Architecture Overview — component-by-component detail
Configuration Guide — every inspector and field
iPad Roadmap — the next platform target