From e4eedbe18f821a422a3de3fc5b163a708342fbd7 Mon Sep 17 00:00:00 2001 From: Zoltan Papp Date: Thu, 14 May 2026 14:52:14 +0200 Subject: [PATCH] [client/ui] Mirror tray profile switch to user-side ProfileManager MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- client/ui/services/profileswitcher.go | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/client/ui/services/profileswitcher.go b/client/ui/services/profileswitcher.go index 1b3d2ba43..5fd8ae1ca 100644 --- a/client/ui/services/profileswitcher.go +++ b/client/ui/services/profileswitcher.go @@ -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)