跳转至

13 · 远程连接与多客户端语义

Herdr 的远程模式不是把本地 TUI 包进 ssh -t。它在远端运行真正拥有 PTY/状态的 Herdr server,本地仍运行 client,通过 SSH bridge 传输与本地相同的 wire protocol。这样 Agent 和仓库都留在远端,输入、主题和屏幕设备留在本地。

13.1 远程拓扑

flowchart LR
    subgraph Local["本地主机"]
      UI["Herdr app client"]
      SSH["SSH process / local forward"]
      Cache["remote binary/install cache"]
    end
    subgraph Remote["远程主机"]
      Bridge["herdr bridge over stdio/socket"]
      Server["Herdr headless server"]
      PTY["PTY + Agent + repo"]
      API["remote session API socket"]
    end

    UI <-->|"wire protocol v20"| SSH
    SSH <-->|"encrypted channel"| Bridge
    Bridge <--> Server
    Server <--> PTY
    Server <--> API
    Cache -."install exact compatible artifact".-> Bridge

主要实现集中在 src/remote/attach.rs 和 src/remote/host_unix.rs。

13.2 为什么不直接用远端 shell TTY

简单 ssh -t host herdr 有几个限制:

  • UI 进程跟 SSH 连接同寿命,断线恢复弱;
  • 本地/远端 Herdr 版本和协议不显式协商;
  • 无法自然加入第二个 observer/client;
  • 本地 terminal capabilities、theme、keyboard profile难以结构化传达;
  • 远端 daemon 的更新/复用/命名 session 难管理;
  • 文件传输和 bridge 失败难给出精确阶段。

显式 remote orchestration 把“连接一个 shell”升级为“发现并连接一个兼容服务”。

13.3 远程准备流程

sequenceDiagram
    participant L as Local herdr
    participant SSH as ssh
    participant R as Remote shell
    participant M as Release manifest
    participant S as Remote server

    L->>SSH: connect target + keepalive/control options
    SSH->>R: probe uname / arch / known binary paths
    R-->>L: platform + candidates + runtime status
    L->>L: choose exact compatible binary/server plan
    alt usable remote binary exists
      L->>R: verify version/protocol/path
    else needs install
      L->>M: resolve exact asset + SHA-256
      L->>R: transfer/download staged artifact
      R->>R: verify/install executable
    end
    L->>S: reuse/start/stop-or-handoff plan
    S-->>L: protocol/capabilities ready
    L->>SSH: establish bridge/forward
    L->>S: client handshake + attach

每一步都可能失败,所以实现保存阶段化诊断,而不是把所有错误压成 “ssh failed”。

13.4 平台探测与 asset key

远端先用 uname 等轻量命令确定 OS/arch,标准化示例:

Linux + amd64  -> linux-x86_64
Linux + arm64  -> linux-aarch64
Darwin + arm64 -> macos-aarch64

未知 OS/架构应明确拒绝,不能下载“最接近”的 artifact。远端平台决定:

  • 发布 manifest 的 asset key;
  • 可执行文件格式/权限;
  • daemon detach 与 Live Handoff capability;
  • socket/SSH 转发方式。

13.5 发现真实二进制,而不是 shim

which herdr 可能返回 ~/.local/share/mise/shims/herdr。shim 依赖交互环境和版本选择,不能当作稳定的 server replacement/import executable。

远端发现会枚举:

  • 显式 HERDR_REMOTE_BINARY;
  • 当前/标准 install paths;
  • mise 的具体 version install 目录;
  • Nix/其他已知布局;
  • PATH 中非 shim 候选。

is_mise_shim_path 直接排除 shim。路径存在还不够,候选要能返回版本/protocol/status。

13.6 “相同版本”不如“相同协议”重要

固定快照使用 client protocol 20。远端二进制选择要满足当前 client 所需协议;发布 manifest 的 archived release metadata也保存各自 protocol,不能拿最新 release 的 protocol 填给旧 artifact。

candidate usable iff:
  executable works
  AND speaks required protocol
  AND platform/arch matches
  AND(安装来源时)checksum verified

若远端正在运行旧 server,但协议仍兼容,可以保留它;若不兼容,则根据 --handoff、remote capability 和用户策略选择 Live Handoff 或明确 stop。不能只因 semver 不同就杀掉活跃 Agent。

13.7 Stable 与 Preview 的精确 artifact

Stable manifest 提供版本、各平台 asset、protocol 和 SHA-256。Preview 还带 build id/commit,并保留 archived exact build:本地 preview client 应找到与自身 build/protocol 对应的远端 artifact,而不是无条件下载“今天最新 preview”。

这是可复现远程协议的关键。滚动 preview 如果只保留一个 URL,本地稍旧的客户端第二天连接新主机时就可能再也找不到兼容 server。

