Commit Graph

3120 Commits

Author SHA1 Message Date
Zoltán Papp
bee92f5fcd ui/frontend: update StatusContext for Peers → DaemonFeed rename
Missed in the previous commit. The StatusContext is the only frontend
consumer of the renamed service (the modules/main/.../peers/Peers.tsx
React component is a different identifier — unchanged).
2026-05-28 21:43:44 +02:00
Zoltan Papp
f4914fdfcc build: replace Wails3 scaffolding placeholders with NetBird identity
The build/config.yml that wails3 init scaffolded shipped with 'My Company',
'My Product', 'com.mycompany.myproduct' and '(c) 2025, My Company' template
defaults. The per-platform assets generated from it (Info.plist,
Info.dev.plist, info.json, nsis/wails_tools.nsh) carried the same strings,
which were visible in macOS Finder Get Info, Windows .exe Properties and
the NSIS installer.

Updated to the NetBird identity used by the legacy Fyne UI on main:

- companyName / copyright   -> 'NetBird GmbH' (matches main release.yml's
                              COPYRIGHT env passed to goversioninfo)
- productName               -> 'NetBird'
- productIdentifier         -> 'io.netbird.client' (matches CFBundleIdentifier)
- description               -> 'NetBird desktop client'
- darwin NSHumanReadableCopyright   -> 'NetBird GmbH'
- windows LegalCopyright            -> 'NetBird GmbH'
- nsis INFO_COPYRIGHT               -> 'NetBird GmbH'

Version fields (0.0.1) are left in place: release builds get the real
version via goversioninfo (Windows) and sign-pipelines (macOS .app),
so the placeholder is only visible in local task package / task run
output and doesn't reach release artifacts.
2026-05-28 21:32:16 +02:00
Zoltán Papp
2cdc6ef1c6 ui: split tray.go into feature files, rename Peers service to DaemonFeed
The 1542-line tray.go grew into a 14-feature kitchen sink. Split it
into feature-coherent same-package siblings, give the daemon-stream
service a name that matches what it actually does, and trim the
cargo-cult context.WithCancel pattern from click handlers.

File layout (tray.go: 1542 → ~470 lines):
  - tray_status.go    onStatusEvent / applyStatus / status indicator
  - tray_icon.go      applyIcon / iconForState (tray icon painting)
  - tray_events.go    onSystemEvent + eventTitle / titleCase, plus a
                      shouldSkipSystemEvent helper that names the
                      three "daemon notification we don't surface"
                      filters
  - tray_session.go   session-expiry row + warning notification flow +
                      handleSessionExpired (moved from tray.go)
  - tray_profiles.go  loadConfig / loadProfiles / switchProfile
  - tray_exitnodes.go exit-node submenu (rebuild / refresh / toggle)

