remote.文档首页

初始化、设备二维码与六位码登录

Agent 默认 Exchange 为 wss://exchange.ivjn.us,CLI/Console 管理 API 地址为 https://exchange.ivjn.us。原有配置优先,不会覆盖自建 Exchange 地址。

SaaS 注册与首次登录

remote-cli register --username alice --email alice@example.com
# 按隐藏输入提示设置密码,注册后向邮箱发送验证码。
remote-cli verify-email --username alice
# 隐藏输入邮件中的 8 位数字验证码,然后登录。
remote-cli login --email alice@example.com
# init 是 login 的别名,不带 --email 时交互输入邮箱和密码。
remote-cli init
# 原有令牌仍可使用:
remote-cli login --server https://exchange.ivjn.us --token-stdin

注册与登录默认连接 https://exchange.ivjn.us,自建服务可加 --server。登录以邮箱为账号身份,地址会去空格并转成小写,格式不合法会直接拒绝。用户名是注册时的工作区名称(3-64 位字母、数字或 ._-)。账号密码不设长度或字符格式限制;服务端仅保存密码哈希。自动化可使用 --password-stdin,不要把密码写进命令参数。新账号验证邮箱前不能登录。CLI 只保存返回的会话令牌,不保存密码,注册等待验证期间保留已有登录。

每次注册创建独立工作区,账号是该工作区的管理员。设备、成员、远程会话、指标和审计均隔离,不同工作区可使用相同设备 ID。六位码批准会把设备或新成员加入批准人的工作区。加入已有工作区应使用该管理员提供的令牌或六位码授权。

Android Console 同样支持注册、密码登录、已有令牌登录和六位码登录。密码只用于当前请求,凭证仍保存到 SecureStore。

密码登录会话有效期 30 天,每个账号最多 32 个有效会话;重复登录不挤掉其他客户端。POST /api/auth/logout 使用当前 Bearer 会话令牌注销,已建立的 WebSocket 最多 5 秒内关闭;到期后重新登录。设备与原有运维令牌保留原生命周期。

纯令牌身份没有密码账号,不能通过邮箱找回。当前不包含计费或跨实例 WebSocket 路由。

邮箱验证与密码找回

remote-cli resend-verification --email alice@example.com
remote-cli verify-email --username alice
remote-cli forgot-password --email alice@example.com
remote-cli reset-password --email alice@example.com
# 按提示输入邮件验证码、新密码,再重新登录。

# 已登录的旧密码账号绑定或更换邮箱,需要再次验证当前密码。
remote-cli set-email --email alice@example.com
remote-cli verify-email --username alice

验证与找回使用不同用途的 8 位数字验证码,15 分钟有效,连续输错 5 次后失效,重新发信会使旧码失效;每个账号每种用途一分钟内只能发一次。验证与重置命令可用 --code 自动化,默认隐藏输入验证码。重置成功会撤销该账号的全部登录会话,已建立的 WebSocket 最多 5 秒内断开;不影响工作区内其他成员和设备。

Console 注册页填写邮箱,随后进入验证页;登录页提供 验证邮箱 和 忘记密码。更换邮箱在验证完成前保留原地址。重发验证和找回请求返回统一提示,不暴露邮箱是否已注册。

邮件功能需要迁移 0004_email.sql 和 SMTP 配置:SMTP_HOST、SMTP_PORT、SMTP_FROM、SMTP_USERNAME、SMTP_PASSWORD、SMTP_SECURITY。默认 STARTTLS,支持隐式 TLS;非加密 SMTP 仅允许本地测试回环地址。SMTP 凭据放服务环境文件,不提交到 Git。不配置邮件服务时,新版注册和发信接口返回 503,既有账号登录不受影响,因此生产启用前必须完成发信配置。SMTP 暂时失败可能留下待验证账号,恢复后使用重发验证邮件,不必重复注册。

Exchange 账号 API

方法与路径 鉴权 用途
POST /api/auth/register 无,限流 {username,password,email},创建待验证账号,返回 202 和 verification_required,不签发会话
POST /api/auth/login 无,限流 {email,password},签发独立登录会话
GET /api/auth/me 运维令牌或登录会话 返回 name、tenant_id、role
POST /api/auth/logout 当前登录会话 撤销当前会话;不撤销其他客户端
POST /api/auth/verify-email 无,限流 {username,code},消费邮箱验证码
POST /api/auth/resend-verification 无,限流 {email},向待验证地址重发邮件
POST /api/auth/forgot-password 无,限流 {email},向已验证地址发送找回验证码
POST /api/auth/reset-password 无,限流 {email,code,password},修改密码并撤销旧会话
POST /api/auth/email 当前登录会话和账号密码 {email,password},申请绑定或更换邮箱,验证后生效

登录成功返回 username、email(若已绑定)、tenant_id、token、expires_in,并设置 Cache-Control: no-store。账号密码使用 Argon2id 默认参数加盐保存;每个 TCP 来源 IP 每分钟最多 20 次账号请求,每个用户名每分钟最多 10 次,密码哈希并发最多 4 个。不信任客户端提供的转发 IP;反向代理后的客户端共享 IP 配额,公共部署应在可信入口配置额外限流。

