Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file not shown.
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 8 additions & 1 deletion src/components/NavigationDocs.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,14 @@ export const docsNavigation = [
{
title: 'MANAGE NETBIRD',
links: [
{ title: 'Control Center', href: '/manage/control-center' },
{
title: 'Control Center',
isOpen: false,
links: [
{ title: 'Overview', href: '/manage/control-center' },
{ title: 'Draft Mode', href: '/manage/control-center/draft-mode' },
],
},
{
title: 'Peers',
isOpen: false,
Expand Down
74 changes: 74 additions & 0 deletions src/components/Video.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { useEffect, useRef } from 'react'
import clsx from 'clsx'

/**
* Looping screen-recording embed for docs pages.
*
* Recordings load lazily and play only while on screen: `preload="metadata"`
* avoids buffering full files up front, and an IntersectionObserver starts
* playback when the video scrolls into view and pauses it when it leaves.
* Controls stay visible so the loop can be paused (WCAG 2.2.2), and autoplay
* is skipped entirely for users who prefer reduced motion.
*
* Usage:
* <Video src="/docs-static/img/manage/example.mp4" label="What the recording shows" />
*/
export function Video({ src, label, className, ...props }) {
const ref = useRef(null)

useEffect(() => {
const video = ref.current
if (!video) return

const reducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)')
let observer = null

const observe = () => {
observer = new IntersectionObserver(
([entry]) => {
if (entry.isIntersecting) {
video.play().catch(() => {})
} else {
video.pause()
}
},
{ threshold: 0.25 }
)
observer.observe(video)
}

// React to the OS setting changing while the page is open.
const handleMotionChange = () => {
if (reducedMotion.matches) {
observer?.disconnect()
observer = null
video.pause()
} else if (!observer) {
observe()
}
}

if (!reducedMotion.matches) observe()
reducedMotion.addEventListener('change', handleMotionChange)

return () => {
reducedMotion.removeEventListener('change', handleMotionChange)
observer?.disconnect()
}
}, [])
Comment thread
coderabbitai[bot] marked this conversation as resolved.

return (
<video
ref={ref}
src={src}
loop
muted
playsInline
controls
preload="metadata"
aria-label={label}
className={clsx('imagewrapper-big', className)}
{...props}
/>
)
}
1 change: 1 addition & 0 deletions src/components/mdx.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export { Button } from '@/components/Button'
export { CodeGroup, Code as code, Pre as pre } from '@/components/Code'
export { Badge } from '@/components/Badge'
export { YouTube }
export { Video } from '@/components/Video'

export const h2 = function H2(props) {
return <Heading level={2} {...props} />
Expand Down
229 changes: 229 additions & 0 deletions src/pages/manage/control-center/draft-mode.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
import {Note, Warning, Video} from "@/components/mdx"

export const description =
'Stage network changes on a working copy of the Control Center canvas, review the exact API requests, and apply everything as one batch with Draft Mode.'

# Plan and Deploy Changes with Draft Mode

Every edit in the NetBird dashboard applies the moment you save it. That is fine for a single tweak, but a real change, like onboarding a new server, spans a peer, a group, and a policy, and a slip lands on production immediately.

**Draft Mode** removes that risk. It turns the [Control Center](/manage/control-center) canvas into a local working copy: sketch the whole change, review the exact API requests, and apply everything as one batch.

<Note>
**Availability**: Draft Mode is in **beta**, available on NetBird Cloud (all plans) and self-hosted.
</Note>
<Note>
**Permissions:** Deploying a draft performs the same operations as making those changes on their regular dashboard pages, so it requires the same permissions. Learn more about [user roles](/manage/team/user-roles).
</Note>

A draft moves through three stages: **build** on the canvas, **review** the change list, and **deploy** as one batch. Nothing touches your account until you click **Approve & Deploy**.

One example carries through the whole page: giving `DevOps` HTTPS access to `staging-01`, a new staging server that is not even installed yet.

## Enter Draft Mode

In Control Center, switch the **Live | Draft** toggle in the top-right corner to **Draft**, then select either **New Empty Draft** or **From Current View**:

- **From Current View**: start from a copy of the canvas you are looking at. Pick this when you are modifying things that already exist; the draft reproduces the live view exactly, network nodes included.
- **New Empty Draft**: start from a blank canvas. Pick this when you are building something new from scratch, like our `staging-01` setup.

<p>
<Video src="/docs-static/img/manage/control-center/control-center-draft-enter.mp4"
label="Switching to Draft Mode: picking a starting point, then the editing toolbar appearing at the bottom of the canvas"/>
</p>

<Warning>
A draft lives only in your browser tab. **Reloading or closing the page discards it**; the browser warns you first, but there is no way to recover a discarded draft. Build and deploy a draft in one sitting.
</Warning>

## Canvas Toolbar

The toolbar at the bottom of the canvas holds the select and hand (pan) tools, zoom controls, and the actions below. Every edit you make in a draft is tracked as a change and applied only at deploy.

### Select and Pan

- **Select** (`V`) is the default tool: click a node to select it, drag it to move it, or drag across empty canvas to select several nodes at once.
- **Hand** (`H`) pans the canvas as you drag, so you can move around a large draft without touching any nodes.

### Add Nodes

Click **Add** in the toolbar (or press `C`) to open the components panel. It covers both kinds of adding:

- **Add new**: drag-and-drop templates for components that do not exist yet (Server, Agent, Policy, Group, Network, Resource). Server and Agent both create placeholder peers and are installed the same way, with a setup key; pick whichever matches the machine's role.
- **Add existing**: searchable lists of the peers, groups, networks, and policies already in your account. Components already on the canvas are marked accordingly.

You can also right-click the canvas to pick a component from the context menu, or press `Alt/⌥+1` through `Alt/⌥+6` to create a new Server, Agent, Policy, Group, Network, or Resource at the center of the view.

<p>
<img src="/docs-static/img/manage/control-center/control-center-draft-components-panel.png"
alt="The components panel with Server and Agent templates under Add New and a searchable list of existing peers" className="imagewrapper-big"/>
</p>

For our example, drag a **Server** template onto the canvas. It arrives as a **placeholder peer**: a stand-in for a machine that has not registered yet (see [Install peers that don't exist yet](#install-peers-that-dont-exist-yet)).

<p>
<Video src="/docs-static/img/manage/control-center/control-center-draft-add-server.mp4"
label="Adding a Server template to the canvas from the components panel"/>
</p>

Then drag a **Group** onto the canvas for the access we are about to grant.

<p>
<Video src="/docs-static/img/manage/control-center/control-center-draft-add-group.mp4"
label="Adding a Group to the canvas from the components panel"/>
</p>

**Network** and **Resource** templates work the same way and integrate with [Networks](/manage/networks); connecting a policy to a network node asks which of its resources the policy should target.

### Undo and Redo

`Ctrl/⌘+Z` undoes any canvas action, `Shift+Ctrl/⌘+Z` redoes it.

### Fit to View

**Fit to view** (`1`) zooms and centers the canvas so the whole draft is on screen.

### Auto-Arrange

**Auto-arrange** (`A`) reflows the nodes into a tidy layout, handy after a lot of dragging. Layout changes are visual only; they are never part of the deployed changes.

## Interact with Nodes

We carry the `staging-01` example through each action below.

### Rename Nodes

Right-click a node and choose **Rename**. We rename the server to `staging-01` and the group to `Staging`. Names must be unique, and the built-in `All` group cannot be renamed.

<p>
<img src="/docs-static/img/manage/control-center/control-center-draft-rename.png"
alt="The right-click menu on a server node with the Rename option highlighted" className="imagewrapper-big"/>
</p>

### Assign Peers to Groups

Drag the `staging-01` node onto the `Staging` group. The peer is absorbed into the group and the group's member count goes up. Membership travels with the group's change, so you never manage it separately.

<p>
<Video src="/docs-static/img/manage/control-center/control-center-draft-drag-to-group.mp4"
label="Dragging the staging-01 peer onto the Staging group, which absorbs it and updates its member count"/>
</p>

To group several peers at once, drag a selection across them, then click **Create Group** in the selection menu (or press `G`) and name the group:

<p>
<Video src="/docs-static/img/manage/control-center/control-center-draft-group-from-selection.mp4"
label="Drag-selecting several peers, clicking Create Group in the selection menu, and naming the new group"/>
</p>

### Connect Nodes to Create Policies

Drag the existing `DevOps` group from the components panel onto the canvas, then drag a connection from `DevOps` to `Staging`. The standard policy editor opens with both sides prefilled. Restrict it to what the team actually needs: TCP port `443` for the web app, not all traffic. The first policy you deploy should already be the least-privilege one.

<p>
<Video src="/docs-static/img/manage/control-center/control-center-draft-connect-policy.mp4"
label="Drawing a connection from the DevOps group to the Staging group, which opens the policy editor prefilled with both sides"/>
</p>

That is the whole change: a new server, its group, and one policy, and nothing has touched your network yet. The **Review & Deploy** counter now reflects each tracked change.

A few more ways to build policies:

- **The side you drag from decides the direction.** Dragging a connection from a node's right handle makes that node the policy's **source**; dragging from the left handle makes it the **destination**.
- **Policies without the editor**: drop a blank **Policy** node and connect a source and a destination to it. The policy is tracked as soon as both sides are set, no dialog involved.
- **Groups on the fly**: typing a new name into the policy editor's source or destination selector creates that group as part of the draft.

### Install Peers That Don't Exist Yet

`staging-01` does not exist yet, and Draft Mode does not need it to. A placeholder peer holds the machine's place on the canvas: you can group it and write policies against it, and when the real machine registers, it inherits everything you prepared.

To turn the placeholder into a real peer, hover it and click **Install**:

1. The **Install NetBird** dialog opens with instructions per operating system. Click **Generate a setup key** and run the shown command on the machine.

<p>
<img src="/docs-static/img/manage/control-center/control-center-draft-install-modal.png"
alt="The Install NetBird dialog with the generated setup key command" className="imagewrapper-big"/>
</p>

2. The placeholder switches to **Waiting** while the machine registers.
3. When it connects, the placeholder is replaced in place: the real `staging-01` takes over its name, its `Staging` membership, and the policy you drew. The temporary setup key is deleted automatically.

<Note>
Installing a peer is a real-world step, not an API call: a draft cannot be deployed until every placeholder has registered or been removed.
Generating the setup key requires a role that can create setup keys; a [Network Admin](/manage/team/user-roles) cannot, so they need an Admin to run the install step or must remove the placeholder before deploying.
</Note>

### Remove and Delete Nodes

**Remove** and **Delete** do different things:

- **Remove** takes a component off the canvas and nothing more. It never asks for confirmation because it never touches your network; removing something also cancels any pending change it was part of. `Backspace` on a selected node does the same.
- **Delete** is only offered for things that actually exist in your account. It always asks for confirmation, shows up as a red **Delete** entry in the review list, and the deletion happens at deploy.

<Note>
Removing an existing group from the canvas does **not** delete it; it only leaves your draft. If your intent is "this group should be gone from my network", you want **Delete**.
</Note>

## Review and Deploy

Click **Review & Deploy**. The review dialog lists every change in the exact order it will be applied, one row per operation:

- Each row shows the affected component and the HTTP request that will be sent (like `POST /groups`). Clicking the request copies it as a `cURL` command; expanding the row shows the request body as a diff.
- For our example you would see the `Staging` group creation, the `DevOps → Staging` policy creation, and the `staging-01` install step.

If something is unfinished, the row carries an amber issue badge you can click to fix it in place: **Install** for a placeholder that has no machine yet, or **No Network** for a resource that is not assigned to any network. **Approve & Deploy** stays disabled until every blocking issue is resolved.

<p>
<img src="/docs-static/img/manage/control-center/control-center-draft-review-issues.png"
alt="The Review & Deploy dialog listing changes, with Waiting and Install issue badges blocking the deploy" className="imagewrapper-big"/>
</p>

To drop a single change without discarding the whole draft, open the row's menu and choose **Remove**; a confirmation lists exactly what that reverts on the canvas before you commit to it.

When everything is green, click **Approve & Deploy**:

- Changes apply in dependency order: groups first, then networks, resources, routing peers, and policies, with deletions last. References resolve as the batch runs, so the new policy points at the `Staging` group created moments earlier.
- If a request fails, the deploy stops with the error. Changes that already applied stay applied and are marked with a check; fix the failing one and click **Approve & Deploy** again to resume from where it stopped.
- On success you get a **Deploy complete** confirmation, the draft closes, and the live canvas rebuilds with your changes in place.

## Leaving a Draft

Click **Cancel** (or switch back to **Live**) to abandon a draft. With pending changes you are asked to confirm discarding them; navigating to any other dashboard page asks the same question first, and a page reload triggers the browser's own warning. Temporary artifacts a draft created for peer installation, like setup keys, are cleaned up automatically when the draft is discarded.

<p>
<img src="/docs-static/img/manage/control-center/control-center-draft-discard.png"
alt="The Discard draft changes confirmation dialog" className="imagewrapper-big"/>
</p>

## When Not to Use Draft Mode

For a single, self-contained edit, like widening one policy's port range, Draft Mode is overhead: edit the policy directly from the live Control Center view (the dashboard confirms before applying live changes) or from its regular page. Drafts are also never stored, so Draft Mode is not a place to park a half-finished configuration and come back to it another day: it fits changes you can build and deploy in one sitting. Draft Mode earns its keep when a change spans several components, needs a machine that is not installed yet, or deserves a review before it goes live. For automated or repeated changes, use the [NetBird API](/api) instead.

## Keyboard Shortcuts

| Key | Action |
|---|---|
| `C` | Open or close the components panel |
| `V` / `H` | Select tool / Hand (pan) tool |
| `Alt/⌥+1` … `Alt/⌥+6` | New Server / Agent / Policy / Group / Network / Resource |
| `G` | Create a group from the selected nodes |
| `Ctrl/⌘+Z`, `Shift+Ctrl/⌘+Z` | Undo / Redo |
| `1` / `A` | Fit to view / Auto-arrange the layout |
| `Backspace` | Remove the selected node from the canvas |
| `F` | Focus a node and dim everything unrelated |

## Recap

- A draft is a working copy of your network: build on the canvas, review the change list, deploy as one batch. Nothing is written until you click **Approve & Deploy**.
- Our example never left that path: we placed `staging-01`, grouped it into `Staging`, and drew a least-privilege HTTPS policy from `DevOps`, all before the server even existed.
- Installing a placeholder slots the real machine into everything you prepared for it.
- Remove edits the canvas; Delete edits your network.
- The review shows the literal API requests in deploy order, and a failed deploy resumes where it stopped.

## Related Docs

- [Control Center overview](/manage/control-center)
- [Manage network access with Groups and Access Policies](/manage/access-control/manage-network-access)
- [Networks and routing peers](/manage/networks)
- [Register machines using setup keys](/manage/peers/register-machines-using-setup-keys)
Loading
Loading