Mutex split: the kitchen-sink t.mu becomes four domain-scoped mutexes
so a long-running gRPC call in one domain can't block status-push
readers in another:
  - statusMu        connected / lastStatus / lastDaemonVersion /
                    lastNetworksRevision / pendingConnectLogin
  - sessionMu       sessionExpiresAt (read by the 30s ticker,
                    written by applySessionExpiry on every status push)
  - profileMu       activeProfile / activeUsername /
                    notificationsEnabled / switchCancel
  - exitNodesMu     row cache (read in reapplyMenuState's Repaint copy)
  - exitNodesRebuildMu  serialises ListNetworks + submenu rebuild +
                        SetMenu (already separate, kept)

Service rename: the "Peers" service handled the daemon's full
SubscribeStatus snapshot (peers, daemon version, management/signal
link state, networks revision, SSO deadline) plus the SubscribeEvents
notification stream and the profile-switch suppression filter. Peers
was a misleading name for a daemon-stream fan-out service. Rename to
DaemonFeed in services/, profileswitcher's stored reference, the
TrayServices struct, main.go wiring, and every doc comment that
referenced it. peers.go → daemon_feed.go. The Status.Peers field
itself (the peer list in the snapshot) is unchanged.

Event constant renames (wire strings unchanged so the frontend keeps
working without regenerating bindings beyond the rename):
  - EventStatus → EventStatusSnapshot
    Payload is a full Status struct (daemon-wide snapshot), not just
    a state-change ping — name the value-shape.
  - EventSystem → EventDaemonNotification
    Payload is a daemon SystemEvent meant to drive an OS toast or a
    Recent Events row. "System" was too generic; "Notification"
    matches what consumers do with it.

Concurrency fixes:
  - WaitExtendAuthSession now preempts a previous in-flight wait
    via the existing SetWaitCancel/CancelWait infrastructure on
    PendingFlow, the same pattern WaitSSOLogin uses. The previous
    waiter exits with codes.Canceled; the authsession service
    translates that to ExtendResult{Preempted: true} so the tray
    and the about-to-expire dialog stay silent on the losing flow
    instead of showing a false-failure toast. Without this, both
    a tray "Extend now" click and a dialog "Stay connected" click
    on the same deadline started two parallel IdP polls, and
    whichever lost the device-code check painted a bogus error.
  - mgmClient.ExtendAuthSession drops the dead backoff retry loop.
    The loop only retried on codes.Canceled, but the inner mgmCtx
    was derived from context.Background() and never cancelled, so
    every real error went straight to backoff.Permanent on the
    first attempt. Replace with a single
    context.WithTimeout(c.ctx, ConnectTimeout) call; daemon
    shutdown now interrupts the RPC and behaviour on real errors
    is unchanged.

Click-handler hygiene: six call sites used the cargo-cult
context.WithCancel(context.Background()) + defer cancel() pattern
without ever calling cancel() externally. Replace with
context.Background() directly (loadConfig, loadProfiles,
runExtendSession, dismissSessionWarning, handleConnect's Up,
handleDisconnect's Down). The one site that genuinely needs the
cancel — switchProfile, which stores it in t.switchCancel so
handleDisconnect can preempt the switch — keeps WithCancel.

Helper extraction: shouldSkipSystemEvent groups the three
"daemon notification we drop on the floor" checks
(new_version_available metadata, progress_window metadata, the
::/0 partner of an exit-node default-route event) behind a single
named predicate. Each had a comment explaining why; collecting
them moves the rationale into the helper docstring and shrinks
onSystemEvent to a router.
2026-05-28 21:26:57 +02:00
Zoltán Papp
3279b705fe session-extend: drop dead retry loop in mgmClient.ExtendAuthSession
The backoff loop only retried on codes.Canceled, but mgmCtx was derived
from context.Background() and never cancelled by anything — so every
real error path (Unavailable, DeadlineExceeded, etc.) went through
backoff.Permanent on the first attempt. The loop was a no-op wrapper
that just held the call open for the daemon's lifetime regardless of
shutdown.

Replace with a single context.WithTimeout(c.ctx, ConnectTimeout) call.
Daemon shutdown now interrupts the RPC; behaviour on real errors is
unchanged.
2026-05-28 19:28:15 +02:00
Zoltán Papp
e94a4cbce5 session-extend: preempt previous WaitExtendAuthSession on new wait
When the tray "Extend now" notification action and the about-to-expire
dialog both start a flow for the same deadline, the daemon was running
two independent IdP polls and the older one surfaced an InvalidArgument
toast as soon as the second RequestExtend overwrote the pending flow.

Follow the WaitSSOLogin pattern: at the top of WaitExtendAuthSession
cancel the previous wait (the SetWaitCancel/CancelWait pair on
PendingFlow already existed but was unused), then register the new
wait's cancel. Preempted callers exit with codes.Canceled; the
authsession service translates that into ExtendResult{Preempted: true}
so the tray and the React dialog can stay silent on the losing flow
instead of showing a false-failure toast / error dialog.
2026-05-28 19:17:46 +02:00
Eduard Gert
c1db8ab0ab add manage profiles to tray 2026-05-28 18:04:38 +02:00
Eduard Gert
2bf945e745 remove unused packages 2026-05-28 17:18:49 +02:00
Eduard Gert
4556d52a60 fix view mode toggle 2026-05-28 16:36:15 +02:00
Eduard Gert
51b243bdfa remove unused stuff, refactor frontend folder structure 2026-05-28 16:26:13 +02:00
Eduard Gert
e09bc8894d Merge remote-tracking branch 'origin/ui-refactor' into ui-refactor 2026-05-28 15:45:51 +02:00
Zoltan Papp
55c1f44fb0 build: drop -buildvcs=false so go embeds vcs.revision into the ui binary
All Wails3 Taskfiles passed -buildvcs=false to go build, which disables
the automatic VCS info embedding Go 1.18+ does by default. As a result
runtime/debug.ReadBuildInfo() returned an empty vcs.revision in our
netbird-ui binary, so the upcoming version.NetbirdCommit() helper from
PR #6263 could not display the git sha for dev builds.

Removed from build:native in all three platform Taskfiles plus the
Windows build:console and the Dockerfile.cross cross-compile script.
go version -m bin/netbird-ui now reports vcs.revision and vcs.modified.
2026-05-28 15:22:05 +02:00
Eduard Gert
ac8d417c12 update exit node tab 2026-05-28 14:43:28 +02:00
Eduard Gert
dccc0ebe4b update resources tab 2026-05-28 14:28:51 +02:00
Zoltán Papp
35498c572a ci: bump node to v22 in release workflow
pnpm 11 requires Node.js >= 22.13 (uses node:sqlite, added in 22.5),
but the release workflow still pinned Node 20. After bumping pnpm to
v11 in the previous commit, the frontend build hook now fails with
ERR_UNKNOWN_BUILTIN_MODULE 'node:sqlite' until Node also moves to 22.
2026-05-28 13:57:30 +02:00
Zoltán Papp
cda621bb27 ci: bump pnpm to v11 in release workflow
The frontend uses pnpm 11 (packageManager field, v11 lockfile, and the
allowBuilds key in pnpm-workspace.yaml is a pnpm 10+ feature), but the
release_ui job's pnpm/action-setup was pinned to v9. v9 rejects the
workspace file with 'packages field missing or empty' before the
frontend build hook can run.
2026-05-28 13:51:43 +02:00
Zoltán Papp
d57b30f8d5 Merge branch 'main' into ui-refactor 2026-05-28 13:43:19 +02:00
Zoltan Papp
d82b950718 frontend: approve esbuild postinstall via pnpm-workspace.yaml
pnpm 11 blocks dependency build scripts by default and exits non-zero
when any are skipped, which made task build fail at install:frontend:deps.
esbuild's postinstall is required to fetch the platform-specific binary.
2026-05-28 13:40:43 +02:00
braginini
3bd058d425 Use old version of the Dock icon 2026-05-28 12:45:36 +02:00
Zoltan Papp
0082f51830 i18n: pluralize exit node nav title 2026-05-28 11:46:19 +02:00
Zoltan Papp
e4420b1f96 tray: separator between troubleshoot and version info in about submenu 2026-05-28 11:44:13 +02:00
Zoltan Papp
a5635f8825 tray: use yellow connecting dot for needs-login state 2026-05-28 11:41:22 +02:00
Riccardo Manfrin
7ea5e37dd4 [client] Improve rosenpass support (#6136)
* Updates rosenpass version

go-rosenpass v0.4.0 → v0.5.42 bump — detailed findings

Change summary
cunicu.li/go-rosenpass  v0.4.0  → v0.5.42   (target)
cilium/ebpf             v0.15.0 → v0.19.0   (transitive)
gopacket/gopacket       v1.1.1  → v1.4.0    (transitive)
wireguard               2023-07 → 2023-12   (transitive)
wireguard/wgctrl        2023-04 → 2024-12   (transitive)

Wire interop

v0.4.0 (in v0.70.5) <-> v0.5.42 OK
v0.5.42 <-> v0.5.42 OK

Quantum resistance: true both ends

---
**Replay error eliminated.**

Before (on v0.4.0):

`ERROR Failed to handle message: failed to load biscuit (ICR1): detected replay`

Recurring every ~50ms for minutes at a time. Gone entirely after both ends upgraded to v0.5.42. Upstream fix in biscuit/replay handling between v0.4.x and v0.5.x series.

* Fixup [::]:port socket trying to send to v4

* Adds more tests on netbird<->rosenpass interactions

* Anticipates rp handler creation before generateConfig

* [client] Moves deterministic key gen into rosenpass

* go mod tidy

* Adds reminder to reason about rosenpass surface area

* Apply code rabbit suggestions
2026-05-28 09:01:18 +02:00
Riccardo Manfrin
9d7ef9b255 [client] Fix statemanager possible deadlock (#6228)
1. Stop() takes m.mu.Lock() and defers m.mu.Unlock()
2. <-m.done under lock
3. periodicStateSave defers close(m.done)
4. periodicStateSave calls PersistState() (line 256) which does m.mu.Lock()

Double Stop() remains idempotent: second cancel() on dead ctx
 (no-op) and reads done already closed (immediate return).
2026-05-28 08:54:15 +02:00
Zoltan Papp
966fbec119 routemanager: enforce a single selected exit node
Exit nodes are mutually exclusive, but the RouteSelector stores routes with
default-on semantics, so every available exit node reported as selected at once.

Reconcile exit-node selection on each network map (and on runtime selection):
keep at most one selected — the user's persisted pick, else whatever management
marks for auto-apply (SkipAutoApply=false), else none. Never auto-activate an
exit node the map doesn't request; it stays off until the user picks it.

The server deselects sibling exit nodes when the user activates one (leaving
non-exit routes untouched), and the tray/React exit-node toggle now appends so
activating an exit node no longer wipes network-route selections.
2026-05-27 20:48:16 +02:00
Zoltan Papp
f693d268b4 tray: selectable exit nodes + push-based network list refresh
Make the tray Exit Node submenu selectable (mutually exclusive, sourced from
ListNetworks by NetID) instead of read-only.

Add networksRevision to the status snapshot, bumped by the route manager on
network-map and selection changes, so the tray and the React NetworksContext
re-fetch ListNetworks via the push stream instead of polling. The peer-status
route list only carries chosen routes, so a candidate exit node appearing or
disappearing would otherwise never reach the UI.
2026-05-27 20:48:16 +02:00
Eduard Gert
09f4109b01 update peers ui 2026-05-27 18:01:06 +02:00
Eduard Gert
ad7d7fa881 change font weight 2026-05-27 17:03:31 +02:00
Pascal Fischer
944a258459 [management] extend nmap monitoring (#6271) 2026-05-27 16:56:02 +02:00
Eduard Gert
b84c7618e7 fix viewmode height, update other ui stuff 2026-05-27 16:40:57 +02:00
Eduard Gert
ec5da43d73 persist viewMode across restarts 2026-05-27 15:52:15 +02:00
Eduard Gert
a8ad73d2d9 add shortcuts in tray for quit and settings item 2026-05-27 15:37:05 +02:00
Eduard Gert
a241112a1d disable resize 2026-05-27 15:35:51 +02:00
Eduard Gert
e62dff0f66 add github, docs links etc. to settings about page 2026-05-27 15:35:40 +02:00
Eduard Gert
5cecca2c23 store viewmode in ui preferences 2026-05-27 15:21:51 +02:00
Eduard Gert
0e83d2ad94 add peer details 2026-05-27 14:52:19 +02:00
Pascal Fischer
1f9a829f2c [management] update log levels (#6266) 2026-05-27 11:43:49 +02:00
Zoltan Papp
004a305e46 tray: add 30s ticker to keep session-expiry countdown fresh 2026-05-27 00:33:09 +02:00
Zoltan Papp
c77e5cef85 tray: revert on-open click handler — OpenMenu freezes tray and React
Binding OnClick/OnRightClick to call OpenMenu() on macOS routes the menu
open through showMenu(), which runs the blocking [button mouseDown:] inside
a dispatched block on the serial main GCD queue. While the menu is open that
block never returns, starving every other main-queue task — both tray item
updates and the webview event delivery that drives React freeze until the
menu closes.

Revert to the pre-d9f0189 state: no click handlers bound, native NSStatusItem
auto-show for left-click, Wails default rightClickHandler for right-click.
refreshSessionExpiresLabel() is kept for the follow-up fix.
2026-05-27 00:24:46 +02:00
Zoltan Papp
13179081d2 Merge branch 'main' into ui-refactor 2026-05-26 23:41:18 +02:00
Zoltan Papp
2d3c8fc555 tray: drop dead iconMenuNetbird and openRoute after menu rework
The menu reorganisation removed the About brand-mark bitmap and rerouted
every openRoute caller to WindowManager auxiliary windows, leaving both
the iconMenuNetbird embed (all three platforms) and the openRoute helper
unreferenced. Remove them so the unused linter passes.
2026-05-26 23:25:45 +02:00
Zoltan Papp
61aa3a53ed tray: re-enable Exit Node menu item when candidates arrive post-connect
The parent Exit Node item's enablement was only refreshed on icon/status
transitions. The daemon ships peer routes in a later snapshot than the
Connected status text, so after a profile switch the candidate list flips
empty to non-empty while the status string is unchanged — leaving the item
greyed and the freshly painted rows unreachable. Re-evaluate enablement in
the exitNodesChanged branch too.
2026-05-26 23:15:03 +02:00
Zoltan Papp
80d6df6260 tray: rework menu layout, exit-node submenu, session countdown wording
- Reorder the menu: status, Connect/Disconnect, profile block, Open
  NetBird, Exit Node, then Settings… / Help & Support / Quit NetBird.
- Rename About → Help & Support, Quit → Quit NetBird, Settings → Settings…
  (ellipsis flags the window-opening action per the macOS HIG); drop the
  brand icon from Open NetBird; enable Documentation (opens docs.netbird.io)
  and add a Troubleshoot entry that deep-links the Settings window.
- Exit Node is now a submenu listing only peers that advertise a default
  route (0.0.0.0/0 or ::/0), sorted case-insensitively; the row stays
  visible but greyed when the tunnel is down or no candidate exists.
- Session row reads "Session expires in <n minutes/hours/days>" and
  recomputes on menu open so the countdown tracks wall time between the
  daemon's status pushes.
2026-05-26 23:15:03 +02:00
Zoltan Papp
53bbc2d551 session: clear stale SSO deadline on teardown and after expiry
The session deadline lived in two sinks kept in sync by hand:
ApplySessionDeadline wrote both the (engine-scoped) sessionwatch.Watcher
and the (server-scoped) peer.Status recorder. The clear paths only
touched the watcher, so the recorder — which is what the Status RPC /
SubscribeStatus snapshot the UI reads from — kept reporting a deadline
that had gone stale, surfacing as a frozen "expires in …" countdown.

Two cases were leaking:
- Profile switch / Down: the watcher is recreated per engine but the
  recorder outlives it, so a switch to a profile whose server sends no
  deadline left the previous profile's value in place.
- In-place expiry: the watcher arms warning timers at T-WarningLead and
  T-FinalWarningLead but nothing at the deadline itself, so once the
  moment passed the recorder kept the now-past value indefinitely.

Make the watcher the single writer of the recorder deadline (Update /
clearLocked / Close all route through SetSessionExpiresAt) so teardown
clears it, and guard GetSessionExpiresAt to report a past deadline as
none so in-place expiry stops painting a stale countdown.
2026-05-26 23:15:03 +02:00
Zoltan Papp
d9f0189b57 tray: reorganise menu, refresh expiry countdown on open
Layout changes:
- Drop "Debug Bundle" row; reach the flow via the in-window Settings UI.
- Move the brand-mark icon from the About row to "Open NetBird".
- Collapse Settings / Exit Node / About into a single block, with the
  Settings → Exit Node order swap to put the configuration entry first.
- Relocate Connect / Disconnect to the bottom block, sharing its
  separator with Quit. Drops the connectSeparator field + lastMenuItem
  helper that only existed to suppress the daemon-unavailable double
  separator in the old position.

Countdown freshness: the daemon's Status snapshots arrive too coarse to
keep a minute-grained "Expires in …" row honest while the menu is
closed. Wails v3 alpha 95 does not expose a public NSMenu needsUpdate
hook, so the tray binds OnClick / OnRightClick and recomputes the label
from cached sessionExpiresAt just before the menu paints. macOS and
Windows right-click additionally call OpenMenu() to restore the native
auto-show that binding the handler suppresses; Linux's dbusmenu host
paints the menu itself.
2026-05-26 23:15:03 +02:00
Bethuel Mmbaga
14af179556 [management] Refactor management server bootstrap (#6256) 2026-05-26 17:44:28 +03:00
Pascal Fischer
1fbb5e6d5d [management] fix owner role update (#6264) 2026-05-26 16:37:58 +02:00
Viktor Liu
6771e35d57 [client] Release js.FuncOf callbacks in wasm ssh and rdp to prevent leaks (#5982) 2026-05-26 14:32:39 +02:00
Eduard Gert
91e0520f27 move locales to client/ui/i18n 2026-05-26 12:34:01 +02:00
Eduard Gert
67a1f3c4fe add peers, networks and exit node list (wip) 2026-05-26 12:09:08 +02:00
Viktor Liu
e89b1e0596 [proxy, client] Bound embed client WireGuard per-Device memory (#5962) 2026-05-26 11:51:53 +02:00