[PR #6083] [client, management] Phase 3.7i of #5989: peer-status visibility — RemotePeerConfig + conn-state pusher + Peers UI (stack 3/4) #27508

Open
opened 2026-08-05 07:08:49 -04:00 by saavagebueno · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/netbirdio/netbird/pull/6083
Author: @MichaelUray
Created: 5/5/2026
Status: 🔄 Open

Base: mainHead: pr/c-phase3.7i-visibility


📝 Commits (10+)

  • c4844cc proto: add ConnectionMode enum and p2p/relay timeout fields to PeerConfig
  • e0ed831 client: add connectionmode package with Mode type and proto bridge
  • c71c951 client/peer: ResolveModeFromEnv with NB_CONNECTION_MODE and deprecation warns
  • 7d90a5b client: add --connection-mode, --relay-timeout, --p2p-timeout CLI flags
  • cc10c9f client/conn_mgr: replace asymmetric Lazy/ForceRelay precedence with Mode
  • dfd48e9 client/peer: connection mode drives skip-ICE branch in Open()
  • 82877f0 client/engine: forward resolved Mode to per-peer ConnConfig
  • cd0abe8 mgmt/types: add ConnectionMode + p2p/relay timeout to Settings
  • 0022145 openapi: add connection_mode + p2p/relay timeout fields to AccountSettings
  • b22128e mgmt/handlers/accounts: accept connection_mode + timeout settings on PUT

📊 Changes

89 files changed (+9174 additions, -1383 deletions)

View changed files

📝 client/android/client.go (+134 -4)
📝 client/android/peer_notifier.go (+18 -0)
📝 client/android/preferences.go (+85 -0)
📝 client/cmd/root.go (+17 -0)
📝 client/cmd/service.go (+18 -0)
📝 client/cmd/service_installer.go (+57 -0)
📝 client/cmd/testutil_test.go (+2 -1)
📝 client/cmd/up.go (+39 -0)
📝 client/internal/conn_mgr.go (+518 -42)
client/internal/conn_mgr_test.go (+221 -0)
client/internal/conn_state_pusher.go (+332 -0)
client/internal/conn_state_pusher_test.go (+145 -0)
📝 client/internal/connect.go (+19 -0)
client/internal/debouncer/debouncer.go (+53 -0)
📝 client/internal/debug/debug.go (+6 -0)
📝 client/internal/engine.go (+160 -4)
client/internal/engine_pusher_adapters.go (+62 -0)
📝 client/internal/engine_test.go (+1 -1)
📝 client/internal/lazyconn/env.go (+5 -0)
📝 client/internal/lazyconn/inactivity/manager.go (+128 -21)

...and 69 more files

📄 Description

[client+management] Phase 3.7i of #5989: peer-status visibility (RemotePeerConfig + conn-state pusher + Peers UI)

Branch: pr/c-phase3.7i-visibility → base pr/b-phase3.5-network-change (stacked PR — depends on PR-B landing first)
Compare: https://github.com/netbirdio/netbird/compare/MichaelUray:netbird:pr/b-phase3.5-network-change...MichaelUray:netbird:pr/c-phase3.7i-visibility?expand=1


Summary

This PR delivers the peer-status visibility layer of #5989: a peer can now see the configured + effective connection-mode of its remotes, the remotes' liveness status as known to the management server, and per-peer aggregate counters. It introduces a unary SyncPeerConnections RPC plus an in-memory store with TTL on the management side, a client-side conn_state_pusher with adaptive heartbeat that batches updates, and a new "Peers" tab in the desktop client UI listing every peer with status badge + per-peer accordion.

This is the third of four stacked PRs implementing #5989. It builds on PR-B.

Why

For diagnosing "why is this peer on relay?", a user needs three things visible from one peer's daemon:

  1. What does the management server know about my remote peers' connectivity? (Are they reachable? When did they last sync?)
  2. What is the configured / effective connection-mode of each remote? (Does the remote have relay-forced set? Is it on p2p-dynamic?)
  3. What is the per-peer ICE-backoff state? (Is this peer in hourly retry?)

Pre-Phase-3.7i, only the first piece was indirectly available, by parsing the peer-list endpoint on the management API. There was no per-peer visibility into the lifecycle a remote was in. The "Peers" tab in the desktop UI used to be a flat list with just status colours.

Issue #5990 ("Per-peer and per-group connection-mode and inactivity-threshold override on the management server") depends on this visibility layer landing first.

What's in this PR

Wire schema

  • proto/management: RemotePeerConfig + PeerSystemMeta extensions for peer-status (effective + configured connection mode, P2P/relay timeouts, last-seen-at-server, server-liveness-known marker, groups).
  • proto/management: SyncPeerConnections unary RPC + PeerConnectionMap messages.
  • proto/daemon: FullStatus aggregate counters + PeerState enrichment.

Management

  • mgmt/peer_connections: Store interface + MemoryStore implementation with TTL.
  • mgmt/peer_connections: SnapshotRouter for on-demand refresh dispatch (one channel per stream, registered when the SyncPeerConnections client subscribes).
  • mgmt: shared peer_connections bootstrap + SyncPeerConnections handler routing.
  • mgmt/grpc: route SnapshotRequest via the existing Sync server-stream, bypassing the per-account debouncer (critical: the snapshot needs to arrive within seconds, not after the next accountUpdate cycle).
  • mgmt/peer: store effective_connection_mode in PeerSystemMeta.
  • mgmt/conversion: appendRemotePeerConfig fills effective + configured + groups + last-seen.
  • mgmt/http: GET + POST /api/peers/{id}/connections handlers with RBAC (so a user can only see remotes they are policy-permitted to reach, and only an admin can trigger a refresh).
  • mgmt/router: register /api/peers/{id}/connections + /refresh routes.

Client

  • client/internal: conn_state_pusher with adaptive heartbeat + snapshot. Batches updates, escalates frequency on dirty state, deescalates back to the configured heartbeat when steady.
  • client/internal: wire conn_state_pusher into Engine + the management Sync receiver loop.
  • mgmt/client+engine: report effective_connection_mode + SyncMeta debounce.
  • mgmt/client: SyncPeerConnections unary client method + interface + mock.
  • client/peer/status: counters in FullStatus + UpdatePeerRemoteMeta. New helpers notifyPeerListChanged, notifyPeerStateChangeListeners.
  • client/engine: plumb RemotePeerConfig into peer.UpdatePeerRemoteMeta.

Desktop UI

  • client/ui: new "Peers" tab in the Networks window. Tab is now first; window renamed to "Peers and Networks"; tray menu rename.
  • Per-peer expandable rows (accordion) with status badge, connection details, latency, ICE-backoff state.
  • Outer-footer Show Full toggle + tab-aware actions.
  • Peer rows use dynamic add/remove + scroll has 600px floor.
  • Persist per-peer expand state across Refresh + size-to-content.
  • Endpoint+RelayServer changes trigger an immediate state push (so the UI doesn't wait for the next heartbeat to reflect a relay-server change).

Android shim

  • client/android: gomobile getters for the 6 peer-status aggregate counters.
  • client/android: enrich PeerInfo with peer.State fields for the Android UI to consume.

Other

  • client/conn_mgr: lazy/dynamic mode change resets peers to Idle + tolerant version parsing.
  • mgmt/peer_connections: nonce/router/refresh hardening per code review (TTL semantics, snapshot router cleanup, refresh-rate-limit).
  • client/conn_state_pusher: dirty-on-fail + initial-snapshot gating.

Tests

  • go test ./client/internal/peer ./client/internal/peer/guard ./management/server/peer_connections/... — pass.
  • go build ./client/... ./management/... — pass on linux/amd64, linux/arm64, windows/amd64.
  • Hardware-validated end-to-end on a 4-peer testbed: management server (NetBird mgmt + dashboard) on Linux, daemon + UI on Windows 11, three OpenWrt routers as remote peers (LAN, Internet srflx, LTE srflx). The new Peers tab correctly shows each peer's connection mode, liveness, ICE-backoff state, and the per-peer counters add up to the totals shown in netbird status -d.

Test plan

  • Mgmt unit tests for peer_connections.Store + SnapshotRouter (TTL, expiry, concurrent reads).
  • Client unit tests for conn_state_pusher (adaptive heartbeat escalation, dirty-snapshot gate).
  • Status-recorder helpers (notifyPeerListChanged, notifyPeerStateChangeListeners) covered by the existing peer-state-recorder test suite.
  • Maintainer review of the new HTTP routes (RBAC behaviour for non-admin users).
  • Maintainer review of the unary RPC bypass of the per-account debouncer (intended; needs explicit acknowledgement).

Use case

A user opening the desktop client UI clicks the new "Peers" tab and immediately sees:

  • Which of their remotes are currently online (server-side liveness).
  • Which connection mode each remote is configured for and what it actually settled on.
  • Whether any remote is in ICE-backoff (with the next-retry timestamp).

Today this is information a NetBird dev would gather by SSH'ing to the daemon, running netbird status -d, then querying the management API by hand. The Peers tab makes it routine.

Linked work

  • Stacks on PR-B.
  • Closes part of #5989.
  • Unblocks #5990 (per-peer/per-group mode override) which needs this visibility layer.

Maintainers are welcome to push directly to this branch.

Summary by CodeRabbit

  • New Features

    • Connection Mode controls (Follow server, relay-forced, p2p, p2p-lazy, p2p-dynamic) surfaced in UI, CLI, installer and per-device preferences; server-pushed defaults shown as hints.
    • New Peers tab with expandable per-peer details, endpoints, latency, byte counters, ICE backoff info, agent/OS and group info.
    • On-demand peer-connection snapshots and refresh API for live per-peer connection maps.
  • Improvements

    • Richer status output and aggregate counters (online/offline, p2p/relayed/idle) for UI and status commands.

Documentation

  • Documentation is not needed

These changes are internal lifecycle / behavioural improvements; no user-visible API or CLI flag added that warrants new public docs. Existing flags/Settings already documented at netbirdio/docs cover the surface area.


🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/netbirdio/netbird/pull/6083 **Author:** [@MichaelUray](https://github.com/MichaelUray) **Created:** 5/5/2026 **Status:** 🔄 Open **Base:** `main` ← **Head:** `pr/c-phase3.7i-visibility` --- ### 📝 Commits (10+) - [`c4844cc`](https://github.com/netbirdio/netbird/commit/c4844ccfd1361ad48194731423a7f873a73a24f7) proto: add ConnectionMode enum and p2p/relay timeout fields to PeerConfig - [`e0ed831`](https://github.com/netbirdio/netbird/commit/e0ed8313697835bafa34556aec487bc782e95307) client: add connectionmode package with Mode type and proto bridge - [`c71c951`](https://github.com/netbirdio/netbird/commit/c71c951d88b9ce5fb2d224684e50e6dd31d77072) client/peer: ResolveModeFromEnv with NB_CONNECTION_MODE and deprecation warns - [`7d90a5b`](https://github.com/netbirdio/netbird/commit/7d90a5bdd5cc598293640d1c8bcfa23ee866e6c6) client: add --connection-mode, --relay-timeout, --p2p-timeout CLI flags - [`cc10c9f`](https://github.com/netbirdio/netbird/commit/cc10c9f108fce4b90edf7d4f4bf399e53057e038) client/conn_mgr: replace asymmetric Lazy/ForceRelay precedence with Mode - [`dfd48e9`](https://github.com/netbirdio/netbird/commit/dfd48e920bf889a20ef699b5aaff1a0a4b64d491) client/peer: connection mode drives skip-ICE branch in Open() - [`82877f0`](https://github.com/netbirdio/netbird/commit/82877f0ce8cb23ba722f393d47b50a508dc692bd) client/engine: forward resolved Mode to per-peer ConnConfig - [`cd0abe8`](https://github.com/netbirdio/netbird/commit/cd0abe890d579c0dfbbb2251b4451d62c5b9e3bf) mgmt/types: add ConnectionMode + p2p/relay timeout to Settings - [`0022145`](https://github.com/netbirdio/netbird/commit/0022145a414e794f7701250460b10badba7741b5) openapi: add connection_mode + p2p/relay timeout fields to AccountSettings - [`b22128e`](https://github.com/netbirdio/netbird/commit/b22128ed14f35d56d75e640f986d2e934414401d) mgmt/handlers/accounts: accept connection_mode + timeout settings on PUT ### 📊 Changes **89 files changed** (+9174 additions, -1383 deletions) <details> <summary>View changed files</summary> 📝 `client/android/client.go` (+134 -4) 📝 `client/android/peer_notifier.go` (+18 -0) 📝 `client/android/preferences.go` (+85 -0) 📝 `client/cmd/root.go` (+17 -0) 📝 `client/cmd/service.go` (+18 -0) 📝 `client/cmd/service_installer.go` (+57 -0) 📝 `client/cmd/testutil_test.go` (+2 -1) 📝 `client/cmd/up.go` (+39 -0) 📝 `client/internal/conn_mgr.go` (+518 -42) ➕ `client/internal/conn_mgr_test.go` (+221 -0) ➕ `client/internal/conn_state_pusher.go` (+332 -0) ➕ `client/internal/conn_state_pusher_test.go` (+145 -0) 📝 `client/internal/connect.go` (+19 -0) ➕ `client/internal/debouncer/debouncer.go` (+53 -0) 📝 `client/internal/debug/debug.go` (+6 -0) 📝 `client/internal/engine.go` (+160 -4) ➕ `client/internal/engine_pusher_adapters.go` (+62 -0) 📝 `client/internal/engine_test.go` (+1 -1) 📝 `client/internal/lazyconn/env.go` (+5 -0) 📝 `client/internal/lazyconn/inactivity/manager.go` (+128 -21) _...and 69 more files_ </details> ### 📄 Description # [client+management] Phase 3.7i of #5989: peer-status visibility (RemotePeerConfig + conn-state pusher + Peers UI) **Branch:** `pr/c-phase3.7i-visibility` → base **`pr/b-phase3.5-network-change`** (stacked PR — depends on PR-B landing first) **Compare:** https://github.com/netbirdio/netbird/compare/MichaelUray:netbird:pr/b-phase3.5-network-change...MichaelUray:netbird:pr/c-phase3.7i-visibility?expand=1 --- ## Summary This PR delivers the **peer-status visibility** layer of #5989: a peer can now see the configured + effective connection-mode of its remotes, the remotes' liveness status as known to the management server, and per-peer aggregate counters. It introduces a unary `SyncPeerConnections` RPC plus an in-memory store with TTL on the management side, a client-side `conn_state_pusher` with adaptive heartbeat that batches updates, and a new "Peers" tab in the desktop client UI listing every peer with status badge + per-peer accordion. This is the third of four stacked PRs implementing #5989. It builds on PR-B. ## Why For diagnosing "why is this peer on relay?", a user needs three things visible from one peer's daemon: 1. **What does the management server know about my remote peers' connectivity?** (Are they reachable? When did they last sync?) 2. **What is the configured / effective connection-mode of each remote?** (Does the remote have `relay-forced` set? Is it on `p2p-dynamic`?) 3. **What is the per-peer ICE-backoff state?** (Is this peer in hourly retry?) Pre-Phase-3.7i, only the first piece was indirectly available, by parsing the peer-list endpoint on the management API. There was no per-peer visibility into the lifecycle a remote was in. The "Peers" tab in the desktop UI used to be a flat list with just status colours. Issue #5990 ("Per-peer and per-group connection-mode and inactivity-threshold override on the management server") depends on this visibility layer landing first. ## What's in this PR ### Wire schema - `proto/management`: `RemotePeerConfig` + `PeerSystemMeta` extensions for peer-status (effective + configured connection mode, P2P/relay timeouts, last-seen-at-server, server-liveness-known marker, groups). - `proto/management`: `SyncPeerConnections` unary RPC + `PeerConnectionMap` messages. - `proto/daemon`: `FullStatus` aggregate counters + `PeerState` enrichment. ### Management - `mgmt/peer_connections`: `Store` interface + `MemoryStore` implementation with TTL. - `mgmt/peer_connections`: `SnapshotRouter` for on-demand refresh dispatch (one channel per stream, registered when the SyncPeerConnections client subscribes). - `mgmt`: shared `peer_connections` bootstrap + `SyncPeerConnections` handler routing. - `mgmt/grpc`: route `SnapshotRequest` via the existing Sync server-stream, bypassing the per-account debouncer (critical: the snapshot needs to arrive within seconds, not after the next accountUpdate cycle). - `mgmt/peer`: store `effective_connection_mode` in `PeerSystemMeta`. - `mgmt/conversion`: `appendRemotePeerConfig` fills effective + configured + groups + last-seen. - `mgmt/http`: `GET` + `POST /api/peers/{id}/connections` handlers with RBAC (so a user can only see remotes they are policy-permitted to reach, and only an admin can trigger a refresh). - `mgmt/router`: register `/api/peers/{id}/connections` + `/refresh` routes. ### Client - `client/internal`: `conn_state_pusher` with adaptive heartbeat + snapshot. Batches updates, escalates frequency on dirty state, deescalates back to the configured heartbeat when steady. - `client/internal`: wire `conn_state_pusher` into Engine + the management `Sync` receiver loop. - `mgmt/client+engine`: report `effective_connection_mode` + `SyncMeta` debounce. - `mgmt/client`: `SyncPeerConnections` unary client method + interface + mock. - `client/peer/status`: counters in `FullStatus` + `UpdatePeerRemoteMeta`. New helpers `notifyPeerListChanged`, `notifyPeerStateChangeListeners`. - `client/engine`: plumb `RemotePeerConfig` into `peer.UpdatePeerRemoteMeta`. ### Desktop UI - `client/ui`: new "Peers" tab in the Networks window. Tab is now first; window renamed to "Peers and Networks"; tray menu rename. - Per-peer expandable rows (accordion) with status badge, connection details, latency, ICE-backoff state. - Outer-footer `Show Full` toggle + tab-aware actions. - Peer rows use dynamic add/remove + scroll has 600px floor. - Persist per-peer expand state across `Refresh` + size-to-content. - Endpoint+RelayServer changes trigger an immediate state push (so the UI doesn't wait for the next heartbeat to reflect a relay-server change). ### Android shim - `client/android`: gomobile getters for the 6 peer-status aggregate counters. - `client/android`: enrich `PeerInfo` with `peer.State` fields for the Android UI to consume. ### Other - `client/conn_mgr`: lazy/dynamic mode change resets peers to Idle + tolerant version parsing. - `mgmt/peer_connections`: nonce/router/refresh hardening per code review (TTL semantics, snapshot router cleanup, refresh-rate-limit). - `client/conn_state_pusher`: dirty-on-fail + initial-snapshot gating. ## Tests - `go test ./client/internal/peer ./client/internal/peer/guard ./management/server/peer_connections/...` — pass. - `go build ./client/... ./management/...` — pass on linux/amd64, linux/arm64, windows/amd64. - Hardware-validated end-to-end on a 4-peer testbed: management server (NetBird mgmt + dashboard) on Linux, daemon + UI on Windows 11, three OpenWrt routers as remote peers (LAN, Internet srflx, LTE srflx). The new Peers tab correctly shows each peer's connection mode, liveness, ICE-backoff state, and the per-peer counters add up to the totals shown in `netbird status -d`. ## Test plan - [x] Mgmt unit tests for `peer_connections.Store` + `SnapshotRouter` (TTL, expiry, concurrent reads). - [x] Client unit tests for `conn_state_pusher` (adaptive heartbeat escalation, dirty-snapshot gate). - [x] Status-recorder helpers (`notifyPeerListChanged`, `notifyPeerStateChangeListeners`) covered by the existing peer-state-recorder test suite. - [ ] Maintainer review of the new HTTP routes (RBAC behaviour for non-admin users). - [ ] Maintainer review of the unary RPC bypass of the per-account debouncer (intended; needs explicit acknowledgement). ## Use case A user opening the desktop client UI clicks the new "Peers" tab and immediately sees: - Which of their remotes are currently online (server-side liveness). - Which connection mode each remote is configured for and what it actually settled on. - Whether any remote is in ICE-backoff (with the next-retry timestamp). Today this is information a NetBird dev would gather by SSH'ing to the daemon, running `netbird status -d`, then querying the management API by hand. The Peers tab makes it routine. ## Linked work - Stacks on PR-B. - Closes part of #5989. - Unblocks #5990 (per-peer/per-group mode override) which needs this visibility layer. **Maintainers are welcome to push directly to this branch.** <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Connection Mode controls (Follow server, relay-forced, p2p, p2p-lazy, p2p-dynamic) surfaced in UI, CLI, installer and per-device preferences; server-pushed defaults shown as hints. * New Peers tab with expandable per-peer details, endpoints, latency, byte counters, ICE backoff info, agent/OS and group info. * On-demand peer-connection snapshots and refresh API for live per-peer connection maps. * **Improvements** * Richer status output and aggregate counters (online/offline, p2p/relayed/idle) for UI and status commands. <!-- end of auto-generated comment: release notes by coderabbit.ai --> ## Documentation - [x] Documentation is **not needed** These changes are internal lifecycle / behavioural improvements; no user-visible API or CLI flag added that warrants new public docs. Existing flags/Settings already documented at netbirdio/docs cover the surface area. --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
saavagebueno added the pull-request label 2026-08-05 07:08:49 -04:00
Sign in to join this conversation.
No Label pull-request
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: DYNR/netbird#27508