Compare commits

...
Author SHA1 Message Date
pascal af49dd6870 add timeline 2026-07-28 18:58:51 +02:00
pascal f6479c1f84 put on top of changeset branch 2026-07-28 17:13:13 +02:00
Eduard GertandClaude Fable 5 b523a7aa46 Live destination cards: match peer card height, show network inline
- Live destination peer cards (ResourceNode standalone branch) get the
  shared 64px card height instead of the old py-1 metrics
- Live standalone resource cards show "Name - Network" inline like
  draft (only drilled views keep hiding it - the header names it there)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-27 17:36:39 +02:00
Eduard GertandClaude Fable 5 8d66b88fea Clarify panel discard dialogs
"Discard changes to X?" read like dismissing the popup — the dialogs now
state the situation ("You have unsaved member/group changes") and spell
out that closing the panel loses the toggled assignments; the confirm
stays the short "Discard".

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-27 16:30:11 +02:00
Eduard GertandClaude Fable 5 c8ba72517c Components panel: no blink on tab switch
Tab/search changes no longer remount the Virtuoso list (which painted
empty for a frame while measuring) — the mounted list scrolls back to
the top imperatively instead; the open/close remount for fresh
measurements stays.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-27 16:19:46 +02:00
Eduard GertandClaude Fable 5 ccd5440a5f Panels swap in place and virtualize; edges pass drags to the pane
- Group/peer panels: no remount on target switch (motion key removed,
  placement kept) — the slide-in only plays when opening from closed;
  search/tab/selection still reset per target
- Peers, resources, and groups lists virtualize with react-virtuoso
  through the styled scroll area (shared PanelVirtuosoScroller); lists
  remount per target so scroll offsets don't carry over
- Edges (and their wide interaction path) get pointer-events: none —
  no edge has a click action, so dragging over connection lines now
  pans the canvas; replaces the dimmed-only rule

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-27 16:15:57 +02:00
Eduard GertandClaude Fable 5 fab63e2c08 Highlight Connections polish; fix draft-entry policy column gaps
- Rename Focus to "Highlight Connections" (Waypoints icon): context menu
  item, header pill text, exit aria-label. No ring on the highlighted
  node (the dim is the signal) and no menu entry on it while active
- Availability relaxed: any node with an edge, as long as the canvas has
  at least two policies
- Dimmed edges get pointer-events: none (incl. the wide interaction
  path) so drag-to-pan works over them in highlight mode
- Draft entry from peer view: adopt live twin sizes BEFORE the build
  layout — its overlap pass measured fresh nodes with an 80px fallback
  and shoved the 60px-pitch policy column apart (huge gaps until Auto
  Arrange re-ran on measured nodes)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-27 16:03:51 +02:00
Eduard GertandClaude Fable 5 700950b452 Peer rename in canvas context menu; user-device modal says Assign
- Extract the peer page's Edit Peer Name modal into shared
  EditPeerNameModal and reuse it from the control-center context menu
  (existing peers only, permission-gated); saving PUTs the peer,
  patches canvas nodes in place, and revalidates /peers
- User-device setup modal: "Select" -> "Assign" (header, step 2, button)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-27 15:42:37 +02:00
Eduard GertandClaude Fable 5 344004f05e Align live canvas card heights and text metrics
- Peer, placeholder, select-peer, and select-user nodes all match the
  group node's 64px height
- SelectGroupNode padding py-3 -> py-3.5 (same as GroupNode)
- DeviceCard default variant adopts the placeholder card's tighter
  title/subtitle metrics so installed and pending peers read the same
- User select avatar on the peer icon-box surface (nb-gray-850)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-27 15:31:54 +02:00
Eduard GertandClaude Fable 5 77fa76d9f1 Draft mode: install steps, group sync, placeholder flows, e2e suites
Placeholder peers (user device / server / agent):
- Track pending installs as install-peer changeset entries: own "Install"
  section in Review & Deploy, resolved by install/select or removal,
  renames follow, deploy skips them (user steps, not API calls)
- User-device setup stepper modal (install NetBird -> select peer);
  the canvas node drops its inline select, upgraded placeholders always
  become plain peer cards
- Placeholders open the peer groups panel (click or Details); assigned
  existing groups ride on the generated setup key as auto_groups
- Group panel lists absorbed draft members (held on the group node) with
  NEW badge, IP/address placeholders, and alert CTAs: "Install or assign"
  / "Install" / "No network" (network picker works node-less via
  assignHeldResourceToNetwork)
- Install hostname matching also scans group-held placeholders

Groups & policies:
- Group renames and membership changes propagate into the group copies
  held by policy nodes/edges (policy-group-sync) so the edit modal and
  PeerGroupSelector stay current
- New side groups added while editing a policy join their column
  (same x, stacked below) instead of landing at the origin
- Group names: "All" reserved, duplicates rejected against canvas drafts
- Dropping into "All" rejected; All still mirrors draft peers added to
  other groups (implicit membership, never deployed)
- No-network draft resources can join groups
- Delete/Backspace routes through the context menu's Remove semantics
  (useNodeRemoval) instead of React Flow's raw deletion

Review & Deploy:
- Canvas warnings for undeployable states (placeholder-peer policies,
  unassigned resources); modal stays open on partial failure so a retry
  resumes; update-group deploys against fresh API state
- Focus mode is explicit only (context menu / header tool) - left-click
  opens panels without dimming
- Drop-eligible groups show a white border mid-drag

Tests & docs: interaction matrix spec (specs/interaction-matrix.md),
three Playwright suites (lifecycle, connections, full matrix) on a shared
e2e helper, testids across draft controls, unit tests for canvas
warnings and policy-group-sync.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-27 15:19:46 +02:00
Eduard GertandClaude Fable 5 9dec55718b Live resource edit/disable, peer Details in context menu, dialog polish
Live resources open the networks page's resource modal on click (PUT on
save) and their context menu offers Focus / Edit / Disable — every
action behind the live-mode warning; the canvas patches from the
response. Peer context menus gain Details (opens the groups panel);
live group Delete is dropped (Focus / Details / Rename remain). Dialog
confirms gain an opt-in dismissOnOutsideClick, enabled for all
control-center warnings.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-27 12:09:57 +02:00
Eduard GertandClaude Fable 5 afad7ebbe3 Peer groups panel: click a peer to assign its groups
Clicking a peer opens the peer-side twin of the group panel — one Groups
tab listing every group ("All" excluded, draft-created ones included)
with assigned-first frozen order, search, and batch apply: draft stages
adds/removes into the changeset (canvas group nodes update in place),
live PUTs each changed group behind one warning confirmation. Same
chrome and close semantics (ESC keycap, discard guard, pane click), and
the clicked peer wears the panel halo — selectedPeerPanel lives in the
narrow DestinationGroup context so PeerNode can ring without
subscribing to CanvasState.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-27 11:53:06 +02:00
Eduard GertandClaude Fable 5 5f352614c8 Order drilled resources like frames; size network selector to its labels
The live single-network view now runs its resource column through
orderFrameResources, so the drilled view, the draft drill-down and the
overview frames all agree (policy-reached resources first). The network
selectors (live + draft drill-down) size to their longest option label,
clamped to the old 256px as max.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-27 11:33:41 +02:00
Eduard GertandClaude Fable 5 65b8f59031 Focus via context menu, live group actions, drilled group order parity
Focus Mode is entered from the node context menus ("Focus", shown only
where it declutters — 4+ edges and 2+ policies in the neighborhood, via
isFocusWorthy) or the "F" shortcut; the header button is gone and the
top-center pill owns the key handling. Live right-click now also covers
groups (Details / Rename / Delete behind live-mode warning confirmations,
PUT/DELETE + canvas patch) and peers/resources (Focus).

The shared drilled layout sorts both group columns by name so the live
single-network view and the draft drill-down agree on the order.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-27 11:25:51 +02:00
Eduard GertandClaude Fable 5 515ab88182 Rework focus mode into an explicit header tool; restore policy click-to-edit
Focus Mode is now armed from a header button next to the mode switcher
(SquareDashedMousePointer, sky accents, "F", switcher-style shell): the
next node click picks the focus target — hover previews the ring, node
buttons are inert, selectors/edge-less nodes are unfocusable — then the
mode goes sticky: the target keeps a glowing sky ring (cc-focus-root),
on-path nodes stay clickable/movable (dimmed ones lock), group panels
open without stealing focus, and only Esc / the top-center pill's X /
the button exit. The pill (switcher surface, fully rounded) names the
focused node. Fit-view moves to "1" to free F.

Policy left-click opens the editor again (live and draft); the context
menu keeps Edit/Disable. Network frames order policy-reached resources
first, via one shared helper so live and draft frames agree.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 18:19:19 +02:00
Eduard GertandClaude Fable 5 102891784e Add a Beta badge to the Draft tab in the mode switcher
Same SmallBadge treatment as the sidebar's Reverse Proxy entry.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 17:33:02 +02:00
Eduard GertandClaude Fable 5 523095d3bd Rebuild the group panel as a membership editor with checkbox assign
The destination group panel becomes a search + Peers/Resources tabs
editor (PeerGroupSelector-style rows and checkboxes): toggles edit a
local selection previewed on the node's count text, and Assign (draft)
stages ONE coalesced update-group change while Save (live) PUTs behind
the live-mode warning. Implicit closes (Esc, keycap, pane click) confirm
before discarding unassigned toggles via a shared close guard; Cancel
discards directly. The changeset/deploy pipeline learns member REMOVALS
(removedPeerIds/removedResourceIds + removedMembers node sets).

Panel chrome: components-panel surface, right-anchored full-height with
header-width tracking (in-place on width changes — no flash), toolbar
spring slide-in, singleton mount, frozen row order per open, and the
canvas pans the group node clear of the panel. Focus mode is skipped for
nodes without connections, and mode switches close the panel and drop
node selection.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 17:30:52 +02:00
Eduard GertandClaude Fable 5 5a113b6b64 Reset the components panel scroll when switching category tabs
Virtuoso keeps its scroll offset across data swaps, so changing the
category (or entering/leaving search) landed mid-list at the previous
tab's position. Remount the list on category/search-mode changes, like
the existing open/close remount.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 16:27:02 +02:00
Eduard GertandClaude Fable 5 25cc20efcc Pin the draft frame grid to the live overview's fixed midline
The draft build centered the frame grid on the measured midpoint of the
source/policy columns, which always landed a few px off the live
overview's fixed midline — and the live-frame anchor shift then pushed
that delta onto the columns, so peer/policy cards hopped a few pixels on
every live↔draft switch. Use the same fixed midline as live
(SOURCE_NODE_HALF_HEIGHT) so the geometry matches exactly and nothing
moves.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 15:35:30 +02:00
Eduard GertandClaude Fable 5 e23272d163 Align the networks overview's columns with the other views' anchors
Policies move from x 480 / -17 center offset to the shared x 500 / +14
anchor, sources drop their -30 half-height shift, and the frame grid
centers on the columns' midline instead — so a policy and its source
keep their world position when switching between the peer/user/group
and networks views. The draft build layout mirrors the same change to
keep entry/Auto Arrange parity.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 15:27:40 +02:00
Eduard GertandClaude Fable 5 80254c7d9a Keep the focused node draggable in focus mode
Focus mode turns global nodesDraggable off so dragging pans, which also
froze the focused node itself. The dim effect now stamps a per-node
draggable: true on the focus root (overrides the global flag) and strips
it when focus ends — works for policy/peer/resource focus and the group
panel alike.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 15:20:27 +02:00
Eduard GertandClaude Fable 5 dd08aea531 Focus policies/peers on click; live policy context menu with Edit + toggle
Clicking a policy or peer now enters focus mode (path highlight + blue
ring) in live and draft instead of opening the policy modal. Policy
editing moves to the right-click menu: in live it offers Edit and
Disable/Enable only — both behind a "you are in live mode"
confirmation — applying via updatePolicy and patching the canvas in
place through refreshLiveViewRef. No Delete in live.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 15:16:40 +02:00
Eduard GertandClaude Fable 5 63abf6bbfb Keep parents before children on the controlled canvas
On a controlled flow, instance.setNodes round-trips through
applyNodeChanges, which keeps replaced nodes at their ORIGINAL index —
so reparenting a resource that predates its frame (network drop
adoption, assign-to-network) left the child in front of the frame and
ReactFlow dropped the containment: correct parentId in state, card
rendered detached at frame-relative coordinates.

Reconcile the invariant centrally in onNodesChange (and heal persisted
snapshots saved with the bad order on draft restore). Also stamp the
owning network on policy-drop destination resources so they don't show
"No Network".

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 14:49:17 +02:00
Eduard GertandClaude Fable 5 5605ff4eed Adopt pre-assigned draft resources when their network lands on canvas
Assigning a resource to a network that isn't a frame yet stamps only the
ref; dropping that network later now adopts those resources into the
frame (and the create-network assign waits for the frame to reach the
ReactFlow store instead of racing it on a 0ms timeout).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 14:49:05 +02:00
Eduard GertandClaude Fable 5 1b29d44146 Make Auto Arrange object-aware: resolve node overlaps after layout
Stragglers outside every layout column (e.g. a standalone resource in a
frames draft, type-bucketed at x 1400 inside the frame grid) could land
on top of another node. Add resolveNodeOverlaps to the shared draft
build layout: larger nodes anchor, smaller intersecting nodes are nudged
the shortest way clear plus a 40px margin, re-checked until free.
Detection is exact intersection so tight column rhythms keep entry-layout
parity. Unit-tested.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 14:21:41 +02:00
Eduard GertandClaude Fable 5 3516fa6f42 Make Auto Arrange reproduce the initial layout in every view
Extract the draft build layout into utils/draft-build-layout.ts
(applyDraftBuildLayout) and use it for the toolbar's Auto Arrange, so
arranging an untouched draft matches the entry layout exactly for drafts
entered from any live view; drill-downs re-run the shared drilled layout.
Delete the divergent applyDraftArrangeLayout and its height-aware pitch
machinery.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 14:15:16 +02:00
Eduard GertandClaude Fable 5 d103f171ea Polish the Assign a network modal
- Footer buttons follow the shared modal pattern (right-aligned, gap-3)
  with the header's separator color; CTA renamed Save -> Assign Network.
- Network select: search inside the popover, popover matches trigger
  width, list capped at 190px, placeholder "Select or create a
  network...".
- New optional `footer` slot on SelectDropdown: content pinned below the
  scrollable options (always visible regardless of scroll/search) — used
  for "Create New Network", styled like a regular entry.
- Creating a network opens the Add Network form ON TOP of the picker
  (nested modal); Cancel returns to the picker instead of closing both.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 13:59:24 +02:00
Eduard GertandClaude Fable 5 8583fd9175 Gate canvas deletion: nothing in live, no standalone edges in draft
onBeforeDelete on the ReactFlow canvas: live mode blocks Backspace/
Delete entirely (the canvas mirrors the account); draft mode keeps node
deletion (edges cascade with their endpoints) but never deletes a
selected connection on its own.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 12:48:57 +02:00
Eduard GertandClaude Fable 5 d43c51495d Center-align source/policy columns with network frames
The networks view stacked source and policy node TOPS on the column
midline while the frame grid centers frames on it, leaving a lone peer
~30px and a policy pill ~17px below their frame. Both the live overview
and the draft rebuild now subtract shared half-height constants.

