Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

2.6 通信机制

2.5 第一条跨机消息的每条命令背后都踩着同一套地层。这一节把一条消息拆开看五步,然后把四个最值得知道的机制讲透。

一条消息的五步

本机(win)                                 对端(yangwsl)
──────────────────────────                  ──────────────────────────
① 组装固定命令
   ssh yangwsl -o BatchMode=yes
       -o ConnectTimeout=10
       crosschat send --to beta2
       --json --origin <身份串>
② 正文写进 stdin ──── ssh 加密管道 ────────►  ③ 对端登录 shell 执行 crosschat
   (命令行里没有正文)                        = 有人在这台机器上敲了
                                              同一条 send:解析名字、
                                              投递、排队/暂存全在对端
⑤ 本机重渲染回执 ◄──── stdout(JSON)────────  ④ 回执 JSON 写回 stdout
   delivered to beta2@yangwsl(9f8e7d6c)…
   (回程路条已经坐在 ref 里)

①② 在本机、③④ 在对端、⑤ 回到本机。五步里没有第三台机器、没有中心服务——这就是课题里“没有中间服务器“的意思。

再换组件视角看同一件事——每台机器上各自跑着什么:

本机 win                                      对端 yangwsl
──────────────────────────                    ──────────────────────────
crosschat send(上面的①②步)                   登录 shell
 · 组装固定命令、注入身份、正文进 stdin ──ssh──►  └─ crosschat send(完整本机权限)
 · 会话分片:短柄 cN、续话线                       · 会话名单:beta2 在这里解析
 · send-log:发起侧记账                ◄─stdout── · 投递管道:忙时排队、暂存+看门狗
 · 重渲染回执(名@name(mid8))                    · 会话分片:信封里 c3 那个柄住这里

对端跑的就是一条完整的本机 send——没有任何“功能受限的远端模式“;跨机与单机共享同一套代码路径,这是“跨机 = 单机 + 一个 –via“在架构上的 literal 含义。

远端命令面为什么固定

① 里那条远端命令是写死的字面:目标(--to 或 --conversation)、--json、--origin,仅此几样,一个可配置项都没有。正文永远走 stdin,永远不出现在命令行里。

这不是偷懒,是安全设计。远端命令经对端的登录 shell(cmd / PowerShell / bash,三家引号规则互相打架)解释——任何“可配置的远端命令“都是一个注入面。项目的选择是让危险字符根本进不了命令行,而不是进了再转义:host 别名只允许字母、数字、.、_、-;--to 的值禁空格与 shell 元字符(名字带空格是单机语义,跨机改用 id8 / 会话引用寻址);--conversation 的值必须 mc1_/mc2_ 开头。三道白名单守的都是同一条防线,正文则整个绕开战场走 stdin——所以 2.5 第一条跨机消息里 $、反引号、引号才敢说“逐字到达“。

三层错误两个超时

失败分三层,每层有自己的错误码和自救动作:

层错误码含义与自救
本地等超时(或 ssh 进程没退)SSH_TRANSPORT_TIMEOUT⚠️ 等满上限杀了本地进程,远端状态不明——勿盲目重发(可能与首次投递叠加),先在对端查 send-log 或 status 核实
ssh 自身失败(退出码 255)SSH_TRANSPORT_FAILED不通/密钥/known_hosts;自证命令就在报错里:ssh <对端> crosschat --version
远端 crosschat 业务错原错误码 + [via 对端] 前缀对端把 crosschat: CODE: message 写到 stderr,本机从 stderr 匹配后同码重抛——MESSAGE_TOO_LARGE 的自纠指引跨机原样有效;匹配不上的(对端崩了)归 REMOTE_FAILED,带 stderr 尾部

两个超时叠着用:外层是本机等待上限,默认 120 秒,环境变量 CROSSCHAT_SSH_TIMEOUT_MS 可调;内层是命令行里固定的 ConnectTimeout=10,TCP 连不上 10 秒就断,不陪它等满 120。BatchMode=yes 同理——任何交互提示(问密码、问指纹确认)都变成快速失败,而不是挂死到超时。

身份跟着消息走

对端的 sshd shell 里没有 agent 的环境变量——没有下面的机制,你的 claude 会话跨机发消息,到对端会被认成“human“,对话从此没法回。

