Repository: netbirdio/netbird

Issue:
Introduce management server option to disable default all to all policy
By default, every account has a default policy allowing all to all traffic of all traffic types.  For automation purposes, I would like an option to disable the creation of this policy that can be passed via the helm chart values.

![Image](https://github.com/user-attachments/assets/d6ae34bb-5600-4c58-9ff1-15ab31bc6aba)

Hints from issue comments:
@mlsmaycon: Thanks @dfry for the suggestion. 

This should be doable. We will update you soon with an estimate.@aliamerj: @mlsmaycon 
can i work on this issue ??

Solution patch:
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 4fc7f315..00898ab2 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -9,7 +9,7 @@ on:
   pull_request:
 
 env:
-  SIGN_PIPE_VER: "v0.0.18"
+  SIGN_PIPE_VER: "v0.0.20"
   GORELEASER_VER: "v2.3.2"
   PRODUCT_NAME: "NetBird"
   COPYRIGHT: "NetBird GmbH"
diff --git a/.github/workflows/test-infrastructure-files.yml b/.github/workflows/test-infrastructure-files.yml
index e2f9e40c..bd37f65c 100644
--- a/.github/workflows/test-infrastructure-files.yml
+++ b/.github/workflows/test-infrastructure-files.yml
@@ -134,6 +134,7 @@ jobs:
           NETBIRD_STORE_ENGINE_MYSQL_DSN: '${{ env.NETBIRD_STORE_ENGINE_MYSQL_DSN }}$'
           CI_NETBIRD_MGMT_IDP_SIGNKEY_REFRESH: false
           CI_NETBIRD_TURN_EXTERNAL_IP: "1.2.3.4"
+          CI_NETBIRD_MGMT_DISABLE_DEFAULT_POLICY: false
 
         run: |
           set -x
@@ -180,6 +181,7 @@ jobs:
           grep -A 7 Relay management.json | egrep '"Secret": ".+"'
           grep DisablePromptLogin management.json | grep 'true'
           grep LoginFlag management.json | grep 0
+          grep DisableDefaultPolicy management.json | grep "$CI_NETBIRD_MGMT_DISABLE_DEFAULT_POLICY"
 
       - name: Install modules
         run: go mod tidy
diff --git a/.goreleaser.yaml b/.goreleaser.yaml
index 693fbfe0..ca5eafa6 100644
--- a/.goreleaser.yaml
+++ b/.goreleaser.yaml
@@ -149,6 +149,7 @@ nfpms:
 dockers:
   - image_templates:
       - netbirdio/netbird:{{ .Version }}-amd64
+      - ghcr.io/netbirdio/netbird:{{ .Version }}-amd64
     ids:
       - netbird
     goarch: amd64
@@ -164,6 +165,7 @@ dockers:
       - "--label=maintainer=dev@netbird.io"
   - image_templates:
       - netbirdio/netbird:{{ .Version }}-arm64v8
+      - ghcr.io/netbirdio/netbird:{{ .Version }}-arm64v8
     ids:
       - netbird
     goarch: arm64
diff --git a/client/Dockerfile b/client/Dockerfile
index 16b2916c..5f1f7004 100644
--- a/client/Dockerfile
+++ b/client/Dockerfile
@@ -1,6 +1,9 @@
 FROM alpine:3.21.3
 # iproute2: busybox doesn't display ip rules properly
 RUN apk add --no-cache ca-certificates ip6tables iproute2 iptables
+
+ARG NETBIRD_BINARY=netbird
+COPY ${NETBIRD_BINARY} /usr/local/bin/netbird
+
 ENV NB_FOREGROUND_MODE=true
 ENTRYPOINT [ "/usr/local/bin/netbird","up"]
-COPY netbird /usr/local/bin/netbird
diff --git a/client/Dockerfile-rootless b/client/Dockerfile-rootless
index 78314ba1..5055cb20 100644
--- a/client/Dockerfile-rootless
+++ b/client/Dockerfile-rootless
@@ -1,6 +1,7 @@
 FROM alpine:3.21.0
 
-COPY netbird /usr/local/bin/netbird
+ARG NETBIRD_BINARY=netbird
+COPY ${NETBIRD_BINARY} /usr/local/bin/netbird
 
 RUN apk add --no-cache ca-certificates \
     && adduser -D -h /var/lib/netbird netbird
diff --git a/client/android/preferences.go b/client/android/preferences.go
index 2a8b197e..2d5668d1 100644
--- a/client/android/preferences.go
+++ b/client/android/preferences.go
@@ -4,12 +4,12 @@ import (
 	"github.com/netbirdio/netbird/client/internal"
 )
 
-// Preferences export a subset of the internal config for gomobile
+// Preferences exports a subset of the internal config for gomobile
 type Preferences struct {
 	configInput internal.ConfigInput
 }
 
-// NewPreferences create new Preferences instance
+// NewPreferences creates a new Preferences instance
 func NewPreferences(configPath string) *Preferences {
 	ci := internal.ConfigInput{
 		ConfigPath: configPath,
@@ -17,7 +17,7 @@ func NewPreferences(configPath string) *Preferences {
 	return &Preferences{ci}
 }
 
-// GetManagementURL read url from config file
+// GetManagementURL reads URL from config file
 func (p *Preferences) GetManagementURL() (string, error) {
 	if p.configInput.ManagementURL != "" {
 		return p.configInput.ManagementURL, nil
@@ -30,12 +30,12 @@ func (p *Preferences) GetManagementURL() (string, error) {
 	return cfg.ManagementURL.String(), err
 }
 
-// SetManagementURL store the given url and wait for commit
+// SetManagementURL stores the given URL and waits for commit
 func (p *Preferences) SetManagementURL(url string) {
 	p.configInput.ManagementURL = url
 }
 
-// GetAdminURL read url from config file
+// GetAdminURL reads URL from config file
 func (p *Preferences) GetAdminURL() (string, error) {
 	if p.configInput.AdminURL != "" {
 		return p.configInput.AdminURL, nil
@@ -48,12 +48,12 @@ func (p *Preferences) GetAdminURL() (string, error) {
 	return cfg.AdminURL.String(), err
 }
 
-// SetAdminURL store the given url and wait for commit
+// SetAdminURL stores the given URL and waits for commit
 func (p *Preferences) SetAdminURL(url string) {
 	p.configInput.AdminURL = url
 }
 
-// GetPreSharedKey read preshared key from config file
+// GetPreSharedKey reads pre-shared key from config file
 func (p *Preferences) GetPreSharedKey() (string, error) {
 	if p.configInput.PreSharedKey != nil {
 		return *p.configInput.PreSharedKey, nil
@@ -66,17 +66,17 @@ func (p *Preferences) GetPreSharedKey() (string, error) {
 	return cfg.PreSharedKey, err
 }
 
-// SetPreSharedKey store the given key and wait for commit
+// SetPreSharedKey stores the given key and waits for commit
 func (p *Preferences) SetPreSharedKey(key string) {
 	p.configInput.PreSharedKey = &key
 }
 
-// SetRosenpassEnabled store if rosenpass is enabled
+// SetRosenpassEnabled stores whether Rosenpass is enabled
 func (p *Preferences) SetRosenpassEnabled(enabled bool) {
 	p.configInput.RosenpassEnabled = &enabled
 }
 
-// GetRosenpassEnabled read rosenpass enabled from config file
+// GetRosenpassEnabled reads Rosenpass enabled status from config file
 func (p *Preferences) GetRosenpassEnabled() (bool, error) {
 	if p.configInput.RosenpassEnabled != nil {
 		return *p.configInput.RosenpassEnabled, nil
@@ -89,12 +89,12 @@ func (p *Preferences) GetRosenpassEnabled() (bool, error) {
 	return cfg.RosenpassEnabled, err
 }
 
-// SetRosenpassPermissive store the given permissive and wait for commit
+// SetRosenpassPermissive stores the given permissive setting and waits for commit
 func (p *Preferences) SetRosenpassPermissive(permissive bool) {
 	p.configInput.RosenpassPermissive = &permissive
 }
 
-// GetRosenpassPermissive read rosenpass permissive from config file
+// GetRosenpassPermissive reads Rosenpass permissive setting from config file
 func (p *Preferences) GetRosenpassPermissive() (bool, error) {
 	if p.configInput.RosenpassPermissive != nil {
 		return *p.configInput.RosenpassPermissive, nil
@@ -107,7 +107,119 @@ func (p *Preferences) GetRosenpassPermissive() (bool, error) {
 	return cfg.RosenpassPermissive, err
 }
 
-// Commit write out the changes into config file
+// GetDisableClientRoutes reads disable client routes setting from config file
+func (p *Preferences) GetDisableClientRoutes() (bool, error) {
+	if p.configInput.DisableClientRoutes != nil {
+		return *p.configInput.DisableClientRoutes, nil
+	}
+
+	cfg, err := internal.ReadConfig(p.configInput.ConfigPath)
+	if err != nil {
+		return false, err
+	}
+	return cfg.DisableClientRoutes, err
+}
+
+// SetDisableClientRoutes stores the given value and waits for commit
+func (p *Preferences) SetDisableClientRoutes(disable bool) {
+	p.configInput.DisableClientRoutes = &disable
+}
+
+// GetDisableServerRoutes reads disable server routes setting from config file
+func (p *Preferences) GetDisableServerRoutes() (bool, error) {
+	if p.configInput.DisableServerRoutes != nil {
+		return *p.configInput.DisableServerRoutes, nil
+	}
+
+	cfg, err := internal.ReadConfig(p.configInput.ConfigPath)
+	if err != nil {
+		return false, err
+	}
+	return cfg.DisableServerRoutes, err
+}
+
+// SetDisableServerRoutes stores the given value and waits for commit
+func (p *Preferences) SetDisableServerRoutes(disable bool) {
+	p.configInput.DisableServerRoutes = &disable
+}
+
+// GetDisableDNS reads disable DNS setting from config file
+func (p *Preferences) GetDisableDNS() (bool, error) {
+	if p.configInput.DisableDNS != nil {
+		return *p.configInput.DisableDNS, nil
+	}
+
+	cfg, err := internal.ReadConfig(p.configInput.ConfigPath)
+	if err != nil {
+		return false, err
+	}
+	return cfg.DisableDNS, err
+}
+
+// SetDisableDNS stores the given value and waits for commit
+func (p *Preferences) SetDisableDNS(disable bool) {
+	p.configInput.DisableDNS = &disable
+}
+
+// GetDisableFirewall reads disable firewall setting from config file
+func (p *Preferences) GetDisableFirewall() (bool, error) {
+	if p.configInput.DisableFirewall != nil {
+		return *p.configInput.DisableFirewall, nil
+	}
+
+	cfg, err := internal.ReadConfig(p.configInput.ConfigPath)
+	if err != nil {
+		return false, err
+	}
+	return cfg.DisableFirewall, err
+}
+
+// SetDisableFirewall stores the given value and waits for commit
+func (p *Preferences) SetDisableFirewall(disable bool) {
+	p.configInput.DisableFirewall = &disable
+}
+
+// GetServerSSHAllowed reads server SSH allowed setting from config file
+func (p *Preferences) GetServerSSHAllowed() (bool, error) {
+	if p.configInput.ServerSSHAllowed != nil {
+		return *p.configInput.ServerSSHAllowed, nil
+	}
+
+	cfg, err := internal.ReadConfig(p.configInput.ConfigPath)
+	if err != nil {
+		return false, err
+	}
+	if cfg.ServerSSHAllowed == nil {
+		// Default to false for security on Android
+		return false, nil
+	}
+	return *cfg.ServerSSHAllowed, err
+}
+
+// SetServerSSHAllowed stores the given value and waits for commit
+func (p *Preferences) SetServerSSHAllowed(allowed bool) {
+	p.configInput.ServerSSHAllowed = &allowed
+}
+
+// GetBlockInbound reads block inbound setting from config file
+func (p *Preferences) GetBlockInbound() (bool, error) {
+	if p.configInput.BlockInbound != nil {
+		return *p.configInput.BlockInbound, nil
+	}
+
+	cfg, err := internal.ReadConfig(p.configInput.ConfigPath)
+	if err != nil {
+		return false, err
+	}
+	return cfg.BlockInbound, err
+}
+
+// SetBlockInbound stores the given value and waits for commit
+func (p *Preferences) SetBlockInbound(block bool) {
+	p.configInput.BlockInbound = &block
+}
+
+// Commit writes out the changes to the config file
 func (p *Preferences) Commit() error {
 	_, err := internal.UpdateOrCreateConfig(p.configInput)
 	return err
diff --git a/client/cmd/system.go b/client/cmd/system.go
index 83ce8d21..f6343240 100644
--- a/client/cmd/system.go
+++ b/client/cmd/system.go
@@ -38,5 +38,5 @@ func init() {
 
 	upCmd.PersistentFlags().BoolVar(&blockInbound, blockInboundFlag, false,
 		"Block inbound connections. If enabled, the client will not allow any inbound connections to the local machine nor routed networks.\n"+
-		"This overrides any policies received from the management service.")
+			"This overrides any policies received from the management service.")
 }
diff --git a/client/iface/device/device_android.go b/client/iface/device/device_android.go
index ab3e611e..ae9e29bd 100644
--- a/client/iface/device/device_android.go
+++ b/client/iface/device/device_android.go
@@ -24,6 +24,7 @@ type WGTunDevice struct {
 	mtu        int
 	iceBind    *bind.ICEBind
 	tunAdapter TunAdapter
+	disableDNS bool
 
 	name           string
 	device         *device.Device
@@ -32,7 +33,7 @@ type WGTunDevice struct {
 	configurer     WGConfigurer
 }
 
-func NewTunDevice(address wgaddr.Address, port int, key string, mtu int, iceBind *bind.ICEBind, tunAdapter TunAdapter) *WGTunDevice {
+func NewTunDevice(address wgaddr.Address, port int, key string, mtu int, iceBind *bind.ICEBind, tunAdapter TunAdapter, disableDNS bool) *WGTunDevice {
 	return &WGTunDevice{
 		address:    address,
 		port:       port,
@@ -40,6 +41,7 @@ func NewTunDevice(address wgaddr.Address, port int, key string, mtu int, iceBind
 		mtu:        mtu,
 		iceBind:    iceBind,
 		tunAdapter: tunAdapter,
+		disableDNS: disableDNS,
 	}
 }
 
@@ -49,6 +51,13 @@ func (t *WGTunDevice) Create(routes []string, dns string, searchDomains []string
 	routesString := routesToString(routes)
 	searchDomainsToString := searchDomainsToString(searchDomains)
 
+	// Skip DNS configuration when DisableDNS is enabled
+	if t.disableDNS {
+		log.Info("DNS is disabled, skipping DNS and search domain configuration")
+		dns = ""
+		searchDomainsToString = ""
+	}
+
 	fd, err := t.tunAdapter.ConfigureInterface(t.address.String(), t.mtu, dns, searchDomainsToString, routesString)
 	if err != nil {
 		log.Errorf("failed to create Android interface: %s", err)
diff --git a/client/iface/iface.go b/client/iface/iface.go
index 7d609f4c..006dfe4e 100644
--- a/client/iface/iface.go
+++ b/client/iface/iface.go
@@ -43,6 +43,7 @@ type WGIFaceOpts struct {
 	MobileArgs   *device.MobileIFaceArguments
 	TransportNet transport.Net
 	FilterFn     bind.FilterFn
+	DisableDNS   bool
 }
 
 // WGIface represents an interface instance
diff --git a/client/iface/iface_new_android.go b/client/iface/iface_new_android.go
index 35046b88..c8babea3 100644
--- a/client/iface/iface_new_android.go
+++ b/client/iface/iface_new_android.go
@@ -18,7 +18,7 @@ func NewWGIFace(opts WGIFaceOpts) (*WGIface, error) {
 
 	wgIFace := &WGIface{
 		userspaceBind:  true,
-		tun:            device.NewTunDevice(wgAddress, opts.WGPort, opts.WGPrivKey, opts.MTU, iceBind, opts.MobileArgs.TunAdapter),
+		tun:            device.NewTunDevice(wgAddress, opts.WGPort, opts.WGPrivKey, opts.MTU, iceBind, opts.MobileArgs.TunAdapter, opts.DisableDNS),
 		wgProxyFactory: wgproxy.NewUSPFactory(iceBind),
 	}
 	return wgIFace, nil
diff --git a/client/internal/acl/manager.go b/client/internal/acl/manager.go
index c8bc9123..32dc7fbb 100644
--- a/client/internal/acl/manager.go
+++ b/client/internal/acl/manager.go
@@ -398,11 +398,15 @@ func (d *DefaultManager) squashAcceptRules(
 	//
 	// We zeroed this to notify squash function that this protocol can't be squashed.
 	addRuleToCalculationMap := func(i int, r *mgmProto.FirewallRule, protocols map[mgmProto.RuleProtocol]*protoMatch) {
-		drop := r.Action == mgmProto.RuleAction_DROP || r.Port != ""
-		if drop {
+		hasPortRestrictions := r.Action == mgmProto.RuleAction_DROP ||
+			r.Port != "" || !portInfoEmpty(r.PortInfo)
+
+		if hasPortRestrictions {
+			// Don't squash rules with port restrictions
 			protocols[r.Protocol] = &protoMatch{ips: map[string]int{}}
 			return
 		}
+
 		if _, ok := protocols[r.Protocol]; !ok {
 			protocols[r.Protocol] = &protoMatch{
 				ips: map[string]int{},
diff --git a/client/internal/config.go b/client/internal/config.go
index 45a7620e..37ee1e1b 100644
--- a/client/internal/config.go
+++ b/client/internal/config.go
@@ -223,6 +223,8 @@ func createNewConfig(input ConfigInput) (*Config, error) {
 	config := &Config{
 		// defaults to false only for new (post 0.26) configurations
 		ServerSSHAllowed: util.False(),
+		// default to disabling server routes on Android for security
+		DisableServerRoutes: runtime.GOOS == "android",
 	}
 
 	if _, err := config.apply(input); err != nil {
@@ -416,9 +418,15 @@ func (config *Config) apply(input ConfigInput) (updated bool, err error) {
 		config.ServerSSHAllowed = input.ServerSSHAllowed
 		updated = true
 	} else if config.ServerSSHAllowed == nil {
-		// enables SSH for configs from old versions to preserve backwards compatibility
-		log.Infof("falling back to enabled SSH server for pre-existing configuration")
-		config.ServerSSHAllowed = util.True()
+		if runtime.GOOS == "android" {
+			// default to disabled SSH on Android for security
+			log.Infof("setting SSH server to false by default on Android")
+			config.ServerSSHAllowed = util.False()
+		} else {
+			// enables SSH for configs from old versions to preserve backwards compatibility
+			log.Infof("falling back to enabled SSH server for pre-existing configuration")
+			config.ServerSSHAllowed = util.True()
+		}
 		updated = true
 	}
 
diff --git a/client/internal/conn_mgr.go b/client/internal/conn_mgr.go
index aac312dc..c630d305 100644
--- a/client/internal/conn_mgr.go
+++ b/client/internal/conn_mgr.go
@@ -175,7 +175,7 @@ func (e *ConnMgr) AddPeerConn(ctx context.Context, peerKey string, conn *peer.Co
 		PeerConnID: conn.ConnID(),
 		Log:        conn.Log,
 	}
-	excluded, err := e.lazyConnMgr.AddPeer(lazyPeerCfg)
+	excluded, err := e.lazyConnMgr.AddPeer(e.lazyCtx, lazyPeerCfg)
 	if err != nil {
 		conn.Log.Errorf("failed to add peer to lazyconn manager: %v", err)
 		if err := conn.Open(ctx); err != nil {
diff --git a/client/internal/dns/handler_chain.go b/client/internal/dns/handler_chain.go
index 22caaa76..7e7e7cc2 100644
--- a/client/internal/dns/handler_chain.go
+++ b/client/internal/dns/handler_chain.go
@@ -11,9 +11,10 @@ import (
 )
 
 const (
-	PriorityDNSRoute    = 100
-	PriorityMatchDomain = 50
-	PriorityDefault     = 1
+	PriorityLocal    = 100
+	PriorityDNSRoute = 75
+	PriorityUpstream = 50
+	PriorityDefault  = 1
 )
 
 type SubdomainMatcher interface {
diff --git a/client/internal/dns/server.go b/client/internal/dns/server.go
index 7b845235..e81aebf9 100644
--- a/client/internal/dns/server.go
+++ b/client/internal/dns/server.go
@@ -527,7 +527,7 @@ func (s *DefaultServer) buildLocalHandlerUpdate(customZones []nbdns.CustomZone)
 		muxUpdates = append(muxUpdates, handlerWrapper{
 			domain:   customZone.Domain,
 			handler:  s.localResolver,
-			priority: PriorityMatchDomain,
+			priority: PriorityLocal,
 		})
 
 		for _, record := range customZone.Records {
@@ -566,7 +566,7 @@ func (s *DefaultServer) buildUpstreamHandlerUpdate(nameServerGroups []*nbdns.Nam
 	groupedNS := groupNSGroupsByDomain(nameServerGroups)
 
 	for _, domainGroup := range groupedNS {
-		basePriority := PriorityMatchDomain
+		basePriority := PriorityUpstream
 		if domainGroup.domain == nbdns.RootZone {
 			basePriority = PriorityDefault
 		}
@@ -588,10 +588,14 @@ func (s *DefaultServer) createHandlersForDomainGroup(domainGroup nsGroupsByDomai
 		// Decrement priority by handler index (0, 1, 2, ...) to avoid conflicts
 		priority := basePriority - i
 
-		// Check if we're about to overlap with the next priority tier
-		if basePriority == PriorityMatchDomain && priority <= PriorityDefault {
+		// Check if we're about to overlap with the next priority tier.
+		// This boundary check ensures that the priority of upstream handlers does not conflict
+		// with the default priority tier. By decrementing the priority for each handler, we avoid
+		// overlaps, but if the calculated priority falls into the default tier, we skip the remaining
+		// handlers to maintain the integrity of the priority system.
+		if basePriority == PriorityUpstream && priority <= PriorityDefault {
 			log.Warnf("too many handlers for domain=%s, would overlap with default priority tier (diff=%d). Skipping remaining handlers",
-				domainGroup.domain, PriorityMatchDomain-PriorityDefault)
+				domainGroup.domain, PriorityUpstream-PriorityDefault)
 			break
 		}
 
diff --git a/client/internal/dns/upstream.go b/client/internal/dns/upstream.go
index 2fbfb3b9..c44d3659 100644
--- a/client/internal/dns/upstream.go
+++ b/client/internal/dns/upstream.go
@@ -2,6 +2,7 @@ package dns
 
 import (
 	"context"
+	"crypto/rand"
 	"crypto/sha256"
 	"encoding/hex"
 	"errors"
@@ -103,19 +104,21 @@ func (u *upstreamResolverBase) Stop() {
 
 // ServeDNS handles a DNS request
 func (u *upstreamResolverBase) ServeDNS(w dns.ResponseWriter, r *dns.Msg) {
+	requestID := GenerateRequestID()
+	logger := log.WithField("request_id", requestID)
 	var err error
 	defer func() {
 		u.checkUpstreamFails(err)
 	}()
 
-	log.Tracef("received upstream question: domain=%s type=%v class=%v", r.Question[0].Name, r.Question[0].Qtype, r.Question[0].Qclass)
+	logger.Tracef("received upstream question: domain=%s type=%v class=%v", r.Question[0].Name, r.Question[0].Qtype, r.Question[0].Qclass)
 	if r.Extra == nil {
 		r.MsgHdr.AuthenticatedData = true
 	}
 
 	select {
 	case <-u.ctx.Done():
-		log.Tracef("%s has been stopped", u)
+		logger.Tracef("%s has been stopped", u)
 		return
 	default:
 	}
@@ -132,35 +135,35 @@ func (u *upstreamResolverBase) ServeDNS(w dns.ResponseWriter, r *dns.Msg) {
 
 		if err != nil {
 			if errors.Is(err, context.DeadlineExceeded) || isTimeout(err) {
-				log.Warnf("upstream %s timed out for question domain=%s", upstream, r.Question[0].Name)
+				logger.Warnf("upstream %s timed out for question domain=%s", upstream, r.Question[0].Name)
 				continue
 			}
-			log.Warnf("failed to query upstream %s for question domain=%s: %s", upstream, r.Question[0].Name, err)
+			logger.Warnf("failed to query upstream %s for question domain=%s: %s", upstream, r.Question[0].Name, err)
 			continue
 		}
 
 		if rm == nil || !rm.Response {
-			log.Warnf("no response from upstream %s for question domain=%s", upstream, r.Question[0].Name)
+			logger.Warnf("no response from upstream %s for question domain=%s", upstream, r.Question[0].Name)
 			continue
 		}
 
 		u.successCount.Add(1)
-		log.Tracef("took %s to query the upstream %s for question domain=%s", t, upstream, r.Question[0].Name)
+		logger.Tracef("took %s to query the upstream %s for question domain=%s", t, upstream, r.Question[0].Name)
 
 		if err = w.WriteMsg(rm); err != nil {
-			log.Errorf("failed to write DNS response for question domain=%s: %s", r.Question[0].Name, err)
+			logger.Errorf("failed to write DNS response for question domain=%s: %s", r.Question[0].Name, err)
 		}
 		// count the fails only if they happen sequentially
 		u.failsCount.Store(0)
 		return
 	}
 	u.failsCount.Add(1)
-	log.Errorf("all queries to the %s failed for question domain=%s", u, r.Question[0].Name)
+	logger.Errorf("all queries to the %s failed for question domain=%s", u, r.Question[0].Name)
 
 	m := new(dns.Msg)
 	m.SetRcode(r, dns.RcodeServerFailure)
 	if err := w.WriteMsg(m); err != nil {
-		log.Errorf("failed to write error response for %s for question domain=%s: %s", u, r.Question[0].Name, err)
+		logger.Errorf("failed to write error response for %s for question domain=%s: %s", u, r.Question[0].Name, err)
 	}
 }
 
@@ -385,3 +388,13 @@ func ExchangeWithFallback(ctx context.Context, client *dns.Client, r *dns.Msg, u
 
 	return rm, t, nil
 }
+
+func GenerateRequestID() string {
+	bytes := make([]byte, 4)
+	_, err := rand.Read(bytes)
+	if err != nil {
+		log.Errorf("failed to generate request ID: %v", err)
+		return ""
+	}
+	return hex.EncodeToString(bytes)
+}
diff --git a/client/internal/dnsfwd/forwarder.go b/client/internal/dnsfwd/forwarder.go
index 45b47963..506c429c 100644
--- a/client/internal/dnsfwd/forwarder.go
+++ b/client/internal/dnsfwd/forwarder.go
@@ -18,14 +18,20 @@ import (
 	nberrors "github.com/netbirdio/netbird/client/errors"
 	firewall "github.com/netbirdio/netbird/client/firewall/manager"
 	"github.com/netbirdio/netbird/client/internal/peer"
-	nbdns "github.com/netbirdio/netbird/dns"
-	"github.com/netbirdio/netbird/management/domain"
 	"github.com/netbirdio/netbird/route"
 )
 
 const errResolveFailed = "failed to resolve query for domain=%s: %v"
 const upstreamTimeout = 15 * time.Second
 
+type resolver interface {
+	LookupNetIP(ctx context.Context, network, host string) ([]netip.Addr, error)
+}
+
+type firewaller interface {
+	UpdateSet(set firewall.Set, prefixes []netip.Prefix) error
+}
+
 type DNSForwarder struct {
 	listenAddress  string
 	ttl            uint32
@@ -38,16 +44,18 @@ type DNSForwarder struct {
 
 	mutex      sync.RWMutex
 	fwdEntries []*ForwarderEntry
-	firewall   firewall.Manager
+	firewall   firewaller
+	resolver   resolver
 }
 
-func NewDNSForwarder(listenAddress string, ttl uint32, firewall firewall.Manager, statusRecorder *peer.Status) *DNSForwarder {
+func NewDNSForwarder(listenAddress string, ttl uint32, firewall firewaller, statusRecorder *peer.Status) *DNSForwarder {
 	log.Debugf("creating DNS forwarder with listen_address=%s ttl=%d", listenAddress, ttl)
 	return &DNSForwarder{
 		listenAddress:  listenAddress,
 		ttl:            ttl,
 		firewall:       firewall,
 		statusRecorder: statusRecorder,
+		resolver:       net.DefaultResolver,
 	}
 }
 
@@ -57,14 +65,17 @@ func (f *DNSForwarder) Listen(entries []*ForwarderEntry) error {
 	// UDP server
 	mux := dns.NewServeMux()
 	f.mux = mux
+	mux.HandleFunc(".", f.handleDNSQueryUDP)
 	f.dnsServer = &dns.Server{
 		Addr:    f.listenAddress,
 		Net:     "udp",
 		Handler: mux,
 	}
+
 	// TCP server
 	tcpMux := dns.NewServeMux()
 	f.tcpMux = tcpMux
+	tcpMux.HandleFunc(".", f.handleDNSQueryTCP)
 	f.tcpServer = &dns.Server{
 		Addr:    f.listenAddress,
 		Net:     "tcp",
@@ -87,30 +98,13 @@ func (f *DNSForwarder) Listen(entries []*ForwarderEntry) error {
 	// return the first error we get (e.g. bind failure or shutdown)
 	return <-errCh
 }
+
 func (f *DNSForwarder) UpdateDomains(entries []*ForwarderEntry) {
 	f.mutex.Lock()
 	defer f.mutex.Unlock()
 
-	if f.mux == nil {
-		log.Debug("DNS mux is nil, skipping domain update")
-		f.fwdEntries = entries
-		return
-	}
-
-	oldDomains := filterDomains(f.fwdEntries)
-	for _, d := range oldDomains {
-		f.mux.HandleRemove(d.PunycodeString())
-		f.tcpMux.HandleRemove(d.PunycodeString())
-	}
-
-	newDomains := filterDomains(entries)
-	for _, d := range newDomains {
-		f.mux.HandleFunc(d.PunycodeString(), f.handleDNSQueryUDP)
-		f.tcpMux.HandleFunc(d.PunycodeString(), f.handleDNSQueryTCP)
-	}
-
 	f.fwdEntries = entries
-	log.Debugf("Updated domains from %v to %v", oldDomains, newDomains)
+	log.Debugf("Updated DNS forwarder with %d domains", len(entries))
 }
 
 func (f *DNSForwarder) Close(ctx context.Context) error {
@@ -157,22 +151,31 @@ func (f *DNSForwarder) handleDNSQuery(w dns.ResponseWriter, query *dns.Msg) *dns
 		return nil
 	}
 
+	mostSpecificResId, matchingEntries := f.getMatchingEntries(strings.TrimSuffix(domain, "."))
+	// query doesn't match any configured domain
+	if mostSpecificResId == "" {
+		resp.Rcode = dns.RcodeRefused
+		if err := w.WriteMsg(resp); err != nil {
+			log.Errorf("failed to write DNS response: %v", err)
+		}
+		return nil
+	}
+
 	ctx, cancel := context.WithTimeout(context.Background(), upstreamTimeout)
 	defer cancel()
-	ips, err := net.DefaultResolver.LookupNetIP(ctx, network, domain)
+	ips, err := f.resolver.LookupNetIP(ctx, network, domain)
 	if err != nil {
 		f.handleDNSError(w, query, resp, domain, err)
 		return nil
 	}
 
-	f.updateInternalState(domain, ips)
+	f.updateInternalState(ips, mostSpecificResId, matchingEntries)
 	f.addIPsToResponse(resp, domain, ips)
 
 	return resp
 }
 
 func (f *DNSForwarder) handleDNSQueryUDP(w dns.ResponseWriter, query *dns.Msg) {
-
 	resp := f.handleDNSQuery(w, query)
 	if resp == nil {
 		return
@@ -206,9 +209,8 @@ func (f *DNSForwarder) handleDNSQueryTCP(w dns.ResponseWriter, query *dns.Msg) {
 	}
 }
 
-func (f *DNSForwarder) updateInternalState(domain string, ips []netip.Addr) {
+func (f *DNSForwarder) updateInternalState(ips []netip.Addr, mostSpecificResId route.ResID, matchingEntries []*ForwarderEntry) {
 	var prefixes []netip.Prefix
-	mostSpecificResId, matchingEntries := f.getMatchingEntries(strings.TrimSuffix(domain, "."))
 	if mostSpecificResId != "" {
 		for _, ip := range ips {
 			var prefix netip.Prefix
@@ -339,16 +341,3 @@ func (f *DNSForwarder) getMatchingEntries(domain string) (route.ResID, []*Forwar
 
 	return selectedResId, matches
 }
-
-// filterDomains returns a list of normalized domains
-func filterDomains(entries []*ForwarderEntry) domain.List {
-	newDomains := make(domain.List, 0, len(entries))
-	for _, d := range entries {
-		if d.Domain == "" {
-			log.Warn("empty domain in DNS forwarder")
-			continue
-		}
-		newDomains = append(newDomains, domain.Domain(nbdns.NormalizeZone(d.Domain.PunycodeString())))
-	}
-	return newDomains
-}
diff --git a/client/internal/engine.go b/client/internal/engine.go
index 253ecb2a..4ea6fbd9 100644
--- a/client/internal/engine.go
+++ b/client/internal/engine.go
@@ -1527,6 +1527,7 @@ func (e *Engine) newWgIface() (*iface.WGIface, error) {
 		MTU:          iface.DefaultMTU,
 		TransportNet: transportNet,
 		FilterFn:     e.addrViaRoutes,
+		DisableDNS:   e.config.DisableDNS,
 	}
 
 	switch runtime.GOOS {
diff --git a/client/internal/lazyconn/inactivity/inactivity.go b/client/internal/lazyconn/inactivity/inactivity.go
index a30c1846..9b7c8511 100644
--- a/client/internal/lazyconn/inactivity/inactivity.go
+++ b/client/internal/lazyconn/inactivity/inactivity.go
@@ -68,3 +68,8 @@ func (i *Monitor) PauseTimer() {
 func (i *Monitor) ResetTimer() {
 	i.timer.Reset(i.inactivityThreshold)
 }
+
+func (i *Monitor) ResetMonitor(ctx context.Context, timeoutChan chan peer.ConnID) {
+	i.Stop()
+	go i.Start(ctx, timeoutChan)
+}
diff --git a/client/internal/lazyconn/manager/manager.go b/client/internal/lazyconn/manager/manager.go
index 718bdbdd..74ede50a 100644
--- a/client/internal/lazyconn/manager/manager.go
+++ b/client/internal/lazyconn/manager/manager.go
@@ -58,7 +58,7 @@ type Manager struct {
 	// Route HA group management
 	peerToHAGroups map[string][]route.HAUniqueID // peer ID -> HA groups they belong to
 	haGroupToPeers map[route.HAUniqueID][]string // HA group -> peer IDs in the group
-	routesMu       sync.RWMutex                  // protects route mappings
+	routesMu       sync.RWMutex
 
 	onInactive chan peerid.ConnID
 }
@@ -146,7 +146,7 @@ func (m *Manager) Start(ctx context.Context) {
 		case peerConnID := <-m.activityManager.OnActivityChan:
 			m.onPeerActivity(ctx, peerConnID)
 		case peerConnID := <-m.onInactive:
-			m.onPeerInactivityTimedOut(peerConnID)
+			m.onPeerInactivityTimedOut(ctx, peerConnID)
 		}
 	}
 }
@@ -197,7 +197,7 @@ func (m *Manager) ExcludePeer(ctx context.Context, peerConfigs []lazyconn.PeerCo
 	return added
 }
 
-func (m *Manager) AddPeer(peerCfg lazyconn.PeerConfig) (bool, error) {
+func (m *Manager) AddPeer(ctx context.Context, peerCfg lazyconn.PeerConfig) (bool, error) {
 	m.managedPeersMu.Lock()
 	defer m.managedPeersMu.Unlock()
 
@@ -225,6 +225,13 @@ func (m *Manager) AddPeer(peerCfg lazyconn.PeerConfig) (bool, error) {
 		peerCfg:         &peerCfg,
 		expectedWatcher: watcherActivity,
 	}
+
+	// Check if this peer should be activated because its HA group peers are active
+	if group, ok := m.shouldActivateNewPeer(peerCfg.PublicKey); ok {
+		peerCfg.Log.Debugf("peer belongs to active HA group %s, will activate immediately", group)
+		m.activateNewPeerInActiveGroup(ctx, peerCfg)
+	}
+
 	return false, nil
 }
 
@@ -315,36 +322,38 @@ func (m *Manager) activateSinglePeer(ctx context.Context, cfg *lazyconn.PeerConf
 
 // activateHAGroupPeers activates all peers in HA groups that the given peer belongs to
 func (m *Manager) activateHAGroupPeers(ctx context.Context, triggerPeerID string) {
+	var peersToActivate []string
+
 	m.routesMu.RLock()
 	haGroups := m.peerToHAGroups[triggerPeerID]
-	m.routesMu.RUnlock()
 
 	if len(haGroups) == 0 {
+		m.routesMu.RUnlock()
 		log.Debugf("peer %s is not part of any HA groups", triggerPeerID)
 		return
 	}
 
-	activatedCount := 0
 	for _, haGroup := range haGroups {
-		m.routesMu.RLock()
 		peers := m.haGroupToPeers[haGroup]
-		m.routesMu.RUnlock()
-
 		for _, peerID := range peers {
-			if peerID == triggerPeerID {
-				continue
+			if peerID != triggerPeerID {
+				peersToActivate = append(peersToActivate, peerID)
 			}
+		}
+	}
+	m.routesMu.RUnlock()
 
-			cfg, mp := m.getPeerForActivation(peerID)
-			if cfg == nil {
-				continue
-			}
+	activatedCount := 0
+	for _, peerID := range peersToActivate {
+		cfg, mp := m.getPeerForActivation(peerID)
+		if cfg == nil {
+			continue
+		}
 
-			if m.activateSinglePeer(ctx, cfg, mp) {
-				activatedCount++
-				cfg.Log.Infof("activated peer as part of HA group %s (triggered by %s)", haGroup, triggerPeerID)
-				m.peerStore.PeerConnOpen(m.engineCtx, cfg.PublicKey)
-			}
+		if m.activateSinglePeer(ctx, cfg, mp) {
+			activatedCount++
+			cfg.Log.Infof("activated peer as part of HA group (triggered by %s)", triggerPeerID)
+			m.peerStore.PeerConnOpen(m.engineCtx, cfg.PublicKey)
 		}
 	}
 
@@ -354,6 +363,51 @@ func (m *Manager) activateHAGroupPeers(ctx context.Context, triggerPeerID string
 	}
 }
 
+// shouldActivateNewPeer checks if a newly added peer should be activated
+// because other peers in its HA groups are already active
+func (m *Manager) shouldActivateNewPeer(peerID string) (route.HAUniqueID, bool) {
+	m.routesMu.RLock()
+	defer m.routesMu.RUnlock()
+
+	haGroups := m.peerToHAGroups[peerID]
+	if len(haGroups) == 0 {
+		return "", false
+	}
+
+	for _, haGroup := range haGroups {
+		peers := m.haGroupToPeers[haGroup]
+		for _, groupPeerID := range peers {
+			if groupPeerID == peerID {
+				continue
+			}
+
+			cfg, ok := m.managedPeers[groupPeerID]
+			if !ok {
+				continue
+			}
+			if mp, ok := m.managedPeersByConnID[cfg.PeerConnID]; ok && mp.expectedWatcher == watcherInactivity {
+				return haGroup, true
+			}
+		}
+	}
+	return "", false
+}
+
+// activateNewPeerInActiveGroup activates a newly added peer that should be active due to HA group
+func (m *Manager) activateNewPeerInActiveGroup(ctx context.Context, peerCfg lazyconn.PeerConfig) {
+	mp, ok := m.managedPeersByConnID[peerCfg.PeerConnID]
+	if !ok {
+		return
+	}
+
+	if !m.activateSinglePeer(ctx, &peerCfg, mp) {
+		return
+	}
+
+	peerCfg.Log.Infof("activated newly added peer due to active HA group peers")
+	m.peerStore.PeerConnOpen(m.engineCtx, peerCfg.PublicKey)
+}
+
 func (m *Manager) addActivePeer(ctx context.Context, peerCfg lazyconn.PeerConfig) error {
 	if _, ok := m.managedPeers[peerCfg.PublicKey]; ok {
 		peerCfg.Log.Warnf("peer already managed")
@@ -415,6 +469,48 @@ func (m *Manager) close() {
 	log.Infof("lazy connection manager closed")
 }
 
+// shouldDeferIdleForHA checks if peer should stay connected due to HA group requirements
+func (m *Manager) shouldDeferIdleForHA(peerID string) bool {
+	m.routesMu.RLock()
+	defer m.routesMu.RUnlock()
+
+	haGroups := m.peerToHAGroups[peerID]
+	if len(haGroups) == 0 {
+		return false
+	}
+
+	for _, haGroup := range haGroups {
+		groupPeers := m.haGroupToPeers[haGroup]
+
+		for _, groupPeerID := range groupPeers {
+			if groupPeerID == peerID {
+				continue
+			}
+
+			cfg, ok := m.managedPeers[groupPeerID]
+			if !ok {
+				continue
+			}
+
+			groupMp, ok := m.managedPeersByConnID[cfg.PeerConnID]
+			if !ok {
+				continue
+			}
+
+			if groupMp.expectedWatcher != watcherInactivity {
+				continue
+			}
+
+			// Other member is still connected, defer idle
+			if peer, ok := m.peerStore.PeerConn(groupPeerID); ok && peer.IsConnected() {
+				return true
+			}
+		}
+	}
+
+	return false
+}
+
 func (m *Manager) onPeerActivity(ctx context.Context, peerConnID peerid.ConnID) {
 	m.managedPeersMu.Lock()
 	defer m.managedPeersMu.Unlock()
@@ -441,7 +537,7 @@ func (m *Manager) onPeerActivity(ctx context.Context, peerConnID peerid.ConnID)
 	m.peerStore.PeerConnOpen(m.engineCtx, mp.peerCfg.PublicKey)
 }
 
-func (m *Manager) onPeerInactivityTimedOut(peerConnID peerid.ConnID) {
+func (m *Manager) onPeerInactivityTimedOut(ctx context.Context, peerConnID peerid.ConnID) {
 	m.managedPeersMu.Lock()
 	defer m.managedPeersMu.Unlock()
 
@@ -456,6 +552,17 @@ func (m *Manager) onPeerInactivityTimedOut(peerConnID peerid.ConnID) {
 		return
 	}
 
+	if m.shouldDeferIdleForHA(mp.peerCfg.PublicKey) {
+		iw, ok := m.inactivityMonitors[peerConnID]
+		if ok {
+			mp.peerCfg.Log.Debugf("resetting inactivity timer due to HA group requirements")
+			iw.ResetMonitor(ctx, m.onInactive)
+		} else {
+			mp.peerCfg.Log.Errorf("inactivity monitor not found for HA defer reset")
+		}
+		return
+	}
+
 	mp.peerCfg.Log.Infof("connection timed out")
 
 	// this is blocking operation, potentially can be optimized
@@ -489,7 +596,7 @@ func (m *Manager) onPeerConnected(peerConnID peerid.ConnID) {
 
 	iw, ok := m.inactivityMonitors[mp.peerCfg.PeerConnID]
 	if !ok {
-		mp.peerCfg.Log.Errorf("inactivity monitor not found for peer")
+		mp.peerCfg.Log.Warnf("inactivity monitor not found for peer")
 		return
 	}
 
diff --git a/client/internal/peer/conn.go b/client/internal/peer/conn.go
index b3302387..c3f44cc7 100644
--- a/client/internal/peer/conn.go
+++ b/client/internal/peer/conn.go
@@ -317,12 +317,12 @@ func (conn *Conn) WgConfig() WgConfig {
 	return conn.config.WgConfig
 }
 
-// IsConnected unit tests only
-// refactor unit test to use status recorder use refactor status recorded to manage connection status in peer.Conn
+// IsConnected returns true if the peer is connected
 func (conn *Conn) IsConnected() bool {
 	conn.mu.Lock()
 	defer conn.mu.Unlock()
-	return conn.currentConnPriority != conntype.None
+
+	return conn.evalStatus() == StatusConnected
 }
 
 func (conn *Conn) GetKey() string {
diff --git a/client/internal/peer/status.go b/client/internal/peer/status.go
index 629afec9..e290ef75 100644
--- a/client/internal/peer/status.go
+++ b/client/internal/peer/status.go
@@ -575,13 +575,12 @@ func (d *Status) UpdatePeerFQDN(peerPubKey, fqdn string) error {
 // FinishPeerListModifications this event invoke the notification
 func (d *Status) FinishPeerListModifications() {
 	d.mux.Lock()
+	defer d.mux.Unlock()
 
 	if !d.peerListChangedForNotification {
-		d.mux.Unlock()
 		return
 	}
 	d.peerListChangedForNotification = false
-	d.mux.Unlock()
 
 	d.notifyPeerListChanged()
 
diff --git a/client/internal/routemanager/dnsinterceptor/handler.go b/client/internal/routemanager/dnsinterceptor/handler.go
index 23478c88..66557e88 100644
--- a/client/internal/routemanager/dnsinterceptor/handler.go
+++ b/client/internal/routemanager/dnsinterceptor/handler.go
@@ -144,15 +144,18 @@ func (d *DnsInterceptor) RemoveAllowedIPs() error {
 
 // ServeDNS implements the dns.Handler interface
 func (d *DnsInterceptor) ServeDNS(w dns.ResponseWriter, r *dns.Msg) {
+	requestID := nbdns.GenerateRequestID()
+	logger := log.WithField("request_id", requestID)
+
 	if len(r.Question) == 0 {
 		return
 	}
-	log.Tracef("received DNS request for domain=%s type=%v class=%v",
+	logger.Tracef("received DNS request for domain=%s type=%v class=%v",
 		r.Question[0].Name, r.Question[0].Qtype, r.Question[0].Qclass)
 
 	// pass if non A/AAAA query
 	if r.Question[0].Qtype != dns.TypeA && r.Question[0].Qtype != dns.TypeAAAA {
-		d.continueToNextHandler(w, r, "non A/AAAA query")
+		d.continueToNextHandler(w, r, logger, "non A/AAAA query")
 		return
 	}
 
@@ -161,19 +164,19 @@ func (d *DnsInterceptor) ServeDNS(w dns.ResponseWriter, r *dns.Msg) {
 	d.mu.RUnlock()
 
 	if peerKey == "" {
-		d.writeDNSError(w, r, "no current peer key")
+		d.writeDNSError(w, r, logger, "no current peer key")
 		return
 	}
 
 	upstreamIP, err := d.getUpstreamIP(peerKey)
 	if err != nil {
-		d.writeDNSError(w, r, fmt.Sprintf("get upstream IP: %v", err))
+		d.writeDNSError(w, r, logger, fmt.Sprintf("get upstream IP: %v", err))
 		return
 	}
 
 	client, err := nbdns.GetClientPrivate(d.wgInterface.Address().IP, d.wgInterface.Name(), nbdns.UpstreamTimeout)
 	if err != nil {
-		d.writeDNSError(w, r, fmt.Sprintf("create DNS client: %v", err))
+		d.writeDNSError(w, r, logger, fmt.Sprintf("create DNS client: %v", err))
 		return
 	}
 
@@ -184,9 +187,9 @@ func (d *DnsInterceptor) ServeDNS(w dns.ResponseWriter, r *dns.Msg) {
 	upstream := fmt.Sprintf("%s:%d", upstreamIP.String(), dnsfwd.ListenPort)
 	reply, _, err := nbdns.ExchangeWithFallback(context.TODO(), client, r, upstream)
 	if err != nil {
-		log.Errorf("failed to exchange DNS request with %s (%s) for domain=%s: %v", upstreamIP.String(), peerKey, r.Question[0].Name, err)
+		logger.Errorf("failed to exchange DNS request with %s (%s) for domain=%s: %v", upstreamIP.String(), peerKey, r.Question[0].Name, err)
 		if err := w.WriteMsg(&dns.Msg{MsgHdr: dns.MsgHdr{Rcode: dns.RcodeServerFailure, Id: r.Id}}); err != nil {
-			log.Errorf("failed writing DNS response: %v", err)
+			logger.Errorf("failed writing DNS response: %v", err)
 		}
 		return
 	}
@@ -196,34 +199,34 @@ func (d *DnsInterceptor) ServeDNS(w dns.ResponseWriter, r *dns.Msg) {
 		answer = reply.Answer
 	}
 
-	log.Tracef("upstream %s (%s) DNS response for domain=%s answers=%v", upstreamIP.String(), peerKey, r.Question[0].Name, answer)
+	logger.Tracef("upstream %s (%s) DNS response for domain=%s answers=%v", upstreamIP.String(), peerKey, r.Question[0].Name, answer)
 
 	reply.Id = r.Id
 	if err := d.writeMsg(w, reply); err != nil {
-		log.Errorf("failed writing DNS response: %v", err)
+		logger.Errorf("failed writing DNS response: %v", err)
 	}
 }
 
-func (d *DnsInterceptor) writeDNSError(w dns.ResponseWriter, r *dns.Msg, reason string) {
-	log.Warnf("failed to query upstream for domain=%s: %s", r.Question[0].Name, reason)
+func (d *DnsInterceptor) writeDNSError(w dns.ResponseWriter, r *dns.Msg, logger *log.Entry, reason string) {
+	logger.Warnf("failed to query upstream for domain=%s: %s", r.Question[0].Name, reason)
 
 	resp := new(dns.Msg)
 	resp.SetRcode(r, dns.RcodeServerFailure)
 	if err := w.WriteMsg(resp); err != nil {
-		log.Errorf("failed to write DNS error response: %v", err)
+		logger.Errorf("failed to write DNS error response: %v", err)
 	}
 }
 
 // continueToNextHandler signals the handler chain to try the next handler
-func (d *DnsInterceptor) continueToNextHandler(w dns.ResponseWriter, r *dns.Msg, reason string) {
-	log.Tracef("continuing to next handler for domain=%s reason=%s", r.Question[0].Name, reason)
+func (d *DnsInterceptor) continueToNextHandler(w dns.ResponseWriter, r *dns.Msg, logger *log.Entry, reason string) {
+	logger.Tracef("continuing to next handler for domain=%s reason=%s", r.Question[0].Name, reason)
 
 	resp := new(dns.Msg)
 	resp.SetRcode(r, dns.RcodeNameError)
 	// Set Zero bit to signal handler chain to continue
 	resp.MsgHdr.Zero = true
 	if err := w.WriteMsg(resp); err != nil {
-		log.Errorf("failed writing DNS continue response: %v", err)
+		logger.Errorf("failed writing DNS continue response: %v", err)
 	}
 }
 
diff --git a/client/internal/routemanager/mock.go b/client/internal/routemanager/mock.go
index 63bad689..742294cd 100644
--- a/client/internal/routemanager/mock.go
+++ b/client/internal/routemanager/mock.go
@@ -15,7 +15,7 @@ import (
 // MockManager is the mock instance of a route manager
 type MockManager struct {
 	ClassifyRoutesFunc           func(routes []*route.Route) (map[route.ID]*route.Route, route.HAMap)
-	UpdateRoutesFunc             func (updateSerial uint64, serverRoutes map[route.ID]*route.Route, clientRoutes route.HAMap, useNewDNSRoute bool) error
+	UpdateRoutesFunc             func(updateSerial uint64, serverRoutes map[route.ID]*route.Route, clientRoutes route.HAMap, useNewDNSRoute bool) error
 	TriggerSelectionFunc         func(haMap route.HAMap)
 	GetRouteSelectorFunc         func() *routeselector.RouteSelector
 	GetClientRoutesFunc          func() route.HAMap
diff --git a/client/internal/routemanager/notifier/notifier.go b/client/internal/routemanager/notifier/notifier.go
index 25a3a71e..3cc7c330 100644
--- a/client/internal/routemanager/notifier/notifier.go
+++ b/client/internal/routemanager/notifier/notifier.go
@@ -32,7 +32,6 @@ func (n *Notifier) SetListener(listener listener.NetworkChangeListener) {
 func (n *Notifier) SetInitialClientRoutes(clientRoutes []*route.Route) {
 	nets := make([]string, 0)
 	for _, r := range clientRoutes {
-		// filter out domain routes
 		if r.IsDynamic() {
 			continue
 		}
@@ -46,30 +45,27 @@ func (n *Notifier) OnNewRoutes(idMap route.HAMap) {
 	if runtime.GOOS != "android" {
 		return
 	}
-	newNets := make([]string, 0)
+
+	var newNets []string
 	for _, routes := range idMap {
 		for _, r := range routes {
+			if r.IsDynamic() {
+				continue
+			}
 			newNets = append(newNets, r.Network.String())
 		}
 	}
 
 	sort.Strings(newNets)
-	switch runtime.GOOS {
-	case "android":
-		if !n.hasDiff(n.initialRouteRanges, newNets) {
-			return
-		}
-	default:
-		if !n.hasDiff(n.routeRanges, newNets) {
-			return
-		}
+	if !n.hasDiff(n.initialRouteRanges, newNets) {
+		return
 	}
 
 	n.routeRanges = newNets
-
 	n.notify()
 }
 
+// OnNewPrefixes is called from iOS only
 func (n *Notifier) OnNewPrefixes(prefixes []netip.Prefix) {
 	newNets := make([]string, 0)
 	for _, prefix := range prefixes {
@@ -77,19 +73,11 @@ func (n *Notifier) OnNewPrefixes(prefixes []netip.Prefix) {
 	}
 
 	sort.Strings(newNets)
-	switch runtime.GOOS {
-	case "android":
-		if !n.hasDiff(n.initialRouteRanges, newNets) {
-			return
-		}
-	default:
-		if !n.hasDiff(n.routeRanges, newNets) {
-			return
-		}
+	if !n.hasDiff(n.routeRanges, newNets) {
+		return
 	}
 
 	n.routeRanges = newNets
-
 	n.notify()
 }
 
diff --git a/client/internal/routemanager/systemops/systemops.go b/client/internal/routemanager/systemops/systemops.go
index 261567dc..8caf22f8 100644
--- a/client/internal/routemanager/systemops/systemops.go
+++ b/client/internal/routemanager/systemops/systemops.go
@@ -28,7 +28,10 @@ func (n Nexthop) String() string {
 	if n.Intf == nil {
 		return n.IP.String()
 	}
-	return fmt.Sprintf("%s @ %d (%s)", n.IP.String(), n.Intf.Index, n.Intf.Name)
+	if n.IP.IsValid() {
+		return fmt.Sprintf("%s @ %d (%s)", n.IP.String(), n.Intf.Index, n.Intf.Name)
+	}
+	return fmt.Sprintf("no-ip @ %d (%s)", n.Intf.Index, n.Intf.Name)
 }
 
 type wgIface interface {
diff --git a/client/netbird.wxs b/client/netbird.wxs
index 5e03a014..91595539 100644
--- a/client/netbird.wxs
+++ b/client/netbird.wxs
@@ -1,8 +1,10 @@
 <Wix
-	xmlns="http://wixtoolset.org/schemas/v4/wxs">
+	xmlns="http://wixtoolset.org/schemas/v4/wxs"
+	xmlns:util="http://wixtoolset.org/schemas/v4/wxs/util">
 	<Package Name="NetBird" Version="$(env.NETBIRD_VERSION)" Manufacturer="NetBird GmbH" Language="1033" UpgradeCode="6456ec4e-3ad6-4b9b-a2be-98e81cb21ccf"
         InstallerVersion="500" Compressed="yes"  Codepage="utf-8" >
 
+
 		<MediaTemplate EmbedCab="yes" />
 
 		<Feature Id="NetbirdFeature" Title="Netbird" Level="1">
@@ -46,29 +48,10 @@
 			<ComponentRef Id="NetbirdFiles" />
 		</ComponentGroup>
 
-		<Property Id="cmd" Value="cmd.exe"/>
-
-		<CustomAction Id="KillDaemon"
-                      ExeCommand='/c "taskkill /im netbird.exe"'
-                      Execute="deferred"
-                      Property="cmd"
-                      Impersonate="no"
-                      Return="ignore"
-            />
+		<util:CloseApplication Id="CloseNetBird" CloseMessage="no" Target="netbird.exe" RebootPrompt="no" />
+		<util:CloseApplication Id="CloseNetBirdUI" CloseMessage="no" Target="netbird-ui.exe" RebootPrompt="no" />
 
-		<CustomAction Id="KillUI"
-                      ExeCommand='/c "taskkill /im netbird-ui.exe"'
-                      Execute="deferred"
-                      Property="cmd"
-                      Impersonate="no"
-                      Return="ignore"
-            />
 
-		<InstallExecuteSequence>
-			<!-- For Uninstallation -->
-			<Custom Action="KillDaemon" Before="RemoveFiles" Condition="Installed"/>
-			<Custom Action="KillUI" After="KillDaemon" Condition="Installed"/>
-		</InstallExecuteSequence>
 
 		<!-- Icons -->
 		<Icon Id="NetbirdIcon" SourceFile=".\client\ui\assets\netbird.ico" />
diff --git a/client/system/info.go b/client/system/info.go
index a0a5fe8b..aff10ece 100644
--- a/client/system/info.go
+++ b/client/system/info.go
@@ -59,16 +59,16 @@ type Info struct {
 	Environment        Environment
 	Files              []File // for posture checks
 
-	RosenpassEnabled      bool
-	RosenpassPermissive   bool
-	ServerSSHAllowed      bool
-
-	DisableClientRoutes   bool
-	DisableServerRoutes   bool
-	DisableDNS            bool
-	DisableFirewall       bool
-	BlockLANAccess        bool
-	BlockInbound          bool
+	RosenpassEnabled    bool
+	RosenpassPermissive bool
+	ServerSSHAllowed    bool
+
+	DisableClientRoutes bool
+	DisableServerRoutes bool
+	DisableDNS          bool
+	DisableFirewall     bool
+	BlockLANAccess      bool
+	BlockInbound        bool
 
 	LazyConnectionEnabled bool
 }
diff --git a/client/ui/client_ui.go b/client/ui/client_ui.go
index a1ca4120..ace5b71e 100644
--- a/client/ui/client_ui.go
+++ b/client/ui/client_ui.go
@@ -280,7 +280,7 @@ func newServiceClient(addr string, logFile string, a fyne.App, showSettings bool
 
 		showAdvancedSettings: showSettings,
 		showNetworks:         showNetworks,
-		update:               version.NewUpdate(),
+		update:               version.NewUpdate("nb/client-ui"),
 	}
 
 	s.eventHandler = newEventHandler(s)
@@ -572,7 +572,7 @@ func (s *serviceClient) updateStatus() error {
 		var systrayIconState bool
 
 		switch {
-		case status.Status == string(internal.StatusConnected) && !s.mUp.Disabled():
+		case status.Status == string(internal.StatusConnected):
 			s.connected = true
 			s.sendNotification = true
 			if s.isUpdateIconActive {
@@ -879,7 +879,7 @@ func (s *serviceClient) onUpdateAvailable() {
 func (s *serviceClient) onSessionExpire() {
 	s.sendNotification = true
 	if s.sendNotification {
-		s.eventHandler.runSelfCommand("login-url", "true")
+		go s.eventHandler.runSelfCommand(s.ctx, "login-url", "true")
 		s.sendNotification = false
 	}
 }
@@ -992,21 +992,6 @@ func (s *serviceClient) restartClient(loginRequest *proto.LoginRequest) error {
 // showLoginURL creates a borderless window styled like a pop-up in the top-right corner using s.wLoginURL.
 func (s *serviceClient) showLoginURL() {
 
-	resp, err := s.login(false)
-	if err != nil {
-		log.Errorf("failed to fetch login URL: %v", err)
-		return
-	}
-	verificationURL := resp.VerificationURIComplete
-	if verificationURL == "" {
-		verificationURL = resp.VerificationURI
-	}
-
-	if verificationURL == "" {
-		log.Error("no verification URL provided in the login response")
-		return
-	}
-
 	resIcon := fyne.NewStaticResource("netbird.png", iconAbout)
 
 	if s.wLoginURL == nil {
@@ -1025,6 +1010,21 @@ func (s *serviceClient) showLoginURL() {
 			return
 		}
 
+		resp, err := s.login(false)
+		if err != nil {
+			log.Errorf("failed to fetch login URL: %v", err)
+			return
+		}
+		verificationURL := resp.VerificationURIComplete
+		if verificationURL == "" {
+			verificationURL = resp.VerificationURI
+		}
+
+		if verificationURL == "" {
+			log.Error("no verification URL provided in the login response")
+			return
+		}
+
 		if err := openURL(verificationURL); err != nil {
 			log.Errorf("failed to open login URL: %v", err)
 			return
@@ -1038,7 +1038,19 @@ func (s *serviceClient) showLoginURL() {
 		}
 
 		label.SetText("Re-authentication successful.\nReconnecting")
-		time.Sleep(300 * time.Millisecond)
+		status, err := conn.Status(s.ctx, &proto.StatusRequest{})
+		if err != nil {
+			log.Errorf("get service status: %v", err)
+			return
+		}
+
+		if status.Status == string(internal.StatusConnected) {
+			label.SetText("Already connected.\nClosing this window.")
+			time.Sleep(2 * time.Second)
+			s.wLoginURL.Close()
+			return
+		}
+
 		_, err = conn.Up(s.ctx, &proto.UpRequest{})
 		if err != nil {
 			label.SetText("Reconnecting failed, please create \na debug bundle in the settings and contact support.")
diff --git a/client/ui/event_handler.go b/client/ui/event_handler.go
index f7072c6b..39ea3867 100644
--- a/client/ui/event_handler.go
+++ b/client/ui/event_handler.go
@@ -12,6 +12,8 @@ import (
 	"fyne.io/fyne/v2"
 	"fyne.io/systray"
 	log "github.com/sirupsen/logrus"
+
+	"github.com/netbirdio/netbird/version"
 )
 
 type eventHandler struct {
@@ -120,7 +122,7 @@ func (h *eventHandler) handleAdvancedSettingsClick() {
 	go func() {
 		defer h.client.mAdvancedSettings.Enable()
 		defer h.client.getSrvConfig()
-		h.runSelfCommand("settings", "true")
+		h.runSelfCommand(h.client.ctx, "settings", "true")
 	}()
 }
 
@@ -128,7 +130,7 @@ func (h *eventHandler) handleCreateDebugBundleClick() {
 	h.client.mCreateDebugBundle.Disable()
 	go func() {
 		defer h.client.mCreateDebugBundle.Enable()
-		h.runSelfCommand("debug", "true")
+		h.runSelfCommand(h.client.ctx, "debug", "true")
 	}()
 }
 
@@ -143,7 +145,7 @@ func (h *eventHandler) handleGitHubClick() {
 }
 
 func (h *eventHandler) handleUpdateClick() {
-	if err := openURL("https://netbird.io/download"); err != nil {
+	if err := openURL(version.DownloadUrl()); err != nil {
 		log.Errorf("failed to open download URL: %v", err)
 	}
 }
@@ -152,7 +154,7 @@ func (h *eventHandler) handleNetworksClick() {
 	h.client.mNetworks.Disable()
 	go func() {
 		defer h.client.mNetworks.Enable()
-		h.runSelfCommand("networks", "true")
+		h.runSelfCommand(h.client.ctx, "networks", "true")
 	}()
 }
 
@@ -170,14 +172,14 @@ func (h *eventHandler) updateConfigWithErr() {
 	}
 }
 
-func (h *eventHandler) runSelfCommand(command, arg string) {
+func (h *eventHandler) runSelfCommand(ctx context.Context, command, arg string) {
 	proc, err := os.Executable()
 	if err != nil {
 		log.Errorf("error getting executable path: %v", err)
 		return
 	}
 
-	cmd := exec.Command(proc,
+	cmd := exec.CommandContext(ctx, proc,
 		fmt.Sprintf("--%s=%s", command, arg),
 		fmt.Sprintf("--daemon-addr=%s", h.client.addr),
 	)
diff --git a/infrastructure_files/base.setup.env b/infrastructure_files/base.setup.env
index fdba1f21..e5993919 100644
--- a/infrastructure_files/base.setup.env
+++ b/infrastructure_files/base.setup.env
@@ -15,6 +15,7 @@ NETBIRD_MGMT_API_CERT_KEY_FILE="/etc/letsencrypt/live/$NETBIRD_LETSENCRYPT_DOMAI
 NETBIRD_MGMT_SINGLE_ACCOUNT_MODE_DOMAIN=$NETBIRD_DOMAIN
 NETBIRD_MGMT_DNS_DOMAIN=${NETBIRD_MGMT_DNS_DOMAIN:-netbird.selfhosted}
 NETBIRD_MGMT_IDP_SIGNKEY_REFRESH=${NETBIRD_MGMT_IDP_SIGNKEY_REFRESH:-false}
+NETBIRD_MGMT_DISABLE_DEFAULT_POLICY=${NETBIRD_MGMT_DISABLE_DEFAULT_POLICY:-false}
 
 # Signal
 NETBIRD_SIGNAL_PROTOCOL="http"
@@ -60,7 +61,7 @@ NETBIRD_TOKEN_SOURCE=${NETBIRD_TOKEN_SOURCE:-accessToken}
 NETBIRD_AUTH_PKCE_REDIRECT_URL_PORTS=${NETBIRD_AUTH_PKCE_REDIRECT_URL_PORTS:-"53000"}
 NETBIRD_AUTH_PKCE_USE_ID_TOKEN=${NETBIRD_AUTH_PKCE_USE_ID_TOKEN:-false}
 NETBIRD_AUTH_PKCE_DISABLE_PROMPT_LOGIN=${NETBIRD_AUTH_PKCE_DISABLE_PROMPT_LOGIN:-false}
-NETBIRD_AUTH_PKCE_LOGIN_FLAG=${NETBIRD_AUTH_PKCE_LOGIN_FLAG:-1}
+NETBIRD_AUTH_PKCE_LOGIN_FLAG=${NETBIRD_AUTH_PKCE_LOGIN_FLAG:-0}
 NETBIRD_AUTH_PKCE_AUDIENCE=$NETBIRD_AUTH_AUDIENCE
 
 # Dashboard
@@ -139,3 +140,4 @@ export NETBIRD_RELAY_PORT
 export NETBIRD_RELAY_ENDPOINT
 export NETBIRD_RELAY_AUTH_SECRET
 export NETBIRD_RELAY_TAG
+export NETBIRD_MGMT_DISABLE_DEFAULT_POLICY
diff --git a/infrastructure_files/getting-started-with-zitadel.sh b/infrastructure_files/getting-started-with-zitadel.sh
index 1e67bd17..2118ef48 100644
--- a/infrastructure_files/getting-started-with-zitadel.sh
+++ b/infrastructure_files/getting-started-with-zitadel.sh
@@ -791,7 +791,6 @@ services:
       - '443:443'
       - '443:443/udp'
       - '80:80'
-      - '8080:8080'
     volumes:
       - netbird_caddy_data:/data
       - ./Caddyfile:/etc/caddy/Caddyfile
diff --git a/infrastructure_files/management.json.tmpl b/infrastructure_files/management.json.tmpl
index 4d09816e..32f08391 100644
--- a/infrastructure_files/management.json.tmpl
+++ b/infrastructure_files/management.json.tmpl
@@ -38,6 +38,7 @@
              "0.0.0.0/0"
         ]
     },
+    "DisableDefaultPolicy": $NETBIRD_MGMT_DISABLE_DEFAULT_POLICY,
     "Datadir": "",
     "DataStoreEncryptionKey": "$NETBIRD_DATASTORE_ENC_KEY",
     "StoreConfig": {
diff --git a/infrastructure_files/setup.env.example b/infrastructure_files/setup.env.example
index b6a20995..382e1e18 100644
--- a/infrastructure_files/setup.env.example
+++ b/infrastructure_files/setup.env.example
@@ -92,7 +92,8 @@ NETBIRD_LETSENCRYPT_EMAIL=""
 NETBIRD_DISABLE_ANONYMOUS_METRICS=false
 # DNS DOMAIN configures the domain name used for peer resolution. By default it is netbird.selfhosted
 NETBIRD_MGMT_DNS_DOMAIN=netbird.selfhosted
-
+# Disable default all-to-all policy for new accounts
+NETBIRD_MGMT_DISABLE_DEFAULT_POLICY=false
 # -------------------------------------------
 # Relay settings
 # -------------------------------------------
diff --git a/management/cmd/management.go b/management/cmd/management.go
index bce09efd..878e4c39 100644
--- a/management/cmd/management.go
+++ b/management/cmd/management.go
@@ -215,7 +215,7 @@ var (
 			peersManager := peers.NewManager(store, permissionsManager)
 			proxyController := integrations.NewController(store)
 			accountManager, err := server.BuildManager(ctx, store, peersUpdateManager, idpManager, mgmtSingleAccModeDomain,
-				dnsDomain, eventStore, geo, userDeleteFromIDPEnabled, integratedPeerValidator, appMetrics, proxyController, settingsManager, permissionsManager)
+				dnsDomain, eventStore, geo, userDeleteFromIDPEnabled, integratedPeerValidator, appMetrics, proxyController, settingsManager, permissionsManager, config.DisableDefaultPolicy)
 			if err != nil {
 				return fmt.Errorf("failed to build default manager: %v", err)
 			}
@@ -357,6 +357,13 @@ var (
 			log.WithContext(ctx).Infof("running HTTP server and gRPC server on the same port: %s", listener.Addr().String())
 			serveGRPCWithHTTP(ctx, listener, rootHandler, tlsEnabled)
 
+			update := version.NewUpdate("nb/management")
+			update.SetDaemonVersion(version.NetbirdVersion())
+			update.SetOnUpdateListener(func() {
+				log.WithContext(ctx).Infof("your management version, \"%s\", is outdated, a new management version is available. Learn more here: https://github.com/netbirdio/netbird/releases", version.NetbirdVersion())
+			})
+			defer update.StopWatch()
+
 			SetupCloseHandler()
 
 			<-stopCh
diff --git a/management/server/account.go b/management/server/account.go
index 82f5ee4a..3b735950 100644
--- a/management/server/account.go
+++ b/management/server/account.go
@@ -24,6 +24,7 @@ import (
 	"golang.org/x/exp/maps"
 
 	nbdns "github.com/netbirdio/netbird/dns"
+	"github.com/netbirdio/netbird/formatter/hook"
 	"github.com/netbirdio/netbird/management/server/account"
 	"github.com/netbirdio/netbird/management/server/activity"
 	nbcache "github.com/netbirdio/netbird/management/server/cache"
@@ -101,6 +102,8 @@ type DefaultAccountManager struct {
 
 	accountUpdateLocks               sync.Map
 	updateAccountPeersBufferInterval atomic.Int64
+
+	disableDefaultPolicy bool
 }
 
 // getJWTGroupsChanges calculates the changes needed to sync a user's JWT groups.
@@ -169,6 +172,7 @@ func BuildManager(
 	proxyController port_forwarding.Controller,
 	settingsManager settings.Manager,
 	permissionsManager permissions.Manager,
+	disableDefaultPolicy bool,
 ) (*DefaultAccountManager, error) {
 	start := time.Now()
 	defer func() {
@@ -194,6 +198,7 @@ func BuildManager(
 		proxyController:          proxyController,
 		settingsManager:          settingsManager,
 		permissionsManager:       permissionsManager,
+		disableDefaultPolicy:     disableDefaultPolicy,
 	}
 
 	am.startWarmup(ctx)
@@ -409,14 +414,15 @@ func (am *DefaultAccountManager) handlePeerLoginExpirationSettings(ctx context.C
 			event = activity.AccountPeerLoginExpirationDisabled
 			am.peerLoginExpiry.Cancel(ctx, []string{accountID})
 		} else {
-			am.checkAndSchedulePeerLoginExpiration(ctx, accountID)
+			am.schedulePeerLoginExpiration(ctx, accountID)
 		}
 		am.StoreEvent(ctx, userID, accountID, accountID, event, nil)
 	}
 
 	if oldSettings.PeerLoginExpiration != newSettings.PeerLoginExpiration {
 		am.StoreEvent(ctx, userID, accountID, accountID, activity.AccountPeerLoginExpirationDurationUpdated, nil)
-		am.checkAndSchedulePeerLoginExpiration(ctx, accountID)
+		am.peerLoginExpiry.Cancel(ctx, []string{accountID})
+		am.schedulePeerLoginExpiration(ctx, accountID)
 	}
 }
 
@@ -454,6 +460,10 @@ func (am *DefaultAccountManager) handleInactivityExpirationSettings(ctx context.
 
 func (am *DefaultAccountManager) peerLoginExpirationJob(ctx context.Context, accountID string) func() (time.Duration, bool) {
 	return func() (time.Duration, bool) {
+		//nolint
+		ctx := context.WithValue(ctx, nbcontext.AccountIDKey, accountID)
+		//nolint
+		ctx = context.WithValue(ctx, hook.ExecutionContextKey, fmt.Sprintf("%s-PEER-EXPIRATION", hook.SystemSource))
 		unlock := am.Store.AcquireWriteLockByUID(ctx, accountID)
 		defer unlock()
 
@@ -478,8 +488,11 @@ func (am *DefaultAccountManager) peerLoginExpirationJob(ctx context.Context, acc
 	}
 }
 
-func (am *DefaultAccountManager) checkAndSchedulePeerLoginExpiration(ctx context.Context, accountID string) {
-	am.peerLoginExpiry.Cancel(ctx, []string{accountID})
+func (am *DefaultAccountManager) schedulePeerLoginExpiration(ctx context.Context, accountID string) {
+	if am.peerLoginExpiry.IsSchedulerRunning(accountID) {
+		log.WithContext(ctx).Tracef("peer login expiration job for account %s is already scheduled", accountID)
+		return
+	}
 	if nextRun, ok := am.getNextPeerExpiration(ctx, accountID); ok {
 		go am.peerLoginExpiry.Schedule(ctx, nextRun, accountID, am.peerLoginExpirationJob(ctx, accountID))
 	}
@@ -534,7 +547,7 @@ func (am *DefaultAccountManager) newAccount(ctx context.Context, userID, domain
 			log.WithContext(ctx).Warnf("an account with ID already exists, retrying...")
 			continue
 		case statusErr.Type() == status.NotFound:
-			newAccount := newAccountWithId(ctx, accountId, userID, domain)
+			newAccount := newAccountWithId(ctx, accountId, userID, domain, am.disableDefaultPolicy)
 			am.StoreEvent(ctx, userID, newAccount.Id, accountId, activity.AccountCreated, nil)
 			return newAccount, nil
 		default:
@@ -1679,7 +1692,7 @@ func (am *DefaultAccountManager) GetAccountSettings(ctx context.Context, account
 }
 
 // newAccountWithId creates a new Account with a default SetupKey (doesn't store in a Store) and provided id
-func newAccountWithId(ctx context.Context, accountID, userID, domain string) *types.Account {
+func newAccountWithId(ctx context.Context, accountID, userID, domain string, disableDefaultPolicy bool) *types.Account {
 	log.WithContext(ctx).Debugf("creating new account")
 
 	network := types.NewNetwork()
@@ -1722,7 +1735,7 @@ func newAccountWithId(ctx context.Context, accountID, userID, domain string) *ty
 		},
 	}
 
-	if err := acc.AddAllGroup(); err != nil {
+	if err := acc.AddAllGroup(disableDefaultPolicy); err != nil {
 		log.WithContext(ctx).Errorf("error adding all group to account %s: %v", acc.Id, err)
 	}
 	return acc
@@ -1824,7 +1837,7 @@ func (am *DefaultAccountManager) GetOrCreateAccountByPrivateDomain(ctx context.C
 			},
 		}
 
-		if err := newAccount.AddAllGroup(); err != nil {
+		if err := newAccount.AddAllGroup(am.disableDefaultPolicy); err != nil {
 			return nil, false, status.Errorf(status.Internal, "failed to add all group to new account by private domain")
 		}
 
@@ -1844,38 +1857,47 @@ func (am *DefaultAccountManager) GetOrCreateAccountByPrivateDomain(ctx context.C
 }
 
 func (am *DefaultAccountManager) UpdateToPrimaryAccount(ctx context.Context, accountId string) (*types.Account, error) {
-	account, err := am.Store.GetAccount(ctx, accountId)
-	if err != nil {
-		return nil, err
-	}
+	var account *types.Account
+	err := am.Store.ExecuteInTransaction(ctx, func(transaction store.Store) error {
+		var err error
+		account, err = transaction.GetAccount(ctx, accountId)
+		if err != nil {
+			return err
+		}
 
-	if account.IsDomainPrimaryAccount {
-		return account, nil
-	}
+		if account.IsDomainPrimaryAccount {
+			return nil
+		}
 
-	existingPrimaryAccountID, err := am.Store.GetAccountIDByPrivateDomain(ctx, store.LockingStrengthShare, account.Domain)
+		existingPrimaryAccountID, err := transaction.GetAccountIDByPrivateDomain(ctx, store.LockingStrengthShare, account.Domain)
 
-	// error is not a not found error
-	if handleNotFound(err) != nil {
-		return nil, err
-	}
+		// error is not a not found error
+		if handleNotFound(err) != nil {
+			return err
+		}
 
-	// a primary account already exists for this private domain
-	if err == nil {
-		log.WithContext(ctx).WithFields(log.Fields{
-			"accountId":         accountId,
-			"existingAccountId": existingPrimaryAccountID,
-		}).Errorf("cannot update account to primary, another account already exists as primary for the same domain")
-		return nil, status.Errorf(status.Internal, "cannot update account to primary")
-	}
+		// a primary account already exists for this private domain
+		if err == nil {
+			log.WithContext(ctx).WithFields(log.Fields{
+				"accountId":         accountId,
+				"existingAccountId": existingPrimaryAccountID,
+			}).Errorf("cannot update account to primary, another account already exists as primary for the same domain")
+			return status.Errorf(status.Internal, "cannot update account to primary")
+		}
 
-	account.IsDomainPrimaryAccount = true
+		account.IsDomainPrimaryAccount = true
 
-	if err := am.Store.SaveAccount(ctx, account); err != nil {
-		log.WithContext(ctx).WithFields(log.Fields{
-			"accountId": accountId,
-		}).Errorf("failed to update account to primary: %v", err)
-		return nil, status.Errorf(status.Internal, "failed to update account to primary")
+		if err := transaction.SaveAccount(ctx, account); err != nil {
+			log.WithContext(ctx).WithFields(log.Fields{
+				"accountId": accountId,
+			}).Errorf("failed to update account to primary: %v", err)
+			return status.Errorf(status.Internal, "failed to update account to primary")
+		}
+
+		return nil
+	})
+	if err != nil {
+		return nil, err
 	}
 
 	return account, nil
diff --git a/management/server/group.go b/management/server/group.go
index c26a0cfc..130a6714 100644
--- a/management/server/group.go
+++ b/management/server/group.go
@@ -664,15 +664,6 @@ func areGroupChangesAffectPeers(ctx context.Context, transaction store.Store, ac
 	return false, nil
 }
 
-func (am *DefaultAccountManager) anyGroupHasPeers(account *types.Account, groupIDs []string) bool {
-	for _, groupID := range groupIDs {
-		if group, exists := account.Groups[groupID]; exists && group.HasPeers() {
-			return true
-		}
-	}
-	return false
-}
-
 // anyGroupHasPeersOrResources checks if any of the given groups in the account have peers or resources.
 func anyGroupHasPeersOrResources(ctx context.Context, transaction store.Store, accountID string, groupIDs []string) (bool, error) {
 	groups, err := transaction.GetGroupsByIDs(ctx, store.LockingStrengthShare, accountID, groupIDs)
diff --git a/management/server/http/api/openapi.yml b/management/server/http/api/openapi.yml
index 58134d37..1c5ca9b0 100644
--- a/management/server/http/api/openapi.yml
+++ b/management/server/http/api/openapi.yml
@@ -426,6 +426,10 @@ components:
               items:
                 type: string
                 example: "stage-host-1"
+            ephemeral:
+              description: Indicates whether the peer is ephemeral or not
+              type: boolean
+              example: false
           required:
             - city_name
             - connected
@@ -450,6 +454,7 @@ components:
             - approval_required
             - serial_number
             - extra_dns_labels
+            - ephemeral
     AccessiblePeer:
       allOf:
         - $ref: '#/components/schemas/PeerMinimum'
diff --git a/management/server/http/api/types.gen.go b/management/server/http/api/types.gen.go
index 0a09d7ca..d27fd2a5 100644
--- a/management/server/http/api/types.gen.go
+++ b/management/server/http/api/types.gen.go
@@ -1016,6 +1016,9 @@ type Peer struct {
 	// DnsLabel Peer's DNS label is the parsed peer name for domain resolution. It is used to form an FQDN by appending the account's domain to the peer label. e.g. peer-dns-label.netbird.cloud
 	DnsLabel string `json:"dns_label"`
 
+	// Ephemeral Indicates whether the peer is ephemeral or not
+	Ephemeral bool `json:"ephemeral"`
+
 	// ExtraDnsLabels Extra DNS labels added to the peer
 	ExtraDnsLabels []string `json:"extra_dns_labels"`
 
@@ -1097,6 +1100,9 @@ type PeerBatch struct {
 	// DnsLabel Peer's DNS label is the parsed peer name for domain resolution. It is used to form an FQDN by appending the account's domain to the peer label. e.g. peer-dns-label.netbird.cloud
 	DnsLabel string `json:"dns_label"`
 
+	// Ephemeral Indicates whether the peer is ephemeral or not
+	Ephemeral bool `json:"ephemeral"`
+
 	// ExtraDnsLabels Extra DNS labels added to the peer
 	ExtraDnsLabels []string `json:"extra_dns_labels"`
 
diff --git a/management/server/http/handlers/peers/peers_handler.go b/management/server/http/handlers/peers/peers_handler.go
index 58ea06ea..8c20ed65 100644
--- a/management/server/http/handlers/peers/peers_handler.go
+++ b/management/server/http/handlers/peers/peers_handler.go
@@ -365,6 +365,7 @@ func toSinglePeerResponse(peer *nbpeer.Peer, groupsInfo []api.GroupMinimum, dnsD
 		CityName:                    peer.Location.CityName,
 		SerialNumber:                peer.Meta.SystemSerialNumber,
 		InactivityExpirationEnabled: peer.InactivityExpirationEnabled,
+		Ephemeral:                   peer.Ephemeral,
 	}
 }
 
diff --git a/management/server/integrated_validator.go b/management/server/integrated_validator.go
index ef77bf10..cfde7c61 100644
--- a/management/server/integrated_validator.go
+++ b/management/server/integrated_validator.go
@@ -37,21 +37,23 @@ func (am *DefaultAccountManager) UpdateIntegratedValidatorGroups(ctx context.Con
 	unlock := am.Store.AcquireWriteLockByUID(ctx, accountID)
 	defer unlock()
 
-	a, err := am.Store.GetAccountByUser(ctx, userID)
-	if err != nil {
-		return err
-	}
+	return am.Store.ExecuteInTransaction(ctx, func(transaction store.Store) error {
+		a, err := transaction.GetAccountByUser(ctx, userID)
+		if err != nil {
+			return err
+		}
 
-	var extra *types.ExtraSettings
+		var extra *types.ExtraSettings
 
-	if a.Settings.Extra != nil {
-		extra = a.Settings.Extra
-	} else {
-		extra = &types.ExtraSettings{}
-		a.Settings.Extra = extra
-	}
-	extra.IntegratedValidatorGroups = groups
-	return am.Store.SaveAccount(ctx, a)
+		if a.Settings.Extra != nil {
+			extra = a.Settings.Extra
+		} else {
+			extra = &types.ExtraSettings{}
+			a.Settings.Extra = extra
+		}
+		extra.IntegratedValidatorGroups = groups
+		return transaction.SaveAccount(ctx, a)
+	})
 }
 
 func (am *DefaultAccountManager) GroupValidation(ctx context.Context, accountID string, groupIDs []string) (bool, error) {
@@ -81,15 +83,12 @@ func (am *DefaultAccountManager) GetValidatedPeers(ctx context.Context, accountI
 	var peers []*nbpeer.Peer
 	var settings *types.Settings
 
-	err = am.Store.ExecuteInTransaction(ctx, func(transaction store.Store) error {
-		groups, err = transaction.GetAccountGroups(ctx, store.LockingStrengthShare, accountID)
-		if err != nil {
-			return err
-		}
+	groups, err = am.Store.GetAccountGroups(ctx, store.LockingStrengthShare, accountID)
+	if err != nil {
+		return nil, err
+	}
 
-		peers, err = transaction.GetAccountPeers(ctx, store.LockingStrengthShare, accountID, "", "")
-		return err
-	})
+	peers, err = am.Store.GetAccountPeers(ctx, store.LockingStrengthShare, accountID, "", "")
 	if err != nil {
 		return nil, err
 	}
diff --git a/management/server/metrics/selfhosted.go b/management/server/metrics/selfhosted.go
index 9a3b22e5..4ce57b1d 100644
--- a/management/server/metrics/selfhosted.go
+++ b/management/server/metrics/selfhosted.go
@@ -184,7 +184,9 @@ func (w *Worker) generateProperties(ctx context.Context) properties {
 		ephemeralPeersSKs         int
 		ephemeralPeersSKUsage     int
 		activePeersLastDay        int
+		activeUserPeersLastDay    int
 		osPeers                   map[string]int
+		activeUsersLastDay        map[string]struct{}
 		userPeers                 int
 		rules                     int
 		rulesProtocol             map[string]int
@@ -203,6 +205,7 @@ func (w *Worker) generateProperties(ctx context.Context) properties {
 		version                   string
 		peerActiveVersions        []string
 		osUIClients               map[string]int
+		rosenpassEnabled          int
 	)
 	start := time.Now()
 	metricsProperties := make(properties)
@@ -210,6 +213,7 @@ func (w *Worker) generateProperties(ctx context.Context) properties {
 	osUIClients = make(map[string]int)
 	rulesProtocol = make(map[string]int)
 	rulesDirection = make(map[string]int)
+	activeUsersLastDay = make(map[string]struct{})
 	uptime = time.Since(w.startupTime).Seconds()
 	connections := w.connManager.GetAllConnectedPeers()
 	version = nbversion.NetbirdVersion()
@@ -277,10 +281,14 @@ func (w *Worker) generateProperties(ctx context.Context) properties {
 		for _, peer := range account.Peers {
 			peers++
 
-			if peer.SSHEnabled {
+			if peer.SSHEnabled || peer.Meta.Flags.ServerSSHAllowed {
 				peersSSHEnabled++
 			}
 
+			if peer.Meta.Flags.RosenpassEnabled {
+				rosenpassEnabled++
+			}
+
 			if peer.UserID != "" {
 				userPeers++
 			}
@@ -299,6 +307,10 @@ func (w *Worker) generateProperties(ctx context.Context) properties {
 			_, connected := connections[peer.ID]
 			if connected || peer.Status.LastSeen.After(w.lastRun) {
 				activePeersLastDay++
+				if peer.UserID != "" {
+					activeUserPeersLastDay++
+					activeUsersLastDay[peer.UserID] = struct{}{}
+				}
 				osActiveKey := osKey + "_active"
 				osActiveCount := osPeers[osActiveKey]
 				osPeers[osActiveKey] = osActiveCount + 1
@@ -320,6 +332,8 @@ func (w *Worker) generateProperties(ctx context.Context) properties {
 	metricsProperties["ephemeral_peers_setup_keys"] = ephemeralPeersSKs
 	metricsProperties["ephemeral_peers_setup_keys_usage"] = ephemeralPeersSKUsage
 	metricsProperties["active_peers_last_day"] = activePeersLastDay
+	metricsProperties["active_user_peers_last_day"] = activeUserPeersLastDay
+	metricsProperties["active_users_last_day"] = len(activeUsersLastDay)
 	metricsProperties["user_peers"] = userPeers
 	metricsProperties["rules"] = rules
 	metricsProperties["rules_with_src_posture_checks"] = rulesWithSrcPostureChecks
@@ -338,6 +352,7 @@ func (w *Worker) generateProperties(ctx context.Context) properties {
 	metricsProperties["ui_clients"] = uiClient
 	metricsProperties["idp_manager"] = w.idpManager
 	metricsProperties["store_engine"] = w.dataSource.GetStoreEngine()
+	metricsProperties["rosenpass_enabled"] = rosenpassEnabled
 
 	for protocol, count := range rulesProtocol {
 		metricsProperties["rules_protocol_"+protocol] = count
diff --git a/management/server/peer.go b/management/server/peer.go
index 4a468a6c..254048a9 100644
--- a/management/server/peer.go
+++ b/management/server/peer.go
@@ -92,7 +92,7 @@ func (am *DefaultAccountManager) getUserAccessiblePeers(ctx context.Context, acc
 
 	// fetch all the peers that have access to the user's peers
 	for _, peer := range peers {
-		aclPeers, _ := account.GetPeerConnectionResources(ctx, peer.ID, approvedPeersMap)
+		aclPeers, _ := account.GetPeerConnectionResources(ctx, peer, approvedPeersMap)
 		for _, p := range aclPeers {
 			peersMap[p.ID] = p
 		}
@@ -133,7 +133,7 @@ func (am *DefaultAccountManager) MarkPeerConnected(ctx context.Context, peerPubK
 		}
 
 		if peer.LoginExpirationEnabled && settings.PeerLoginExpirationEnabled {
-			am.checkAndSchedulePeerLoginExpiration(ctx, accountID)
+			am.schedulePeerLoginExpiration(ctx, accountID)
 		}
 
 		if peer.InactivityExpirationEnabled && settings.PeerInactivityExpirationEnabled {
@@ -296,7 +296,8 @@ func (am *DefaultAccountManager) UpdatePeer(ctx context.Context, accountID, user
 		am.StoreEvent(ctx, userID, peer.IP.String(), accountID, event, peer.EventMeta(dnsDomain))
 
 		if peer.AddedWithSSOLogin() && peer.LoginExpirationEnabled && settings.PeerLoginExpirationEnabled {
-			am.checkAndSchedulePeerLoginExpiration(ctx, accountID)
+			am.peerLoginExpiry.Cancel(ctx, []string{accountID})
+			am.schedulePeerLoginExpiration(ctx, accountID)
 		}
 	}
 
@@ -1148,7 +1149,7 @@ func (am *DefaultAccountManager) checkIfUserOwnsPeer(ctx context.Context, accoun
 	}
 
 	for _, p := range userPeers {
-		aclPeers, _ := account.GetPeerConnectionResources(ctx, p.ID, approvedPeersMap)
+		aclPeers, _ := account.GetPeerConnectionResources(ctx, p, approvedPeersMap)
 		for _, aclPeer := range aclPeers {
 			if aclPeer.ID == peer.ID {
 				return peer, nil
@@ -1168,7 +1169,7 @@ func (am *DefaultAccountManager) UpdateAccountPeers(ctx context.Context, account
 		return
 	}
 
-	start := time.Now()
+	globalStart := time.Now()
 
 	approvedPeersMap, err := am.integratedPeerValidator.GetValidatedPeers(account.Id, maps.Values(account.Groups), maps.Values(account.Peers), account.Settings.Extra)
 	if err != nil {
@@ -1203,18 +1204,27 @@ func (am *DefaultAccountManager) UpdateAccountPeers(ctx context.Context, account
 			defer wg.Done()
 			defer func() { <-semaphore }()
 
+			start := time.Now()
+
 			postureChecks, err := am.getPeerPostureChecks(account, p.ID)
 			if err != nil {
 				log.WithContext(ctx).Debugf("failed to get posture checks for peer %s: %v", peer.ID, err)
 				return
 			}
 
+			am.metrics.UpdateChannelMetrics().CountCalcPostureChecksDuration(time.Since(start))
+			start = time.Now()
+
 			remotePeerNetworkMap := account.GetPeerNetworkMap(ctx, p.ID, customZone, approvedPeersMap, resourcePolicies, routers, am.metrics.AccountManagerMetrics())
 
+			am.metrics.UpdateChannelMetrics().CountCalcPeerNetworkMapDuration(time.Since(start))
+			start = time.Now()
+
 			proxyNetworkMap, ok := proxyNetworkMaps[p.ID]
 			if ok {
 				remotePeerNetworkMap.Merge(proxyNetworkMap)
 			}
+			am.metrics.UpdateChannelMetrics().CountMergeNetworkMapDuration(time.Since(start))
 
 			extraSetting, err := am.settingsManager.GetExtraSettings(ctx, accountID)
 			if err != nil {
@@ -1222,7 +1232,10 @@ func (am *DefaultAccountManager) UpdateAccountPeers(ctx context.Context, account
 				return
 			}
 
+			start = time.Now()
 			update := toSyncResponse(ctx, nil, p, nil, nil, remotePeerNetworkMap, dnsDomain, postureChecks, dnsCache, account.Settings, extraSetting)
+			am.metrics.UpdateChannelMetrics().CountToSyncResponseDuration(time.Since(start))
+
 			am.peersUpdateManager.SendUpdate(ctx, p.ID, &UpdateMessage{Update: update, NetworkMap: remotePeerNetworkMap})
 		}(peer)
 	}
@@ -1231,7 +1244,7 @@ func (am *DefaultAccountManager) UpdateAccountPeers(ctx context.Context, account
 
 	wg.Wait()
 	if am.metrics != nil {
-		am.metrics.AccountManagerMetrics().CountUpdateAccountPeersDuration(time.Since(start))
+		am.metrics.AccountManagerMetrics().CountUpdateAccountPeersDuration(time.Since(globalStart))
 	}
 }
 
diff --git a/management/server/posture/nb_version.go b/management/server/posture/nb_version.go
index e98e8e79..33bf01ad 100644
--- a/management/server/posture/nb_version.go
+++ b/management/server/posture/nb_version.go
@@ -24,20 +24,12 @@ func sanitizeVersion(version string) string {
 }
 
 func (n *NBVersionCheck) Check(ctx context.Context, peer nbpeer.Peer) (bool, error) {
-	peerVersion := sanitizeVersion(peer.Meta.WtVersion)
-	minVersion := sanitizeVersion(n.MinVersion)
-
-	peerNBVersion, err := version.NewVersion(peerVersion)
-	if err != nil {
-		return false, err
-	}
-
-	constraints, err := version.NewConstraint(">= " + minVersion)
+	meetsMin, err := MeetsMinVersion(n.MinVersion, peer.Meta.WtVersion)
 	if err != nil {
 		return false, err
 	}
 
-	if constraints.Check(peerNBVersion) {
+	if meetsMin {
 		return true, nil
 	}
 
@@ -60,3 +52,21 @@ func (n *NBVersionCheck) Validate() error {
 	}
 	return nil
 }
+
+// MeetsMinVersion checks if the peer's version meets or exceeds the minimum required version
+func MeetsMinVersion(minVer, peerVer string) (bool, error) {
+	peerVer = sanitizeVersion(peerVer)
+	minVer = sanitizeVersion(minVer)
+
+	peerNBVer, err := version.NewVersion(peerVer)
+	if err != nil {
+		return false, err
+	}
+
+	constraints, err := version.NewConstraint(">= " + minVer)
+	if err != nil {
+		return false, err
+	}
+
+	return constraints.Check(peerNBVer), nil
+}
diff --git a/management/server/route.go b/management/server/route.go
index 02755a70..32ff3997 100644
--- a/management/server/route.go
+++ b/management/server/route.go
@@ -4,19 +4,19 @@ import (
 	"context"
 	"fmt"
 	"net/netip"
+	"slices"
 	"unicode/utf8"
 
 	"github.com/rs/xid"
 
-	"github.com/netbirdio/netbird/management/server/permissions/modules"
-	"github.com/netbirdio/netbird/management/server/permissions/operations"
-	"github.com/netbirdio/netbird/management/server/store"
-	"github.com/netbirdio/netbird/management/server/types"
-
 	"github.com/netbirdio/netbird/management/domain"
 	"github.com/netbirdio/netbird/management/proto"
 	"github.com/netbirdio/netbird/management/server/activity"
+	"github.com/netbirdio/netbird/management/server/permissions/modules"
+	"github.com/netbirdio/netbird/management/server/permissions/operations"
 	"github.com/netbirdio/netbird/management/server/status"
+	"github.com/netbirdio/netbird/management/server/store"
+	"github.com/netbirdio/netbird/management/server/types"
 	"github.com/netbirdio/netbird/route"
 )
 
@@ -30,13 +30,19 @@ func (am *DefaultAccountManager) GetRoute(ctx context.Context, accountID string,
 		return nil, status.NewPermissionDeniedError()
 	}
 
-	return am.Store.GetRouteByID(ctx, store.LockingStrengthShare, string(routeID), accountID)
+	return am.Store.GetRouteByID(ctx, store.LockingStrengthShare, accountID, string(routeID))
 }
 
 // checkRoutePrefixOrDomainsExistForPeers checks if a route with a given prefix exists for a single peer or multiple peer groups.
-func (am *DefaultAccountManager) checkRoutePrefixOrDomainsExistForPeers(account *types.Account, peerID string, routeID route.ID, peerGroupIDs []string, prefix netip.Prefix, domains domain.List) error {
+func checkRoutePrefixOrDomainsExistForPeers(ctx context.Context, transaction store.Store, accountID string, checkRoute *route.Route, groupsMap map[string]*types.Group) error {
 	// routes can have both peer and peer_groups
-	routesWithPrefix := account.GetRoutesByPrefixOrDomains(prefix, domains)
+	prefix := checkRoute.Network
+	domains := checkRoute.Domains
+
+	routesWithPrefix, err := getRoutesByPrefixOrDomains(ctx, transaction, accountID, prefix, domains)
+	if err != nil {
+		return err
+	}
 
 	// lets remember all the peers and the peer groups from routesWithPrefix
 	seenPeers := make(map[string]bool)
@@ -45,18 +51,24 @@ func (am *DefaultAccountManager) checkRoutePrefixOrDomainsExistForPeers(account
 	for _, prefixRoute := range routesWithPrefix {
 		// we skip route(s) with the same network ID as we want to allow updating of the existing route
 		// when creating a new route routeID is newly generated so nothing will be skipped
-		if routeID == prefixRoute.ID {
+		if checkRoute.ID == prefixRoute.ID {
 			continue
 		}
 
 		if prefixRoute.Peer != "" {
 			seenPeers[string(prefixRoute.ID)] = true
 		}
+
+		peerGroupsMap, err := transaction.GetGroupsByIDs(ctx, store.LockingStrengthShare, accountID, prefixRoute.PeerGroups)
+		if err != nil {
+			return err
+		}
+
 		for _, groupID := range prefixRoute.PeerGroups {
 			seenPeerGroups[groupID] = true
 
-			group := account.GetGroup(groupID)
-			if group == nil {
+			group, ok := peerGroupsMap[groupID]
+			if !ok || group == nil {
 				return status.Errorf(
 					status.InvalidArgument, "failed to add route with %s - peer group %s doesn't exist",
 					getRouteDescriptor(prefix, domains), groupID,
@@ -69,12 +81,13 @@ func (am *DefaultAccountManager) checkRoutePrefixOrDomainsExistForPeers(account
 		}
 	}
 
-	if peerID != "" {
+	if peerID := checkRoute.Peer; peerID != "" {
 		// check that peerID exists and is not in any route as single peer or part of the group
-		peer := account.GetPeer(peerID)
-		if peer == nil {
+		_, err = transaction.GetPeerByID(context.Background(), store.LockingStrengthShare, accountID, peerID)
+		if err != nil {
 			return status.Errorf(status.InvalidArgument, "peer with ID %s not found", peerID)
 		}
+
 		if _, ok := seenPeers[peerID]; ok {
 			return status.Errorf(status.AlreadyExists,
 				"failed to add route with %s - peer %s already has this route", getRouteDescriptor(prefix, domains), peerID)
@@ -82,9 +95,8 @@ func (am *DefaultAccountManager) checkRoutePrefixOrDomainsExistForPeers(account
 	}
 
 	// check that peerGroupIDs are not in any route peerGroups list
-	for _, groupID := range peerGroupIDs {
-		group := account.GetGroup(groupID) // we validated the group existence before entering this function, no need to check again.
-
+	for _, groupID := range checkRoute.PeerGroups {
+		group := groupsMap[groupID] // we validated the group existence before entering this function, no need to check again.
 		if _, ok := seenPeerGroups[groupID]; ok {
 			return status.Errorf(
 				status.AlreadyExists, "failed to add route with %s - peer group %s already has this route",
@@ -92,12 +104,18 @@ func (am *DefaultAccountManager) checkRoutePrefixOrDomainsExistForPeers(account
 		}
 
 		// check that the peers from peerGroupIDs groups are not the same peers we saw in routesWithPrefix
+		peersMap, err := transaction.GetPeersByIDs(ctx, store.LockingStrengthShare, accountID, group.Peers)
+		if err != nil {
+			return err
+		}
+
 		for _, id := range group.Peers {
 			if _, ok := seenPeers[id]; ok {
-				peer := account.GetPeer(id)
-				if peer == nil {
-					return status.Errorf(status.InvalidArgument, "peer with ID %s not found", peerID)
+				peer, ok := peersMap[id]
+				if !ok || peer == nil {
+					return status.Errorf(status.InvalidArgument, "peer with ID %s not found", id)
 				}
+
 				return status.Errorf(status.AlreadyExists,
 					"failed to add route with %s - peer %s from the group %s already has this route",
 					getRouteDescriptor(prefix, domains), peer.Name, group.Name)
@@ -128,97 +146,58 @@ func (am *DefaultAccountManager) CreateRoute(ctx context.Context, accountID stri
 		return nil, status.NewPermissionDeniedError()
 	}
 
-	account, err := am.Store.GetAccount(ctx, accountID)
-	if err != nil {
-		return nil, err
-	}
-
 	if len(domains) > 0 && prefix.IsValid() {
 		return nil, status.Errorf(status.InvalidArgument, "domains and network should not be provided at the same time")
 	}
 
-	if len(domains) == 0 && !prefix.IsValid() {
-		return nil, status.Errorf(status.InvalidArgument, "invalid Prefix")
-	}
-
-	if len(domains) > 0 {
-		prefix = getPlaceholderIP()
-	}
-
-	if peerID != "" && len(peerGroupIDs) != 0 {
-		return nil, status.Errorf(
-			status.InvalidArgument,
-			"peer with ID %s and peers group %s should not be provided at the same time",
-			peerID, peerGroupIDs)
-	}
-
-	var newRoute route.Route
-	newRoute.ID = route.ID(xid.New().String())
+	var newRoute *route.Route
+	var updateAccountPeers bool
+
+	err = am.Store.ExecuteInTransaction(ctx, func(transaction store.Store) error {
+		newRoute = &route.Route{
+			ID:                  route.ID(xid.New().String()),
+			AccountID:           accountID,
+			Network:             prefix,
+			Domains:             domains,
+			KeepRoute:           keepRoute,
+			NetID:               netID,
+			Description:         description,
+			Peer:                peerID,
+			PeerGroups:          peerGroupIDs,
+			NetworkType:         networkType,
+			Masquerade:          masquerade,
+			Metric:              metric,
+			Enabled:             enabled,
+			Groups:              groups,
+			AccessControlGroups: accessControlGroupIDs,
+		}
 
-	if len(peerGroupIDs) > 0 {
-		err = validateGroups(peerGroupIDs, account.Groups)
-		if err != nil {
-			return nil, err
+		if err = validateRoute(ctx, transaction, accountID, newRoute); err != nil {
+			return err
 		}
-	}
 
-	if len(accessControlGroupIDs) > 0 {
-		err = validateGroups(accessControlGroupIDs, account.Groups)
+		updateAccountPeers, err = areRouteChangesAffectPeers(ctx, transaction, newRoute)
 		if err != nil {
-			return nil, err
+			return err
 		}
-	}
-
-	err = am.checkRoutePrefixOrDomainsExistForPeers(account, peerID, newRoute.ID, peerGroupIDs, prefix, domains)
-	if err != nil {
-		return nil, err
-	}
 
-	if metric < route.MinMetric || metric > route.MaxMetric {
-		return nil, status.Errorf(status.InvalidArgument, "metric should be between %d and %d", route.MinMetric, route.MaxMetric)
-	}
-
-	if utf8.RuneCountInString(string(netID)) > route.MaxNetIDChar || netID == "" {
-		return nil, status.Errorf(status.InvalidArgument, "identifier should be between 1 and %d", route.MaxNetIDChar)
-	}
+		if err = transaction.IncrementNetworkSerial(ctx, store.LockingStrengthUpdate, accountID); err != nil {
+			return err
+		}
 
-	err = validateGroups(groups, account.Groups)
+		return transaction.SaveRoute(ctx, store.LockingStrengthUpdate, newRoute)
+	})
 	if err != nil {
 		return nil, err
 	}
 
-	newRoute.Peer = peerID
-	newRoute.PeerGroups = peerGroupIDs
-	newRoute.Network = prefix
-	newRoute.Domains = domains
-	newRoute.NetworkType = networkType
-	newRoute.Description = description
-	newRoute.NetID = netID
-	newRoute.Masquerade = masquerade
-	newRoute.Metric = metric
-	newRoute.Enabled = enabled
-	newRoute.Groups = groups
-	newRoute.KeepRoute = keepRoute
-	newRoute.AccessControlGroups = accessControlGroupIDs
-
-	if account.Routes == nil {
-		account.Routes = make(map[route.ID]*route.Route)
-	}
-
-	account.Routes[newRoute.ID] = &newRoute
-
-	account.Network.IncSerial()
-	if err = am.Store.SaveAccount(ctx, account); err != nil {
-		return nil, err
-	}
+	am.StoreEvent(ctx, userID, string(newRoute.ID), accountID, activity.RouteCreated, newRoute.EventMeta())
 
-	if am.isRouteChangeAffectPeers(account, &newRoute) {
+	if updateAccountPeers {
 		am.UpdateAccountPeers(ctx, accountID)
 	}
 
-	am.StoreEvent(ctx, userID, string(newRoute.ID), accountID, activity.RouteCreated, newRoute.EventMeta())
-
-	return &newRoute, nil
+	return newRoute, nil
 }
 
 // SaveRoute saves route
@@ -226,18 +205,6 @@ func (am *DefaultAccountManager) SaveRoute(ctx context.Context, accountID, userI
 	unlock := am.Store.AcquireWriteLockByUID(ctx, accountID)
 	defer unlock()
 
-	if routeToSave == nil {
-		return status.Errorf(status.InvalidArgument, "route provided is nil")
-	}
-
-	if routeToSave.Metric < route.MinMetric || routeToSave.Metric > route.MaxMetric {
-		return status.Errorf(status.InvalidArgument, "metric should be between %d and %d", route.MinMetric, route.MaxMetric)
-	}
-
-	if utf8.RuneCountInString(string(routeToSave.NetID)) > route.MaxNetIDChar || routeToSave.NetID == "" {
-		return status.Errorf(status.InvalidArgument, "identifier should be between 1 and %d", route.MaxNetIDChar)
-	}
-
 	allowed, err := am.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, modules.Routes, operations.Update)
 	if err != nil {
 		return status.NewPermissionValidationError(err)
@@ -246,65 +213,47 @@ func (am *DefaultAccountManager) SaveRoute(ctx context.Context, accountID, userI
 		return status.NewPermissionDeniedError()
 	}
 
-	account, err := am.Store.GetAccount(ctx, accountID)
-	if err != nil {
-		return err
-	}
-
-	if len(routeToSave.Domains) > 0 && routeToSave.Network.IsValid() {
-		return status.Errorf(status.InvalidArgument, "domains and network should not be provided at the same time")
-	}
-
-	if len(routeToSave.Domains) == 0 && !routeToSave.Network.IsValid() {
-		return status.Errorf(status.InvalidArgument, "invalid Prefix")
-	}
+	var oldRoute *route.Route
+	var oldRouteAffectsPeers bool
+	var newRouteAffectsPeers bool
 
-	if len(routeToSave.Domains) > 0 {
-		routeToSave.Network = getPlaceholderIP()
-	}
+	err = am.Store.ExecuteInTransaction(ctx, func(transaction store.Store) error {
+		if err = validateRoute(ctx, transaction, accountID, routeToSave); err != nil {
+			return err
+		}
 
-	if routeToSave.Peer != "" && len(routeToSave.PeerGroups) != 0 {
-		return status.Errorf(status.InvalidArgument, "peer with ID and peer groups should not be provided at the same time")
-	}
+		oldRoute, err = transaction.GetRouteByID(ctx, store.LockingStrengthUpdate, accountID, string(routeToSave.ID))
+		if err != nil {
+			return err
+		}
 
-	if len(routeToSave.PeerGroups) > 0 {
-		err = validateGroups(routeToSave.PeerGroups, account.Groups)
+		oldRouteAffectsPeers, err = areRouteChangesAffectPeers(ctx, transaction, oldRoute)
 		if err != nil {
 			return err
 		}
-	}
 
-	if len(routeToSave.AccessControlGroups) > 0 {
-		err = validateGroups(routeToSave.AccessControlGroups, account.Groups)
+		newRouteAffectsPeers, err = areRouteChangesAffectPeers(ctx, transaction, routeToSave)
 		if err != nil {
 			return err
 		}
-	}
+		routeToSave.AccountID = accountID
 
-	err = am.checkRoutePrefixOrDomainsExistForPeers(account, routeToSave.Peer, routeToSave.ID, routeToSave.Copy().PeerGroups, routeToSave.Network, routeToSave.Domains)
-	if err != nil {
-		return err
-	}
+		if err = transaction.IncrementNetworkSerial(ctx, store.LockingStrengthUpdate, accountID); err != nil {
+			return err
+		}
 
-	err = validateGroups(routeToSave.Groups, account.Groups)
+		return transaction.SaveRoute(ctx, store.LockingStrengthUpdate, routeToSave)
+	})
 	if err != nil {
 		return err
 	}
 
-	oldRoute := account.Routes[routeToSave.ID]
-	account.Routes[routeToSave.ID] = routeToSave
-
-	account.Network.IncSerial()
-	if err = am.Store.SaveAccount(ctx, account); err != nil {
-		return err
-	}
+	am.StoreEvent(ctx, userID, string(routeToSave.ID), accountID, activity.RouteUpdated, routeToSave.EventMeta())
 
-	if am.isRouteChangeAffectPeers(account, oldRoute) || am.isRouteChangeAffectPeers(account, routeToSave) {
+	if oldRouteAffectsPeers || newRouteAffectsPeers {
 		am.UpdateAccountPeers(ctx, accountID)
 	}
 
-	am.StoreEvent(ctx, userID, string(routeToSave.ID), accountID, activity.RouteUpdated, routeToSave.EventMeta())
-
 	return nil
 }
 
@@ -321,25 +270,30 @@ func (am *DefaultAccountManager) DeleteRoute(ctx context.Context, accountID stri
 		return status.NewPermissionDeniedError()
 	}
 
-	account, err := am.Store.GetAccount(ctx, accountID)
-	if err != nil {
-		return err
-	}
+	var route *route.Route
+	var updateAccountPeers bool
 
-	routy := account.Routes[routeID]
-	if routy == nil {
-		return status.Errorf(status.NotFound, "route with ID %s doesn't exist", routeID)
-	}
-	delete(account.Routes, routeID)
+	err = am.Store.ExecuteInTransaction(ctx, func(transaction store.Store) error {
+		route, err = transaction.GetRouteByID(ctx, store.LockingStrengthUpdate, accountID, string(routeID))
+		if err != nil {
+			return err
+		}
 
-	account.Network.IncSerial()
-	if err = am.Store.SaveAccount(ctx, account); err != nil {
-		return err
-	}
+		updateAccountPeers, err = areRouteChangesAffectPeers(ctx, transaction, route)
+		if err != nil {
+			return err
+		}
 
-	am.StoreEvent(ctx, userID, string(routy.ID), accountID, activity.RouteRemoved, routy.EventMeta())
+		if err = transaction.IncrementNetworkSerial(ctx, store.LockingStrengthUpdate, accountID); err != nil {
+			return err
+		}
+
+		return transaction.DeleteRoute(ctx, store.LockingStrengthUpdate, accountID, string(routeID))
+	})
 
-	if am.isRouteChangeAffectPeers(account, routy) {
+	am.StoreEvent(ctx, userID, string(route.ID), accountID, activity.RouteRemoved, route.EventMeta())
+
+	if updateAccountPeers {
 		am.UpdateAccountPeers(ctx, accountID)
 	}
 
@@ -359,6 +313,70 @@ func (am *DefaultAccountManager) ListRoutes(ctx context.Context, accountID, user
 	return am.Store.GetAccountRoutes(ctx, store.LockingStrengthShare, accountID)
 }
 
+func validateRoute(ctx context.Context, transaction store.Store, accountID string, routeToSave *route.Route) error {
+	if routeToSave == nil {
+		return status.Errorf(status.InvalidArgument, "route provided is nil")
+	}
+
+	if routeToSave.Metric < route.MinMetric || routeToSave.Metric > route.MaxMetric {
+		return status.Errorf(status.InvalidArgument, "metric should be between %d and %d", route.MinMetric, route.MaxMetric)
+	}
+
+	if utf8.RuneCountInString(string(routeToSave.NetID)) > route.MaxNetIDChar || routeToSave.NetID == "" {
+		return status.Errorf(status.InvalidArgument, "identifier should be between 1 and %d", route.MaxNetIDChar)
+	}
+
+	if len(routeToSave.Domains) > 0 && routeToSave.Network.IsValid() {
+		return status.Errorf(status.InvalidArgument, "domains and network should not be provided at the same time")
+	}
+
+	if len(routeToSave.Domains) == 0 && !routeToSave.Network.IsValid() {
+		return status.Errorf(status.InvalidArgument, "invalid Prefix")
+	}
+
+	if len(routeToSave.Domains) > 0 {
+		routeToSave.Network = getPlaceholderIP()
+	}
+
+	if routeToSave.Peer != "" && len(routeToSave.PeerGroups) != 0 {
+		return status.Errorf(status.InvalidArgument, "peer with ID and peer groups should not be provided at the same time")
+	}
+
+	groupsMap, err := validateRouteGroups(ctx, transaction, accountID, routeToSave)
+	if err != nil {
+		return err
+	}
+
+	return checkRoutePrefixOrDomainsExistForPeers(ctx, transaction, accountID, routeToSave, groupsMap)
+}
+
+// validateRouteGroups validates the route groups and returns the validated groups map.
+func validateRouteGroups(ctx context.Context, transaction store.Store, accountID string, routeToSave *route.Route) (map[string]*types.Group, error) {
+	groupsToValidate := slices.Concat(routeToSave.Groups, routeToSave.PeerGroups, routeToSave.AccessControlGroups)
+	groupsMap, err := transaction.GetGroupsByIDs(ctx, store.LockingStrengthShare, accountID, groupsToValidate)
+	if err != nil {
+		return nil, err
+	}
+
+	if len(routeToSave.PeerGroups) > 0 {
+		if err = validateGroups(routeToSave.PeerGroups, groupsMap); err != nil {
+			return nil, err
+		}
+	}
+
+	if len(routeToSave.AccessControlGroups) > 0 {
+		if err = validateGroups(routeToSave.AccessControlGroups, groupsMap); err != nil {
+			return nil, err
+		}
+	}
+
+	if err = validateGroups(routeToSave.Groups, groupsMap); err != nil {
+		return nil, err
+	}
+
+	return groupsMap, nil
+}
+
 func toProtocolRoute(route *route.Route) *proto.Route {
 	return &proto.Route{
 		ID:          string(route.ID),
@@ -455,8 +473,40 @@ func getProtoPortInfo(rule *types.RouteFirewallRule) *proto.PortInfo {
 	return &portInfo
 }
 
-// isRouteChangeAffectPeers checks if a given route affects peers by determining
-// if it has a routing peer, distribution, or peer groups that include peers
-func (am *DefaultAccountManager) isRouteChangeAffectPeers(account *types.Account, route *route.Route) bool {
-	return am.anyGroupHasPeers(account, route.Groups) || am.anyGroupHasPeers(account, route.PeerGroups) || route.Peer != ""
+// areRouteChangesAffectPeers checks if a given route affects peers by determining
+// if it has a routing peer, distribution, or peer groups that include peers.
+func areRouteChangesAffectPeers(ctx context.Context, transaction store.Store, route *route.Route) (bool, error) {
+	if route.Peer != "" {
+		return true, nil
+	}
+
+	hasPeers, err := anyGroupHasPeersOrResources(ctx, transaction, route.AccountID, route.Groups)
+	if err != nil {
+		return false, err
+	}
+
+	if hasPeers {
+		return true, nil
+	}
+
+	return anyGroupHasPeersOrResources(ctx, transaction, route.AccountID, route.PeerGroups)
+}
+
+// GetRoutesByPrefixOrDomains return list of routes by account and route prefix
+func getRoutesByPrefixOrDomains(ctx context.Context, transaction store.Store, accountID string, prefix netip.Prefix, domains domain.List) ([]*route.Route, error) {
+	accountRoutes, err := transaction.GetAccountRoutes(ctx, store.LockingStrengthShare, accountID)
+	if err != nil {
+		return nil, err
+	}
+
+	routes := make([]*route.Route, 0)
+	for _, r := range accountRoutes {
+		dynamic := r.IsDynamic()
+		if dynamic && r.Domains.PunycodeString() == domains.PunycodeString() ||
+			!dynamic && r.Network.String() == prefix.String() {
+			routes = append(routes, r)
+		}
+	}
+
+	return routes, nil
 }
diff --git a/management/server/scheduler.go b/management/server/scheduler.go
index 147b50fc..df73c9a1 100644
--- a/management/server/scheduler.go
+++ b/management/server/scheduler.go
@@ -12,6 +12,7 @@ import (
 type Scheduler interface {
 	Cancel(ctx context.Context, IDs []string)
 	Schedule(ctx context.Context, in time.Duration, ID string, job func() (nextRunIn time.Duration, reschedule bool))
+	IsSchedulerRunning(ID string) bool
 }
 
 // MockScheduler is a mock implementation of  Scheduler
@@ -26,7 +27,7 @@ func (mock *MockScheduler) Cancel(ctx context.Context, IDs []string) {
 		mock.CancelFunc(ctx, IDs)
 		return
 	}
-	log.WithContext(ctx).Errorf("MockScheduler doesn't have Cancel function defined ")
+	log.WithContext(ctx).Warnf("MockScheduler doesn't have Cancel function defined ")
 }
 
 // Schedule mocks the Schedule function of the Scheduler interface
@@ -35,7 +36,13 @@ func (mock *MockScheduler) Schedule(ctx context.Context, in time.Duration, ID st
 		mock.ScheduleFunc(ctx, in, ID, job)
 		return
 	}
-	log.WithContext(ctx).Errorf("MockScheduler doesn't have Schedule function defined")
+	log.WithContext(ctx).Warnf("MockScheduler doesn't have Schedule function defined")
+}
+
+func (mock *MockScheduler) IsSchedulerRunning(ID string) bool {
+	// MockScheduler does not implement IsSchedulerRunning, so we return false
+	log.Warnf("MockScheduler doesn't have IsSchedulerRunning function defined")
+	return false
 }
 
 // DefaultScheduler is a generic structure that allows to schedule jobs (functions) to run in the future and cancel them.
@@ -124,3 +131,11 @@ func (wm *DefaultScheduler) Schedule(ctx context.Context, in time.Duration, ID s
 
 	}()
 }
+
+// IsSchedulerRunning checks if a job with the provided ID is scheduled to run
+func (wm *DefaultScheduler) IsSchedulerRunning(ID string) bool {
+	wm.mu.Lock()
+	defer wm.mu.Unlock()
+	_, ok := wm.jobs[ID]
+	return ok
+}
diff --git a/management/server/status/error.go b/management/server/status/error.go
index 8fbe0bad..5a6f6d1a 100644
--- a/management/server/status/error.go
+++ b/management/server/status/error.go
@@ -227,3 +227,7 @@ func NewUserRoleNotFoundError(role string) error {
 func NewOperationNotFoundError(operation operations.Operation) error {
 	return Errorf(NotFound, "operation: %s not found", operation)
 }
+
+func NewRouteNotFoundError(routeID string) error {
+	return Errorf(NotFound, "route: %s not found", routeID)
+}
diff --git a/management/server/store/sql_store.go b/management/server/store/sql_store.go
index d8189077..72a73a57 100644
--- a/management/server/store/sql_store.go
+++ b/management/server/store/sql_store.go
@@ -23,8 +23,6 @@ import (
 	"gorm.io/gorm/clause"
 	"gorm.io/gorm/logger"
 
-	"github.com/netbirdio/netbird/management/server/util"
-
 	nbdns "github.com/netbirdio/netbird/dns"
 	resourceTypes "github.com/netbirdio/netbird/management/server/networks/resources/types"
 	routerTypes "github.com/netbirdio/netbird/management/server/networks/routers/types"
@@ -34,6 +32,7 @@ import (
 	"github.com/netbirdio/netbird/management/server/status"
 	"github.com/netbirdio/netbird/management/server/telemetry"
 	"github.com/netbirdio/netbird/management/server/types"
+	"github.com/netbirdio/netbird/management/server/util"
 	"github.com/netbirdio/netbird/route"
 )
 
@@ -1185,7 +1184,7 @@ func NewSqliteStoreFromFileStore(ctx context.Context, fileStore *FileStore, data
 	for _, account := range fileStore.GetAllAccounts(ctx) {
 		_, err = account.GetGroupAll()
 		if err != nil {
-			if err := account.AddAllGroup(); err != nil {
+			if err := account.AddAllGroup(false); err != nil {
 				return nil, err
 			}
 		}
@@ -1968,12 +1967,58 @@ func (s *SqlStore) DeletePostureChecks(ctx context.Context, lockStrength Locking
 
 // GetAccountRoutes retrieves network routes for an account.
 func (s *SqlStore) GetAccountRoutes(ctx context.Context, lockStrength LockingStrength, accountID string) ([]*route.Route, error) {
-	return getRecords[*route.Route](s.db, lockStrength, accountID)
+	var routes []*route.Route
+	result := s.db.Clauses(clause.Locking{Strength: string(lockStrength)}).
+		Find(&routes, accountIDCondition, accountID)
+	if err := result.Error; err != nil {
+		log.WithContext(ctx).Errorf("failed to get routes from the store: %s", err)
+		return nil, status.Errorf(status.Internal, "failed to get routes from store")
+	}
+
+	return routes, nil
 }
 
 // GetRouteByID retrieves a route by its ID and account ID.
-func (s *SqlStore) GetRouteByID(ctx context.Context, lockStrength LockingStrength, routeID string, accountID string) (*route.Route, error) {
-	return getRecordByID[route.Route](s.db, lockStrength, routeID, accountID)
+func (s *SqlStore) GetRouteByID(ctx context.Context, lockStrength LockingStrength, accountID string, routeID string) (*route.Route, error) {
+	var route *route.Route
+	result := s.db.Clauses(clause.Locking{Strength: string(lockStrength)}).
+		First(&route, accountAndIDQueryCondition, accountID, routeID)
+	if err := result.Error; err != nil {
+		if errors.Is(err, gorm.ErrRecordNotFound) {
+			return nil, status.NewRouteNotFoundError(routeID)
+		}
+		log.WithContext(ctx).Errorf("failed to get route from the store: %s", err)
+		return nil, status.Errorf(status.Internal, "failed to get route from store")
+	}
+
+	return route, nil
+}
+
+// SaveRoute saves a route to the database.
+func (s *SqlStore) SaveRoute(ctx context.Context, lockStrength LockingStrength, route *route.Route) error {
+	result := s.db.Clauses(clause.Locking{Strength: string(lockStrength)}).Save(route)
+	if err := result.Error; err != nil {
+		log.WithContext(ctx).Errorf("failed to save route to the store: %s", err)
+		return status.Errorf(status.Internal, "failed to save route to store")
+	}
+
+	return nil
+}
+
+// DeleteRoute deletes a route from the database.
+func (s *SqlStore) DeleteRoute(ctx context.Context, lockStrength LockingStrength, accountID, routeID string) error {
+	result := s.db.Clauses(clause.Locking{Strength: string(lockStrength)}).
+		Delete(&route.Route{}, accountAndIDQueryCondition, accountID, routeID)
+	if err := result.Error; err != nil {
+		log.WithContext(ctx).Errorf("failed to delete route from the store: %s", err)
+		return status.Errorf(status.Internal, "failed to delete route from store")
+	}
+
+	if result.RowsAffected == 0 {
+		return status.NewRouteNotFoundError(routeID)
+	}
+
+	return nil
 }
 
 // GetAccountSetupKeys retrieves setup keys for an account.
@@ -2104,49 +2149,6 @@ func (s *SqlStore) DeleteNameServerGroup(ctx context.Context, lockStrength Locki
 	return nil
 }
 
-// getRecords retrieves records from the database based on the account ID.
-func getRecords[T any](db *gorm.DB, lockStrength LockingStrength, accountID string) ([]T, error) {
-	tx := db
-	if lockStrength != LockingStrengthNone {
-		tx = tx.Clauses(clause.Locking{Strength: string(lockStrength)})
-	}
-
-	var record []T
-
-	result := tx.Find(&record, accountIDCondition, accountID)
-	if err := result.Error; err != nil {
-		parts := strings.Split(fmt.Sprintf("%T", record), ".")
-		recordType := parts[len(parts)-1]
-
-		return nil, status.Errorf(status.Internal, "failed to get account %ss from store: %v", recordType, err)
-	}
-
-	return record, nil
-}
-
-// getRecordByID retrieves a record by its ID and account ID from the database.
-func getRecordByID[T any](db *gorm.DB, lockStrength LockingStrength, recordID, accountID string) (*T, error) {
-	tx := db
-	if lockStrength != LockingStrengthNone {
-		tx = tx.Clauses(clause.Locking{Strength: string(lockStrength)})
-	}
-
-	var record T
-
-	result := tx.Clauses(clause.Locking{Strength: string(lockStrength)}).
-		First(&record, accountAndIDQueryCondition, accountID, recordID)
-	if err := result.Error; err != nil {
-		parts := strings.Split(fmt.Sprintf("%T", record), ".")
-		recordType := parts[len(parts)-1]
-
-		if errors.Is(result.Error, gorm.ErrRecordNotFound) {
-			return nil, status.Errorf(status.NotFound, "%s not found", recordType)
-		}
-		return nil, status.Errorf(status.Internal, "failed to get %s from store: %v", recordType, err)
-	}
-	return &record, nil
-}
-
 // SaveDNSSettings saves the DNS settings to the store.
 func (s *SqlStore) SaveDNSSettings(ctx context.Context, lockStrength LockingStrength, accountID string, settings *types.DNSSettings) error {
 	result := s.db.Clauses(clause.Locking{Strength: string(lockStrength)}).Model(&types.Account{}).
diff --git a/management/server/store/store.go b/management/server/store/store.go
index c7b10345..f66130ad 100644
--- a/management/server/store/store.go
+++ b/management/server/store/store.go
@@ -145,7 +145,9 @@ type Store interface {
 	DeleteSetupKey(ctx context.Context, lockStrength LockingStrength, accountID, keyID string) error
 
 	GetAccountRoutes(ctx context.Context, lockStrength LockingStrength, accountID string) ([]*route.Route, error)
-	GetRouteByID(ctx context.Context, lockStrength LockingStrength, routeID string, accountID string) (*route.Route, error)
+	GetRouteByID(ctx context.Context, lockStrength LockingStrength, accountID, routeID string) (*route.Route, error)
+	SaveRoute(ctx context.Context, lockStrength LockingStrength, route *route.Route) error
+	DeleteRoute(ctx context.Context, lockStrength LockingStrength, accountID, routeID string) error
 
 	GetAccountNameServerGroups(ctx context.Context, lockStrength LockingStrength, accountID string) ([]*dns.NameServerGroup, error)
 	GetNameServerGroupByID(ctx context.Context, lockStrength LockingStrength, nameServerGroupID string, accountID string) (*dns.NameServerGroup, error)
@@ -389,7 +391,7 @@ func addAllGroupToAccount(ctx context.Context, store Store) error {
 
 		_, err := account.GetGroupAll()
 		if err != nil {
-			if err := account.AddAllGroup(); err != nil {
+			if err := account.AddAllGroup(false); err != nil {
 				return err
 			}
 			shouldSave = true
diff --git a/management/server/telemetry/updatechannel_metrics.go b/management/server/telemetry/updatechannel_metrics.go
index 584b9ec2..2b280b35 100644
--- a/management/server/telemetry/updatechannel_metrics.go
+++ b/management/server/telemetry/updatechannel_metrics.go
@@ -18,6 +18,10 @@ type UpdateChannelMetrics struct {
 	getAllConnectedPeersDurationMicro metric.Int64Histogram
 	getAllConnectedPeers              metric.Int64Histogram
 	hasChannelDurationMicro           metric.Int64Histogram
+	calcPostureChecksDurationMicro    metric.Int64Histogram
+	calcPeerNetworkMapDurationMs      metric.Int64Histogram
+	mergeNetworkMapDurationMicro      metric.Int64Histogram
+	toSyncResponseDurationMicro       metric.Int64Histogram
 	ctx                               context.Context
 }
 
@@ -89,6 +93,38 @@ func NewUpdateChannelMetrics(ctx context.Context, meter metric.Meter) (*UpdateCh
 		return nil, err
 	}
 
+	calcPostureChecksDurationMicro, err := meter.Int64Histogram("management.updatechannel.calc.posturechecks.duration.micro",
+		metric.WithUnit("microseconds"),
+		metric.WithDescription("Duration of how long it takes to get the posture checks for a peer"),
+	)
+	if err != nil {
+		return nil, err
+	}
+
+	calcPeerNetworkMapDurationMs, err := meter.Int64Histogram("management.updatechannel.calc.networkmap.duration.ms",
+		metric.WithUnit("milliseconds"),
+		metric.WithDescription("Duration of how long it takes to calculate the network map for a peer"),
+	)
+	if err != nil {
+		return nil, err
+	}
+
+	mergeNetworkMapDurationMicro, err := meter.Int64Histogram("management.updatechannel.merge.networkmap.duration.micro",
+		metric.WithUnit("microseconds"),
+		metric.WithDescription("Duration of how long it takes to merge the network maps for a peer"),
+	)
+	if err != nil {
+		return nil, err
+	}
+
+	toSyncResponseDurationMicro, err := meter.Int64Histogram("management.updatechannel.tosyncresponse.duration.micro",
+		metric.WithUnit("microseconds"),
+		metric.WithDescription("Duration of how long it takes to convert the network map to sync response"),
+	)
+	if err != nil {
+		return nil, err
+	}
+
 	return &UpdateChannelMetrics{
 		createChannelDurationMicro:        createChannelDurationMicro,
 		closeChannelDurationMicro:         closeChannelDurationMicro,
@@ -98,6 +134,10 @@ func NewUpdateChannelMetrics(ctx context.Context, meter metric.Meter) (*UpdateCh
 		getAllConnectedPeersDurationMicro: getAllConnectedPeersDurationMicro,
 		getAllConnectedPeers:              getAllConnectedPeers,
 		hasChannelDurationMicro:           hasChannelDurationMicro,
+		calcPostureChecksDurationMicro:    calcPostureChecksDurationMicro,
+		calcPeerNetworkMapDurationMs:      calcPeerNetworkMapDurationMs,
+		mergeNetworkMapDurationMicro:      mergeNetworkMapDurationMicro,
+		toSyncResponseDurationMicro:       toSyncResponseDurationMicro,
 		ctx:                               ctx,
 	}, nil
 }
@@ -137,3 +177,19 @@ func (metrics *UpdateChannelMetrics) CountGetAllConnectedPeersDuration(duration
 func (metrics *UpdateChannelMetrics) CountHasChannelDuration(duration time.Duration) {
 	metrics.hasChannelDurationMicro.Record(metrics.ctx, duration.Microseconds())
 }
+
+func (metrics *UpdateChannelMetrics) CountCalcPostureChecksDuration(duration time.Duration) {
+	metrics.calcPostureChecksDurationMicro.Record(metrics.ctx, duration.Microseconds())
+}
+
+func (metrics *UpdateChannelMetrics) CountCalcPeerNetworkMapDuration(duration time.Duration) {
+	metrics.calcPeerNetworkMapDurationMs.Record(metrics.ctx, duration.Milliseconds())
+}
+
+func (metrics *UpdateChannelMetrics) CountMergeNetworkMapDuration(duration time.Duration) {
+	metrics.mergeNetworkMapDurationMicro.Record(metrics.ctx, duration.Microseconds())
+}
+
+func (metrics *UpdateChannelMetrics) CountToSyncResponseDuration(duration time.Duration) {
+	metrics.toSyncResponseDurationMicro.Record(metrics.ctx, duration.Microseconds())
+}
diff --git a/management/server/types/account.go b/management/server/types/account.go
index da230f0b..5a62ee4c 100644
--- a/management/server/types/account.go
+++ b/management/server/types/account.go
@@ -36,6 +36,9 @@ const (
 	PublicCategory  = "public"
 	PrivateCategory = "private"
 	UnknownCategory = "unknown"
+
+	// firewallRuleMinPortRangesVer defines the minimum peer version that supports port range rules.
+	firewallRuleMinPortRangesVer = "0.48.0"
 )
 
 type LookupMap map[string]struct{}
@@ -248,7 +251,7 @@ func (a *Account) GetPeerNetworkMap(
 		}
 	}
 
-	aclPeers, firewallRules := a.GetPeerConnectionResources(ctx, peerID, validatedPeersMap)
+	aclPeers, firewallRules := a.GetPeerConnectionResources(ctx, peer, validatedPeersMap)
 	// exclude expired peers
 	var peersToConnect []*nbpeer.Peer
 	var expiredPeers []*nbpeer.Peer
@@ -961,8 +964,9 @@ func (a *Account) UserGroupsRemoveFromPeers(userID string, groups ...string) map
 // GetPeerConnectionResources for a given peer
 //
 // This function returns the list of peers and firewall rules that are applicable to a given peer.
-func (a *Account) GetPeerConnectionResources(ctx context.Context, peerID string, validatedPeersMap map[string]struct{}) ([]*nbpeer.Peer, []*FirewallRule) {
-	generateResources, getAccumulatedResources := a.connResourcesGenerator(ctx)
+func (a *Account) GetPeerConnectionResources(ctx context.Context, peer *nbpeer.Peer, validatedPeersMap map[string]struct{}) ([]*nbpeer.Peer, []*FirewallRule) {
+	generateResources, getAccumulatedResources := a.connResourcesGenerator(ctx, peer)
+
 	for _, policy := range a.Policies {
 		if !policy.Enabled {
 			continue
@@ -973,8 +977,8 @@ func (a *Account) GetPeerConnectionResources(ctx context.Context, peerID string,
 				continue
 			}
 
-			sourcePeers, peerInSources := a.getAllPeersFromGroups(ctx, rule.Sources, peerID, policy.SourcePostureChecks, validatedPeersMap)
-			destinationPeers, peerInDestinations := a.getAllPeersFromGroups(ctx, rule.Destinations, peerID, nil, validatedPeersMap)
+			sourcePeers, peerInSources := a.getAllPeersFromGroups(ctx, rule.Sources, peer.ID, policy.SourcePostureChecks, validatedPeersMap)
+			destinationPeers, peerInDestinations := a.getAllPeersFromGroups(ctx, rule.Destinations, peer.ID, nil, validatedPeersMap)
 
 			if rule.Bidirectional {
 				if peerInSources {
@@ -1003,7 +1007,7 @@ func (a *Account) GetPeerConnectionResources(ctx context.Context, peerID string,
 // The generator function is used to generate the list of peers and firewall rules that are applicable to a given peer.
 // It safe to call the generator function multiple times for same peer and different rules no duplicates will be
 // generated. The accumulator function returns the result of all the generator calls.
-func (a *Account) connResourcesGenerator(ctx context.Context) (func(*PolicyRule, []*nbpeer.Peer, int), func() ([]*nbpeer.Peer, []*FirewallRule)) {
+func (a *Account) connResourcesGenerator(ctx context.Context, targetPeer *nbpeer.Peer) (func(*PolicyRule, []*nbpeer.Peer, int), func() ([]*nbpeer.Peer, []*FirewallRule)) {
 	rulesExists := make(map[string]struct{})
 	peersExists := make(map[string]struct{})
 	rules := make([]*FirewallRule, 0)
@@ -1051,17 +1055,7 @@ func (a *Account) connResourcesGenerator(ctx context.Context) (func(*PolicyRule,
 					continue
 				}
 
-				for _, port := range rule.Ports {
-					pr := fr // clone rule and add set new port
-					pr.Port = port
-					rules = append(rules, &pr)
-				}
-
-				for _, portRange := range rule.PortRanges {
-					pr := fr
-					pr.PortRange = portRange
-					rules = append(rules, &pr)
-				}
+				rules = append(rules, expandPortsAndRanges(fr, rule, targetPeer)...)
 			}
 		}, func() ([]*nbpeer.Peer, []*FirewallRule) {
 			return peers, rules
@@ -1552,7 +1546,7 @@ func getPoliciesSourcePeers(policies []*Policy, groups map[string]*Group) map[st
 }
 
 // AddAllGroup to account object if it doesn't exist
-func (a *Account) AddAllGroup() error {
+func (a *Account) AddAllGroup(disableDefaultPolicy bool) error {
 	if len(a.Groups) == 0 {
 		allGroup := &Group{
 			ID:     xid.New().String(),
@@ -1564,6 +1558,10 @@ func (a *Account) AddAllGroup() error {
 		}
 		a.Groups = map[string]*Group{allGroup.ID: allGroup}
 
+		if disableDefaultPolicy {
+			return nil
+		}
+
 		id := xid.New().String()
 
 		defaultPolicy := &Policy{
@@ -1590,3 +1588,45 @@ func (a *Account) AddAllGroup() error {
 	}
 	return nil
 }
+
+// expandPortsAndRanges expands Ports and PortRanges of a rule into individual firewall rules
+func expandPortsAndRanges(base FirewallRule, rule *PolicyRule, peer *nbpeer.Peer) []*FirewallRule {
+	var expanded []*FirewallRule
+
+	if len(rule.Ports) > 0 {
+		for _, port := range rule.Ports {
+			fr := base
+			fr.Port = port
+			expanded = append(expanded, &fr)
+		}
+		return expanded
+	}
+
+	supportPortRanges := peerSupportsPortRanges(peer.Meta.WtVersion)
+	for _, portRange := range rule.PortRanges {
+		fr := base
+
+		if supportPortRanges {
+			fr.PortRange = portRange
+		} else {
+			// Peer doesn't support port ranges, only allow single-port ranges
+			if portRange.Start != portRange.End {
+				continue
+			}
+			fr.Port = strconv.FormatUint(uint64(portRange.Start), 10)
+		}
+		expanded = append(expanded, &fr)
+	}
+
+	return expanded
+}
+
+// peerSupportsPortRanges checks if the peer version supports port ranges.
+func peerSupportsPortRanges(peerVer string) bool {
+	if strings.Contains(peerVer, "dev") {
+		return true
+	}
+
+	meetMinVer, err := posture.MeetsMinVersion(firewallRuleMinPortRangesVer, peerVer)
+	return err == nil && meetMinVer
+}
diff --git a/management/server/types/config.go b/management/server/types/config.go
index 13e0f62b..5c64d6c6 100644
--- a/management/server/types/config.go
+++ b/management/server/types/config.go
@@ -53,6 +53,9 @@ type Config struct {
 	StoreConfig StoreConfig
 
 	ReverseProxy ReverseProxy
+	
+	// disable default all-to-all policy
+	DisableDefaultPolicy bool
 }
 
 // GetAuthAudiences returns the audience from the http config and device authorization flow config
diff --git a/management/server/types/firewall_rule.go b/management/server/types/firewall_rule.go
index ef54abea..19222a60 100644
--- a/management/server/types/firewall_rule.go
+++ b/management/server/types/firewall_rule.go
@@ -76,7 +76,6 @@ func generateRouteFirewallRules(ctx context.Context, route *nbroute.Route, rule
 		rules = append(rules, generateRulesWithPortRanges(baseRule, rule, rulesExists)...)
 	} else {
 		rules = append(rules, generateRulesWithPorts(ctx, baseRule, rule, rulesExists)...)
-
 	}
 
 	// TODO: generate IPv6 rules for dynamic routes
diff --git a/release_files/install.sh b/release_files/install.sh
index 0f63529e..856d332c 100755
--- a/release_files/install.sh
+++ b/release_files/install.sh
@@ -130,7 +130,7 @@ repo_gpgcheck=1
 EOF
 }
 
-add_aur_repo() {
+install_aur_package() {
     INSTALL_PKGS="git base-devel go"
     REMOVE_PKGS=""
 
@@ -154,8 +154,10 @@ add_aur_repo() {
         cd netbird-ui && makepkg -sri --noconfirm
     fi
 
-    # Clean up the installed packages
-    ${SUDO} pacman -Rs "$REMOVE_PKGS" --noconfirm
+    if [ -n "$REMOVE_PKGS" ]; then
+      # Clean up the installed packages
+      ${SUDO} pacman -Rs "$REMOVE_PKGS" --noconfirm
+    fi
 }
 
 prepare_tun_module() {
@@ -277,7 +279,9 @@ install_netbird() {
     ;;
     pacman)
         ${SUDO} pacman -Syy
-        add_aur_repo
+        install_aur_package
+        # in-line with the docs at https://wiki.archlinux.org/title/Netbird
+        ${SUDO} systemctl enable --now netbird@main.service
     ;;
     pkg)
         # Check if the package is already installed
@@ -494,4 +498,4 @@ case "$UPDATE_FLAG" in
     ;;
     *)
       install_netbird
-esac
\ No newline at end of file
+esac
diff --git a/version/update.go b/version/update.go
index 3f548ea8..272eef4c 100644
--- a/version/update.go
+++ b/version/update.go
@@ -21,6 +21,7 @@ var (
 // Update fetch the version info periodically and notify the onUpdateListener in case the UI version or the
 // daemon version are deprecated
 type Update struct {
+	httpAgent       string
 	uiVersion       *goversion.Version
 	daemonVersion   *goversion.Version
 	latestAvailable *goversion.Version
@@ -34,7 +35,7 @@ type Update struct {
 }
 
 // NewUpdate instantiate Update and start to fetch the new version information
-func NewUpdate() *Update {
+func NewUpdate(httpAgent string) *Update {
 	currentVersion, err := goversion.NewVersion(version)
 	if err != nil {
 		currentVersion, _ = goversion.NewVersion("0.0.0")
@@ -43,6 +44,7 @@ func NewUpdate() *Update {
 	latestAvailable, _ := goversion.NewVersion("0.0.0")
 
 	u := &Update{
+		httpAgent:       httpAgent,
 		latestAvailable: latestAvailable,
 		uiVersion:       currentVersion,
 		fetchTicker:     time.NewTicker(fetchPeriod),
@@ -112,7 +114,15 @@ func (u *Update) startFetcher() {
 func (u *Update) fetchVersion() bool {
 	log.Debugf("fetching version info from %s", versionURL)
 
-	resp, err := http.Get(versionURL)
+	req, err := http.NewRequest("GET", versionURL, nil)
+	if err != nil {
+		log.Errorf("failed to create request for version info: %s", err)
+		return false
+	}
+
+	req.Header.Set("User-Agent", u.httpAgent)
+
+	resp, err := http.DefaultClient.Do(req)
 	if err != nil {
 		log.Errorf("failed to fetch version info: %s", err)
 		return false



Test patch:
diff --git a/client/cmd/testutil_test.go b/client/cmd/testutil_test.go
index 258a8daf..b5a80d63 100644
--- a/client/cmd/testutil_test.go
+++ b/client/cmd/testutil_test.go
@@ -103,7 +103,7 @@ func startManagement(t *testing.T, config *types.Config, testFile string) (*grpc
 		Return(&types.Settings{}, nil).
 		AnyTimes()
 
-	accountManager, err := mgmt.BuildManager(context.Background(), store, peersUpdateManager, nil, "", "netbird.selfhosted", eventStore, nil, false, iv, metrics, port_forwarding.NewControllerMock(), settingsMockManager, permissionsManagerMock)
+	accountManager, err := mgmt.BuildManager(context.Background(), store, peersUpdateManager, nil, "", "netbird.selfhosted", eventStore, nil, false, iv, metrics, port_forwarding.NewControllerMock(), settingsMockManager, permissionsManagerMock, false)
 	if err != nil {
 		t.Fatal(err)
 	}
diff --git a/client/internal/acl/manager_test.go b/client/internal/acl/manager_test.go
index 16620033..b378de8c 100644
--- a/client/internal/acl/manager_test.go
+++ b/client/internal/acl/manager_test.go
@@ -330,6 +330,434 @@ func TestDefaultManagerSquashRulesNoAffect(t *testing.T) {
 	assert.Equal(t, len(networkMap.FirewallRules), len(rules))
 }
 
+func TestDefaultManagerSquashRulesWithPortRestrictions(t *testing.T) {
+	tests := []struct {
+		name          string
+		rules         []*mgmProto.FirewallRule
+		expectedCount int
+		description   string
+	}{
+		{
+			name: "should not squash rules with port ranges",
+			rules: []*mgmProto.FirewallRule{
+				{
+					PeerIP:    "10.93.0.1",
+					Direction: mgmProto.RuleDirection_IN,
+					Action:    mgmProto.RuleAction_ACCEPT,
+					Protocol:  mgmProto.RuleProtocol_TCP,
+					PortInfo: &mgmProto.PortInfo{
+						PortSelection: &mgmProto.PortInfo_Range_{
+							Range: &mgmProto.PortInfo_Range{
+								Start: 8080,
+								End:   8090,
+							},
+						},
+					},
+				},
+				{
+					PeerIP:    "10.93.0.2",
+					Direction: mgmProto.RuleDirection_IN,
+					Action:    mgmProto.RuleAction_ACCEPT,
+					Protocol:  mgmProto.RuleProtocol_TCP,
+					PortInfo: &mgmProto.PortInfo{
+						PortSelection: &mgmProto.PortInfo_Range_{
+							Range: &mgmProto.PortInfo_Range{
+								Start: 8080,
+								End:   8090,
+							},
+						},
+					},
+				},
+				{
+					PeerIP:    "10.93.0.3",
+					Direction: mgmProto.RuleDirection_IN,
+					Action:    mgmProto.RuleAction_ACCEPT,
+					Protocol:  mgmProto.RuleProtocol_TCP,
+					PortInfo: &mgmProto.PortInfo{
+						PortSelection: &mgmProto.PortInfo_Range_{
+							Range: &mgmProto.PortInfo_Range{
+								Start: 8080,
+								End:   8090,
+							},
+						},
+					},
+				},
+				{
+					PeerIP:    "10.93.0.4",
+					Direction: mgmProto.RuleDirection_IN,
+					Action:    mgmProto.RuleAction_ACCEPT,
+					Protocol:  mgmProto.RuleProtocol_TCP,
+					PortInfo: &mgmProto.PortInfo{
+						PortSelection: &mgmProto.PortInfo_Range_{
+							Range: &mgmProto.PortInfo_Range{
+								Start: 8080,
+								End:   8090,
+							},
+						},
+					},
+				},
+			},
+			expectedCount: 4,
+			description:   "Rules with port ranges should not be squashed even if they cover all peers",
+		},
+		{
+			name: "should not squash rules with specific ports",
+			rules: []*mgmProto.FirewallRule{
+				{
+					PeerIP:    "10.93.0.1",
+					Direction: mgmProto.RuleDirection_IN,
+					Action:    mgmProto.RuleAction_ACCEPT,
+					Protocol:  mgmProto.RuleProtocol_TCP,
+					PortInfo: &mgmProto.PortInfo{
+						PortSelection: &mgmProto.PortInfo_Port{
+							Port: 80,
+						},
+					},
+				},
+				{
+					PeerIP:    "10.93.0.2",
+					Direction: mgmProto.RuleDirection_IN,
+					Action:    mgmProto.RuleAction_ACCEPT,
+					Protocol:  mgmProto.RuleProtocol_TCP,
+					PortInfo: &mgmProto.PortInfo{
+						PortSelection: &mgmProto.PortInfo_Port{
+							Port: 80,
+						},
+					},
+				},
+				{
+					PeerIP:    "10.93.0.3",
+					Direction: mgmProto.RuleDirection_IN,
+					Action:    mgmProto.RuleAction_ACCEPT,
+					Protocol:  mgmProto.RuleProtocol_TCP,
+					PortInfo: &mgmProto.PortInfo{
+						PortSelection: &mgmProto.PortInfo_Port{
+							Port: 80,
+						},
+					},
+				},
+				{
+					PeerIP:    "10.93.0.4",
+					Direction: mgmProto.RuleDirection_IN,
+					Action:    mgmProto.RuleAction_ACCEPT,
+					Protocol:  mgmProto.RuleProtocol_TCP,
+					PortInfo: &mgmProto.PortInfo{
+						PortSelection: &mgmProto.PortInfo_Port{
+							Port: 80,
+						},
+					},
+				},
+			},
+			expectedCount: 4,
+			description:   "Rules with specific ports should not be squashed even if they cover all peers",
+		},
+		{
+			name: "should not squash rules with legacy port field",
+			rules: []*mgmProto.FirewallRule{
+				{
+					PeerIP:    "10.93.0.1",
+					Direction: mgmProto.RuleDirection_IN,
+					Action:    mgmProto.RuleAction_ACCEPT,
+					Protocol:  mgmProto.RuleProtocol_TCP,
+					Port:      "443",
+				},
+				{
+					PeerIP:    "10.93.0.2",
+					Direction: mgmProto.RuleDirection_IN,
+					Action:    mgmProto.RuleAction_ACCEPT,
+					Protocol:  mgmProto.RuleProtocol_TCP,
+					Port:      "443",
+				},
+				{
+					PeerIP:    "10.93.0.3",
+					Direction: mgmProto.RuleDirection_IN,
+					Action:    mgmProto.RuleAction_ACCEPT,
+					Protocol:  mgmProto.RuleProtocol_TCP,
+					Port:      "443",
+				},
+				{
+					PeerIP:    "10.93.0.4",
+					Direction: mgmProto.RuleDirection_IN,
+					Action:    mgmProto.RuleAction_ACCEPT,
+					Protocol:  mgmProto.RuleProtocol_TCP,
+					Port:      "443",
+				},
+			},
+			expectedCount: 4,
+			description:   "Rules with legacy port field should not be squashed",
+		},
+		{
+			name: "should not squash rules with DROP action",
+			rules: []*mgmProto.FirewallRule{
+				{
+					PeerIP:    "10.93.0.1",
+					Direction: mgmProto.RuleDirection_IN,
+					Action:    mgmProto.RuleAction_DROP,
+					Protocol:  mgmProto.RuleProtocol_TCP,
+				},
+				{
+					PeerIP:    "10.93.0.2",
+					Direction: mgmProto.RuleDirection_IN,
+					Action:    mgmProto.RuleAction_DROP,
+					Protocol:  mgmProto.RuleProtocol_TCP,
+				},
+				{
+					PeerIP:    "10.93.0.3",
+					Direction: mgmProto.RuleDirection_IN,
+					Action:    mgmProto.RuleAction_DROP,
+					Protocol:  mgmProto.RuleProtocol_TCP,
+				},
+				{
+					PeerIP:    "10.93.0.4",
+					Direction: mgmProto.RuleDirection_IN,
+					Action:    mgmProto.RuleAction_DROP,
+					Protocol:  mgmProto.RuleProtocol_TCP,
+				},
+			},
+			expectedCount: 4,
+			description:   "Rules with DROP action should not be squashed",
+		},
+		{
+			name: "should squash rules without port restrictions",
+			rules: []*mgmProto.FirewallRule{
+				{
+					PeerIP:    "10.93.0.1",
+					Direction: mgmProto.RuleDirection_IN,
+					Action:    mgmProto.RuleAction_ACCEPT,
+					Protocol:  mgmProto.RuleProtocol_TCP,
+				},
+				{
+					PeerIP:    "10.93.0.2",
+					Direction: mgmProto.RuleDirection_IN,
+					Action:    mgmProto.RuleAction_ACCEPT,
+					Protocol:  mgmProto.RuleProtocol_TCP,
+				},
+				{
+					PeerIP:    "10.93.0.3",
+					Direction: mgmProto.RuleDirection_IN,
+					Action:    mgmProto.RuleAction_ACCEPT,
+					Protocol:  mgmProto.RuleProtocol_TCP,
+				},
+				{
+					PeerIP:    "10.93.0.4",
+					Direction: mgmProto.RuleDirection_IN,
+					Action:    mgmProto.RuleAction_ACCEPT,
+					Protocol:  mgmProto.RuleProtocol_TCP,
+				},
+			},
+			expectedCount: 1,
+			description:   "Rules without port restrictions should be squashed into a single 0.0.0.0 rule",
+		},
+		{
+			name: "mixed rules should not squash protocol with port restrictions",
+			rules: []*mgmProto.FirewallRule{
+				{
+					PeerIP:    "10.93.0.1",
+					Direction: mgmProto.RuleDirection_IN,
+					Action:    mgmProto.RuleAction_ACCEPT,
+					Protocol:  mgmProto.RuleProtocol_TCP,
+				},
+				{
+					PeerIP:    "10.93.0.2",
+					Direction: mgmProto.RuleDirection_IN,
+					Action:    mgmProto.RuleAction_ACCEPT,
+					Protocol:  mgmProto.RuleProtocol_TCP,
+					PortInfo: &mgmProto.PortInfo{
+						PortSelection: &mgmProto.PortInfo_Port{
+							Port: 80,
+						},
+					},
+				},
+				{
+					PeerIP:    "10.93.0.3",
+					Direction: mgmProto.RuleDirection_IN,
+					Action:    mgmProto.RuleAction_ACCEPT,
+					Protocol:  mgmProto.RuleProtocol_TCP,
+				},
+				{
+					PeerIP:    "10.93.0.4",
+					Direction: mgmProto.RuleDirection_IN,
+					Action:    mgmProto.RuleAction_ACCEPT,
+					Protocol:  mgmProto.RuleProtocol_TCP,
+				},
+			},
+			expectedCount: 4,
+			description:   "TCP should not be squashed because one rule has port restrictions",
+		},
+		{
+			name: "should squash UDP but not TCP when TCP has port restrictions",
+			rules: []*mgmProto.FirewallRule{
+				// TCP rules with port restrictions - should NOT be squashed
+				{
+					PeerIP:    "10.93.0.1",
+					Direction: mgmProto.RuleDirection_IN,
+					Action:    mgmProto.RuleAction_ACCEPT,
+					Protocol:  mgmProto.RuleProtocol_TCP,
+					Port:      "443",
+				},
+				{
+					PeerIP:    "10.93.0.2",
+					Direction: mgmProto.RuleDirection_IN,
+					Action:    mgmProto.RuleAction_ACCEPT,
+					Protocol:  mgmProto.RuleProtocol_TCP,
+					Port:      "443",
+				},
+				{
+					PeerIP:    "10.93.0.3",
+					Direction: mgmProto.RuleDirection_IN,
+					Action:    mgmProto.RuleAction_ACCEPT,
+					Protocol:  mgmProto.RuleProtocol_TCP,
+					Port:      "443",
+				},
+				{
+					PeerIP:    "10.93.0.4",
+					Direction: mgmProto.RuleDirection_IN,
+					Action:    mgmProto.RuleAction_ACCEPT,
+					Protocol:  mgmProto.RuleProtocol_TCP,
+					Port:      "443",
+				},
+				// UDP rules without port restrictions - SHOULD be squashed
+				{
+					PeerIP:    "10.93.0.1",
+					Direction: mgmProto.RuleDirection_IN,
+					Action:    mgmProto.RuleAction_ACCEPT,
+					Protocol:  mgmProto.RuleProtocol_UDP,
+				},
+				{
+					PeerIP:    "10.93.0.2",
+					Direction: mgmProto.RuleDirection_IN,
+					Action:    mgmProto.RuleAction_ACCEPT,
+					Protocol:  mgmProto.RuleProtocol_UDP,
+				},
+				{
+					PeerIP:    "10.93.0.3",
+					Direction: mgmProto.RuleDirection_IN,
+					Action:    mgmProto.RuleAction_ACCEPT,
+					Protocol:  mgmProto.RuleProtocol_UDP,
+				},
+				{
+					PeerIP:    "10.93.0.4",
+					Direction: mgmProto.RuleDirection_IN,
+					Action:    mgmProto.RuleAction_ACCEPT,
+					Protocol:  mgmProto.RuleProtocol_UDP,
+				},
+			},
+			expectedCount: 5, // 4 TCP rules + 1 squashed UDP rule (0.0.0.0)
+			description:   "UDP should be squashed to 0.0.0.0 rule, but TCP should remain as individual rules due to port restrictions",
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			networkMap := &mgmProto.NetworkMap{
+				RemotePeers: []*mgmProto.RemotePeerConfig{
+					{AllowedIps: []string{"10.93.0.1"}},
+					{AllowedIps: []string{"10.93.0.2"}},
+					{AllowedIps: []string{"10.93.0.3"}},
+					{AllowedIps: []string{"10.93.0.4"}},
+				},
+				FirewallRules: tt.rules,
+			}
+
+			manager := &DefaultManager{}
+			rules, _ := manager.squashAcceptRules(networkMap)
+
+			assert.Equal(t, tt.expectedCount, len(rules), tt.description)
+
+			// For squashed rules, verify we get the expected 0.0.0.0 rule
+			if tt.expectedCount == 1 {
+				assert.Equal(t, "0.0.0.0", rules[0].PeerIP)
+				assert.Equal(t, mgmProto.RuleDirection_IN, rules[0].Direction)
+				assert.Equal(t, mgmProto.RuleAction_ACCEPT, rules[0].Action)
+			}
+		})
+	}
+}
+
+func TestPortInfoEmpty(t *testing.T) {
+	tests := []struct {
+		name     string
+		portInfo *mgmProto.PortInfo
+		expected bool
+	}{
+		{
+			name:     "nil PortInfo should be empty",
+			portInfo: nil,
+			expected: true,
+		},
+		{
+			name: "PortInfo with zero port should be empty",
+			portInfo: &mgmProto.PortInfo{
+				PortSelection: &mgmProto.PortInfo_Port{
+					Port: 0,
+				},
+			},
+			expected: true,
+		},
+		{
+			name: "PortInfo with valid port should not be empty",
+			portInfo: &mgmProto.PortInfo{
+				PortSelection: &mgmProto.PortInfo_Port{
+					Port: 80,
+				},
+			},
+			expected: false,
+		},
+		{
+			name: "PortInfo with nil range should be empty",
+			portInfo: &mgmProto.PortInfo{
+				PortSelection: &mgmProto.PortInfo_Range_{
+					Range: nil,
+				},
+			},
+			expected: true,
+		},
+		{
+			name: "PortInfo with zero start range should be empty",
+			portInfo: &mgmProto.PortInfo{
+				PortSelection: &mgmProto.PortInfo_Range_{
+					Range: &mgmProto.PortInfo_Range{
+						Start: 0,
+						End:   100,
+					},
+				},
+			},
+			expected: true,
+		},
+		{
+			name: "PortInfo with zero end range should be empty",
+			portInfo: &mgmProto.PortInfo{
+				PortSelection: &mgmProto.PortInfo_Range_{
+					Range: &mgmProto.PortInfo_Range{
+						Start: 80,
+						End:   0,
+					},
+				},
+			},
+			expected: true,
+		},
+		{
+			name: "PortInfo with valid range should not be empty",
+			portInfo: &mgmProto.PortInfo{
+				PortSelection: &mgmProto.PortInfo_Range_{
+					Range: &mgmProto.PortInfo_Range{
+						Start: 8080,
+						End:   8090,
+					},
+				},
+			},
+			expected: false,
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			result := portInfoEmpty(tt.portInfo)
+			assert.Equal(t, tt.expected, result)
+		})
+	}
+}
+
 func TestDefaultManagerEnableSSHRules(t *testing.T) {
 	networkMap := &mgmProto.NetworkMap{
 		PeerConfig: &mgmProto.PeerConfig{
diff --git a/client/internal/dns/handler_chain_test.go b/client/internal/dns/handler_chain_test.go
index 5f03e075..72c0004d 100644
--- a/client/internal/dns/handler_chain_test.go
+++ b/client/internal/dns/handler_chain_test.go
@@ -22,7 +22,7 @@ func TestHandlerChain_ServeDNS_Priorities(t *testing.T) {
 
 	// Setup handlers with different priorities
 	chain.AddHandler("example.com.", defaultHandler, nbdns.PriorityDefault)
-	chain.AddHandler("example.com.", matchDomainHandler, nbdns.PriorityMatchDomain)
+	chain.AddHandler("example.com.", matchDomainHandler, nbdns.PriorityUpstream)
 	chain.AddHandler("example.com.", dnsRouteHandler, nbdns.PriorityDNSRoute)
 
 	// Create test request
@@ -200,7 +200,7 @@ func TestHandlerChain_ServeDNS_OverlappingDomains(t *testing.T) {
 				priority int
 			}{
 				{pattern: "*.example.com.", priority: nbdns.PriorityDefault},
-				{pattern: "*.example.com.", priority: nbdns.PriorityMatchDomain},
+				{pattern: "*.example.com.", priority: nbdns.PriorityUpstream},
 				{pattern: "*.example.com.", priority: nbdns.PriorityDNSRoute},
 			},
 			queryDomain:     "test.example.com.",
@@ -214,7 +214,7 @@ func TestHandlerChain_ServeDNS_OverlappingDomains(t *testing.T) {
 				priority int
 			}{
 				{pattern: "*.example.com.", priority: nbdns.PriorityDefault},
-				{pattern: "test.example.com.", priority: nbdns.PriorityMatchDomain},
+				{pattern: "test.example.com.", priority: nbdns.PriorityUpstream},
 				{pattern: "*.test.example.com.", priority: nbdns.PriorityDNSRoute},
 			},
 			queryDomain:     "sub.test.example.com.",
@@ -281,7 +281,7 @@ func TestHandlerChain_ServeDNS_ChainContinuation(t *testing.T) {
 
 	// Add handlers in priority order
 	chain.AddHandler("example.com.", handler1, nbdns.PriorityDNSRoute)
-	chain.AddHandler("example.com.", handler2, nbdns.PriorityMatchDomain)
+	chain.AddHandler("example.com.", handler2, nbdns.PriorityUpstream)
 	chain.AddHandler("example.com.", handler3, nbdns.PriorityDefault)
 
 	// Create test request
@@ -344,13 +344,13 @@ func TestHandlerChain_PriorityDeregistration(t *testing.T) {
 				priority int
 			}{
 				{"add", "example.com.", nbdns.PriorityDNSRoute},
-				{"add", "example.com.", nbdns.PriorityMatchDomain},
+				{"add", "example.com.", nbdns.PriorityUpstream},
 				{"remove", "example.com.", nbdns.PriorityDNSRoute},
 			},
 			query: "example.com.",
 			expectedCalls: map[int]bool{
-				nbdns.PriorityDNSRoute:    false,
-				nbdns.PriorityMatchDomain: true,
+				nbdns.PriorityDNSRoute: false,
+				nbdns.PriorityUpstream: true,
 			},
 		},
 		{
@@ -361,13 +361,13 @@ func TestHandlerChain_PriorityDeregistration(t *testing.T) {
 				priority int
 			}{
 				{"add", "example.com.", nbdns.PriorityDNSRoute},
-				{"add", "example.com.", nbdns.PriorityMatchDomain},
-				{"remove", "example.com.", nbdns.PriorityMatchDomain},
+				{"add", "example.com.", nbdns.PriorityUpstream},
+				{"remove", "example.com.", nbdns.PriorityUpstream},
 			},
 			query: "example.com.",
 			expectedCalls: map[int]bool{
-				nbdns.PriorityDNSRoute:    true,
-				nbdns.PriorityMatchDomain: false,
+				nbdns.PriorityDNSRoute: true,
+				nbdns.PriorityUpstream: false,
 			},
 		},
 		{
@@ -378,16 +378,16 @@ func TestHandlerChain_PriorityDeregistration(t *testing.T) {
 				priority int
 			}{
 				{"add", "example.com.", nbdns.PriorityDNSRoute},
-				{"add", "example.com.", nbdns.PriorityMatchDomain},
+				{"add", "example.com.", nbdns.PriorityUpstream},
 				{"add", "example.com.", nbdns.PriorityDefault},
 				{"remove", "example.com.", nbdns.PriorityDNSRoute},
-				{"remove", "example.com.", nbdns.PriorityMatchDomain},
+				{"remove", "example.com.", nbdns.PriorityUpstream},
 			},
 			query: "example.com.",
 			expectedCalls: map[int]bool{
-				nbdns.PriorityDNSRoute:    false,
-				nbdns.PriorityMatchDomain: false,
-				nbdns.PriorityDefault:     true,
+				nbdns.PriorityDNSRoute: false,
+				nbdns.PriorityUpstream: false,
+				nbdns.PriorityDefault:  true,
 			},
 		},
 	}
@@ -454,7 +454,7 @@ func TestHandlerChain_MultiPriorityHandling(t *testing.T) {
 	// Add handlers in mixed order
 	chain.AddHandler(testDomain, defaultHandler, nbdns.PriorityDefault)
 	chain.AddHandler(testDomain, routeHandler, nbdns.PriorityDNSRoute)
-	chain.AddHandler(testDomain, matchHandler, nbdns.PriorityMatchDomain)
+	chain.AddHandler(testDomain, matchHandler, nbdns.PriorityUpstream)
 
 	// Test 1: Initial state
 	w1 := &nbdns.ResponseWriterChain{ResponseWriter: &test.MockResponseWriter{}}
@@ -490,7 +490,7 @@ func TestHandlerChain_MultiPriorityHandling(t *testing.T) {
 	defaultHandler.Calls = nil
 
 	// Test 3: Remove middle priority handler
-	chain.RemoveHandler(testDomain, nbdns.PriorityMatchDomain)
+	chain.RemoveHandler(testDomain, nbdns.PriorityUpstream)
 
 	w3 := &nbdns.ResponseWriterChain{ResponseWriter: &test.MockResponseWriter{}}
 	// Now lowest priority handler (defaultHandler) should be called
@@ -607,7 +607,7 @@ func TestHandlerChain_CaseSensitivity(t *testing.T) {
 				shouldMatch bool
 			}{
 				{"EXAMPLE.COM.", nbdns.PriorityDefault, false, false},
-				{"example.com.", nbdns.PriorityMatchDomain, false, false},
+				{"example.com.", nbdns.PriorityUpstream, false, false},
 				{"Example.Com.", nbdns.PriorityDNSRoute, false, true},
 			},
 			query:         "example.com.",
@@ -702,8 +702,8 @@ func TestHandlerChain_DomainSpecificityOrdering(t *testing.T) {
 				priority  int
 				subdomain bool
 			}{
-				{"add", "example.com.", nbdns.PriorityMatchDomain, true},
-				{"add", "sub.example.com.", nbdns.PriorityMatchDomain, false},
+				{"add", "example.com.", nbdns.PriorityUpstream, true},
+				{"add", "sub.example.com.", nbdns.PriorityUpstream, false},
 			},
 			query:         "sub.example.com.",
 			expectedMatch: "sub.example.com.",
@@ -717,8 +717,8 @@ func TestHandlerChain_DomainSpecificityOrdering(t *testing.T) {
 				priority  int
 				subdomain bool
 			}{
-				{"add", "example.com.", nbdns.PriorityMatchDomain, true},
-				{"add", "sub.example.com.", nbdns.PriorityMatchDomain, true},
+				{"add", "example.com.", nbdns.PriorityUpstream, true},
+				{"add", "sub.example.com.", nbdns.PriorityUpstream, true},
 			},
 			query:         "sub.example.com.",
 			expectedMatch: "sub.example.com.",
@@ -732,10 +732,10 @@ func TestHandlerChain_DomainSpecificityOrdering(t *testing.T) {
 				priority  int
 				subdomain bool
 			}{
-				{"add", "example.com.", nbdns.PriorityMatchDomain, true},
-				{"add", "sub.example.com.", nbdns.PriorityMatchDomain, true},
-				{"add", "test.sub.example.com.", nbdns.PriorityMatchDomain, false},
-				{"remove", "test.sub.example.com.", nbdns.PriorityMatchDomain, false},
+				{"add", "example.com.", nbdns.PriorityUpstream, true},
+				{"add", "sub.example.com.", nbdns.PriorityUpstream, true},
+				{"add", "test.sub.example.com.", nbdns.PriorityUpstream, false},
+				{"remove", "test.sub.example.com.", nbdns.PriorityUpstream, false},
 			},
 			query:         "test.sub.example.com.",
 			expectedMatch: "sub.example.com.",
@@ -749,7 +749,7 @@ func TestHandlerChain_DomainSpecificityOrdering(t *testing.T) {
 				priority  int
 				subdomain bool
 			}{
-				{"add", "sub.example.com.", nbdns.PriorityMatchDomain, false},
+				{"add", "sub.example.com.", nbdns.PriorityUpstream, false},
 				{"add", "example.com.", nbdns.PriorityDNSRoute, true},
 			},
 			query:         "sub.example.com.",
@@ -764,9 +764,9 @@ func TestHandlerChain_DomainSpecificityOrdering(t *testing.T) {
 				priority  int
 				subdomain bool
 			}{
-				{"add", "example.com.", nbdns.PriorityMatchDomain, true},
-				{"add", "other.example.com.", nbdns.PriorityMatchDomain, true},
-				{"add", "sub.example.com.", nbdns.PriorityMatchDomain, false},
+				{"add", "example.com.", nbdns.PriorityUpstream, true},
+				{"add", "other.example.com.", nbdns.PriorityUpstream, true},
+				{"add", "sub.example.com.", nbdns.PriorityUpstream, false},
 			},
 			query:         "sub.example.com.",
 			expectedMatch: "sub.example.com.",
diff --git a/client/internal/dns/server_test.go b/client/internal/dns/server_test.go
index e55b2791..1cf59fb5 100644
--- a/client/internal/dns/server_test.go
+++ b/client/internal/dns/server_test.go
@@ -164,12 +164,12 @@ func TestUpdateDNSServer(t *testing.T) {
 				generateDummyHandler("netbird.io", nameServers).ID(): handlerWrapper{
 					domain:   "netbird.io",
 					handler:  dummyHandler,
-					priority: PriorityMatchDomain,
+					priority: PriorityUpstream,
 				},
 				dummyHandler.ID(): handlerWrapper{
 					domain:   "netbird.cloud",
 					handler:  dummyHandler,
-					priority: PriorityMatchDomain,
+					priority: PriorityLocal,
 				},
 				generateDummyHandler(".", nameServers).ID(): handlerWrapper{
 					domain:   nbdns.RootZone,
@@ -186,7 +186,7 @@ func TestUpdateDNSServer(t *testing.T) {
 				generateDummyHandler(zoneRecords[0].Name, nameServers).ID(): handlerWrapper{
 					domain:   "netbird.cloud",
 					handler:  dummyHandler,
-					priority: PriorityMatchDomain,
+					priority: PriorityUpstream,
 				},
 			},
 			initSerial:  0,
@@ -210,12 +210,12 @@ func TestUpdateDNSServer(t *testing.T) {
 				generateDummyHandler("netbird.io", nameServers).ID(): handlerWrapper{
 					domain:   "netbird.io",
 					handler:  dummyHandler,
-					priority: PriorityMatchDomain,
+					priority: PriorityUpstream,
 				},
 				"local-resolver": handlerWrapper{
 					domain:   "netbird.cloud",
 					handler:  dummyHandler,
-					priority: PriorityMatchDomain,
+					priority: PriorityLocal,
 				},
 			},
 			expectedLocalQs: []dns.Question{{Name: zoneRecords[0].Name, Qtype: 1, Qclass: 1}},
@@ -305,7 +305,7 @@ func TestUpdateDNSServer(t *testing.T) {
 				generateDummyHandler(zoneRecords[0].Name, nameServers).ID(): handlerWrapper{
 					domain:   zoneRecords[0].Name,
 					handler:  dummyHandler,
-					priority: PriorityMatchDomain,
+					priority: PriorityUpstream,
 				},
 			},
 			initSerial:          0,
@@ -321,7 +321,7 @@ func TestUpdateDNSServer(t *testing.T) {
 				generateDummyHandler(zoneRecords[0].Name, nameServers).ID(): handlerWrapper{
 					domain:   zoneRecords[0].Name,
 					handler:  dummyHandler,
-					priority: PriorityMatchDomain,
+					priority: PriorityUpstream,
 				},
 			},
 			initSerial:          0,
@@ -495,7 +495,7 @@ func TestDNSFakeResolverHandleUpdates(t *testing.T) {
 		"id1": handlerWrapper{
 			domain:   zoneRecords[0].Name,
 			handler:  &local.Resolver{},
-			priority: PriorityMatchDomain,
+			priority: PriorityUpstream,
 		},
 	}
 	//dnsServer.localResolver.RegisteredMap = local.RegistrationMap{local.BuildRecordKey("netbird.cloud", dns.ClassINET, dns.TypeA): struct{}{}}
@@ -978,7 +978,7 @@ func TestHandlerChain_DomainPriorities(t *testing.T) {
 	}
 
 	chain.AddHandler("example.com.", dnsRouteHandler, PriorityDNSRoute)
-	chain.AddHandler("example.com.", upstreamHandler, PriorityMatchDomain)
+	chain.AddHandler("example.com.", upstreamHandler, PriorityUpstream)
 
 	testCases := []struct {
 		name            string
@@ -1059,14 +1059,14 @@ func TestDefaultServer_UpdateMux(t *testing.T) {
 			handler: &mockHandler{
 				Id: "upstream-group1",
 			},
-			priority: PriorityMatchDomain,
+			priority: PriorityUpstream,
 		},
 		"upstream-group2": {
 			domain: "example.com",
 			handler: &mockHandler{
 				Id: "upstream-group2",
 			},
-			priority: PriorityMatchDomain - 1,
+			priority: PriorityUpstream - 1,
 		},
 	}
 
@@ -1093,21 +1093,21 @@ func TestDefaultServer_UpdateMux(t *testing.T) {
 			handler: &mockHandler{
 				Id: "upstream-group1",
 			},
-			priority: PriorityMatchDomain,
+			priority: PriorityUpstream,
 		},
 		"upstream-group2": {
 			domain: "example.com",
 			handler: &mockHandler{
 				Id: "upstream-group2",
 			},
-			priority: PriorityMatchDomain - 1,
+			priority: PriorityUpstream - 1,
 		},
 		"upstream-other": {
 			domain: "other.com",
 			handler: &mockHandler{
 				Id: "upstream-other",
 			},
-			priority: PriorityMatchDomain,
+			priority: PriorityUpstream,
 		},
 	}
 
@@ -1128,7 +1128,7 @@ func TestDefaultServer_UpdateMux(t *testing.T) {
 					handler: &mockHandler{
 						Id: "upstream-group2",
 					},
-					priority: PriorityMatchDomain - 1,
+					priority: PriorityUpstream - 1,
 				},
 			},
 			expectedHandlers: map[string]string{
@@ -1146,7 +1146,7 @@ func TestDefaultServer_UpdateMux(t *testing.T) {
 					handler: &mockHandler{
 						Id: "upstream-group1",
 					},
-					priority: PriorityMatchDomain,
+					priority: PriorityUpstream,
 				},
 			},
 			expectedHandlers: map[string]string{
@@ -1164,7 +1164,7 @@ func TestDefaultServer_UpdateMux(t *testing.T) {
 					handler: &mockHandler{
 						Id: "upstream-group3",
 					},
-					priority: PriorityMatchDomain + 1,
+					priority: PriorityUpstream + 1,
 				},
 				// Keep existing groups with their original priorities
 				{
@@ -1172,14 +1172,14 @@ func TestDefaultServer_UpdateMux(t *testing.T) {
 					handler: &mockHandler{
 						Id: "upstream-group1",
 					},
-					priority: PriorityMatchDomain,
+					priority: PriorityUpstream,
 				},
 				{
 					domain: "example.com",
 					handler: &mockHandler{
 						Id: "upstream-group2",
 					},
-					priority: PriorityMatchDomain - 1,
+					priority: PriorityUpstream - 1,
 				},
 			},
 			expectedHandlers: map[string]string{
@@ -1199,14 +1199,14 @@ func TestDefaultServer_UpdateMux(t *testing.T) {
 					handler: &mockHandler{
 						Id: "upstream-group1",
 					},
-					priority: PriorityMatchDomain,
+					priority: PriorityUpstream,
 				},
 				{
 					domain: "example.com",
 					handler: &mockHandler{
 						Id: "upstream-group2",
 					},
-					priority: PriorityMatchDomain - 1,
+					priority: PriorityUpstream - 1,
 				},
 				// Add group3 with lowest priority
 				{
@@ -1214,7 +1214,7 @@ func TestDefaultServer_UpdateMux(t *testing.T) {
 					handler: &mockHandler{
 						Id: "upstream-group3",
 					},
-					priority: PriorityMatchDomain - 2,
+					priority: PriorityUpstream - 2,
 				},
 			},
 			expectedHandlers: map[string]string{
@@ -1335,14 +1335,14 @@ func TestDefaultServer_UpdateMux(t *testing.T) {
 					handler: &mockHandler{
 						Id: "upstream-group1",
 					},
-					priority: PriorityMatchDomain,
+					priority: PriorityUpstream,
 				},
 				{
 					domain: "other.com",
 					handler: &mockHandler{
 						Id: "upstream-other",
 					},
-					priority: PriorityMatchDomain,
+					priority: PriorityUpstream,
 				},
 			},
 			expectedHandlers: map[string]string{
@@ -1360,28 +1360,28 @@ func TestDefaultServer_UpdateMux(t *testing.T) {
 					handler: &mockHandler{
 						Id: "upstream-group1",
 					},
-					priority: PriorityMatchDomain,
+					priority: PriorityUpstream,
 				},
 				{
 					domain: "example.com",
 					handler: &mockHandler{
 						Id: "upstream-group2",
 					},
-					priority: PriorityMatchDomain - 1,
+					priority: PriorityUpstream - 1,
 				},
 				{
 					domain: "other.com",
 					handler: &mockHandler{
 						Id: "upstream-other",
 					},
-					priority: PriorityMatchDomain,
+					priority: PriorityUpstream,
 				},
 				{
 					domain: "new.com",
 					handler: &mockHandler{
 						Id: "upstream-new",
 					},
-					priority: PriorityMatchDomain,
+					priority: PriorityUpstream,
 				},
 			},
 			expectedHandlers: map[string]string{
@@ -1791,14 +1791,14 @@ func TestExtraDomainsRefCounting(t *testing.T) {
 
 	// Register domains from different handlers with same domain
 	server.RegisterHandler(domain.List{"*.shared.example.com"}, &MockHandler{}, PriorityDNSRoute)
-	server.RegisterHandler(domain.List{"shared.example.com."}, &MockHandler{}, PriorityMatchDomain)
+	server.RegisterHandler(domain.List{"shared.example.com."}, &MockHandler{}, PriorityUpstream)
 
 	// Verify refcount is 2
 	zoneKey := toZone("shared.example.com")
 	assert.Equal(t, 2, server.extraDomains[zoneKey], "Refcount should be 2 after registering same domain twice")
 
 	// Deregister one handler
-	server.DeregisterHandler(domain.List{"shared.example.com"}, PriorityMatchDomain)
+	server.DeregisterHandler(domain.List{"shared.example.com"}, PriorityUpstream)
 
 	// Verify refcount is 1
 	assert.Equal(t, 1, server.extraDomains[zoneKey], "Refcount should be 1 after deregistering one handler")
@@ -1925,7 +1925,7 @@ func TestDomainCaseHandling(t *testing.T) {
 	}
 
 	server.RegisterHandler(domain.List{"MIXED.example.com"}, &MockHandler{}, PriorityDefault)
-	server.RegisterHandler(domain.List{"mixed.EXAMPLE.com"}, &MockHandler{}, PriorityMatchDomain)
+	server.RegisterHandler(domain.List{"mixed.EXAMPLE.com"}, &MockHandler{}, PriorityUpstream)
 
 	assert.Equal(t, 1, len(server.extraDomains), "Case differences should be normalized")
 
@@ -1945,3 +1945,111 @@ func TestDomainCaseHandling(t *testing.T) {
 	assert.Contains(t, domains, "config.example.com.", "Mixed case domain should be normalized and pre.sent")
 	assert.Contains(t, domains, "mixed.example.com.", "Mixed case domain should be normalized and present")
 }
+
+func TestLocalResolverPriorityInServer(t *testing.T) {
+	server := &DefaultServer{
+		ctx:           context.Background(),
+		wgInterface:   &mocWGIface{},
+		handlerChain:  NewHandlerChain(),
+		localResolver: local.NewResolver(),
+		service:       &mockService{},
+		extraDomains:  make(map[domain.Domain]int),
+	}
+
+	config := nbdns.Config{
+		ServiceEnable: true,
+		CustomZones: []nbdns.CustomZone{
+			{
+				Domain: "local.example.com",
+				Records: []nbdns.SimpleRecord{
+					{
+						Name:  "test.local.example.com",
+						Type:  int(dns.TypeA),
+						Class: nbdns.DefaultClass,
+						TTL:   300,
+						RData: "192.168.1.100",
+					},
+				},
+			},
+		},
+		NameServerGroups: []*nbdns.NameServerGroup{
+			{
+				Domains: []string{"local.example.com"}, // Same domain as local records
+				NameServers: []nbdns.NameServer{
+					{
+						IP:     netip.MustParseAddr("8.8.8.8"),
+						NSType: nbdns.UDPNameServerType,
+						Port:   53,
+					},
+				},
+			},
+		},
+	}
+
+	localMuxUpdates, _, err := server.buildLocalHandlerUpdate(config.CustomZones)
+	assert.NoError(t, err)
+
+	upstreamMuxUpdates, err := server.buildUpstreamHandlerUpdate(config.NameServerGroups)
+	assert.NoError(t, err)
+
+	// Verify that local handler has higher priority than upstream for same domain
+	var localPriority, upstreamPriority int
+	localFound, upstreamFound := false, false
+
+	for _, update := range localMuxUpdates {
+		if update.domain == "local.example.com" {
+			localPriority = update.priority
+			localFound = true
+		}
+	}
+
+	for _, update := range upstreamMuxUpdates {
+		if update.domain == "local.example.com" {
+			upstreamPriority = update.priority
+			upstreamFound = true
+		}
+	}
+
+	assert.True(t, localFound, "Local handler should be found")
+	assert.True(t, upstreamFound, "Upstream handler should be found")
+	assert.Greater(t, localPriority, upstreamPriority,
+		"Local handler priority (%d) should be higher than upstream priority (%d)",
+		localPriority, upstreamPriority)
+	assert.Equal(t, PriorityLocal, localPriority, "Local handler should use PriorityLocal")
+	assert.Equal(t, PriorityUpstream, upstreamPriority, "Upstream handler should use PriorityUpstream")
+}
+
+func TestLocalResolverPriorityConstants(t *testing.T) {
+	// Test that priority constants are ordered correctly
+	assert.Greater(t, PriorityLocal, PriorityDNSRoute, "Local priority should be higher than DNS route")
+	assert.Greater(t, PriorityLocal, PriorityUpstream, "Local priority should be higher than upstream")
+	assert.Greater(t, PriorityUpstream, PriorityDefault, "Upstream priority should be higher than default")
+
+	// Test that local resolver uses the correct priority
+	server := &DefaultServer{
+		localResolver: local.NewResolver(),
+	}
+
+	config := nbdns.Config{
+		CustomZones: []nbdns.CustomZone{
+			{
+				Domain: "local.example.com",
+				Records: []nbdns.SimpleRecord{
+					{
+						Name:  "test.local.example.com",
+						Type:  int(dns.TypeA),
+						Class: nbdns.DefaultClass,
+						TTL:   300,
+						RData: "192.168.1.100",
+					},
+				},
+			},
+		},
+	}
+
+	localMuxUpdates, _, err := server.buildLocalHandlerUpdate(config.CustomZones)
+	assert.NoError(t, err)
+	assert.Len(t, localMuxUpdates, 1)
+	assert.Equal(t, PriorityLocal, localMuxUpdates[0].priority, "Local handler should use PriorityLocal")
+	assert.Equal(t, "local.example.com", localMuxUpdates[0].domain)
+}
diff --git a/client/internal/dnsfwd/forwarder_test.go b/client/internal/dnsfwd/forwarder_test.go
index f0829bbb..d8228c73 100644
--- a/client/internal/dnsfwd/forwarder_test.go
+++ b/client/internal/dnsfwd/forwarder_test.go
@@ -1,11 +1,21 @@
 package dnsfwd
 
 import (
+	"context"
+	"fmt"
+	"net/netip"
+	"strings"
 	"testing"
+	"time"
 
+	"github.com/miekg/dns"
 	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/mock"
 	"github.com/stretchr/testify/require"
 
+	firewall "github.com/netbirdio/netbird/client/firewall/manager"
+	"github.com/netbirdio/netbird/client/internal/dns/test"
+	"github.com/netbirdio/netbird/client/internal/peer"
 	"github.com/netbirdio/netbird/management/domain"
 	"github.com/netbirdio/netbird/route"
 )
@@ -13,7 +23,7 @@ import (
 func Test_getMatchingEntries(t *testing.T) {
 	testCases := []struct {
 		name           string
-		storedMappings map[string]route.ResID // key: domain pattern, value: resId
+		storedMappings map[string]route.ResID
 		queryDomain    string
 		expectedResId  route.ResID
 	}{
@@ -44,7 +54,7 @@ func Test_getMatchingEntries(t *testing.T) {
 		{
 			name:           "Wildcard pattern does not match different domain",
 			storedMappings: map[string]route.ResID{"*.example.com": "res4"},
-			queryDomain:    "foo.notexample.com",
+			queryDomain:    "foo.example.org",
 			expectedResId:  "",
 		},
 		{
@@ -101,3 +111,619 @@ func Test_getMatchingEntries(t *testing.T) {
 		})
 	}
 }
+
+type MockFirewall struct {
+	mock.Mock
+}
+
+func (m *MockFirewall) UpdateSet(set firewall.Set, prefixes []netip.Prefix) error {
+	args := m.Called(set, prefixes)
+	return args.Error(0)
+}
+
+type MockResolver struct {
+	mock.Mock
+}
+
+func (m *MockResolver) LookupNetIP(ctx context.Context, network, host string) ([]netip.Addr, error) {
+	args := m.Called(ctx, network, host)
+	return args.Get(0).([]netip.Addr), args.Error(1)
+}
+
+func TestDNSForwarder_SubdomainAccessLogic(t *testing.T) {
+	tests := []struct {
+		name             string
+		configuredDomain string
+		queryDomain      string
+		shouldMatch      bool
+		expectedResID    route.ResID
+		description      string
+	}{
+		{
+			name:             "exact domain match should be allowed",
+			configuredDomain: "example.com",
+			queryDomain:      "example.com",
+			shouldMatch:      true,
+			expectedResID:    "test-res-id",
+			description:      "Direct match to configured domain should work",
+		},
+		{
+			name:             "subdomain access should be restricted",
+			configuredDomain: "example.com",
+			queryDomain:      "mail.example.com",
+			shouldMatch:      false,
+			expectedResID:    "",
+			description:      "Subdomain should not be accessible unless explicitly configured",
+		},
+		{
+			name:             "wildcard should allow subdomains",
+			configuredDomain: "*.example.com",
+			queryDomain:      "mail.example.com",
+			shouldMatch:      true,
+			expectedResID:    "test-res-id",
+			description:      "Wildcard domains should allow subdomain access",
+		},
+		{
+			name:             "wildcard should allow base domain",
+			configuredDomain: "*.example.com",
+			queryDomain:      "example.com",
+			shouldMatch:      true,
+			expectedResID:    "test-res-id",
+			description:      "Wildcard should also match the base domain",
+		},
+		{
+			name:             "deep subdomain should be restricted",
+			configuredDomain: "example.com",
+			queryDomain:      "deep.mail.example.com",
+			shouldMatch:      false,
+			expectedResID:    "",
+			description:      "Deep subdomains should not be accessible",
+		},
+		{
+			name:             "wildcard allows deep subdomains",
+			configuredDomain: "*.example.com",
+			queryDomain:      "deep.mail.example.com",
+			shouldMatch:      true,
+			expectedResID:    "test-res-id",
+			description:      "Wildcard should allow deep subdomains",
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			forwarder := &DNSForwarder{}
+
+			d, err := domain.FromString(tt.configuredDomain)
+			require.NoError(t, err)
+
+			entries := []*ForwarderEntry{
+				{
+					Domain: d,
+					ResID:  "test-res-id",
+				},
+			}
+
+			forwarder.UpdateDomains(entries)
+
+			resID, matchingEntries := forwarder.getMatchingEntries(tt.queryDomain)
+
+			if tt.shouldMatch {
+				assert.Equal(t, tt.expectedResID, resID, "Expected matching ResID")
+				assert.NotEmpty(t, matchingEntries, "Expected matching entries")
+				t.Logf("✓ Domain %s correctly matches pattern %s", tt.queryDomain, tt.configuredDomain)
+			} else {
+				assert.Equal(t, tt.expectedResID, resID, "Expected no ResID match")
+				assert.Empty(t, matchingEntries, "Expected no matching entries")
+				t.Logf("✓ Domain %s correctly does NOT match pattern %s", tt.queryDomain, tt.configuredDomain)
+			}
+		})
+	}
+}
+
+func TestDNSForwarder_UnauthorizedDomainAccess(t *testing.T) {
+	if testing.Short() {
+		t.Skip("Skipping integration test in short mode")
+	}
+
+	tests := []struct {
+		name             string
+		configuredDomain string
+		queryDomain      string
+		shouldResolve    bool
+		description      string
+	}{
+		{
+			name:             "configured exact domain resolves",
+			configuredDomain: "example.com",
+			queryDomain:      "example.com",
+			shouldResolve:    true,
+			description:      "Exact match should resolve",
+		},
+		{
+			name:             "unauthorized subdomain blocked",
+			configuredDomain: "example.com",
+			queryDomain:      "mail.example.com",
+			shouldResolve:    false,
+			description:      "Subdomain should be blocked without wildcard",
+		},
+		{
+			name:             "wildcard allows subdomain",
+			configuredDomain: "*.example.com",
+			queryDomain:      "mail.example.com",
+			shouldResolve:    true,
+			description:      "Wildcard should allow subdomain",
+		},
+		{
+			name:             "wildcard allows base domain",
+			configuredDomain: "*.example.com",
+			queryDomain:      "example.com",
+			shouldResolve:    true,
+			description:      "Wildcard should allow base domain",
+		},
+		{
+			name:             "unrelated domain blocked",
+			configuredDomain: "example.com",
+			queryDomain:      "example.org",
+			shouldResolve:    false,
+			description:      "Unrelated domain should be blocked",
+		},
+		{
+			name:             "deep subdomain blocked",
+			configuredDomain: "example.com",
+			queryDomain:      "deep.mail.example.com",
+			shouldResolve:    false,
+			description:      "Deep subdomain should be blocked",
+		},
+		{
+			name:             "wildcard allows deep subdomain",
+			configuredDomain: "*.example.com",
+			queryDomain:      "deep.mail.example.com",
+			shouldResolve:    true,
+			description:      "Wildcard should allow deep subdomain",
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			mockFirewall := &MockFirewall{}
+			mockResolver := &MockResolver{}
+
+			if tt.shouldResolve {
+				mockFirewall.On("UpdateSet", mock.AnythingOfType("manager.Set"), mock.AnythingOfType("[]netip.Prefix")).Return(nil)
+
+				// Mock successful DNS resolution
+				fakeIP := netip.MustParseAddr("1.2.3.4")
+				mockResolver.On("LookupNetIP", mock.Anything, "ip4", dns.Fqdn(tt.queryDomain)).Return([]netip.Addr{fakeIP}, nil)
+			}
+
+			forwarder := NewDNSForwarder("127.0.0.1:0", 300, mockFirewall, &peer.Status{})
+			forwarder.resolver = mockResolver
+
+			d, err := domain.FromString(tt.configuredDomain)
+			require.NoError(t, err)
+
+			entries := []*ForwarderEntry{
+				{
+					Domain: d,
+					ResID:  "test-res-id",
+					Set:    firewall.NewDomainSet([]domain.Domain{d}),
+				},
+			}
+
+			forwarder.UpdateDomains(entries)
+
+			query := &dns.Msg{}
+			query.SetQuestion(dns.Fqdn(tt.queryDomain), dns.TypeA)
+
+			mockWriter := &test.MockResponseWriter{}
+			resp := forwarder.handleDNSQuery(mockWriter, query)
+
+			if tt.shouldResolve {
+				require.NotNil(t, resp, "Expected response for authorized domain")
+				require.Equal(t, dns.RcodeSuccess, resp.Rcode, "Expected successful response")
+				assert.NotEmpty(t, resp.Answer, "Expected DNS answer records")
+
+				time.Sleep(10 * time.Millisecond)
+				mockFirewall.AssertExpectations(t)
+				mockResolver.AssertExpectations(t)
+			} else {
+				if resp != nil {
+					assert.True(t, len(resp.Answer) == 0 || resp.Rcode != dns.RcodeSuccess,
+						"Unauthorized domain should not return successful answers")
+				}
+				mockFirewall.AssertNotCalled(t, "UpdateSet")
+				mockResolver.AssertNotCalled(t, "LookupNetIP")
+			}
+		})
+	}
+}
+
+func TestDNSForwarder_FirewallSetUpdates(t *testing.T) {
+	tests := []struct {
+		name              string
+		configuredDomains []string
+		query             string
+		mockIP            string
+		shouldResolve     bool
+		expectedSetCount  int // How many sets should be updated
+		description       string
+	}{
+		{
+			name:              "exact domain gets firewall update",
+			configuredDomains: []string{"example.com"},
+			query:             "example.com",
+			mockIP:            "1.1.1.1",
+			shouldResolve:     true,
+			expectedSetCount:  1,
+			description:       "Single exact match updates one set",
+		},
+		{
+			name:              "wildcard domain gets firewall update",
+			configuredDomains: []string{"*.example.com"},
+			query:             "mail.example.com",
+			mockIP:            "1.1.1.2",
+			shouldResolve:     true,
+			expectedSetCount:  1,
+			description:       "Wildcard match updates one set",
+		},
+		{
+			name:              "overlapping exact and wildcard both get updates",
+			configuredDomains: []string{"*.example.com", "mail.example.com"},
+			query:             "mail.example.com",
+			mockIP:            "1.1.1.3",
+			shouldResolve:     true,
+			expectedSetCount:  2,
+			description:       "Both exact and wildcard sets should be updated",
+		},
+		{
+			name:              "unauthorized domain gets no firewall update",
+			configuredDomains: []string{"example.com"},
+			query:             "mail.example.com",
+			mockIP:            "1.1.1.4",
+			shouldResolve:     false,
+			expectedSetCount:  0,
+			description:       "No firewall update for unauthorized domains",
+		},
+		{
+			name:              "multiple wildcards matching get all updated",
+			configuredDomains: []string{"*.example.com", "*.sub.example.com"},
+			query:             "test.sub.example.com",
+			mockIP:            "1.1.1.5",
+			shouldResolve:     true,
+			expectedSetCount:  2,
+			description:       "All matching wildcard sets should be updated",
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			mockFirewall := &MockFirewall{}
+			mockResolver := &MockResolver{}
+
+			// Set up forwarder
+			forwarder := NewDNSForwarder("127.0.0.1:0", 300, mockFirewall, &peer.Status{})
+			forwarder.resolver = mockResolver
+
+			// Create entries and track sets
+			var entries []*ForwarderEntry
+			sets := make([]firewall.Set, 0)
+
+			for i, configDomain := range tt.configuredDomains {
+				d, err := domain.FromString(configDomain)
+				require.NoError(t, err)
+
+				set := firewall.NewDomainSet([]domain.Domain{d})
+				sets = append(sets, set)
+
+				entries = append(entries, &ForwarderEntry{
+					Domain: d,
+					ResID:  route.ResID(fmt.Sprintf("res-%d", i)),
+					Set:    set,
+				})
+			}
+
+			forwarder.UpdateDomains(entries)
+
+			// Set up mocks
+			if tt.shouldResolve {
+				fakeIP := netip.MustParseAddr(tt.mockIP)
+				mockResolver.On("LookupNetIP", mock.Anything, "ip4", dns.Fqdn(tt.query)).
+					Return([]netip.Addr{fakeIP}, nil).Once()
+
+				expectedPrefixes := []netip.Prefix{netip.PrefixFrom(fakeIP, 32)}
+
+				// Count how many sets should actually match
+				updateCount := 0
+				for i, entry := range entries {
+					domain := strings.ToLower(tt.query)
+					pattern := entry.Domain.PunycodeString()
+
+					matches := false
+					if strings.HasPrefix(pattern, "*.") {
+						baseDomain := strings.TrimPrefix(pattern, "*.")
+						if domain == baseDomain || strings.HasSuffix(domain, "."+baseDomain) {
+							matches = true
+						}
+					} else if domain == pattern {
+						matches = true
+					}
+
+					if matches {
+						mockFirewall.On("UpdateSet", sets[i], expectedPrefixes).Return(nil).Once()
+						updateCount++
+					}
+				}
+
+				assert.Equal(t, tt.expectedSetCount, updateCount,
+					"Expected %d sets to be updated, but mock expects %d",
+					tt.expectedSetCount, updateCount)
+			}
+
+			// Execute query
+			dnsQuery := &dns.Msg{}
+			dnsQuery.SetQuestion(dns.Fqdn(tt.query), dns.TypeA)
+
+			mockWriter := &test.MockResponseWriter{}
+			resp := forwarder.handleDNSQuery(mockWriter, dnsQuery)
+
+			// Verify response
+			if tt.shouldResolve {
+				require.NotNil(t, resp, "Expected response for authorized domain")
+				require.Equal(t, dns.RcodeSuccess, resp.Rcode)
+				require.NotEmpty(t, resp.Answer)
+			} else if resp != nil {
+				assert.True(t, resp.Rcode == dns.RcodeRefused || len(resp.Answer) == 0,
+					"Unauthorized domain should be refused or have no answers")
+			}
+
+			// Verify all mock expectations were met
+			mockFirewall.AssertExpectations(t)
+			mockResolver.AssertExpectations(t)
+		})
+	}
+}
+
+// Test to verify that multiple IPs for one domain result in all prefixes being sent together
+func TestDNSForwarder_MultipleIPsInSingleUpdate(t *testing.T) {
+	mockFirewall := &MockFirewall{}
+	mockResolver := &MockResolver{}
+
+	forwarder := NewDNSForwarder("127.0.0.1:0", 300, mockFirewall, &peer.Status{})
+	forwarder.resolver = mockResolver
+
+	// Configure a single domain
+	d, err := domain.FromString("example.com")
+	require.NoError(t, err)
+
+	set := firewall.NewDomainSet([]domain.Domain{d})
+	entries := []*ForwarderEntry{{
+		Domain: d,
+		ResID:  "test-res",
+		Set:    set,
+	}}
+
+	forwarder.UpdateDomains(entries)
+
+	// Mock resolver returns multiple IPs
+	ips := []netip.Addr{
+		netip.MustParseAddr("1.1.1.1"),
+		netip.MustParseAddr("1.1.1.2"),
+		netip.MustParseAddr("1.1.1.3"),
+	}
+	mockResolver.On("LookupNetIP", mock.Anything, "ip4", "example.com.").
+		Return(ips, nil).Once()
+
+	// Expect ONE UpdateSet call with ALL prefixes
+	expectedPrefixes := []netip.Prefix{
+		netip.PrefixFrom(ips[0], 32),
+		netip.PrefixFrom(ips[1], 32),
+		netip.PrefixFrom(ips[2], 32),
+	}
+	mockFirewall.On("UpdateSet", set, expectedPrefixes).Return(nil).Once()
+
+	// Execute query
+	query := &dns.Msg{}
+	query.SetQuestion("example.com.", dns.TypeA)
+
+	mockWriter := &test.MockResponseWriter{}
+	resp := forwarder.handleDNSQuery(mockWriter, query)
+
+	// Verify response contains all IPs
+	require.NotNil(t, resp)
+	require.Equal(t, dns.RcodeSuccess, resp.Rcode)
+	require.Len(t, resp.Answer, 3, "Should have 3 answer records")
+
+	// Verify mocks
+	mockFirewall.AssertExpectations(t)
+	mockResolver.AssertExpectations(t)
+}
+
+func TestDNSForwarder_ResponseCodes(t *testing.T) {
+	tests := []struct {
+		name         string
+		queryType    uint16
+		queryDomain  string
+		configured   string
+		expectedCode int
+		description  string
+	}{
+		{
+			name:         "unauthorized domain returns REFUSED",
+			queryType:    dns.TypeA,
+			queryDomain:  "evil.com",
+			configured:   "example.com",
+			expectedCode: dns.RcodeRefused,
+			description:  "RFC compliant REFUSED for unauthorized queries",
+		},
+		{
+			name:         "unsupported query type returns NOTIMP",
+			queryType:    dns.TypeMX,
+			queryDomain:  "example.com",
+			configured:   "example.com",
+			expectedCode: dns.RcodeNotImplemented,
+			description:  "RFC compliant NOTIMP for unsupported types",
+		},
+		{
+			name:         "CNAME query returns NOTIMP",
+			queryType:    dns.TypeCNAME,
+			queryDomain:  "example.com",
+			configured:   "example.com",
+			expectedCode: dns.RcodeNotImplemented,
+			description:  "CNAME queries not supported",
+		},
+		{
+			name:         "TXT query returns NOTIMP",
+			queryType:    dns.TypeTXT,
+			queryDomain:  "example.com",
+			configured:   "example.com",
+			expectedCode: dns.RcodeNotImplemented,
+			description:  "TXT queries not supported",
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			forwarder := NewDNSForwarder("127.0.0.1:0", 300, nil, &peer.Status{})
+
+			d, err := domain.FromString(tt.configured)
+			require.NoError(t, err)
+
+			entries := []*ForwarderEntry{{Domain: d, ResID: "test-res"}}
+			forwarder.UpdateDomains(entries)
+
+			query := &dns.Msg{}
+			query.SetQuestion(dns.Fqdn(tt.queryDomain), tt.queryType)
+
+			// Capture the written response
+			var writtenResp *dns.Msg
+			mockWriter := &test.MockResponseWriter{
+				WriteMsgFunc: func(m *dns.Msg) error {
+					writtenResp = m
+					return nil
+				},
+			}
+
+			_ = forwarder.handleDNSQuery(mockWriter, query)
+
+			// Check the response written to the writer
+			require.NotNil(t, writtenResp, "Expected response to be written")
+			assert.Equal(t, tt.expectedCode, writtenResp.Rcode, tt.description)
+		})
+	}
+}
+
+func TestDNSForwarder_TCPTruncation(t *testing.T) {
+	// Test that large UDP responses are truncated with TC bit set
+	mockResolver := &MockResolver{}
+	forwarder := NewDNSForwarder("127.0.0.1:0", 300, nil, &peer.Status{})
+	forwarder.resolver = mockResolver
+
+	d, _ := domain.FromString("example.com")
+	entries := []*ForwarderEntry{{Domain: d, ResID: "test-res"}}
+	forwarder.UpdateDomains(entries)
+
+	// Mock many IPs to create a large response
+	var manyIPs []netip.Addr
+	for i := 0; i < 100; i++ {
+		manyIPs = append(manyIPs, netip.MustParseAddr(fmt.Sprintf("1.1.1.%d", i%256)))
+	}
+	mockResolver.On("LookupNetIP", mock.Anything, "ip4", "example.com.").Return(manyIPs, nil)
+
+	// Query without EDNS0
+	query := &dns.Msg{}
+	query.SetQuestion("example.com.", dns.TypeA)
+
+	var writtenResp *dns.Msg
+	mockWriter := &test.MockResponseWriter{
+		WriteMsgFunc: func(m *dns.Msg) error {
+			writtenResp = m
+			return nil
+		},
+	}
+	forwarder.handleDNSQueryUDP(mockWriter, query)
+
+	require.NotNil(t, writtenResp)
+	assert.True(t, writtenResp.Truncated, "Large response should be truncated")
+	assert.LessOrEqual(t, writtenResp.Len(), dns.MinMsgSize, "Response should fit in minimum UDP size")
+}
+
+func TestDNSForwarder_MultipleOverlappingPatterns(t *testing.T) {
+	// Test complex overlapping pattern scenarios
+	mockFirewall := &MockFirewall{}
+	mockResolver := &MockResolver{}
+
+	forwarder := NewDNSForwarder("127.0.0.1:0", 300, mockFirewall, &peer.Status{})
+	forwarder.resolver = mockResolver
+
+	// Set up complex overlapping patterns
+	patterns := []string{
+		"*.example.com",         // Matches all subdomains
+		"*.mail.example.com",    // More specific wildcard
+		"smtp.mail.example.com", // Exact match
+		"example.com",           // Base domain
+	}
+
+	var entries []*ForwarderEntry
+	sets := make(map[string]firewall.Set)
+
+	for _, pattern := range patterns {
+		d, _ := domain.FromString(pattern)
+		set := firewall.NewDomainSet([]domain.Domain{d})
+		sets[pattern] = set
+		entries = append(entries, &ForwarderEntry{
+			Domain: d,
+			ResID:  route.ResID("res-" + pattern),
+			Set:    set,
+		})
+	}
+
+	forwarder.UpdateDomains(entries)
+
+	// Test smtp.mail.example.com - should match 3 patterns
+	fakeIP := netip.MustParseAddr("1.2.3.4")
+	mockResolver.On("LookupNetIP", mock.Anything, "ip4", "smtp.mail.example.com.").Return([]netip.Addr{fakeIP}, nil)
+
+	expectedPrefix := netip.PrefixFrom(fakeIP, 32)
+	// All three matching patterns should get firewall updates
+	mockFirewall.On("UpdateSet", sets["smtp.mail.example.com"], []netip.Prefix{expectedPrefix}).Return(nil)
+	mockFirewall.On("UpdateSet", sets["*.mail.example.com"], []netip.Prefix{expectedPrefix}).Return(nil)
+	mockFirewall.On("UpdateSet", sets["*.example.com"], []netip.Prefix{expectedPrefix}).Return(nil)
+
+	query := &dns.Msg{}
+	query.SetQuestion("smtp.mail.example.com.", dns.TypeA)
+
+	mockWriter := &test.MockResponseWriter{}
+	resp := forwarder.handleDNSQuery(mockWriter, query)
+
+	require.NotNil(t, resp)
+	assert.Equal(t, dns.RcodeSuccess, resp.Rcode)
+
+	// Verify all three sets were updated
+	mockFirewall.AssertExpectations(t)
+
+	// Verify the most specific ResID was selected
+	// (exact match should win over wildcards)
+	resID, matches := forwarder.getMatchingEntries("smtp.mail.example.com")
+	assert.Equal(t, route.ResID("res-smtp.mail.example.com"), resID)
+	assert.Len(t, matches, 3, "Should match 3 patterns")
+}
+
+func TestDNSForwarder_EmptyQuery(t *testing.T) {
+	// Test handling of malformed query with no questions
+	forwarder := NewDNSForwarder("127.0.0.1:0", 300, nil, &peer.Status{})
+
+	query := &dns.Msg{}
+	// Don't set any question
+
+	writeCalled := false
+	mockWriter := &test.MockResponseWriter{
+		WriteMsgFunc: func(m *dns.Msg) error {
+			writeCalled = true
+			return nil
+		},
+	}
+	resp := forwarder.handleDNSQuery(mockWriter, query)
+
+	assert.Nil(t, resp, "Should return nil for empty query")
+	assert.False(t, writeCalled, "Should not write response for empty query")
+}
diff --git a/client/internal/engine_test.go b/client/internal/engine_test.go
index 8c084e36..d9c9881d 100644
--- a/client/internal/engine_test.go
+++ b/client/internal/engine_test.go
@@ -1476,7 +1476,7 @@ func startManagement(t *testing.T, dataDir, testFile string) (*grpc.Server, stri
 
 	permissionsManager := permissions.NewManager(store)
 
-	accountManager, err := server.BuildManager(context.Background(), store, peersUpdateManager, nil, "", "netbird.selfhosted", eventStore, nil, false, ia, metrics, port_forwarding.NewControllerMock(), settingsMockManager, permissionsManager)
+	accountManager, err := server.BuildManager(context.Background(), store, peersUpdateManager, nil, "", "netbird.selfhosted", eventStore, nil, false, ia, metrics, port_forwarding.NewControllerMock(), settingsMockManager, permissionsManager, false)
 	if err != nil {
 		return nil, "", err
 	}
diff --git a/client/server/server_test.go b/client/server/server_test.go
index f2dff76f..376b7e8b 100644
--- a/client/server/server_test.go
+++ b/client/server/server_test.go
@@ -206,7 +206,7 @@ func startManagement(t *testing.T, signalAddr string, counter *int) (*grpc.Serve
 	settingsMockManager := settings.NewMockManager(ctrl)
 	permissionsManagerMock := permissions.NewMockManager(ctrl)
 
-	accountManager, err := server.BuildManager(context.Background(), store, peersUpdateManager, nil, "", "netbird.selfhosted", eventStore, nil, false, ia, metrics, port_forwarding.NewControllerMock(), settingsMockManager, permissionsManagerMock)
+	accountManager, err := server.BuildManager(context.Background(), store, peersUpdateManager, nil, "", "netbird.selfhosted", eventStore, nil, false, ia, metrics, port_forwarding.NewControllerMock(), settingsMockManager, permissionsManagerMock, false)
 	if err != nil {
 		return nil, "", err
 	}
diff --git a/infrastructure_files/tests/setup.env b/infrastructure_files/tests/setup.env
index dc1d6e01..5287e6c5 100644
--- a/infrastructure_files/tests/setup.env
+++ b/infrastructure_files/tests/setup.env
@@ -29,3 +29,4 @@ NETBIRD_TURN_EXTERNAL_IP=1.2.3.4
 NETBIRD_RELAY_PORT=33445
 NETBIRD_AUTH_PKCE_DISABLE_PROMPT_LOGIN=true
 NETBIRD_AUTH_PKCE_LOGIN_FLAG=0
+NETBIRD_MGMT_DISABLE_DEFAULT_POLICY=$CI_NETBIRD_MGMT_DISABLE_DEFAULT_POLICY
diff --git a/management/client/client_test.go b/management/client/client_test.go
index b22a7993..c163d183 100644
--- a/management/client/client_test.go
+++ b/management/client/client_test.go
@@ -100,7 +100,7 @@ func startManagement(t *testing.T) (*grpc.Server, net.Listener) {
 		Return(true, nil).
 		AnyTimes()
 
-	accountManager, err := mgmt.BuildManager(context.Background(), store, peersUpdateManager, nil, "", "netbird.selfhosted", eventStore, nil, false, ia, metrics, port_forwarding.NewControllerMock(), settingsMockManager, permissionsManagerMock)
+	accountManager, err := mgmt.BuildManager(context.Background(), store, peersUpdateManager, nil, "", "netbird.selfhosted", eventStore, nil, false, ia, metrics, port_forwarding.NewControllerMock(), settingsMockManager, permissionsManagerMock, false)
 	if err != nil {
 		t.Fatal(err)
 	}
diff --git a/management/server/account_test.go b/management/server/account_test.go
index ba0191c0..49fcedd5 100644
--- a/management/server/account_test.go
+++ b/management/server/account_test.go
@@ -373,7 +373,7 @@ func TestAccount_GetPeerNetworkMap(t *testing.T) {
 	}
 
 	for _, testCase := range tt {
-		account := newAccountWithId(context.Background(), "account-1", userID, "netbird.io")
+		account := newAccountWithId(context.Background(), "account-1", userID, "netbird.io", false)
 		account.UpdateSettings(&testCase.accountSettings)
 		account.Network = network
 		account.Peers = testCase.peers
@@ -398,7 +398,7 @@ func TestNewAccount(t *testing.T) {
 	domain := "netbird.io"
 	userId := "account_creator"
 	accountID := "account_id"
-	account := newAccountWithId(context.Background(), accountID, userId, domain)
+	account := newAccountWithId(context.Background(), accountID, userId, domain, false)
 	verifyNewAccountHasDefaultFields(t, account, userId, domain, []string{userId})
 }
 
@@ -640,7 +640,7 @@ func TestDefaultAccountManager_GetAccountIDFromToken(t *testing.T) {
 func TestDefaultAccountManager_SyncUserJWTGroups(t *testing.T) {
 	userId := "user-id"
 	domain := "test.domain"
-	_ = newAccountWithId(context.Background(), "", userId, domain)
+	_ = newAccountWithId(context.Background(), "", userId, domain, false)
 	manager, err := createManager(t)
 	require.NoError(t, err, "unable to create account manager")
 	accountID, err := manager.GetAccountIDByUserID(context.Background(), userId, domain)
@@ -793,7 +793,7 @@ func TestAccountManager_GetAccountByUserID(t *testing.T) {
 }
 
 func createAccount(am *DefaultAccountManager, accountID, userID, domain string) (*types.Account, error) {
-	account := newAccountWithId(context.Background(), accountID, userID, domain)
+	account := newAccountWithId(context.Background(), accountID, userID, domain, false)
 	err := am.Store.SaveAccount(context.Background(), account)
 	if err != nil {
 		return nil, err
@@ -1862,11 +1862,8 @@ func TestDefaultAccountManager_MarkPeerConnected_PeerLoginExpiration(t *testing.
 	require.NoError(t, err, "expecting to update account settings successfully but got error")
 
 	wg := &sync.WaitGroup{}
-	wg.Add(2)
+	wg.Add(1)
 	manager.peerLoginExpiry = &MockScheduler{
-		CancelFunc: func(ctx context.Context, IDs []string) {
-			wg.Done()
-		},
 		ScheduleFunc: func(ctx context.Context, in time.Duration, ID string, job func() (nextRunIn time.Duration, reschedule bool)) {
 			wg.Done()
 		},
@@ -2882,7 +2879,7 @@ func createManager(t testing.TB) (*DefaultAccountManager, error) {
 
 	permissionsManager := permissions.NewManager(store)
 
-	manager, err := BuildManager(context.Background(), store, NewPeersUpdateManager(nil), nil, "", "netbird.cloud", eventStore, nil, false, MocIntegratedValidator{}, metrics, port_forwarding.NewControllerMock(), settingsMockManager, permissionsManager)
+	manager, err := BuildManager(context.Background(), store, NewPeersUpdateManager(nil), nil, "", "netbird.cloud", eventStore, nil, false, MocIntegratedValidator{}, metrics, port_forwarding.NewControllerMock(), settingsMockManager, permissionsManager, false)
 	if err != nil {
 		return nil, err
 	}
diff --git a/management/server/dns_test.go b/management/server/dns_test.go
index 36476b14..02bb042d 100644
--- a/management/server/dns_test.go
+++ b/management/server/dns_test.go
@@ -217,7 +217,7 @@ func createDNSManager(t *testing.T) (*DefaultAccountManager, error) {
 
 	settingsMockManager := settings.NewMockManager(ctrl)
 	permissionsManager := permissions.NewManager(store)
-	return BuildManager(context.Background(), store, NewPeersUpdateManager(nil), nil, "", "netbird.test", eventStore, nil, false, MocIntegratedValidator{}, metrics, port_forwarding.NewControllerMock(), settingsMockManager, permissionsManager)
+	return BuildManager(context.Background(), store, NewPeersUpdateManager(nil), nil, "", "netbird.test", eventStore, nil, false, MocIntegratedValidator{}, metrics, port_forwarding.NewControllerMock(), settingsMockManager, permissionsManager, false)
 }
 
 func createDNSStore(t *testing.T) (store.Store, error) {
@@ -267,7 +267,7 @@ func initTestDNSAccount(t *testing.T, am *DefaultAccountManager) (*types.Account
 
 	domain := "example.com"
 
-	account := newAccountWithId(context.Background(), dnsAccountID, dnsAdminUserID, domain)
+	account := newAccountWithId(context.Background(), dnsAccountID, dnsAdminUserID, domain, false)
 
 	account.Users[dnsRegularUserID] = &types.User{
 		Id:   dnsRegularUserID,
diff --git a/management/server/ephemeral_test.go b/management/server/ephemeral_test.go
index 38477f7a..3cf6ae7f 100644
--- a/management/server/ephemeral_test.go
+++ b/management/server/ephemeral_test.go
@@ -127,7 +127,7 @@ func TestNewManagerPeerDisconnected(t *testing.T) {
 }
 
 func seedPeers(store *MockStore, numberOfPeers int, numberOfEphemeralPeers int) {
-	store.account = newAccountWithId(context.Background(), "my account", "", "")
+	store.account = newAccountWithId(context.Background(), "my account", "", "", false)
 
 	for i := 0; i < numberOfPeers; i++ {
 		peerId := fmt.Sprintf("peer_%d", i)
diff --git a/management/server/group_test.go b/management/server/group_test.go
index 4966f2b3..631fe3a7 100644
--- a/management/server/group_test.go
+++ b/management/server/group_test.go
@@ -369,7 +369,7 @@ func initTestGroupAccount(am *DefaultAccountManager) (*DefaultAccountManager, *t
 		Id:         "example user",
 		AutoGroups: []string{groupForUsers.ID},
 	}
-	account := newAccountWithId(context.Background(), accountID, groupAdminUserID, domain)
+	account := newAccountWithId(context.Background(), accountID, groupAdminUserID, domain, false)
 	account.Routes[routeResource.ID] = routeResource
 	account.Routes[routePeerGroupResource.ID] = routePeerGroupResource
 	account.NameServerGroups[nameServerGroup.ID] = nameServerGroup
diff --git a/management/server/http/testing/testing_tools/tools.go b/management/server/http/testing/testing_tools/tools.go
index 8c5d2e38..829bff45 100644
--- a/management/server/http/testing/testing_tools/tools.go
+++ b/management/server/http/testing/testing_tools/tools.go
@@ -1,5 +1,4 @@
 package testing_tools
-
 import (
 	"bytes"
 	"context"
@@ -138,7 +137,7 @@ func BuildApiBlackBoxWithDBState(t TB, sqlFile string, expectedPeerUpdate *serve
 	userManager := users.NewManager(store)
 	permissionsManager := permissions.NewManager(store)
 	settingsManager := settings.NewManager(store, userManager, integrations.NewManager(&activity.InMemoryEventStore{}), permissionsManager)
-	am, err := server.BuildManager(context.Background(), store, peersUpdateManager, nil, "", "", &activity.InMemoryEventStore{}, geoMock, false, validatorMock, metrics, proxyController, settingsManager, permissionsManager)
+	am, err := server.BuildManager(context.Background(), store, peersUpdateManager, nil, "", "", &activity.InMemoryEventStore{}, geoMock, false, validatorMock, metrics, proxyController, settingsManager, permissionsManager, false)
 	if err != nil {
 		t.Fatalf("Failed to create manager: %v", err)
 	}
diff --git a/management/server/management_proto_test.go b/management/server/management_proto_test.go
index b85a43da..337890ef 100644
--- a/management/server/management_proto_test.go
+++ b/management/server/management_proto_test.go
@@ -444,7 +444,7 @@ func startManagementForTest(t *testing.T, testFile string, config *types.Config)
 	permissionsManager := permissions.NewManager(store)
 
 	accountManager, err := BuildManager(ctx, store, peersUpdateManager, nil, "", "netbird.selfhosted",
-		eventStore, nil, false, MocIntegratedValidator{}, metrics, port_forwarding.NewControllerMock(), settingsMockManager, permissionsManager)
+		eventStore, nil, false, MocIntegratedValidator{}, metrics, port_forwarding.NewControllerMock(), settingsMockManager, permissionsManager, false)
 
 	if err != nil {
 		cleanup()
diff --git a/management/server/management_test.go b/management/server/management_test.go
index a4f9a5e3..0a6b3f75 100644
--- a/management/server/management_test.go
+++ b/management/server/management_test.go
@@ -211,7 +211,7 @@ func startServer(
 		port_forwarding.NewControllerMock(),
 		settingsMockManager,
 		permissionsManager,
-	)
+		false)
 	if err != nil {
 		t.Fatalf("failed creating an account manager: %v", err)
 	}
diff --git a/management/server/metrics/selfhosted_test.go b/management/server/metrics/selfhosted_test.go
index de668640..db0d90e6 100644
--- a/management/server/metrics/selfhosted_test.go
+++ b/management/server/metrics/selfhosted_test.go
@@ -47,8 +47,8 @@ func (mockDatasource) GetAllAccounts(_ context.Context) []*types.Account {
 				"1": {
 					ID:         "1",
 					UserID:     "test",
-					SSHEnabled: true,
-					Meta:       nbpeer.PeerSystemMeta{GoOS: "linux", WtVersion: "0.0.1"},
+					SSHEnabled: false,
+					Meta:       nbpeer.PeerSystemMeta{GoOS: "linux", WtVersion: "0.0.1", Flags: nbpeer.Flags{ServerSSHAllowed: true, RosenpassEnabled: true}},
 				},
 			},
 			Policies: []*types.Policy{
@@ -312,7 +312,19 @@ func TestGenerateProperties(t *testing.T) {
 	}
 
 	if properties["posture_checks"] != 2 {
-		t.Errorf("expected 1 posture_checks, got %d", properties["posture_checks"])
+		t.Errorf("expected 2 posture_checks, got %d", properties["posture_checks"])
+	}
+
+	if properties["rosenpass_enabled"] != 1 {
+		t.Errorf("expected 1 rosenpass_enabled, got %d", properties["rosenpass_enabled"])
+	}
+
+	if properties["active_user_peers_last_day"] != 2 {
+		t.Errorf("expected 2 active_user_peers_last_day, got %d", properties["active_user_peers_last_day"])
+	}
+
+	if properties["active_users_last_day"] != 1 {
+		t.Errorf("expected 1 active_users_last_day, got %d", properties["active_users_last_day"])
 	}
 
 }
diff --git a/management/server/nameserver_test.go b/management/server/nameserver_test.go
index dc4be594..75d1e797 100644
--- a/management/server/nameserver_test.go
+++ b/management/server/nameserver_test.go
@@ -779,7 +779,7 @@ func createNSManager(t *testing.T) (*DefaultAccountManager, error) {
 	t.Cleanup(ctrl.Finish)
 	settingsMockManager := settings.NewMockManager(ctrl)
 	permissionsManager := permissions.NewManager(store)
-	return BuildManager(context.Background(), store, NewPeersUpdateManager(nil), nil, "", "netbird.selfhosted", eventStore, nil, false, MocIntegratedValidator{}, metrics, port_forwarding.NewControllerMock(), settingsMockManager, permissionsManager)
+	return BuildManager(context.Background(), store, NewPeersUpdateManager(nil), nil, "", "netbird.selfhosted", eventStore, nil, false, MocIntegratedValidator{}, metrics, port_forwarding.NewControllerMock(), settingsMockManager, permissionsManager, false)
 }
 
 func createNSStore(t *testing.T) (store.Store, error) {
@@ -848,7 +848,7 @@ func initTestNSAccount(t *testing.T, am *DefaultAccountManager) (*types.Account,
 	userID := testUserID
 	domain := "example.com"
 
-	account := newAccountWithId(context.Background(), accountID, userID, domain)
+	account := newAccountWithId(context.Background(), accountID, userID, domain, false)
 
 	account.NameServerGroups[existingNSGroup.ID] = &existingNSGroup
 
diff --git a/management/server/peer_test.go b/management/server/peer_test.go
index 3d782f04..775385a2 100644
--- a/management/server/peer_test.go
+++ b/management/server/peer_test.go
@@ -480,7 +480,7 @@ func TestDefaultAccountManager_GetPeer(t *testing.T) {
 	accountID := "test_account"
 	adminUser := "account_creator"
 	someUser := "some_user"
-	account := newAccountWithId(context.Background(), accountID, adminUser, "")
+	account := newAccountWithId(context.Background(), accountID, adminUser, "", false)
 	account.Users[someUser] = &types.User{
 		Id:   someUser,
 		Role: types.UserRoleUser,
@@ -667,7 +667,7 @@ func TestDefaultAccountManager_GetPeers(t *testing.T) {
 			accountID := "test_account"
 			adminUser := "account_creator"
 			someUser := "some_user"
-			account := newAccountWithId(context.Background(), accountID, adminUser, "")
+			account := newAccountWithId(context.Background(), accountID, adminUser, "", false)
 			account.Users[someUser] = &types.User{
 				Id:            someUser,
 				Role:          testCase.role,
@@ -737,7 +737,7 @@ func setupTestAccountManager(b testing.TB, peers int, groups int) (*DefaultAccou
 	adminUser := "account_creator"
 	regularUser := "regular_user"
 
-	account := newAccountWithId(context.Background(), accountID, adminUser, "")
+	account := newAccountWithId(context.Background(), accountID, adminUser, "", false)
 	account.Users[regularUser] = &types.User{
 		Id:   regularUser,
 		Role: types.UserRoleUser,
@@ -1267,7 +1267,7 @@ func Test_RegisterPeerByUser(t *testing.T) {
 	settingsMockManager := settings.NewMockManager(ctrl)
 	permissionsManager := permissions.NewManager(s)
 
-	am, err := BuildManager(context.Background(), s, NewPeersUpdateManager(nil), nil, "", "netbird.cloud", eventStore, nil, false, MocIntegratedValidator{}, metrics, port_forwarding.NewControllerMock(), settingsMockManager, permissionsManager)
+	am, err := BuildManager(context.Background(), s, NewPeersUpdateManager(nil), nil, "", "netbird.cloud", eventStore, nil, false, MocIntegratedValidator{}, metrics, port_forwarding.NewControllerMock(), settingsMockManager, permissionsManager, false)
 	assert.NoError(t, err)
 
 	existingAccountID := "bf1c8084-ba50-4ce7-9439-34653001fc3b"
@@ -1342,7 +1342,7 @@ func Test_RegisterPeerBySetupKey(t *testing.T) {
 	settingsMockManager := settings.NewMockManager(ctrl)
 	permissionsManager := permissions.NewManager(s)
 
-	am, err := BuildManager(context.Background(), s, NewPeersUpdateManager(nil), nil, "", "netbird.cloud", eventStore, nil, false, MocIntegratedValidator{}, metrics, port_forwarding.NewControllerMock(), settingsMockManager, permissionsManager)
+	am, err := BuildManager(context.Background(), s, NewPeersUpdateManager(nil), nil, "", "netbird.cloud", eventStore, nil, false, MocIntegratedValidator{}, metrics, port_forwarding.NewControllerMock(), settingsMockManager, permissionsManager, false)
 	assert.NoError(t, err)
 
 	existingAccountID := "bf1c8084-ba50-4ce7-9439-34653001fc3b"
@@ -1477,7 +1477,7 @@ func Test_RegisterPeerRollbackOnFailure(t *testing.T) {
 
 	permissionsManager := permissions.NewManager(s)
 
-	am, err := BuildManager(context.Background(), s, NewPeersUpdateManager(nil), nil, "", "netbird.cloud", eventStore, nil, false, MocIntegratedValidator{}, metrics, port_forwarding.NewControllerMock(), settingsMockManager, permissionsManager)
+	am, err := BuildManager(context.Background(), s, NewPeersUpdateManager(nil), nil, "", "netbird.cloud", eventStore, nil, false, MocIntegratedValidator{}, metrics, port_forwarding.NewControllerMock(), settingsMockManager, permissionsManager, false)
 	assert.NoError(t, err)
 
 	existingAccountID := "bf1c8084-ba50-4ce7-9439-34653001fc3b"
@@ -1546,7 +1546,7 @@ func Test_LoginPeer(t *testing.T) {
 	settingsMockManager := settings.NewMockManager(ctrl)
 	permissionsManager := permissions.NewManager(s)
 
-	am, err := BuildManager(context.Background(), s, NewPeersUpdateManager(nil), nil, "", "netbird.cloud", eventStore, nil, false, MocIntegratedValidator{}, metrics, port_forwarding.NewControllerMock(), settingsMockManager, permissionsManager)
+	am, err := BuildManager(context.Background(), s, NewPeersUpdateManager(nil), nil, "", "netbird.cloud", eventStore, nil, false, MocIntegratedValidator{}, metrics, port_forwarding.NewControllerMock(), settingsMockManager, permissionsManager, false)
 	assert.NoError(t, err)
 
 	existingAccountID := "bf1c8084-ba50-4ce7-9439-34653001fc3b"
@@ -2052,7 +2052,7 @@ func Test_DeletePeer(t *testing.T) {
 	// account with an admin and a regular user
 	accountID := "test_account"
 	adminUser := "account_creator"
-	account := newAccountWithId(context.Background(), accountID, adminUser, "")
+	account := newAccountWithId(context.Background(), accountID, adminUser, "", false)
 	account.Peers = map[string]*nbpeer.Peer{
 		"peer1": {
 			ID:        "peer1",
diff --git a/management/server/policy_test.go b/management/server/policy_test.go
index 679ec3b8..4352f3cf 100644
--- a/management/server/policy_test.go
+++ b/management/server/policy_test.go
@@ -27,6 +27,7 @@ func TestAccount_getPeersByPolicy(t *testing.T) {
 				ID:     "peerB",
 				IP:     net.ParseIP("100.65.80.39"),
 				Status: &nbpeer.PeerStatus{},
+				Meta:   nbpeer.PeerSystemMeta{WtVersion: "0.48.0"},
 			},
 			"peerC": {
 				ID:     "peerC",
@@ -63,6 +64,12 @@ func TestAccount_getPeersByPolicy(t *testing.T) {
 				IP:     net.ParseIP("100.65.31.2"),
 				Status: &nbpeer.PeerStatus{},
 			},
+			"peerK": {
+				ID:     "peerK",
+				IP:     net.ParseIP("100.32.80.1"),
+				Status: &nbpeer.PeerStatus{},
+				Meta:   nbpeer.PeerSystemMeta{WtVersion: "0.30.0"},
+			},
 		},
 		Groups: map[string]*types.Group{
 			"GroupAll": {
@@ -111,6 +118,13 @@ func TestAccount_getPeersByPolicy(t *testing.T) {
 					"peerI",
 				},
 			},
+			"GroupWorkflow": {
+				ID:   "GroupWorkflow",
+				Name: "workflow",
+				Peers: []string{
+					"peerK",
+				},
+			},
 		},
 		Policies: []*types.Policy{
 			{
@@ -189,6 +203,39 @@ func TestAccount_getPeersByPolicy(t *testing.T) {
 					},
 				},
 			},
+			{
+				ID:          "RuleWorkflow",
+				Name:        "Workflow",
+				Description: "No description",
+				Enabled:     true,
+				Rules: []*types.PolicyRule{
+					{
+						ID:            "RuleWorkflow",
+						Name:          "Workflow",
+						Description:   "No description",
+						Bidirectional: true,
+						Enabled:       true,
+						Protocol:      types.PolicyRuleProtocolTCP,
+						Action:        types.PolicyTrafficActionAccept,
+						PortRanges: []types.RulePortRange{
+							{
+								Start: 8088,
+								End:   8088,
+							},
+							{
+								Start: 9090,
+								End:   9095,
+							},
+						},
+						Sources: []string{
+							"GroupWorkflow",
+						},
+						Destinations: []string{
+							"GroupDMZ",
+						},
+					},
+				},
+			},
 		},
 	}
 
@@ -199,14 +246,14 @@ func TestAccount_getPeersByPolicy(t *testing.T) {
 
 	t.Run("check that all peers get map", func(t *testing.T) {
 		for _, p := range account.Peers {
-			peers, firewallRules := account.GetPeerConnectionResources(context.Background(), p.ID, validatedPeers)
-			assert.GreaterOrEqual(t, len(peers), 2, "minimum number peers should present")
-			assert.GreaterOrEqual(t, len(firewallRules), 2, "minimum number of firewall rules should present")
+			peers, firewallRules := account.GetPeerConnectionResources(context.Background(), p, validatedPeers)
+			assert.GreaterOrEqual(t, len(peers), 1, "minimum number peers should present")
+			assert.GreaterOrEqual(t, len(firewallRules), 1, "minimum number of firewall rules should present")
 		}
 	})
 
 	t.Run("check first peer map details", func(t *testing.T) {
-		peers, firewallRules := account.GetPeerConnectionResources(context.Background(), "peerB", validatedPeers)
+		peers, firewallRules := account.GetPeerConnectionResources(context.Background(), account.Peers["peerB"], validatedPeers)
 		assert.Len(t, peers, 8)
 		assert.Contains(t, peers, account.Peers["peerA"])
 		assert.Contains(t, peers, account.Peers["peerC"])
@@ -364,6 +411,32 @@ func TestAccount_getPeersByPolicy(t *testing.T) {
 			assert.True(t, contains, "rule not found in expected rules %#v", rule)
 		}
 	})
+
+	t.Run("check port ranges support for older peers", func(t *testing.T) {
+		peers, firewallRules := account.GetPeerConnectionResources(context.Background(), account.Peers["peerK"], validatedPeers)
+		assert.Len(t, peers, 1)
+		assert.Contains(t, peers, account.Peers["peerI"])
+
+		expectedFirewallRules := []*types.FirewallRule{
+			{
+				PeerIP:    "100.65.31.2",
+				Direction: types.FirewallRuleDirectionIN,
+				Action:    "accept",
+				Protocol:  "tcp",
+				Port:      "8088",
+				PolicyID:  "RuleWorkflow",
+			},
+			{
+				PeerIP:    "100.65.31.2",
+				Direction: types.FirewallRuleDirectionOUT,
+				Action:    "accept",
+				Protocol:  "tcp",
+				Port:      "8088",
+				PolicyID:  "RuleWorkflow",
+			},
+		}
+		assert.ElementsMatch(t, firewallRules, expectedFirewallRules)
+	})
 }
 
 func TestAccount_getPeersByPolicyDirect(t *testing.T) {
@@ -466,10 +539,10 @@ func TestAccount_getPeersByPolicyDirect(t *testing.T) {
 	}
 
 	t.Run("check first peer map", func(t *testing.T) {
-		peers, firewallRules := account.GetPeerConnectionResources(context.Background(), "peerB", approvedPeers)
+		peers, firewallRules := account.GetPeerConnectionResources(context.Background(), account.Peers["peerB"], approvedPeers)
 		assert.Contains(t, peers, account.Peers["peerC"])
 
-		epectedFirewallRules := []*types.FirewallRule{
+		expectedFirewallRules := []*types.FirewallRule{
 			{
 				PeerIP:    "100.65.254.139",
 				Direction: types.FirewallRuleDirectionIN,
@@ -487,19 +560,19 @@ func TestAccount_getPeersByPolicyDirect(t *testing.T) {
 				PolicyID:  "RuleSwarm",
 			},
 		}
-		assert.Len(t, firewallRules, len(epectedFirewallRules))
-		slices.SortFunc(epectedFirewallRules, sortFunc())
+		assert.Len(t, firewallRules, len(expectedFirewallRules))
+		slices.SortFunc(expectedFirewallRules, sortFunc())
 		slices.SortFunc(firewallRules, sortFunc())
 		for i := range firewallRules {
-			assert.Equal(t, epectedFirewallRules[i], firewallRules[i])
+			assert.Equal(t, expectedFirewallRules[i], firewallRules[i])
 		}
 	})
 
 	t.Run("check second peer map", func(t *testing.T) {
-		peers, firewallRules := account.GetPeerConnectionResources(context.Background(), "peerC", approvedPeers)
+		peers, firewallRules := account.GetPeerConnectionResources(context.Background(), account.Peers["peerC"], approvedPeers)
 		assert.Contains(t, peers, account.Peers["peerB"])
 
-		epectedFirewallRules := []*types.FirewallRule{
+		expectedFirewallRules := []*types.FirewallRule{
 			{
 				PeerIP:    "100.65.80.39",
 				Direction: types.FirewallRuleDirectionIN,
@@ -517,21 +590,21 @@ func TestAccount_getPeersByPolicyDirect(t *testing.T) {
 				PolicyID:  "RuleSwarm",
 			},
 		}
-		assert.Len(t, firewallRules, len(epectedFirewallRules))
-		slices.SortFunc(epectedFirewallRules, sortFunc())
+		assert.Len(t, firewallRules, len(expectedFirewallRules))
+		slices.SortFunc(expectedFirewallRules, sortFunc())
 		slices.SortFunc(firewallRules, sortFunc())
 		for i := range firewallRules {
-			assert.Equal(t, epectedFirewallRules[i], firewallRules[i])
+			assert.Equal(t, expectedFirewallRules[i], firewallRules[i])
 		}
 	})
 
 	account.Policies[1].Rules[0].Bidirectional = false
 
 	t.Run("check first peer map directional only", func(t *testing.T) {
-		peers, firewallRules := account.GetPeerConnectionResources(context.Background(), "peerB", approvedPeers)
+		peers, firewallRules := account.GetPeerConnectionResources(context.Background(), account.Peers["peerB"], approvedPeers)
 		assert.Contains(t, peers, account.Peers["peerC"])
 
-		epectedFirewallRules := []*types.FirewallRule{
+		expectedFirewallRules := []*types.FirewallRule{
 			{
 				PeerIP:    "100.65.254.139",
 				Direction: types.FirewallRuleDirectionOUT,
@@ -541,19 +614,19 @@ func TestAccount_getPeersByPolicyDirect(t *testing.T) {
 				PolicyID:  "RuleSwarm",
 			},
 		}
-		assert.Len(t, firewallRules, len(epectedFirewallRules))
-		slices.SortFunc(epectedFirewallRules, sortFunc())
+		assert.Len(t, firewallRules, len(expectedFirewallRules))
+		slices.SortFunc(expectedFirewallRules, sortFunc())
 		slices.SortFunc(firewallRules, sortFunc())
 		for i := range firewallRules {
-			assert.Equal(t, epectedFirewallRules[i], firewallRules[i])
+			assert.Equal(t, expectedFirewallRules[i], firewallRules[i])
 		}
 	})
 
 	t.Run("check second peer map directional only", func(t *testing.T) {
-		peers, firewallRules := account.GetPeerConnectionResources(context.Background(), "peerC", approvedPeers)
+		peers, firewallRules := account.GetPeerConnectionResources(context.Background(), account.Peers["peerC"], approvedPeers)
 		assert.Contains(t, peers, account.Peers["peerB"])
 
-		epectedFirewallRules := []*types.FirewallRule{
+		expectedFirewallRules := []*types.FirewallRule{
 			{
 				PeerIP:    "100.65.80.39",
 				Direction: types.FirewallRuleDirectionIN,
@@ -563,11 +636,11 @@ func TestAccount_getPeersByPolicyDirect(t *testing.T) {
 				PolicyID:  "RuleSwarm",
 			},
 		}
-		assert.Len(t, firewallRules, len(epectedFirewallRules))
-		slices.SortFunc(epectedFirewallRules, sortFunc())
+		assert.Len(t, firewallRules, len(expectedFirewallRules))
+		slices.SortFunc(expectedFirewallRules, sortFunc())
 		slices.SortFunc(firewallRules, sortFunc())
 		for i := range firewallRules {
-			assert.Equal(t, epectedFirewallRules[i], firewallRules[i])
+			assert.Equal(t, expectedFirewallRules[i], firewallRules[i])
 		}
 	})
 }
@@ -748,7 +821,7 @@ func TestAccount_getPeersByPolicyPostureChecks(t *testing.T) {
 	t.Run("verify peer's network map with default group peer list", func(t *testing.T) {
 		// peerB doesn't fulfill the NB posture check but is included in the destination group Swarm,
 		// will establish a connection with all source peers satisfying the NB posture check.
-		peers, firewallRules := account.GetPeerConnectionResources(context.Background(), "peerB", approvedPeers)
+		peers, firewallRules := account.GetPeerConnectionResources(context.Background(), account.Peers["peerB"], approvedPeers)
 		assert.Len(t, peers, 4)
 		assert.Len(t, firewallRules, 4)
 		assert.Contains(t, peers, account.Peers["peerA"])
@@ -758,7 +831,7 @@ func TestAccount_getPeersByPolicyPostureChecks(t *testing.T) {
 
 		// peerC satisfy the NB posture check, should establish connection to all destination group peer's
 		// We expect a single permissive firewall rule which all outgoing connections
-		peers, firewallRules = account.GetPeerConnectionResources(context.Background(), "peerC", approvedPeers)
+		peers, firewallRules = account.GetPeerConnectionResources(context.Background(), account.Peers["peerC"], approvedPeers)
 		assert.Len(t, peers, len(account.Groups["GroupSwarm"].Peers))
 		assert.Len(t, firewallRules, 1)
 		expectedFirewallRules := []*types.FirewallRule{
@@ -775,7 +848,7 @@ func TestAccount_getPeersByPolicyPostureChecks(t *testing.T) {
 
 		// peerE doesn't fulfill the NB posture check and exists in only destination group Swarm,
 		// all source group peers satisfying the NB posture check should establish connection
-		peers, firewallRules = account.GetPeerConnectionResources(context.Background(), "peerE", approvedPeers)
+		peers, firewallRules = account.GetPeerConnectionResources(context.Background(), account.Peers["peerE"], approvedPeers)
 		assert.Len(t, peers, 4)
 		assert.Len(t, firewallRules, 4)
 		assert.Contains(t, peers, account.Peers["peerA"])
@@ -785,7 +858,7 @@ func TestAccount_getPeersByPolicyPostureChecks(t *testing.T) {
 
 		// peerI doesn't fulfill the OS version posture check and exists in only destination group Swarm,
 		// all source group peers satisfying the NB posture check should establish connection
-		peers, firewallRules = account.GetPeerConnectionResources(context.Background(), "peerI", approvedPeers)
+		peers, firewallRules = account.GetPeerConnectionResources(context.Background(), account.Peers["peerI"], approvedPeers)
 		assert.Len(t, peers, 4)
 		assert.Len(t, firewallRules, 4)
 		assert.Contains(t, peers, account.Peers["peerA"])
@@ -800,19 +873,19 @@ func TestAccount_getPeersByPolicyPostureChecks(t *testing.T) {
 
 		// peerB doesn't satisfy the NB posture check, and doesn't exist in destination group peer's
 		// no connection should be established to any peer of destination group
-		peers, firewallRules := account.GetPeerConnectionResources(context.Background(), "peerB", approvedPeers)
+		peers, firewallRules := account.GetPeerConnectionResources(context.Background(), account.Peers["peerB"], approvedPeers)
 		assert.Len(t, peers, 0)
 		assert.Len(t, firewallRules, 0)
 
 		// peerI doesn't satisfy the OS version posture check, and doesn't exist in destination group peer's
 		// no connection should be established to any peer of destination group
-		peers, firewallRules = account.GetPeerConnectionResources(context.Background(), "peerI", approvedPeers)
+		peers, firewallRules = account.GetPeerConnectionResources(context.Background(), account.Peers["peerI"], approvedPeers)
 		assert.Len(t, peers, 0)
 		assert.Len(t, firewallRules, 0)
 
 		// peerC satisfy the NB posture check, should establish connection to all destination group peer's
 		// We expect a single permissive firewall rule which all outgoing connections
-		peers, firewallRules = account.GetPeerConnectionResources(context.Background(), "peerC", approvedPeers)
+		peers, firewallRules = account.GetPeerConnectionResources(context.Background(), account.Peers["peerC"], approvedPeers)
 		assert.Len(t, peers, len(account.Groups["GroupSwarm"].Peers))
 		assert.Len(t, firewallRules, len(account.Groups["GroupSwarm"].Peers))
 
@@ -827,14 +900,14 @@ func TestAccount_getPeersByPolicyPostureChecks(t *testing.T) {
 
 		// peerE doesn't fulfill the NB posture check and exists in only destination group Swarm,
 		// all source group peers satisfying the NB posture check should establish connection
-		peers, firewallRules = account.GetPeerConnectionResources(context.Background(), "peerE", approvedPeers)
+		peers, firewallRules = account.GetPeerConnectionResources(context.Background(), account.Peers["peerE"], approvedPeers)
 		assert.Len(t, peers, 3)
 		assert.Len(t, firewallRules, 3)
 		assert.Contains(t, peers, account.Peers["peerA"])
 		assert.Contains(t, peers, account.Peers["peerC"])
 		assert.Contains(t, peers, account.Peers["peerD"])
 
-		peers, firewallRules = account.GetPeerConnectionResources(context.Background(), "peerA", approvedPeers)
+		peers, firewallRules = account.GetPeerConnectionResources(context.Background(), account.Peers["peerA"], approvedPeers)
 		assert.Len(t, peers, 5)
 		// assert peers from Group Swarm
 		assert.Contains(t, peers, account.Peers["peerD"])
diff --git a/management/server/posture/nb_version_test.go b/management/server/posture/nb_version_test.go
index 1bf48545..d3478afc 100644
--- a/management/server/posture/nb_version_test.go
+++ b/management/server/posture/nb_version_test.go
@@ -139,3 +139,68 @@ func TestNBVersionCheck_Validate(t *testing.T) {
 		})
 	}
 }
+
+func TestMeetsMinVersion(t *testing.T) {
+	tests := []struct {
+		name    string
+		minVer  string
+		peerVer string
+		want    bool
+		wantErr bool
+	}{
+		{
+			name:    "Peer version greater than min version",
+			minVer:  "0.26.0",
+			peerVer: "0.60.1",
+			want:    true,
+			wantErr: false,
+		},
+		{
+			name:    "Peer version equals min version",
+			minVer:  "1.0.0",
+			peerVer: "1.0.0",
+			want:    true,
+			wantErr: false,
+		},
+		{
+			name:    "Peer version less than min version",
+			minVer:  "1.0.0",
+			peerVer: "0.9.9",
+			want:    false,
+			wantErr: false,
+		},
+		{
+			name:    "Peer version with pre-release tag greater than min version",
+			minVer:  "1.0.0",
+			peerVer: "1.0.1-alpha",
+			want:    true,
+			wantErr: false,
+		},
+		{
+			name:    "Invalid peer version format",
+			minVer:  "1.0.0",
+			peerVer: "dev",
+			want:    false,
+			wantErr: true,
+		},
+		{
+			name:    "Invalid min version format",
+			minVer:  "invalid.version",
+			peerVer: "1.0.0",
+			want:    false,
+			wantErr: true,
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			got, err := MeetsMinVersion(tt.minVer, tt.peerVer)
+			if tt.wantErr {
+				assert.Error(t, err)
+			} else {
+				assert.NoError(t, err)
+			}
+			assert.Equal(t, tt.want, got)
+		})
+	}
+}
diff --git a/management/server/posture_checks_test.go b/management/server/posture_checks_test.go
index 8bd2fab6..f9346737 100644
--- a/management/server/posture_checks_test.go
+++ b/management/server/posture_checks_test.go
@@ -106,7 +106,7 @@ func initTestPostureChecksAccount(am *DefaultAccountManager) (*types.Account, er
 		Role: types.UserRoleUser,
 	}
 
-	account := newAccountWithId(context.Background(), accountID, groupAdminUserID, domain)
+	account := newAccountWithId(context.Background(), accountID, groupAdminUserID, domain, false)
 	account.Users[admin.Id] = admin
 	account.Users[user.Id] = user
 
diff --git a/management/server/route_test.go b/management/server/route_test.go
index 833477b5..77cbc75b 100644
--- a/management/server/route_test.go
+++ b/management/server/route_test.go
@@ -1284,7 +1284,7 @@ func createRouterManager(t *testing.T) (*DefaultAccountManager, error) {
 
 	permissionsManager := permissions.NewManager(store)
 
-	return BuildManager(context.Background(), store, NewPeersUpdateManager(nil), nil, "", "netbird.selfhosted", eventStore, nil, false, MocIntegratedValidator{}, metrics, port_forwarding.NewControllerMock(), settingsMockManager, permissionsManager)
+	return BuildManager(context.Background(), store, NewPeersUpdateManager(nil), nil, "", "netbird.selfhosted", eventStore, nil, false, MocIntegratedValidator{}, metrics, port_forwarding.NewControllerMock(), settingsMockManager, permissionsManager, false)
 }
 
 func createRouterStore(t *testing.T) (store.Store, error) {
@@ -1305,7 +1305,7 @@ func initTestRouteAccount(t *testing.T, am *DefaultAccountManager) (*types.Accou
 	accountID := "testingAcc"
 	domain := "example.com"
 
-	account := newAccountWithId(context.Background(), accountID, userID, domain)
+	account := newAccountWithId(context.Background(), accountID, userID, domain, false)
 	err := am.Store.SaveAccount(context.Background(), account)
 	if err != nil {
 		return nil, err
diff --git a/management/server/store/sql_store_test.go b/management/server/store/sql_store_test.go
index 2c1f5f8e..f187be8c 100644
--- a/management/server/store/sql_store_test.go
+++ b/management/server/store/sql_store_test.go
@@ -19,21 +19,17 @@ import (
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
 
-	"github.com/netbirdio/netbird/management/server/util"
-
 	nbdns "github.com/netbirdio/netbird/dns"
 	resourceTypes "github.com/netbirdio/netbird/management/server/networks/resources/types"
 	routerTypes "github.com/netbirdio/netbird/management/server/networks/routers/types"
 	networkTypes "github.com/netbirdio/netbird/management/server/networks/types"
+	nbpeer "github.com/netbirdio/netbird/management/server/peer"
 	"github.com/netbirdio/netbird/management/server/posture"
-	"github.com/netbirdio/netbird/management/server/types"
-
-	route2 "github.com/netbirdio/netbird/route"
-
 	"github.com/netbirdio/netbird/management/server/status"
-
-	nbpeer "github.com/netbirdio/netbird/management/server/peer"
+	"github.com/netbirdio/netbird/management/server/types"
+	"github.com/netbirdio/netbird/management/server/util"
 	nbroute "github.com/netbirdio/netbird/route"
+	route2 "github.com/netbirdio/netbird/route"
 )
 
 func runTestForAllEngines(t *testing.T, testDataFile string, f func(t *testing.T, store Store)) {
@@ -2048,7 +2044,7 @@ func newAccountWithId(ctx context.Context, accountID, userID, domain string) *ty
 		},
 	}
 
-	if err := acc.AddAllGroup(); err != nil {
+	if err := acc.AddAllGroup(false); err != nil {
 		log.WithContext(ctx).Errorf("error adding all group to account %s: %v", acc.Id, err)
 	}
 	return acc
@@ -3247,6 +3243,132 @@ func TestSqlStore_SaveGroups_LargeBatch(t *testing.T) {
 	require.NoError(t, err)
 	require.Equal(t, 8003, len(accountGroups))
 }
+func TestSqlStore_GetAccountRoutes(t *testing.T) {
+	store, cleanup, err := NewTestStoreFromSQL(context.Background(), "../testdata/extended-store.sql", t.TempDir())
+	t.Cleanup(cleanup)
+	require.NoError(t, err)
+
+	tests := []struct {
+		name          string
+		accountID     string
+		expectedCount int
+	}{
+		{
+			name:          "retrieve routes by existing account ID",
+			accountID:     "bf1c8084-ba50-4ce7-9439-34653001fc3b",
+			expectedCount: 1,
+		},
+		{
+			name:          "non-existing account ID",
+			accountID:     "nonexistent",
+			expectedCount: 0,
+		},
+		{
+			name:          "empty account ID",
+			accountID:     "",
+			expectedCount: 0,
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			routes, err := store.GetAccountRoutes(context.Background(), LockingStrengthShare, tt.accountID)
+			require.NoError(t, err)
+			require.Len(t, routes, tt.expectedCount)
+		})
+	}
+}
+
+func TestSqlStore_GetRouteByID(t *testing.T) {
+	store, cleanup, err := NewTestStoreFromSQL(context.Background(), "../testdata/extended-store.sql", t.TempDir())
+	t.Cleanup(cleanup)
+	require.NoError(t, err)
+
+	accountID := "bf1c8084-ba50-4ce7-9439-34653001fc3b"
+	tests := []struct {
+		name        string
+		routeID     string
+		expectError bool
+	}{
+		{
+			name:        "retrieve existing route",
+			routeID:     "ct03t427qv97vmtmglog",
+			expectError: false,
+		},
+		{
+			name:        "retrieve non-existing route",
+			routeID:     "non-existing",
+			expectError: true,
+		},
+		{
+			name:        "retrieve with empty route ID",
+			routeID:     "",
+			expectError: true,
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			route, err := store.GetRouteByID(context.Background(), LockingStrengthShare, accountID, tt.routeID)
+			if tt.expectError {
+				require.Error(t, err)
+				sErr, ok := status.FromError(err)
+				require.True(t, ok)
+				require.Equal(t, sErr.Type(), status.NotFound)
+				require.Nil(t, route)
+			} else {
+				require.NoError(t, err)
+				require.NotNil(t, route)
+				require.Equal(t, tt.routeID, string(route.ID))
+			}
+		})
+	}
+}
+
+func TestSqlStore_SaveRoute(t *testing.T) {
+	store, cleanup, err := NewTestStoreFromSQL(context.Background(), "../testdata/extended-store.sql", t.TempDir())
+	t.Cleanup(cleanup)
+	require.NoError(t, err)
+
+	accountID := "bf1c8084-ba50-4ce7-9439-34653001fc3b"
+
+	route := &route2.Route{
+		ID:                  "route-id",
+		AccountID:           accountID,
+		Network:             netip.MustParsePrefix("10.10.0.0/16"),
+		NetID:               "netID",
+		PeerGroups:          []string{"routeA"},
+		NetworkType:         route2.IPv4Network,
+		Masquerade:          true,
+		Metric:              9999,
+		Enabled:             true,
+		Groups:              []string{"groupA"},
+		AccessControlGroups: []string{},
+	}
+	err = store.SaveRoute(context.Background(), LockingStrengthUpdate, route)
+	require.NoError(t, err)
+
+	saveRoute, err := store.GetRouteByID(context.Background(), LockingStrengthShare, accountID, string(route.ID))
+	require.NoError(t, err)
+	require.Equal(t, route, saveRoute)
+
+}
+
+func TestSqlStore_DeleteRoute(t *testing.T) {
+	store, cleanup, err := NewTestStoreFromSQL(context.Background(), "../testdata/extended-store.sql", t.TempDir())
+	t.Cleanup(cleanup)
+	require.NoError(t, err)
+
+	accountID := "bf1c8084-ba50-4ce7-9439-34653001fc3b"
+	routeID := "ct03t427qv97vmtmglog"
+
+	err = store.DeleteRoute(context.Background(), LockingStrengthUpdate, accountID, routeID)
+	require.NoError(t, err)
+
+	route, err := store.GetRouteByID(context.Background(), LockingStrengthShare, accountID, routeID)
+	require.Error(t, err)
+	require.Nil(t, route)
+}
 
 func TestSqlStore_GetAccountMeta(t *testing.T) {
 	store, cleanup, err := NewTestStoreFromSQL(context.Background(), "../testdata/extended-store.sql", t.TempDir())
diff --git a/management/server/testdata/extended-store.sql b/management/server/testdata/extended-store.sql
index 324bf629..0393d1ad 100644
--- a/management/server/testdata/extended-store.sql
+++ b/management/server/testdata/extended-store.sql
@@ -38,4 +38,5 @@ INSERT INTO "groups" VALUES('cfefqs706sqkneg59g2g','bf1c8084-ba50-4ce7-9439-3465
 INSERT INTO posture_checks VALUES('csplshq7qv948l48f7t0','NetBird Version > 0.32.0','','bf1c8084-ba50-4ce7-9439-34653001fc3b','{"NBVersionCheck":{"MinVersion":"0.31.0"}}');
 INSERT INTO posture_checks VALUES('cspnllq7qv95uq1r4k90','Allow Berlin and Deny local network 172.16.1.0/24','','bf1c8084-ba50-4ce7-9439-34653001fc3b','{"GeoLocationCheck":{"Locations":[{"CountryCode":"DE","CityName":"Berlin"}],"Action":"allow"},"PeerNetworkRangeCheck":{"Action":"deny","Ranges":["172.16.1.0/24"]}}');
 INSERT INTO name_server_groups VALUES('csqdelq7qv97ncu7d9t0','bf1c8084-ba50-4ce7-9439-34653001fc3b','Google DNS','Google DNS Servers','[{"IP":"8.8.8.8","NSType":1,"Port":53},{"IP":"8.8.4.4","NSType":1,"Port":53}]','["cfefqs706sqkneg59g2g"]',1,'[]',1,0);
+INSERT INTO routes VALUES('ct03t427qv97vmtmglog','bf1c8084-ba50-4ce7-9439-34653001fc3b','"10.10.0.0/16"',NULL,0,'aws-eu-central-1-vpc','Production VPC in Frankfurt','ct03r5q7qv97vmtmglng',NULL,1,1,9999,1,'["cfefqs706sqkneg59g2g"]',NULL);
 INSERT INTO installations VALUES(1,'');
diff --git a/management/server/user_test.go b/management/server/user_test.go
index 66bdc168..7508e060 100644
--- a/management/server/user_test.go
+++ b/management/server/user_test.go
@@ -56,7 +56,7 @@ func TestUser_CreatePAT_ForSameUser(t *testing.T) {
 	}
 	t.Cleanup(cleanup)
 
-	account := newAccountWithId(context.Background(), mockAccountID, mockUserID, "")
+	account := newAccountWithId(context.Background(), mockAccountID, mockUserID, "", false)
 
 	err = s.SaveAccount(context.Background(), account)
 	if err != nil {
@@ -103,7 +103,7 @@ func TestUser_CreatePAT_ForDifferentUser(t *testing.T) {
 	}
 	t.Cleanup(cleanup)
 
-	account := newAccountWithId(context.Background(), mockAccountID, mockUserID, "")
+	account := newAccountWithId(context.Background(), mockAccountID, mockUserID, "", false)
 	account.Users[mockTargetUserId] = &types.User{
 		Id:            mockTargetUserId,
 		IsServiceUser: false,
@@ -131,7 +131,7 @@ func TestUser_CreatePAT_ForServiceUser(t *testing.T) {
 	}
 	t.Cleanup(cleanup)
 
-	account := newAccountWithId(context.Background(), mockAccountID, mockUserID, "")
+	account := newAccountWithId(context.Background(), mockAccountID, mockUserID, "", false)
 	account.Users[mockTargetUserId] = &types.User{
 		Id:            mockTargetUserId,
 		IsServiceUser: true,
@@ -163,7 +163,7 @@ func TestUser_CreatePAT_WithWrongExpiration(t *testing.T) {
 	}
 	t.Cleanup(cleanup)
 
-	account := newAccountWithId(context.Background(), mockAccountID, mockUserID, "")
+	account := newAccountWithId(context.Background(), mockAccountID, mockUserID, "", false)
 
 	err = store.SaveAccount(context.Background(), account)
 	if err != nil {
@@ -188,7 +188,7 @@ func TestUser_CreatePAT_WithEmptyName(t *testing.T) {
 	}
 	t.Cleanup(cleanup)
 
-	account := newAccountWithId(context.Background(), mockAccountID, mockUserID, "")
+	account := newAccountWithId(context.Background(), mockAccountID, mockUserID, "", false)
 
 	err = store.SaveAccount(context.Background(), account)
 	if err != nil {
@@ -213,7 +213,7 @@ func TestUser_DeletePAT(t *testing.T) {
 	}
 	t.Cleanup(cleanup)
 
-	account := newAccountWithId(context.Background(), mockAccountID, mockUserID, "")
+	account := newAccountWithId(context.Background(), mockAccountID, mockUserID, "", false)
 	account.Users[mockUserID] = &types.User{
 		Id: mockUserID,
 		PATs: map[string]*types.PersonalAccessToken{
@@ -256,7 +256,7 @@ func TestUser_GetPAT(t *testing.T) {
 	}
 	t.Cleanup(cleanup)
 
-	account := newAccountWithId(context.Background(), mockAccountID, mockUserID, "")
+	account := newAccountWithId(context.Background(), mockAccountID, mockUserID, "", false)
 	account.Users[mockUserID] = &types.User{
 		Id:        mockUserID,
 		AccountID: mockAccountID,
@@ -296,7 +296,7 @@ func TestUser_GetAllPATs(t *testing.T) {
 	}
 	t.Cleanup(cleanup)
 
-	account := newAccountWithId(context.Background(), mockAccountID, mockUserID, "")
+	account := newAccountWithId(context.Background(), mockAccountID, mockUserID, "", false)
 	account.Users[mockUserID] = &types.User{
 		Id:        mockUserID,
 		AccountID: mockAccountID,
@@ -406,7 +406,7 @@ func TestUser_CreateServiceUser(t *testing.T) {
 	}
 	t.Cleanup(cleanup)
 
-	account := newAccountWithId(context.Background(), mockAccountID, mockUserID, "")
+	account := newAccountWithId(context.Background(), mockAccountID, mockUserID, "", false)
 
 	err = store.SaveAccount(context.Background(), account)
 	if err != nil {
@@ -453,7 +453,7 @@ func TestUser_CreateUser_ServiceUser(t *testing.T) {
 	}
 	t.Cleanup(cleanup)
 
-	account := newAccountWithId(context.Background(), mockAccountID, mockUserID, "")
+	account := newAccountWithId(context.Background(), mockAccountID, mockUserID, "", false)
 
 	err = store.SaveAccount(context.Background(), account)
 	if err != nil {
@@ -501,7 +501,7 @@ func TestUser_CreateUser_RegularUser(t *testing.T) {
 	}
 	t.Cleanup(cleanup)
 
-	account := newAccountWithId(context.Background(), mockAccountID, mockUserID, "")
+	account := newAccountWithId(context.Background(), mockAccountID, mockUserID, "", false)
 
 	err = store.SaveAccount(context.Background(), account)
 	if err != nil {
@@ -532,7 +532,7 @@ func TestUser_InviteNewUser(t *testing.T) {
 	}
 	t.Cleanup(cleanup)
 
-	account := newAccountWithId(context.Background(), mockAccountID, mockUserID, "")
+	account := newAccountWithId(context.Background(), mockAccountID, mockUserID, "", false)
 
 	err = store.SaveAccount(context.Background(), account)
 	if err != nil {
@@ -639,7 +639,7 @@ func TestUser_DeleteUser_ServiceUser(t *testing.T) {
 			}
 			t.Cleanup(cleanup)
 
-			account := newAccountWithId(context.Background(), mockAccountID, mockUserID, "")
+			account := newAccountWithId(context.Background(), mockAccountID, mockUserID, "", false)
 			account.Users[mockServiceUserID] = tt.serviceUser
 
 			err = store.SaveAccount(context.Background(), account)
@@ -678,7 +678,7 @@ func TestUser_DeleteUser_SelfDelete(t *testing.T) {
 	}
 	t.Cleanup(cleanup)
 
-	account := newAccountWithId(context.Background(), mockAccountID, mockUserID, "")
+	account := newAccountWithId(context.Background(), mockAccountID, mockUserID, "", false)
 
 	err = store.SaveAccount(context.Background(), account)
 	if err != nil {
@@ -705,7 +705,7 @@ func TestUser_DeleteUser_regularUser(t *testing.T) {
 	}
 	t.Cleanup(cleanup)
 
-	account := newAccountWithId(context.Background(), mockAccountID, mockUserID, "")
+	account := newAccountWithId(context.Background(), mockAccountID, mockUserID, "", false)
 
 	targetId := "user2"
 	account.Users[targetId] = &types.User{
@@ -792,7 +792,7 @@ func TestUser_DeleteUser_RegularUsers(t *testing.T) {
 	}
 	t.Cleanup(cleanup)
 
-	account := newAccountWithId(context.Background(), mockAccountID, mockUserID, "")
+	account := newAccountWithId(context.Background(), mockAccountID, mockUserID, "", false)
 
 	targetId := "user2"
 	account.Users[targetId] = &types.User{
@@ -952,7 +952,7 @@ func TestDefaultAccountManager_GetUser(t *testing.T) {
 	}
 	t.Cleanup(cleanup)
 
-	account := newAccountWithId(context.Background(), mockAccountID, mockUserID, "")
+	account := newAccountWithId(context.Background(), mockAccountID, mockUserID, "", false)
 
 	err = store.SaveAccount(context.Background(), account)
 	if err != nil {
@@ -988,7 +988,7 @@ func TestDefaultAccountManager_ListUsers(t *testing.T) {
 	}
 	t.Cleanup(cleanup)
 
-	account := newAccountWithId(context.Background(), mockAccountID, mockUserID, "")
+	account := newAccountWithId(context.Background(), mockAccountID, mockUserID, "", false)
 	account.Users["normal_user1"] = types.NewRegularUser("normal_user1")
 	account.Users["normal_user2"] = types.NewRegularUser("normal_user2")
 
@@ -1030,7 +1030,7 @@ func TestDefaultAccountManager_ExternalCache(t *testing.T) {
 	}
 	t.Cleanup(cleanup)
 
-	account := newAccountWithId(context.Background(), mockAccountID, mockUserID, "")
+	account := newAccountWithId(context.Background(), mockAccountID, mockUserID, "", false)
 	externalUser := &types.User{
 		Id:     "externalUser",
 		Role:   types.UserRoleUser,
@@ -1098,7 +1098,7 @@ func TestUser_GetUsersFromAccount_ForAdmin(t *testing.T) {
 	}
 	t.Cleanup(cleanup)
 
-	account := newAccountWithId(context.Background(), mockAccountID, mockUserID, "")
+	account := newAccountWithId(context.Background(), mockAccountID, mockUserID, "", false)
 	account.Users[mockServiceUserID] = &types.User{
 		Id:            mockServiceUserID,
 		Role:          "user",
@@ -1132,7 +1132,7 @@ func TestUser_GetUsersFromAccount_ForUser(t *testing.T) {
 	}
 	t.Cleanup(cleanup)
 
-	account := newAccountWithId(context.Background(), mockAccountID, mockUserID, "")
+	account := newAccountWithId(context.Background(), mockAccountID, mockUserID, "", false)
 	account.Users[mockServiceUserID] = &types.User{
 		Id:            mockServiceUserID,
 		Role:          "user",
@@ -1499,7 +1499,7 @@ func TestSaveOrAddUser_PreventAccountSwitch(t *testing.T) {
 	}
 	t.Cleanup(cleanup)
 
-	account1 := newAccountWithId(context.Background(), "account1", "ownerAccount1", "")
+	account1 := newAccountWithId(context.Background(), "account1", "ownerAccount1", "", false)
 	targetId := "user2"
 	account1.Users[targetId] = &types.User{
 		Id:              targetId,
@@ -1508,7 +1508,7 @@ func TestSaveOrAddUser_PreventAccountSwitch(t *testing.T) {
 	}
 	require.NoError(t, s.SaveAccount(context.Background(), account1))
 
-	account2 := newAccountWithId(context.Background(), "account2", "ownerAccount2", "")
+	account2 := newAccountWithId(context.Background(), "account2", "ownerAccount2", "", false)
 	require.NoError(t, s.SaveAccount(context.Background(), account2))
 
 	permissionsManager := permissions.NewManager(s)
@@ -1535,7 +1535,7 @@ func TestDefaultAccountManager_GetCurrentUserInfo(t *testing.T) {
 	}
 	t.Cleanup(cleanup)
 
-	account1 := newAccountWithId(context.Background(), "account1", "account1Owner", "")
+	account1 := newAccountWithId(context.Background(), "account1", "account1Owner", "", false)
 	account1.Settings.RegularUsersViewBlocked = false
 	account1.Users["blocked-user"] = &types.User{
 		Id:        "blocked-user",
@@ -1557,7 +1557,7 @@ func TestDefaultAccountManager_GetCurrentUserInfo(t *testing.T) {
 	}
 	require.NoError(t, store.SaveAccount(context.Background(), account1))
 
-	account2 := newAccountWithId(context.Background(), "account2", "account2Owner", "")
+	account2 := newAccountWithId(context.Background(), "account2", "account2Owner", "", false)
 	account2.Users["settings-blocked-user"] = &types.User{
 		Id:   "settings-blocked-user",
 		Role: types.UserRoleUser,
diff --git a/version/update_test.go b/version/update_test.go
index 4537ce22..a733714c 100644
--- a/version/update_test.go
+++ b/version/update_test.go
@@ -9,6 +9,8 @@ import (
 	"time"
 )
 
+const httpAgent = "pkg/test"
+
 func TestNewUpdate(t *testing.T) {
 	version = "1.0.0"
 	svr := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
@@ -21,7 +23,7 @@ func TestNewUpdate(t *testing.T) {
 	wg.Add(1)
 
 	onUpdate := false
-	u := NewUpdate()
+	u := NewUpdate(httpAgent)
 	defer u.StopWatch()
 	u.SetOnUpdateListener(func() {
 		onUpdate = true
@@ -46,7 +48,7 @@ func TestDoNotUpdate(t *testing.T) {
 	wg.Add(1)
 
 	onUpdate := false
-	u := NewUpdate()
+	u := NewUpdate(httpAgent)
 	defer u.StopWatch()
 	u.SetOnUpdateListener(func() {
 		onUpdate = true
@@ -71,7 +73,7 @@ func TestDaemonUpdate(t *testing.T) {
 	wg.Add(1)
 
 	onUpdate := false
-	u := NewUpdate()
+	u := NewUpdate(httpAgent)
 	defer u.StopWatch()
 	u.SetOnUpdateListener(func() {
 		onUpdate = true