mcpField
R
rstream CLI MCP

rstream CLI MCP

本地服务
rstream.io云与基础设施11 天前更新

关于 rstream CLI MCP

rstream CLI MCP 将 rstream CLI 的设置、隧道和远程操作能力提供给 Codex 等客户端。它基于 rstream 的出站连接隧道平台,可用于本地服务暴露及受控的跨环境访问。

主要功能

  1. 01通过隧道连接本地与私有服务
  2. 02支持发布型和私有隧道
  3. 03提供 WebTTY 与远程操作
  4. 04使用浏览器流程进行 CLI 登录
  5. 05通过 doctor 输出结构化诊断

适用场景

  1. 01为测试或演示暴露本地服务
  2. 02远程访问不同环境中的设备
  3. 03运行自动化环境的配置诊断
  4. 04通过 Codex 启动并完成登录授权

MCP 相关

本地 stdio MCP 服务向 Codex 提供 rstream 操作能力。文档列出 rstream_auth_start 和 rstream_auth_poll,用于启动授权并在本地保存令牌。

配置

本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。

mcp.json · 通用配置
{
"mcpServers": {
"rstreamlabs-rstream": {
"command": "docker",
"args": [
"run",
"-i",
"--rm",
"docker.io/rstream/rstream:1.36.4"
]
}
}
}
启动命令docker run -i --rm docker.io/rstream/rstream:1.36.4

本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。

安装包
ocidocker.io/rstream/rstream:1.36.4

工具

还没有获取到工具列表

本地服务需要在你的电脑上运行后才能读取工具,维护者暂未提供工具列表。

详细介绍

rstream-go

rstream-go 是 rstream 的 Go SDK。rstream 是一个安全连接平台,基于全球分布式边缘网络和轻量级 Agent 构建。Agent 会从本地和私有环境建立仅出站隧道,而边缘网络负责验证流量、执行访问策略,并将请求路由到上游服务。rstream 支持 HTTP 和非 HTTP 工作负载,并通过连接日志和指标提供端到端可见性。

Go SDK 是参考实现。它涵盖最广泛的 rstream API,并且在协议支持和隧道生命周期功能方面最为完善。rstream CLI 使用 Go 编写,位于此存储库中,因此 SDK 和 CLI 共用相同的配置模型和操作行为。

需要原生集成?C++ SDK 位于 https://github.com/rstreamlabs/rstream-cpp。

什么是隧道?

隧道是一种安全地公开服务的方式,无需开放入站端口、使用公共 IP 或更改 NAT。在 rstream 中,隧道通过出站方式连接到边缘网络,从而降低暴露风险,同时确保访问可控且可观测。

使用 rstream 创建已发布隧道后,你会获得一个转发地址,用于将入站流量路由到本地服务。例如,针对 localhost:8080 的隧道会提供类似 https://abc123.rstream.io 的转发地址,将 HTTP 请求转发到本地端口 8080。

rstream 的工作方式

rstream 会在运行服务或设备的环境与 rstream 边缘网络之间建立出站隧道。客户端通过已发布隧道的转发地址或私有隧道的隧道标识符连接到边缘网络。边缘网络会验证连接、应用策略,并通过现有隧道路径将流量转发到上游服务。

隧道传输经过加密,边缘网络的执行决策会通过日志和指标呈现。

隧道类型

rstream 支持两种基本隧道类型:

字节流隧道(类似 TCP)可为 HTTP 和 TLS 等协议以及自定义字节流服务提供可靠、有序的传输。

数据报隧道(类似 UDP)可为 QUIC 和 DTLS 等协议以及自定义数据报服务提供低延迟、面向消息的通信。

已发布隧道与私有隧道

已发布隧道可通过转发地址供标准客户端(浏览器、curl 等)访问。根据协议和部署方式,可为已发布隧道配置边缘身份验证和访问策略。

私有隧道需要通过 rstream 客户端连接。访问私有隧道时,可通过 rstream 拨号器使用名称(如果已指定)或 ID,而非公共转发地址。

使用场景

本地开发:公开本地服务,用于测试、演示和协作,无需更改网络配置。

设备群运维:为不同环境中的设备和机器提供受控访问,并保持一致的身份、策略和可观测性。

基础设施和平台:将 rstream 用作连接层,支持内部工具、CI 工作流和生产环境访问路径。

生成式 AI 工作流:在多个执行器或机器组成的设备群中分发工作,同时确保访问范围受限且可审计。

实时系统:支持遥测、流式传输和数据报工作负载等低延迟流量模式。

支持的功能

核心隧道功能:为类 TCP 和类 UDP 工作负载创建隧道,并仅通过出站连接进行通信。

多协议支持:HTTP(1.1、2、3)、TLS、DTLS、QUIC,以及 HTTP 隧道中的 WebSocket 和 WebTransport。

访问控制:根据隧道配置,支持 IP 限制、GeoIP 策略、双向 TLS、基于令牌的访问和基于账户的访问。

运行可见性:提供流量、执行决策和性能指标的连接日志与指标。

传输配置:支持选择 IPv4/IPv6、覆盖 DNS、绑定接口、使用 HTTP CONNECT 代理、SOCKS5 代理,以及在 QUIC 传输中使用 MASQUE 代理。

弹性:支持长时间运行的 Agent、重连行为和传输层多路复用,以保持连接稳定。

控制通道的存活状态与负载的生命周期有意分开。协商的心跳宽限期可吸收短暂的网络中断。如果宽限期到期,监听器将停止接受新的流,并重新连接;而已接受的字节流和已注册的 QUIC 数据报通道仍可继续使用,直至应用关闭它们或数据路径本身结束。显式关闭隧道、控制通道、客户端或进程时,仍会关闭其拥有的会话。因此,SDK 调用方仍须正常管理每个由 Accept 返回的连接;即使 ControlChannel.Done 报告传输故障,也必须关闭该连接。

支持的协议

HTTP 协议:HTTP/1.1、HTTP/2(H2C)、HTTP/3,并支持 WebSocket 和 WebTransport。

