In Gateway-owned pairing, the Gateway is the source of truth for which nodes
are allowed to join. UIs (macOS app, future clients) are just frontends that
approve or reject pending requests.Important: WS nodes use device pairing (role node) during connect.
node.pair.* is a separate pairing store and does not gate the WS handshake.
Only clients that explicitly call node.pair.* use this flow.
node.pair.requested — emitted when a new pending request is created.
node.pair.resolved — emitted when a request is approved/rejected/expired.
Methods:
node.pair.request — create or reuse a pending request.
node.pair.list — list pending + paired nodes (operator.pairing).
node.pair.approve — approve a pending request (issues token).
node.pair.reject — reject a pending request.
node.pair.verify — verify { nodeId, token }.
Notes:
node.pair.request is idempotent per node: repeated calls return the same
pending request.
Repeated requests for the same pending node also refresh the stored node
metadata and the latest allowlisted declared command snapshot for operator visibility.
Approval always generates a fresh token; no token is ever returned from
node.pair.request.
Requests may include silent: true as a hint for auto-approval flows.
node.pair.approve uses the pending request’s declared commands to enforce
extra approval scopes:
Node pairing is a trust/identity flow plus token issuance.
It does not pin the live node command surface per node.
Live node commands come from what the node declares on connect after the
gateway’s global node command policy (gateway.nodes.allowCommands /
denyCommands) is applied.
Per-node system.run allow/ask policy lives on the node in
exec.approvals.node.*, not in the pairing record.
Breaking change: Starting with 2026.3.31, node commands are disabled until node pairing is approved. Device pairing alone is no longer enough to expose declared node commands.
When a node connects for the first time, pairing is requested automatically. Until the pairing request is approved, all pending node commands from that node are filtered and will not execute. Once trust is established through pairing approval, the node’s declared commands become available subject to the normal command policy.This means:
Nodes that were previously relying on device pairing alone to expose commands must now complete node pairing.
Commands queued before pairing approval are dropped, not deferred.
Breaking change: Node-originated runs now stay on a reduced trusted surface.
Node-originated summaries and related session events are restricted to the intended trusted surface. Notification-driven or node-triggered flows that previously relied on broader host or session tool access may need adjustment. This hardening ensures that node events cannot escalate into host-level tool access beyond what the node’s trust boundary permits.
When an already paired device reconnects with only non-sensitive metadata
changes (for example, display name or client platform hints), FluffBuzz treats
that as a metadata-upgrade. Silent auto-approval is narrow: it applies only
to trusted local CLI/helper reconnects that already proved possession of the
shared token or password over loopback. Browser/Control UI clients and remote
clients still use the explicit re-approval flow. Scope upgrades (read to
write/admin) and public key changes are not eligible for metadata-upgrade
auto-approval — they stay as explicit re-approval requests.
/pair qr renders the pairing payload as structured media so mobile and
browser clients can scan it directly.Deleting a device also sweeps any stale pending pairing requests for that
device id, so nodes pending does not show orphaned rows after a revoke.
Gateway pairing treats a connection as loopback only when both the raw socket
and any upstream proxy evidence agree. If a request arrives on loopback but
carries X-Forwarded-For / X-Forwarded-Host / X-Forwarded-Proto headers
that point at a non-local origin, that forwarded-header evidence disqualifies
the loopback locality claim. The pairing path then requires explicit approval
instead of silently treating the request as a same-host connect. See
Trusted Proxy Auth for the equivalent rule on
operator auth.
The transport is stateless; it does not store membership.
If the Gateway is offline or pairing is disabled, nodes cannot pair.
If the Gateway is in remote mode, pairing still happens against the remote Gateway’s store.
{"docsConfig":{"theme":"mint","$schema":"https://mintlify.com/docs.json","name":"FluffBuzz","description":"Self-hosted gateway that connects Discord, Google Chat, iMessage, Matrix, Microsoft Teams, Signal, Slack, Telegram, WhatsApp, Zalo, and more to AI coding agents. Run one Gateway process on your own machine and message your AI assistant from anywhere.","colors":{"primary":"#FF5A36","light":"#FF8A6B","dark":"#FF5A36"},"logo":{"light":"https://mintcdn.com/fluffbuzz/VO8hwd9kPKIfBD4j/assets/pixel-puppy.svg?fit=max\u0026auto=format\u0026n=VO8hwd9kPKIfBD4j\u0026q=85\u0026s=c7c49f589942c32b48525de4cc5cb3b8","dark":"https://mintcdn.com/fluffbuzz/VO8hwd9kPKIfBD4j/assets/pixel-puppy.svg?fit=max\u0026auto=format\u0026n=VO8hwd9kPKIfBD4j\u0026q=85\u0026s=c7c49f589942c32b48525de4cc5cb3b8"},"favicon":"https://mintcdn.com/fluffbuzz/VO8hwd9kPKIfBD4j/assets/pixel-puppy.svg?fit=max\u0026auto=format\u0026n=VO8hwd9kPKIfBD4j\u0026q=85\u0026s=c7c49f589942c32b48525de4cc5cb3b8","navbar":{"links":[{"icon":"github","href":"https://github.com/fluffbuzz/fluffbuzz","label":"GitHub"},{"icon":"package","href":"https://github.com/fluffbuzz/fluffbuzz/releases","label":"Releases"},{"icon":"discord","href":"https://discord.com/invite/buzz","label":"Discord"}]},"fonts":{"heading":{"family":"DM Sans"},"body":{"family":"Fragment Mono"}},"icons":{"library":"lucide"},"styling":{"codeblocks":{"theme":{"light":"min-light","dark":"min-dark"}},"latex":true},"navigation":{"languages":[{"language":"en","href":""}]}},"deployment":{"deploymentMetadataValue":{"subdomain":"fluffbuzz","hasAudioNarration":false,"actualSubdomain":"fluffbuzz","customDomains":["docs.fluffbuzz.com"],"gitSource":{"type":"github","owner":"fluffbuzz","repo":"fluffbuzz","deployBranch":"main","contentDirectory":"docs","isPrivate":true,"mountPath":"","label":""},"chroma":{"liveDeploymentHistoryId":"6a21d3e170c69447c7253106","collectionId":"ed17b4fc-264c-4fbe-86d2-09225735a998","schemaVersion":"v2"},"entitlements":{},"buildId":"6ae94919bf5f6a4758f2bb8e:success","clientVersion":"0.0.4054","searchFilterCounts":[{"language":"en","tag":"Reference","count":81},{"language":"en","tag":"Tools \u0026 Plugins","count":76},{"language":"en","tag":"Gateway \u0026 Ops","count":57},{"language":"en","tag":"Models","count":54},{"language":"en","tag":"CLI commands","count":52},{"language":"en","tag":"Providers","count":49},{"language":"en","tag":"Tools","count":38},{"language":"en","tag":"Gateway","count":36},{"language":"en","tag":"Channels","count":32},{"language":"en","tag":"Install","count":30}]},"assistantConfig":{"enableAskAiButton":false,"enableStarterQuestions":false,"enableContextualStarterQuestions":false,"enableAskAiSearchBar":true},"basePath":"","hasChatPermissions":false,"hasWebMcpPermissions":false,"toggles":[{"name":"kill-isr","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"agent-mintlify-slack","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"agent-integrations-settings","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"rbac-expansion","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"automations-pr-grouping","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"custom-prefix","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"analytics-v7","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"user-flows-analytics","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"no-publish-flow","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-onboarding","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"llms-txt-v2","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"snapshot-serving-live","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"stripTemperatureMiddleware","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"support-platform-pylon-zendesk","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"snapshot-serving","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"instant-rollback","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"granular-notifications-server","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"granular-notifications","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"automations-fable","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"fixed-ai-action-pricing","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"automation-resolve-merge-conflict","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"scoped-publishing","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"mintql","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"page-level-analytics","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"editor-version-history-v2","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"billing-page-v2","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"full-page-search","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workspace-projects","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"assistant-v5","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"byok-sep-25","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"fork-pr-previews","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"revalidation-jwt","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"vite-origin","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"billing-updated-9-15","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-sso-scim","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"hobby-abuse-scan","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"dashboard-inbox","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-signup","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"turbopuffer-dual-write","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-enabled","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"approve-review-flow","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"sso-setup-deadline-banner","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"turbopuffer-serve","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false}],"googleFontsInlined":true},"customCss":[{"filePath":"style.css","content":"#content>h1:first-of-type{display:none!important}.nav-tabs{position:relative}.nav-tabs-item>div{opacity:0}.nav-tabs-underline{position:absolute;left:0;bottom:0;height:1.5px;width:var(--nav-tab-underline-width, 0);transform:translateX(var(--nav-tab-underline-x, 0));background-color:rgb(var(--primary));border-radius:999px;pointer-events:none;opacity:0;transition:transform 260ms ease-in-out,width 260ms ease-in-out,opacity 160ms ease-in-out;will-change:transform,width}html.dark .nav-tabs-underline{background-color:rgb(var(--primary-light))}.nav-tabs-underline-ready .nav-tabs-underline{opacity:1}.showcase-actions a,.showcase-hero,.showcase-jump-links a{border:1px solid color-mix(in oklab,rgb(var(--primary)) 24%,transparent);border-radius:8px}.showcase-hero{display:grid;gap:18px;margin:8px 0 22px;padding:clamp(18px,3vw,30px);background:color-mix(in oklab,rgb(var(--primary)) 5%,transparent);box-shadow:0 18px 48px -34px rgba(0,0,0,.45)}.showcase-kicker{margin:0;font-size:12px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;opacity:.72}.showcase-lead{margin:0;max-width:48rem;font-size:clamp(18px,2vw,23px);line-height:1.6}.showcase-actions,.showcase-jump-links{display:flex;flex-wrap:wrap;gap:10px}.showcase-actions a,.showcase-jump-links a{display:inline-flex;align-items:center;justify-content:center;min-height:40px;padding:0 14px;border-bottom:1px solid color-mix(in oklab,rgb(var(--primary)) 24%,transparent);background:color-mix(in oklab,rgb(var(--primary)) 4%,transparent);text-decoration:none;transition:transform .16s ease,border-color .16s ease,background .16s ease}.showcase-actions a:first-child{background:color-mix(in oklab,rgb(var(--primary)) 12%,transparent);border-color:color-mix(in oklab,rgb(var(--primary)) 36%,transparent)}.showcase-actions a:hover,.showcase-jump-links a:hover{transform:translateY(-1px);border-color:color-mix(in oklab,rgb(var(--primary)) 46%,transparent)}.showcase-highlights{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:12px}.showcase-highlight,.showcase-video-card{border:1px solid color-mix(in oklab,rgb(var(--primary)) 18%,transparent);border-radius:8px;background:color-mix(in oklab,rgb(var(--primary)) 3%,transparent)}.showcase-highlight{padding:14px}.showcase-highlight strong{display:block;margin-bottom:6px}.showcase-highlight span,.showcase-section-intro,.showcase-video-card p{opacity:.74}.showcase-jump-links{margin:18px 0 28px}.showcase-section-intro{margin:0 0 16px}.showcase-video-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:18px;margin:0 0 28px}.showcase-video-card{padding:14px;box-shadow:0 18px 44px -32px rgba(0,0,0,.48)}.showcase-video-card h3{margin:0 0 8px}.showcase-video-card p{margin:0 0 12px}.showcase-video-card a{border-bottom:0}.showcase-video-shell{position:relative;margin-bottom:14px;padding-bottom:56.25%;overflow:hidden;border-radius:8px;background:#0a0a0a}.showcase-video-shell iframe{position:absolute;inset:0;width:100%;height:100%;border:0}@media (max-width:960px){.showcase-highlights,.showcase-video-grid{grid-template-columns:1fr}}"}],"page":{"pagePath":"gateway/pairing","slug":"/gateway/pairing","pageMetadata":{"summary":"Gateway-owned node pairing (Option B) for iOS and other remote nodes","read_when":["Implementing node pairing approvals without macOS UI","Adding CLI flows for approving remote nodes","Extending gateway protocol with node management"],"title":"Gateway-owned pairing","description":null,"href":"/gateway/pairing"},"pageType":"default","mdx":{"mdxSource":{"compiledSource":"\"use strict\";const{Fragment:_Fragment,jsx:_jsx,jsxs:_jsxs}=arguments[0];const{useMDXComponents:_provideComponents}=arguments[0];function _createMdxContent(props){const _components={a:`a`,code:`code`,li:`li`,ol:`ol`,p:`p`,pre:`pre`,span:`span`,strong:`strong`,ul:`ul`,..._provideComponents(),...props.components},{CodeBlock,Heading,Warning}=_components;if(!CodeBlock)_missingMdxReference(`CodeBlock`,true);if(!Heading)_missingMdxReference(`Heading`,true);if(!Warning)_missingMdxReference(`Warning`,true);return _jsxs(_Fragment,{children:[_jsx(Heading,{level:`1`,id:`gateway-owned-pairing-option-b`,children:`Gateway-owned pairing (Option B)`}),`\n`,_jsxs(_components.p,{children:[`In Gateway-owned pairing, the `,_jsx(_components.strong,{children:`Gateway`}),` is the source of truth for which nodes\nare allowed to join. UIs (macOS app, future clients) are just frontends that\napprove or reject pending requests.`]}),`\n`,_jsxs(_components.p,{children:[_jsx(_components.strong,{children:`Important:`}),` WS nodes use `,_jsx(_components.strong,{children:`device pairing`}),` (role `,_jsx(_components.code,{children:`node`}),`) during `,_jsx(_components.code,{children:`connect`}),`.\n`,_jsx(_components.code,{children:`node.pair.*`}),` is a separate pairing store and does `,_jsx(_components.strong,{children:`not`}),` gate the WS handshake.\nOnly clients that explicitly call `,_jsx(_components.code,{children:`node.pair.*`}),` use this flow.`]}),`\n`,_jsx(Heading,{level:`2`,id:`concepts`,children:`Concepts`}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[_jsx(_components.strong,{children:`Pending request`}),`: a node asked to join; requires approval.`]}),`\n`,_jsxs(_components.li,{children:[_jsx(_components.strong,{children:`Paired node`}),`: approved node with an issued auth token.`]}),`\n`,_jsxs(_components.li,{children:[_jsx(_components.strong,{children:`Transport`}),`: the Gateway WS endpoint forwards requests but does not decide\nmembership. (Legacy TCP bridge support has been removed.)`]}),`\n`]}),`\n`,_jsx(Heading,{level:`2`,id:`how-pairing-works`,children:`How pairing works`}),`\n`,_jsxs(_components.ol,{children:[`\n`,_jsx(_components.li,{children:`A node connects to the Gateway WS and requests pairing.`}),`\n`,_jsxs(_components.li,{children:[`The Gateway stores a `,_jsx(_components.strong,{children:`pending request`}),` and emits `,_jsx(_components.code,{children:`node.pair.requested`}),`.`]}),`\n`,_jsx(_components.li,{children:`You approve or reject the request (CLI or UI).`}),`\n`,_jsxs(_components.li,{children:[`On approval, the Gateway issues a `,_jsx(_components.strong,{children:`new token`}),` (tokens are rotated on re‑pair).`]}),`\n`,_jsx(_components.li,{children:`The node reconnects using the token and is now “paired”.`}),`\n`]}),`\n`,_jsxs(_components.p,{children:[`Pending requests expire automatically after `,_jsx(_components.strong,{children:`5 minutes`}),`.`]}),`\n`,_jsx(Heading,{level:`2`,id:`cli-workflow-headless-friendly`,children:`CLI workflow (headless friendly)`}),`\n`,_jsx(CodeBlock,{filename:``,numberOfLines:`5`,language:`shellscript`,children:_jsx(_components.pre,{className:`shiki shiki-themes min-light min-dark`,style:{backgroundColor:`#ffffff`,\"--shiki-dark-bg\":`#1f1f1f`,color:`#24292eff`,\"--shiki-dark\":`#b392f0`},language:`shellscript`,children:_jsxs(_components.code,{language:`shellscript`,numberOfLines:`5`,children:[_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#6F42C1`,\"--shiki-dark\":`#B392F0`},children:`fluffbuzz`}),_jsx(_components.span,{style:{color:`#2B5581`,\"--shiki-dark\":`#9DB1C5`},children:` nodes`}),_jsx(_components.span,{style:{color:`#2B5581`,\"--shiki-dark\":`#9DB1C5`},children:` pending`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#6F42C1`,\"--shiki-dark\":`#B392F0`},children:`fluffbuzz`}),_jsx(_components.span,{style:{color:`#2B5581`,\"--shiki-dark\":`#9DB1C5`},children:` nodes`}),_jsx(_components.span,{style:{color:`#2B5581`,\"--shiki-dark\":`#9DB1C5`},children:` approve`}),_jsx(_components.span,{style:{color:`#D32F2F`,\"--shiki-dark\":`#F97583`},children:` \u003c`}),_jsx(_components.span,{style:{color:`#2B5581`,\"--shiki-dark\":`#9DB1C5`},children:`requestI`}),_jsx(_components.span,{style:{color:`#2B5581`,\"--shiki-dark\":`#9DB1C5`},children:`d`}),_jsx(_components.span,{style:{color:`#D32F2F`,\"--shiki-dark\":`#F97583`},children:`>`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#6F42C1`,\"--shiki-dark\":`#B392F0`},children:`fluffbuzz`}),_jsx(_components.span,{style:{color:`#2B5581`,\"--shiki-dark\":`#9DB1C5`},children:` nodes`}),_jsx(_components.span,{style:{color:`#2B5581`,\"--shiki-dark\":`#9DB1C5`},children:` reject`}),_jsx(_components.span,{style:{color:`#D32F2F`,\"--shiki-dark\":`#F97583`},children:` \u003c`}),_jsx(_components.span,{style:{color:`#2B5581`,\"--shiki-dark\":`#9DB1C5`},children:`requestI`}),_jsx(_components.span,{style:{color:`#2B5581`,\"--shiki-dark\":`#9DB1C5`},children:`d`}),_jsx(_components.span,{style:{color:`#D32F2F`,\"--shiki-dark\":`#F97583`},children:`>`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#6F42C1`,\"--shiki-dark\":`#B392F0`},children:`fluffbuzz`}),_jsx(_components.span,{style:{color:`#2B5581`,\"--shiki-dark\":`#9DB1C5`},children:` nodes`}),_jsx(_components.span,{style:{color:`#2B5581`,\"--shiki-dark\":`#9DB1C5`},children:` status`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#6F42C1`,\"--shiki-dark\":`#B392F0`},children:`fluffbuzz`}),_jsx(_components.span,{style:{color:`#2B5581`,\"--shiki-dark\":`#9DB1C5`},children:` nodes`}),_jsx(_components.span,{style:{color:`#2B5581`,\"--shiki-dark\":`#9DB1C5`},children:` rename`}),_jsx(_components.span,{style:{color:`#2B5581`,\"--shiki-dark\":`#9DB1C5`},children:` --node`}),_jsx(_components.span,{style:{color:`#D32F2F`,\"--shiki-dark\":`#F97583`},children:` \u003c`}),_jsx(_components.span,{style:{color:`#2B5581`,\"--shiki-dark\":`#9DB1C5`},children:`id`}),_jsx(_components.span,{style:{color:`#D32F2F`,\"--shiki-dark\":`#F97583`},children:`|`}),_jsx(_components.span,{style:{color:`#6F42C1`,\"--shiki-dark\":`#B392F0`},children:`name`}),_jsx(_components.span,{style:{color:`#D32F2F`,\"--shiki-dark\":`#F97583`},children:`|`}),_jsx(_components.span,{style:{color:`#6F42C1`,\"--shiki-dark\":`#B392F0`},children:`ip`}),_jsx(_components.span,{style:{color:`#24292EFF`,\"--shiki-dark\":`#B392F0`},children:`> `}),_jsx(_components.span,{style:{color:`#2B5581`,\"--shiki-dark\":`#9DB1C5`},children:`--name`}),_jsx(_components.span,{style:{color:`#22863A`,\"--shiki-dark\":`#FFAB70`},children:` \"Living Room iPad\"`})]}),`\n`]})})}),`\n`,_jsxs(_components.p,{children:[_jsx(_components.code,{children:`nodes status`}),` shows paired/connected nodes and their capabilities.`]}),`\n`,_jsx(Heading,{level:`2`,id:`api-surface-gateway-protocol`,children:`API surface (gateway protocol)`}),`\n`,_jsx(_components.p,{children:`Events:`}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[_jsx(_components.code,{children:`node.pair.requested`}),` — emitted when a new pending request is created.`]}),`\n`,_jsxs(_components.li,{children:[_jsx(_components.code,{children:`node.pair.resolved`}),` — emitted when a request is approved/rejected/expired.`]}),`\n`]}),`\n`,_jsx(_components.p,{children:`Methods:`}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[_jsx(_components.code,{children:`node.pair.request`}),` — create or reuse a pending request.`]}),`\n`,_jsxs(_components.li,{children:[_jsx(_components.code,{children:`node.pair.list`}),` — list pending + paired nodes (`,_jsx(_components.code,{children:`operator.pairing`}),`).`]}),`\n`,_jsxs(_components.li,{children:[_jsx(_components.code,{children:`node.pair.approve`}),` — approve a pending request (issues token).`]}),`\n`,_jsxs(_components.li,{children:[_jsx(_components.code,{children:`node.pair.reject`}),` — reject a pending request.`]}),`\n`,_jsxs(_components.li,{children:[_jsx(_components.code,{children:`node.pair.verify`}),` — verify `,_jsx(_components.code,{children:`{ nodeId, token }`}),`.`]}),`\n`]}),`\n`,_jsx(_components.p,{children:`Notes:`}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[_jsx(_components.code,{children:`node.pair.request`}),` is idempotent per node: repeated calls return the same\npending request.`]}),`\n`,_jsx(_components.li,{children:`Repeated requests for the same pending node also refresh the stored node\nmetadata and the latest allowlisted declared command snapshot for operator visibility.`}),`\n`,_jsxs(_components.li,{children:[`Approval `,_jsx(_components.strong,{children:`always`}),` generates a fresh token; no token is ever returned from\n`,_jsx(_components.code,{children:`node.pair.request`}),`.`]}),`\n`,_jsxs(_components.li,{children:[`Requests may include `,_jsx(_components.code,{children:`silent: true`}),` as a hint for auto-approval flows.`]}),`\n`,_jsxs(_components.li,{children:[_jsx(_components.code,{children:`node.pair.approve`}),` uses the pending request’s declared commands to enforce\nextra approval scopes:`,`\n`,_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[`commandless request: `,_jsx(_components.code,{children:`operator.pairing`})]}),`\n`,_jsxs(_components.li,{children:[`non-exec command request: `,_jsx(_components.code,{children:`operator.pairing`}),` + `,_jsx(_components.code,{children:`operator.write`})]}),`\n`,_jsxs(_components.li,{children:[_jsx(_components.code,{children:`system.run`}),` / `,_jsx(_components.code,{children:`system.run.prepare`}),` / `,_jsx(_components.code,{children:`system.which`}),` request:\n`,_jsx(_components.code,{children:`operator.pairing`}),` + `,_jsx(_components.code,{children:`operator.admin`})]}),`\n`]}),`\n`]}),`\n`]}),`\n`,_jsx(_components.p,{children:`Important:`}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsx(_components.li,{children:`Node pairing is a trust/identity flow plus token issuance.`}),`\n`,_jsxs(_components.li,{children:[`It does `,_jsx(_components.strong,{children:`not`}),` pin the live node command surface per node.`]}),`\n`,_jsxs(_components.li,{children:[`Live node commands come from what the node declares on connect after the\ngateway’s global node command policy (`,_jsx(_components.code,{children:`gateway.nodes.allowCommands`}),` /\n`,_jsx(_components.code,{children:`denyCommands`}),`) is applied.`]}),`\n`,_jsxs(_components.li,{children:[`Per-node `,_jsx(_components.code,{children:`system.run`}),` allow/ask policy lives on the node in\n`,_jsx(_components.code,{children:`exec.approvals.node.*`}),`, not in the pairing record.`]}),`\n`]}),`\n`,_jsx(Heading,{level:`2`,id:`node-command-gating-2026-3-31+`,children:`Node command gating (2026.3.31+)`}),`\n`,_jsx(Warning,{children:_jsxs(_components.p,{children:[_jsx(_components.strong,{children:`Breaking change:`}),` Starting with `,_jsx(_components.code,{children:`2026.3.31`}),`, node commands are disabled until node pairing is approved. Device pairing alone is no longer enough to expose declared node commands.`]})}),`\n`,_jsx(_components.p,{children:`When a node connects for the first time, pairing is requested automatically. Until the pairing request is approved, all pending node commands from that node are filtered and will not execute. Once trust is established through pairing approval, the node’s declared commands become available subject to the normal command policy.`}),`\n`,_jsx(_components.p,{children:`This means:`}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsx(_components.li,{children:`Nodes that were previously relying on device pairing alone to expose commands must now complete node pairing.`}),`\n`,_jsx(_components.li,{children:`Commands queued before pairing approval are dropped, not deferred.`}),`\n`]}),`\n`,_jsx(Heading,{level:`2`,id:`node-event-trust-boundaries-2026-3-31+`,children:`Node event trust boundaries (2026.3.31+)`}),`\n`,_jsx(Warning,{children:_jsxs(_components.p,{children:[_jsx(_components.strong,{children:`Breaking change:`}),` Node-originated runs now stay on a reduced trusted surface.`]})}),`\n`,_jsx(_components.p,{children:`Node-originated summaries and related session events are restricted to the intended trusted surface. Notification-driven or node-triggered flows that previously relied on broader host or session tool access may need adjustment. This hardening ensures that node events cannot escalate into host-level tool access beyond what the node’s trust boundary permits.`}),`\n`,_jsx(Heading,{level:`2`,id:`auto-approval-macos-app`,children:`Auto-approval (macOS app)`}),`\n`,_jsxs(_components.p,{children:[`The macOS app can optionally attempt a `,_jsx(_components.strong,{children:`silent approval`}),` when:`]}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[`the request is marked `,_jsx(_components.code,{children:`silent`}),`, and`]}),`\n`,_jsx(_components.li,{children:`the app can verify an SSH connection to the gateway host using the same user.`}),`\n`]}),`\n`,_jsx(_components.p,{children:`If silent approval fails, it falls back to the normal “Approve/Reject” prompt.`}),`\n`,_jsx(Heading,{level:`2`,id:`metadata-upgrade-auto-approval`,children:`Metadata-upgrade auto-approval`}),`\n`,_jsxs(_components.p,{children:[`When an already paired device reconnects with only non-sensitive metadata\nchanges (for example, display name or client platform hints), FluffBuzz treats\nthat as a `,_jsx(_components.code,{children:`metadata-upgrade`}),`. Silent auto-approval is narrow: it applies only\nto trusted local CLI/helper reconnects that already proved possession of the\nshared token or password over loopback. Browser/Control UI clients and remote\nclients still use the explicit re-approval flow. Scope upgrades (read to\nwrite/admin) and public key changes are `,_jsx(_components.strong,{children:`not`}),` eligible for metadata-upgrade\nauto-approval — they stay as explicit re-approval requests.`]}),`\n`,_jsx(Heading,{level:`2`,id:`qr-pairing-helpers`,children:`QR pairing helpers`}),`\n`,_jsxs(_components.p,{children:[_jsx(_components.code,{children:`/pair qr`}),` renders the pairing payload as structured media so mobile and\nbrowser clients can scan it directly.`]}),`\n`,_jsxs(_components.p,{children:[`Deleting a device also sweeps any stale pending pairing requests for that\ndevice id, so `,_jsx(_components.code,{children:`nodes pending`}),` does not show orphaned rows after a revoke.`]}),`\n`,_jsx(Heading,{level:`2`,id:`locality-and-forwarded-headers`,children:`Locality and forwarded headers`}),`\n`,_jsxs(_components.p,{children:[`Gateway pairing treats a connection as loopback only when both the raw socket\nand any upstream proxy evidence agree. If a request arrives on loopback but\ncarries `,_jsx(_components.code,{children:`X-Forwarded-For`}),` / `,_jsx(_components.code,{children:`X-Forwarded-Host`}),` / `,_jsx(_components.code,{children:`X-Forwarded-Proto`}),` headers\nthat point at a non-local origin, that forwarded-header evidence disqualifies\nthe loopback locality claim. The pairing path then requires explicit approval\ninstead of silently treating the request as a same-host connect. See\n`,_jsx(_components.a,{href:`/gateway/trusted-proxy-auth`,children:`Trusted Proxy Auth`}),` for the equivalent rule on\noperator auth.`]}),`\n`,_jsx(Heading,{level:`2`,id:`storage-local-private`,children:`Storage (local, private)`}),`\n`,_jsxs(_components.p,{children:[`Pairing state is stored under the Gateway state directory (default `,_jsx(_components.code,{children:`~/.fluffbuzz`}),`):`]}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsx(_components.li,{children:_jsx(_components.code,{children:`~/.fluffbuzz/nodes/paired.json`})}),`\n`,_jsx(_components.li,{children:_jsx(_components.code,{children:`~/.fluffbuzz/nodes/pending.json`})}),`\n`]}),`\n`,_jsxs(_components.p,{children:[`If you override `,_jsx(_components.code,{children:`FLUFFBUZZ_STATE_DIR`}),`, the `,_jsx(_components.code,{children:`nodes/`}),` folder moves with it.`]}),`\n`,_jsx(_components.p,{children:`Security notes:`}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[`Tokens are secrets; treat `,_jsx(_components.code,{children:`paired.json`}),` as sensitive.`]}),`\n`,_jsx(_components.li,{children:`Rotating a token requires re-approval (or deleting the node entry).`}),`\n`]}),`\n`,_jsx(Heading,{level:`2`,id:`transport-behavior`,children:`Transport behavior`}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[`The transport is `,_jsx(_components.strong,{children:`stateless`}),`; it does not store membership.`]}),`\n`,_jsx(_components.li,{children:`If the Gateway is offline or pairing is disabled, nodes cannot pair.`}),`\n`,_jsx(_components.li,{children:`If the Gateway is in remote mode, pairing still happens against the remote Gateway’s store.`}),`\n`]})]})}function MDXContent(props={}){const{wrapper:MDXLayout}={..._provideComponents(),...props.components};return MDXLayout?_jsx(MDXLayout,{...props,children:_jsx(_createMdxContent,{...props})}):_createMdxContent(props)}return{default:MDXContent};function _missingMdxReference(id,component){throw new Error(`Expected `+(component?`component`:`object`)+\" `\"+id+\"` to be defined: you likely forgot to import, pass, or provide it.\")}","frontmatter":{},"scope":{"codeStyling":{"theme":{"light":"min-light","dark":"min-dark"}},"config":{},"pageMetadata":{"summary":"Gateway-owned node pairing (Option B) for iOS and other remote nodes","read_when":["Implementing node pairing approvals without macOS UI","Adding CLI flows for approving remote nodes","Extending gateway protocol with node management"],"title":"Gateway-owned pairing","description":null,"href":"/gateway/pairing"}}},"mdxSourceWithNoJs":null},"mdxExtracts":{"tableOfContents":[{"title":"Gateway-owned pairing (Option B)","slug":"gateway-owned-pairing-option-b","depth":1,"children":[]},{"title":"Concepts","slug":"concepts","depth":2,"children":[]},{"title":"How pairing works","slug":"how-pairing-works","depth":2,"children":[]},{"title":"CLI workflow (headless friendly)","slug":"cli-workflow-headless-friendly","depth":2,"children":[]},{"title":"API surface (gateway protocol)","slug":"api-surface-gateway-protocol","depth":2,"children":[]},{"title":"Node command gating (2026.3.31+)","slug":"node-command-gating-2026-3-31+","depth":2,"children":[]},{"title":"Node event trust boundaries (2026.3.31+)","slug":"node-event-trust-boundaries-2026-3-31+","depth":2,"children":[]},{"title":"Auto-approval (macOS app)","slug":"auto-approval-macos-app","depth":2,"children":[]},{"title":"Metadata-upgrade auto-approval","slug":"metadata-upgrade-auto-approval","depth":2,"children":[]},{"title":"QR pairing helpers","slug":"qr-pairing-helpers","depth":2,"children":[]},{"title":"Locality and forwarded headers","slug":"locality-and-forwarded-headers","depth":2,"children":[]},{"title":"Storage (local, private)","slug":"storage-local-private","depth":2,"children":[]},{"title":"Transport behavior","slug":"transport-behavior","depth":2,"children":[]}],"codeExamples":{}},"jsLinks":["/_mintlify/custom/fluffbuzz/27fa1e35cfee10a0/nav-tabs-underline.js"],"subdomain":"fluffbuzz","siteName":"FluffBuzz","title":"Gateway-owned pairing - FluffBuzz","navMaps":{"firstHrefInVersion":{},"firstHrefInLanguage":{"en":{"href":"/gateway/pairing","matchLevel":0}}},"lastModified":"2026-04-25T04:30:17.179Z","originalFileLocation":"gateway/pairing.md","locale":"en"},"headHtml":"\u003cscript data-mint-head>(function(){var d=document.documentElement,p=null;if(!p){try{p=localStorage.getItem(\"isDarkMode\")}catch(e){}if(p==='true')p='dark';else if(p==='false')p='light';if(p!=='light'\u0026\u0026p!=='dark'\u0026\u0026p!=='system')p=\"system\"}var r=p==='system'?(matchMedia(\"(prefers-color-scheme: dark)\").matches?'dark':'light'):p;d.classList.remove(r==='dark'?'light':'dark');d.classList.add(r);d.setAttribute(\"data-theme-preference\",p)})();\u003c/script>\u003cmeta data-mint-head name=\"application-name\" content=\"FluffBuzz\">\u003cmeta data-mint-head name=\"generator\" content=\"Mintlify\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"192x192\" href=\"/mintlify-assets/_mintlify/favicons/fluffbuzz/h_uS76i3fqM2yZ3M/_generated/favicon/android-chrome-192x192.png\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"apple-touch-icon\" type=\"image/png\" sizes=\"180x180\" href=\"/mintlify-assets/_mintlify/favicons/fluffbuzz/h_uS76i3fqM2yZ3M/_generated/favicon/apple-touch-icon.png\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"16x16\" href=\"/mintlify-assets/_mintlify/favicons/fluffbuzz/h_uS76i3fqM2yZ3M/_generated/favicon/favicon-16x16.png\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"32x32\" href=\"/mintlify-assets/_mintlify/favicons/fluffbuzz/h_uS76i3fqM2yZ3M/_generated/favicon/favicon-32x32.png\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"shortcut icon\" type=\"image/x-icon\" href=\"/mintlify-assets/_mintlify/favicons/fluffbuzz/h_uS76i3fqM2yZ3M/_generated/favicon/favicon.ico\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"192x192\" href=\"/mintlify-assets/_mintlify/favicons/fluffbuzz/h_uS76i3fqM2yZ3M/_generated/favicon-dark/android-chrome-192x192.png\" media=\"(prefers-color-scheme: dark)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"16x16\" href=\"/mintlify-assets/_mintlify/favicons/fluffbuzz/h_uS76i3fqM2yZ3M/_generated/favicon-dark/favicon-16x16.png\" media=\"(prefers-color-scheme: dark)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"32x32\" href=\"/mintlify-assets/_mintlify/favicons/fluffbuzz/h_uS76i3fqM2yZ3M/_generated/favicon-dark/favicon-32x32.png\" media=\"(prefers-color-scheme: dark)\">\u003clink data-mint-head rel=\"shortcut icon\" type=\"image/x-icon\" href=\"/mintlify-assets/_mintlify/favicons/fluffbuzz/h_uS76i3fqM2yZ3M/_generated/favicon-dark/favicon.ico\" media=\"(prefers-color-scheme: dark)\">\u003cmeta data-mint-head property=\"og:title\" content=\"Gateway-owned pairing - FluffBuzz\">\u003cmeta data-mint-head property=\"og:site_name\" content=\"FluffBuzz\">\u003cmeta data-mint-head property=\"og:url\" content=\"https://docs.fluffbuzz.com/gateway/pairing\">\u003cmeta data-mint-head property=\"og:type\" content=\"website\">\u003cmeta data-mint-head property=\"og:image\" content=\"https://fluffbuzz.mintlify.app/_mintlify/api/og/v2?division=Networking+and+discovery\u0026amp;title=Gateway-owned+pairing\u0026amp;theme=7c84968c96e6a1ff7bd83e0d\u0026amp;signature=87a18d24d7f838e344de454ed7898e6d69f0e229d57a613feb2db2b6721b336e\">\u003cmeta data-mint-head property=\"og:image:width\" content=\"1200\">\u003cmeta data-mint-head property=\"og:image:height\" content=\"630\">\u003cmeta data-mint-head name=\"twitter:card\" content=\"summary_large_image\">\u003cmeta data-mint-head name=\"twitter:title\" content=\"Gateway-owned pairing - FluffBuzz\">\u003cmeta data-mint-head name=\"twitter:image\" content=\"https://fluffbuzz.mintlify.app/_mintlify/api/og/v2?division=Networking+and+discovery\u0026amp;title=Gateway-owned+pairing\u0026amp;theme=7c84968c96e6a1ff7bd83e0d\u0026amp;signature=87a18d24d7f838e344de454ed7898e6d69f0e229d57a613feb2db2b6721b336e\">\u003cmeta data-mint-head name=\"twitter:image:width\" content=\"1200\">\u003cmeta data-mint-head name=\"twitter:image:height\" content=\"630\">\u003cmeta data-mint-head name=\"msapplication-config\" content=\"/mintlify-assets/_mintlify/favicons/fluffbuzz/h_uS76i3fqM2yZ3M/_generated/favicon/browserconfig.xml\">\u003cmeta data-mint-head name=\"apple-mobile-web-app-title\" content=\"FluffBuzz\">\u003cmeta data-mint-head name=\"msapplication-TileColor\" content=\"#FF5A36\">\u003clink data-mint-head rel=\"canonical\" href=\"https://docs.fluffbuzz.com/gateway/pairing\">\u003clink data-mint-head rel=\"alternate\" type=\"application/xml\" href=\"/sitemap.xml\">\u003clink data-mint-head rel=\"alternate\" type=\"text/markdown\" href=\"/gateway/pairing.md\">\u003cscript data-mint-head type=\"application/ld+json\">{\"@context\":\"https://schema.org\",\"@graph\":[{\"@type\":\"Organization\",\"@id\":\"https://docs.fluffbuzz.com/#organization\",\"name\":\"FluffBuzz\",\"url\":\"https://docs.fluffbuzz.com\",\"logo\":{\"@type\":\"ImageObject\",\"url\":\"https://mintcdn.com/fluffbuzz/VO8hwd9kPKIfBD4j/assets/pixel-puppy.svg?fit=max\u0026auto=format\u0026n=VO8hwd9kPKIfBD4j\u0026q=85\u0026s=c7c49f589942c32b48525de4cc5cb3b8\"}},{\"@type\":\"WebSite\",\"@id\":\"https://docs.fluffbuzz.com#website\",\"name\":\"FluffBuzz\",\"url\":\"https://docs.fluffbuzz.com\",\"publisher\":{\"@id\":\"https://docs.fluffbuzz.com/#organization\"}},{\"@type\":\"WebPage\",\"@id\":\"https://docs.fluffbuzz.com/gateway/pairing#webpage\",\"url\":\"https://docs.fluffbuzz.com/gateway/pairing\",\"name\":\"Gateway-owned pairing\",\"dateModified\":\"2026-04-25T04:30:17.179Z\",\"isPartOf\":{\"@id\":\"https://docs.fluffbuzz.com#website\"},\"breadcrumb\":{\"@id\":\"https://docs.fluffbuzz.com/gateway/pairing#breadcrumb\"}},{\"@type\":\"BreadcrumbList\",\"@id\":\"https://docs.fluffbuzz.com/gateway/pairing#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Gateway\",\"item\":\"https://docs.fluffbuzz.com/gateway/index\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Networking and discovery\",\"item\":\"https://docs.fluffbuzz.com/network\"},{\"@type\":\"ListItem\",\"position\":3,\"name\":\"Gateway-owned pairing\",\"item\":\"https://docs.fluffbuzz.com/gateway/pairing\"}]},{\"@type\":[\"Article\",\"TechArticle\"],\"@id\":\"https://docs.fluffbuzz.com/gateway/pairing#article\",\"headline\":\"Gateway-owned pairing\",\"name\":\"Gateway-owned pairing\",\"url\":\"https://docs.fluffbuzz.com/gateway/pairing\",\"mainEntityOfPage\":{\"@id\":\"https://docs.fluffbuzz.com/gateway/pairing#webpage\"},\"dateModified\":\"2026-04-25T04:30:17.179Z\",\"publisher\":{\"@id\":\"https://docs.fluffbuzz.com/#organization\"},\"isPartOf\":{\"@id\":\"https://docs.fluffbuzz.com#website\"}}]}\u003c/script>\u003cscript data-mint-head type=\"application/ld+json\">{\"@context\":\"https://schema.org\",\"@type\":\"WebSite\",\"name\":\"FluffBuzz\",\"creator\":{\"@type\":\"Organization\",\"name\":\"Mintlify\",\"url\":\"https://mintlify.com\"}}\u003c/script>","siteName":"FluffBuzz","nav":{"global":null,"languages":[{"language":"en","tabs":[{"tab":"Get started","pages":[{"title":"Get started","href":""}]},{"tab":"Install","pages":[{"title":"Install","href":"/install"}]},{"tab":"Channels","pages":[{"title":"Channels","href":"/channels"}]},{"tab":"Agents","pages":[{"title":"Agents","href":"/concepts/architecture"}]},{"tab":"Tools \u0026 Plugins","pages":[{"title":"Tools \u0026 Plugins","href":"/tools"}]},{"tab":"Models","pages":[{"title":"Models","href":"/providers"}]},{"tab":"Platforms","pages":[{"title":"Platforms","href":"/platforms"}]},{"tab":"Gateway \u0026 Ops","groups":[{"group":"Gateway","pages":[{"title":"Gateway runbook","href":"/gateway"},{"group":"Configuration and operations","pages":[{"title":"Configuration","href":"/gateway/configuration"},{"title":"Configuration reference","href":"/gateway/configuration-reference"},{"title":"Configuration examples","href":"/gateway/configuration-examples"},{"title":"Authentication","href":"/gateway/authentication"},{"title":"Auth credential semantics","href":"/auth-credential-semantics"},{"title":"Secrets management","href":"/gateway/secrets"},{"title":"Secrets apply plan contract","href":"/gateway/secrets-plan-contract"},{"title":"Trusted proxy auth","href":"/gateway/trusted-proxy-auth"},{"title":"Health checks","href":"/gateway/health"},{"title":"Diagnostics export","href":"/gateway/diagnostics"},{"title":"Heartbeat","href":"/gateway/heartbeat"},{"title":"Doctor","href":"/gateway/doctor"},{"title":"Gateway logging","href":"/gateway/logging"},{"title":"Logging overview","href":"/logging"},{"title":"Gateway lock","href":"/gateway/gateway-lock"},{"title":"Background exec and process tool","href":"/gateway/background-process"},{"title":"Multiple gateways","href":"/gateway/multiple-gateways"},{"title":"Troubleshooting","href":"/gateway/troubleshooting"}]},{"group":"Security and sandboxing","pages":[{"title":"Security","href":"/gateway/security/index"},{"title":"Security audit checks","href":"/gateway/security/audit-checks"},{"title":"Sandboxing","href":"/gateway/sandboxing"},{"title":"OpenShell","href":"/gateway/openshell"},{"title":"Sandbox vs tool policy vs elevated","href":"/gateway/sandbox-vs-tool-policy-vs-elevated"}]},{"group":"Protocols and APIs","pages":[{"title":"Gateway protocol","href":"/gateway/protocol"},{"title":"Bridge protocol","href":"/gateway/bridge-protocol"},{"title":"OpenAI chat completions","href":"/gateway/openai-http-api"},{"title":"OpenResponses API","href":"/gateway/openresponses-http-api"},{"title":"Tools invoke API","href":"/gateway/tools-invoke-http-api"},{"title":"CLI backends","href":"/gateway/cli-backends"},{"title":"Local models","href":"/gateway/local-models"}]},{"group":"Networking and discovery","pages":[{"title":"Network","href":"/network"},{"title":"Network model","description":null,"href":"/gateway/network-model"},{"title":"Gateway-owned pairing","href":"/gateway/pairing"},{"title":"Discovery and transports","description":null,"href":"/gateway/discovery"},{"title":"Bonjour discovery","href":"/gateway/bonjour"}]}]},{"group":"Remote access","pages":[{"title":"Remote access","href":"/gateway/remote"},{"title":"Remote gateway setup","href":"/gateway/remote-gateway-readme"},{"title":"Tailscale","href":"/gateway/tailscale"}]},{"group":"Security","pages":[{"title":"Formal verification (security models)","href":"/security/formal-verification"},{"title":"Threat model (MITRE ATLAS)","href":"/security/THREAT-MODEL-ATLAS"},{"title":"Contributing to the threat model","href":"/security/CONTRIBUTING-THREAT-MODEL"}]},{"group":"Nodes and media","pages":[{"title":"Nodes","href":"/nodes/index"},{"title":"Node troubleshooting","href":"/nodes/troubleshooting"},{"group":"Media capabilities","pages":[{"title":"Media understanding","href":"/nodes/media-understanding"},{"title":"Image and media support","href":"/nodes/images"},{"title":"Audio and voice notes","href":"/nodes/audio"},{"title":"Camera capture","href":"/nodes/camera"},{"title":"Text-to-speech","href":"/tools/tts"}]},{"group":"Node features","pages":[{"title":"Talk mode","href":"/nodes/talk"},{"title":"Voice wake","href":"/nodes/voicewake"},{"title":"Location command","href":"/nodes/location-command"}]}]},{"group":"Web interfaces","pages":[{"title":"Web","href":"/web/index"},{"title":"Control UI","href":"/web/control-ui"},{"title":"Dashboard","href":"/web/dashboard"},{"title":"WebChat","href":"/web/webchat"},{"title":"TUI","href":"/web/tui"}]}]},{"tab":"Reference","pages":[{"title":"Reference","href":"/cli"}]},{"tab":"Help","pages":[{"title":"Help","href":"/help"}]}]}]},"basePath":"","title":"Gateway-owned pairing - FluffBuzz","buildId":"dpl_345ReqzYaAjzS9QpeJemsKdb7KDj","deploymentId":"dpl_345ReqzYaAjzS9QpeJemsKdb7KDj","dynamics":[],"chunks":[]}