Usage
This operator workflow covers obtaining the CLI and device Agent, signing in, adding a device, connecting it, running a remote command, and opening a shell. Browser and mobile consoles, the Android Agent, pairing, and self-update are covered by the links at the end.
Replace example addresses and tokens with your own. Tokens appear only once when created; never put them in the repository.
Named identities and account grants. Global --profile <name> takes priority over REMOTE_CLI_PROFILE. Named identities store config.toml, seal private keys, root trust and grant caches in <config_base>/remote-cli/profiles/<name>/; config_base keeps the existing XDG_CONFIG_HOME, HOME/.config or Windows APPDATA convention. Names allow 1–64 ASCII letters, digits, dots, hyphens or underscores, excluding . and ... A named profile takes priority over REMOTE_CLI_CONFIG; without a profile, existing paths and REMOTE_CLI_CONFIG behavior remain unchanged. Log in separately for each profile and select it on subsequent calls.
operator set accepts an account ID or unique name; device arguments are device IDs. --restricted limits visibility to explicitly granted devices; --unrestricted restores role-based visibility. Only superadmin can change scope. Superadmin controls roles and grants; without a superadmin, admins can manage within their allowed scope, excluding themselves. operator grant list returns only grants visible to the caller. Database device grants and signed seal grants are separate checks: changing a device grant does not replace seal approve. Account creation does not support atomic scope assignment; save the one-time token from operator add, then use operator set / operator grant set as separate requests.
All operator verbs support -o text|json|xml. JSON requests use op=list|add|set|grant_list|grant_set|grant_remove. set takes operator and optional role / restricted (at least one); grant_set takes operator, device and a required boolean can_manage. grant_list optionally takes operator; grant_remove takes operator and device. Do not combine --json with a subcommand.
remote-cli --profile alice login --server https://exchange.example.com
remote-cli --profile root operator set alice --restricted
remote-cli --profile root operator set alice --role readonly
remote-cli --profile root operator grant list --operator alice -o json
remote-cli --profile root operator grant set alice lab --view
remote-cli --profile root operator grant set alice lab --manage
remote-cli --profile root operator grant remove alice lab
remote-cli --profile root operator set alice --unrestricted
remote-cli --profile root operator --json '{"op":"grant_set","operator":"alice","device":"lab","can_manage":false}' -o json
XML requests and output
Check remote-cli --help for flags supported by the installed version. --xml and --xml-file add XML input to existing --json / --json-file request entry points. The four inputs are mutually exclusive and supply the complete body: do not mix them with the same command options or subcommands. Device positional rules are unchanged. Select input and output independently; -o text|json|xml defaults to text.
remote-cli device --xml '<request><op>list</op></request>' -o xml
remote-cli exec my-device --xml-file request.xml -o xml
Leaves default to strings; elements with children default to objects. Use type="number", type="boolean", type="array" and type="null" for numbers, booleans, arrays and null. Arrays require <item> children; empty objects require type="object". Use <field name="arbitrary key">value</field> for arbitrary keys and <request type="object"/> for an empty request.
<request>
<command>echo hello</command>
<timeout_secs type="number">30</timeout_secs>
<hold type="boolean">false</hold>
<env><GREETING>hello & welcome</GREETING></env>
</request>
The output root is <response type="...">, with <field name="..." type="..."> object members and <item type="..."> array members. Types are explicit and text is escaped for XML 1.0. Runtime errors return ok=false / error with a nonzero exit code; command-line syntax errors still go to stderr.
XML output supports device list/update, operator list/add/set/grant, exec, session list/kill, ui, ubus, sensors, net, metrics, preset, audit, browser, including exec job operations. Other commands explicitly reject -o xml; they can still use XML requests with their existing output formats.
Streaming exec / sensors --live / browser session emits one complete XML document per line; parse each line separately. Exec data events contain stream / encoding=base64 / data; Base64 preserves the original bytes, and plain exec ends with an exit-status record. Interactive input to browser session remains JSON Lines. ui xml without -o xml retains raw UI hierarchy XML.
Requests are limited to 1 MiB and 64 nested levels. Duplicate fields, mixed text and children, DTDs and external entities are rejected. XML 1.0 cannot represent every control character; choose JSON when such text must be retained. Agent configuration and the wire protocol remain JSON; XML only converts CLI input and output.
Obtain remote-cli and remote-agent
Build from the repository root:
cargo build --release
The CLI is target/release/remote-cli. The same command also builds target/release/remote-agent. For devices with limited flash, use the size-optimized Agent:
cargo build -p agent --no-default-features --profile release-small
The output is target/release-small/remote-agent.
Windows, Linux, and macOS can also install either binary using the website installation scripts; see Multiplatform device deployment. The commands below assume remote-cli and remote-agent are in PATH; if you just built locally, use the target/... paths above.
For a self-hosted relay, follow Development to start exchange with DATABASE_URL, then obtain the administrator token from its startup log. For a public instance, request an operator token from its administrator.
Sign in
remote-cli login --server http://127.0.0.1:8080 --token '<运维令牌>'
Use http:// or https:// for --server. The CLI derives a WebSocket address with the same security level: https maps to wss, and http to ws.
After successful login, configuration is written to:
- Linux / macOS:
$XDG_CONFIG_HOME/remote-cli/config.toml, or~/.config/remote-cli/config.tomlwhenXDG_CONFIG_HOMEis unset. - Windows:
%APPDATA%\remote-cli\config.toml.
The file has only two keys: server and token. Set REMOTE_CLI_CONFIG to use another path. Subsequent device, exec, and shell commands read this configuration, so you do not need to supply the token each time.
Add a device
remote-cli device add lab-router --name "实验室路由"
<id> is the device ID: letters, digits, -, _, and . only, up to 64 characters. --name is optional; the display name defaults to the ID. The device token in the output is shown only once.
Start the Agent
On the machine to manage, start the device Agent with the server, device ID, and newly created device token:
remote-agent --server wss://127.0.0.1:8080 --device-id lab-router --token '<设备令牌>'
These three arguments also support environment variables: REMOTE_SERVER, REMOTE_DEVICE_ID, and REMOTE_TOKEN.
--server must begin with ws:// or wss://. When only a host is specified (for example, wss://mgmt.example.com), the Agent appends /ws/device; an explicit path is used unchanged. For a local exchange without TLS, use ws://127.0.0.1:8080.
To keep the token out of process arguments, create JSON with the keys server, device_id, and token:
{
"server": "wss://mgmt.example.com",
"device_id": "lab-router",
"token": "<设备令牌>"
}
Then run remote-agent --config /path/to/config.json. For desktop installation and autostart, see Multiplatform device deployment.
Both remote-cli and remote-agent WebSockets read proxy settings in the order HTTPS_PROXY / https_proxy → ALL_PROXY / all_proxy → HTTP_PROXY / http_proxy, and honor NO_PROXY / no_proxy. The Agent uses the same connection path in headless and --ui modes. Proxies must support HTTP CONNECT (SOCKS is unsupported); WSS advertises only http/1.1 through ALPN. Set variables in the service environment for background services; see Outbound proxy and connection keepalive.
Once the device connects:
remote-cli device list
The list shows its online status.
Run a remote command
Everything after -- is passed to the device, without further parsing by the local CLI:
remote-cli exec lab-router -- uname -a
The number of arguments after -- determines how the remote side interprets them:
- Multiple arguments are passed as argv, preserving each argument boundary, as if entered directly on the device. The CLI adds quoting where needed (PowerShell rules for Windows devices).
- One argument passes the whole string to the device shell as a command line, supporting pipes,
;, wildcards, and variables.
remote-cli exec lab-router -- sh -c 'uname -a; cat /proc/uptime' # 多个参数,第三个参数完整交给 sh -c
remote-cli exec lab-router -- 'dmesg | tail -20' # 一个参数,管道在设备上执行
With multiple arguments, literal * and $VAR passed through by the local shell (common in PowerShell) remain literal and are not expanded remotely. To expand them on the device, use a single argument, such as -- 'ls /etc/*.conf'.
exec enables hold by default: the default timeout is 3600 seconds (hard limit 86400 seconds), and an operator disconnect detaches output without killing the job. Set a time limit with --timeout, for example remote-cli exec lab-router --timeout 120 -- uname -a; --hold-timeout overrides the hold limit. Add --no-hold to cancel on operator disconnect, with a default timeout of 60 seconds. When the command returns normally, its exit code becomes the exit code of remote-cli.
Inspect and stop jobs, and release session slots
remote-cli exec-holds lab-router
remote-cli exec-hold-kill lab-router <job_id>
remote-cli exec-hold-tail lab-router <job_id>
remote-cli exec-hold-cat lab-router <job_id>
remote-cli exec-attach lab-router <job_id>
remote-cli sessions lab-router
remote-cli session-kill lab-router <session_id>
exec-holds lists exec jobs and active downloads, uploads, and directory queries, showing job_id, kind, status, age_secs, the current device-side session_id (if any), and a truncated command/path summary. exec-hold-kill terminates the process group or aborts a file transfer by job ID; tail, cat, and attach apply only to exec jobs.
sessions reads the Exchange routing table directly, showing the current count/limit and device-side session IDs. session-kill uses this device-side ID to send cancellation to the device and immediately release the Exchange slot, even without a device reply. Neither operation opens an additional device session or consumes a concurrency slot, making them useful for stuck sessions at capacity. Readonly accounts cannot use them.
An exec timeout terminates the entire process group; output draining and sending the completion message both have time limits. At the concurrency limit, Exchange first reclaims routes that have lost their operator or exceeded their deadline, then tries the new session. Exec deadlines are the actual execution limit plus 15 seconds of grace; file transfers allow 3600 seconds plus 15 seconds (the device also has a 30-second idle timeout); other sessions allow 24 hours. Before its deadline, a hold exec is not killed merely because the operator disconnects; disconnect still only detaches. Explicit session-kill or exceeding the deadline cancels the held process.
Open a shell
remote-cli shell lab-router
This is a PTY session on the device, rather than SSH. Exiting the shell ends the session.
Other entry points
- Browser console: Web console
- Mobile console: Mobile client
- Android device APK: Standalone Android APK
- QR codes, six-digit codes, and accounts: Initialization and pairing
- CLI and Agent self-update: Self-update
End-to-end encryption (sealed sessions)
remote-cli exec <设备> -- <命令> and remote-cli shell <设备> use end-to-end sealed sessions by default, without extra flags. Exchange forwards only ciphertext and cannot see commands, output, or terminal contents. Devices trust only the workspace root (the superadministrator's key, generated automatically when the superadministrator logs in) and grants signed by that root. The root client automatically issues grants to members according to their account roles. See the one-time setup in End-to-end sealed sessions.
All CLI device sessions use only end-to-end sealing, without extra flags. Missing device capability information or a failed sealed handshake never causes a switch to plaintext. Direct LAN connections and browser operations enforce the same sealing requirements.
Durable background tasks
Agents advertising tasks execute single operations or declarative DAGs on the device. Submitted work continues after the Console disconnects. Reconnect to read progress, output and local artifacts. Runtime restarts mark unfinished work interrupted; it is never replayed automatically.
Creation requires a sealed manage/owner grant. Choose dependencies, fixed conditions, ancestor JSON Pointer references, failure policy and up to five explicit retries. Retries can repeat side effects; cancellation does not roll them back. Web and mobile Consoles provide forms and JSON import/export. Desktop, Android, browser and LuCI local interfaces provide history, output, cancellation and deletion.
Start with the operation catalog: support and exclusions vary by platform. Stage files before creation. The default limits are 30 days, 1000 tasks, 64 MiB total, 8 MiB text per task, four parallel steps (two on OpenWrt), and a 30-minute step timeout configurable up to 24 hours. Old Agents do not display a task entry.
remote-cli task DEVICE operations
remote-cli task DEVICE create pipeline.json --wait
remote-cli task DEVICE list
remote-cli task DEVICE get TASK_ID
remote-cli task DEVICE logs TASK_ID --offset 0
remote-cli task DEVICE cancel TASK_ID
remote-cli task DEVICE upload ./input.bin
remote-cli task DEVICE download TASK_ID STEP.artifact ./result.json
remote-cli task DEVICE delete TASK_ID
Managed services
Task Service mode supervises native foreground commands with persistent desired state, exit restart policies, exponential backoff, start rate limits, and recovery after an Agent restart. Definitions default to stopped; stop and wait before editing or deleting. Expired or revoked authorization stops the service. Ordinary DAG runs are still never replayed after restart. Web, mobile, local desktop and Android offer management; configure LuCI services through the Console or CLI. Browser Agents do not supervise system processes.
Use command.command or command.argv with optional cwd/env, restart (never/on_failure/always), start_secs, stop_grace_secs, backoff_secs, max_backoff_secs, restart_window_secs and max_starts. Windows graceful shutdown requires stop_command; Unix defaults to SIGTERM, followed by forced tree cleanup after the grace period. Configure Agent startup at boot separately. service get returns run_id for task get/logs.
remote-cli task DEVICE service put worker.json
remote-cli task DEVICE service list
remote-cli task DEVICE service start worker
remote-cli task DEVICE service get worker
remote-cli task DEVICE service restart worker
remote-cli task DEVICE service stop worker
remote-cli task DEVICE service delete worker
remote-agent task service put worker.json