On this page
On this page
Nodes and media
Node file transfers
Terminal file uploads
The Control UI can drag files into an open paired-node terminal. The native node host advertises the admin-only terminal.upload command; approve the pairing upgrade when it first appears. Each file is limited to 16 MiB, staged in a private temporary directory on that node, and returned to the terminal as a shell-quoted path without executing it.
Path insertion supports PowerShell, cmd.exe, and recognized POSIX shells (sh, Bash, Dash, Ash, Ksh, Zsh, and Fish), including Git Bash on Windows. Other shell overrides are refused because their quoting rules cannot be inferred safely; run the node host inside WSL for native WSL paths. cmd.exe paths containing % or ! are also refused because that shell expands those characters even inside double quotes.
Agent file transfers
The File Transfer plugin provides independently selectable directory-listing, fetch, and write tools. Allowing one tool does not make the others available; node-command and path policies still apply.
Tool paths preserve literal filename whitespace. On POSIX nodes, "x " and "x"
are different targets; include the trailing space when selecting the former.
The same node-command, path-policy, and approval checks apply to the exact path.
Gateway workspace files
The File Transfer plugin can connect Gateway workspace-file callers to an already paired node. Configure the agent ID, exact node ID, and the node's absolute POSIX workspace path:
Placement does not grant access. Authorize the required node commands and file
paths separately; the example grants only AGENTS.md. Gateway callers retain
their own document allowlists. Use the resolved workspace root. Document reads
and writes reject symlinks. Bootstrap reads can follow directory aliases inside
that root when the node policy permits it; final-file symlinks remain rejected.
There is no local-file fallback while the
configured node is unavailable or its workspace service is stopped.
Agents sharing a Gateway workspace must use the same node and remote root;
identical mappings share one binding, while conflicting mappings fail startup.
| Workspace operation | Node command |
|---|---|
| Read bytes and their resolved source path | file.fetch |
| Write bytes | file.write |
| List directory entries | dir.list |
| Read type, size, and modification time | file.stat |
file.stat adds no model tool. It accepts regular files and directories without
fetching contents or listing the parent, under the existing read-path policy.
For bootstrap reads, file.fetch.rootPath confines parent-alias resolution to
the resolved workspace root; it does not grant access beyond the node policy.
Unary reads and writes retain the 16 MiB transfer limit; directory reads consume
the existing dir.list pages. file.write.expectedSha256 verifies the submitted bytes, not
the previous file version. Owner-document conflict checks remain in the Gateway.
With the Gateway's node duplex transport, the mapping also registers Memory file access, workspace Skill management, and input attachment staging. Each operation still needs its command and path grants. The mapping does not launch an agent harness or move all Gateway state. Use the following example to configure both hosts.
Split-storage setup
Use this setup to keep the main agent's workspace on a persistent Linux node.
The Gateway keeps configuration, credentials, sessions, and canonical media
references. The node stores workspace documents, Memory source files, workspace
Skills, and staged input attachments. Memory indexing and search configuration
remain Gateway-owned; there is no separate remote Memory switch.
| Host | Responsibility | Example workspace path |
|---|---|---|
| Gateway | Maps the agent workspace and authorizes node access | /srv/openclaw/workspace-main |
| Paired node | Stores files and runs the workspace file services | /workspace |
These paths can differ. The Gateway path identifies the workspace binding;
remoteRoot identifies the actual node directory. This setup does not copy an
existing Gateway workspace.
Prepare and pair the node
Install matching OpenClaw builds on both hosts. The bundled File Transfer plugin
is enabled by default; the examples enable it explicitly. If either host uses
plugins.allow, add file-transfer to that host's existing list. Keep other
required plugins in the list.
Create /workspace on the node and give the node service account read and write
access. Use its resolved absolute path, with no symlink aliases. Before enabling
the Gateway mapping, populate this directory:
- For an existing agent, copy its workspace files from the current host. Preserve its instructions and Memory files, and keep a backup before switching hosts.
- For a fresh agent, create
AGENTS.mdand the other documents needed by that agent. Start from the templates in the workspace file map and customize them for the agent. Do not overwrite existing documents.
Confirm that /workspace/AGENTS.md exists and is readable and writable by the
node service account. The Gateway does not create bootstrap documents after a
remote workspace mapping is registered.
Merge this into the node's openclaw.json:
Memory and Skills requests require remoteRoot to match a configured node
agent's workspace. The node's agent ID need not match the Gateway's agent ID.
Do not copy the Gateway's file-path policy into the node configuration: the
Gateway owns that policy.
From the Gateway Control UI Devices page, create a node setup link. Run it
on the node, under the account that owns /workspace:
Keep the node running. On the Gateway, inspect pairing status. An administrator-issued setup code can approve the initial device and command surface automatically. If requests remain pending, inspect and approve them; device pairing and command-surface approval have distinct request IDs:
Use the signed node ID from this output in both places in the next example.
Do not substitute a display name or the node run --node-id client instance ID.
For an existing node, reconnect it after enabling the plugin and approve any
new command-surface request. A saved --commands restriction
still applies when you omit the flag; update it deliberately if it excludes a
required command.
Configure the Gateway
Merge this into the Gateway's openclaw.json. Preserve existing model,
channel, authentication, and plugin settings. Replace both node ID placeholders
with the same approved node ID:
The example grants read and write access to the whole node workspace, including
hidden paths. Use a dedicated workspace with no unrelated private files. The
root entry permits operations on the directory itself; /** covers its contents.
The grants cover these operations:
| Operation | Required commands | Paths covered by the example |
|---|---|---|
| Workspace documents | file.fetch, file.write, dir.list, file.stat |
Owner documents and workspace files |
| Memory file access and maintenance | workspace.memory |
Workspace root, Memory source files, and maintenance output |
| Workspace Skills | workspace.skills, file.create |
skills, .agents/skills, installation records, and source staging |
| Input attachment staging | file.create, file.stat, file.fetch |
media/inbound/openclaw-staged-* directories and their ownership markers |
To narrow the policy, retain the paths needed by each enabled operation. See
Workspace Skill installation for installation
paths. Gateway-installed bundled, managed, and plugin Skills remain on the
Gateway; this mapping does not copy them to the node. Explicit additional remote
roots need their own grants. Do not pre-create the openclaw-staged-* directories;
OpenClaw creates and checks their ownership markers.
policyVersion: 2 is required for these positive path rules. Without a matching
node policy, access is denied. ask defaults to off, and followSymlinks
defaults to false; both are explicit here. Memory and Skills need existing
path grants, so do not use ask: "always" for this setup.
The Gateway command allowlist, approved node command surface, and file-path policy are separate checks. A command deny or saved node command restriction still blocks access. See Node command policy.
Select execution separately
The storage mapping does not select a model or move shell execution. Choose the harness setup separately:
- For a remote Codex app-server, use Codex app-server policy.
Set
appServer.remoteWorkspaceRootto/workspaceand run the app-server against the same files as the paired node. WebSocket transport remains experimental and unsupported for production. The remote host needs Node.js for Codex's attachment reader. - For native worker inference, use Worker-local inference. Its device profile, model credentials, worker admission, and managed workspaces have a separate lifecycle. Selecting that profile does not create this persistent file-node mapping.
Review each tool's execution host. The mapping does not redirect arbitrary
Gateway-local filesystem or shell tools. In Codex, use
codexDynamicToolsExclude
to omit OpenClaw tools that should not be exposed. For example, exclude
gateway_exec and gateway_process when the model must not use Gateway shell
tools. Native Codex tools and other plugins retain their own permissions.
Verify the setup
Validate both configuration files with openclaw config validate, then apply
the Gateway configuration. After the command allowlist is active, reconnect the
node: restart its installed service with openclaw node restart, or stop and
run openclaw node run without --pair under the same account and state
directory to reuse its saved connection. On the Gateway, inspect
openclaw nodes pending and approve the updated command surface with
openclaw nodes approve <nodeRequestId> if requested. A Gateway configuration
reload alone cannot approve commands withheld during the earlier pairing.
Start a fresh session for the mapped agent. Check each boundary before using the setup for normal work:
- On the Gateway, run
openclaw nodes describe --node <paired-node-id>. Confirm that the node is connected and exposes all seven commands above. Check pending approvals if a command is missing or unavailable. - In the Control UI agent file editor, read and update
AGENTS.mdformain. Confirm the change in/workspace/AGENTS.mdon the node. - Put a distinctive entry in a node Memory source file. Use the agent's Memory read and search tools to check it after indexing. The existing Memory provider configuration still applies; a file mapping does not configure embeddings.
- Add a workspace Skill under
/workspace/skills/<skill-name>/SKILL.mdand confirm discovery in a new session. If you use Skill installation, also test installation and removal through the normal Gateway workflow. - Send a small input attachment to the configured harness. Confirm that the
turn can read its staged path under
/workspace/media/inbound/. The canonical media reference remains on the Gateway.
If the node disconnects or the workspace service stops, mapped workspace access
fails instead of reading a Gateway-local copy. Restore the node connection and
retry. A Workspace does not belong to this node error means the node's configured
agent workspace does not match remoteRoot. Path-policy errors require the
specific missing grant; adding a command alone cannot fix them. Do not remove
the mapping to hide an outage, because that changes which files are authoritative.
Workspace Skill installation
A paired workspace uses the native Skill installers for source publication, source records, and ClawHub update and removal. Gateway keeps installation policy and change hooks; the node keeps installed files and provenance. Local workspaces keep their existing installation path.
Allow workspace.skills and file.create, read access to the workspace, and
write access to these paths (replace /workspace with the configured root):
/workspace/skillsand/workspace/skills/**, including native replacement staging./workspace/.clawhub/lock.jsonand/workspace/.clawdhub/lock.jsonfor installation tracking and its compatibility checks./workspace/.openclaw/skill-installs/**for temporary source uploads.
The adapter opens an admitted node operation before uploading a bounded source archive. The node extracts a private copy and waits for Gateway's installation policy decision before publishing it. It retires staging when that operation ends. Source transfers retain the file plugin's archive and extraction limits; links and special files are rejected. A disconnected node fails explicitly.
ClawHub updates and removal retain native version and local-modification checks. An installed Skill is discovered through the existing workspace catalog; this does not add remote Skills to channel command menus.
Binary transfers for services
Plugin services can use the existing node channel to transfer file bytes:
file.fetchaccepts binary transfer when both hosts support it, bounded by the caller's byte limit and node policy. Existing unary calls keep their current behavior.file.createreceives bytes over that channel and publishes a complete file without replacing an existing file. It checks the admitted size and SHA-256 digest before publishing.
Both commands retain node pairing, file-path authorization, and approval checks. These transport commands do not automatically stage task attachments; the workspace adapter supplies that integration.
Transferred files
Every successful file fetch saves the bytes in the Gateway's file-transfer media
store and returns both localPath and mediaId, including for inlined text and
images. Fetched files keep a sanitized filename stem in saved copies and forwarded
attachments. The detected media type selects the extension: train.py classified
as plain text becomes train.txt. Saved copies include a unique suffix to keep
repeated fetches distinct.
When node writing is available, pass that mediaId as sourceMediaId to
reuse the saved bytes. sourceMediaId does not accept a local path or an ID from
another media store. For inline bytes, use contentBase64 instead.
Directory tools return at most 8192 UTF-8 bytes of model-visible text, including
the external-content wrapper. dir_list shows complete names, directory flags,
and sizes. To continue a text-limited listing, pass the text's nextPageToken
as pageToken with the same node and path; it resumes immediately after the last
displayed entry. The default request remains 200 entries, with a ceiling of 5000.
Full returned metadata and the original page token remain in structured details.
dir_fetch saves the whole tree and shows its local rootDir, total fileCount,
and a bounded prefix of complete relPath and size records. Combine rootDir
with a listed relPath for local follow-up operations. Omitted files remain
saved under that root and can be inspected with available local file or directory
capabilities; fetching has no pagination. Full manifest and attachment metadata
remain in structured details. If a path exceeds the text budget or would be
rewritten by security sanitization, the text reports the omission rather than
showing a partial or altered path. A listing that cannot display its first entry
explicitly reports that pagination cannot advance.
If a saved file's metadata cannot be read, dir_fetch fails instead of reporting
a successful transfer with an incomplete manifest.
Directory fetch policy checks the source-tree descendants and then the archive
member identities admitted by the same bounded parser and policy planner used
for extraction. It does not use human-readable tar listings: admitted Unicode
and newline names retain their exact spelling, and producer-added AppleDouble
files are checked rather than hidden. Parent paths are checked even when the
archive omits directory headers. The 5000-descendant cap includes those implicit
directories, counting shared parents only once. A denied path rejects the whole transfer.
Resolved source path/device/inode binding, byte-count and SHA-256 verification,
link/traversal/collision checks, and extraction limits still apply. Malformed
archive headers and destination-platform filename restrictions still reject;
filenames are not truncated or repaired to make an archive pass.