diff --git a/mdx/rehype.mjs b/mdx/rehype.mjs
index d6b85d9d..4dfa606c 100644
--- a/mdx/rehype.mjs
+++ b/mdx/rehype.mjs
@@ -165,11 +165,30 @@ function rehypeAddMDXExports(getExports) {
}
}
+// Components that render their own `h2` from `title` and `id` props. The
+// heading is not in the MDX tree, so it is added to the table of contents here.
+const sectionComponents = ['TroubleshootingStart', 'TroubleshootingTiles', 'StillStuck']
+
+function jsxStringAttribute(node, name) {
+ let attribute = node.attributes?.find((a) => a.name === name)
+ return typeof attribute?.value === 'string' ? attribute.value : undefined
+}
+
function getSections(node) {
let sections = []
for (let child of node.children ?? []) {
- if (child.type === 'element' && ['h2', 'h3', 'h4', 'h5', 'h6'].includes(child.tagName)) {
+ if (child.type === 'mdxJsxFlowElement' && sectionComponents.includes(child.name)) {
+ let title = jsxStringAttribute(child, 'title')
+ let id = jsxStringAttribute(child, 'id')
+ if (title && id) {
+ sections.push(`{
+ title: ${JSON.stringify(title)},
+ id: ${JSON.stringify(id)},
+ tagName: "h2"
+ }`)
+ }
+ } else if (child.type === 'element' && ['h2', 'h3', 'h4', 'h5', 'h6'].includes(child.tagName)) {
sections.push(`{
title: ${JSON.stringify(toString(child))},
id: ${JSON.stringify(child.properties.id)},
diff --git a/src/components/StillStuck.jsx b/src/components/StillStuck.jsx
index 5eb9b145..06920e74 100644
--- a/src/components/StillStuck.jsx
+++ b/src/components/StillStuck.jsx
@@ -1,48 +1,105 @@
import { Fragment } from 'react'
+import Link from 'next/link'
import { Button } from '@/components/Button'
/**
* "Still stuck?" call-to-action banner for the bottom of the Troubleshooting
- * hub. Renders a short prompt and a row of action buttons.
+ * hub. Renders a centered title, a row of action buttons with an optional note
+ * under each, and an optional row of secondary links.
*
* @param {string} [title='Still stuck?']
- * @param {string} description
- * @param {Array<{label: string, href: string, primary?: boolean}>} actions
+ * @param {string} [id] - Optional id for the title anchor (listed in "On this page")
+ * @param {string} [description]
+ * @param {Array<{label: string, href: string, primary?: boolean, note?: string}>} actions
* @param {string} [separator] - Optional word rendered between buttons (e.g. "or"),
* so two equally-weighted actions don't read as a recommended-vs-secondary pair.
+ * @param {string} [linksLabel] - Lead-in text, on its own line above the secondary links
+ * @param {Array<{label: string, href: string}>} [links] - Secondary text links
*/
export function StillStuck({
title = 'Still stuck?',
+ id,
description,
actions = [],
separator,
+ linksLabel,
+ links = [],
}) {
+ // `not-prose` on the card would also drop the prose layout (left margin and
+ // max width), so the outer div keeps it and matches TroubleshootingTiles.
return (
-
+ )}
+ {/* Stacked in reading order on phones. From `sm` up, a two-row grid puts
+ each note under its button and keeps the separator level with the
+ buttons, not the notes. */}
+
)
diff --git a/src/pages/help/netbird-support.mdx b/src/pages/help/netbird-support.mdx
index 1e5ab522..737c2fbf 100644
--- a/src/pages/help/netbird-support.mdx
+++ b/src/pages/help/netbird-support.mdx
@@ -1,7 +1,7 @@
import { SupportBanner } from "@/components/SupportBanner"
export const description =
- "NetBird Support for (paying) Cloud customers and users, and commercial-license self-hosted deployments: reach the team with a pre-filled report."
+ "NetBird Support, mainly for paying Cloud customers and commercial-license self-hosted deployments: reach the team with a pre-filled report."
export const reportTemplate = `NetBird issue report
=====================
@@ -39,7 +39,7 @@ export const supportMailto = `mailto:support@netbird.io?subject=${encodeURICompo
diff --git a/src/pages/help/troubleshooting-account-access.mdx b/src/pages/help/troubleshooting-account-access.mdx
index bfd22280..ab52d3bb 100644
--- a/src/pages/help/troubleshooting-account-access.mdx
+++ b/src/pages/help/troubleshooting-account-access.mdx
@@ -1,11 +1,13 @@
import {Note} from "@/components/mdx";
-export const description = "Why NetBird shows you a Waiting for Approval screen, or adds you to an account you don't recognize, and how to resolve it safely."
+export const description = "Why NetBird shows you a Waiting for Approval screen, adds you to an account you don't recognize, or gives a teammate an account of their own, and how to resolve it safely."
# Pending approval and account invitations
Signed in to NetBird and landed on a **Waiting for Approval** screen, or in a NetBird account you don't recognize? This usually is not a bug. It almost always means you were added to an existing organization automatically, and someone in that organization still has to approve you.
+The opposite also happens: a teammate signs in and gets a new, empty account of their own instead of joining yours. See [A teammate got their own account](#a-teammate-got-their-own-account).
+
## What the Waiting for Approval screen tells you
After you sign in, the dashboard shows three steps: **Account Created**, **Waiting for Approval**, and **Join Account**. Your user exists, but the account requires new users to be approved before they can use it, so you are held at the second step until someone approves you.
@@ -23,7 +25,7 @@ The screen has two buttons:
NetBird can add people to an account without a manual, one-by-one invite:
-- **Indirect (domain) invites.** If an account has verified your email domain, new sign-ups with that domain join it automatically. See [Indirect user invites](/manage/team/add-users-to-your-network#indirect-user-invites).
+- **Domain matching.** On NetBird Cloud, new sign-ups join an existing account automatically when their email domain belongs to it: either the private domain the account signed up with, or another domain the account has verified as a [sign-in domain](/manage/team/sign-in-domains). See [Indirect user invites](/manage/team/add-users-to-your-network#indirect-user-invites).
- **Identity provider sync.** If your organization connects NetBird to its identity provider, being added to a synced group in the IdP provisions your user in NetBird. See [Provision users and groups from your identity provider](/manage/team/idp-sync).
- **Approval is required.** When an account turns on [user approval](/manage/team/approve-users#require-user-approval), new users stay blocked until an admin approves them. That block is the Waiting for Approval screen you see.
@@ -38,6 +40,28 @@ NetBird can add people to an account without a manual, one-by-one invite:
On self-hosted NetBird, the Waiting for Approval screen needs dashboard v2.93.0 or later, and the owner's address on it needs management v0.80.0 or later. Older dashboards show a **User Approval Pending** error page with only a **Sign Out** button instead: the steps above still apply, but after you are approved, sign out and sign in again.
+## A teammate got their own account
+
+A teammate signs in to NetBird Cloud and lands in a new, empty account, with none of your peers, users, or policies. NetBird did not match their email domain to your account, so it created a separate one for them. One of these is usually the reason:
+
+- **They use another of your business's domains.** Only the domain your account signed up with matches from the start. A country domain such as `company.co.uk`, a subsidiary, or a second brand matches only after an Owner or Admin adds and verifies it as a [sign-in domain](/manage/team/sign-in-domains).
+- **They signed in with a personal address**, such as a Gmail account. Public email domains never match an account. Ask them to sign in with their work address instead. You can't [invite them directly](/manage/team/add-users-to-your-network#direct-user-invites) now that they have their own account, so if they need to keep the personal address, contact [NetBird Support](/help/netbird-support).
+- **Your domain was not classified as private.** NetBird only groups sign-ups by domain when it classifies the domain as private. If it is unsure, it plays safe and does not group them. [NetBird Support](/help/netbird-support) can correct the classification. See [Indirect user invites](/manage/team/add-users-to-your-network#indirect-user-invites).
+
+To fix it for the next person, an Owner or Admin adds and verifies the domain under **Settings → Authentication → Sign-in Domains**, or invites the user directly. Do this before onboarding a team on another domain, not after.
+
+The **Sign-in Domains** section only appears once your account's own domain is classified as private. If you don't see it, invite the user directly or contact [NetBird Support](/help/netbird-support).
+
+To confirm the cause, ask your teammate which email address they signed in with, and check its domain against the Sign-in Domains list.
+
+Verifying a domain does not move a teammate who already has an account of their own: it only changes where new sign-ins land. For a teammate already in a separate account, contact [NetBird Support](/help/netbird-support).
+
+If your account requires user approval, a teammate who joins through domain matching is held for approval. That is the [Waiting for Approval](#what-the-waiting-for-approval-screen-tells-you) screen above, and an admin approves them as described in [What to do](#what-to-do).
+
+
+On self-hosted NetBird, domain matching and sign-in domains do not apply: by default, every user joins the same account.
+
+
Still unsure who owns the account, or think you were added to the wrong one? Reach out through [NetBird Support](/help/netbird-support) and the team can help confirm the account and its owner.
diff --git a/src/pages/help/troubleshooting.mdx b/src/pages/help/troubleshooting.mdx
index 62a9a447..2e0ab167 100644
--- a/src/pages/help/troubleshooting.mdx
+++ b/src/pages/help/troubleshooting.mdx
@@ -2,43 +2,90 @@ import { TroubleshootingTiles } from "@/components/TroubleshootingTiles"
import { StillStuck } from "@/components/StillStuck"
export const description =
- "Start here to troubleshoot NetBird. Find your issue by area and jump straight to the relevant guide."
+ "Start here to troubleshoot NetBird. See the common issues, or browse by area."
# Troubleshooting
-Something not working as expected? Pick the area closest to your problem and jump to the
-guide and section that covers it. Each section links to troubleshooting docs that live
-next to the feature they cover, so nothing here is a copy.
+Something not working as expected? Check the common issues below, or browse by area.
+
+## Common issues
+
+The problems support sees most often.
+
+
+
+| Symptom | Start here |
+|---|---|
+| The dashboard shows **Waiting for Approval** after you sign in | [Pending approval and account invitations](/help/troubleshooting-account-access) |
+| The client keeps asking you to log in again, or a peer shows `Login required` | [Peer session expiration](/manage/settings/enforce-periodic-user-authentication) |
+| A peer shows `Relayed` instead of `P2P` | [Troubleshooting relayed connections](/help/troubleshooting-relayed-connections) |
+| The tunnel works but is slow, or takes long to connect | [Troubleshooting performance](/help/troubleshooting-performance) |
+| A service behind a routing peer is unreachable, or a new resource never shows up | [Troubleshooting resource connectivity](/help/troubleshooting-resource-connectivity) |
+| A network or host firewall blocks NetBird, or traffic stops at a security product such as a SASE or SSL-inspection service | [Ports & firewalls](/about-netbird/ports-and-firewalls) On the peer itself: [Symptoms of host-based firewall issues](/about-netbird/ports-and-firewalls#symptoms-of-host-based-firewall-issues) |
+| Names do not resolve, or DNS stops working after NetBird disconnects | [DNS troubleshooting](/manage/dns/troubleshooting) |
+| A NetBird Reverse Proxy service returns `502`, times out, or its certificate is never issued | [Reverse proxy troubleshooting](/manage/reverse-proxy/troubleshooting) Certificate: [Certificate is not issued after verification](/manage/reverse-proxy/custom-domains#certificate-is-not-issued-after-verification) |
+
+
diff --git a/src/pages/selfhosted/troubleshooting.mdx b/src/pages/selfhosted/troubleshooting.mdx
index 0cd1bc5f..9b999102 100644
--- a/src/pages/selfhosted/troubleshooting.mdx
+++ b/src/pages/selfhosted/troubleshooting.mdx
@@ -41,6 +41,7 @@ This page helps with issues when self-hosting NetBird. Collect the diagnostics b