Installation & Releases

HAProxy UI ships two ways: a browser build (SvelteKit, any host that can serve static files) and a desktop build (Tauri 2, native window, Rust backend). Both render the identical UI. The desktop build is the canonical target because it is the only one that can keep API tokens out of the browser.

Releases are published to GitHub Releases and updated in-app via tauri-plugin-updater.

Requirements

Component

Requirement

Node.js

20+

Package manager

pnpm 9+

Rust (desktop only)

stable toolchain via rustup

macOS (desktop)

Xcode Command Line Tools

Linux (desktop)

webkit2gtk-4.1, gtk-3, ayatana-appindicator3, rsvg2, soup-3.0, ssl, patchelf, build-essential

Windows (desktop)

WebView2 runtime (preinstalled on Windows 10+)

Network

Reachability to the Dataplane API (port 5555 in the reference stack)

Install from source

git clone https://gitlab.starstorm.dev/starstorm/ha-proxy-ui.git
cd ha-proxy-ui
pnpm install
 
cp .env.example .env # fill in Dataplane URL + token, or leave for the mock

Development

pnpm dev # Vite dev server, HMR, http://localhost:5173
pnpm dev:tauri # native window with HMR against the Vite dev server
pnpm test:unit # Vitest watch mode
pnpm test:e2e # Playwright

Production builds

pnpm build # browser production build (static, into build/)
pnpm build:tauri # Tauri production binary + installers
pnpm preview # serve the browser production build locally

Artifacts land in src-tauri/target/release/bundle/ — .app + .dmg on macOS, .deb / .AppImage / .rpm on Linux, .msi / .exe on Windows.

Where the desktop app stores things

The app data directory resolves from the bundle identifier com.haproxy-ui.app:

OS

Path

macOS

~/Library/Application Support/com.haproxy-ui.app/

Linux

~/.local/share/com.haproxy-ui.app/

Windows

%APPDATA%\com.haproxy-ui.app\

  • Instances (per-target URL, label, auth scheme, mode, default flag) → instances.json in that directory.

  • API tokens → the OS keychain, never instances.json and never localStorage. See Secrets & keychain.

  • Working draft → localStorage under haproxy-ui:draft:v1:<instanceId>, so an in-progress edit survives a relaunch. Cleared on successful apply or explicit discard.

Desktop-only features

  • Multi-instance picker — register any number of Dataplane targets and switch between them from a sidebar dropdown. Each entry holds a URL, label, auth scheme and mode.

  • GitOps mode — push or pull the assembled haproxy.cfg against a git remote instead of, or alongside of, the Dataplane API. SSH keys are used via ssh-agent from the Rust side.

Both route through Rust commands, so the UI renders them in browser mode too — when no Tauri shell is detected, persistence falls back to localStorage. The desktop app remains the canonical target.

Auto-updates

The desktop binary checks for updates on startup and shows a prompt when a newer version is available. Both the endpoint and the public key live in src-tauri/tauri.conf.json under plugins.updater.

Before the first release can update, the updater owner must be set. The repo ships with the literal token __OWNER__ in the endpoint URL, so the startup check fails fast against github.com/__OWNER__/... rather than silently polling the wrong repository. Replace it with the org the repo actually lives under, and make it match the release URL exactly — a mismatch means shipped binaries never find a latest.json and the check fails silently.

The pubkey is the Ed25519 public half of the signing keypair. Do not rotate it casually: every shipped release must be signed with the matching private half, or existing installs reject the update. Tauri 2 supports two public keys during a transition via plugins.updater.pubkeys.

Cutting a release

Two CI workflows run on GitHub Actions:

  • build.yml — PR and main smoke builds. Does not publish a release.

  • release.yml — tag-triggered on any v* push; cross-platform build, signs, and publishes a draft release.

  1. Bump the version in both package.json and src-tauri/tauri.conf.json. They are intentionally separate files today — Tauri reads its own for the updater manifest, pnpm reads the other — and must stay in lockstep.

  2. Tag and push: git tag v0.0.1 && git push origin v0.0.1.

  3. Each matrix leg uploads to the same draft release, plus a signed latest.json at the root.

  4. Un-draft the release when the matrices finish, so users on prerelease: false get notified.

Required repo secrets: TAURI_SIGNING_PRIVATE_KEY and TAURI_SIGNING_PRIVATE_KEY_PASSWORD.

Known distribution gaps

  • macOS notarization is not configured. Bundles are ad-hoc signed, so distributing outside the build machine triggers Gatekeeper. A proper notarization step needs APPLE_CERTIFICATE, APPLE_CERTIFICATE_PASSWORD, APPLE_SIGNING_IDENTITY and an App Store Connect API key as repo secrets.

  • Windows code signing is not configured. MSI/EXE bundles ship unsigned and will trigger SmartScreen. Add WINDOWS_CERTIFICATE + password and a bundle.windows.signCommand when ready.

  • The updater pipeline is desktop-only. The planned iPad build does not use tauri-plugin-updater — it is reinstalled via the App Store.

See iPad Roadmap for the mobile target.

Uninstall

rm -rf "/Applications/HAProxy UI.app"
rm -rf ~/Library/Application Support/com.haproxy-ui.app/
 
# secrets live in the OS keychain and must be removed separately
security delete-generic-password -s "instance:<instance-id>" 2>/dev/null || true


Was this article helpful?