[client/ui] Mirror tray profile switch to user-side ProfileManager

The Fyne UI used to write the active profile to both fronts on every
switch (profile.go:264-273): the daemon SwitchProfile RPC for
/var/lib/netbird/active_profile.json, then profileManager.SwitchProfile
for the user-side ~/Library/Application Support/netbird/active_profile.
The Wails ProfileSwitcher only kept the first.

Without the user-side mirror, a UI tray switch updates the daemon's
state but the CLI ProfileManager.GetActiveProfile() still returns the
stale "default". The next "netbird up" then sends ProfileName="default"
in the Login/Up request, and the daemon silently switches back to
default, reverting whatever the user just picked in the tray.

Mirror the daemon switch with profilemanager.NewProfileManager().
SwitchProfile after the daemon RPC succeeds. The daemon stays the
authority — a user-side write failure is logged as a warning, not a
hard error.
This commit is contained in:
Zoltan Papp
2026-05-14 14:52:14 +02:00
parent fc1db63fc3
commit e4eedbe18f

View File

@@ -8,6 +8,8 @@ import (
"strings"
log "github.com/sirupsen/logrus"
"github.com/netbirdio/netbird/client/internal/profilemanager"
)
// ProfileSwitcher encapsulates the full profile-switching reconnect policy so
@@ -62,6 +64,21 @@ func (s *ProfileSwitcher) SwitchActive(ctx context.Context, p ProfileRef) error
return fmt.Errorf("switch profile %q: %w", p.ProfileName, err)
}
// Mirror the daemon-side switch into the user-side ProfileManager state
// (~/Library/Application Support/netbird/active_profile on macOS, the
// equivalent user config dir elsewhere). The CLI's `netbird up` reads
// from this file (cmd/up.go: pm.GetActiveProfile()) and then sends the
// resolved name back in the Login/Up RPC — if it diverges from the
// daemon-side /var/lib/netbird/active_profile.json, the daemon will
// silently switch its active profile to whatever the CLI sends, so the
// next CLI `up` after a UI switch reverts the profile. Failures here
// don't abort the switch (the daemon is the authority; the local
// mirror is a cache the CLI consults), but they leave the CLI's view
// stale until the next successful switch — surface as a warning.
if err := profilemanager.NewProfileManager().SwitchProfile(p.ProfileName); err != nil {
log.Warnf("profileswitcher: mirror to user-side ProfileManager failed: %v", err)
}
if needsDown {
if err := s.connection.Down(ctx); err != nil {
log.Errorf("profileswitcher: Down: %v", err)