Skip to main content

Message format

Every message is one JSON object. A request names a command in t; include an id when you want a correlated reply (reads), omit it for fire-and-forget (HID writes).
On connect, the server sends a hello frame with the protocol version, whether auth is required, and (on remote.luau) which danger gates are enabled, so a client can verify compatibility and capability with no round-trip.

Command surface

The full surface below is served by remote.luau (protocol 1.2). Send { "t": "commands", "id": 1 } to introspect exactly what a given server exposes; commands itself is 1.2 only. The minimal remote_access.lua (protocol 1.1) serves a fixed subset of those rows, not all of them: hid.down, hid.up, hid.press, hid.type, hid.move, hid.move_to, hid.scroll, screen.pixel, screen.resolution, screen.displays, screen.capture, system.mouse, system.window, system.time, input.keys, input.is_down, input.modifiers, clipboard.get, clipboard.set, window.list, window.find, window.activate, window.move, hello, ping, auth, subscribe, unsubscribe, lua.exec. * gated off by default (see Authentication). sandboxed to the script directory and gated behind ALLOW_FILE. system.window reads the active (foreground) window; the window.* commands list, find, activate, move, and wait on windows. hid.move_smooth glides the cursor to a point over a duration server-side (one call instead of streaming hid.move frames); screen.capture_window captures a single window by handle or title without computing a region. screen.capture (protocol 1.1.0+) returns a display as a base64 PNG plus its geometry and the cursor position, and screen.displays enumerates monitors with signed virtual-desktop origins. Together they are the basis for screenshot-driven, computer-use-style automation. screen.capture takes display (1-based index from screen.displays) and region ({ x, y, w, h } in display-local coordinates). Omit display and it captures the display under the cursor, falling back to the primary one; omit region and it captures that display whole. On remote.luau it also takes max_edge, which defaults to 1280: the frame is downscaled so its long edge fits, keeping a 4K capture under the script memory limit. Pass max_edge = 0 for native resolution. remote_access.lua has no max_edge and always returns native resolution. Async commands (net.*, dialog.*, window.wait*, hid.press, hid.typewriter, hid.move_smooth, screen.capture, screen.capture_window, lua.exec) run in a coroutine on the server and reply when the underlying call completes. Always include an id for them. Screen, Window, and Registry commands depend on platform support: Screen and Window are limited or absent on Linux, Clipboard is Windows/macOS, and Registry is Windows-only. See Platforms & limits.

Authentication

Auth is off by default. The server binds 0.0.0.0, so an unauthenticated server is reachable by any LAN peer that can reach its port, not only localhost. Set a token before starting either reference server on a networked machine. To require a token:
  • remote.luau: set the Auth token field in the script’s settings panel. It is compared over SHA-256 (so timing can’t leak its length or prefix) and is redacted from ui.get/ui.get_all so an authenticated client can’t read it back.
  • remote_access.lua: set AUTH_TOKEN at the top of the script before installing.
Either way the client then sends { "t": "auth", "token": "..." } first; every non-handshake command is refused with unauthenticated until it does. Dangerous surfaces are gated off by default in remote.luau and must be enabled by editing the file constants (they are deliberately not wire-flippable): ALLOW_EXEC (shell exec, process kill), ALLOW_FILE (filesystem, TOML files), ALLOW_REGISTRY (registry writes), and ALLOW_LUA_EXEC (arbitrary Lua via lua.exec). A gated command returns disabled when its flag is off. Leave these off unless you trust every client that can reach the port.

Timing

The protocol does not guarantee RPC latency, message rate, or end-to-end input timing. Results depend on host hardware, build type, WebSocket path, script tick cadence, queue depth, active transport, and the command itself. Fire-and-forget send rate measures how quickly a client writes frames, not when Rebind processes them or when the operating system receives input. Benchmark those layers separately on the deployment you intend to use.