Draft fixes this exposed: packFrameGrid also runs for a single frame
(previously left at the far layout column, so the live-anchor shift
dragged the columns ~350px out of line), and the frame-grid midline
falls back to the live twin's measured height instead of an 80px guess.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 12:39:44 +02:00
Eduard GertandClaude Fable 5 47a01c68c1 Unify column spacing and kill the live<->draft mode-switch flicker
- Source columns (live peer/group/user views, user-view peers, draft
  rebuild) share the destination column's 100px pitch instead of 80/120.
- Live destination resources show "Name - Network" inline like draft
  (network ref stamped in addDestinationResourceNodes).
- Draft rebuild: dual-role groups (source AND destination) restack in
  the destination column like the live view shows them.
- Mode-switch flicker: React Flow hides unmeasured nodes for one frame,
  blanking the swapped canvas. Draft nodes now adopt their live twin's
  measured size (initialWidth/Height), including renamed ids (self-ref
  dest-group clones, destination resources/peers).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 18:04:01 +02:00
Eduard GertandClaude Fable 5 966d7bca36 Group view: show policies where the group is a destination
Policies were matched by rule.sources only, so a destination-only group
rendered an empty view. Those policies now render mirrored to the left
(sources -> policy -> selected group); source groups carry explicit
enabled state so they don't fall back to the incoming-edge check and
render dimmed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 17:38:25 +02:00
Eduard GertandClaude Fable 5 e3ef24e9bd Rewrite control-center CLAUDE.md: 326 → 67 lines
Keep architecture, layout/sort parity rules, changeset/deploy facts, and
the hard-won perf/camera gotchas; drop per-file trees and feature
narration discoverable from code. Fix stale claims (change types,
deploy order, provider names, focus-mode scope).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 17:27:25 +02:00
Eduard GertandClaude Fable 5 e79de5e317 Match draft rebuild layout/order to the live view it was entered from
Peer/group/user-entered drafts used the networks-overview rhythm (160
base, policies 480/90) and scattered destinations across layout buckets.
Now frameless drafts use the live views' spacing (120 + DEFAULT_LAYOUT_
CONFIG), restack destinations as one x1000/100 column ordered like live
(groups first, then resources/peers by first policy edge), sort policies
with the views' sortBy, and name-sort destinations on group-view entry.
Existing destination resources get their real network stamped (no bogus
"No Network" badge), and the live destination-peer card sizes to content
(w-auto) like the draft peer card.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 17:21:30 +02:00
Eduard GertandClaude Fable 5 a6f43c8ef0 Fix top-left node flash when returning to control center
On client-side nav back to the page, nodes commit before fitView's
measurement wait completes and painted at the default viewport. The
first fit after mount now hides the viewport (cc-prefit, instant hide),
snap-fits without animation, and fades the scene in already framed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 16:57:45 +02:00
Eduard GertandClaude Fable 5 cc0455d4a5 Unify live/draft network view: interactive live frames, matching layout
- Live frames gain the draft chrome for real: Add Resource opens the
  networks ResourceModalContent (real POST), routing bar Add + zero-state
  open the real RoutingPeerModalContent; the Add band is reserved in both
  modes
