* [client] Fix the Windows tray deadlock on re-entrant window creation The Wails systray runs the left-click handler synchronously inside the tray window procedure, and creating a window on a running app pumps a nested Win32 message loop while WebView2 initialises. ensureWindow held the non-reentrant createMu across that creation, so the second button-up of a double click re-entered ShowWindow from the pump and blocked the main thread on its own lock. A goroutine holding createMu while the main thread pumped, and the Open* dialogs holding mu across NewWithOptions, Show, Hide and InvokeSync, exposed the same inversion. WindowManager now serialises creation with a per-slot creating flag and queues the callers' operations until the window exists, and no Wails call runs while mu is held. The tray click and second-instance handlers call ShowWindow off the message loop. * [client] Serialize window operations while a slot is being created Callers arriving after the window is published but before the creator has drained the queue took the existing-window fast path and could run ahead of older queued operations, so a newer SetURL could be overwritten by an older one. withWindow now queues every caller while the creating flag is set and clears the flag only once the queue is seen empty under the lock. A factory panic or a nil window left the creating flag set and the slot dead; creation and drain now reset that state on early exit. hideOtherWindows records the windows it hid only when no restore ran in between, tracked by a generation counter, and re-shows them otherwise, so a restore racing the hide cannot strand hidden windows. * [misc] Run the client/ui subpackage tests in CI The three test workflows filtered the package list with a `/client/ui` prefix match, which dropped the subpackages along with the package that cannot compile without a frontend build. `services`, `preferences`, `i18n` and `authsession` all carry Go-side unit tests that never ran, including the window manager re-entrancy regression test. Anchor the pattern so only `client/ui` itself is excluded. The linux leg keeps the prefix match on 386, where only the 64-bit gtk4/webkitgtk dev packages are installed and the Wails application package would fail to link, and the alpine container job keeps it for the same reason. * [misc] Run the client/ui subpackage tests on a gtk4 4.10 runner The previous commit let the subpackages into the linux client job, where client/ui/services failed to build: the wails runtime's linux cgo layer uses GtkFileDialog, which arrived in gtk4 4.10, and the job's ubuntu-22.04 runner ships 4.6. Move them to their own job pinned to ubuntu-24.04 and restore the linux client job's original exclusion, leaving the 386 and privileged legs on the runner they have used since 2024. The new job needs no build cache, sudo or privileged tag, so it stays a few seconds long. Darwin and Windows keep the anchored pattern from the previous commit and already run these tests green, including the window manager re-entrancy regression test on the platform the deadlock was reported on. * [client] Defer a window close that lands while the window is still being created WindowManager publishes a dialog's slot only after the factory returns, and on Windows the factory blocks in the WebView2 embed pump. A Close* arriving in that gap found a nil slot and returned without doing anything, so the dialog appeared afterwards for a flow that had already been cancelled. The pre-fix Open* dialog functions held mu across the whole creation, which blocked a concurrent Close* until the slot was set; removing that lock hold reopened this gap. Close* now goes through closeWindow: while the slot is being created it records a closer in pendingClose, and finishCreation runs that closer before any queued operation, so a window that is going away is never shown and Wails never sees a Show on a destroyed window, which would recreate it. Ops queued behind a close are dropped; windowOp carries no factory, so they cannot be replayed into a new creation, and the frontend callers reissue on the next state change. The browser-login slot uses the same restoring closer from both CloseBrowserLogin and CloseRenewFlow, since the popup's WindowClosing hook only restores on a user close. Where two closers race one creation the first registered wins, so a later caller cannot replace a restoring closer with one that does not restore.
Start using NetBird at netbird.io
See Documentation
Join our Slack channel or our Community forum
🚀 We are hiring! Join us at https://netbird.io/careers
🤖 NetBird Agent Network (Beta)
Identity-aware access control for AI agents — keyless access to LLM APIs and private resources over the encrypted NetBird tunnel. See
agent-network/or read the docs at netbird.ai.
NetBird combines a configuration-free peer-to-peer private network and a centralized access control system in a single platform, making it easy to create secure private networks for your organization or home.
Connect. NetBird creates a WireGuard-based overlay network that automatically connects your machines over an encrypted tunnel, leaving behind the hassle of opening ports, complex firewall rules, VPN gateways, and so forth.
Secure. NetBird enables secure remote access by applying granular access policies while allowing you to manage them intuitively from a single place. Works universally on any infrastructure.
https://github.com/user-attachments/assets/10cec749-bb56-4ab3-97af-4e38850108d2
Self-host NetBird (video)
Key features
Quickstart with NetBird Cloud
- Download and install NetBird at https://app.netbird.io/install.
- Follow the steps to sign up with Google, Microsoft, GitHub or your email address.
- Check the NetBird admin UI.
Quickstart with self-hosted NetBird
This is the quickest way to try self-hosted NetBird. It should take around 5 minutes to get started if you already have a public domain and a VM. Follow the Advanced guide with a custom identity provider for installations with different IdPs.
Infrastructure requirements:
- A Linux VM with at least 1 CPU and 2 GB of memory.
- The VM should be publicly accessible on TCP ports 80 and 443 and UDP port 3478.
- A public domain name pointing to the VM.
Software requirements:
- Docker with the Compose plugin (Compose v2 or higher). See the Docker installation guide.
Steps
- Download and run the installation script:
export NETBIRD_DOMAIN=netbird.example.com; curl -fsSL https://github.com/netbirdio/netbird/releases/latest/download/getting-started.sh | bash
A bit on NetBird internals
- Every machine in the network runs the NetBird agent, which manages WireGuard.
- Every agent connects to the Management Service, which holds network state, manages peer IPs, and distributes updates to agents.
- Agents use ICE (via pion/ice) to discover connection candidates for peer-to-peer connections.
- Candidates are discovered with the help of STUN servers.
- Agents negotiate a connection through the Signal Service, exchanging end-to-end encrypted messages with candidates.
- When NAT traversal fails (e.g. mobile carrier-grade NAT) and a direct p2p connection isn't possible, the system falls back to a Relay Service and a secure WireGuard tunnel is established through it.
See a complete architecture overview for details.
Community projects
- NetBird installer script
- netbird-tui - terminal UI for managing NetBird peers, routes, and settings
- caddy-netbird - Caddy plugin that embeds a NetBird client for proxying HTTP and TCP/UDP traffic through NetBird networks
Note: The main branch may be in an unstable or even broken state during development.
For stable versions, see releases.
Support acknowledgement
In November 2022, NetBird joined the StartUpSecure program sponsored by the Federal Ministry of Education and Research of the Federal Republic of Germany. Together with the CISPA Helmholtz Center for Information Security, NetBird brings security best practices and simplicity to private networking.
Acknowledgements
We build on open source technologies like WireGuard®, Pion ICE, and Rosenpass. We greatly appreciate the work these projects are doing, and we'd love it if you could support them too (e.g., by starring or contributing).
Legal
This repository is licensed under the BSD-3-Clause license, which applies to all parts of the repository except for the directories management/, signal/ and relay/. Those directories are licensed under the GNU Affero General Public License version 3.0 (AGPLv3). See the respective LICENSE files inside each directory.
WireGuard and the WireGuard logo are registered trademarks of Jason A. Donenfeld.