Rust Exchange 默认关闭公开注册。需显式设置 EXCHANGE_ALLOW_REGISTRATION=true 并配置 SMTP 才能启用;false 关闭新注册,已有账号仍可登录。

让 Agent 显示六位码

Android Remote Agent:默认地址已填好,点击 Connect with a six-digit code。应用会显示一个 5 分钟有效的六位码。在已登录的管理员 CLI 上执行:

remote-cli pair 012345
# 可选:覆盖默认 ID 和显示名称
remote-cli pair 012345 --device-id my-android --name "我的手机"

CLI 获取连接码时显示设备名、身份类型和建议设备 ID,确认是自己的设备后授权。默认 ID 使用设备名的小写 ASCII 字母、数字和 ._-,去掉空格及其他字符;无可用字符时使用 device,最多 64 字符。同一工作区重名时依次追加数字,例如 realmev5、realmev51、realmev52,必要时截短基础 ID。Exchange 在创建时处理并发冲突,CLI 打印最终分配的 ID;手动指定的 ID 重名也追加数字。Android 使用系统设备名(不可用时使用型号),原生 Agent 使用主机名。Agent 自动取得独立设备令牌、加密保存并启动连接。不会自动开启辅助功能或屏幕控制。取消、过期或鉴权失败不会替换已有配置;Agent 正在连接时需先停止。

电脑、路由器与 Linux Agent 添加设备

Windows、macOS、OpenWrt、Linux Agent 无需手工填写设备令牌,任选一种方式:

平台 方式一:配对码 / 设备二维码 方式二:粘贴邀请
Windows / macOS(桌面 --ui,双击启动) 本地页面 Add this device → Get pairing code,显示六位码和二维码 本地页面 Invite 文本框粘贴后点 Join with invite
OpenWrt(luci-app-remote) LuCI 服务 → 远程管理 → 添加设备 → 获取配对码,显示六位码和二维码 同页 方式二:邀请内容 粘贴后点 加入
Linux / 所有平台命令行 remote-agent pair(终端显示六位码和字符二维码) remote-agent join '<邀请内容>',或 remote-agent join - 从标准输入读取

方式一:Agent 申请 5 分钟有效的六位码,同时显示二维码,内容为 remote://pair?code=012345&server=wss%3A%2F%2Fexchange.ivjn.us(不含任何凭证)。管理员任选: Android Console 添加设备 → 输入配对码 → 扫描设备二维码(相机扫描,核对服务端一致后查询并确认批准);或输入六位码;Web Console 批准配对码 可输入六位码或粘贴 remote://pair?...;或 remote-cli pair 012345(也接受该 URI)。批准后 Agent 自动保存令牌并连接。

方式二:管理员在 Android Console 添加设备 → 扫码添加 生成一次性邀请,点击 复制邀请(电脑 / 路由器粘贴);或运行 remote-cli qr --device-id pc-01 --text。邀请是二维码中的 JSON({"type":"remote-enrollment",...}),5 分钟内有效、只能使用一次。也接受设备令牌二维码(remote-cli device qr)的内容。

保存位置与连接:

  • 桌面 UI:写入当前配置文件(默认 %LOCALAPPDATA%\remote-agent\config.json / ~/.config/remote-agent/config.json)并立即连接。
  • OpenWrt:写入 UCI remote-agent.main(server / device_id / token,enabled=1),再重启 procd 服务。
  • 命令行:默认写入桌面配置路径(OpenWrt 默认写 UCI),--config PATH 指定 JSON 文件,--uci 强制写 UCI;保存后在前台连接(--no-connect 只保存)。如已有服务在用该配置,请重启服务。

已配置凭证时默认拒绝替换:命令行需 --force,桌面 UI 与 LuCI 会先确认。已有的令牌方式(--device-id/--token、配置文件、LuCI 设置页)不变。

remote-agent pair                       # 显示六位码 + 二维码,批准后保存并连接
remote-agent pair --server wss://your-exchange.example.com --config ./agent.json
remote-agent join '{"type":"remote-enrollment","version":1,...}'
remote-agent join - < invite.txt --no-connect

原生配对要求 wss://,仅本机回环允许 ws:// 以便测试;Android 和 Console 配对要求 HTTPS/WSS。

Console 六位码登录

Android Remote Console 登录页点击 使用六位码登录;Web Console(/console)登录卡片选择 六位码 标签,点击获取登录码。管理员可任选其一批准:

remote-cli pair 012345 --operator-name mobile-alice
# 只读客户端:追加 --role readonly

或在已登录的 Web Console 工作空间点击 批准配对码(仅管理员可见;只读操作员无此入口),输入对方屏幕上的六位码,核对类型与标签后批准。