- Live frames contain destination resource-group rows like draft
  (per-network ids); peer-only destination groups are skipped in the
  networks view (live never drew them; networks-entered drafts drop them
  + their edges); draft group folding matches canDropGroupIntoNetwork
  (one member resource in exactly one network, peers don't block)
- Identical geometry and order across modes: shared FRAME_GRID_BASE_X,
  frame heights re-seeded after folding, group rows first, sources
  (groups + source peers) as ONE 160-pitch column, policies at x480/90,
  every column + the frame grid sorted by name
- Routing-peers dropdown polish: rows prefetch on trigger hover (no
  skeleton flash), skeleton geometry matches the final list, exact list
  height (no scrollbar for short lists), tighter search-to-rows gap

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 16:48:57 +02:00
Eduard GertandClaude Fable 5 f3d4ef75ae Focus mode works in draft mode too
useGroupFocusDim and the focus drag-lock no longer gate on live mode;
className joins the history's transient keys so focus toggles never
create undo entries.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 16:05:22 +02:00
Eduard GertandClaude Fable 5 b3dde3f6a2 Live focus mode + policy nodes in networks overview; drop user-detail view
- Networks overview builds source -> policy node -> network frame with
  smart edges (like draft) instead of labeled straight lines; sources
  left, policies middle, packFrameGrid right; frames whose policies are
  all disabled dim (frame + child rows)
- AnimatedLine is a SmartEdge-style bezier with a midpoint label chip
  (orthogonal channel router removed); drill-out reveal zooms out
- Focus mode (live): clicking a group opens its panel, clicking a
  peer (user view) or resource card sets focusedNodeId; everything off
  the node's path dims to grayscale (forward+backward closures) via
  cc-dimmed className; dimmed nodes are non-interactive and clicks fall
  through to the pane which exits focus; node dragging disabled while
  focused (dragging pans; per-node draggable:true removed - it
  overrides the global flag)
- User-detail view removed: previousSelectedUser breadcrumb,
  forceSinglePeerView/forceSingleUserView, user-peer history level,
  onForceSingleUserView
- Bigger frame grid gaps (280/200)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 16:03:27 +02:00
Eduard GertandClaude Fable 5 93010cd3d7 Drill-out reveal zooms out instead of growing in
The fly-out's reveal reused the drill-in's grow-in (0.7x -> fit), which
read inverted going back; direction "out" now starts at 1.45x and
settles outward, mirroring the dive.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 14:56:12 +02:00
Eduard GertandClaude Fable 5 34b2ce3493 Fix whole-canvas re-renders: stable ReactFlow props + memoized contexts
React profiler showed ~2700-fiber User-Blocking commits (120-170ms) on
every drag tick, hover flip and click. Root causes and fixes:

- DraftModeProvider context value memoized (hover state changes handed a
  fresh value to every useDraftMode consumer = every node)
- Every callback/object prop on <ReactFlow> is identity-stable via
  useStableHandler; inline callbacks defeated GraphView's memo so each
  nodes update re-rendered the entire canvas subtree
- selectedDestinationGroup split into its own context (group click
  re-rendered every node via useCanvasUI)
- Edges subscribe to endpoint rects via useEdgeNodeRect (value equality)
  instead of useInternalNode; ResourceNode's parent-hidden check likewise
- elevateEdgesOnSelect off (edge DOM reshuffle restarted dash animations)
- Draft persistence + history capture skip while dragging and pre-check
  structurally before stringifying; history ignores transient node state
- Lazy router-rows fetch for draft frames too; Add Resource button uses
  a ref instead of mounting useControlCenterData per frame
- Components panel, PeersToolbar, DestinationGroupPanel, CanvasToolbar,
  DraftEmptyCanvas subscribe structurally (useStructuralNodes), not to
  the churning canvas context
- Drag drop-target highlight throttled to ~8px steps
- Live networks overview arranged like draft (sources left, packFrameGrid
  staggered frame grid right; d3 force layout removed from this view)
- Draft build: resource-only destination groups fold INTO their network
  frame; frames seeded with the capped grid; useAnySourceGroupEnabled
  skips when enabled is explicit

Verified via Chrome trace on the live app: 4ms/s JS during a 125Hz drag
over 36 network frames, no long tasks.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 14:50:43 +02:00
Eduard GertandClaude Fable 5 be6744aa18 Live view policy destinations render as cards
destinationResourceNode gets standalone: true in the shared builder —
resource destinations route to the StandaloneResourceNode card, peer
destinations get the same card surface as PeerNode's card variant
(previously both were transparent rows).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 10:44:20 +02:00
Eduard GertandClaude Fable 5 6eec46e37c Draft overview network selector + drill history survives mode switches
Draft mode (not drilled) shows the top-left network SelectDropdown like
live: "All Networks" first, canvas frames sorted by name, picking one
drills in. Drill history entries are consumed deferred (150ms) so the
transient deactivation during draft/live switches reuses the entry
instead of racing history.back() — browser Back after switching modes
now exits to the overview instead of leaving the page.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 10:24:26 +02:00
Eduard GertandClaude Fable 5 a9836381d4 Hide peer connect handles in live networks view
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 10:13:19 +02:00
Eduard GertandClaude Fable 5 5882d95343 Browser Back exits canvas drill-downs; lighter simple connection lines
useDrillDownBrowserHistory tracks a stack of drill levels (network
drill-down, user-to-peer breadcrumb): entering pushes one same-URL
history entry, popstate exits through the same path as the UI back
arrow, UI exits consume the entry silently. SimpleConnection stroke
lightened from #595959 to #7d7d7d.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 10:08:57 +02:00
Eduard GertandClaude Fable 5 6526f72f4d Fix misaligned camera when returning to control center via client nav
Warm SWR cache makes the view initialize before ReactFlow measured the
freshly remounted nodes; fitView now retries (bounded rAF loop) until
every target node has a measured size before fitting.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 09:54:58 +02:00
Eduard GertandClaude Fable 5 8aebc55be2 Group view initial pick falls back to All when only All has policies
getFirstGroup excluded All from the has-policies check, so an empty
non-All group could win over a populated All view. New order: non-All
group with policies, then All with policies, then any non-All, then any.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 09:51:54 +02:00
Eduard GertandClaude Fable 5 aeea740bd2 User view peers render as draft-style cards without connect handles
PeerNode now always renders the invisible AllHandles edge anchors
(GroupNode's pattern) so showHandles:false only hides the visible
connect bubbles instead of detaching edges.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 09:39:39 +02:00
Eduard GertandClaude Fable 5 7c0f6b4d66 Live policy saves patch the canvas in place from the PUT response
No more waiting on the SWR /policies revalidation (or a layout reset):
refreshLiveView rebuilds the current view with the saved policy spliced
in, keeping positions and camera; all view appliers accept a
policiesOverride and the policy modal reads the canvas node's policy
first in both modes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 09:29:26 +02:00
Eduard GertandClaude Fable 5 9908a5ddcb Shared drill-down canvas transition (dive/fly-out) for draft and live
utils/canvas-transition.ts: one continuous zoom stitched across an invisible
swap — accelerating dive/zoom-out + fade-out (half ease-in), swap while
transparent, grow-in reveal + fade-in (half ease-out) into a viewport
computed from the new scene. One-liner facades drillInto/drillOutOf (Node or
Rect targets) for any future drill-down; isCanvasTransitionActive suppresses
view-init fitViews mid-transition.

- Draft drill-down enter/exit refactored onto it (position-aware close-up on
  exit, exitingRef guards the reconciling repair; frame layout + edge
  attachment + row/card rendering key off frame.hidden so nothing morphs
  while visible)
- Live network select dives into the clicked frame (dropdown picks zoom from
  center), back flies out to the overview

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-22 17:54:02 +02:00
Eduard GertandClaude Fable 5 acabf71011 Exiting draft mirrors the draft's drill state in live
Backed out of the drill inside draft → live lands on the all-networks
overview instead of the stale drilled snapshot.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-22 16:41:19 +02:00
Eduard GertandClaude Fable 5 9e5816c5a4 Unify the single-network (drilled) layout across live and draft
- utils/drilled-layout.ts: ONE layout (applyDrilledLayout, 95px resource
  pitch, getDrilledFrameAnchor incl. the column's +5 centerY offset) shared
  by the live single-network view, the draft drill-down, and live→draft
- Draft drill re-lays the kept world out with it (parent positions
  snapshotted, restored on exit with hidden flags + viewport); drilled frame
  grid is a fixed-pitch single column, cards auto-size (no forced width)
- Live networks render as read-only frames (RoutingPeersBar without Add,
  frame anchors/handles for edges, hover highlight, click/row-click drills,
  bottom padding instead of the draft Add band); drilled resources render as
  standalone cards in live too, with direct policy→resource edges
- Entering draft from live drilled carries the network as a frame and enters
  the draft drill-down in the same commit (pre-hidden, no flash); exiting
  draft while drilled lands in the live view of that network
- Live all-networks lines are blue with an All fallback label; peer sources
  drawn in both live network views; NEW badge only for id-less networks
- Drill header: name chip matches the live selector (icon size/color,
  typography, paddings), edit segment for all frames (existing networks open
  the real network modal), RoutingPeersBar height/chevron aligned with the
  fixed-height network selector; SelectDropdown defers onChange past its
  close animation (no blink) and uses a 16px chevron

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-22 16:14:07 +02:00
Eduard GertandClaude Fable 5 48e899ed65 Shared RoutingPeersBar for draft frames and the live network header
Extracts the frame's [status | Add] routing button group + searchable
routers popover into RoutingPeersBar (indicator, row type, peer-count and
sort helpers). NetworkNode consumes it unchanged; the live single-network
header's NetworkRoutingPeerCount now renders the same bar over the API
routers, with Add/edit navigating to the routing-peers tab. Status button
left padding 14px.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-22 12:37:13 +02:00
Eduard GertandClaude Fable 5 fc68538457 Frame's Add Resource button drops a blank row instead of opening the editor
Matches the context menu's Add Resource (addResourceToFrame); the editor
still opens on row click.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-22 12:14:40 +02:00
Eduard GertandClaude Fable 5 b994d703ff Network frame routing-peers dropdown
- Frame status button gains a ChevronsUpDown trigger and a PeerSelector-style
  popover (search by peer/group name, no enter badge): one row per router with
  muted OS icon + peer name or group badge + group name (peer count inline)
- Draft router rows open the routing-peer modal prefilled (editChangeId on
  routingPeerModal); save replaces the create-router change
- Status counts peers, not routers: groups contribute their peer count,
  disabled routers contribute nothing and render at 50% opacity, sorted last
- Floating bar nudged 2px left; NetworkRouter API routers of existing
  networks are listed too (read-only)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-22 12:08:18 +02:00
Eduard GertandClaude Fable 5 59af25d45c Groups can drop into network frames; selection, count-label and grouping fixes
- Drag a group onto a network frame → converts in place to a resource-group
  row inside it (same node id, policy edges re-attach to the frame). Eligible
  when the group is empty, has a resource already in that network, or carries
  unassigned draft resources (canDropGroupIntoNetwork, tested)
- Grouped standalone draft resources ride on the group node (data.draftResources);
  dropping the group into a frame records create-resource changes assigning
  them to that network with the group membership
- Frame children are selectable:false — full-width rows no longer join any
  grazing rubber-band selection (phantom Create Group members)
- Create Group resolves draft resources via getDraftResource so their new-…
  ids reach the group (count label, changeset membership, policy regrouping)
- Group count label leads with resources: "2 Resources, 1 Peer"

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-22 11:24:00 +02:00
Eduard GertandClaude Fable 5 ee7f1d1dfb Resource-sourced drags onto peers/groups open the policy modal flipped
Dragging from a resource onto a peer/group prefills the create-policy modal
with the resource as destination and the peer/group as source (framed
resources keep the network destination scope). Also: context-menu Add
Routing Peer uses Share2Icon, NetworkNode Add buttons use CirclePlusIcon.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-22 10:56:27 +02:00
Eduard GertandClaude Fable 5 d6bc80670f Dropped and dragged nodes settle on top of the canvas z-order
Shared getTopZIndex (max settled z + 2, beating frames and their children)
applied in placeNode, dropExistingNetworkFrame, addNewGroup, and
onNodeDragStop — e.g. a peer dropped over a network frame paints above it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-22 10:35:49 +02:00
Eduard GertandClaude Fable 5 e8ba1d7378 Policy Remove empties its sources/destinations; re-drop uses draft state
- Removing an existing policy records an update-policy change with emptied sources/destinations (draft policies still just cancel their pending create)
- Components panel re-drop (and network/resource auto-drawn policies) prefer the pending update-policy state over API data, so a disconnected policy doesn't come back with its deployed sides

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-22 10:29:01 +02:00
Eduard GertandClaude Fable 5 1f6028253e Draft policies: pull in resource policies on drop, canvas-only policy Remove, delete confirms
- Dropping an existing network/resource also draws the API policies targeting its resources (getPoliciesTargetingResources + drawResourcePolicies)
- Policy context menu Remove (CircleMinus, no confirm) removes only the policy node and edges, keeps sources/destinations on canvas, cancels pending create / drops pending updates
- Deletes (policy/resource/group) confirm: marked for deletion, deleted on Review & Deploy
- Group node removal strips the group from connected policies (like peers/resources); deferred policy sweep skips policies removed in the same cascade
- Remove icons switched to CircleMinusIcon; unit tests for the policy matcher

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-22 10:15:09 +02:00
Eduard GertandClaude Opus 4.8 b5e3d8ff88 Fix group node "Details" being immediately dismissed
The context menu's item-click and outside-click shared one onClose that
cleared the panel too, so "Details" opened the group panel then instantly
closed it. Split into onClose (menu only, for item clicks — stopPropagation so
it doesn't reach the document listener) and onDismiss (everything, for outside
click/scroll). Details now opens the panel and it sticks; outside click still
dismisses menu + panel in one click.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-21 17:09:15 +02:00
Eduard GertandClaude Opus 4.8 3a3b0785c3 Fix live hierarchical layout alignment regression
centerNodesVertically is shared by the live hierarchical layout and the draft
auto-arrange; the height-aware pitch added for tall network frames broke live
column alignment (unequal per-node pitches). Make height-aware pitch opt-in
(auto-arrange only); live keeps a fixed pitch so equal-height nodes stay in a
straight line.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-21 17:03:52 +02:00
Eduard GertandClaude Opus 4.8 0913a6aa2c Standalone resources, existing-network frames, resource changeset ops
- Dropped resources are standalone cards with an inline network label / "No
  Network" picker (DraftResourceNetworkModal); assign via drag-into-frame or
  the picker; extract StandaloneResourceNode
- Existing networks drop as full frames (dropExistingNetworkFrame) with their
  resources as children; reparent an already-dropped resource instead of
  duplicating its node id
- Refactor frame-ness to an explicit data.frame flag (isFrameNode /
  isDraftNetworkNode); network-new- now means only "draft network", existing
  frames keep their real id
- Resource context menu: Edit (all), Enable/Disable, Rename (draft), and
  Remove vs Delete (existing framed) — wired into the changeset via new
  update-resource / delete-resource change types + deploy PUT/DELETE; draft
  enabled folds into create-resource
- Components panel: existing resources show "name - network" and can't drop
  into a frame; resource drags highlight the target frame (white border)
- Frame z-layer so no node renders between a frame and its resources
- Auto-arrange stacks columns by measured node height
- Group node "Details" menu item opens the panel; policy left-click no longer
  leaves a halo; misc spacing/badge/icon tweaks

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-21 16:57:21 +02:00
Eduard GertandClaude Opus 4.8 aa8be0655d Network frame refinements: resource groups, spacing, auto-arrange heights
- Add "Add Resource Group" context-menu item (default group icon); blank
  group rows drop first in the frame and count toward layout/overflow
- Group naming now consistent ("Group", "Group (2)", …) everywhere
- Resource groups: Rename + Details context menu, sky halo on right-click,
  hover-only white ring during connects, "No Resources" count label
- Group node context menu gains "Details" (list icon) to open the panel;
  clicking a group no longer toggles the panel closed (only outside click)
- Newly added resources/groups always land first in the frame grid
- Auto-arrange stacks columns by measured node height so tall network
  frames (with floating routing bars) don't overlap neighbours
- Tune Add Resource button spacing (top gap + bottom padding)
- Remove routing-peer HelpCircle + HA tooltip; nudge "+N more" cell 1px
- Fix User Device select node NEW badge alignment

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-21 12:09:57 +02:00
Eduard GertandClaude Opus 4.8 1471767e27 Network frame: floating Add/routing controls + bottom Add Resource button
- PeerNode: server/agent placeholder Install button uses the shared Button
  (secondary, xs) to match the User Device select node.
- NetworkNode: rework the frame's floating controls — an "Add" resource
  control and a routing-peers button group ([● status | + Add]) sized to
  match the Install button (secondary, xs).
- Add a secondary "Add Resource" button in the frame body: centered when
  empty, full-width bottom row once resources exist; opens the resource
  editor. Reserve a bottom band for it in the frame layout (mutually
  exclusive with the "+N More" overflow footer).
- Sparse frames (0/1 resource) match the two-resource width; empty frames
  get a little extra height so the centered button breathes.
- Hovering the floating controls / add button no longer highlights the
  frame or reveals its ConnectHandle (new ConnectHandle `hidden` prop).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-20 17:28:13 +02:00
Eduard GertandClaude Opus 4.8 b26c6f0a12 Network frame: header Add dropdown + "+N More" overflow footer
Rework the draft network frame's resource affordances:

- Move adding into a header "Add" dropdown (chevron) on the right of the
  name/count, with Routing Peer (server icon) and Resource (resource
  icon) items. Removes the separate "Add" button from the floating
  routing-peer status pill and drops the always-present "Add Resource"
  child row (AddNetworkResourceNode deleted).
- Summarize resources past the visible cap with a "+N More" footer
  rendered by NetworkNode in a band useNetworkFrameLayout reserves at the
  frame bottom (pointer-events-none, so clicking still drills in),
  replacing the fade mask and the short-lived ResourceOverflowNode.

useNetworkFrameLayout is simplified to lay out only resource children and
sweeps legacy add-resource/overflow child nodes from persisted drafts.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-20 10:27:31 +02:00
Eduard GertandClaude Opus 4.8 b1824b50ba Add networks empty with an always-present Add Resource row
New draft networks are created without seeded resources: drop the
layout-testing seed (30 fake resources + 10 resource groups) from
addDraftNetwork so a network starts empty.

Every network frame — including a freshly-added empty one — now always
contains the "Add Resource" child row: useNetworkFrameLayout creates it
if missing and no longer removes it when the frame is empty (dead
obsoleteAddRows path removed). The centered "No resources yet" hint is
gone; the header subtitle reads "No Resources" when empty and the frame
keeps its header separator.

Also removes the bottom-fade gradient overlay from the existing-network
card's resource preview grid.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-20 09:41:49 +02:00
Eduard GertandClaude Fable 5 9bfb73231d Draft editors, context menus, components panel & selection toolbar
- Resource editor gains a create-mode (frame's Add Resource row opens the
  modal; the node is only created on save) and the provider nesting fix
  for the resource-modal refactor; new DraftNetworkEditModal (name +
  description) shared by the network context menu and drill-down header.
- Context menus: Edit items (SquarePen) for policies/networks/resources,
  policy Remove vs Delete split (draft vs API), PencilLine renames,
  Add Resource on frames, Policy above Group ordering.
- Components panel: draft peers/policies/networks listed with NEW badges
  (IP placeholder, kind icons), "Networks & Resources" category with
  existing networks droppable as live cards, virtualization measurement
  fixes, panel hides once a real drag starts.
- Selection toolbar: Remove option next to Create Group, padding fix.
- Auto-names drop the "New " prefix ("Group", "Resource").

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 17:54:06 +02:00
Eduard GertandClaude Fable 5 987054f765 Network frames v2: destination picker, drill-down, routing modal, layout
Spec §10 implementation plus the frame interaction model:

- Connections: peer/group drops onto a frame or its contained rows open
  the create-policy modal (source prefilled, destination scoped to the
  network, one-way); POLICY↔network drags open the minimal destination
  picker (DraftNetworkDestinationModal). Drag no longer creates routers.
- Policy edges to framed resources attach to the frame in the parent view
  and to the actual resource in the drill-down (useFrameEdgeAttachment,
  pure logic + tests in utils/frame-view).
- Drill-down: clicking a frame enters a live-mode-like single-network view
  (no frame box, square-ish grid, back/name/edit/routing-count header);
  hidden flags + viewport restore on exit.
- Routing peers: the frame button group and context menu open the networks
  page's routing-peer modal; routers are changeset-only (no canvas nodes),
  counts derive from create-router changes, deploy sends the modal's
  metric/masquerade/enabled.
- Frame layout: 4x2 row-major grid capped at 6 visible resources with a
  fade-out on the last row, always-visible Add Resource row, tunable
  padding/gap/width constants, drop z-order fix for stacked frames.
- Node visuals: NEW badges after draft names, frame-hover highlights
  header/border/icon boxes, placeholder select copy, min widths.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 17:53:51 +02:00
Eduard GertandClaude Fable 5 71cc5c038d Pure-data modal modes and draft-aware props for shared components
- NetworkModal, NetworkRoutingPeerModal and NetworkResourceModal gain a
  useSave={false} mode: no API calls, values return via onSaved — the
  draft canvas reuses the exact networks-page modals.
- AccessControlModal: destinationScope restricts the destination selector
  to a network's resources/groups (no peers) and locks the policy one-way.
- PeerGroupSelector: groupIds whitelist (disables inline group creation),
  initialTab (open tab without reordering triggers), scoped empty states,
  and a default icon for type-less draft resources.
- ResourceBadge/PeerOperatingSystemIcon: default resource icon and
  Server/Agent/User-Device icons for draft placeholder peers.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 17:53:31 +02:00
Eduard GertandClaude Fable 5 991ee63b6d DRY up NetworkNode and the full-area target handle
NetworkNode: RoutingPeersIndicator and AddRoutingPeerButton extracted
(shared by the draft frame and the live card). The invisible
whole-node target handle becomes handles/FullAreaTargetHandle, reused
by AllHandles, PolicyNode, and NetworkNode.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 14:20:59 +02:00
Eduard GertandClaude Fable 5 869ae3af02 Dragging a contained resource moves its network frame
Frame children are draggable again (non-draggable nodes emit no drag
events — the gesture fell through to canvas panning). A drag-start
handler snapshots child + frame positions; each drag tick transfers
the child's displacement to the frame while snapping the child back
into its slot, so the whole frame (and every resource in it) follows
the cursor. Framed resources stay excluded from drag-into-group.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 14:14:22 +02:00
Eduard GertandClaude Fable 5 fe5eae8625 Dragging a contained resource drags its network frame
Restore ReactFlow's fall-through: frame children are non-draggable,
so a drag on a resource moves the parent frame (and everything in
it). Clicks still open the resource editor.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 13:53:49 +02:00
Eduard GertandClaude Fable 5 79096499c1 Stop contained resources from dragging their network frame
Dragging a non-draggable frame child fell through to dragging the
parent — contained resources now carry the "nodrag" class (clicks
still open the editor). Frame width reduced to 340px.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 13:51:33 +02:00
Eduard GertandClaude Fable 5 c75f4d93f1 Document accepted draft-mode v1 limitations
specs/limitations.md: framed resources can't drag into groups (use the
editor's Groups field), routing-edge-only deletion leaves its router
change, network descriptions not editable on canvas.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 13:48:49 +02:00
Eduard GertandClaude Fable 5 7d1281f941 Enlarge the draft network frame
420px wide with a taller header (64px), roomier rows and padding;
contained resources keep spanning the frame width.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 13:46:37 +02:00
Eduard GertandClaude Fable 5 a57f4a6276 Fixed full-width resources inside network frames; roomier nodes
- Contained resources are laid out by the frame: not draggable, and
  spanning (basically) the full frame width; popping out of a removed
  frame restores free dragging and auto width
- Network frame widened (360px) with slightly taller header/padding
  and peer-style hover treatment
- Group nodes get a little more padding

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 13:44:51 +02:00
Eduard GertandClaude Fable 5 b9e65b1ad6 Simplify draft resource/network node presentation
- No "Set up" affordance above incomplete resources — the dimmed
  x.x.x.x placeholder and click-to-edit carry that state
- The resource editor no longer opens on drop (click / context-menu
  Edit instead)
- Network frames: solid background with a dashed border
- Routing-peer indicator hidden entirely at 0 (the floating Add
  Routing Peer button covers it); NEW badges removed from draft
  resource and network nodes

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 13:37:59 +02:00
Eduard GertandClaude Fable 5 2f86fd0421 Draft resources: peer-style cards inside auto-created network frames
- Dropping a resource auto-creates a draft network FRAME around it —
  a bordered, slightly opaque container node; the resource lives
  inside as a ReactFlow child (frame grows per member, children pop
  out at absolute positions when the frame is removed)
- Draft networks (panel template + editor inline-create) use the
  frame rendering: header with name/NEW badge/routing traffic light,
  "No resources yet" hint, floating Add Routing Peer button
- The draft resource node mirrors the placeholder-peer card: derived
  type icon box, NEW badge, dimmed "x.x.x.x" address placeholder;
  left-click and context-menu Edit open the resource editor
- Resources carry a single LEFT connect handle — dragging into a
  policy sets it as the destination (source side rejected); network
  assignment reparents into frames instead of membership edges
- Auto-arrange is parent-aware: child connectivity resolves onto the
  frame, children keep their relative positions

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 13:26:08 +02:00
Eduard GertandClaude Fable 5 15475d8b14 Add unit tests for draft networks, resources & routing peers
- draft-connect: peer/group→network router connects, resource→network
  membership, network/resource source rejection, policy-side resource
  rules, draft-resource prefill with derived types
- changeset: create-network/resource/router recording, fold-into-create,
  network removal cascade, group-rename following into resource/router
  refs, placeholder router id renames, membership cleanup
- capabilities: routing-peer/assignable/configurable/renamable matrix
  rows; complete vs incomplete draft-resource groupability
- helpers: address→type derivation, isCompleteDraftResource, policy
  gating on tracked draft resources
- storage: round-trip of the three new change types

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 12:36:34 +02:00
Eduard GertandClaude Fable 5 e89d7251e2 Implement draft-mode networks, resources & routing peers
Implements specs/draft-networks-resources.md (v1):

- Networks drop with a create-network change (unique auto-name,
  deployable immediately); NetworkNode is draft-aware — NEW badge,
  draft resource count/previews, routing-peer traffic light from
  routing edges, floating "Add Routing Peer" button at 0 routers,
  Rename / Add Routing Peer / Remove context menu (removal cascades
  to dependent resource/router changes)
- Resources drop + open the new DraftResourceEditorModal (unique
  name, validated address, network selector with API + draft
  networks and inline create, optional groups/description);
  incomplete resources carry an amber "Set up" affordance and stay
  out of the changeset; completing one re-runs referencing policies
- Routing peers: peer/group → network connects (and the Add Routing
  Peer install flow) draw "routes" edges and record create-router;
  placeholder routers gate until install, then the upgrade sweep
  records them with the real peer id
- Resources are destinations-only (hard-rejected as connect
  sources); draft resources participate in policies with new-… ids
  via additionalResources, join groups (deploy fans membership out
  through the resource's own groups field)
- Deploy: groups → networks → resources → routers → policies with
  networkClientToId / resourceClientToId resolution and live-modal
  router defaults; Review & Deploy shows non-blocking warnings for
  router-less networks and policy-less resources

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 12:36:34 +02:00
Eduard GertandClaude Fable 5 9a7b313a19 Add Vitest unit tests for draft mode logic
npm run test:unit — Vitest + jsdom, no infra (e2e stays on Playwright).

- draft-connect.test.ts: full connect matrix (modal prefills incl.
  placeholders and XOR-side rules; direct policy-side edits with
  handle mapping, duplicate/occupied no-ops)
- node-capabilities.test.ts: per-node-kind capability spec (rename /
  install / peer-select / join-a-group for every node type)
- DraftChangesetContext.test.tsx: coalescing rules via renderHook
- helpers.test.ts: isDeployablePolicy, policy regrouping, IP/hostname
  placeholders
- draft-storage.test.ts: persistence round-trips
- Legacy hand-rolled ip/version test scripts excluded from vitest

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 12:07:36 +02:00
Eduard GertandClaude Fable 5 db261b184a User Device select node, upgrade follow-through, and draft consistency fixes
User Device placeholder:
- Drops a canvas node like Server/Agent, rendered as a live-style
  peer-select dropdown ("Select user device..."); choosing a peer
  upgrades the node in place and keeps the dropdown so the selection
  can be switched — policies and edges follow the new peer
- Install buttons float top-left above all placeholder nodes (inside
  the node so they zoom with the canvas), secondary styling
- Placeholder ids renamed new-<uuid> → draft-<uuid>

Group membership follow-through:
- Grouping/dragging placeholders records them as members with their
  draft ids; on install/select the id is renamed across the changeset
  (replacePeerIdInGroups) and group nodes' addedMembers; deploy
  filters never-installed draft ids
- Group counts/addedMembers update on every canvas instance of a
  group (source node + destination copies)
- Policies referencing a grouped or removed peer/resource are rewired
  (group takes the side) or cleared (side emptied) — an incomplete
  policy drops its pending create/update so broken state never deploys

Other:
- Peer view → draft: the picked peer is skipped when a canvas group
  already contains it (was overlapping the group node)
- Auto-arrange vertical spacing increased (160/80/160)
- Connect rules extracted to utils/draft-connect.ts and capability
  predicates to utils/node-capabilities.ts (pure, unit-testable),
  wired into NodeContextMenu + useDragToGroup

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 12:07:36 +02:00
Eduard GertandClaude Fable 5 ca6ac21d52 Add spec for draft-mode networks, resources & routing peers
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 12:01:15 +02:00
Eduard GertandClaude Fable 5 f27f7b189b Draft peers, blank policies, and placeholder install flow
Components panel & context menus:
- Panel not-found state matches the global search modal styling
- Context menus (canvas + node) flip away from viewport edges
- User Device template delays panel close so its modal opens first
- Template icon boxes match existing entities (solid bg)

Placeholder peers (Server / Agent):
- Node mirrors the real peer card (layout, text sizes, truncation);
  IP slot shows a dimmed placeholder from the account network range
- Unique per-drop names ("Agent", "Agent (1)", ...), renamable via
  context menu with uniqueness check across draft peers
- Selectable in policy modals (additionalPeers in PeerGroupSelector)
  and connectable like real peers via their draft-<uuid> ids
- Install modal suggests a unique hostname (netbird up --hostname),
  stamped on the node; useDraftPeerUpgrade watches the peers list and
  swaps installed placeholders for real peers in place, re-recording
  their policies into the changeset

Policies in draft:
- Policy template drops a blank policy node (no modal) named
  "Policy" / "Policy (1)"; blank/undeployable policies stay out of
  the changeset until they have both sides and no placeholder refs
- Peers can be dragged onto policy sides (single-peer XOR groups)
- Connect prefills the create modal (peers as peer resource, groups
  as lists) and resets stale prefill state on every connect
- Peer selector dropdown opens on the tab of the current selection

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-15 17:34:01 +02:00
Eduard Gert dee5c6e244 Use SmartEdge for policy edges in live views
Migrate peer/group/user/network view policy edges from the DirectionIn
edge type to SmartEdge, enabling bidirectional rendering and dynamic
side selection in live mode. DirectionIn and BidirectionalEdges remain
registered but unused.
2026-07-15 11:58:00 +02:00
Eduard Gert 6db980fcff Add draft persistence, undo/redo, and review & deploy flow
- Persist draft changes and canvas to localStorage, restored on mount
- Snapshot-based undo/redo for canvas and changeset (DraftHistoryContext)
- Review & deploy modal with grouped changes and deploy changeset hook
- Discard draft flow and leave guard for unsaved drafts
- Group rename modal and draft node creation hooks
- Move components panel into draft/ and adjust related UI
2026-07-15 11:45:32 +02:00
Eduard GertandClaude Fable 5 6a299aa594 Hide network selector in header when no networks exist
In live mode on the networks tab, the header network selector now only
renders when there is at least one network, matching the empty state
shown by ControlCenterEmptyStates.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-15 11:29:27 +02:00
Eduard Gert 050d28d7af adjust empty draft state 2026-07-14 11:59:34 +02:00
Eduard Gert 6abf5d21c1 adjust empty draft state 2026-07-14 11:59:30 +02:00
Eduard Gert 368cfac0fe adjust sidebar 2026-07-13 17:03:09 +02:00
Eduard Gert 5c220e5176 adjust sidebar 2026-07-13 13:45:03 +02:00
Eduard Gert 9f5e0edd45 adjust save and apply 2026-07-09 09:45:01 +02:00
Eduard Gert 9a99c5930f adjust draft mode ui 2026-07-08 17:26:24 +02:00
Eduard Gert 1c10d195e7 add blue halo when selecting node 2026-07-07 12:19:18 +02:00
Eduard GertandClaude Opus 4.7 a57da47f45 Merge control-center-edit: draft mode, canvas toolbar, connect handles
Overlays feature/control-center-edit's control-center paths onto main:
new draft/ mode with canvas toolbar, keyboard shortcuts, drag-to-group,
create-group modal, connect-handles for drawing policies, SmartEdge
routing, and refactored ControlCenterContext into CanvasState + UI +
Policy providers. Adds hooks/views split (peer/group/user/network).

AccessControlModal + useAccessControl gain initialSourceGroups /
initialSourceResource so draft-mode policy creation can seed the source
side (previously only destination could be pre-filled).

Unrelated cloud-repo changes (data-testid -> data-cy renames, DNS,
reverse-proxy, integrations, etc.) are excluded.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-07-06 17:10:19 +02:00
Misha Bragin 8d41de995c Fix Skip TLS doc link and model pricing format (#699) 2026-07-03 07:03:42 +02:00
Misha Bragin 30dd75d95d Feature/add skip tls verification (#697) 2026-07-01 22:25:05 +02:00
Zoltan Papp bfa064a19d Add Metrics settings tab with metrics push toggle (#613)
* Add Metrics settings tab with metrics push toggle

Add a dedicated Metrics tab to Settings with a toggle for enabling
anonymous performance metrics collection. Moves metrics out of the
Clients tab into its own tab with description and docs link.

* Update docs link
2026-07-01 19:00:00 +02:00
Misha Bragin db73d18370 Feature/add provider session (#695) 2026-07-01 16:32:44 +02:00
Misha Bragin ba32178425 Fix peer table page reset when switching kind (#696) 2026-07-01 16:09:15 +02:00
4ea4fd4a4c Reload nginx after patching CSP header (#687)
* fix(docker): reload nginx after patching CSP header

nginx is started by supervisord (priority 100) before init_react_envs.sh
(priority 201) and is never reloaded afterwards, so it keeps serving the
static default.conf CSP that has no connect-src. Over plain HTTP this blocks
OIDC discovery and the management API, breaking browser SSH/RDP auth.

Reload nginx at the end of the init script so the dynamically built CSP
header takes effect.

* Update docker/init_react_envs.sh

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* fix(docker): log nginx -t output on failed CSP reload

When the reload never succeeds, run nginx -t before exiting so the logs show
whether the patched config is invalid or nginx never came up.

* conditionally emit `upgrade-insecure-requests` in CSP

---------

Co-authored-by: Viktor Liu <17948409+lixmal@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: mlsmaycon <mlsmaycon@gmail.com>
2026-07-01 15:32:22 +02:00
Bethuel Mmbaga deaae9159c Show reason for peer login expiration events (#694) 2026-07-01 12:32:11 +03:00
Bethuel Mmbaga d7fd24244d Prevent gated useFetchApi hooks from overwriting shared cache entries (#692) 2026-06-30 17:44:38 +03:00
Misha Bragin 9230b3e4e3 Add Claude provider selector and Bedrock (#691) 2026-06-30 11:50:15 +02:00
Misha Bragin 19ad8d6806 Prefill Vertex endpoint (#689) 2026-06-29 10:53:07 +02:00
Misha Bragin 38fc2970e6 Support Vertex keyfile upload and agent config (#688) 2026-06-28 22:34:54 +02:00
Misha Bragin 4ec4d29e84 The empty-state peer card now renders AddPeerDropdown 2026-06-28 12:12:11 +02:00
Misha Bragin 944d1b5bf6 Agent network (#684) 2026-06-27 19:11:41 +02:00
Max Bruch c325c170ab Add origin URL of AUTH_AUTHORITY to CSP_DOMAINS #679 (#680) 2026-06-26 13:11:52 +02:00
Bethuel Mmbaga 76529fc089 Preserve inactivity expiration on partial peer updates (#676) 2026-06-23 17:30:06 +03:00
Maycon Santos 8c22e64d02 Restrict cloud/licensed-only API calls in open-source mode (#675)
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
2026-06-23 15:59:15 +02:00
Maycon Santos 7653e3411c Merge NetBird cloud edition into the dashboard (#674)
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.
2026-06-21 16:01:08 +02:00
Maycon Santos 86b6e23981 Update announcements.json (#673) 2026-06-20 16:43:04 +02:00
Eduard Gert 389a0c0785 update banner (#672) 2026-06-20 15:59:23 +02:00
Misha Bragin bf6601782d DNS Zones & Setup modal improvements (#669) 2026-06-15 16:02:33 +02:00
Max 5eac5162e0 Insert link to remote jobs documentation (#664)
Replaced general link to docs homepage with exact Documentation link for Remote Jobs
2026-06-12 09:39:36 +02:00
Brandon Hopkins 1d8d32dd12 Edit banner to IPv6 kh link (#662) 2026-06-05 12:54:59 -07:00
Brandon Hopkins 578d890bb5 Add Logout URL to the Identity Provider dialog (#657)
* Add Logout URL section

* Condensed the URL section, added logout note
2026-06-05 08:28:32 -07:00
Maycon Santos e8f0f20455 Update reverse proxy modals (#661)
* add multi-environment deployment options for reverse proxy setup

* refactor reverse proxy modal to handle state updates more robustly
2026-06-04 20:43:45 +02:00
Misha Bragin 8c94090e3d Group Networks and Routes under Network Routing (#660) 2026-06-04 19:44:13 +02:00
Misha Bragin 1917df6f60 Improve Table Filters Layout (#654) 2026-06-04 18:24:53 +02:00
Misha Bragin 358b477ded Move setup keys to Settings (#653) 2026-05-27 17:17:04 +02:00
Maycon Santosandcoderabbitai[bot] f535fe2667 Feature/private service expose (#646)
* 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>
2026-05-26 21:37:11 +02:00
Misha Bragin 52f7020a0f Fix broken SSH and RDP (#650) 2026-05-26 20:14:36 +02:00
Misha Bragin a604643f9a Feature/simplified layout (#649)
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.
2026-05-25 13:09:30 +02:00
Maycon SantosandEduard Gert 42cd088c5d rebuild self-hosted page as Clusters with type features (#641)
* 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>
2026-05-20 11:48:38 +02:00
Maycon Santos 7400ac806e remove self-hosted proxies menu item (#640) 2026-05-14 17:50:07 +02:00
Viktor Liu 240ff5af9a Fix IPv6 input across reverse proxy, routes and resources (#638) 2026-05-14 16:43:02 +02:00
Eduard Gert dc86c30463 Add self-hosted proxies (#636)
* Add self-hosted proxies

* fix selfhosted badge for domain
2026-05-12 15:22:12 +02:00
Nicolas Frati e58f75ae3c Enable MFA for local users toggle (#615)
* implement enable mfa for local users toggle

* fix visibility check

* Added beta badge to MFA auth toggle
2026-05-08 16:51:17 +02:00
Viktor Liu dc1adebd27 Add IPv6 overlay settings and peer display (#594) 2026-05-07 15:20:12 +02:00
Bethuel Mmbaga d76cbd1122 Add Microsoft AD FS support for embedded Dex identity providers (#625) 2026-04-28 12:42:48 +03:00
Eduard Gert 01330e0f58 Fix missing peer context in group network routes tab (#620) 2026-04-23 17:05:05 +02:00
Viktor Liu e9ac1a1a23 Add CrowdSec IP reputation (#600) 2026-04-21 12:29:37 +02:00
raghvendra b53802a5c5 fix: prevent storage clear and logout on failed account deletion (#611) 2026-04-13 09:07:14 +02:00
Eduard Gert 9addc18956 Fix reverse proxy mode selection (#606)
* Fix reverse proxy mode selection

* Fix isNetBirdHosted

* Fix activity description
2026-04-09 09:52:35 +02:00
shuuri-labs 9701e6503b Add new pull request template + enforce documentation acknowledgement… (#602)
* Add new pull request template + enforce documentation acknowledgement in new workflow

* fix docs-ack workflow: pass PR number via env and simplify checkbox validation
2026-04-02 21:39:38 +02:00
Eduard Gert 0841caecbb Fix dns zone domain validation and peers last seen sort (#595) 2026-03-25 17:50:22 +01:00
Eduard Gert c7846760d1 Add reverse proxy auth headers (#593)
* Add reverse proxy access rules

* Fix coderabbit comments

* Fix coderabbit comments

* Fix coderabbit comments

* Add auth header modal

* Remove password managers from auth headers

* fix unique id

* Remove gradient, fix button roundness

* update lucide, add additional event auth methods

* Clear existing header value on change
2026-03-25 14:31:36 +01:00
Viktor Liu 8c283b6ef9 Support optional subdomain for reverse proxy domains (#589) 2026-03-24 16:01:01 +01:00
Eduard Gert 34ae3b4da6 Add reverse proxy access rules (#592)
* Add reverse proxy access rules

* Fix coderabbit comments

* Fix coderabbit comments

* Fix coderabbit comments
2026-03-24 16:00:31 +01:00
Viktor LiuandEduard Gert aff2365ef7 Add layer 4 protocol support to reverse proxy (#579)
* Add layer 4 proto support

* Fix initialResource fallback and UDP session_idle_timeout

* Fix tlsResourceId init for resource-driven create flows, UDP timeout label

* Address PR review: ServiceMode enum, resource init fix, modal title, a11y

* Add L4 protocol values to ReverseProxyTargetProtocol, remove unsafe double cast

* Add aria-labels to L4 port/host inputs

* Unify domain input for all service modes including L4

* Support L4 proxy events

* Fix custom port reset on edit and show port in L4 service link

* Remove redundant listen port from L4 target cell

* Show link only for HTTP/TLS services, copy-on-click for TCP/UDP

* Move mode badge before domain and use fixed width for alignment

* Fix HTTP services to open as link instead of copy

* Hide old proxy clusters from L4 domain selector

* Move service type inside modal

* Update auth cell

* Add target selector component

* Extract into separate components

* hide services types for not supported clusters

* Remove advanced settings tab in http targetmodal and use accordion instead

* Update advanced settings

* Update target device row

* Update text

* Add type cell

* Fix flat target name cell

* Update modal title

* Fix edit target in flat table

* Remove unused proxycluster interface

* Move proxy type icon into type component

* sync cloud

* use emptyrow

* fix l4 type

* fix duplicate error notification

* Set the correct target type

* Fix subnet host editable

* Fix subnet host editable

* hide selector when initial resource or peer

* Rename dropdown

* Update text

* update status cell

* merge cloud

* Update tooltips

* Address coderabbit comments

* Fix skeleton device card

* Update listen port tooltip

* Adjust padding

* update package-lock.json

* bump next to 16.1.7

---------

Co-authored-by: Eduard Gert <kontakt@eduardgert.de>
2026-03-18 17:43:00 +01:00
Zoltan PappandEduard Gert bad057d415 [dashboard] feat: add auto_update_always toggle to client settings (#580)
* [dashboard] feat: add auto_update_always toggle to client settings

Add "Always Update" toggle to the Clients settings tab that controls
whether updates are installed automatically in the background or require
user interaction from the UI. Includes a warning icon and caution callout
when enabled to highlight the risk of disrupting active connections.

* [dashboard] fix: improve auto-update UI clarity and toggle label

Clarify that automatic updates require user interaction by updating the
description. Rename "Always Update" to "Force Automatic Updates" for
clarity. Move warning callout below the toggle switch instead of inside it.

* Update src/modules/settings/ClientSettingsTab.tsx

Co-authored-by: Eduard Gert <kontakt@eduardgert.de>

---------

Co-authored-by: Eduard Gert <kontakt@eduardgert.de>
2026-03-16 15:35:44 +01:00
Misha Bragin 4d846e2c94 Improve text for optional resource setiings (#584) 2026-03-12 20:48:09 +01:00
Eduard Gert 15fb6e0b05 Refactor resource modal (#582) 2026-03-12 16:30:51 +01:00
Eduard Gert 55c5525626 Fix resource group policy when adding single resource as destination (#581) 2026-03-11 19:23:59 +01:00
Eduard Gert c0c1f4688e Add proxy events sort (#560)
* Add proxy events sort

* Fix coderabbit comment

* Disable local sort when server pagination is used
2026-03-10 10:10:53 +01:00
Eduard Gert b5a8f751ba Create policies inside resources (#568)
* Add acl tooltips

* Adjust resource modal and add tooltips

* Prevent nextjs navigation trigger on tab change

* Update wording

* add acl into resource

* Refactor resource policies

* Add prop to hide group edit and disable redirect

* Add skeleton loader to network page

* Create policy for new resources

* Show existing policies if groups are matching

* Add confirm dialog after creating resource without policy

* Add dialog if user edits policy that is used in multiple resources

* Add callout when selecting resource groups containing policies

* Add dialog if deleting policies containing resources

* Fix stale policies and new group creation in resource modal

* Remove whitespace

* Fix sort

* Cleanup

* Address coderabbit comments

* Fix policy alignment

* Fix initial resource

* disable selector if user did not select  resource groups

* Consider current resource when editing / deleting policy

* Remove unused mutate

* Fix dot position

* Remove ask for policy

* Fix policy index

* Fix multiple resource confirm dialog on policy cell
2026-03-10 10:10:38 +01:00
Eduard Gert 10a8e7b745 Fix stale certificate issued state (#575)
* Fix stale certificate issued state

* fix coderabbit
2026-03-09 10:08:35 +01:00
Viktor Liu 60e8394010 Add per-target options to reverse proxy (#576) 2026-03-06 18:55:28 +01:00
Eduard Gert 9420214059 Bump minimatch and ajv dependencies (#572) 2026-03-02 11:32:52 +01:00
b949f60afe Feature/client service expose (#567)
* add draft

* add reverse proxy activities

* move peer expose settings into client settings tab and fix activity descriptions

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* prevent false positive group report

* add docs link

* allow save when groups are added to the setting

* Add loading skeleton to client settings, update icon, use grouphelper to allow creating new groups, remove .patch

* mv expose settings from extra settings

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Eduard Gert <kontakt@eduardgert.de>
2026-02-24 14:54:58 +01:00
Eduard Gert d498e4cc25 Fix dns records pagination (#566) 2026-02-20 21:42:26 +01:00
Eduard Gert 130dc0c32c Fix group unused filter (#565) 2026-02-19 10:32:14 +01:00
Eduard Gert f5824d6ddb Allow empty groups for reverse proxy sso auth (#563) 2026-02-18 16:27:13 +01:00
Eduard Gert 829395f908 Add hover to reverse proxy auth methods (#564) 2026-02-18 13:39:19 +01:00
Eduard Gert 8eebec78b4 Preserve query params for ssh and rdp (#559) 2026-02-16 17:34:08 +01:00
raghvendra 3e01a6dafd refactor: simplify FullScreenLoading to use boolean prop instead of string union (#555) 2026-02-16 11:10:26 +01:00
Maycon Santos 1555b94043 Fix service cluster status (#556) 2026-02-16 09:23:22 +01:00
Eduard Gert 6c62127d42 Update announcement (#553) 2026-02-13 20:56:40 +01:00
Eduard Gert b71d0fde89 Add reverse proxy (#552)
* **New Features**
  * Full Reverse Proxy UI: Services, Targets, Clusters, Custom Domains (with verification) and a Proxy Events page.
  * In-app modals for service auth (SSO, password, PIN) and a new PIN input component.

* **Improvements**
  * Network & Peer pages: tabbed views (Resources, Routing Peers, Services) and improved tables, search and filters.
  * Toast stacking/visibility and global toast styling refined.
2026-02-13 18:59:16 +01:00
Misha Bragin 84c239ce30 Indicate that local user auth is disabled (#551) 2026-02-12 15:16:34 +01:00
Aaron Dewes ba66201c64 Remove architecture info tooltip for MacOS (#550)
* Remove architecture info tooltip for MacOS

Previously, this tooltip helped users determine which binary to download. Since #501, there is only one universal binary download link, so keeping the tooltip explaining how to determine the CPU architecture is unnecessary.

* fix: Remove unused imports
2026-02-12 11:21:08 +01:00
raghvendra c6341e000f docs: fix broken Auth0 quickstart link in README (#548)
* docs: fix broken Auth0 quickstart link

* docs: spell error fixes in readme

* docs: fix typo in NETBIRD_MGMT_API_ENDPOINT placeholder in readme
2026-02-09 11:41:09 +08:00
Eduard Gert 750f660bcc Update NextJS to 16.1.6 (#547)
* Update NextJS to 16.1.6

* Update Node in workflow

* Fix rabbit comments

* Fix types

* Add engines field
2026-02-02 15:34:23 +01:00
Misha Bragin ea148545e8 Disable local users when LocalAuthDisabled = true (#546) 2026-02-01 14:31:57 +01:00
Misha Bragin d2febbf27b Fix version comparison (#544) 2026-01-27 14:13:27 +01:00
Misha Bragin 615b4487ad Point to the right upgrade doc (#543) 2026-01-27 12:23:56 +01:00
Misha Bragin a7c7800916 Add invite notification count badge (#542) 2026-01-27 10:44:39 +01:00
Eduard Gert 3d51e0893e Update announcement (#538)
* Update announcement

* Fix repeated fetches
2026-01-27 09:33:43 +01:00
Misha Bragin d7d44b5817 Adjust Invites API (#541)
* Add API adjustments

* Invite_link renamed to invite_token
2026-01-26 19:25:56 +01:00
Misha Bragin f67f39b68b Local user invites (#539) 2026-01-25 21:40:49 +01:00
dependabot[bot] d2bc7a1f57 Bump lodash from 4.17.21 to 4.17.23 (#537)
Bumps [lodash](https://github.com/lodash/lodash) from 4.17.21 to 4.17.23.
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.21...4.17.23)

---
updated-dependencies:
- dependency-name: lodash
  dependency-version: 4.17.23
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-23 13:28:28 +01:00
Eduard Gert 818ba5daa4 Allow wildcard dns zone records (#536) 2026-01-20 17:32:14 +01:00
Ali AmerandEduard Gert 3a30f76629 Add Frontend Support for Peer Debug Bundle Trigger and History (#485)
* implement debug ui

* update job ui

* Add type cell, show tooltip if peer is offline, add copy to clipboard for upload key, show error reason in tooltip

* update job event description

---------

Co-authored-by: Eduard Gert <kontakt@eduardgert.de>
2026-01-20 17:12:33 +01:00
Misha Bragin 34dc21c89d Add password change (embedded Idp) (#535) 2026-01-20 15:00:14 +01:00
Eduard Gert 2e37703622 Update CONTRIBUTOR_LICENSE_AGREEMENT.md (#534) 2026-01-19 14:55:04 +01:00
Eduard Gert 8aec338c43 Fix dns doc link (#533) 2026-01-19 10:01:55 +01:00
Viktor Liu f4f0c240fd Bump wasm to v0.63.0 (#531) 2026-01-19 09:49:26 +01:00
Viktor Liu 04e22a3c7e Enable SSH for Windows and Android peers (#532)
* Enable SSH for Windows and Android peers, hide update badge for temporary peers

* Fix RDP to use tcp protocol instead of netbird-ssh
2026-01-19 09:49:08 +01:00
Eduard Gert 54ef076303 Fix config vars (#529) 2026-01-16 19:59:42 +01:00
Eduard Gert 92676b6c38 Add DNS zones (#528) 2026-01-16 17:33:16 +01:00
Eduard Gert 3affa8908f Redirect /setup to /peers if no setup is required (#526)
* Redirect /setup to /peers if not setup is required

* Fix bad state while redirect

* Prevent redirect to /setup if already on /setup

* Fix loading state
2026-01-08 15:01:45 +01:00
Eduard Gert 52fd984912 Add user view to control center (#525) 2026-01-07 17:53:55 +01:00
Misha BraginandEduard Gert 83e3159ee4 Configure Identity Providers in the UI (#523)
* Add user creation with password copy

* Add initial identity provider view

* Add IdP logos

* Add IdP id to user

* Add IdP logo to user obj

* Fix okta icon

* Return callback URL when creating an IdP

* Create user for self-hosted

* Clear up password from the state

* Show IdPs and create user when enabled

* Fetch IdPs only when embedded idp is enabled

* Update src/app/(dashboard)/settings/page.tsx

Co-authored-by: Eduard Gert <kontakt@eduardgert.de>

* Update src/app/(dashboard)/settings/page.tsx

Co-authored-by: Eduard Gert <kontakt@eduardgert.de>

* Update src/modules/settings/IdentityProvidersTab.tsx

Co-authored-by: Eduard Gert <kontakt@eduardgert.de>

* Update src/modules/settings/IdentityProviderModal.tsx

Co-authored-by: Eduard Gert <kontakt@eduardgert.de>

* Update src/modules/settings/IdentityProvidersTab.tsx

Co-authored-by: Eduard Gert <kontakt@eduardgert.de>

* Update src/modules/settings/IdentityProviderModal.tsx

Co-authored-by: Eduard Gert <kontakt@eduardgert.de>

* Rename IdentityProvider to SSOIdentityProvider

* Fix build and extract icons

* Fix initial onboarding

* Add icons

* Move name to the top

* Fix setup wizard background color

* Update instance setup ui

* Update instance setup ui

* Use input component

* Move idp label and icons

* Fix setup wizard width

* Add authentik and keycloak

* Add idp hints

* Handle idp permissions

* Consider selfhosted instances when checking if netbird is hosted

* Update redirect

* Add max retries to redirect

* Require new secret when clientid changed

* Add callback URL on the idp creation step

* Add idp activity events

---------

Co-authored-by: Eduard Gert <kontakt@eduardgert.de>
2026-01-07 14:43:30 +01:00
Eduard Gert bf81aeb02d Add fine-grained ssh policy (#522)
* Add fine-grained ssh policy

* Update version text

* Fix coderabbit comment
2025-12-30 09:27:17 +01:00
Eduard Gert b058e66e32 Add auto update setting (#519) 2025-12-29 12:38:50 +01:00
Eduard Gert 8d6b617cbd Update NextJS to 14.2.35 (#518) 2025-12-22 11:02:29 +01:00
Eduard Gert 47db655e9f Update eslint and tailwind (#515) 2025-11-27 17:38:18 +01:00
dependabot[bot] 0661cbf9f4 Bump js-yaml from 4.1.0 to 4.1.1 (#509)
Bumps [js-yaml](https://github.com/nodeca/js-yaml) from 4.1.0 to 4.1.1.
- [Changelog](https://github.com/nodeca/js-yaml/blob/master/CHANGELOG.md)
- [Commits](https://github.com/nodeca/js-yaml/compare/4.1.0...4.1.1)

---
updated-dependencies:
- dependency-name: js-yaml
  dependency-version: 4.1.1
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-27 15:25:50 +01:00
Eduard Gert 240a96fa8b Add onboarding for new accounts (#514) 2025-11-27 14:49:58 +01:00
Eduard Gert 43bc069a49 Increase ssh detection timeout (#512) 2025-11-21 10:32:50 +01:00
Eduard Gert 936de0f4f3 Add ssh policy info for peers (#511) 2025-11-20 14:29:14 +01:00
Eduard Gert d81b75a946 Bump browser ssh versions for ssh rewrite (#510)
* Bump browser ssh versions for ssh rewrite

* Remove cypress temporary
2025-11-18 17:07:58 +01:00
Eduard Gert a632eeeef0 Remove dns0eu (#508) 2025-11-10 14:21:58 +01:00
Eduard Gert e2219aeea0 Add group update activity event (#504) 2025-11-10 10:50:04 +01:00
Eduard Gert 63f4c69eb4 Fix native ssh detection (#505) 2025-11-07 09:33:58 +01:00
Eduard Gert b1af256296 Add wasm client version (#503) 2025-11-06 10:59:41 +01:00
Eduard Gertandaliamerj 4027894a2e Feature/groups page (#498)
* move our group membership from the settings menu, into the Team menu

* add action to the table and new group page

* update group page and return group settings to settings menu

* new update

* fix bug

* group action: add peer to group

* group action: add user to group

* Update wording, redirect to group page after creation

* Add better table loading skeleton

* Adjust group name cell

* Update wording

* Update sort order

* Refactor

* Merge main

* Fix button height

* Fix resources table

* Adjust table loading skeleton

* Adjust table loading skeleton

* Add loading to tab triggers

* Update meta

* Update group location

* Fix rename

* Refactor group details

* Fix linked peers

* Fix group usage

* Fix incrementing peer count

* Prevent renaming to already existing group

* Fix group name click

* Update group nav

* Make group table cells clickable

* Fix breadcrumbs

* Update wording

* Add confirmation before removing users from group

* Add permissions

* Add initial group for network routes

* Add acl and routing peer groups

---------

Co-authored-by: aliamerj <aliamer19ali@gmail.com>
2025-11-05 12:08:49 +01:00
Yanis64andEduard Gert af90792595 Add multi-group support for JWT allow groups with tag system (#500)
* fix: add multi-group support for JWT allow groups with tag system

* Update src/modules/settings/GroupsTab.tsx to use the Badge component

Co-authored-by: Eduard Gert <kontakt@eduardgert.de>

* chore(GroupsTab): import Badge components

---------

Co-authored-by: Eduard Gert <kontakt@eduardgert.de>
2025-11-03 16:09:15 +01:00
Eduard Gert 9a401733b3 Fix toggle for p2p policies (#501) 2025-10-31 13:21:23 +01:00
Eduard Gert 07b6895380 Sync SSH & RDP changes (#495) 2025-10-16 14:44:26 +02:00
Eduard Gert 9e2e38764e Add control center (#494)
* Add control center

* Update rdp doc link
2025-10-09 11:26:21 +02:00
Maycon Santos d9fb379abf Enable connect buttons (#493) 2025-10-06 16:23:00 -03:00
Eduard Gert 831673d0d6 Sync with cloud (#491)
implements a "Sync with cloud" functionality that includes various UI improvements, code refactoring, and component extractions. The changes focus on enhancing the user interface, improving code organization, and adding new features for remote access and activity tracking.

- Refactors inline components into reusable shared components
- Adds new activity tracking for group operations
- Updates remote access configuration and UI components
- Enhances styling and layout for better user experience
2025-10-03 14:37:11 +02:00
Maycon Santos bc4aac10aa Add browser client support (#490)
* Sync wasm rdp and ssh client

* sync package-lock

* remove msp ref

* add ephemeral info
2025-10-02 00:41:08 +02:00
Eduard Gert 38e14a6c64 Allow delete groups issued by jwt (#487) 2025-09-10 14:41:19 +02:00
Maycon Santos b79c6615b4 Add user approval feature (#486)
implements a user approval feature that allows administrators to manually approve new users before they can access the system. The feature adds approval workflow controls and error handling for blocked/pending users.

Adds user approval toggle in authentication settings
Implements approve/reject actions for pending users in the users table
Creates error page for blocked/pending approval scenarios
2025-09-02 15:25:30 +02:00
hakansa 5d4e491611 Add skip_auto_apply feature to exit nodes and update related components (#484) 2025-08-20 13:11:45 +02:00
Eduard Gert 9b1f920863 Update dependencies (#483) 2025-08-19 17:32:22 +02:00
Eduard Gert 7c7f0a0f10 Add network range (#482) 2025-08-13 14:18:59 +02:00
braginini 76541c701c Update LICENSE 2025-08-05 11:19:13 +02:00
Eduard Gert d2046fee21 Add resource search, unidirectional policies for all/icmp and dns0 template (#479) 2025-07-30 17:01:49 +02:00
Eduard Gert 8e2cbe1d2a Add support for port ranges (#475) 2025-06-20 10:26:53 +02:00
Eduard Gert 8a08583225 Do not redirect on same page (#471) 2025-06-05 19:30:25 +02:00
Eduard GertandViktor Liu 1defac4e34 Update wording for dns domain, macOS and Windows install steps (#470)
* Update wording for dns domain, macOS and Windows install steps

* Update src/modules/settings/NetworkSettingsTab.tsx

Co-authored-by: Viktor Liu <17948409+lixmal@users.noreply.github.com>

---------

Co-authored-by: Viktor Liu <17948409+lixmal@users.noreply.github.com>
2025-06-05 13:16:42 +02:00
Eduard Gert fa68f98cd0 Remove permission for add peer button (#469) 2025-06-05 13:12:38 +02:00
Eduard Gert 3f6e4c4e4f Add lazy connection setting (#465) 2025-06-04 11:54:18 +02:00
Eduard GertandMaycon Santos 0e2661caea Merge cloud changes to public (#462)
* Add changes from dashboard cloud

* Add changes from dashboard cloud

* Update next.js version

* Small formatting changes

* remove unknown permission check

---------

Co-authored-by: Maycon Santos <mlsmaycon@gmail.com>
2025-05-05 15:30:28 +02:00
Eduard Gert d7c5f7e183 Hide update available for mobile devices (#106) (#460)
(cherry picked from commit 7f248ae060385acb1245591bd46e2bb6d53ed908)
2025-04-28 11:58:26 +02:00
Eduard Gert ebbe865ce0 Add custom dns domain (#458)
* Update domain validator

* Add custom dns domain
2025-04-28 11:58:16 +02:00
Eduard Gert 6c0ab88488 Update domain validator (#459) 2025-04-28 11:53:44 +02:00
Eduard Gert a50576d851 Fix nameserver port input for Safari (#456) 2025-04-28 11:21:25 +02:00
Eduard Gert 676250266c Fix browse posture checks table filters (#448) 2025-04-07 10:23:07 +02:00
Vladislav Tropnikov 042c65a652 Add display of ID if user does not have email (#450)
* Add display of ID if user does not have email

* Update PeerNameCell.tsx

* Add more possible id parameters

* Hide user if there is nothing

* change id order

* Keep default behavior
2025-03-27 17:30:26 +01:00
Misha Bragin 96f2d39e54 Add CLA 2025-03-18 15:58:05 +01:00
Eduard Gert 61e11d3740 Apply recent cloud changes (#447)
* Add resource description, add single resource for acl, add icons for group badges, add inactivity expiration

* Add extra dns labels, remove routing restriction
2025-02-21 15:53:40 +01:00
Edouard VanbelleandEduard Gert c8e3b50f1b Display serial number on peer information and on peers table (#444)
* display serial number on peer information and on peers table

  * add serial on peers list (included in OS information to minimize informations)
  * permit a lookup via serial number
  * add serial on peer information

* Update os icon to match existing one and hide serial if it does not exist

---------

Co-authored-by: Eduard Gert <kontakt@eduardgert.de>
2025-01-30 13:14:38 +01:00
Eduard GertandMisha Bragin 25be69e7bb Add improvements to new networks features (#439)
* Fix wrong ui state for routing peer modal in networks

* Add confirmation dialog when blocking users

* Keep peer sort order when switching pages

* Update sidebar navigation order and remove deprecation notice

* Fix issue when hovering over truncated text in a group badge closes the multiple groups popover

* Update group text in network resource modal

* Update networks page text

* Fix line height

* Add search to resource table

* Switch networks flow to create first resources and then add routers

* Add enabled toggle to routing peers

* Add enabled toggle to network resources

* Add resource group modal and adjust tables

* Clarify networks

* Fix not properly aligned horizontal scroll bar

* Add option to install netbird after creating a setup key

* Fix text for install netbird modal

* Show resources count in group settings

* Fix "no results" and "no routing peers" text showing at the same time

* Fix wording

* Fix resource policy count

* Hide resource count when selection source groups

* Extend networks routing peer modal with option to create a setup key and install netbird

* Add option for horizontal stepper

* Generate setup key when installing netbird from routing peer modal

* Add confirm dialog to let the user know a one-off setup-key will be created. This avoids accidental clicking and later confusion on the setup keys page

---------

Co-authored-by: Misha Bragin <bangvalo@gmail.com>
2025-01-20 16:18:21 +01:00
Eduard Gert 43e5d5cf53 Fix activity search and allow searching for meta fields (#440) 2025-01-15 16:41:55 +01:00
Eduard Gert 18819d6fdf Add confirmation dialog when blocking users (#437) 2025-01-15 16:29:05 +01:00
Eduard Gert 158804c1ac Fix wrong ui state for routing peer modal in networks (#436) 2025-01-15 16:28:50 +01:00
Misha Bragin 14d2d68819 Update links to networks doc (#435) 2024-12-27 21:48:36 +01:00
Pascal Fischer 40902b3629 add resources to groups update operation (#434) 2024-12-27 14:20:11 +01:00
Pascal Fischer fa9bcea4ab Update links for networks concept (#433) 2024-12-23 18:08:06 +01:00
Eduard Gert 3ba7acdecf Add new networks feature (#427) 2024-12-23 13:20:01 +03:00
Eduard Gert c7775ade8c Hide groups for regular users (#423) 2024-11-20 17:50:28 +01:00
Maycon Santos cd3e75b640 Add setup-key improvements (#420)
- Add support to key deletion
- Add custom and unlimited expiration
2024-11-01 16:04:43 +01:00
Jon "The Nice Guy" Spriggs f8281c8057 Typo (#418)
Protocol appears to include the : delimiter
2024-10-22 11:19:16 +02:00
Eduard Gert c1fcadaefe Fix resetting acl groups on switching active toggle (#417) 2024-10-07 17:31:42 +02:00
Jon "The Nice Guy" SpriggsandEduard Gert a0c4520f4b Add admin-url to the add-peer dialogue (#416)
* Add admin-url to the add-peer dialogue

* Missed "let" from defining the variable

* Update netbird.ts

Fix isNetBirdHosted check

---------

Co-authored-by: Eduard Gert <eduard@netbird.io>
2024-10-07 17:25:03 +02:00
Eduard Gert 76ef50a886 Add Access Control Groups & various UI / UX improvements (#415)
* Update codespell

* Add access control group, add various ui / ux improvements
2024-10-04 19:54:49 +02:00
Maycon Santos 58cec8fcd1 ignore mappin spelling (#408) 2024-08-13 15:59:38 +02:00
Eduard Gert d34ae9beb2 Sync changes with netbird cloud (#407)
* Update axa oidc library and package.json

* Update ACL port state to show correct value

* Filter user groups by unique groups only

* Add peer multiselect, optimize dropdown performance for peer selection, remove 'all' group from some dropdowns, various ui / ux optimizations

* Add peer multiselect, optimize dropdown performance for peer selection, remove 'all' group from some dropdowns, various ui / ux optimizations
2024-08-13 15:51:22 +02:00
Eduard Gert 650496f670 Include all settings in put request to prevent overwrite (#405) 2024-07-31 18:48:59 +02:00
Tom Hubrecht 121778c4a6 Fix package-lock.json (#401) 2024-07-12 10:35:31 +02:00
juliaroesschen d4102c5d04 fix typo in route update modal (#397) 2024-07-04 15:05:57 +02:00
pascal-fischer e78c35bdbe Fix DNS modal to allow one char domains (#393)
* update regex to allow one char domains in DNS routing modal

* update regex
2024-07-04 10:50:37 +02:00
juliaroesschen 6ebee98695 Fix typo in Network Routes dialogue (#395) 2024-07-04 10:48:49 +02:00
juliaroesschen f4b28d5f40 Fix typo in routes modal 2024-06-28 11:38:39 +02:00
Eduard Gert b4b6d9295b Add DNS routes (#390) 2024-06-17 09:32:55 +02:00
Maycon Santos 4898742ee9 Fix http://localhost:3000/ url validation case (#388)
* Fix http://localhost:3000/ url validation case

* adjust min regex occurrences
2024-06-12 18:18:14 +02:00
Eduard Gert 79164e9dd5 Add process posture check (#378)
* Add process posture check

* Add support for separate linux and mac paths
2024-06-12 16:32:10 +02:00
Eduard Gert 5caeab118b UX changes for modals and refactoring (#380) 2024-05-08 14:42:04 +02:00
Eduard Gert 3f943bb7d4 Use next/font/local instead of next/font/google (#376) 2024-04-19 17:12:56 +02:00
Eduard Gert 96b939e6cc Add changes from cloud repo to public one (#377)
* Remove unused files

* Update activity descriptions

* Update SelectDropdown

* Update redirect logic for / page

* Update HelpText.tsx

* Update wording for exit nodes
2024-04-19 17:12:37 +02:00
Eduard Gert 5e13548b81 Add better input validation for setup-keys, nameserver and routes (#373)
* Return the correct promise for errors

* Update icon

* Add better validation for routes

* Add better validation for DNS

* Add better validation for setup keys

* Merge exit nodes to input validation
2024-04-17 15:27:21 +02:00
Eduard Gert 2272a1d2a4 Add Exit Nodes (#374)
* Add exit node feature

* Fix spelling

* Hide masquerade for exit nodes

* Add exit node information to peers list

* Change exit node button, add indicator to peers table

* Add steps to route modal

* Add hook to check if peer has exit nodes

* Hide exit node indicator for regular users

* Add documentation links
2024-04-17 13:11:38 +02:00
Eduard Gert fc3da50346 Add fallbacks for setup key name & setup key group names (#370)
* Add try catch block for global search

* Add fallback for group name

* Add fallback for setup key name

* Do not load setup key modal if it's not open

* Check if auto_groups actually exists for the setup keys

* Add fallback for group names in setup keys table

* Add fallback for group names in peers table
2024-04-11 16:42:27 +02:00
1153 changed files with 161088 additions and 11755 deletions

No files matched your search

+19
View File
@@ -0,0 +1,19 @@
## Issue ticket number and link
## Documentation
Select exactly one:
- [ ] I added/updated documentation for this change
- [ ] Documentation is **not needed** for this change (explain why)
### Docs PR URL (required if "docs added" is checked)
Paste the PR link from https://github.com/netbirdio/docs here:
https://github.com/netbirdio/docs/pull/__
## E2E tests
Optional: override the image tags used by the Playwright e2e workflow.
Defaults to `main` when omitted.
management-cloud-tag: main
reverse-proxy-tag: main
+82 -17
View File
@@ -2,25 +2,31 @@ name: build and push
on:
push:
branches:
- "feature/**"
- main
tags:
- "**"
pull_request:
# Cancel in-progress runs on the same ref (PR or branch) when a new commit
# arrives, so we don't waste CI building superseded commits.
concurrency:
group: build-and-push-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
env:
IMAGE_NAME: netbirdio/dashboard
DOCKERHUB_IMAGE: netbirdio/dashboard
GHCR_IMAGE: ghcr.io/netbirdio/dashboard-cloud
jobs:
build_n_push:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v5
- name: setup-node
uses: actions/setup-node@v3
uses: actions/setup-node@v5
with:
node-version: '18'
node-version: '20'
cache: 'npm'
- name: Install dependencies
@@ -28,34 +34,93 @@ jobs:
- run: echo '{}' > .local-config.json
- name: Download IronRDP release TS files
uses: robinraju/release-downloader@v1.7
with:
token: ${{ secrets.GITHUB_TOKEN }}
repository: netbirdio/IronRDP
latest: true
fileName: "*.ts"
out-file-path: 'public/ironrdp-pkg'
- name: Download IronRDP release JS files
uses: robinraju/release-downloader@v1.7
with:
token: ${{ secrets.GITHUB_TOKEN }}
repository: netbirdio/IronRDP
latest: true
fileName: "*.js"
out-file-path: 'public/ironrdp-pkg'
- name: Download IronRDP release WASM file
uses: robinraju/release-downloader@v1.7
with:
token: ${{ secrets.GITHUB_TOKEN }}
repository: netbirdio/IronRDP
latest: true
fileName: "ironrdp_web_bg.wasm"
out-file-path: 'public/ironrdp-pkg'
- name: Get version from tag
id: version
run: |
if [[ "${{ github.ref }}" == refs/tags/* ]]; then
echo "version=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
else
echo "version=development" >> $GITHUB_OUTPUT
fi
- name: Build
run: npm run build
env:
NEXT_PUBLIC_DASHBOARD_VERSION: ${{ steps.version.outputs.version }}
-
name: Set up QEMU
uses: docker/setup-qemu-action@v2
uses: docker/setup-qemu-action@v3
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
uses: docker/setup-buildx-action@v3
-
name: Docker meta
id: meta
uses: docker/metadata-action@v4
uses: docker/metadata-action@v5
with:
images: ${{ env.IMAGE_NAME }}
-
name: Login to DockerHub
if: github.event_name != 'pull_request'
uses: docker/login-action@v2
images: |
${{ env.DOCKERHUB_IMAGE }}
${{ env.GHCR_IMAGE }}
flavor: |
latest=false
tags: |
type=schedule
type=ref,event=branch
type=ref,event=tag
type=raw,value=latest,enable=${{ startsWith(github.ref, 'refs/tags/') }}
type=ref,event=pr
type=sha
- name: Log in to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.NB_DOCKER_USER }}
password: ${{ secrets.NB_DOCKER_TOKEN }}
-
name: Docker build and push
uses: docker/build-push-action@v3
- name: Log in to the GitHub Container registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
file: docker/Dockerfile
push: ${{ github.event_name != 'pull_request' }}
push: true
platforms: linux/amd64,linux/arm64,linux/arm
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
- run: |
echo '### Pushed tags' >> $GITHUB_STEP_SUMMARY
echo '${{ steps.meta.outputs.tags }}' >> $GITHUB_STEP_SUMMARY
+2 -1
View File
@@ -12,4 +12,5 @@ jobs:
uses: codespell-project/actions-codespell@v2
with:
only_warn: 1
skip: package-lock.json,*.svg
skip: package-lock.json,*.svg
ignore_words_list: mappin, allTime
+105
View File
@@ -0,0 +1,105 @@
name: Docs Acknowledgement
on:
pull_request:
types: [opened, edited, synchronize]
permissions:
contents: read
pull-requests: read
jobs:
docs-ack:
name: Require docs PR URL or explicit "not needed"
runs-on: ubuntu-latest
steps:
- name: Read PR body
id: body
shell: bash
run: |
set -euo pipefail
BODY_B64=$(jq -r '.pull_request.body // "" | @base64' "$GITHUB_EVENT_PATH")
{
echo "body_b64=$BODY_B64"
} >> "$GITHUB_OUTPUT"
- name: Validate checkbox selection
id: validate
shell: bash
env:
BODY_B64: ${{ steps.body.outputs.body_b64 }}
run: |
set -euo pipefail
if ! body="$(printf '%s' "$BODY_B64" | base64 -d)"; then
echo "::error::Failed to decode PR body from base64. Data may be corrupted or missing."
exit 1
fi
added_checked=$(printf '%s' "$body" | grep -Ei '^[[:space:]]*-\s*\[x\]\s*I added/updated documentation' | wc -l | tr -d '[:space:]' || true)
noneed_checked=$(printf '%s' "$body" | grep -Ei '^[[:space:]]*-\s*\[x\]\s*Documentation is \*\*not needed\*\*' | wc -l | tr -d '[:space:]' || true)
total=$((added_checked + noneed_checked))
if [ "$total" -ne 1 ]; then
echo "::error::You must check exactly one docs option in the PR template (either 'docs added' OR 'not needed')."
exit 1
fi
if [ "$added_checked" -eq 1 ]; then
echo "mode=added" >> "$GITHUB_OUTPUT"
else
echo "mode=noneed" >> "$GITHUB_OUTPUT"
fi
- name: Extract docs PR URL (when 'docs added')
if: steps.validate.outputs.mode == 'added'
id: extract
shell: bash
env:
BODY_B64: ${{ steps.body.outputs.body_b64 }}
run: |
set -euo pipefail
body="$(printf '%s' "$BODY_B64" | base64 -d)"
# Strictly require HTTPS and that it's a PR in netbirdio/docs
# e.g., https://github.com/netbirdio/docs/pull/1234
url="$(printf '%s' "$body" | grep -Eo 'https://github\.com/netbirdio/docs/pull/[0-9]+' | head -n1 || true)"
if [ -z "${url:-}" ]; then
echo "::error::You checked 'docs added' but didn't include a valid HTTPS PR link to netbirdio/docs (e.g., https://github.com/netbirdio/docs/pull/1234)."
exit 1
fi
pr_number="$(printf '%s' "$url" | sed -E 's#.*/pull/([0-9]+)$#\1#')"
{
echo "url=$url"
echo "pr_number=$pr_number"
} >> "$GITHUB_OUTPUT"
- name: Verify docs PR exists (and is open or merged)
if: steps.validate.outputs.mode == 'added'
uses: actions/github-script@v7
id: verify
env:
PR_NUMBER: ${{ steps.extract.outputs.pr_number }}
with:
script: |
const prNumber = parseInt(process.env.PR_NUMBER, 10);
const { data } = await github.rest.pulls.get({
owner: 'netbirdio',
repo: 'docs',
pull_number: prNumber
});
// Allow open or merged PRs
const ok = data.state === 'open' || data.merged === true;
core.setOutput('state', data.state);
core.setOutput('merged', String(!!data.merged));
if (!ok) {
core.setFailed(`Docs PR #${prNumber} exists but is neither open nor merged (state=${data.state}, merged=${data.merged}).`);
}
result-encoding: string
github-token: ${{ secrets.GITHUB_TOKEN }}
- name: All good
run: echo "Documentation requirement satisfied ✅"
+162
View File
@@ -0,0 +1,162 @@
name: Playwright E2E Tests
on:
push:
branches: [main]
pull_request:
# `edited` is included so that updating the PR description (e.g. to set
# `management-cloud-tag: <tag>` or `reverse-proxy-tag: <tag>`) re-triggers
# the e2e run with the new tag.
types: [opened, synchronize, reopened, edited]
workflow_dispatch:
inputs:
management-cloud-tag:
description: 'Management Cloud image tag'
required: true
type: string
default: 'main'
reverse-proxy-tag:
description: 'Reverse Proxy image tag'
required: true
type: string
default: 'main'
env:
REGISTRY: ghcr.io
jobs:
playwright-run:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v5
with:
ref: ${{ github.event.pull_request.head.sha || github.ref }}
fetch-depth: 0
- name: setup-node
uses: actions/setup-node@v5
with:
node-version: '20'
cache: 'npm'
- name: Install dependencies
run: npm install
- name: Install Playwright browsers
run: npx playwright install --with-deps chromium
- name: Log in to the Container registry
uses: docker/login-action@v4
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.CI_DOCKER_PULL_GITHUB_TOKEN }}
- run: echo '{}' > .local-config.json
- name: Install jq
run: sudo apt-get install jq
- name: Resolve management-cloud image tag
id: management_tag
env:
INPUT_TAG: ${{ inputs.management-cloud-tag }}
PR_BODY: ${{ github.event.pull_request.body }}
run: |
# Use workflow_dispatch input if provided, otherwise parse PR body.
# Falls back to `main` when not specified.
if [ -n "$INPUT_TAG" ]; then
TAG="$INPUT_TAG"
else
TAG=$(printf '%s' "$PR_BODY" \
| grep -iE '^[[:space:]]*management-cloud-tag:[[:space:]]*[A-Za-z0-9._-]+[[:space:]]*$' \
| head -n1 \
| sed -E 's/^[[:space:]]*[Mm]anagement-cloud-tag:[[:space:]]*([A-Za-z0-9._-]+)[[:space:]]*$/\1/' \
|| true)
if [ -z "$TAG" ]; then
TAG="main"
fi
fi
echo "Using management-cloud tag: $TAG"
echo "tag=$TAG" >> "$GITHUB_OUTPUT"
- name: Resolve reverse-proxy image tag
id: reverse_proxy_tag
env:
INPUT_TAG: ${{ inputs.reverse-proxy-tag }}
PR_BODY: ${{ github.event.pull_request.body }}
run: |
if [ -n "$INPUT_TAG" ]; then
TAG="$INPUT_TAG"
else
TAG=$(printf '%s' "$PR_BODY" \
| grep -iE '^[[:space:]]*reverse-proxy-tag:[[:space:]]*[A-Za-z0-9._-]+[[:space:]]*$' \
| head -n1 \
| sed -E 's/^[[:space:]]*[Rr]everse-proxy-tag:[[:space:]]*([A-Za-z0-9._-]+)[[:space:]]*$/\1/' \
|| true)
if [ -z "$TAG" ]; then
TAG="main"
fi
fi
echo "Using reverse-proxy tag: $TAG"
echo "tag=$TAG" >> "$GITHUB_OUTPUT"
- name: Setup test environment
env:
MANAGEMENT_IMAGE_TAG: ${{ steps.management_tag.outputs.tag }}
REVERSE_PROXY_IMAGE_TAG: ${{ steps.reverse_proxy_tag.outputs.tag }}
run: cd ./e2e/environment && bash create-test-env.sh
- name: Run Playwright tests
id: playwright
run: |
set -o pipefail
npm run test:ci 2>&1 | tee playwright-output.log
- name: Append Playwright summary to job summary
if: always() && hashFiles('e2e/test-results/results.json') != ''
run: |
if [ -f e2e/test-results/results.json ]; then
passed=$(jq '.stats.expected // 0' e2e/test-results/results.json)
failed=$(jq '.stats.unexpected // 0' e2e/test-results/results.json)
skipped=$(jq '.stats.skipped // 0' e2e/test-results/results.json)
duration=$(jq '.stats.duration // 0' e2e/test-results/results.json)
{
echo '### Playwright results'
echo ''
echo "| Passed | Failed | Skipped | Duration |"
echo "|--------|--------|---------|----------|"
echo "| $passed | $failed | $skipped | ${duration}ms |"
} >> "$GITHUB_STEP_SUMMARY"
fi
- name: Collect container logs
if: failure()
run: |
cd e2e/environment
docker compose logs management --tail=500 --no-color > management.log 2>&1 || true
docker compose logs reverse-proxy --tail=500 --no-color > reverse-proxy.log 2>&1 || true
- uses: actions/upload-artifact@v7
if: ${{ !cancelled() }}
with:
name: playwright-report
path: e2e/playwright-report/
- uses: actions/upload-artifact@v7
if: ${{ failure() }}
with:
name: playwright-traces
path: e2e/test-results/
- uses: actions/upload-artifact@v7
if: ${{ failure() }}
with:
name: management-logs
path: e2e/environment/management.log
- uses: actions/upload-artifact@v7
if: ${{ failure() }}
with:
name: reverse-proxy-logs
path: e2e/environment/reverse-proxy.log
+13 -1
View File
@@ -36,8 +36,20 @@ yarn-error.log*
next-env.d.ts
# config
.local-config.json
.local*config*.json
.test-config.json
e2e/playwright.env.json
e2e/fixtures/auth/*.json
e2e/test-results/
e2e/playwright-report/
/test-results/
/playwright-report/
.configs/.local-config.zitadel.json
.configs/.staging-config.json
.configs/.temp-config.json
.configs
/public/ironrdp-pkg/
/public/netbird.wasm
.idea
src/.local-config*
+1 -1
View File
@@ -1,3 +1,3 @@
Mikhail Bragin (https://github.com/braginini)
Maycon Santos (https://github.com/mlsmaycon)
Wiretrustee UG (haftungsbeschränkt)
NetBird GmbH
+64
View File
@@ -0,0 +1,64 @@
## Contributor License Agreement
This Contributor License Agreement (referred to as the "Agreement") is entered into by the individual
submitting this Agreement and NetBird GmbH, Brunnenstraße 196, 10119 Berlin, Germany,
referred to as "NetBird" (collectively, the "Parties"). The Agreement outlines the terms and conditions
under which NetBird may utilize software contributions provided by the Contributor for inclusion in
its software development projects. By submitting this Agreement, the Contributor confirms their acceptance
of the terms and conditions outlined below. The Contributor further represents that they are authorized to
complete this process as described herein.
## 1 Preamble
In order to clarify the IP Rights situation with regard to Contributions from any person or entity, NetBird
must have a contributor license agreement on file to be signed by each Contributor, containing the license
terms below. This license serves as protection for both the Contributor as well as NetBird and its software users;
it does not change Contributor’s rights to use his/her own Contributions for any other purpose.
## 2 Definitions
2.1 “IP Rights” shall mean all industrial and intellectual property rights, whether registered or not registered, whether created by Contributor or acquired by Contributor from third parties, and similar rights, including (but not limited to) semiconductor property rights, design rights, copyrights (including in the form of database rights and rights to software), all neighbouring rights (Leistungsschutzrechte), trademarks, service marks, titles, internet domain names, trade names and other labelling rights, rights deriving from corresponding applications and registrations of such rights as well as any licenses (Nutzungsrechte) under and entitlements to any such intellectual and industrial property rights.
2.2 "Contribution" shall mean any original work of authorship, including any modifications or additions to an existing work, that is or previously has been intentionally Submitted by Contributor to NetBird for inclusion in, or documentation of any Work.
2.3 "Contributor" shall mean the copyright owner or legal entity authorized by the copyright owner that is concluding this Agreement with NetBird. For legal entities, the entity making a Contribution and all other entities that control, are controlled by, or are under common control with that entity are considered to be a single Contributor. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
2.4 "Submitted" shall mean any form of electronic, verbal, or written communication sent to NetBird or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, NetBird for the purpose of discussing and improving the Work, but excluding communication that is marked or otherwise designated in writing by Contributor as "Not a Contribution".
2.5 "Work" means any of the products owned or managed by NetBird, in particular, but not exclusively, software.
## 3 Licenses
3.1 Subject to the terms and conditions of this agreement, Contributor hereby grants to NetBird and to recipients of software distributed by NetBird a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable license to reproduce by any means and in any form, in whole or in part, permanently or temporarily, the Contributions (including loading, displaying, executing, transmitting or storing works for the purpose of executing and processing data or transferring them to video, audio and other data carriers), including the right to distribute, display and present such Contributions and make them available to the public (e.g. via the internet) and to transmit and display such Contributions by any means. The license also includes the right to modify, translate, adapt, edit and otherwise alter the Contributions and to use these results in the same manner as the original Contributions and derivative works. Except for licenses in patents acc. to Sec. 3, such license refers to any IP Rights in the Contributions and derivative works. The Contributor acknowledges that NetBird is not required to credit them by name for their Contribution and agrees to waive any moral rights associated with their Contribution in relation to NetBird or its sublicensees.
3.2 Subject to the terms and conditions of this agreement, Contributor hereby grants to NetBird and to recipients of software distributed by NetBird a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license in the Contributions to make, have made, use, sell, offer to sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by the Contributor which are necessarily infringed by Contributor‘s Contribution(s) alone or by combination of Contributor’s Contribution(s) with the Work to which such Contribution(s) was Submitted.
3.3 NetBird hereby accepts such licenses.
## 4 Contributor’s Representations
4.1 Contributor represents that Contributor is legally entitled to grant the above license. If Contributor’s employer has IP Rights to Contributor’s Contributions, Contributor represent that he/she has received permission to make Contributions on behalf of such employer, that such employer has waived such IP Rights to the Contributions of Contributor to NetBird, or that such employer has executed a separate contributor license agreement with NetBird.
4.2 Contributor represents that any Contribution is his/her original creation.
4.3 Contributor represents to his/her best knowledge that any Contribution does not violate any third party IP Rights.
4.4 Contributor represents that any Contribution submission includes complete details of any third-party license or other restriction (including, but not limited to, related patents and trademarks) of which Contributor is personally aware and which are associated with any part of the Contribution.
4.5 The Contributor represents that their Contribution does not include any work distributed under a copyleft license.
## 5 Information obligation
Contributor agrees to notify NetBird of any facts or circumstances of which Contributor become aware that would make these representations inaccurate in any respect.
## 6 Submission of Third-Party works
Should Contributor wish to submit work that is not Contributor’s original creation, Contributor may submit it to NetBird separately from any Contribution, identifying the complete details of its source and of any license or other restriction (including, but not limited to, related patents, trademarks, and license agreements) of which Contributor are personally aware, and conspicuously marking the work as "Submitted on behalf of a third-party: [named here]".
## 7 No Consideration
Unless compensation is mandatory under statutory law, no compensation for any license under this agreement shall be payable.
## 8 Final Provisions
8.1 Laws. This Agreement is governed by the laws of the Federal Republic of Germany.
8.2 Venue. Place of jurisdiction shall, to the extent legally permissible, be Berlin, Germany.
8.3 Severability. If any provision in this agreement is unlawful, invalid or ineffective, it shall not affect the enforceability or effectiveness of the remainder of this agreement. The parties agree to replace any unlawful, invalid or ineffective provision with a provision that comes as close as possible to the commercial intent and purpose of the original provision. This section also applies accordingly to any gaps in the contract.
8.4 Variations. Any variations, amendments or supplements to this Agreement must be in writing. This also applies to any variation of this Section 8.4.
+655 -7
View File
File diff suppressed because it is too large. Load diff
+12 -8
View File
@@ -10,6 +10,7 @@ See [NetBird repo](https://github.com/netbirdio/netbird)
The purpose of this project is simple - make it easy to manage VPN built with [NetBird](https://github.com/netbirdio/netbird).
The dashboard makes it possible to:
- track the status of your peers
- remove peers
- manage Setup Keys (to authenticate new peers)
@@ -17,23 +18,25 @@ The dashboard makes it possible to:
- define access controls
## Some Screenshots
<img src="./src/assets/screenshots/peers.png" alt="peers"/>
<img src="./src/assets/screenshots/add-peer.png" alt="add-peer"/>
## Technologies Used
- NextJS
- ReactJS
- Tailwind CSS
- [React Flow](https://reactflow.dev/) for the Control Center
- Auth0
- Nginx
- Docker
- Let's Encrypt
## How to run
Disclaimer. We believe that proper user management system is not a trivial task and requires quite some effort to make it right. Therefore we decided to
use Auth0 service that covers all our needs (user management, social login, JTW for the management API).
use Auth0 service that covers all our needs (user management, social login, JWT for the management API).
Auth0 so far is the only 3rd party dependency that can't be really self-hosted.
1. Install [Docker](https://docs.docker.com/get-docker/)
@@ -42,9 +45,9 @@ Auth0 so far is the only 3rd party dependency that can't be really self-hosted.
`AUTH0_DOMAIN` `AUTH0_CLIENT_ID` `AUTH0_AUDIENCE`
To obtain these, please use [Auth0 React SDK Guide](https://auth0.com/docs/quickstart/spa/react/01-login#configure-auth0) up until "Configure Allowed Web Origins"
To obtain these, please use [Auth0 React SDK Guide](https://auth0.com/docs/quickstart/spa/react) up until "Configure Allowed Web Origins"
4. NetBird UI Dashboard uses NetBirds Management Service HTTP API, so setting `NETBIRD_MGMT_API_ENDPOINT` is required. Most likely it will be `http://localhost:33071` if you are hosting Management API on the same server.
4. NetBird UI Dashboard uses NetBird's Management Service HTTP API, so setting `NETBIRD_MGMT_API_ENDPOINT` is required. Most likely it will be `http://localhost:33071` if you are hosting Management API on the same server.
5. Run docker container without SSL (Let's Encrypt):
```shell
@@ -53,9 +56,10 @@ Auth0 so far is the only 3rd party dependency that can't be really self-hosted.
-e AUTH0_DOMAIN=<SET YOUR AUTH DOMAIN> \
-e AUTH0_CLIENT_ID=<SET YOUR CLIENT ID> \
-e AUTH0_AUDIENCE=<SET YOUR AUDIENCE> \
-e NETBIRD_MGMT_API_ENDPOINT=<SET YOUR MANAGEMETN API URL> \
-e NETBIRD_MGMT_API_ENDPOINT=<SET YOUR MANAGEMENT API URL> \
netbirdio/dashboard:main
```
6. Run docker container with SSL (Let's Encrypt):
```shell
@@ -67,7 +71,7 @@ Auth0 so far is the only 3rd party dependency that can't be really self-hosted.
-e AUTH0_DOMAIN=<SET YOUR AUTH DOMAIN> \
-e AUTH0_CLIENT_ID=<SET YOUR CLEITN ID> \
-e AUTH0_AUDIENCE=<SET YOUR AUDIENCE> \
-e NETBIRD_MGMT_API_ENDPOINT=<SET YOUR MANAGEMETN API URL> \
-e NETBIRD_MGMT_API_ENDPOINT=<SET YOUR MANAGEMENT API URL> \
netbirdio/dashboard:main
```
@@ -83,11 +87,11 @@ Open [http://localhost:3000](http://localhost:3000) with your browser to see the
You can start editing by modifying the code inside `src/..`
The page auto-updates as you edit the file.
## How to migrate from old dashboard (v1)
## How to migrate from old dashboard (v1)
The new dashboard comes with a new docker image `netbirdio/dashboard:main`.
To migrate from the old dashboard (v1) `wiretrustee/dashboard:main` to the new one, please follow the steps below.
1. Stop the dashboard container `docker compose down dashboard`
2. Replace the docker image name in your `docker-compose.yml` with `netbirdio/dashboard:main`
3. Recreate the dashboard container `docker compose up -d --force-recreate dashboard`
3. Recreate the dashboard container `docker compose up -d --force-recreate dashboard`
+12
View File
@@ -0,0 +1,12 @@
[
{
"tag": "Preview",
"text": "The new NetBird desktop app is here - now available as a 0.75 release candidate.",
"link": "https://github.com/netbirdio/netbird/discussions/6483",
"linkText": "Learn more",
"variant": "important",
"isExternal": true,
"closeable": true,
"isCloudOnly": false
}
]
Executable
+77
View File
@@ -0,0 +1,77 @@
#!/usr/bin/env bash
#
# Build the netbird-dashboard Docker image for linux/amd64 and export it as a
# portable tarball that can be loaded and run on a remote server.
#
# The dashboard image is nginx serving a Next.js *static export*: the Dockerfile
# only COPYs the pre-built out/ directory, so we build out/ on the host first
# (the static files are arch-independent) and then assemble the amd64 image.
#
# Usage:
# ./build.sh # build + export netbird-dashboard.tar.gz
# IMAGE_TAG=v1 ./build.sh # override the image tag
# SKIP_BUILD=1 ./build.sh # reuse an existing out/ (skip npm build)
#
set -euo pipefail
# Always run relative to this script's directory (the project root).
cd "$(dirname "$0")"
IMAGE_NAME="${IMAGE_NAME:-netbird-dashboard}"
IMAGE_TAG="${IMAGE_TAG:-amd64}"
IMAGE="${IMAGE_NAME}:${IMAGE_TAG}"
OUTPUT="${OUTPUT:-${IMAGE_NAME}.tar.gz}"
# Build the static export (out/) unless told to reuse an existing one. Next 16
# no longer runs ESLint during `next build`; types are checked by the build via
# tsc (verify separately with `npx tsc --noEmit`).
if [[ "${SKIP_BUILD:-0}" != "1" ]]; then
if [[ ! -d node_modules ]]; then
echo "==> Installing dependencies (npm ci)"
npm ci
fi
echo "==> Building static export (next build) into out/"
npx next build
fi
if [[ ! -d out ]]; then
echo "ERROR: out/ not found — the static export did not build." >&2
exit 1
fi
# The .dockerignore lives under docker/, but Docker reads it from the build
# context root. Stage it for the duration of the build so node_modules/.next
# don't get shipped into the build context.
cleanup() { rm -f .dockerignore; }
trap cleanup EXIT
cp docker/.dockerignore .dockerignore
echo "==> Building ${IMAGE} for linux/amd64"
docker build \
--platform linux/amd64 \
-f docker/Dockerfile \
-t "${IMAGE}" \
.
echo "==> Exporting ${IMAGE} to ${OUTPUT}"
docker save "${IMAGE}" | gzip > "${OUTPUT}"
echo "==> Done: ${OUTPUT} ($(du -h "${OUTPUT}" | cut -f1))"
echo
echo "Copy and run on the server:"
echo " scp ${OUTPUT} user@your-server:/tmp/"
echo " docker load < /tmp/${OUTPUT}"
echo
echo "Run (nginx serves on container port 80). Set the runtime env vars the"
echo "dashboard templates at startup — for the Agent Network onboarding test,"
echo "flip NETBIRD_AGENT_NETWORK_ONLY=true:"
echo " docker run -d --name ${IMAGE_NAME} --restart unless-stopped \\"
echo " -p 8080:80 \\"
echo " -e NETBIRD_MGMT_API_ENDPOINT=https://your-mgmt:443 \\"
echo " -e AUTH_AUTHORITY=https://your-idp/realms/netbird \\"
echo " -e AUTH_CLIENT_ID=netbird-dashboard \\"
echo " -e AUTH_AUDIENCE=netbird-dashboard \\"
echo " -e AUTH_SUPPORTED_SCOPES='openid profile email' \\"
echo " -e USE_AUTH0=false \\"
echo " -e NETBIRD_AGENT_NETWORK_ONLY=true \\"
echo " ${IMAGE}"
+14 -2
View File
@@ -12,5 +12,17 @@
"tokenSource": "$NETBIRD_TOKEN_SOURCE",
"dragQueryParams": "$NETBIRD_DRAG_QUERY_PARAMS",
"hotjarTrackID": "$NETBIRD_HOTJAR_TRACK_ID",
"googleAnalyticsID": "$NETBIRD_GOOGLE_ANALYTICS_ID"
}
"googleAnalyticsID": "$NETBIRD_GOOGLE_ANALYTICS_ID",
"googleTagManagerID": "$NETBIRD_GOOGLE_TAG_MANAGER_ID",
"authServiceUrl": "$NETBIRD_AUTH_SERVICE_URL",
"wasmPath": "$NETBIRD_WASM_PATH",
"licensed": "$NETBIRD_LICENSED",
"cloud": "$NETBIRD_CLOUD",
"agentNetworkOnly": "$NETBIRD_AGENT_NETWORK_ONLY",
"agentNetworkEnabled": "$NETBIRD_AGENT_NETWORK_ENABLED",
"hubspotPortalId": "$NETBIRD_HUBSPOT_PORTAL_ID",
"hubspotSignupFormId": "$NETBIRD_HUBSPOT_SIGNUP_FORM_ID",
"hubspotOnboardingFormId": "$NETBIRD_HUBSPOT_ONBOARDING_FORM_ID",
"hubspotSurveyFormId": "$NETBIRD_HUBSPOT_SURVEY_FORM_ID",
"analyticsExcludedEmails": "$NETBIRD_ANALYTICS_EXCLUDED_EMAILS"
}
-15
View File
@@ -1,15 +0,0 @@
import { defineConfig } from "cypress";
export default defineConfig({
e2e: {
baseUrl: "http://localhost:3000",
},
component: {
devServer: {
framework: "next",
bundler: "webpack",
},
},
viewportWidth: 1920,
viewportHeight: 1080,
});
-13
View File
@@ -1,13 +0,0 @@
describe("Click all tabs in peer modal", () => {
it("passes", () => {
cy.visit("/install");
cy.get("div").contains("Linux").click();
cy.get("[data-cy=copy-to-clipboard]").click();
cy.get("div").contains("Windows").click();
cy.get("[data-cy=copy-to-clipboard]").click();
cy.get("div").contains("Android").click();
cy.get("[data-cy=copy-to-clipboard]").click();
cy.get("div").contains("Docker").click();
cy.get("[data-cy=copy-to-clipboard]").click();
});
});
-5
View File
@@ -1,5 +0,0 @@
{
"name": "Using fixtures to represent data",
"email": "hello@cypress.io",
"body": "Fixtures are a great way to mock data for responses to routes"
}
-37
View File
@@ -1,37 +0,0 @@
/// <reference types="cypress" />
// ***********************************************
// This example commands.ts shows you how to
// create various custom commands and overwrite
// existing commands.
//
// For more comprehensive examples of custom
// commands please read more here:
// https://on.cypress.io/custom-commands
// ***********************************************
//
//
// -- This is a parent command --
// Cypress.Commands.add('login', (email, password) => { ... })
//
//
// -- This is a child command --
// Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... })
//
//
// -- This is a dual command --
// Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... })
//
//
// -- This will overwrite an existing command --
// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... })
//
// declare global {
// namespace Cypress {
// interface Chainable {
// login(email: string, password: string): Chainable<void>
// drag(subject: string, options?: Partial<TypeOptions>): Chainable<Element>
// dismiss(subject: string, options?: Partial<TypeOptions>): Chainable<Element>
// visit(originalFn: CommandOriginalFn, url: string, options: Partial<VisitOptions>): Chainable<Element>
// }
// }
// }
-20
View File
@@ -1,20 +0,0 @@
// ***********************************************************
// This example support/e2e.ts is processed and
// loaded automatically before your test files.
//
// This is a great place to put global configuration and
// behavior that modifies Cypress.
//
// You can change the location of this file or turn off
// automatically serving support files with the
// 'supportFile' configuration option.
//
// You can read more here:
// https://on.cypress.io/configuration
// ***********************************************************
// Import commands.js using ES2015 syntax:
import './commands'
// Alternatively you can use CommonJS syntax:
// require('./commands')
-9
View File
@@ -1,9 +0,0 @@
{
"compilerOptions": {
"target": "es5",
"lib": ["es5", "dom"],
"baseUrl": "http://localhost:3000",
"types": ["cypress", "node"],
},
"include": ["**/*.ts"]
}
+12
View File
@@ -0,0 +1,12 @@
# The image only needs the pre-built static export (out/) and the docker/
# config files. Keep the build context small by dropping everything heavy and
# irrelevant — without this, `docker build .` ships node_modules and .next.
node_modules
.next
.git
.github
e2e
src
public
*.tar.gz
*.tar
Loaded 20 of 1153 files, more files were not shown because too many files have changed in this diff. Show more