We clock the next Offer initiation to the OnDataPathRekeyed, so we have 2 minutes
ahead of us to do our attempts and stuff before to give up.
On failure, we will know because we will not receive a new answer.. but more importantly
the wg handshake will fail :D
Define OnDataPathRekeyed event to transition from control plane path to data plane path over the WG tunnel.
Keep confirm ALWAYS on NEW established WG tunnel (posthandshake with rekeying). We keep an active method
irrelevant of the WG handshake (we might decide that the indirect wg handshake is sufficient in the future).
Optimistic commit on responder(when sending answer), while on initiator we set it on getting the answer
- Have just one manager => one lock
- Session state is needed in driver to => we have it available now.
- Isomorphically align to rosenpass components and functionality
File Role rosenpass equivalent
kem.go primitive pure X25519MLKEM768 crypto.go/handshake
message.go Offer/Answer/Confirm + Encode/Decode messages.go
manager.go Manager stateful, single lock server logic
callbacks.go WGCallbackHandler (seam output) Handler
Transport (interfaccia) seam trasporto pluggable Conn
## Describe your changes
On SNI hosts that report icon clicks via Activate (KDE Plasma, Waybar),
Wails fired the left-click handler on every dbusmenu 'opened' event, so
a right click opened the tray menu and immediately raised the main
window, which stole focus and closed the menu.
Host Left click Right click
KDE Plasma, Waybar main window (Activate) menu (host-rendered)
GNOME Shell + AppIndicator menu only menu only
Minimal WMs via XEmbed host main window (Activate) XEmbed GTK popup
Point the wails/v3 replace at the netbirdio fork (v3.0.0-beta.3 plus the
fix): once the host has sent Activate/SecondaryActivate, a menu open no
longer fires the click handler, while AppIndicator-only hosts that
signal clicks solely via 'opened' keep the old behavior.
## Issue ticket number and link
<!--
Required for anything that changes behavior. Link the issue (or the
validated
discussion it came from) that the NetBird team already agreed on. See
https://github.com/netbirdio/netbird/blob/main/CONTRIBUTING.md#ticket-first-pr-second
-->
## Stack
<!-- branch-stack -->
### Checklist
- [x] 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)
- [ ] I ran and tested this change locally — I did not rely on CI to
find out whether it works
- [ ] This PR has a single purpose (not a fix + refactor + feature in
one)
- [ ] This change is a trivial fix, **OR** it links an issue the NetBird
team agreed on beforehand. Changes to the public API, gRPC protocols,
functionality behavior, CLI / service flags, or new features always need
that agreement first. See
[CONTRIBUTING.md](https://github.com/netbirdio/netbird/blob/main/CONTRIBUTING.md#ticket-first-pr-second).
> By submitting this pull request, you confirm that you have read and
agree to the terms of the [Contributor License
Agreement](https://github.com/netbirdio/netbird/blob/main/CONTRIBUTOR_LICENSE_AGREEMENT.md).
## Documentation
Select exactly one:
- [ ] I added/updated documentation for this change
- [x] 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:
https://github.com/netbirdio/docs/pull/__
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
## Chores
* Updated underlying application components to support compatibility and
ongoing maintenance.
* Clarified Linux tray interaction documentation, including
platform-specific left- and right-click behavior and menu activation.
* No user-facing features, workflow changes, or visual updates are
included.
* Existing Linux tray behavior remains unchanged.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Describe your changes
After the SSO session expires, the daemon tears the engine down
permanently
(management returns `PermissionDenied` → `runCancel()` → the retry loop
exits
for good). The "Session expired" dialog's Login button still drove the
extend-session flow, which requires a live engine: the user completed
the full
browser SSO + 2FA round trip only to get
`Failed to extend the session — engine is not initialised`, with no way
out
other than quitting and relaunching the client.
Reproduce:
1. Log in on a desktop client with session expiration enabled (e.g. 16h
TTL).
2. Let the session expire (e.g. leave the machine asleep overnight).
3. Wake it, click **Login** on the "Session expired" dialog, complete
SSO + 2FA.
4. The error dialog appears and every retry fails the same way.
Changes:
- The expired branch of the session-expiration dialog now emits
`trigger-login`, driving the full `Login → SSO → Up` sequence that
rebuilds
the client, instead of the extend flow (an expired session can no longer
be
extended).
- `RequestExtendAuthSession` fails fast when the engine is already gone,
so the
browser/2FA round trip is not wasted on a doomed extend.
- The expired tray row navigated the main window to `/#/login`, a route
that
does not exist and fell through to the main page without starting a
login;
it now emits `trigger-login` as well.
## Issue ticket number and link
<!--
Required for anything that changes behavior. Link the issue (or the
validated
discussion it came from) that the NetBird team already agreed on. See
https://github.com/netbirdio/netbird/blob/main/CONTRIBUTING.md#ticket-first-pr-second
-->
## Stack
<!-- branch-stack -->
### Checklist
- [x] 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)
- [ ] I ran and tested this change locally — I did not rely on CI to
find out whether it works
- [ ] This PR has a single purpose (not a fix + refactor + feature in
one)
- [ ] This change is a trivial fix, **OR** it links an issue the NetBird
team agreed on beforehand. Changes to the public API, gRPC protocols,
functionality behavior, CLI / service flags, or new features always need
that agreement first. See
[CONTRIBUTING.md](https://github.com/netbirdio/netbird/blob/main/CONTRIBUTING.md#ticket-first-pr-second).
> By submitting this pull request, you confirm that you have read and
agree to the terms of the [Contributor License
Agreement](https://github.com/netbirdio/netbird/blob/main/CONTRIBUTOR_LICENSE_AGREEMENT.md).
## Documentation
Select exactly one:
- [ ] I added/updated documentation for this change
- [x] 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:
https://github.com/netbirdio/docs/pull/__
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **Bug Fixes**
- Improved session extension handling when the client engine is
unavailable by prompting users to log in again.
- Updated expired-session behavior to trigger the standard login flow,
providing a more consistent sign-in experience.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Describe your changes
The expected paths were hardcoded with Unix separators while
profileAccountPathFor builds the result with filepath.Join, so the
comparison failed on Windows. Derive the expectations with
filepath.FromSlash to keep the test platform-independent.
## Issue ticket number and link
<!--
Required for anything that changes behavior. Link the issue (or the
validated
discussion it came from) that the NetBird team already agreed on. See
https://github.com/netbirdio/netbird/blob/main/CONTRIBUTING.md#ticket-first-pr-second
-->
## Stack
<!-- branch-stack -->
### Checklist
- [x] 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)
- [ ] I ran and tested this change locally — I did not rely on CI to
find out whether it works
- [ ] This PR has a single purpose (not a fix + refactor + feature in
one)
- [ ] This change is a trivial fix, **OR** it links an issue the NetBird
team agreed on beforehand. Changes to the public API, gRPC protocols,
functionality behavior, CLI / service flags, or new features always need
that agreement first. See
[CONTRIBUTING.md](https://github.com/netbirdio/netbird/blob/main/CONTRIBUTING.md#ticket-first-pr-second).
> By submitting this pull request, you confirm that you have read and
agree to the terms of the [Contributor License
Agreement](https://github.com/netbirdio/netbird/blob/main/CONTRIBUTOR_LICENSE_AGREEMENT.md).
## Documentation
Select exactly one:
- [ ] I added/updated documentation for this change
- [x] 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:
https://github.com/netbirdio/docs/pull/__
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Tests**
* Updated profile account path test expectations to use
platform-appropriate path separators, improving test reliability across
operating systems.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Describe your changes
Probe the daemon login with IsLoginRequired
The Login probe attemptLogin(ctx, "", "") on an unregistered peer ends
in registerPeer with no setup key and no JWT, which fails locally with
InvalidArgument before reaching Management. Since #6983 classified that
as StatusLoginFailed and returned early, every setup-key enrolment and
every expired-session SSO re-login aborted before using its credentials,
breaking all netbird-cloud e2e runs from commit e90be36cd.
IsLoginRequired asks the question the probe actually means - is the
peer's key alone still accepted - and reports Management's refusal as a
decision (needsLogin) instead of an error, the same pattern
foregroundLogin, Android and iOS already use.
## Issue ticket number and link
<!--
Required for anything that changes behavior. Link the issue (or the
validated
discussion it came from) that the NetBird team already agreed on. See
https://github.com/netbirdio/netbird/blob/main/CONTRIBUTING.md#ticket-first-pr-second
-->
## Stack
<!-- branch-stack -->
### Checklist
- [x] 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)
- [ ] I ran and tested this change locally — I did not rely on CI to
find out whether it works
- [ ] This PR has a single purpose (not a fix + refactor + feature in
one)
- [ ] This change is a trivial fix, **OR** it links an issue the NetBird
team agreed on beforehand. Changes to the public API, gRPC protocols,
functionality behavior, CLI / service flags, or new features always need
that agreement first. See
[CONTRIBUTING.md](https://github.com/netbirdio/netbird/blob/main/CONTRIBUTING.md#ticket-first-pr-second).
> By submitting this pull request, you confirm that you have read and
agree to the terms of the [Contributor License
Agreement](https://github.com/netbirdio/netbird/blob/main/CONTRIBUTOR_LICENSE_AGREEMENT.md).
## Documentation
Select exactly one:
- [ ] I added/updated documentation for this change
- [x] 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:
https://github.com/netbirdio/docs/pull/__
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Bug Fixes**
* Improved login handling when Management connectivity checks fail.
* Prevented unnecessary SSO prompts for already-authenticated sessions.
* Preserved setup-key login behavior while ensuring authentication
attempts proceed correctly.
* Login failures now return a clear failure status when authentication
state cannot be verified.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
The Android binding never recorded which account a profile belongs to,
so every interactive login and every session extend went to the IdP with
no login_hint. With nothing to go on the IdP picks an account itself,
which on a session extend means re-authenticating an account the profile
is already signed in with.
Store the email the PKCE flow already parses out of the ID token, and
pass it back as the hint on later flows. An empty hint stays meaningful:
a fresh profile, or one that was logged out, deliberately leaves the
choice to the IdP, which is how a profile changes accounts. Logout
clears the stored email for that reason — while it is on disk it would
steer the next login straight back into the account just logged out of.
The email is keyed off the profile's config path rather than the active
profile: Auth.login runs in a goroutine, so the active profile can
change under a flow already in flight. It lands in
<profile>.account.json, not the <profile>.state.json desktop uses for
the same data — there the email and the engine's state manager sit in
different directories, but on Android both resolve under files/, and the
state manager rewrites the whole file from its own keys.
## Describe your changes
## Issue ticket number and link
## Stack
<!-- branch-stack -->
### Checklist
- [x] 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](https://github.com/netbirdio/netbird/blob/main/CONTRIBUTING.md#discuss-changes-with-the-netbird-team-first).
> By submitting this pull request, you confirm that you have read and
agree to the terms of the [Contributor License
Agreement](https://github.com/netbirdio/netbird/blob/main/CONTRIBUTOR_LICENSE_AGREEMENT.md).
## Documentation
Select exactly one:
- [ ] I added/updated documentation for this change
- [x] 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:
https://github.com/netbirdio/docs/pull/__
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Added account email and active-status details to Android profile
information.
* Improved SSO sign-in and session renewal by restoring the previously
used account as a login hint.
* Added Android-specific profile email persistence with automatic
cleanup on logout.
* **Bug Fixes**
* Profile email persistence failures now generate warnings without
blocking login or logout.
* Improved handling of missing or unreadable account data and repeated
logout cleanup.
* **Tests**
* Added coverage for account-file naming, email persistence, and logout
behavior.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Describe your changes
NewAuth builds a fresh in-memory configuration on every call, which
means a new WireGuard key each time. The peer registers under that key
and the key is written out, so any peer registered by an earlier call is
orphaned on the server — a client that enrols twice leaves two entries
and owns neither.
It also breaks the enrol-then-run sequence. `RunWithoutLogin` reloads
the configuration from disk through `UpdateOrCreateConfig`, so the
identity that registered is not necessarily the identity that runs, and
the management stream rejects it:
```
failed to login to Management Service: rpc error: code = PermissionDenied
desc = no peer auth method provided, please use a setup key or interactive SSO login
```
followed by a panic in `ConnectClient.run`.
### How it was found
Embedding the Android client in an application that enrols with a setup
key and then runs. Eight orphaned peers accumulated on a self-hosted
management server before the cause was clear, because every restart
registered a new one.
### The change
`NewAuth` passes `ConfigPath` and uses `UpdateOrCreateConfig`, so an
existing configuration is reused and one is only created when absent. A
caller wanting a fresh identity can delete the file — which is what
"forget this account" already does.
### Test
`TestNewAuth_ReusesPersistedIdentity` fails on the current code:
```
--- FAIL: TestNewAuth_ReusesPersistedIdentity (0.00s)
login_test.go:33: private key changed between calls: a second enrolment would orphan the peer registered by the first
```
and passes with the fix. `TestNewAuth_CreatesConfigWhenAbsent` covers
the first-enrolment path being unchanged. Both run in `client/android`
on Linux.
Per CONTRIBUTING, opening directly as a bug fix rather than raising an
issue first.
## Issue ticket number and link
[NET-1465](https://linear.app/netbird/issue/NET-1465/agent-network-rest-api-settings-defaults-bootstrap-via-put-provider)
## Stack
<!-- branch-stack -->
### Checklist
- [x] 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)
- [ ] I ran and tested this change locally — I did not rely on CI to
find out whether it works
- [ ] This PR has a single purpose (not a fix + refactor + feature in
one)
- [ ] This change is a trivial fix, **OR** it links an issue the NetBird
team agreed on beforehand. Changes to the public API, gRPC protocols,
functionality behavior, CLI / service flags, or new features always need
that agreement first. See
[CONTRIBUTING.md](https://github.com/netbirdio/netbird/blob/main/CONTRIBUTING.md#ticket-first-pr-second).
> By submitting this pull request, you confirm that you have read and
agree to the terms of the [Contributor License
Agreement](https://github.com/netbirdio/netbird/blob/main/CONTRIBUTOR_LICENSE_AGREEMENT.md).
## Documentation
Select exactly one:
- [ ] I added/updated documentation for this change
- [x] Documentation is **not needed** for this change (explain why): the
API reference is generated from the OpenAPI spec, which this PR updates
in-repo.
### Docs PR URL (required if "docs added" is checked)
Paste the PR link from https://github.com/netbirdio/docs here:
https://github.com/netbirdio/docs/pull/__
Co-authored-by: Zoltan Papp <zoltan.pmail@gmail.com>
## Describe your changes
Pull fresh TUN settings on Android rebuild instead of push
The Android TUN rebuild consumed state pushed through notifications and
a Java-side snapshot, and both sources were unreliable. The DNS
search-domain notifier fired OnNetworkChanged with an empty string,
which the rebuild handler treated as the new route list, so any search
domain change rebuilt the TUN with zero routes and cut all tunnel
traffic. The rebuild also reused the search domains cached at the last
establish, so search domain updates never reached the TUN at runtime.
Make the notification a pure trigger and let the Java side pull a fresh
snapshot instead. Expose GetTunSettings on the Android SDK client: it
returns the current TUN route ranges, derived on demand by the route
manager from the client routes, the exit-node selection and the fake IP
blocks, together with the DNS search domains. The route notifier keeps
only its last-announced baseline to suppress triggers for unchanged
syncs; the TUN route state is owned by the route manager. SearchDomains
now locks the DNS server mutex since the pull arrives from a Java
thread.
Requires the matching android-client change that switches recreateTUN to
the pull API.
## Issue ticket number and link
## Stack
<!-- branch-stack -->
### Checklist
- [x] 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](https://github.com/netbirdio/netbird/blob/main/CONTRIBUTING.md#discuss-changes-with-the-netbird-team-first).
> By submitting this pull request, you confirm that you have read and
agree to the terms of the [Contributor License
Agreement](https://github.com/netbirdio/netbird/blob/main/CONTRIBUTOR_LICENSE_AGREEMENT.md).
## Documentation
Select exactly one:
- [ ] I added/updated documentation for this change
- [x] 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:
https://github.com/netbirdio/docs/pull/__
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **New Features**
- Added access to current TUN route ranges and DNS search domains.
- TUN settings are returned in a mobile-friendly format for easier
integration.
- **Improvements**
- Route changes are detected and synchronized more reliably.
- Current routing information now reflects active routes, including
supported fake-IP ranges.
- Simplified network initialization for more consistent startup
behavior.
- **API Changes**
- Removed the obsolete network-map retrieval method from the management
client interface.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Describe your changes
In unattended installs/updates there may be no logged-in user, so
there's no context to start the GUI (nor anyone to see it).
The bug is the GUI being started in the wrong user context / inheriting
the wrong `$HOME` (OS mechanics aside).
Today the GUI is started by a per-user LaunchAgent, i.e. on behalf of
the user who logs in — no login ⇒ no GUI.
The patch aligns to this: it launches the GUI on behalf of the logged-in
console user if one exists, otherwise it delegates the launch to the
per-user LaunchAgent at next login.
Additionally it logs when default UI settings are applied.
Note (small caveat): the LaunchAgent auto-starts the GUI at login only
once it's been registered — which happens on the first GUI launch in the
user's context. On an MDM/unattended fresh install done with no user
logged in (where the user has never run the GUI before), they may need
to start it manually once; it self-registers from then on.
## Issue ticket number and link
No public issue — reported internally (community report on Slack: macOS
advanced-view + onboarding reset on every update, esp. via MDM/Munki).
Buggy line on main:
dd2bdc0de3/release_files/darwin_pkg/postinstall (L33)
## Stack
<!-- branch-stack -->
### Checklist
- [x] 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)
> By submitting this pull request, you confirm that you have read and
agree to the terms of the [Contributor License
Agreement](https://github.com/netbirdio/netbird/blob/main/CONTRIBUTOR_LICENSE_AGREEMENT.md).
## Documentation
Select exactly one:
- [ ] I added/updated documentation for this change
- [x] Documentation is **not needed** for this change (explain why)
Internal macOS installer / GUI-launch behavior. No public API, CLI, or
configuration change: the fix only changes the user context the desktop
GUI is launched in after a pkg install/update.
### Docs PR URL (required if "docs added" is checked)
Paste the PR link from https://github.com/netbirdio/docs here:
N/A
<!-- codesmith:footer -->
---
<a
href="https://app.blacksmith.sh/netbirdio/codesmith/netbird/pr/6962"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-dark-v2.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-light-v2.svg"><img
alt="View with [code]smith"
src="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-dark-v2.svg"></picture></a>
<a
href="https://backend.blacksmith.sh/track/enable-autofix?expires=1787923973&installation_model_id=427504&pr_number=6962&repository=netbirdio%2Fnetbird&return_to=https%3A%2F%2Fgithub.com%2Fnetbirdio%2Fnetbird%2Fpull%2F6962&signature=5226bbc7986fc60eb8b4ab77e98ac17a6534864763fc6407048bcbbb1550394b"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-light.svg"><img
alt="Autofix with [code]smith"
src="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-dark.svg"></picture></a>
<sup>Need help on this PR? Tag <code>@codesmith-bot</code> with what you
need. Autofix is disabled.</sup>
<!-- codesmith:autofix:disabled -->
<!-- /codesmith:footer -->
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **Bug Fixes**
- Improved macOS installation and updater UI launching to occur only
when an active, valid GUI console session is detected.
- Prevented UI launches during unattended/system, root, or
login-window-related installs.
- Ensured the app is launched in the correct console-user context, and
skips cleanly when username/UID resolution fails.
- **Improvements**
- Added clearer informational logging when the UI preferences file is
not found and default preferences are used.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Describe your changes
The Android route notifier and the DNS search-domain notifier both
delivered OnNetworkChanged from a fire-and-forget goroutine per update.
Two updates in quick succession could reach the Java side reordered: the
TUN rebuild handler applies them in arrival order and compares against
the last applied parameters, so a stale route set delivered last won as
the final TUN state. This is the same reordering hazard fixed for iOS in
#6454.
Wrap the Android network change listener into the shared tunnelnotifier
FIFO introduced in #6870, the same way RunOniOS does, and deliver both
notifiers synchronously into it. Enqueueing is non-blocking, a single
delivery goroutine preserves order, and calls into Java never overlap.
Also stop hasRouteDiff from sorting the notifier's shared route slices
in place; compare sorted copies instead.
## Issue ticket number and link
## Stack
<!-- branch-stack -->
### Checklist
- [x] 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](https://github.com/netbirdio/netbird/blob/main/CONTRIBUTING.md#discuss-changes-with-the-netbird-team-first).
> By submitting this pull request, you confirm that you have read and
agree to the terms of the [Contributor License
Agreement](https://github.com/netbirdio/netbird/blob/main/CONTRIBUTOR_LICENSE_AGREEMENT.md).
## Documentation
Select exactly one:
- [ ] I added/updated documentation for this change
- [x] 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:
https://github.com/netbirdio/docs/pull/__
The fake IP manager was only created at route manager construction, from the DNS feature flag fetched by the initial GetNetworkMap call. When the flag flipped to true mid-session, UpdateRoutes set useNewDNSRoute but never created the manager, so domain routes added after the flip got a DNS interceptor with a nil fake IP manager.
internalDnatFw only checked for a firewall and GOOS, so the interceptor took the DNAT path and called GetFakeIP/AllocateFakeIP on the nil *fakeip.Manager. These methods lock m.mu first, which is a nil pointer dereference: the first DNS answer for such a route panicked and crashed the VPN service. The fake IP blocks (240.0.0.0/8 and its v6 pair) also never reached the TUN, since only the constructor registered them.
Create the manager and its TUN routes from UpdateRoutes when the flag turns on, notify so the fake IP blocks get into the TUN without a client route change, and treat a nil manager as no internal DNAT.
This is groundwork for removing the initial GetNetworkMap fetch, after which every startup goes through the flag-off-to-on transition.
## Describe your changes
Three fixes to how the desktop GUI keeps the account email that backs
the SSO `login_hint`. Each is independent and reviewable on its own.
**1. Store the email after a GUI SSO login**
The daemon returns the authenticated user's email from `WaitSSOLogin`
but cannot persist it: it runs as root while the per-profile state file
is user-owned. The CLI's `handleSSOLogin` writes it after its own
`WaitSSOLogin`; the GUI path read the value and dropped it.
The profile was therefore left with no email, so `Profiles.List` showed
no account for it, and later logins and session extends went out with no
`login_hint` — leaving the IdP to pick an account instead of reusing the
one the profile belongs to. Mirror the CLI and store it, next to the
`Logout` path that already clears the same file for the same reason.
**2. File the email against the profile the login ran for**
`SetActiveProfileState` resolves the target itself, so it writes to
whichever profile is active when it is called. A GUI SSO login spans
seconds of user interaction in the browser, and the tray stays clickable
throughout: switching profiles in that window left the email filed under
the profile that happened to be active when the flow returned. The wrong
profile then advertised an account it does not own, and offered it as
the `login_hint` next time.
Adds `SetProfileState(id, state)`, the write-side counterpart of the
existing `GetProfileState(id)`, and keeps `SetActiveProfileState` as a
wrapper for callers with no particular profile in mind. `Login` now
reports the profile it resolved so the frontend can hand it back with
the SSO wait, which closes the window.
**3. Delete the email when a profile is removed**
Removing a profile left its state file behind: the daemon deletes what
it owns, but the email file is user-owned and out of reach for a root
daemon — the same split that already puts the `Logout` cleanup on the UI
side.
Beyond the stray file, legacy profiles are keyed by name rather than by
a generated ID, so recreating a profile under a removed one's name
inherited its email — shown as the account in the profile list and sent
as the `login_hint` on the next login.
## Issue ticket number and link
## Stack
<!-- branch-stack -->
### Checklist
- [x] 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](https://github.com/netbirdio/netbird/blob/main/CONTRIBUTING.md#discuss-changes-with-the-netbird-team-first).
> By submitting this pull request, you confirm that you have read and
agree to the terms of the [Contributor License
Agreement](https://github.com/netbirdio/netbird/blob/main/CONTRIBUTOR_LICENSE_AGREEMENT.md).
## Documentation
Select exactly one:
- [ ] I added/updated documentation for this change
- [x] 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:
https://github.com/netbirdio/docs/pull/__
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **New Features**
- SSO login details can now be saved to the specific profile selected
during sign-in.
- Profile state can be managed independently for different profiles.
- **Bug Fixes**
- Removing a profile now also cleans up its associated saved state.
- Cleanup issues no longer prevent successful profile removal and are
handled gracefully.
- Existing active-profile behavior remains unchanged.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Describe your changes
Removes redundant `Login` RPCs from the iOS SDK bindings.
## Issue ticket number and link
## Stack
<!-- branch-stack -->
### Checklist
- [x] 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](https://github.com/netbirdio/netbird/blob/main/CONTRIBUTING.md#discuss-changes-with-the-netbird-team-first).
> By submitting this pull request, you confirm that you have read and
agree to the terms of the [Contributor License
Agreement](https://github.com/netbirdio/netbird/blob/main/CONTRIBUTOR_LICENSE_AGREEMENT.md).
## Documentation
Select exactly one:
- [ ] I added/updated documentation for this change
- [x] 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:
https://github.com/netbirdio/docs/pull/__
<!-- codesmith:footer -->
---
<a
href="https://app.blacksmith.sh/netbirdio/codesmith/netbird/pr/6931"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-dark-v2.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-light-v2.svg"><img
alt="View with [code]smith"
src="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-dark-v2.svg"></picture></a>
<a
href="https://backend.blacksmith.sh/track/enable-autofix?expires=1787779607&installation_model_id=427504&pr_number=6931&repository=netbirdio%2Fnetbird&return_to=https%3A%2F%2Fgithub.com%2Fnetbirdio%2Fnetbird%2Fpull%2F6931&signature=ce1631be2a5ffdba58c44b4669b0d480dc9f956102cf10a0c7b5845a800cdc68"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-light.svg"><img
alt="Autofix with [code]smith"
src="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-dark.svg"></picture></a>
<sup>Need help on this PR? Tag <code>@codesmith-bot</code> with what you
need. Autofix is disabled.</sup>
<!-- codesmith:autofix:disabled -->
<!-- /codesmith:footer -->
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Added an interactive iOS login option that starts authentication
directly when needed.
* Improved login flow handling, including clearer error reporting and
successful-login notifications.
* Login configuration is now saved automatically after successful
authentication when applicable.
* **Bug Fixes**
* Prevented duplicate login requests during iOS startup.
* Improved startup behavior and error propagation when the management
service is unavailable.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Removes redundant `Login` RPCs from the iOS SDK bindings. Both changed
files are behind
the `ios` build tag — Android, desktop and the shared core are not
affected.
### Problem
`auth.Auth.IsLoginRequired()` is not a cheap probe: it calls
`doMgmLogin()` and classifies
the resulting error, so every "is login required?" check costs a **full
`Login` RPC**. There
is no lighter way to ask. As a result the iOS client issued ~7 `Login`
requests before the
first `Sync`, where Android issues ~3, and the extra ones were
indistinguishable from real
logins in the management logs.
Three of those came from this package:
1. `Run()` called `LoginSync()` before starting the engine, which
performs `IsLoginRequired`
**and** `Login` — two RPCs. This duplicated the engine's own
`loginToManagement`
(`client/internal/connect.go`), which runs immediately before the first
`Sync` and is the
authoritative login. The `Login(ctx, "", "")` inside `LoginSync` could
not even establish
anything: with an empty setup key and empty JWT, a registration attempt
fails by
construction, so it was a pure check.
2. `Auth.login()` called `IsLoginRequired()` again before opening the
browser, even when the
caller had already determined that login is needed.
This is not only wasted traffic:
- **It pushes peers toward the server-side login ban.** In
`management/internals/shared/grpc/loginfilter.go`, every login with
unchanged metadata
increments `sessionCounter`, and exceeding `reconnLimitForBan` (30)
within
`reconnThreshold` (5 min) bans the peer for `baseBlockDuration` (10
min), doubling on
repeat. Redundant logins carry identical metadata, so they count against
exactly this
budget. At 7 logins per connect the budget is exhausted after ~4
reconnects instead of
~10 — reachable on flaky mobile networks.
- **Each redundant check is a potential 2-minute stall.**
`IsLoginRequired` retries with
backoff up to `MaxElapsedTime` (2 min) and returns `true` on failure, so
an unreachable
server was reported as "login required" rather than as a timeout, and
the `LoginSync`
pre-flight could abort engine startup on that basis.
### Changes
**`client/ios/NetBirdSDK/client.go`** — `Run()` no longer performs the
`LoginSync()`
pre-flight. The engine's `loginToManagement` remains the single
authoritative login.
**`client/ios/NetBirdSDK/login.go`** — new exported `LoginInteractive`,
which skips the
`IsLoginRequired()` pre-flight and goes straight to the browser /
device-code flow, for
callers that have already established login is required.
`LoginWithDeviceName` keeps the
check for callers where the auth state is unknown (tvOS). Both now
delegate to a shared
`startLogin()`.
### Why this is safe
An expired or revoked session still fails the connection, one step later
and through a
single path: `loginToManagement` returns `PermissionDenied` → the
deferred
`MarkManagementDisconnected` records it on the shared status recorder →
`ClientStop` fires
the listener's disconnect callback, where `IsLoginRequiredCached()`
reports login-required →
the client tears the tunnel down. The error is also returned out of
`Run()`.
Where the server is unreachable, the engine now retries with backoff and
recovers on its
own instead of aborting the start.
Co-authored-by: Zoltan Papp <zoltan.pmail@gmail.com>
## Describe your changes
`getInterfaceIndex` in the iOS upstream DNS resolver dereferenced the
result of `net.InterfaceByName` before checking the error, so a missing
interface (e.g. during teardown or renaming) caused a nil-pointer panic
instead of a DNS client error.
The helper now returns a wrapped error before touching the interface;
the only caller, `GetClientPrivate`, already propagates the error.
The helper moved to an un-build-tagged file so it can be unit-tested on
host platforms while remaining available to the iOS build. Added a test
covering the missing-interface path. Verified with the new host test, an
iOS arm64 CGO compile, and `git diff --check`.
## Issue ticket number and link
N/A
## Stack
<!-- branch-stack -->
Standalone PR based on `main`.
### Checklist
- [x] Is it a bug fix
- [ ] Is a typo/documentation fix
- [ ] Is a feature enhancement
- [ ] It is a refactor
- [x] Created tests that fail without the change (if possible)
- [x] 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](https://github.com/netbirdio/netbird/blob/main/CONTRIBUTING.md#discuss-changes-with-the-netbird-team-first).
> By submitting this pull request, you confirm that you have read and
agree to the terms of the [Contributor License
Agreement](https://github.com/netbirdio/netbird/blob/main/CONTRIBUTOR_LICENSE_AGREEMENT.md).
## Documentation
Select exactly one:
- [ ] I added/updated documentation for this change
- [x] Documentation is **not needed** for this change (explain why)
Internal crash fix in the iOS DNS path; no user-facing behavior or
configuration changes.
### Docs PR URL (required if "docs added" is checked)
N/A
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Bug Fixes**
* Improved handling of network interface lookup failures with clearer
error messages that identify the affected interface.
* Added validation for network interface lookups, including reliable
error handling when an interface cannot be found.
* **Tests**
* Added coverage for both successful interface resolution and
missing-interface scenarios.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Describe your changes
`NB_LAZY_CONN_INACTIVITY_THRESHOLD` was parsed with `strconv.Atoi`, i.e.
as a bare
integer number of minutes. The documentation, however, states it takes a
Go duration
(e.g. `30m`, `1h`). As a result any documented value such as `30m` or
`5m` failed to
parse and **silently fell back to the 15m default**, so the setting
appeared to have
no effect.
`inactivityThresholdEnv()` now parses the value with
`time.ParseDuration`, matching
the docs. A bare integer is still accepted as a number of minutes for
backwards
compatibility, and an unparseable value logs a warning and falls back to
the default.
Added `TestInactivityThresholdEnv` covering Go-duration values
(`30m`/`1h`/`90s`), the
bare-integer minutes fallback, and zero/negative/garbage inputs.
## Issue ticket number and link
N/A