diff --git a/docs/io.netbird.client.plist b/docs/io.netbird.client.plist
new file mode 100644
index 000000000..f42b6b3d2
--- /dev/null
+++ b/docs/io.netbird.client.plist
@@ -0,0 +1,126 @@
+
+
+
+
+
+
+
+ managementURL
+ https://api.netbird.io:443
+
+
+
+
+
+
+ allowServerSSH
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/docs/netbird-macos.mobileconfig b/docs/netbird-macos.mobileconfig
new file mode 100644
index 000000000..53453db5c
--- /dev/null
+++ b/docs/netbird-macos.mobileconfig
@@ -0,0 +1,159 @@
+
+
+
+
+
+
+ PayloadType
+ Configuration
+ PayloadVersion
+ 1
+ PayloadIdentifier
+ io.netbird.client.mdm
+ PayloadUUID
+ 11111111-1111-1111-1111-111111111111
+ PayloadDisplayName
+ NetBird MDM Policy
+ PayloadDescription
+ Enforces NetBird client configuration. Values written here override any local user / CLI / on-disk setting and are re-applied at every daemon boot and on every 1-minute MDM reload tick.
+ PayloadOrganization
+ NetBird
+ PayloadScope
+ System
+ PayloadRemovalDisallowed
+
+
+ PayloadContent
+
+
+
+ PayloadType
+ com.apple.ManagedClient.preferences
+ PayloadVersion
+ 1
+ PayloadIdentifier
+ io.netbird.client.mdm.preferences
+ PayloadUUID
+ 22222222-2222-2222-2222-222222222222
+ PayloadDisplayName
+ NetBird Managed Preferences
+ PayloadEnabled
+
+
+ PayloadContent
+
+ io.netbird.client
+
+ Forced
+
+
+ mcx_preference_settings
+
+
+
+ managementURL
+ https://api.netbird.io:443
+
+
+
+
+
+
+ allowServerSSH
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/docs/netbird-macos.sh b/docs/netbird-macos.sh
new file mode 100755
index 000000000..c2493fa70
--- /dev/null
+++ b/docs/netbird-macos.sh
@@ -0,0 +1,179 @@
+#!/bin/bash
+#
+# SYNOPSIS
+# Push the NetBird MDM policy to a macOS device via JumpCloud Commands.
+#
+# DESCRIPTION
+# This is the macOS counterpart of docs/netbird-policy.reg.ps1.
+# It writes the values declared in the "POLICY VALUES" block below to
+# the managed-preferences plist that the NetBird daemon's
+# client/mdm/policy_darwin.go loader reads on every 1-minute MDM
+# reload tick:
+#
+# /Library/Managed Preferences/io.netbird.client.plist
+#
+# Once the plist lands, the daemon picks up the new values without
+# restart (the ticker calls Config.apply() → applyMDMPolicy() and
+# restarts the engine on diff).
+#
+# DEPLOYMENT (JumpCloud)
+# 1. Admin Console -> Device Management -> Commands -> +.
+# 2. Type: Mac, Shell, Run as: root.
+# 3. Paste this file verbatim into the command body.
+# 4. Bind to the target system group, save, run.
+#
+# IMPORTANT: PERSISTENCE
+# macOS wipes /Library/Managed Preferences/ at every boot on devices
+# that are NOT MDM-enrolled. For a persistent fleet rollout, push the
+# companion docs/netbird-macos.mobileconfig as a Custom Configuration
+# Profile (Admin Console -> MDM -> Mac Custom Configuration Profiles)
+# instead of this script. Use this script when:
+# - the device is MDM-enrolled (file survives reboots), or
+# - you need a one-shot test push before reboot, or
+# - you orchestrate via JumpCloud Commands and want the same
+# variable-driven workflow as the Windows .ps1 sibling.
+#
+# IDEMPOTENCY: re-running with the same values is a no-op from the
+# daemon's point of view (the 1-minute reload ticker diff returns empty).
+#
+# SECURITY: PreSharedKey is redacted in this script's log output.
+
+set -euo pipefail
+
+### POLICY VALUES — EDIT THIS BLOCK ###########################################
+#
+# Set each variable below to the desired value. Set to empty string ""
+# or to NULL to omit a key entirely (the daemon treats an absent key
+# as "no enforcement" for that field). Booleans use "true"/"false"
+# (lowercase). Integers as decimal.
+#
+# Reference for key names + accepted values:
+# client/mdm/policy.go (Key* constants)
+# docs/netbird-macos.mobileconfig (sample profile)
+# docs/netbird.admx + .adml (Windows ADMX schema)
+#
+NULL='__UNSET__'
+managementURL='https://api.netbird.io:443'
+preSharedKey="$NULL" # secret; redacted in log
+allowServerSSH='true'
+blockInbound="$NULL"
+disableAutoConnect="$NULL"
+disableClientRoutes="$NULL"
+disableServerRoutes="$NULL"
+disableMetricsCollection="$NULL"
+disableUpdateSettings="$NULL"
+disableProfiles="$NULL"
+disableNetworks="$NULL"
+rosenpassEnabled="$NULL"
+rosenpassPermissive="$NULL"
+wireguardPort='51820'
+splitTunnelMode="$NULL" # "allow" or "disallow", Android-only at the daemon level
+splitTunnelApps="$NULL" # comma-separated app IDs, Android-only
+##############################################################################
+
+readonly PLIST_DIR='/Library/Managed Preferences'
+readonly PLIST_PATH="$PLIST_DIR/io.netbird.client.plist"
+readonly LOG_TAG='netbird-mdm'
+
+log() {
+ /usr/bin/logger -t "$LOG_TAG" "$*"
+ printf '%s [%s] %s\n' "$(date -u '+%Y-%m-%dT%H:%M:%SZ')" "$LOG_TAG" "$*"
+}
+
+is_set() {
+ [[ -n "$1" && "$1" != "$NULL" ]]
+}
+
+start_plist() {
+ cat > "$PLIST_PATH.tmp" <<'EOF'
+
+
+
+
+EOF
+}
+
+end_plist() {
+ cat >> "$PLIST_PATH.tmp" <<'EOF'
+
+
+EOF
+}
+
+emit_string() {
+ local key="$1" value="$2" log_value="$2"
+ # Escape XML entities in the value
+ local escaped
+ escaped="$(printf '%s' "$value" | sed -e 's/&/\&/g' -e 's/\</g' -e 's/>/\>/g')"
+ printf ' %s\n %s\n' "$key" "$escaped" >> "$PLIST_PATH.tmp"
+ if [[ "$key" == "preSharedKey" ]]; then
+ log_value='********** (secret)'
+ fi
+ log "set $key = $log_value"
+}
+
+emit_bool() {
+ local key="$1" value="$2"
+ local xml_bool
+ case "$value" in
+ true|True|TRUE|1|yes) xml_bool='' ; value='true' ;;
+ false|False|FALSE|0|no) xml_bool='' ; value='false' ;;
+ *) log "invalid boolean for $key: $value (must be true/false); skipping"; return ;;
+ esac
+ printf ' %s\n %s\n' "$key" "$xml_bool" >> "$PLIST_PATH.tmp"
+ log "set $key = $value"
+}
+
+emit_int() {
+ local key="$1" value="$2"
+ if ! [[ "$value" =~ ^[0-9]+$ ]]; then
+ log "invalid integer for $key: $value (must be decimal); skipping"
+ return
+ fi
+ printf ' %s\n %s\n' "$key" "$value" >> "$PLIST_PATH.tmp"
+ log "set $key = $value"
+}
+
+main() {
+ log "applying NetBird MDM policy to $PLIST_PATH"
+ /bin/mkdir -p "$PLIST_DIR"
+ start_plist
+
+ is_set "$managementURL" && emit_string managementURL "$managementURL"
+ is_set "$preSharedKey" && emit_string preSharedKey "$preSharedKey"
+ is_set "$allowServerSSH" && emit_bool allowServerSSH "$allowServerSSH"
+ is_set "$blockInbound" && emit_bool blockInbound "$blockInbound"
+ is_set "$disableAutoConnect" && emit_bool disableAutoConnect "$disableAutoConnect"
+ is_set "$disableClientRoutes" && emit_bool disableClientRoutes "$disableClientRoutes"
+ is_set "$disableServerRoutes" && emit_bool disableServerRoutes "$disableServerRoutes"
+ is_set "$disableMetricsCollection" && emit_bool disableMetricsCollection "$disableMetricsCollection"
+ is_set "$disableUpdateSettings" && emit_bool disableUpdateSettings "$disableUpdateSettings"
+ is_set "$disableProfiles" && emit_bool disableProfiles "$disableProfiles"
+ is_set "$disableNetworks" && emit_bool disableNetworks "$disableNetworks"
+ is_set "$rosenpassEnabled" && emit_bool rosenpassEnabled "$rosenpassEnabled"
+ is_set "$rosenpassPermissive" && emit_bool rosenpassPermissive "$rosenpassPermissive"
+ is_set "$wireguardPort" && emit_int wireguardPort "$wireguardPort"
+ is_set "$splitTunnelMode" && emit_string splitTunnelMode "$splitTunnelMode"
+ is_set "$splitTunnelApps" && emit_string splitTunnelApps "$splitTunnelApps"
+
+ end_plist
+
+ if ! /usr/bin/plutil -lint "$PLIST_PATH.tmp" >/dev/null 2>&1; then
+ log "ERROR: generated plist failed plutil lint; not installing"
+ /usr/bin/plutil -lint "$PLIST_PATH.tmp" >&2 || true
+ /bin/rm -f "$PLIST_PATH.tmp"
+ exit 1
+ fi
+
+ /bin/mv -f "$PLIST_PATH.tmp" "$PLIST_PATH"
+ /usr/sbin/chown root:wheel "$PLIST_PATH"
+ /bin/chmod 644 "$PLIST_PATH"
+
+ log "policy installed; NetBird daemon will pick it up within the next 1-minute reload tick"
+
+ # Optional: kick the daemon for an immediate apply. Safe — does
+ # nothing on a host where NetBird is not yet installed.
+ /bin/launchctl kickstart -k system/io.netbird.client 2>/dev/null || true
+}
+
+main "$@"
diff --git a/docs/netbird-policy.reg b/docs/netbird-policy.reg
new file mode 100644
index 000000000..ba4402e50
Binary files /dev/null and b/docs/netbird-policy.reg differ
diff --git a/docs/netbird-policy.reg.ps1 b/docs/netbird-policy.reg.ps1
new file mode 100644
index 000000000..011d706dc
--- /dev/null
+++ b/docs/netbird-policy.reg.ps1
@@ -0,0 +1,94 @@
+#requires -Version 5.1
+<#
+.SYNOPSIS
+ Push the NetBird MDM policy to a Windows device via JumpCloud Commands
+ by importing a sidecar netbird-policy.reg file.
+
+.DESCRIPTION
+ Windows counterpart of docs/netbird-macos.sh. Outcome:
+ HKLM\Software\Policies\NetBird populated from the attached
+ netbird-policy.reg file, daemon picks up the change via the
+ 1-minute MDM reload ticker.
+
+ Deployment:
+ 1. Admin Console -> Device Management -> Commands -> +.
+ 2. Type: Windows PowerShell. Run as: SYSTEM.
+ 3. Paste this file verbatim into the command body.
+ 4. In the same command, attach `netbird-policy.reg` as a file.
+ JumpCloud copies attached files into the command's working
+ directory before invoking the script, so `$PSScriptRoot` or
+ Get-Location resolves to where the .reg lives.
+ 5. Bind to the target system group, save, run.
+
+ Producing the .reg file:
+ On a reference machine, after configuring the policy values either
+ via gpedit (GPO) or manual `reg add`, export with:
+
+ reg export "HKLM\Software\Policies\NetBird" netbird-policy.reg /y
+
+ Then attach the resulting file to the JumpCloud command.
+
+ Semantics:
+ - The script nukes the existing HKLM\Software\Policies\NetBird key
+ before importing the .reg, so the .reg is the SINGLE SOURCE OF
+ TRUTH. Any value present in the registry but absent from the .reg
+ is removed. This is what an MDM admin almost always wants.
+ - Setting the .reg to an empty (header-only) file effectively unsets
+ the policy.
+
+ Idempotency: re-running the script with the same .reg is a no-op from
+ the daemon's perspective (values identical → 1-min ticker sees no
+ diff → engine not restarted).
+
+ Exit codes: 0 = success; 1 = .reg missing or reg.exe error.
+#>
+
+$ErrorActionPreference = "Stop"
+
+$RegFileName = "netbird-policy.reg"
+$RegKey = "HKLM\Software\Policies\NetBird"
+
+# Resolve the attached .reg file: JumpCloud copies command attachments
+# into C:\Windows\Temp\ before invoking the script. Cwd / $PSScriptRoot
+# fallbacks cover the local-dev case where you might dot-source this
+# from elsewhere.
+$candidates = @(
+ (Join-Path "$env:WINDIR\Temp" $RegFileName)
+ (Join-Path (Get-Location) $RegFileName)
+ (Join-Path $PSScriptRoot $RegFileName)
+) | Where-Object { Test-Path $_ }
+
+if ($candidates.Count -eq 0) {
+ Write-Error "[netbird-mdm] $RegFileName not found in working directory or `$PSScriptRoot. Attach the file to the JumpCloud command."
+ exit 1
+}
+$regFile = $candidates[0]
+Write-Host "[netbird-mdm] using $regFile"
+
+# Wipe the existing policy key so the .reg is authoritative.
+$existed = Test-Path "Registry::HKEY_LOCAL_MACHINE\Software\Policies\NetBird"
+if ($existed) {
+ & reg.exe delete $RegKey /f | Out-Null
+ if ($LASTEXITCODE -ne 0) {
+ Write-Error "[netbird-mdm] failed to clear $RegKey before import (exit $LASTEXITCODE)"
+ exit 1
+ }
+ Write-Host "[netbird-mdm] cleared previous values under $RegKey"
+}
+
+# Import. reg.exe writes both data and (re-)creates the key if needed.
+& reg.exe import $regFile
+if ($LASTEXITCODE -ne 0) {
+ Write-Error "[netbird-mdm] reg import failed (exit $LASTEXITCODE)"
+ exit 1
+}
+
+# Audit dump so the JumpCloud per-execution log captures the applied state.
+Write-Host "[netbird-mdm] final policy state under $RegKey :"
+& reg.exe query $RegKey /s
+
+# Daemon's 1-min reload ticker picks up the change automatically.
+# Uncomment to force immediate convergence (skips the ticker wait):
+# Restart-Service netbird -Force -ErrorAction SilentlyContinue
+
+exit 0
diff --git a/docs/netbird.adml b/docs/netbird.adml
new file mode 100644
index 000000000..d49b05022
--- /dev/null
+++ b/docs/netbird.adml
@@ -0,0 +1,95 @@
+
+
+ NetBird Client Policies
+ Group Policy template for NetBird client MDM-managed settings. Values are written under HKLM\Software\Policies\NetBird and consumed by the netbird daemon at startup and every 1-minute reload tick.
+
+
+
+
+ NetBird
+ NetBird Client 0.40+
+
+
+ Management URL
+ URL of the NetBird management server. Format: https://host[:port]. When set, users cannot override this value via UI or CLI.
+
+ Pre-shared key
+ WireGuard pre-shared key used as an additional symmetric secret on every peer-to-peer tunnel. Secret value.
+
+
+ Disable auto-connect
+ When enabled, the NetBird tunnel does not auto-connect at daemon startup. Equivalent to --disable-auto-connect.
+
+ Disable client routes
+ When enabled, this client will not consume routes advertised by routing peers. Equivalent to --disable-client-routes.
+
+ Disable server routes
+ When enabled, this client will not act as a routing peer for other clients. Equivalent to --disable-server-routes.
+
+ Block inbound
+ When enabled, the client firewall blocks all inbound peer traffic on the WireGuard interface. Equivalent to --block-inbound.
+
+
+
+
+ Enable Rosenpass
+ Enables Rosenpass post-quantum key exchange on WireGuard tunnels. Both peers must support it.
+
+ Rosenpass permissive
+ When enabled, the client falls back to plain WireGuard if a peer does not support Rosenpass; otherwise it refuses the connection.
+
+ WireGuard port
+ UDP port used by the local WireGuard interface. Allowed range: 1-65535.
+
+ Split tunnel
+ Restrict the NetBird tunnel to or from a chosen list of application package names. Choose either the allow mode (only the listed apps route through NetBird) or the disallow mode (the listed apps bypass NetBird; everything else routes through). The mode is mutually exclusive — only one can be active at a time. Android-only at the daemon level; Windows/macOS/iOS clients ignore this policy.
+ Allow only listed apps (everything else bypasses)
+ Disallow listed apps (everything else routes)
+
+
+ Disable update settings
+ When enabled, blocks every configuration change from the client UI and from the CLI (netbird up / login / setconfig). The Settings view stays viewable but read-only. Equivalent to --disable-update-settings.
+
+ Disable profiles
+ When enabled, the client UI/CLI cannot list, create, switch or remove NetBird connection profiles. Equivalent to --disable-profiles.
+
+ Disable networks
+ When enabled, the client UI/CLI cannot list, select or deselect NetBird networks (the corresponding daemon RPCs return Unavailable). Equivalent to --disable-networks.
+
+ Disable metrics collection
+ When enabled, the client does not collect or report local usage metrics.
+
+
+
+
+
+
+
+ https://api.netbird.io:443
+
+
+
+
+
+
+
+
+
+
+ WireGuard UDP port:
+
+
+
+ Mode:
+
+
+
+
+
+
+
+
diff --git a/docs/netbird.admx b/docs/netbird.admx
new file mode 100644
index 000000000..2f7645d63
--- /dev/null
+++ b/docs/netbird.admx
@@ -0,0 +1,223 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ - allow
+ - disallow
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+