13.8 校验与安装

下载或传输后:

  1. 写 staging path;
  2. 对 stable artifact 要求 manifest 提供 SHA-256;
  3. 用 src/checksum.rs 校验 64 位十六进制 digest;
  4. 设置 Unix executable permission;
  5. 再作为 server/bridge candidate 运行 status probe;
  6. 失败不覆盖已有可用二进制。

传输加密不能代替 artifact 完整性。SSH 保护通道,checksum 证明实际文件与发布 manifest 一致。

13.9 SSH control 与 keepalive

频繁运行 probe、copy、bridge 时,每次重新认证成本高。Unix 路径使用 ControlMaster=auto 等连接复用选项;管理的 SSH config还设置:

ServerAliveInterval 15
ServerAliveCountMax 4

实现尊重 OpenSSH “first value wins”,不覆盖用户显式 keepalive。Windows 管理配置保留 keepalive,但不强行使用 Unix control socket 语义。

配置文件权限必须限制为用户可读写,避免 SSH 拒绝或泄漏主机配置。

13.10 sun_path 长度约束

Unix domain socket 路径通常有很短的固定上限。把完整 user@very-long-host + session-name 拼进本地 forward path 会 bind 失败。

Herdr 计算短 hash,把 target/session identity 压缩到可验证长度,并有测试 local_forward_socket_path_fits_in_sun_path。

需要同时满足:

  • 同 target/session 稳定;
  • 不同组合碰撞概率可接受;
  • 父目录权限安全;
  • 字节长度而非字符数符合 OS 上限;
  • stale socket 可验证后清理。

13.11 Bridge 的责任要薄

Bridge 主要在 SSH stdio 与远端/local socket 间双向 copy/flush。它不应复制 App state 或重新解释 frame:

  • 协议握手仍由真正 client/server 完成;
  • EOF 在任一方向传播,另一方向有界退出;
  • partial write/Interrupted 正确重试;
  • drop 时不无限等不存在的对端;
  • 日志不能混入二进制 stdout 通道。

薄 bridge 让本地和远端共享同一 wire protocol 语义,减少第三套协议。

13.12 Remote server 决策矩阵

远端状态 协议 binary 计划
未运行 — 兼容 启动 server
运行中 兼容 任意可用 直接 attach,通常不扰动
运行中 不兼容 新兼容 artifact 若启用且支持则 Live Handoff
运行中 不兼容 新兼容 artifact 否则确认 stop 后替换
status 不明 不明 任意 停止自动破坏动作,给诊断
preview exact build 缺失 不明 无 要求显式 remote binary/手动安装

“状态不明”不能等同“未运行”;否则网络瞬断会触发错误 stop/install。

13.13 多客户端角色

远程只是 transport 变化,server 仍支持多 client:

角色 读取 frame 写输入 改共享 PTY 尺寸 典型用途
app foreground 是 是 是 主交互 UI
app observer 是 否 否 旁观/第二窗口
direct terminal control 目标流 有 lease 时 按协议 直接附着 pane
direct terminal observe 目标流 否 否 日志/监看

一个 writable owner + 多 observer 避免网络两端同时向 shell 注入交错输入。takeover 是显式事件,并清理旧 input lease。

13.14 重连命令与 session identity

远端 attach 成功后,client 可得到可复现的 reattach 信息:target、命名 session、必要 remote flags。它不能只记临时 local forward socket,因为该路径在下次进程中可能重建。

重连关键是逻辑 identity:

SSH target + Herdr session namespace + client role/attach target

临时 socket/control path 都是从它派生的实现细节。

13.15 失败边界

失败 期望行为
SSH 认证失败 不创建/删除远端二进制,显示原 ssh 诊断
平台不支持 在下载前拒绝
checksum mismatch 删除/隔离 staging,不执行
bridge 中断 client 断线;远端 server/PTY 继续
incompatible server handoff 失败 尽量确认旧 server 是否仍活,再询问 stop
forward socket stale 探测 owner 后清理,不盲删活连接
本地窗口 resize storm 合并尺寸消息,只有 foreground 改 PTY

13.16 本章结论

  1. 远程模式延伸 server/client 架构,不是把整个 TUI 塞进 SSH TTY;
  2. 远端准备要验证平台、真实 binary、protocol 和 checksum;
  3. preview 必须保留 exact build artifact,才能复现兼容性;
  4. bridge 保持字节转发,业务语义仍由共享 wire protocol 承担;
  5. 多客户端的 foreground、lease、尺寸和 observer 规则在网络边界后仍不变。

下一章补齐产品外围但同样关键的三层:配置如何带诊断地局部加载,平台差异如何集中,self-update 又如何尊重 package manager 和活跃 server 所有权。