解法是 --origin:本机发送时把调用方身份(claude/codex/human、完整会话 id、显示名)连同本机 hostname 和 mid 打成一个串,整体 base64 编码,作为命令行的一枚旗标带给对端。它是机器注入的,不出现在帮助里,agent 平时看不见——但对端就靠它署名:信封里的 from-name 与 from-machine 都来源于此。

两条不变量,都从真实缺陷里固化而来:

  1. origin 在场即权威。对端进程若泡在残留的 agent 环境变量里,也不能让残留改写署名——否则对方来信会被标成本机会话发的(真实缺陷 B3.1,已回归锚定)。
  2. 完整 id 才可路由,显示名只给人看。早期版本把显示名放进了路由字段,回信路条整个断掉(2026-10-07 实测缺陷,现有回归测试锚定)。

回信怎么认路

2.5 第一条跨机消息的回信命令只有 --conversation c3,没有地址——因为回信需要的全部路条,压缩在 ref 这个自包含的串里:双方端点(工具 + 完整 id + 机器名 + 指纹)、一个对话号、轮次计数。它不查询任何服务器,解码即路由。

ref 以 mc2_ 开头,后面是一段 base64url 的二进制打包。三个设计要点:

mc2_<base64url>
┌─────────┬─────────────┬─────────────┬────────┬────────┬───────┬──────┐
│ flags   │ 发起方 id   │ 收件方 id   │ 机器名 │ 指纹   │ 对话号│ 轮次 │
│ varint  │ UUID=16裸字节│ UUID=16裸字节│ 长度+串│ 长度+串│ 裸字节│varint│
└─────────┴─────────────┴─────────────┴────────┴────────┴───────┴──────┘
  类型码与存在位     │← 非 UUID 的 id 走"长度前缀+原串"逃生门,格式漂移不翻车
  1. flags 一枚 varint 记全部布局——两端是什么类型、每个可选字段在不在,一位一义
  2. UUID 压 16 个裸字节——claude/codex 的会话 id 恰是 UUID 形态,最高频路径最省;不是 UUID 的走长度前缀 + 原串,id 格式将来漂移也不会翻车
  3. 轮次用 varint——不是定长一字节,长对话过了 127 轮照样编

这套打包把回信路条从约 290 字符压到约 84——信封每轮都要带它,双方上下文都在付这个成本。日常你更不会见到完整 ref:信封和回执行尾给的是两字符短柄(c3),柄在本机会话分片里映射回完整 ref,照抄即路由;柄的分配与消息成败解耦(发失败了也不污染续话线)。旧的 mc1_ 格式(一版 JSON 打包)永久兼容解码,旧信封不过期。

回程选路在执行侧自动做,决策就三问:

对端执行「--conversation 回信、无 --via」
    │ 从 ref 解出对端机器名(= 发起方 hostname)
    ▼
本机 ssh config 里有这个别名吗?
    ├─ 有 ──► 走 ssh(2.5 走的就是这条)
    └─ 没有 ── 本机入了 tcp 星吗?
              ├─ 有 ──► 换 tcp 腿(第3课)
              └─ 没有 ─► 照旧试 ssh,失败时报错指路

你手写的 --via 永远一字不动,自动选路只在它缺席时补位。(tcp 会话的 ref 还多一段星标识,那是第3课 跨机通信(tcp)的地盘。)

同名机器

两台机器都叫 yang 的联邦真实存在。这时 hostname 失效,mid 顶上:

  • 两端 mid 都在 → mid 相同才算同一台机器;hostname 相同骗不过去
  • 任一端 mid 读不到 → 退回比 hostname(老语义,mid 不可读时仍正确)

跨机来件发现“对端 hostname 与本机相同、指纹不同“时,投递照常跨机路由,但回执会追加一行人话警告:

⚠ 两台机器同名 (yang):已按跨机路由(machine-id 不同)。建议改名避免混淆。

顺带一个实现细节:win 的 MachineGuid 带连字符、Linux 的 machine-id 是纯十六进制——比对前统一归一(小写、去非十六进制字符),但只在比对时归一,存储与传输保持各自原样。

一句话记住

跨机 = 单机 + 一个 --via:命令走一条固定的 ssh 管道,身份和回程路条都在消息自己身上——所以你只管发,地址的事系统自己认得。