Skip to content

[client] Make lazy connections opt-out via NB_LAZY_CONN#6617

Merged
lixmal merged 3 commits into
mainfrom
lazy-conn-opt-out
Jul 2, 2026
Merged

[client] Make lazy connections opt-out via NB_LAZY_CONN#6617
lixmal merged 3 commits into
mainfrom
lazy-conn-opt-out

Conversation

@lixmal

@lixmal lixmal commented Jun 30, 2026

Copy link
Copy Markdown
Collaborator

Describe your changes

Lazy connections are becoming the default (enabled by management for new accounts in #6571). This reworks the client-side control so lazy is opt-out, driven by management with explicit local overrides, and removes the old experimental opt-in.

Resolution order: NB_LAZY_CONN env var (if set) > MDM policy override > management feature flag > off.

  • Add the tri-state NB_LAZY_CONN env var (on/off/unset); set, it overrides the management feature flag in both directions; unset defers to management
  • Add an MDM policy override (lazyConnection key, on/off) with the same semantics, below the env var
  • Remove the experimental NB_ENABLE_EXPERIMENTAL_LAZY_CONN variable
  • Retire the --enable-lazy-connection CLI flag: inert and hidden (kept so existing invocations don't error)
  • Remove the local LazyConnectionEnabled setting end to end (config, daemon apply, desktop UI checkbox, MDM bool, and login telemetry); lazy is no longer a persisted local toggle
  • Point the Android/iOS env exports at the new variable name

The per-peer lazy flag previously reported to management via login metadata is no longer sent (it reflected the removed local setting). Runtime lazy status in netbird status is unaffected.

Issue ticket number and link

Complements #6571.

Stack

Checklist

  • Is it a bug fix
  • Is a typo/documentation fix
  • Is a feature enhancement
  • It is a refactor
  • Created tests that fail without the change (if possible)
  • This change does not modify the public API, gRPC protocols, functionality behavior, CLI / service flags, or introduce a new feature — OR I have discussed it with the NetBird team beforehand (link the issue / Slack thread in the description). See CONTRIBUTING.md.

By submitting this pull request, you confirm that you have read and agree to the terms of the Contributor License Agreement.

Documentation

Select exactly one:

  • I added/updated documentation for this change
  • Documentation is not needed for this change (explain why)

Docs PR URL (required if "docs added" is checked)

Paste the PR link from https://github.com/netbirdio/docs here:

netbirdio/docs#821

Summary by CodeRabbit

  • New Features
    • Lazy connections are now controlled via NB_LAZY_CONN (on/off/unset), with unset deferring to management using consistent precedence.
    • Added MDM policy support for lazyConnection, including normalization of on/off/yes/no values.
  • Bug Fixes
    • Local overrides reliably take priority; remote changes and related status updates are ignored when overridden.
  • UI / CLI
    • Removed the lazy-connections toggle from the tray UI and settings sync.
    • Deprecated --enable-lazy-connection; it’s no longer used by up.
  • Tests / Other
    • Expanded unit test coverage for env parsing, override resolution, and MDM mapping; updated debug output.

@coderabbitai

coderabbitai Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 226e04c9-2236-447a-9268-6108b9e569e3

📥 Commits

Reviewing files that changed from the base of the PR and between a007c45 and 9be9f0d.

📒 Files selected for processing (2)
  • client/mdm/policy.go
  • client/mdm/policy_test.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • client/mdm/policy_test.go
  • client/mdm/policy.go

📝 Walkthrough

Walkthrough

Replaces the lazy-connection boolean path with tri-state NB_LAZY_CONN, threads the new state through client config and connection management, and removes lazy-connection propagation from server, CLI, UI, Android, iOS, and management metadata.

Changes

LazyConn Tri-State Override

Layer / File(s) Summary
lazyconn env, config, and policy contract
client/internal/lazyconn/env.go, client/internal/profilemanager/config.go, client/mdm/policy.go, client/mdm/canonical_loaders.go, client/internal/lazyconn/env_test.go, client/internal/profilemanager/config_mdm_test.go, client/mdm/policy_test.go
Replaces the boolean env helper with NB_LAZY_CONN tri-state parsing, changes profile config storage to a runtime lazy-connection string, and adds the MDM policy key plus canonical key handling and coverage.
client lazy-connection resolution and propagation
client/internal/conn_mgr.go, client/internal/connect.go, client/internal/engine.go, client/system/info.go, client/internal/auth/auth.go, client/internal/debug/debug.go, client/internal/debug/debug_test.go, client/internal/conn_mgr_test.go
Replaces the local lazy-connection boolean with a resolved lazyForce override in ConnMgr, parses the new config string into engine state, and updates auth, debug, and system info handling to the new field shape.
server config, metadata, and override handling
client/server/mdm.go, client/server/server.go, shared/management/client/grpc.go, client/server/setconfig_test.go
Stops propagating the lazy-connection field through server status, config mapping, and override detection; updates MDM restart handling, SetConfig coverage tests, and management peer metadata to match the removed field.
CLI, UI, and mobile surface updates
client/android/env_list.go, client/ios/NetBirdSDK/env_list.go, client/cmd/root.go, client/cmd/up.go, client/ui/const.go, client/ui/client_ui.go, client/ui/event_handler.go
Updates Android and iOS env key exports, deprecates the CLI flag, removes lazy-connection request wiring from up, and removes the UI toggle and event handling for the lazy-connection menu item.

Estimated code review effort: 4 (Complex) | ~45 minutes

Possibly related PRs

  • netbirdio/netbird#5310: Updates the mobile-exported lazy-connection env key wiring that this change repoints again.
  • netbirdio/netbird#6374: Extends the MDM policy parsing path that this change adds lazyConnection handling to.

Suggested reviewers: pappz, riccardomanfrin

Poem

🐇 I hopped through NB_LAZY_CONN with care,
On, off, or unset — each state now has air.
The old checkbox faded, the flags found their place,
And lazy connections now know their own pace.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 68.75% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and clearly describes the main change: making lazy connections opt-out via NB_LAZY_CONN.
Description check ✅ Passed The description includes all required sections and a sufficiently detailed change summary, issue reference, checklist, and documentation link.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch lazy-conn-opt-out

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
client/internal/conn_mgr.go (1)

70-82: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Clear the lazy-connection status on every inactive path.

Start() and UpdatedRemoteFeatureFlag() now no-op in several “manager not running” cases (lazyForceNone, Rosenpass blocking startup, and remote enabled=false while lazyConnMgr is nil) without calling UpdateLazyConnection(false). Since ConnMgr receives a shared *peer.Status, a stale true can survive reconfiguration and report lazy connections as enabled even though no manager is active.

💡 Proposed fix
 switch e.force {
 case lazyForceOff:
 	log.Infof("lazy connection manager is disabled by %s", lazyconn.EnvLazyConn)
 	e.statusRecorder.UpdateLazyConnection(false)
 	return
 case lazyForceNone:
 	log.Infof("lazy connection manager is managed by the management feature flag")
+	e.statusRecorder.UpdateLazyConnection(false)
 	return
 }
 
 if e.rosenpassEnabled {
 	log.Warnf("rosenpass connection manager is enabled, lazy connection manager will not be started")
+	e.statusRecorder.UpdateLazyConnection(false)
 	return
 }
@@
 	if enabled {
@@
 		if e.rosenpassEnabled {
 			log.Infof("rosenpass connection manager is enabled, lazy connection manager will not be started")
+			e.statusRecorder.UpdateLazyConnection(false)
 			return nil
 		}
@@
 	} else {
 		if e.lazyConnMgr == nil {
+			e.statusRecorder.UpdateLazyConnection(false)
 			return nil
 		}

Also applies to: 93-119

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@client/internal/conn_mgr.go` around lines 70 - 82, Clear the lazy-connection
status whenever the manager is not active. In ConnMgr.Start and
UpdatedRemoteFeatureFlag, make sure every early return path that leaves the lazy
connection manager stopped (lazyForceNone, rosenpassEnabled blocking startup,
and remote enabled=false when lazyConnMgr is nil) calls
statusRecorder.UpdateLazyConnection(false) before returning. Use the existing
ConnMgr, Start, UpdatedRemoteFeatureFlag, and UpdateLazyConnection symbols to
update all inactive branches consistently so a stale true cannot remain in
shared peer.Status.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@client/internal/conn_mgr.go`:
- Around line 70-82: Clear the lazy-connection status whenever the manager is
not active. In ConnMgr.Start and UpdatedRemoteFeatureFlag, make sure every early
return path that leaves the lazy connection manager stopped (lazyForceNone,
rosenpassEnabled blocking startup, and remote enabled=false when lazyConnMgr is
nil) calls statusRecorder.UpdateLazyConnection(false) before returning. Use the
existing ConnMgr, Start, UpdatedRemoteFeatureFlag, and UpdateLazyConnection
symbols to update all inactive branches consistently so a stale true cannot
remain in shared peer.Status.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: fcaf8b21-6c2d-4a3f-ad3d-e60f80dcb89e

📥 Commits

Reviewing files that changed from the base of the PR and between 4ef6529 and fb71d70.

📒 Files selected for processing (8)
  • client/android/env_list.go
  • client/cmd/root.go
  • client/internal/conn_mgr.go
  • client/internal/conn_mgr_test.go
  • client/internal/lazyconn/env.go
  • client/internal/lazyconn/env_test.go
  • client/ios/NetBirdSDK/env_list.go
  • client/ui/const.go

@lixmal
lixmal force-pushed the lazy-conn-opt-out branch from fb71d70 to 4791267 Compare June 30, 2026 10:44
@github-actions

github-actions Bot commented Jun 30, 2026

Copy link
Copy Markdown

Release artifacts

Built for PR head 9be9f0d in workflow run #16420.

Artifact Link
All release artifacts Download
Linux packages Download
Windows packages Download
macOS packages Download
UI artifacts Download
UI macOS artifacts Download

GHCR images (amd64)

No GHCR images were pushed.

This comment is updated by the Release workflow. Artifact links expire according to the workflow retention policy.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
client/internal/conn_mgr_test.go (1)

10-23: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add precedence coverage for local config fallback.

The table covers NB_LAZY_CONN, but not the documented fallback to local config or env-over-local-config precedence. Add cases for env unset + local config on/off, plus env set overriding local config.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@client/internal/conn_mgr_test.go` around lines 10 - 23, Add test coverage for
the `resolveLazyForce` precedence rules around local config fallback. Extend
`TestResolveLazyForce` to include cases where `NB_LAZY_CONN` is unset and the
local config forces on/off, as well as cases where `NB_LAZY_CONN` is set and
should override the local config in both directions. Use the existing
`resolveLazyForce` and `lazyForce` table pattern to verify the documented
env-over-local-config behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@client/internal/conn_mgr_test.go`:
- Around line 10-23: Add test coverage for the `resolveLazyForce` precedence
rules around local config fallback. Extend `TestResolveLazyForce` to include
cases where `NB_LAZY_CONN` is unset and the local config forces on/off, as well
as cases where `NB_LAZY_CONN` is set and should override the local config in
both directions. Use the existing `resolveLazyForce` and `lazyForce` table
pattern to verify the documented env-over-local-config behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 027fb85a-19af-4542-895e-f46fa5c94b0b

📥 Commits

Reviewing files that changed from the base of the PR and between fb71d70 and 4791267.

📒 Files selected for processing (9)
  • client/android/env_list.go
  • client/cmd/root.go
  • client/cmd/up.go
  • client/internal/conn_mgr.go
  • client/internal/conn_mgr_test.go
  • client/internal/lazyconn/env.go
  • client/internal/lazyconn/env_test.go
  • client/ios/NetBirdSDK/env_list.go
  • client/ui/const.go
💤 Files with no reviewable changes (1)
  • client/cmd/up.go
✅ Files skipped from review due to trivial changes (3)
  • client/ui/const.go
  • client/android/env_list.go
  • client/cmd/root.go
🚧 Files skipped from review as they are similar to previous changes (3)
  • client/internal/lazyconn/env.go
  • client/ios/NetBirdSDK/env_list.go
  • client/internal/lazyconn/env_test.go

@lixmal
lixmal force-pushed the lazy-conn-opt-out branch from 68690e9 to 159e573 Compare June 30, 2026 15:01

@riccardomanfrin riccardomanfrin left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you need to add the new key in canonical_loaders.go:13

Otherwise it will be discarded as unknown for darwin and windows

}
}

if v, ok := policy.GetString(mdm.KeyLazyConnection); ok {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if we use GetBool we behave like the other bools (e.g. we allow also for things like "yes"/"no")

@lixmal lixmal Jul 2, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in a007c45

Comment thread client/internal/lazyconn/env.go Outdated
if err != nil {
log.Warnf("failed to parse %s: %v", EnvEnableLazyConn, err)
return false
log.Warnf("failed to parse %s value %q: %v", EnvLazyConn, raw, err)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this was a previous mistake but here we might log that the value might come either from NB_ env OR from MDM... might allow faster recon of why a flag is there

@lixmal lixmal Jul 2, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in a007c45

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
client/mdm/policy.go (1)

66-75: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Case-sensitive value lookup could silently ignore MDM policy values.

GetBool's string branch looks up boolStringLiterals[t] using the raw value. A capitalized value (e.g. "On", "TRUE", "Yes") — plausible from plist/registry-authored MDM configs — won't match, so GetBool returns (false, false) and callers treat the key as unset rather than applying the intended override. This is inconsistent with the doc comment's promise of tolerant parsing and with the case-insensitive key handling already used in canonicalKey (client/mdm/canonical_loaders.go).

🐛 Proposed fix to normalize case before lookup
 	case string:
-		b, known := boolStringLiterals[t]
+		b, known := boolStringLiterals[strings.ToLower(strings.TrimSpace(t))]
 		return b, known

Also applies to: 160-180

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@client/mdm/policy.go` around lines 66 - 75, Normalize boolean string parsing
in GetBool so capitalized MDM values are not treated as missing. Update the
string branch to compare against boolStringLiterals using a lowercased value,
and keep the tolerant parsing behavior aligned with the doc comment and
canonicalKey’s case-insensitive handling. Use the GetBool method and
boolStringLiterals map in client/mdm/policy.go as the anchor points for the
change.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@client/mdm/policy.go`:
- Around line 66-75: Normalize boolean string parsing in GetBool so capitalized
MDM values are not treated as missing. Update the string branch to compare
against boolStringLiterals using a lowercased value, and keep the tolerant
parsing behavior aligned with the doc comment and canonicalKey’s
case-insensitive handling. Use the GetBool method and boolStringLiterals map in
client/mdm/policy.go as the anchor points for the change.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: bbefb048-cf46-4ba4-a8c5-60b20d4d644a

📥 Commits

Reviewing files that changed from the base of the PR and between 159e573 and a007c45.

📒 Files selected for processing (6)
  • client/internal/lazyconn/env.go
  • client/internal/profilemanager/config.go
  • client/internal/profilemanager/config_mdm_test.go
  • client/mdm/canonical_loaders.go
  • client/mdm/policy.go
  • client/mdm/policy_test.go
✅ Files skipped from review due to trivial changes (1)
  • client/mdm/policy_test.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • client/internal/lazyconn/env.go
  • client/internal/profilemanager/config.go

riccardomanfrin
riccardomanfrin previously approved these changes Jul 2, 2026
@sonarqubecloud

sonarqubecloud Bot commented Jul 2, 2026

Copy link
Copy Markdown

@lixmal
lixmal merged commit 1d8b5f6 into main Jul 2, 2026
44 of 45 checks passed
@lixmal
lixmal deleted the lazy-conn-opt-out branch July 2, 2026 08:58
pappz added a commit that referenced this pull request Jul 3, 2026
Lazy connections are now opt-out and controlled centrally by NB_LAZY_CONN,
MDM policy (lazyConnection), and the management feature flag (#6617). The
per-device UI toggle no longer fits this model: after #6617 the daemon stopped
persisting the setting and dropped it from GetConfig, so the Wails toggle always
read back OFF and its writes did not survive a restart.

Remove the toggle and the orphaned runtime plumbing, matching what main did for
the Fyne UI:
- drop the toggle from SettingsNetwork and the lazy i18n keys
- drop LazyConnectionEnabled from the UI settings service and SetConfig request
- drop the runtime-apply block in server.SetConfig
- delete Engine.SetLazyConnEnabled and ConnMgr.SetLocalLazyConn

The proto fields and FullStatus status reporting are left intact.
@mlsmaycon mlsmaycon mentioned this pull request Jul 6, 2026
8 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants