* feat(node-host): advertise an explicit node command allowlist Persist exact node command selection and restrict ancillary publication and hosting. Preserve the unchanged assertion baseline under the work-order stop rule; check:changed requests removing the obsolete runtime.ts count (2 to 0). * feat(plugin-sdk): session transcript catalog reader Expose bounded read-only native display pages and portable attribution through the existing runtime subpath. Keep pagination scoped to the original active transcript branch and allow an explicit bounded native cursor length. * feat(session-share): read-only OpenClaw session catalog across paired gateways Publish explicitly selected native session groups through two paired node commands. Validate the closed wire contract, reject remote profile claims, and keep receiver identity binding opt-in and display-only. * fix(gateway): show published session catalogs to view-scoped roles Let publication consent satisfy catalog read visibility for roles allowed to view others, while owner-only and unprofiled callers stay hidden. Preserve published attribution without accepting a remote local-session adoption claim. Regression tests reproduce four pre-fix failures; final validation stopped at the work-order baseline gate. * docs: session sharing across gateways Document sessions-only node setup, explicit publication groups, receiver attribution, view-scoped catalog access, and read-only limits. Add the bundled plugin inventory and generated reference entry. Live proof runbook remains outside the repository; build and rig execution are blocked by the work-order baseline restriction. * fix(session-share): preserve source storage and paired reconnects Respect configured stores through listing, paging, and revocation. Keep cold listings available and bound raw transcript reads. Prefer the established paired node credential on service restart, suppress unrelated host metrics, and refresh the approved plugin configuration docs. * refactor(gateway): separate authorized catalog reads Keep the catalog dispatcher within its owned scope and preserve post-read role checks and sender projection. Align the rebased tests with their shared setup and imports.
18 KiB
| summary | read_when | title | ||
|---|---|---|---|---|
| CLI reference for `openclaw node` (headless node host) |
|
Node |
openclaw node
Run a headless node host that connects to the Gateway WebSocket and exposes
system.run / system.which on this machine by default. Use --commands to
restrict the advertised surface, for example to read-only session sharing.
On macOS, the menu bar app already embeds this node-host runtime into its own
node connection and adds native Mac capabilities. Use openclaw node run on a
Mac only when you intentionally want a headless node without the app. Running
both creates two node identities for the same machine.
Why use a node host?
Use a node host when you want agents to run commands on other machines in your network without installing a full macOS companion app there.
Common use cases:
- Run commands on remote Linux/Windows boxes (build servers, lab machines, NAS).
- Keep exec sandboxed on the gateway, but delegate approved runs to other hosts.
- Provide a lightweight, headless execution target for automation or CI nodes.
Execution is still guarded by exec approvals and per-agent allowlists on the node host, so you can keep command access scoped and explicit.
openclaw node run can publish plugin or MCP-backed tools after it connects.
The Gateway trusts descriptors from the paired node by default, while requiring
each descriptor's command to remain in the node's approved command surface. The
agent sees each accepted descriptor as a normal plugin tool, but execution still
goes through node.invoke, so disconnecting the node removes the tool from new
agent runs. Gateway operators can disable publication with
gateway.nodes.pluginTools.enabled: false.
For declarative MCP tools, add the normal MCP server shape under
nodeHost.mcp.servers in openclaw.json on the node machine, then restart the
node host. The node declares the approval-gated mcp.tools.call.v1 command
family and publishes listed tools after connecting; changing the server list
later does not require re-pairing. See
Node-hosted MCP servers.
Browser proxy (zero-config)
Node hosts automatically advertise a browser proxy if browser.enabled is not
disabled on the node. This lets the agent use browser automation on that node
without extra configuration.
By default, the proxy exposes the node's normal browser profile surface. If you
set nodeHost.browserProxy.allowProfiles, the proxy becomes restrictive:
non-allowlisted profile targeting is rejected, and persistent profile
create/delete routes are blocked through the proxy.
Disable it on the node if needed:
{
nodeHost: {
browserProxy: {
enabled: false,
},
},
}
Run (foreground)
For one-paste onboarding, use openclaw connect. It accepts a
single-use join URL or the same setup code forms as --pair, then runs this
node-host runtime.
openclaw node run --host <gateway-host> --port 18789
Or paste a short-lived node setup link from the Control UI Devices page:
openclaw node run --pair "oc-pair://<setup-code>"
Options:
--host <host>: Gateway WebSocket host (default:127.0.0.1)--pair <code-or-url>: Read the Gateway endpoint, bootstrap token, TLS mode, and optional certificate pin from a setup code oroc-pair://URL. Explicit gateway flags override values from--pair.--port <port>: Gateway WebSocket port (default:18789)--context-path <path>: Gateway WebSocket context path (e.g./openclaw-gw). Appended to the WebSocket URL.--tls: Use TLS for the gateway connection--no-tls: Force a plaintext Gateway connection even when the local Gateway config enables TLS--tls-fingerprint <sha256>: Expected TLS certificate fingerprint (sha256)--node-id <id>: Override the client instance ID stored in shared SQLite state (does not reset pairing)--display-name <name>: Override the node display name--commands <ids>: Persist an exact comma-separated command allowlist (repeatable); advertise only available matches and their required capabilities. Disables computer use, skills, plugin tools, MCP servers, and worker hosting. Omitting the flag preserves the saved list.--all-commands: Advertise the full default command surface and forget any saved--commandsallowlist. Cannot be combined with--commands.--share-installed-apps: On macOS, advertise installed applications throughdevice.apps--no-share-installed-apps: Disable installed application sharing
Gateway auth for node host
--pair uses a 10-minute single-use bootstrap token for the first connection.
After pairing, reconnects use the durable device credential. Administrator-minted
bootstrap enrollment approves the device and its first declared command surface,
including system.run when declared. Later command, capability, or permission
expansion still requires openclaw nodes approve. Gateway command policy and
the node host's exec approvals remain separate gates.
Local exec approvals default to full with ask: "off"; configure them before
using a setup link if that access is too broad. node install --pair is
intentionally unavailable because a short-lived bearer setup link must not be
persisted in service arguments.
openclaw node run and openclaw node install resolve gateway auth from config/env (no --token/--password flags on node commands):
OPENCLAW_GATEWAY_TOKEN/OPENCLAW_GATEWAY_PASSWORDare checked first.- When reconnecting to the saved Gateway endpoint with a paired node credential, use that credential and skip config auth. An explicit environment override supplies only its own credentials.
- Otherwise, local config fallback applies:
gateway.auth.token/gateway.auth.password. - In local mode, node host intentionally does not inherit
gateway.remote.token/gateway.remote.password. - If config fallback selects an unresolved
gateway.auth.token/gateway.auth.passwordSecretRef, node auth resolution fails closed (no remote fallback masking). - In
gateway.mode=remote, remote client fields (gateway.remote.token/gateway.remote.password) are also eligible per remote precedence rules. - Node host auth resolution only honors
OPENCLAW_GATEWAY_*env vars.
The saved endpoint includes its host, port, TLS mode, and context path. Changing any of these restores normal config/env auth resolution. A node can therefore share its state directory with a local Gateway while reconnecting to a different paired Gateway, without sending the local Gateway's password on restart.
For a Gateway behind Cloudflare Access, set CF_ACCESS_CLIENT_ID and
CF_ACCESS_CLIENT_SECRET together before openclaw connect, openclaw node run, or openclaw node install. The node stores env SecretRefs under its
canonical gateway.cloudflareAccess.clientId and clientSecret connection
keys. Installed services keep the values in the managed service environment
file, not in service arguments or inline supervisor definitions. Access
credentials require HTTPS/WSS; plaintext HTTP/WS fails before SecretRef
resolution while credential-free plaintext node routes remain unchanged. See
Gateway deployments that cannot host nodes.
For a node connecting to a plaintext ws:// Gateway, loopback, private IP
literals, .local, and Tailnet *.ts.net hosts are accepted. For other
trusted private-DNS names, set OPENCLAW_ALLOW_INSECURE_PRIVATE_WS=1; without
it, node startup fails closed and asks you to use wss://, an SSH tunnel, or
Tailscale. This is a process-environment opt-in, not an openclaw.json config
key.
openclaw node install persists it into the supervised node service when it is
present in the install command environment.
Service (background)
Install a headless node host as a user service (launchd on macOS, systemd on Linux, Windows Task Scheduler on Windows).
openclaw node install --host <gateway-host> --port 18789
Options:
--host <host>: Gateway WebSocket host (default:127.0.0.1)--port <port>: Gateway WebSocket port (default:18789)--context-path <path>: Gateway WebSocket context path (e.g./openclaw-gw). Appended to the WebSocket URL.--tls: Use TLS for the gateway connection--no-tls: Force a plaintext Gateway connection even when the local Gateway config enables TLS--tls-fingerprint <sha256>: Expected TLS certificate fingerprint (sha256)--node-id <id>: Override the client instance ID stored in shared SQLite state (does not reset pairing)--display-name <name>: Override the node display name--commands <ids>: Persist the command allowlist for the installed service (repeatable), with the same restrictions asnode run.--all-commands: Advertise the full default command surface and forget any saved--commandsallowlist. Cannot be combined with--commands.--share-installed-apps: On macOS, advertise installed applications throughdevice.apps--no-share-installed-apps: Disable installed application sharing--runtime <node|bun>: Service runtime (default:node). Bun 1.4+ with WAL-reset-safenode:sqliteis an explicit opt-in; Node remains recommended.--force: Reinstall/overwrite if already installed
Set OPENCLAW_WRAPPER to an executable wrapper file to use it instead of the
selected runtime and CLI entrypoint. The wrapper receives node run and the
connection arguments; it must launch OpenClaw and forward those arguments.
If installation reports a runtime probe failure, check the executable and
working directory named in the error. For example, when switching users with
runuser, first change to a directory that the target user can read. A failed
probe does not mean that the installed Node version is unsupported; upgrade
advice is reserved for missing or unsupported runtimes.
Linux (systemd user service): Run
sudo loginctl enable-linger <user>after install. Without lingering,systemd --usertears down the node service when your last SSH session ends, so the node silently goes offline after logout.openclaw node installprints this warning when it detects lingering is disabled.
Manage the service:
openclaw node status
openclaw node start
openclaw node stop
openclaw node restart
openclaw node uninstall
Use openclaw node run for a foreground node host (no service).
To remove a saved command allowlist, run openclaw node run --all-commands
in the foreground, or reinstall the service with
openclaw node install --force --all-commands. The reset is durable; the
replacement service arguments no longer carry --commands.
Service commands accept --json for machine-readable output.
node start and node restart print install hints and exit nonzero when no
managed node service is installed; run openclaw node install first. Stopping
an absent service remains a successful no-op.
The node host retries Gateway restart and network closes in-process. If the Gateway reports a terminal token/password/bootstrap auth pause, the node host logs the close detail and exits non-zero so launchd/systemd/Task Scheduler can restart it with fresh config and credentials. Pairing-required pauses stay in the foreground flow so the pending request can be approved.
Pairing
The first connection creates a pending device pairing request (role: node) on the Gateway.
When the Gateway host can SSH to the node host non-interactively (same user,
trusted host key), the pending request is approved automatically: the Gateway
runs openclaw node identity --json on the node host over SSH and approves on
an exact device-key match. This is on by default; see
SSH-verified device auto-approval
for requirements and how to disable it (gateway.nodes.pairing.sshVerify: false).
Otherwise approve manually via:
openclaw devices list
openclaw devices approve <deviceRequestId>
Device approval admits the connection, not its command surface. Restart an
installed node with openclaw node restart, or stop and rerun the foreground
openclaw node run command. A node paused on PAIRING_REQUIRED does not resume
automatically after manual approval. This reconnect creates a separate
command-surface request on the Gateway:
openclaw nodes pending
openclaw nodes approve <nodeRequestId>
openclaw nodes describe --node <idOrNameOrIp>
The device and node request IDs are distinct. An initial unapproved surface has no effective commands. SSH-verified and bootstrap enrollment can approve the first surface automatically; later expansions require approval. Previously approved commands that remain declared and allowed stay effective while an expansion waits.
Inspect the local node identity the Gateway verifies against:
openclaw node identity --json
It prints the device ID and public key from the primary row in
state/openclaw.sqlite and never creates the database or a new identity.
On tightly controlled node networks, the Gateway operator can explicitly opt in to auto-approving first-time node pairing from trusted CIDRs:
{
gateway: {
nodes: {
pairing: {
autoApproveCidrs: ["192.168.1.0/24"],
},
},
},
}
This is disabled by default (autoApproveCidrs is unset). It only applies to
fresh role: node pairing with no requested scopes, from a client IP the
Gateway trusts. Operator/browser clients, Control UI, WebChat, and role,
scope, metadata, or public-key upgrades still require manual approval.
Trusted-network device approval does not approve the node's command surface.
Inspect openclaw nodes pending and approve the separate surface request.
If the node retries pairing with changed auth details (role/scopes/public key),
the previous pending request is superseded and a new requestId is created.
Run openclaw devices list again before approval.
Identity and pairing state
The headless node separates its client instance ID from the signed device
identity that the Gateway uses for pairing and routing. This state lives in the
OpenClaw state directory (~/.openclaw by default, or $OPENCLAW_STATE_DIR
when set):
| State | Purpose |
|---|---|
state/openclaw.sqlite (config_machine_state, key nodeHost.config) |
Client instance ID, display name, and Gateway connection metadata. The client sends this ID as instanceId. |
state/openclaw.sqlite (device_identities, primary) |
Signed Ed25519 keypair and derived device ID. For signed connections, this device ID is the routed node ID and pairing identity. |
state/openclaw.sqlite (device_auth_tokens) |
Paired device tokens, keyed by cryptographic device ID and role. |
gatewayLocal in node.list and node.describe marks an exact match with the
primary device identity in the Gateway's state directory. Overriding --node-id
does not change it. A node with its own state directory and key is separate, even
on the same machine. Listing or describing nodes does not create identity credentials.
--node-id changes only the client instance ID in shared SQLite state. It does
not change the cryptographic device ID or clear pairing auth. Migrating a retired
node.json with openclaw doctor --fix likewise does not reset pairing. To
revoke and re-pair a node:
- On the Gateway, run
openclaw nodes remove --node <id|name|ip>. - On the node, restart the installed service with
openclaw node restart, or stop and rerun the foregroundopenclaw node runcommand. This starts the device-pairing flow. Ifopenclaw devices listdoes not show a request and the node reportsAUTH_DEVICE_TOKEN_MISMATCH, restart or rerun it once more. The rejected attempt clears the now-revoked local token; the next attempt can request pairing. - On the Gateway, run
openclaw devices list, thenopenclaw devices approve <deviceRequestId>. - Restart or rerun the node again. A client paused for pairing does not resume automatically after approval; this reconnect creates the separate command-surface request.
- On the Gateway, run
openclaw nodes pending, thenopenclaw nodes approve <nodeRequestId>.
The two request IDs are distinct. An applicable trusted-CIDR policy can auto-approve the first-time device-pairing step; command-surface approval remains a separate check.
Older OpenClaw releases stored node-host state in node.json, the signed
identity in identity/device.json, and paired auth in
identity/device-auth.json. Stop the node host and run
openclaw doctor --fix once; Doctor claims each retired source, validates it,
imports and verifies the canonical SQLite row, then removes the old file. Normal
node commands fail closed with this repair instruction while either retired file
or an interrupted Doctor claim remains. Keep state/openclaw.sqlite private;
it contains the device keypair and auth tokens.
Exec approvals
system.run is gated by local exec approvals:
$OPENCLAW_STATE_DIR/state/openclaw.sqlite#exec_approvals_config, or~/.openclaw/state/openclaw.sqlite#exec_approvals_configwhen the variable is unset- Exec approvals
openclaw approvals --node <id|name|ip>(edit from the Gateway)
For approved async node exec, OpenClaw prepares a canonical systemRunPlan
before prompting. The later approved system.run forward reuses that stored
plan, so edits to command/cwd/session fields after the approval request was
created are rejected instead of changing what the node executes.