WebTTY

WebTTY

Remote terminal access over rstream tunnels.


rstream WebTTY is the remote terminal protocol built for rstream tunnels and is available on Basic, Pro, Enterprise, and self-hosted deployments. It is intended for cases where a remote terminal must be reachable from the CLI, from the rstream Dashboard, or from a browser-based integration without depending on a conventional inbound service such as SSH.

A WebTTY server runs on the remote machine and establishes the outbound connection to the rstream edge network. The operator side can then connect through the native rstrm:// dialer, through the Dashboard, or through a published wss:// endpoint when the session is opened from a browser-based client. The protocol is built on web transports and fits naturally into the rstream tunnel model while still covering the core terminal workflows usually associated with SSH. The full operational path, including automation and a comparison with SSH, is covered in Access Remote Machines with rstream WebTTY.

Security model

WebTTY access is always mediated by the rstream edge network. In the native CLI workflow, access is granted through the authenticated rstream client context that dials the tunnel. In the published browser workflow, access is granted through a short-lived token bound to the published session. In both cases, the edge verifies the caller before the session reaches the remote machine.

This distinction matters because a published WebTTY server is not equivalent to a published TCP service such as SSH. Publication creates a browser-capable wss:// entrypoint on the edge, but it does not make the remote terminal openly reachable without authentication. The private form removes that browser entrypoint entirely and keeps the server on the native rstream path only.

The execution model is also straightforward. rstream webtty server is the long-running server process and the terminal sessions run in that process context. By default, commands execute as the operating-system user that launched the server. The client can request a different target user, working directory, or environment when that is part of the operational contract of the machine and permitted by the host.

WebTTY servers advertise their machine-readable capabilities through tunnel labels. Current servers expose rstream.webtty.capabilities=exec and rstream.webtty.exec.path=/. When the filesystem sidecar is enabled, they also expose rstream.webtty.fs.path=/fs and rstream.webtty.fs.mode=read-write or read-only. Agents and dashboards should read those labels instead of assuming that every WebTTY server has the same feature set.

Server modes

The local validation form listens on a regular WebSocket address and does not create a tunnel:

rstream webtty server --listen 127.0.0.1:8080
rstream webtty client --url ws://127.0.0.1:8080

When started with --rstream, the server is exposed through an rstream tunnel. The default form is published and is the one used by the Dashboard:

rstream webtty server --rstream -v --name homelab-macmini

This published form creates the HTTP tunnel shape required by browser sessions and attaches the standard WebTTY labels. It is the form used by the Dashboard and by custom browser-based clients.

The same server can also stay private:

rstream webtty server --rstream -v --name homelab-macmini --no-publish

In private mode there is no published browser entrypoint. The server remains reachable through the native rstream dialer, which keeps the CLI workflow unchanged while removing the public surface entirely.

Discovery and operator workflows

The dedicated inventory command for WebTTY servers is:

rstream webtty list

This command only returns the WebTTY servers that are currently online. It is the simplest discovery surface for operators and for scripts. Structured output is available through -o json, and the quiet form -q returns only the native dial targets.

rstream webtty list -o json
rstream webtty list -q
rstream webtty list --filter 'labels.rstream.webtty.label.role=codex' -o json

For a live operator console across WebTTY servers, tunnels, and clients, use:

rstream ui

rstream ui subscribes to the signaling API and keeps the inventory current without a manual refresh cycle. From the WebTTY view, pressing Enter opens the selected server directly inside the UI. This is the operator-facing workflow when inventory, inspection, and connection happen in the same terminal session.

When lower-level tunnel properties or label-level filtering are required, the raw tunnel inventory remains available:

rstream tunnel list --filter 'labels.application-protocol=rstream.webtty'

The Dashboard relies on that same tunnel surface to identify published WebTTY servers and open the browser session.

Native CLI connections

The WebTTY client accepts ws://, wss://, and rstrm:// URLs. For an rstream-backed server, the native CLI path is usually the most direct:

rstream webtty client --url rstrm://homelab-macmini -- printf 'hello world\n'
rstream webtty client --url rstrm://homelab-macmini

The first form runs a command and returns. The second opens an interactive terminal. The same syntax works whether the server is published or private because the CLI dials the tunnel through the local rstream configuration rather than through the published host.

For agent workflows, rstream webtty exec is the non-interactive alias. It disables TTY allocation, captures stdout and stderr, and defaults to JSON output:

rstream webtty exec --url rstrm://homelab-macmini -- uname -a

That command is intended for automation systems such as Codex that need one command result at a time rather than a live terminal.

Filesystem sidecar

WebTTY can optionally expose a WebDAV filesystem sidecar next to the terminal endpoint. It is disabled by default. Enable it by choosing an explicit root directory:

rstream webtty server --rstream --name homelab-macmini --fs-root "$HOME"

The sidecar is read-write by default because command execution can already write to the same machine. Use --fs-read-only when the filesystem view should be inspect-only:

rstream webtty server --rstream --name homelab-macmini --fs-root "$HOME" --fs-read-only

The CLI exposes the sidecar through rstream webtty fs:

rstream webtty fs ls --url rstrm://homelab-macmini /
rstream webtty fs read --url rstrm://homelab-macmini /README.md
rstream webtty fs upload --url rstrm://homelab-macmini ./local.txt /uploads/local.txt
rstream webtty fs download --url rstrm://homelab-macmini /reports/report.txt ./report.txt

The filesystem path is advertised as rstream.webtty.fs.path=/fs, so clients can discover it from inventory before attempting file operations. File paths are relative to the configured --fs-root: if the server starts with --fs-root "$HOME/project", compose.yaml is /compose.yaml, not /home/user/project/compose.yaml.

