From 3aa62e31a6f35f2eb32977a874754d70c28c947e Mon Sep 17 00:00:00 2001 From: mlsmaycon Date: Thu, 21 May 2026 14:52:43 +0200 Subject: [PATCH] fix(synth): refresh account netmap on embedded proxy connect/disconnect MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SynthesizePrivateServiceZones emits A records keyed on the proxy peer's Status.Connected flag and tunnel IP, so the synth output changes every time an embedded `netbird proxy` peer flips state. The trigger was missing: MarkPeerConnected only called OnPeersUpdated when the peer was LoginExpired, and MarkPeerDisconnected never called it at all. Result: when a fresh proxy reconnects, user peers in the account hold their stale netmap (or no synth at all) until some unrelated change pokes the controller. Fire OnPeersUpdated whenever an embedded proxy peer transitions connected/disconnected. OnPeersUpdated routes through bufferSendUpdateAccountPeers so consecutive flaps coalesce and don't storm the controller. AddPeer already calls OnPeersAdded for the new peer ID but that only recomputes the proxy peer's own netmap — user peers still need this new account-wide refresh to pick up the proxy peer's tunnel IP for their private-service DNS records. --- management/server/peer.go | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/management/server/peer.go b/management/server/peer.go index 34b681f51..37cacee41 100644 --- a/management/server/peer.go +++ b/management/server/peer.go @@ -125,6 +125,18 @@ func (am *DefaultAccountManager) MarkPeerConnected(ctx context.Context, peerPubK } } + // An embedded proxy peer flipping to connected is the trigger for + // SynthesizePrivateServiceZones to emit DNS A records pointing at its + // tunnel IP. Without an account-wide netmap recompute, user peers keep + // the stale synth (or no synth at all on first connect) until some + // other change pokes the controller. Fire OnPeersUpdated so the + // buffered recompute fans the new state out to every peer. + if peer.ProxyMeta.Embedded { + if err := am.networkMapController.OnPeersUpdated(ctx, accountID, []string{peer.ID}); err != nil { + log.WithContext(ctx).Warnf("notify network map controller of embedded proxy %s connect: %v", peer.ID, err) + } + } + return nil } @@ -160,6 +172,17 @@ func (am *DefaultAccountManager) MarkPeerDisconnected(ctx context.Context, peerP return nil } am.metrics.AccountManagerMetrics().CountPeerStatusUpdate(telemetry.PeerStatusDisconnect, telemetry.PeerStatusApplied) + + // Symmetric with MarkPeerConnected: when an embedded proxy peer goes + // offline, drive an account-wide netmap recompute so the synthesized + // DNS records that pointed at it are pulled. Without this the records + // linger client-side at TTL until something else triggers a refresh. + if peer.ProxyMeta.Embedded { + if err := am.networkMapController.OnPeersUpdated(ctx, accountID, []string{peer.ID}); err != nil { + log.WithContext(ctx).Warnf("notify network map controller of embedded proxy %s disconnect: %v", peer.ID, err) + } + } + return nil }