Files
netbird/client/ui
Zoltan Papp fc1db63fc3 [client/ui] Fix profile-submenu race, restore Connect re-auth flow
Three tray fixes after the SubscribeStatus stream refactor:

* loadProfiles now serializes via a dedicated profileLoadMu and runs
  AFTER the SetHidden/SetEnabled writes inside applyStatus's iconChanged
  block. Previously the status-driven refresh fired before the menu-item
  writes finished, so processMenu's clearMenu/re-add NSMenu rebuild
  raced against SetHidden on darwin — the Disconnect entry could end
  up visible-but-disabled even when applyStatus had requested it hidden.

* The status row is no longer a hidden "Login" entry. It now renders
  as a plain enabled label (so the text isn't greyed out) but has no
  OnClick handler — clicks are no-ops, matching the legacy Fyne UI.
  All actionable transitions flow through Connect/Disconnect.

* handleConnect routes NeedsLogin/SessionExpired/LoginFailed to the
  frontend's /login route (which already runs Login → WaitSSOLogin →
  Up) instead of calling Up directly. The plain Up RPC errors with
  "up already in progress: current status NeedsLogin" in those
  states; the legacy Fyne UI drove the SSO dance from the Connect
  button as well.
2026-05-14 14:52:03 +02:00
..

NetBird desktop UI (Wails3 + React)

Replaces client/ui (Fyne). One binary on Windows / macOS / Linux, talks to the NetBird daemon over gRPC, renders a React frontend in a WebView.

Prerequisites

  • Go ≥ 1.25, Node ≥ 20, pnpm (corepack enable && corepack prepare pnpm@latest --activate)
  • wails3 CLI: go install github.com/wailsapp/wails/v3/cmd/wails3@latest
  • task: go install github.com/go-task/task/v3/cmd/task@latest
  • A running NetBird daemon (default: unix:///var/run/netbird.sock, Windows tcp://127.0.0.1:41731)
  • Linux only: libwebkit2gtk-4.1-dev, libgtk-3-dev, libayatana-appindicator3-dev

Develop without rebuilding

cd client/ui
task dev

task dev runs Vite (port 9245) + the Go binary + a *.go watcher. Frontend edits hot-reload instantly. Go edits trigger a rebuild and relaunch. Pass daemon flags after --:

task dev -- --daemon-addr=tcp://127.0.0.1:41731

For pure UI work (no native window, fastest loop):

cd frontend && pnpm dev

Production build

task build

Output in bin/. Frontend assets are embedded into the binary.

Cross-compile Windows from Linux

Install the mingw-w64 toolchain once:

sudo apt install gcc-mingw-w64-x86-64           # Debian/Ubuntu
sudo dnf install mingw64-gcc                    # Fedora
sudo pacman -S mingw-w64-gcc                    # Arch

Then:

CGO_ENABLED=1 task windows:build

Produces bin/netbird-ui.exe. macOS cross-compile from Linux is not supported (signing and notarization need a real Mac).

Windows console build (logs in the terminal)

Default windows:build links the binary as a Windows GUI app, which detaches from the launching console — logrus output, fmt.Println, and panics go nowhere visible. To debug tray/event/daemon issues:

CGO_ENABLED=1 task windows:build:console

Produces bin/netbird-ui-console.exe. Run it from cmd.exe / PowerShell / Windows Terminal and stdout/stderr land in that terminal. Same flag works on a native Windows build (drop the CGO_ENABLED=1 if your toolchain already has it set).

Regenerating bindings

When a Go service signature changes:

wails3 generate bindings

task dev does this automatically on *.go save.

Tray icons

Source SVGs live in assets/svg/ (state.svg + state-macos.svg). After editing any SVG, rasterize to the PNGs the Go side embeds:

task common:generate:tray:icons

Requires Inkscape. Commit the resulting assets/*.png files alongside the SVG change so CI doesn't need Inkscape installed.