The sidecar resolves --fs-root to a filesystem boundary and rejects WebDAV paths whose symlinks resolve outside that root. It is still not a sandbox: operations run with the permissions of the WebTTY server process.

Published browser and WSS access

When a WebTTY server is published, two browser-facing paths become possible. The first is the rstream Dashboard, which resolves the published endpoint, obtains the required token, and opens the terminal session in the browser. The second is a browser-based integration built outside the Dashboard, using the published wss:// endpoint and a compatible WebTTY client.

That same transport can also be used directly from the CLI when a URL-based flow is required.

The published host can be resolved from tunnel inventory:

rstream tunnel list --filter 'name=homelab-macmini,labels.application-protocol=rstream.webtty' -o json

The direct wss:// form then becomes:

rstream webtty client --url 'wss://<published-host>?rstream.token=<token>'

Published WebTTY always requires that token on the direct wss:// path. It must authorize tunnels.streams.create-delete and carry a tunnel connect resource for the selected WebTTY server. The Dashboard obtains a short-lived WebTTY token from the Control plane API and injects it before opening the browser session. Direct CLI or SDK use of the published endpoint requires the same token to be supplied explicitly. The CLI and SDK token workflow is documented separately in Tokens and CLI Workflow.

For browser integrations, mint that token server-side for the selected tunnel shortly before opening the terminal, then attach it to the wss:// URL.

Advanced session controls

The client exposes the main session controls that usually matter in terminal operations. --interactive and --no-interactive force the execution mode explicitly. --tty and --no-tty do the same for TTY allocation. These controls are most useful when WebTTY is embedded into a broader operational toolchain and the session mode must remain predictable.

The remote process can also be shaped through environment variables, a working directory, and a target user:

rstream webtty client \
  --url rstrm://homelab-macmini \
  -e APP_ENV=prod \
  -e TERM=xterm-256color \
  -w /srv/app \
  -u admin \
  -- sh -lc 'printf "%s|%s|%s\n" "$PWD" "$APP_ENV" "$(id -un)"'

On the server side, the operational flags are centered on exposure and process lifecycle. --listen keeps the server local. --rstream exposes it through a tunnel. --name, --publish, and --no-publish define how that tunnel is addressed and whether a browser-facing entrypoint exists. --retry, --retry-interval, and --shutdown-timeout control reconnect and shutdown behavior for long-running processes.

Running at startup

rstream webtty server is the resident process for a WebTTY machine. For a device, workstation, or server that should remain reachable after logout or reboot, run that process under the host's normal service manager instead of starting it from an interactive terminal.

On Linux, use a systemd unit. A user-scoped service keeps the process tied to the user-owned ~/.rstream context and can survive logout and reboot when linger is enabled. A system-scoped service is a better fit when the machine must expose WebTTY before any user logs in, or when the rstream config is managed under /etc or another service-account path.

On macOS, use a LaunchAgent when WebTTY should start with the user session that owns the rstream config. On Windows, use a scheduled task or service wrapper that runs under the account holding the rstream config. In containerized deployments, prefer rstream run --docker --watch with a Compose restart policy when the goal is service exposure from Docker labels rather than terminal access to the host itself.

Choose one owner for each long-running surface. WebTTY should keep command and filesystem access online. Docker labels should keep application tunnels reconciled. Application-specific gateway services, such as a private egress gateway, should own their own service unit. The rstream CLI, Dashboard, and MCP tools can then discover and operate those surfaces without becoming their process supervisor.

The step-by-step WebTTY service examples are in Access Remote Machines with rstream WebTTY. Docker label persistence is covered in Docker Labels. The private egress guide shows the system-level service pattern for a gateway process in Build a Private Residential Egress Gateway with MASQUE and rstream.

Codex and MCP

For Codex, rstream can act as a connectivity layer: Codex discovers remote WebTTY machines, runs narrow commands, reads or writes files through the sidecar, and exposes remote-local services without requiring inbound access to the target machine.

Configure the local Codex MCP server with:

rstream codex setup

This adds a local MCP server that runs rstream mcp serve. The hosted /api/mcp endpoint covers rstream discovery, OpenAPI loading, identity checks, workspace and project lookup, project creation planning, explicit project creation or checkout start, and short-lived delegated token minting with an explicit bearer token; local remote-machine operations go through the CLI MCP server because it can prepare the operator's project context from the local login token and use the private tunnel dialer.

When Codex needs network access to a service that only listens on the WebTTY host, it can call rstream_remote_expose. The tool connects through WebTTY, runs rstream forward on the remote host, and can expose HTTP, TCP, UDP, TLS, DTLS, QUIC, or a device-local MCP server. Pass the exec_path advertised by WebTTY inventory even when it is the current default /. The persistent remote runner currently expects a POSIX shell on the WebTTY host. Private exposures carry HTTP/1.x, HTTP/2, MCP, TCP, and TLS over bytestream tunnels and UDP, HTTP/3, DTLS, and QUIC over datagram tunnels. For MCP surfaces, passing mcp_path adds the discovery labels used by rstream_remote_mcp_discover, rstream_remote_mcp_tools, and rstream_remote_mcp_call.

When the agent runtime cannot start a local stdio MCP process, publish that local tool surface through rstream:

rstream mcp publish --name codex-rstream-mcp --label role=codex

The published MCP endpoint uses Streamable HTTP at /mcp, advertises application-protocol=rstream.mcp, and is token-protected at the rstream edge by default.

Published MCP follows the same HTTP tunnel token rules as other protected HTTP endpoints: automated clients send Authorization: Bearer <token>, while browser-constrained clients may use rstream.token=<token> in the URL. Scope that token to the MCP tunnel and /mcp path.