Files
netbird/client/ui/grpc.go
Zoltan Papp 6b44d65cac report daemon-down as DaemonUnavailable on initial Peers.Get and gate UI
- Peers.Get returns Status{Status: DaemonUnavailable} on Unavailable
  instead of an error so the React useStatus initial refresh picks up
  the same string the live event stream emits — the overlay no longer
  depends on receiving the synthetic event during boot.
- ProfileContext.refresh swallows Unavailable so the redundant
  "Load Profiles Failed" popup does not overlap the overlay.
- Tray Profiles submenu is disabled while the daemon is unavailable,
  matching the existing settings/debug/connect gating.
- gRPC client uses a 5s ConnectParams MaxDelay; the default 120s cap
  was keeping the SubChannel in backoff for tens of seconds after the
  daemon came back, masking the recovery.
2026-05-18 12:33:46 +02:00

73 lines
1.8 KiB
Go

//go:build !android && !ios && !freebsd && !js
package main
import (
"fmt"
"runtime"
"strings"
"sync"
"time"
"google.golang.org/grpc"
"google.golang.org/grpc/backoff"
"google.golang.org/grpc/credentials/insecure"
"github.com/netbirdio/netbird/client/proto"
"github.com/netbirdio/netbird/client/ui/desktop"
)
// Conn is a lazy, lock-protected gRPC connection to the NetBird daemon.
// One Conn instance is shared by all services so they reuse the same channel.
type Conn struct {
addr string
mu sync.Mutex
client proto.DaemonServiceClient
}
func NewConn(addr string) *Conn {
return &Conn{addr: addr}
}
func (c *Conn) Client() (proto.DaemonServiceClient, error) {
c.mu.Lock()
defer c.mu.Unlock()
if c.client != nil {
return c.client, nil
}
cc, err := grpc.NewClient(
strings.TrimPrefix(c.addr, "tcp://"),
grpc.WithTransportCredentials(insecure.NewCredentials()),
grpc.WithUserAgent(desktop.GetUIUserAgent()),
// Without ConnectParams the SubChannel uses gRPC's default 120s
// MaxDelay, so after a couple of failed dials the UI waits 30-60s
// before noticing a freshly-started daemon. The Wails UI is a
// desktop client expecting prompt reconnects, not a high-fanout
// backend, so a 5s cap is a better trade-off than the default.
grpc.WithConnectParams(grpc.ConnectParams{
Backoff: backoff.Config{
BaseDelay: 1 * time.Second,
Multiplier: 1.6,
Jitter: 0.2,
MaxDelay: 5 * time.Second,
},
}),
)
if err != nil {
return nil, fmt.Errorf("dial daemon: %w", err)
}
c.client = proto.NewDaemonServiceClient(cc)
return c.client, nil
}
// DaemonAddr returns the default daemon gRPC address for the current OS.
// Linux/macOS use a Unix socket; Windows uses TCP loopback.
func DaemonAddr() string {
if runtime.GOOS == "windows" {
return "tcp://127.0.0.1:41731"
}
return "unix:///var/run/netbird.sock"
}