安全传输:用于 Agent 到边缘节点连接的 TLS 和基于 QUIC 的传输;在部署启用时,也支持将 DTLS 和 QUIC 用作已发布的隧道协议。

网络选项:IPv4/IPv6、MPTCP、HTTP CONNECT、SOCKS5、用于 QUIC 传输的 MASQUE CONNECT-UDP,以及自定义 DNS 解析。

兼容性

rstream 兼容 Linux、macOS 13 或更高版本以及 Windows。此外,rstream 还支持 FreeBSD、OpenBSD 和 NetBSD 等其他 UNIX 系统,但需手动安装。

FIPS 140-3 配置文件

rstream-go 为经过审查的 Linux 客户端部署提供受限的 FIPS 140-3 构建配置文件。FIPS SDK 和 CLI 构件嵌入并使用 NIST CMVP 验证的 Go 密码模块,证书 5247, 其总体安全级别为 1。它们固定使用该模块以及经过审查的 quic-go 和 webtransport-go 版本,在获批模式下运行该模块,并在请求使用已排除的传输、协议或运行时配置时安全失败。

适用于该构件的声明为 FIPS 140-3 Inside — Go Cryptographic Module, Certificate #5247 (Overall Security Level 1)。安全级别和证书适用于嵌入式 Go 密码模块;完整的 rstream 产品尚未单独通过 CMVP 模块验证。这与标准多协议发行版是不同的构建。

此配置文件支持 TLS/mTLS、直接 QUIC、普通 HTTP/3,以及通过 WebTransport 实现的经身份验证的 E2E WebTTY。WebTTY 使用 P-256/HKDF-SHA256/AES-256-GCM 随机 nonce 协议配置;标准构建可使用此配置或旧版 X25519 配置,而 FIPS 构建仅接受前者。

请参阅 docs/010-fips-140-3-profile.md,了解支持边界、排除项、构建流程、运行时证据和模块升级流程。

安装(rstream CLI)

本节中的安装方式会安装 rstream CLI 二进制文件及其运行时依赖项,但不会将 Go SDK 作为库依赖项安装。

本地构建

要在当前平台上从此仓库本地构建 CLI,请运行:

make

构建过程会应用经过验证的临时 ICE 并发补丁。使用 SDK 的 WebRTC 文件系统传输的应用也必须自行准备构建;Go 不会继承依赖项替换。有关直接 Go 命令和采用方式,请参阅 ICE 补丁指南。

Debian/Ubuntu

对于基于 Debian 的发行版,安装程序会部署打包好的 CLI 二进制文件及其依赖项:

sudo /bin/bash -i -c "$(curl -fsSL https://rstream.io/scripts/install-debian.sh)"
macOS

在 macOS 上,Homebrew tap 提供标准的 CLI 安装方式:

brew tap rstreamlabs/rstream && brew install rstream
Windows

在 Windows 上,通过官方 WinGet Community Repository 安装 rstream:

winget install --id rstream.rstream --exact --source winget

如果无法使用 winget,或者新的稳定版本尚未进入 Community Repository,请使用 PowerShell 安装程序:

& { Invoke-Expression ([System.Text.Encoding]::UTF8.GetString((Invoke-WebRequest -Uri 'https://rstream.io/scripts/install.ps1' -UseBasicParsing).Content)) }
手动安装

对于通用环境,请使用手动安装脚本安装 CLI 二进制文件:

/bin/bash -i -c "$(curl -fsSL https://rstream.io/scripts/install.sh)"
Docker

如果在容器中运行 CLI,请拉取公共镜像:

docker pull rstream/rstream:latest

身份验证

开发者机器上的标准方式是通过浏览器登录:

rstream login

默认使用 OAuth 2.0 Device Authorization Grant。为兼容性检查,旧版 rstream 登录流程仍可通过 rstream login --auth-flow legacy 使用。

如果这是一个新的 rstream 账户,rstream login 打开的浏览器页面也可用于注册或登录,并批准 CLI 访问权限。

在执行 rstream codex setup 后,Codex 可以通过本地 MCP 启动相同的流程:rstream_auth_start 会返回批准 URL,而 rstream_auth_poll 会在本地保存获批的令牌,但不会将其返回到提示词中。只有当提供方无法将批准码嵌入 URL 时,才会单独返回该批准码。默认的 MCP 登录作用域有限;当用户明确要求 Codex 创建项目或更改项目设置时,rstream_auth_start 可以请求范围更广的 permissions 数组,托管批准页面会显示此提升后的授权范围。

有关高级身份验证模式(基于令牌的登录、远程设备流程和项目作用域上下文),请参阅 docs/001-cli-workflow.md。

运行 SDK 示例前,请确保已通过 CLI 设置项目上下文(rstream project use <project-endpoint>)。SDK 和 CLI 使用相同的配置模型和配置文件。

对于 Agent 和 CI 检查,请运行:

rstream doctor -o json

诊断输出涵盖配置、上下文、令牌声明、Control plane API 身份验证、项目解析、DNS、TLS 和引擎清单,不会打印机密信息。Go 应用可以使用 doctor.Run 获取相同的结构化诊断,而无需调用 CLI。

标准 Go CLI 和配置包还支持外部 mTLS 签名器,以适配具有特定于供应商 API 的 HSM。本地可执行文件负责提供公开身份和签名,而 rstream 负责处理 TLS;此后端也可在不使用 CGO 的情况下运行。

环境变量

这些变量在 CLI 和 SDK 配置解析中通用。常规使用时优先选择配置上下文;自动化或受限环境中则使用覆盖项。

  • RSTREAM_CONFIG:覆盖 CLI 配置文件路径。
  • RSTREAM_CONTEXT:按名称选择上下文。
  • RSTREAM_ENGINE:覆盖用于 Engine API 操作的引擎 URL。
  • RSTREAM_AUTHENTICATION_TOKEN:覆盖身份验证令牌。
  • RSTREAM_MTLS_CERT_FILE:用于 mTLS Agent 身份验证的客户端证书文件。
  • RSTREAM_MTLS_KEY_FILE:用于 mTLS Agent 身份验证的客户端私钥文件。
  • RSTREAM_API_URL:覆盖控制平面 API URL。
  • RSTREAM_REGION:为托管项目选择有权访问的区域。
  • RSTREAM_CONTROL_PLANE_HEADERS:以 JSON 对象形式添加控制平面请求标头。

