docs/gateway/pairing.md
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.Pending requests expire automatically after 5 minutes.
openclaw nodes pending
openclaw nodes approve <requestId>
openclaw nodes reject <requestId>
openclaw nodes status
openclaw nodes remove --node <id|name|ip>
openclaw nodes rename --node <id|name|ip> --name "Living Room iPad"
nodes status shows paired/connected nodes and their capabilities.
Events:
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.remove — remove a stale paired node entry.node.pair.verify — verify { nodeId, token }.Notes:
node.pair.request is idempotent per node: repeated calls return the same
pending request.node.pair.request.silent: true as a hint for auto-approval flows.node.pair.approve uses the pending request's declared commands to enforce
extra approval scopes:
operator.pairingoperator.pairing + operator.writesystem.run / system.run.prepare / system.which request:
operator.pairing + operator.admingateway.nodes.allowCommands and denyCommands) is applied.system.run allow and ask policy lives on the node in exec.approvals.node.*, not in the pairing record.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:
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.
Durable node presence updates follow the same identity boundary. The node.presence.alive event is
accepted only from authenticated node device sessions and updates pairing metadata only when the
device/node identity is already paired. Self-declared client.id values are not enough to write
last-seen state.
The macOS app can optionally attempt a silent approval when:
silent, andIf silent approval fails, it falls back to the normal “Approve/Reject” prompt.
WS device pairing for role: node remains manual by default. For private
node networks where the Gateway already trusts the network path, operators can
opt in with explicit CIDRs or exact IPs:
{
gateway: {
nodes: {
pairing: {
autoApproveCidrs: ["192.168.1.0/24"],
},
},
},
}
Security boundary:
gateway.nodes.pairing.autoApproveCidrs is unset.role: node device pairing with no requested scopes is eligible.When an already paired device reconnects with only non-sensitive metadata
changes (for example, display name or client platform hints), OpenClaw treats
that as a metadata-upgrade. Silent auto-approval is narrow: it applies only
to trusted non-browser local reconnects that already proved possession of local
or shared credentials, including same-host native app reconnects after OS
version metadata changes. 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.
Pairing state is stored under the Gateway state directory (default ~/.openclaw):
~/.openclaw/nodes/paired.json~/.openclaw/nodes/pending.jsonIf you override OPENCLAW_STATE_DIR, the nodes/ folder moves with it.
Security notes:
paired.json as sensitive.