Gate premium endpoints so the open-source dashboard stops calling
endpoints the open-source management server does not serve:
- MSP/Distributor: only fetch /integrations/msp, /integrations/msp/switcher
and /integrations/msp/reseller on NetBird Cloud
- EDR bypass: only fetch /peers/edr/bypassed when licensed
- Event Streaming: gate /integrations/event-streaming GETs on the license
in addition to the existing permission check
- useIsLicensed: cache the licensed-only probe result (per API origin, 1h
TTL) so it no longer re-fires on every page load
- Team > Users: show the Identity Provider Sync tile in open-source mode so
IdP sync remains discoverable
Brings the unified dashboard into the open-source repo. Premium features
ship in the open code, gated at runtime via NETBIRD_CLOUD and
NETBIRD_LICENSED, with upgrade prompts for unlicensed self-hosted
deployments. Adds the cloud-only feature areas (billing, integrations,
MSP, traffic events, notifications) and the Playwright e2e suite.
* feat: private service expose
Dashboard surface for the netbird private-service feature: the
reverse-proxy modal gains a Private toggle and the target modal gains
a Direct-upstream option with custom upstream host, both feeding the
backend's Service.private + target.direct_upstream / target.host
fields. The Proxy Events page wraps its table in PeersProvider so
peer-name fallback resolution works for tunnel-peer callers.
Reverse-proxy modal changes (ReverseProxyModal.tsx):
- Private toggle that pivots the modal between standalone and cluster
target types and auto-injects the cluster target.
Reverse-proxy target modal changes:
- Direct upstream toggle (target dials via the host stack instead of
the embedded NetBird client).
- Custom upstream host input revealed when Direct upstream is on.
- New ReverseProxyClusterTargetSelector + ReverseProxyAddressInput.
- useReverseProxyTargetOptions updated for the new option shape.
Proxy Events table:
- Reuses across the reverse-proxy events surface; UserCell falls back
to the peer name when no user is attached to the call.
* feat(reverse-proxy): move NetBird-only access to auth tab + access groups
Restructures the Private (NetBird-only) flow so the auth model is
clearer:
- Removes the 'Private (NetBird-only)' toggle from the Service main
page. Service mode is the only primary choice now.
- Adds a 'NetBird-only access' toggle on the Authentication tab, gated
on serviceMode=HTTP and selectedDomain.supports_private===true. When
the cluster doesn't support it, the toggle is disabled with an
inline note explaining why.
- Adds an Access Groups picker (PeerGroupSelector) inline on the auth
tab when NetBird-only is on. Replaces the previous bearer-auth
distribution_groups overload — these groups go on the new
service.access_groups field on the wire.
- When NetBird-only is enabled, all other auth modes (SSO, password,
PIN, headers, link) are hidden — the inbound peer's WireGuard
identity is the only auth path.
- Adds a 'Direct upstream' toggle to the Advanced settings tab,
gated on isPrivate + cluster supports private. The toggle is
service-level in the UI; on save it patches the (single) cluster
target's options.direct_upstream.
- togglePrivate now also clears bearer/password/pin/header/link
state when entering private mode (strict mutual exclusivity).
* fix(reverse-proxy): tighten NetBird-only flow per review
Five fixes from the first cut:
1. Auth-tab design mismatch. The NetBird-only block was an inline
FancyToggleSwitch + inline PeerGroupSelector. It now follows the
same SettingCard.Item pattern as SSO/Password/PIN/Headers: a
clickable row showing enabled state that opens a dedicated modal
(AuthNetBirdOnlyModal) where the access groups are picked.
2. Trimmed wordy NetBird-only description down to one line.
3. Trimmed wordy Direct upstream help text.
4. Removed the per-target Direct upstream toggle from the target
modal. Direct upstream now lives only at the service level (under
Advanced settings) for private services. Cluster targets still
imply direct_upstream via the existing sanitizeTargets path, so
the wire stays correct.
5. togglePrivate no longer drops the user's existing targets when
entering private mode. The previous behavior silently dropped any
non-cluster targets the operator had configured, leaving the
targets list empty and the Save button disabled. Now: targets
stay put, canSaveService gates on 'all targets are cluster type'
so the Save button accurately reflects what the backend will
accept, and an inline warning explains what to fix when the
constraint isn't met.
The AuthNetBirdOnlyModal requires at least one access group before
Enable becomes clickable, mirroring the SSO modal's pattern.
* fix(reverse-proxy): allow any target type on private services
The cluster-target restriction was a holdover from the previous
auth-by-bearer-groups model where only cluster targets exposed a
proxy peer that could host the ACL. With the new access_groups path
the ACL is server-side and works regardless of how the proxy reaches
the upstream.
- Drop the inline 'Private services only support cluster targets'
warning.
- canSaveService no longer requires all targets to be cluster type.
- Service-level Direct upstream now applies to every target (cluster,
peer, host, domain) when private, so the operator can mix target
types and still control the dial path globally.
- Tightened NetBird-only description to '...connected peers in the
selected NetBird groups.' (per review).
- Tightened Direct upstream help to '...reachable without a Wireguard
connection.' (per review).
A future iteration may add a dedicated cluster-only mode with its own
guided flow; for now the operator picks whatever target types suit
their topology.
* fix(reverse-proxy): count NetBird-only access as protection
The 'No Protection Configured' popup fired even on private services
because isUnprotected only checked the password/PIN/bearer/header/link
auth modes. NetBird-only access is also a form of protection (tunnel
identity + access groups), so include isPrivate in the gate.
* fix(reverse-proxy): show NetBird-only auth and resolve access group names
Two display bugs for private services:
- ReverseProxyModal seeded accessGroups with {id}-only stubs, which
made useGroupHelper skip its ID→Group resolution and render empty
pills in the NetBird-only modal when editing an existing service.
Pass the raw string[] so useGroupHelper resolves full Group objects
with names against the GroupsProvider.
- ReverseProxyAuthCell only inspected password/pin/bearer/headers
auth flags, so a NetBird-only service displayed "No Auth" in the
services table. Add a NetBird-only entry that counts toward the
auth badge, renders as "NetBird Only" with the CircleUser icon
when it's the single auth, and lists the access groups (name +
user count) in the hover.
* feat(reverse-proxy): unify proxy-cluster target into the peer/resource selector
Operators picking a target for a service now see Proxy Clusters as a
third tab alongside Peers and Resources, gated on at least one cluster
advertising supports_private. When the service already has a proxy
cluster the tab lists only that cluster; when it doesn't, picking a
cluster commits it as the service's domain. The dedicated private-mode
cluster picker is removed in favour of this unified flow.
Selecting a cluster target now also auto-flips the service to
NetBird-only, since cluster targets are only reachable over the
WireGuard overlay and SSO/password/PIN advertise an auth path no
public client could exercise. The Access Control tab carries a note
explaining that an allow rule for the account's NetBird network range
is applied automatically alongside any operator-configured rules.
PeerGroupSelector gains opt-in showClusters/clusters/selectedCluster/
onClusterChange props plus a Proxy Clusters tab in its TabsList; the
tabOrder union widens to include "clusters". All call sites that don't
pass showClusters render unchanged.
* fix(reverse-proxy): polish private-service copy and lock Direct Upstream for cluster targets
Title-case NetBird-Only Access and Direct Upstream so the labels read
consistently across the auth tab, the dedicated modal, and the
services-table auth cell.
Cluster targets are reached via the embedded proxy's host network
stack and have no WireGuard endpoint to fall back to, so toggling
Direct Upstream off would silently break them. When any target is a
cluster the toggle is forced on, the FancyToggleSwitch is disabled,
and the helper text explains why. The save path writes the locked-on
value to every target's options.direct_upstream so the persisted
state matches what the UI shows.
* fix(reverse-proxy): support dynamic placeholder and restrict input to IPv4 or IPv6 when required
* fix(reverse-proxy): rename "Agent" to "Peer" in events user cell tooltip
* feat(reverse-proxy): gate NetBird-Only + Direct Upstream on cluster capability
When the selected cluster doesn't have at least one connected embedded
proxy (`netbird proxy`), the NetBird-Only Access and Direct Upstream
controls are visually disabled with a tooltip explaining why, instead of
silently no-op (NetBird-Only) or hidden (Direct Upstream).
- ReverseProxyCluster interface gains the optional `private` flag that
the management API now exposes on /api/reverse-proxies/clusters.
- ClustersFeaturesCell renders a new "Private" badge for clusters whose
`private === true`, matching the other capability badges.
- SettingCard.Item gains a `disabled` prop: opacity-50 + cursor-not-allowed,
click/keyboard guarded, Add/Edit button greyed out. No visual change
for existing usages (default false).
- ReverseProxyModal:
- NetBird-Only Access card is now wrapped in a FullTooltip that
activates only when the cluster lacks support; the SettingCard.Item
is rendered with `disabled` in that case. Description text stops
switching based on cluster state — the tooltip explains the gate.
- Direct Upstream toggle is no longer hidden when supports_private is
false; it stays visible and disabled (alongside the existing
hasClusterTarget locked-on case), wrapped in a tooltip explaining
the cluster requirement. Existing private services whose cluster
lost the capability now show the control disabled rather than
vanishing.
* disable button when no group were selected
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Feature/private service expose update (#651)
* feat(reverse-proxy): refine UI state and copy for NetBird-Only and Direct Upstream controls
- Default `Direct Upstream` to `false` for peer/resource targets; enhance tooltip descriptions.
- Improve conditional rendering of `NetBird-Only Access` setting with appropriate tooltips for unsupported clusters.
- Add cluster badge interaction in `PeerGroupSelector` and dynamic placeholder handling.
- Simplify `ReverseProxyTargetSelector` messaging for proxy forwarding options.
- Enhance `ClustersFeaturesCell` with richer description for the "Private" badge.
* refactor(reverse-proxy): improve tooltip and modal text for clarity
---------
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Splits the Peers sidebar entry into User Devices (/peers/users) and Servers (/peers/servers), with /peers redirecting to User Devices and a shared kind filter splitting peers
by whether the owner is a real user vs a service/no-user. The Servers page description and an inline link replace what was the old "Setup Keys" sidebar item under Peers.
* feat(reverse-proxy): rebuild self-hosted page as Clusters with type + features
The Self-Hosted Proxies page was account-only by design but the
underlying API already returned every cluster the account could see.
Lifting that filter and renaming the page surfaces shared clusters
too — operators can see what NetBird-deployed clusters are reachable
alongside their own self-hosted ones, with online status and feature
support visible per row.
ReverseProxyCluster matches the new backend shape: `type`
(account/shared), `online`, and the three capability flags. The
`isSelfHostedCluster` provider hook now compares against `type ===
account` instead of a deprecated boolean.
Page folder renamed self-hosted-proxies → clusters (history-preserving
git mv). Table columns: Cluster (with an EphemeralPeerIndicator-style
icon next to the name marking account vs shared and a colored dot for
online status), Connected Proxies (plain numeric), Features (one
tooltip-backed badge per supported capability), Actions (Delete only
on account-owned rows; shared clusters render an empty action cell).
Empty state shows when the list is fully empty with a doc link in the
page header. Sidebar entry restored under Reverse Proxy.
* Update record in modal, update doc link, update modal title
* update reverse proxy documentation links to latest anchors
* update cluster modal description to "proxy cluster" instead of "self-hosted cluster"
---------
Co-authored-by: Eduard Gert <kontakt@eduardgert.de>