解析行为遵循 config.NewClientFromEnv() 使用的相同模型:首先评估显式 SDK 选项,然后评估环境覆盖项,最后评估配置文件中的上下文/环境值。RSTREAM_CONFIG 会在回退到默认配置位置之前选择配置文件路径。令牌身份验证与 mTLS Agent 身份验证不能同时用于控制通道连接。设置 mTLS 证书和密钥变量后,该连接不会使用配置文件中的令牌;同时设置 mTLS 变量与 RSTREAM_AUTHENTICATION_TOKEN 会报错。Engine HTTP API 请求会复用令牌或证书身份;证书客户端会发现单独的 mTLS 授权机构。

区域选择需要托管项目端点,且不能与显式引擎覆盖项同时使用。控制平面标头用于额外的部署访问层。身份验证、转发和逐跳标头为保留项;网络 I/O 之前会拒绝格式错误的值和不区分大小写的重复项。

使用方法

基本 HTTP 隧道

使用此命令发布本地 HTTP 服务,并采用默认协议和发布设置。

# Create an HTTP tunnel for local port 8080 (default: HTTP protocol, published)
rstream forward 8080

此命令会创建公共 HTTP 隧道并显示转发地址(例如,https://abc123.rstream.io)。发送到此 URL 的任何 HTTP 请求都会转发到 localhost:8080。隧道会一直保持活动状态,直到你停止该命令。

如需直接共享文件,请运行 rstream files ./exports 或 rstream files ./backup.tar.zst --password。该命令包含只读浏览器 UI、WebDAV、可续传的单个文件下载和目录 ZIP。使用 --read-write 可启用 WebDAV API 上传,使用 --no-ui 可禁用内嵌页面。有关访问策略、筛选、上传限制和完整 CLI 参考,请参阅文件共享。

TLS 隧道

当暴露的隧道端点必须终止 TLS 时,请使用 --tls。

# Create a secure TLS tunnel for local port 8080
rstream forward 8080 --tls

此命令会创建一个通过 rstream 网络访问的安全 TLS 加密隧道。标准 TLS 客户端可以连接到该隧道的转发地址。

私有隧道

使用 --no-publish 创建只能通过 rstream 客户端访问的私有隧道。

# Create a private tunnel (not publicly accessible)
rstream forward 22 --tls --no-publish --name ssh-tunnel

私有隧道需要 rstream 客户端连接,并通过名称或 ID(而非公共 URL)进行标识。

WebTTY 远程终端

使用 rstream webtty server -v --rstream 通过 rstream 暴露远程 shell。在 rstream 模式下,WebTTY 默认使用已发布的 HTTP/WebSocket 隧道和标准 WebTTY 标签。添加 --no-publish 可改为创建私有隧道。

# Start a published WebTTY server over rstream
rstream webtty server -v --rstream --name shell

# Start a private WebTTY server over rstream
rstream webtty server -v --rstream --name shell --no-publish

# Create and enroll a registered WebTTY server on this machine
rstream project use project-endpoint
rstream webtty server create prod-shell --enroll

# Start an enrolled registered WebTTY server over managed rstream WebTTY
rstream webtty server -v --server-id server_id

# If the server record is created from another machine, enroll on the runtime host
rstream project use project-endpoint
rstream webtty server create prod-shell
rstream webtty server enroll server_id

# Start a WebTTY daemon from an operator-managed runtime config
rstream webtty server -v --webtty-config /etc/rstream/webtty/prod-shell.yaml

# Start a local plain WebTTY server over TLS
rstream webtty server -v --transport plain --allow-unauthenticated --tls-cert-file server.crt --tls-key-file server.key

# Start a local WebTransport WebTTY server for a browser app
rstream webtty server -v --transport webtransport --allow-unauthenticated --allowed-origin http://127.0.0.1:3000 --tls-cert-file server.crt --tls-key-file server.key

# Create and show local WebTTY endpoint identities
rstream webtty identity create --name dev-shell
rstream webtty identity show --name dev-shell --endpoint-identity
rstream webtty identity create --name client-laptop
rstream webtty identity show --name client-laptop --endpoint-identity
rstream webtty identity list

# Authorize the client public endpoint identity on the server host
rstream webtty authorized-client add client-laptop --identity dev-shell --key client_endpoint_identity
rstream webtty authorized-client list --identity dev-shell

# Start a local WebTTY server that requires E2E and client proof
rstream webtty server -v --allow-unauthenticated --identity dev-shell

# Start a server that requires an explicit OS login identity
rstream webtty server -v --execution-mode login --login-user operator

# Connect through a standard websocket endpoint
rstream webtty client --url wss://example.rstream.io/ -- whoami

# Connect through the native rstream dialer using a tunnel name or ID
rstream webtty client --url rstrm://shell -- whoami

# Execute a command with JSON output for agents and scripts
rstream webtty exec --url rstrm://shell -- uname -a

# Execute through a locally known E2E WebTTY server endpoint identity
rstream webtty known-server add dev-shell --key server_endpoint_identity --client-identity client-laptop
rstream webtty known-server list
rstream webtty exec --url rstrm://dev-shell -- whoami
rstream webtty exec --url ws://127.0.0.1:8080 --known-server dev-shell -- whoami

# Expose a WebDAV filesystem sidecar rooted at $HOME
rstream webtty server -v --rstream --name shell --fs-root "$HOME"

# Read a file through the filesystem sidecar
rstream webtty fs read --url rstrm://shell /README.md

# List the available WebTTY servers
rstream webtty list

# Inspect managed WebTTY sessions and recorded events
rstream webtty sessions list
rstream webtty sessions show session_id
rstream webtty sessions events session_id
rstream webtty sessions export session_id --format text
rstream webtty sessions export session_id --format json --file session-export.json
rstream webtty sessions participants session_id
rstream webtty sessions join session_id
rstream webtty sessions join session_id --interactive --request-control
rstream webtty sessions control-requests session_id

# List workspaces, select a project, and enroll this CLI as a trusted device
rstream workspace list
rstream project use project-endpoint
rstream workspace device enroll --label ops-cli
rstream workspace device status

# Use advertised exec_path and fs_path values even when they match the defaults

# Register local rstream MCP tools for Codex
rstream codex setup

# Also start the configured server and verify the MCP handshake and tools
rstream codex setup --verify

# From Codex MCP, expose a service that is local to the remote WebTTY host
# rstream_remote_expose webtty_url=rstrm://shell port=8765 protocol=http
# For a direct authenticated-E2E WebTTY URL, also pass known_server=shell

# From Codex MCP, expose and discover a remote MCP surface on that host
# rstream_remote_expose webtty_url=rstrm://shell port=8765 mcp_path=/mcp labels=role=robot
# rstream_remote_mcp_discover

# Expose a local dev server through an MCP-managed local tunnel
# Available to Codex through rstream_local_tunnel_expose
rstream forward 3000 --name codex-local-tunnel --label role=codex

# Publish the local rstream MCP server over an HTTP tunnel
rstream mcp publish --name codex-rstream-mcp --label role=codex

# Open the live terminal UI for clients, tunnels, and WebTTY servers
rstream ui

rstream codex setup --verify 始终会验证新启动的 MCP 进程。 已附加到打开的 Codex 任务的 MCP 进程会继续运行启动时所用版本的 CLI;替换已签名的 Homebrew 二进制文件无法替换该进程中的代码。每次升级 rstream CLI 后,请打开新的 Codex 任务或重新加载 Codex,再依赖 MCP,然后执行一次实际的工具调用。代码签名可保留二进制文件的身份和完整性,但不会重启长期运行的进程。 在 rstream ui 中,按 c 打开上下文和项目选择器。配置好的本地上下文会立即显示,包括没有 Control plane API 的未关联上下文。所选凭据允许发现项目时,当前 Control plane API 的项目会在后台加载。按 1 和 2(或 Tab)可在独立的上下文视图和项目视图之间切换。每个视图都会显示明确的 CURRENT 和 DEFAULT 列,以及所选行的详情面板。按 Enter 仅为当前 UI 进程切换,或按 d 切换并将所选上下文保存为当前 rstream 配置文件中的默认上下文。选择器还支持按 / 搜索,以及按 r 刷新远程项目。

项目发现失败不会隐藏或禁用本地上下文。显式指定 --api-url 或 RSTREAM_API_URL 后,关联上下文和远程项目会限定为该 API,而未关联上下文仍可选择。RSTREAM_ENGINE 仍是强制覆盖项:如果切换到的上下文指向不同引擎,UI 会拒绝切换。更改上下文前,请按 Ctrl+g q 关闭活动的 WebTTY 会话。

登录执行模式无需密码。--login-user <username> 指定拥有每个远程会话的现有操作系统账户,不是 rstream 账户,也不是通过 WebTTY 客户端连接的用户。例如,--login-user alice 会使用本地 alice 的主目录、shell 和身份运行命令。在 Linux 或 macOS 上运行 id -un,或在 Windows PowerShell 中运行 $env:USERNAME,即可打印要传给此选项的本地用户名。rstream 不会创建账户;如果无法解析配置的账户,服务器会拒绝启动。在类 Unix 系统上,选择其他 OS 用户会应用目标 uid、主 gid 和补充组,因此 WebTTY 服务器进程需要具备相应权限。在 Windows 上,--login-user 必须设置为运行服务器的同一 Windows 账户;不支持切换到其他账户或使用密码。

已发布的 WebTTY 隧道既可以通过其转发 wss:// 地址访问,也可以使用原生的 rstrm://<tunnel-id-or-name> 格式访问。私有 WebTTY 隧道只能通过原生的 rstrm:// 格式访问。WebTTY 服务器通过标签公布功能、执行模式和端点路径:命令执行使用 exec_path,当前默认为 /;可选的文件系统 sidecar 使用 fs_path,当前在设置 --fs-root 时默认为 /fs。文件系统路径相对于配置的根目录:如果服务器以 --fs-root "$HOME/project" 启动,则应将 compose.yaml 读取为 /compose.yaml,而不是 /home/user/project/compose.yaml。sidecar 会拒绝解析到配置根目录之外的符号链接,但它不是沙箱,仍使用 WebTTY 服务器进程的权限。文件系统 sidecar 是独立的 WebDAV 接口;启用 WebTTY E2E 负载加密时,会拒绝使用该接口。

使用 rstream webtty server -v --rstream 创建的隧道 WebTTY 服务器会使用 HTTP/WebSocket 隧道路径和标准 WebTTY 清单标签。已注册的 WebTTY 服务器通常在运行时主机上通过 rstream webtty server create <name> --enroll 创建并注册。分离式机器配置会先创建记录,然后在运行时主机上运行 rstream webtty server enroll <server-id>。生成的本地注册信息保存在 ~/.rstream/webtty/enrollments/<server-id>.yaml 下;使用 --server-id 启动时,发布由管理的 protocol=webtty 隧道。注册信息中包含 control plane 服务器 ID、项目 ID、服务器公钥指纹、本地身份文件路径和服务器加密策略。注册使用本地经过身份验证的 rstream 上下文,不使用操作员运行时配置。 实时托管式附加由引擎协调:参与者和控制请求资源使用 HTTP 控制 API,然后终端流升级为带有 Attach 握手的 WebTTY protobuf。直接 WebTTY 服务端只接受新的 Open 会话,并会以明确的协议错误拒绝托管式附加握手。CLI 实时附加会先检查引擎能力文档,然后从受信任的工作区设备或本地 WebTTY 身份授权中解析 E2E 解密材料,再打开终端流。

当守护进程应从 YAML 配置,而不是一长串标志时,请使用 --webtty-config 或 RSTREAM_WEBTTY_CONFIG。此运行时配置由运维人员管理,可能包含 server.serverId;如果包含,CLI 会在发布托管式 WebTTY 前加载该服务器 ID 对应的生成注册信息:

version: 1
server:
  serverId: srv_prod_shell_01
  transport: websocket
  executionMode: login
  loginUser: operator
  labels:
    env: production
    role: bastion

CLI 标志会覆盖 --webtty-config 中的值,而 --server-id 表示启用 rstream 托管模式。只有当生成的注册信息存放在默认路径 ~/.rstream/webtty/enrollments/<server-id>.yaml 之外时,才能在运行时配置中使用 server.serverEnrollment。

WebTTY E2E 会保留可见的 protobuf 会话信封,同时加密 stdin/stdout/stderr 载荷字节。服务器身份是本地主机身份,默认存储在 ~/.rstream/webtty/identities/*.identity.json 下,文件模式为 0600。工作区密钥是独立的产品密钥,应存放在工作区密钥层级下,而不是 WebTTY 身份目录中。显式服务器身份材料、已知服务器密钥,或策略要求加密的已注册服务器,都会启用受保护模式。对于 rstrm://... 目标,客户端可以在打开会话前解析标签和本地已知服务器条目。ws://... 直连目标需要直接服务器信任材料。使用 --known-server <name> 从本地信任存储中选择固定服务器,或者在自动化直接管理信任材料时传入 --known-server-key。只有当命令必须拒绝明文连接时,才使用 --e2e 作为失败关闭断言。

本地 WebTTY 文件采用以下布局:

  • ~/.rstream/webtty/identities/<name>.identity.json
  • ~/.rstream/webtty/enrollments/<server-id>.yaml
  • ~/.rstream/webtty/known_servers.json
  • ~/.rstream/workspaces/<workspace-id>/devices/<device-id>.json
  • 运维人员管理的守护进程配置,例如 /etc/rstream/webtty/prod-shell.yaml

在 POSIX 系统上,身份、注册信息、已知服务器和工作区设备文件均以 0600 权限创建;如果组用户或其他用户可以读取这些文件,运行时会拒绝使用。

容器和 CI 部署可使用以下环境变量覆盖项: RSTREAM_WEBTTY_CONFIG、RSTREAM_WEBTTY_AUTH_TOKEN、 RSTREAM_WEBTTY_IDENTITY、RSTREAM_WEBTTY_IDENTITY_FILE、 RSTREAM_WEBTTY_AUTHORIZED_CLIENT_KEYS、RSTREAM_WEBTTY_KNOWN_SERVER_KEY、 以及 RSTREAM_WEBTTY_KNOWN_SERVERS_FILE。

服务器身份的优先级依次为:用于内联端点身份 JSON 文档的 RSTREAM_WEBTTY_IDENTITY、--identity-file、--identity、RSTREAM_WEBTTY_IDENTITY_FILE、已注册服务器的注册信息身份文件,然后是为使用 --e2e 且未指定显式身份而启动的临时服务器所用的默认文件 ~/.rstream/webtty/identities/default.identity.json。E2E 服务器需要授权客户端签名密钥,这些密钥可来自默认授权客户端存储、--authorized-client-key、RSTREAM_WEBTTY_AUTHORIZED_CLIENT_KEYS,或显式指定的授权客户端文件。 客户端身份优先级使用相同的端点身份来源:先使用 RSTREAM_WEBTTY_IDENTITY,然后是 --identity-file、--identity、 RSTREAM_WEBTTY_IDENTITY_FILE,最后是存储在 ~/.rstream/webtty/known_servers.json 中、作用域限定于目标的 client_identity。经过身份验证的 E2E 客户端不会静默使用默认身份。客户端已知服务端材料来自 --known-server、重复指定的 --known-server-key、 RSTREAM_WEBTTY_KNOWN_SERVER_KEY、--known-servers-file 或 RSTREAM_WEBTTY_KNOWN_SERVERS_FILE;密钥值通常应为通过 rstream webtty identity show --endpoint-identity 输出的端点身份。 默认的已知服务端存储适用于已解析的 rstrm://... 连接,并按目标限定作用域。对于直接的 ws://... 连接,如果没有已解析的隧道元数据,应传入 --known-server <name> 或显式信任材料。 较低层级的辅助函数仍接受仅含两个字段的加密形式,供专用的纯加密集成使用,但这并非推荐的运维方式。如果未显式提供受信任的服务端密钥,系统会加载默认的 ~/.rstream/webtty/known_servers.json,因此无需额外的客户端标志即可推断出 E2E。

如果运行时主机已是受信任的工作区设备,工作区管理的已注册服务端会在注册期间自动固定公共工作区密钥集身份。如果本地信任材料之后发生变化,请使用 rstream webtty server trust <server-id> 刷新固定信息。此修复命令可让远程服务端验证工作区管理的客户端证明,而无需将工作区私钥复制到主机。

已实现的 E2E 辅助工具套件使用 AES-256-GCM 加密有效负载,采用全新的 96 位随机数,并使用 HPKE 基本模式 DHKEM(X25519, HKDF-SHA256), HKDF-SHA256, AES-256-GCM 密钥封装。 Go WebTTY tls:// 纯传输和本地 WebTransport 的最低版本为 TLS 1.3。

完整的 WebTTY CLI、本地文件、运行时配置和 E2E 工作流参考,请参阅 docs/006-webtty.md。

rstream mcp serve 通过 stdio 暴露受信任且关联设备的 MCP 界面。rstream codex setup 会为 Codex 安装该服务端,而 rstream codex setup --verify 会执行有界的 MCP 2025-11-25 握手,并在无需登录或网络访问的情况下检查必要的本地工具。

CLI 界面负责工作站上下文、本地隧道、WebTTY E2E 身份和授权、文件系统 sidecar、远程服务暴露以及远程 MCP 桥接。托管的 /api/mcp 界面负责受 OAuth 作用域限制的 Control Plane 自动化,并有意无法使用设备私有的 E2E 材料。两者共用的工具具有相同的名称、标题、行为注解、语义输出键和对象形式的结果契约。

rstream mcp publish 通过 /mcp 上的 Streamable HTTP 暴露设备界面。已发布客户端需要短期有效、作用域严格受限的 rstream 令牌;当前全界面发布和包含机密的结果限制均有明确跟踪记录,因此在这些问题解决前,不能将其视为最小权限委托边界。

有关设置、双界面契约、安全边界、验证和待完成的加固工作,请参阅 docs/009-mcp.md。

Netcat 风格的 TCP 和 rstream 流

使用 rstream netcat 通过普通 TCP 或原生 rstream 隧道建立字节流会话。该命令也可用 rstream ncat 和 rstream nc 调用。

# Connect to a plain TCP service
rstream nc 127.0.0.1:1234

# Expose a command over TCP
rstream nc -L 127.0.0.1:1234 -c "date"

# Expose a local SSH daemon through a private rstream tunnel
rstream nc -L rstrm://ssh-server -R 127.0.0.1:22

# Connect to that private rstream tunnel by name or ID
rstream nc rstrm://ssh-server

当 --listen 使用 rstrm://[name] 时,CLI 会创建一个未发布的私有字节流隧道。如果未提供名称,启动时会打印生成的隧道标识符,以便另一个 rstream 客户端拨入。

要通过 SSH 访问专用计算机,rstream forward 通常是更简单的服务端入口。在可访问本地 SSH 守护进程的远程计算机上启动它:

rstream forward 22 --bytestream --no-publish --name ssh-server

在客户端计算机上,使用一次性 SSH 命令验证连接路径:

ssh -o 'ProxyCommand rstream nc rstrm://ssh-server' admin@ssh-server hostname

若要定期使用,请将客户端配置移入 SSH ProxyCommand:

Host ssh-server
  HostName ssh-server
  User admin
  ProxyCommand rstream nc rstrm://%h

这样可保持隧道私有,同时 SSH 仍会执行常规的主机密钥验证和用户身份验证。

在客户端模式下,--exec/--sh-exec 会运行本地命令,并将其标准输入/标准输出桥接到连接,从而无需通过 shell 管道即可实现双向传输:

rstream nc rstrm://ssh-server -c "my-local-client"
Netcat 数据报模式

rstream nc --datagram(-u)传输的是数据包,而非字节流。数据报模式要求两端都使用 rstrm:// 端点:--listen rstrm://[name] 会创建一个未发布的私有数据报隧道,而 rstream nc -u rstrm://<id-or-name> 则会拨入该隧道。数据报模式不支持 --remote。

由于标准输入/输出是字节流,因此通过显式分帧来保留标准输入/输出上的数据包边界,使用 --framing 选择分帧方式。默认且目前唯一支持的分帧方式是 rfc4571,它会按照 RFC 4571 的定义,在每个数据报前添加一个 2 字节的大端序长度字段。标准输入/输出上的一帧对应隧道上的一个数据报,因此,任何读写这种分帧格式的程序都可以通过隧道交换数据包,无需进行其他适配。

# Expose a datagram producer; one child process per accepted session
rstream nc -u -L rstrm://media -c "media-producer"

# Dial the tunnel; stdio carries RFC 4571 frames
rstream nc -u rstrm://media

# Or run a local consumer with bidirectional framed stdio
rstream nc -u rstrm://media -c "media-consumer" --idle-timeout 60s

数据报隧道也可以桥接本地 UDP 套接字,而非使用标准输入/输出,从而让原生支持 UDP 的应用程序直接连接,无需考虑分帧。一个 UDP 数据包对应一个隧道数据报,--framing 不适用于 udp 端点。

# Bridge tunnel sessions to a local UDP service (one connected socket per session)
rstream nc -u -L rstrm://media -R udp://127.0.0.1:5004

# Bind a local UDP socket and bridge it to a tunnel (one session per local peer)
rstream nc -u -L udp://127.0.0.1:5004 -R rstrm://media

# Receive-only local apps never send first; pin the peer to open the session eagerly
rstream nc -u -L udp://127.0.0.1:5004 -R rstrm://media --udp-peer 127.0.0.1:5006

CLI 默认会自动选择隧道传输方式:优先使用 QUIC;UDP 路径不可用时则回退到 TLS。选择 QUIC 时,隧道端的数据包可以通过 QUIC 数据报(RFC 9221)传输:这些数据报受拥塞控制,但不会重传,因此无法保证送达,并且每个数据报都必须符合路径 MTU 限制(对于标准输入/输出帧和 UDP 数据包,约 1200 字节是安全的有效负载目标)。过大的数据报会被丢弃并记录日志,但不会终止会话。当必须无损保留数据包边界时,请使用 --datagram-guaranteed-delivery;即使控制通道使用 QUIC,该模式也会通过可靠流传输数据包。

关闭数据报通道的任一端也会关闭对端会话。--idle-timeout 用于处理另一种情况:在给定时长内未收到数据报时关闭会话。仅在预期会收到入站数据包的一端使用它。仅发送数据的生产端不会收到流量来刷新截止时间,因此不得设置该选项。在数据报 exec 会话中,子进程的标准错误会输出到本地标准错误,而非连接,因为原始标准错误字节会破坏分帧。

UDP/数据报隧道

对于数据报传输,需要加密的类 UDP 流量时,请选择 DTLS 模式。

# Create a DTLS tunnel for UDP traffic on port 5000
rstream forward 5000 --dtls

DTLS 隧道会自动处理数据报流量。使用 --dtls 时,--datagram 标志会自动启用。

运行(声明式隧道)

rstream run 根据 YAML 文件或 Docker 标签同步隧道,并可选择监视和协调。

  • forward:根据 CLI 参数创建单个隧道并立即转发(单个隧道,交互式)。
  • run --apply:根据 YAML 声明隧道列表,支持监视和协调。
  • run --docker:根据 Docker 标签发现隧道,支持监视和协调。
# Apply a YAML spec once
rstream -v run --apply examples/run-yaml/tunnels.yaml

# Watch the YAML for changes and reconcile
rstream -v run --apply examples/run-yaml/tunnels.yaml --watch

# Discover tunnels from Docker labels
rstream -v run --docker --watch

完整的 YAML 架构、Docker 标签参考和协调详情,请参阅 docs/008-cmd-run.md。

构建和编译

rstream 提供功能全面的 Makefile,支持多个平台和打包格式。

开发

对于日常开发循环,这些目标用于构建、测试和清理本地构建产物。

make          # Build for current platform
make clean    # Clean build artifacts
make tests    # Run test suite
make examples # Build example applications
跨平台编译

生成适用于多个目标平台的二进制文件时,请使用:

make cross    # Build for all supported platforms

支持 Linux、macOS、Windows 和 BSD 系统,涵盖多种架构,包括 x86、ARM、MIPS、PowerPC 和 RISC-V。

打包

对于发行版打包工作流,这些目标用于构建归档文件和平台软件包:

make pkg         # Create packages for current platform
make pkg-cross   # Create packages for all platforms
make deb         # Create Debian/Ubuntu packages
make docker      # Build Docker images
make nupkg       # Create Windows NuGet packages

代码示例

Go SDK 可让应用以编程方式创建和管理隧道。以下示例使用 config.NewClientFromEnv() 读取与 CLI 相同的配置和环境设置。请确保已设置默认上下文(或 RSTREAM_ENGINE),并根据所选 Agent 控制通道的认证要求,提供 RSTREAM_AUTHENTICATION_TOKEN 或 mTLS 证书/密钥环境变量。Engine HTTP API 操作也支持通过自动发现进行证书认证。

托管 TURN 凭据

SDK 还提供用于生成托管 TURN 凭据的辅助函数。

  • 保留一个 Client,并调用 client.CreateTURNCredentials(ctx, options) 进行续期。
  • 证书客户端直接使用发现的 Engine API,并采用内存缓存。
  • Auto 模式会优先在已知中继元数据时进行符合条件的本地 PAT/APP 推导,然后通过 Control plane 签发;仅当 Engine 可用时才回退到 Engine。
  • 支持显式的 api、engine、pat 和 app 模式。两种托管 API 都需要 turn.credentials.create;本地推导需要 turn.relay.allocate。
  • config.CreateTURNCredentialsFromEnv(...) 保留了对共享配置的支持。

有关传输、授权、有效期和 CLI 行为,请参阅 Engine HTTP mTLS and TURN。

TURN 域名是放置在 ICE URL 中的中继主机名。TURN realm 是用于凭据推导的认证范围。连接到全局边缘网络的区域中继可以使用不同的值。上下文解析会明确保留这两个字段,绝不会从 Engine 地址推断 realm。

package main

import (
	"context"
	"fmt"

	"github.com/rstreamlabs/rstream-go/config"
)

func main() {
	turn, err := config.CreateTURNCredentialsFromEnv(
		context.Background(),
		config.TURNCredentialsEnvOptions{},
	)
	if err != nil {
		panic(err)
	}
	fmt.Println(turn.Username)
	fmt.Println(turn.URLs)
}
显式客户端选项(例外)

仅当需要绕过配置和环境解析时才使用此方式。

package main

import "github.com/rstreamlabs/rstream-go"

func main() {
	client, err := rstream.NewClient(rstream.ClientOptions{
		Engine: "engine.example:443",
		Token:  "authentication_token",
	})
	if err != nil {
		panic(err)
	}
	_ = client
}
HTTP 服务端(已发布隧道)

此示例创建一个已发布的 HTTP 隧道,并通过隧道监听器提供请求服务。ForwardingAddress() 输出的转发地址是可通过浏览器或 curl 访问的公共 URL。

package main

import (
	"context"
	"fmt"
	"net"
	"net/http"

	"github.com/rstreamlabs/rstream-go"
	"github.com/rstreamlabs/rstream-go/config"
)

func main() {
	client, err := config.NewClientFromEnv()
	if err != nil {
		panic(err)
	}
	ctrl, err := client.Connect(context.Background(), nil)
	if err != nil {
		panic(err)
	}
	defer ctrl.Close()
	tunnel, err := ctrl.CreateTunnel(context.Background(), rstream.TunnelProperties{
		Protocol:    rstream.ProtocolPtr(rstream.ProtocolHTTP),
		HTTPVersion: rstream.HTTPVersionPtr(rstream.HTTP1_1),
		Publish:     rstream.BoolPtr(true),
	})
	if err != nil {
		panic(err)
	}
	defer tunnel.Close()
	addr, _ := tunnel.ForwardingAddress()
	fmt.Printf("Server accessible at: %s\n", addr)
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintln(w, "Hello from rstream!")
	})
	http.Serve(tunnel.(net.Listener), nil)
}
TLS 回显(私有隧道)

此示例展示了私有隧道工作流。服务端创建一个名为 echo 的未发布隧道,并接受传入的隧道连接。客户端按名称拨号连接私有隧道,并通过生成的流交换数据。

服务端代码:

package main

import (
	"context"
	"io"
	"log"
	"net"

	"github.com/rstreamlabs/rstream-go"
	"github.com/rstreamlabs/rstream-go/config"
)

func main() {
	client, err := config.NewClientFromEnv()
	if err != nil {
		panic(err)
	}
	ctrl, err := client.Connect(context.Background(), nil)
	if err != nil {
		panic(err)
	}
	defer ctrl.Close()
	tunnel, err := ctrl.CreateTunnel(context.Background(), rstream.TunnelProperties{
		Name:    rstream.StringPtr("echo"),
		Publish: rstream.BoolPtr(false),
	})
	if err != nil {
		panic(err)
	}
	defer tunnel.Close()
	props, err := tunnel.Properties()
	if err != nil {
		panic(err)
	}
	log.Printf("Echo server running as private tunnel: %s", *props.Name)
	listener := tunnel.(net.Listener)
	for {
		conn, err := listener.Accept()
		if err != nil {
			break
		}
		go func(c net.Conn) {
			defer c.Close()
			log.Printf("New connection from %s", c.RemoteAddr())
			io.Copy(c, c)
		}(conn)
	}
}

客户端代码:

package main

import (
	"context"
	"fmt"

	"github.com/rstreamlabs/rstream-go"
	"github.com/rstreamlabs/rstream-go/config"
)

func main() {
	client, err := config.NewClientFromEnv()
	if err != nil {
		panic(err)
	}
	conn, err := client.Dial(context.Background(), rstream.Addr{
		IdOrName: "echo",
	})
	if err != nil {
		panic(err)
	}
	defer conn.Close()
	message := "Hello from private tunnel!"
	conn.Write([]byte(message))
	buffer := make([]byte, len(message))
	conn.Read(buffer)
	fmt.Printf("Sent: %s\nReceived: %s\n", message, string(buffer))
}
DTLS 数据报服务端

此示例创建一个已发布的 DTLS 数据报隧道。转发地址是数据报客户端连接的位置。服务端使用数据包监听器 API 接受数据报会话并回显数据包。

package main

import (
	"context"
	"fmt"
	"log"

	"github.com/rstreamlabs/rstream-go"
	"github.com/rstreamlabs/rstream-go/config"
)

func main() {
	client, err := config.NewClientFromEnv()
	if err != nil {
		panic(err)
	}
	ctrl, err := client.Connect(context.Background(), nil)
	if err != nil {
		panic(err)
	}
	defer ctrl.Close()
	tunnel, err := ctrl.CreateTunnel(context.Background(), rstream.TunnelProperties{
		Name:     rstream.StringPtr("dtls"),
		Type:     rstream.TunnelTypePtr(rstream.TunnelTypeDatagram),
		Protocol: rstream.ProtocolPtr(rstream.ProtocolDTLS),
		Publish:  rstream.BoolPtr(true),
	})
	if err != nil {
		panic(err)
	}
	defer tunnel.Close()
	addr, _ := tunnel.ForwardingAddress()
	fmt.Printf("DTLS server accessible at: %s\n", addr)
	packetListener := tunnel.(rstream.PacketListener)
	for {
		conn, _, err := packetListener.Accept()
		if err != nil {
			log.Printf("Accept error: %v", err)
			continue
		}
		go func() {
			defer conn.Close()
			buffer := make([]byte, 1024)
			for {
				n, addr, err := conn.ReadFrom(buffer)
				if err != nil {
					break
				}
				log.Printf("Received %d bytes from %s", n, addr)
				n, err = conn.WriteTo(buffer[:n], addr)
				if err != nil {
					break
				}
			}
		}()
	}
}
QUIC 服务端

此示例创建一个已发布的 QUIC 数据报隧道。QUIC 是一种现代传输协议,旨在实现低延迟并增强对网络变化的适应能力。示例会生成本地 TLS 配置,并通过隧道数据包监听器提供 QUIC 流服务。

package main

import (
	"context"
	"crypto/rand"
	"crypto/rsa"
	"crypto/tls"
	"crypto/x509"
	"encoding/pem"
	"fmt"
	"log"
	"math/big"

	"github.com/quic-go/quic-go"
	"github.com/rstreamlabs/rstream-go"
	"github.com/rstreamlabs/rstream-go/config"
)

func generateTLSConfig() (*tls.Config, error) {
	key, err := rsa.GenerateKey(rand.Reader, 1024)
	if err != nil {
		return nil, err
	}
	template := x509.Certificate{SerialNumber: big.NewInt(1)}
	certDER, err := x509.CreateCertificate(rand.Reader, &template, &template, &key.PublicKey, key)
	if err != nil {
		return nil, err
	}
	keyPEM := pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(key)})
	certPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: certDER})
	tlsCert, err := tls.X509KeyPair(certPEM, keyPEM)
	if err != nil {
		return nil, err
	}
	return &tls.Config{
		Certificates: []tls.Certificate{tlsCert},
	}, nil
}

func main() {
	client, err := config.NewClientFromEnv()
	if err != nil {
		panic(err)
	}
	ctrl, err := client.Connect(context.Background(), nil)
	if err != nil {
		panic(err)
	}
	defer ctrl.Close()
	tunnel, err := ctrl.CreateTunnel(context.Background(), rstream.TunnelProperties{
		Name:     rstream.StringPtr("quic"),
		Type:     rstream.TunnelTypePtr(rstream.TunnelTypeDatagram),
		Protocol: rstream.ProtocolPtr(rstream.ProtocolQUIC),
		Publish:  rstream.BoolPtr(true),
	})
	if err != nil {
		panic(err)
	}
	defer tunnel.Close()
	addr, _ := tunnel.ForwardingAddress()
	fmt.Printf("QUIC server accessible at: %s\n", addr)
	tlsCfg, err := generateTLSConfig()
	if err != nil {
		panic(err)
	}
	transport := quic.Transport{
		Conn: rstream.PacketConnFromPacketListener(tunnel.(rstream.PacketListener)),
	}
	listener, err := transport.Listen(tlsCfg, nil)
	defer listener.Close()
	for {
		conn, err := listener.Accept(context.Background())
		if err != nil {
			log.Printf("Accept error: %v", err)
			continue
		}
		go func() {
			defer conn.CloseWithError(0, "server done")
			stream, err := conn.AcceptStream(context.Background())
			if err != nil {
				return
			}
			defer stream.Close()
			buffer := make([]byte, 1024)
			for {
				n, err := stream.Read(buffer)
				if err != nil {
					break
				}
				log.Printf("Received %d bytes from %s", n, conn.RemoteAddr())
				n, err = stream.Write(buffer[:n])
				if err != nil {
					break
				}
			}
		}()
	}
}

参考资料

运维和高级 CLI/SDK 工作流:

贡献

欢迎并鼓励提交拉取请求。无论是修复错误、添加功能、改进文档,还是提出增强建议,您的贡献都能帮助 rstream 更好地服务每个人。在本地构建、运行检查,并提交附有清晰验证说明且范围明确的拉取请求。请参阅 CONTRIBUTING.md,了解本仓库特有的贡献指南。

支持

获取帮助:
[email protected]

报告安全问题:
[email protected]

请参阅 SECURITY.md,了解本仓库采用的安全问题报告指南。

许可证

本仓库基于 Apache License 2.0 授权。请参阅 LICENSE。

评论

支持 Markdown

第一次接触 MCP?

从协议是什么,到在客户端里装上第一个服务,这几条回答覆盖了大多数入门问题。

一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。