默认创建独立 operator 账号,不复制管理员自身的令牌。管理员可明确指定 --role admin 或 --role readonly。Android Console 收到授权后再次验证凭证,保存到 SecureStore 并进入设备列表;Web Console 将操作员凭证保留在当前页面内存中(与账号 / 令牌登录相同),进入设备列表。取消或失败时仍可使用原有账号或令牌登录,不会清掉后续其他登录方式的输入。

Remote Agent 扫 CLI 码登录

remote-cli qr --device-id my-phone
# 输出 SVG 供手机扫描:
remote-cli qr --device-id my-phone --output ./phone-login.svg

在已登录管理员 CLI 上生成二维码,手机 Remote Agent 点击 Scan connection QR code,确认服务端和设备 ID 后点击 Connect。二维码只携带随机 256 位临时票据,5 分钟有效,只能绑定一台设备,不含长期设备令牌。扫描后由 Exchange 签发设备令牌,手机通过 Keystore 保存并连接。二维码对应的设备 ID 必须是新的;不会替换或轮换已有设备。

相同手机领取请求携带固定随机 claim_id,网络断开后重试可以取回同一结果;不同 claim_id 不能重复领取。保存后手机确认清除票据。临时票据在进程内存中保存,Exchange 重启会失效;已开始领取的结果另保留最多 5 分钟供原客户端重试。

方法与路径 鉴权 用途
POST /api/pairing/invite 管理员 {device_id,name?},生成票据,限制每管理员每分钟 10 次、全局 256 个
POST /api/pairing/claim Bearer <ticket> {claim_id},返回独立设备凭证;重试必须沿用同一 32 位十六进制 claim_id
POST /api/pairing/claim/complete 同上 保存成功后确认清除

生成设备二维码

# 创建设备,同时在终端显示二维码
remote-cli device add my-android --qr
# 新建 SVG 文件,可在电脑打开后供手机扫描
remote-cli device add another-phone --qr-output ./another-phone.svg
# 使用手里已有的设备令牌,不创建或轮换设备
remote-cli device qr my-android --output ./my-android.svg
# 明确轮换令牌并生成二维码;原令牌立即失效
remote-cli device rotate-token my-android --qr

device qr 以隐藏输入读取设备令牌,也支持 --token-stdin。设备二维码只包含设备令牌,不能使用运维令牌。输出文件拒绝覆盖,包含凭证,应妥善保管并在使用后删除。二维码要求 HTTPS/WSS。

Android Console:创建新设备后会直接显示设备二维码;设备详情的 轮换令牌并生成二维码 会先说明断线影响并要求确认。服务端只保存令牌哈希,不能从已存在的设备记录恢复旧二维码,因此不会为了查看二维码而静默轮换令牌。

Exchange 配对 API

方法与路径 鉴权 用途
POST /api/pairing/start 无,限流 提交 kind: device/operator、label,取得 code、poll_token、expires_in: 300、interval: 3
GET /api/pairing/{code} 管理员运维令牌 查看申请类型、标签和剩余时间,不返回凭证
POST /api/pairing/approve 管理员运维令牌 提交 code、kind、identity、可选 name、role,创建独立设备或账号
POST /api/pairing/poll Authorization: Bearer <poll_token> 返回 pending 或 approved;批准结果含 credentials.kind/identity/token
POST /api/pairing/complete 同上 客户端保存成功后清除临时授权;重复调用安全
GET /ws/pairing 无,限流 原生 Agent 配对 WebSocket,依次接收 code、approved 或 expired 事件,保存后发送文本 complete

六位码只用来让已认证管理员定位并确认请求,不能用来轮询或换取凭证。轮询凭证为随机 256 位秘密,不放入 URL。重复批准被拒绝;身份创建失败会释放授权占用以便重试。批准结果可在同一私有轮询凭证下重读,避免一次网络丢包导致凭证永久丢失;客户端保存后确认清除。

等待授权与批准待领取分别最多保留 5 分钟。未配置 REDIS_URL 时,临时会话只保存在当前 Exchange 进程内存中,重启后需重新领码,多实例需把配对请求固定到同一实例。配置了 Redis 后,配对条目与限流计数写入 Redis(与 5 分钟 TTL 对齐),多实例可任意分发、不必粘性。每个实际 TCP 来源 IP 每分钟最多领取 10 次,管理员每分钟最多查询或批准 20 次;内存模式另有全局最多 256 个待处理会话的上限。代理转发的客户端 IP 不作为可信限流依据;同一反向代理后的用户共享来源额度。过期数据由 TTL/定时清理清除,响应设置 Cache-Control: no-store,不记录配对秘密或签发令牌明文。

后端已有设备 ID/账号不能被配对请求覆盖。管理员应核对代码和申请端;不要批准他人诱导提供的登录码。

连接码 API:POST /api/pairing/start 可提交可选 device_name,响应返回该设备名。管理员 GET /api/pairing/{code} 返回 device_name 和当前工作区的 suggested_device_id,查询不会占用 ID。POST /api/pairing/approve 的设备 identity 可省略,成功返回 200 和 {identity}(最终 ID,不含令牌)。原生 WebSocket /ws/pairing?device_name=... 支持 URL 编码的设备名。