返回博客

同步

配对之后,数据怎么走:Syzygy 的 P2P 同步机制拆解

从配对的三条状态链,到路径探测的预算与排序,再到同步队列的任务状态与离线行为,拆解 Syzygy 点对点剪贴板同步的完整机制,并给出对应每个环节的排障方法。

Syzygy 团队发布于 阅读约 13 分钟更新于
  • #P2P 同步
  • #设备配对
  • #剪贴板同步
  • #端到端加密
  • #点对点传输
  • #网络诊断
本文目录

你在电脑上复制了一段内容,手机就放在旁边,网络也正常,内容却没有出现在手机的历史里。这种时候,问题可能出在四个互不相同的环节:两台设备是否被允许同步(信任)、数据该走哪条路(路径)、传输任务由谁安排(调度)、内容何时算真正到手(落库)。把这四件事混在一起描述,"同步失败"就永远只能靠重启解决。Syzygy 把它们拆成了可以单独观察的机制,本文逐一拆解,并解释每个环节失败时会表现成什么样子。

文中机制来自两类一手材料:随代码维护的设计决策记录(ADR),以及对应模块的实现。凡是实现与设计意图之间还有差距的地方,本文如实标注,不做美化。

配对不是一次点击,而是三条独立的状态链

在界面上,配对看起来就是"点一下发起、对方确认、完成"。底层实际有三条各自独立的状态链在推进:

状态链拥有者成功的含义
Authorized → Active → release授权服务商业关系(席位与配对资格)最终确认
waiting → pending_confirm → confirming → 终态配对会话这次交互的成功或失败
安全材料导入 → trusted peer → runtime ready本地节点加密材料落盘、本地信任与运行就绪

拆开的原因很实际:这三件事由不同的组件负责,失败原因也完全不同。授权被拒是席位或角色问题,会话取消是用户行为,材料导入失败是本地状态问题。混成一个状态字段,任何一环出问题都没法定位。这不是一次跨服务的原子事务,本地已可信而授权尚未确认的中间态客观存在,设计上按当前阶段的失败与重试机制处理;补偿与恢复的覆盖范围需要分别验证,文档明确拒绝"所有阶段已有自动回滚"这种说法。设计决策里同时禁止了几种捷径:可达性预检只证明"能连上",不代表"有配对权限";二维码扫描成功、对端探测成功,都只是单环节的进展;本地信任已写入但授权尚未确认时,系统不会自动把关系"补"成成功。

本地信任链里还有一个容易被忽略的就绪屏障。导入配对令牌之后,设备要等发现协议的密钥所有者真正就绪,才对外发布"我准备好了"的信号;安全材料落盘、运行时就绪、服务端关系生效各有一个独立的提交点。没有这道屏障,对端可能拿着一台半成品设备开始同步,同步到一半才发现密钥状态不完整。

Syzygy 设备配对界面:找到对端设备并发起配对
Syzygy 设备配对界面:找到对端设备并发起配对

配对入口:在对端列表里选中设备、查看信息、发起配对。之后的授权、确认、信任提交各走各的状态链。

这份诚实也延伸到未完成的部分:设计决策记录成文时,真机回归账本上还挂着一项配对运行时的待验证问题。决策文档明确写着"不能把实现和测试的存在当作真实设备回归通过的证明"。把这类边界写进文档,是排障时判断"是设计如此还是故障"的前提。

为什么新建配对必须联网

配对的授权环节需要授权服务在线。这是一个容易被视为限制的设计决定,动机是防止席位被重复使用:配对资格是一种计数资源,1 个席位对应 1 台 Pro 电脑与最多 2 台免费手机,如果允许离线配对,同一席位可以在两批设备上各自成立,额度就失去了意义。授权服务是额度的唯一权威计数方,新建配对时实时验证,确认后转为生效关系。

配对的操作路径本身不长:在一台设备上打开 Peers(设备)界面,选择配对新设备,选中对端后进入对方的信息展示列,点击发起配对;对方确认,双方各自完成本地信任提交,同步资格随之建立。

角色组合的判定是一条纯函数规则,客户端不会因为本地显示"已购买"就自行放行。现行矩阵如下:

设备组合结果
免费手机 ↔ Pro 电脑允许,手机侧额度 1,电脑侧额度 2
Pro 电脑/手机之间的任意组合允许,不设关系额度
涉及免费电脑的组合拒绝
免费手机 ↔ 免费手机拒绝
免费手机 ↔ Pro 手机拒绝

两个边界值得记住。其一,"已授权"与"已生效"是两个状态,授权通过后还要等本地流程完成才真正生效。其二,配对被拒不会停掉本地节点:被拒设备上的剪贴板历史、本地加工能力照常工作,只是不能与那台设备同步。领域模型里保留着离线缓存授权、局域网授权这样的类型,但它们被明确排除在隐式回退之外:若未来开放离线配对,必须重新做一致性与撤销设计,而不是顺手把开关打开。席位购买与激活的完整操作见 Pro 激活指南。

密钥怎么走,密文在哪里

信任链的终点是加密材料落盘。Syzygy 的内容加密使用 XChaCha20-Poly1305,按块流式处理;正文在捕获的那一刻就完成加密,落盘的是密文,设备间传输的也是密文,中继转发的是它读不懂的字节。

密钥的传递路径只有一条:配对令牌。发起配对的设备生成令牌,里面带着内容加密密钥与发现协议的独立密钥,对方设备导入令牌时校验密钥标识与安全模式,全部匹配才写入信任。安全模式校验有个严格的细节:即使握手过程中已经收到了对方的数据,模式不匹配时依然不写成功关系,防止"数据都到了就放松检查"。密钥本体由每台设备本地保管,密钥标识用于区分密钥世代。配对完成后,密钥不再有任何外发动作。

Syzygy 受信任设备列表:信任关系建立在加密材料落盘之上
Syzygy 受信任设备列表:信任关系建立在加密材料落盘之上

信任列表:每一条信任关系的背后都是一次完成落盘的密钥导入,删除信任即终止同步资格。

一个被明确写下来的取舍:加密采用确定性 nonce,同一明文加同一密钥永远得到同一份密文。收益是存储去重依然成立,两台设备复制过的相同内容,底层只保留一份密文;代价是密文之间可以比对出"内容是否相同",这类等价泄露在个人两三台设备的规模下被评估并书面接受。对应的边界是:去重只在同一把密钥的世代内有效,换钥之后相同明文会生成新的密文与新的哈希。

摘要也不裸奔。安全模式启用时,设备向发现协议广播的头部信息(含预览内容)同样以密文发送,没有完成配对的第三方即使收到广播字节,也还原不出预览内容。密文通道覆盖的正是这些容易被忽略的边角:正文、摘要、发现广播,一个都不例外。

flowchart TD
    A["设备 A 复制内容"] --> B{"是否已建立 Pro/Team 可信关系?"}
    B -->|"否"| C["留在本地工作台"]
    B -->|"是"| D["客户端本地加密并探测路径"]
    D --> E{"能否直连?"}
    E -->|"局域网 / NAT 穿透"| F["P2P 直传"]
    E -->|"直连失败"| G["加密 relay:服务器只搬运密文"]
    F --> H["设备 B 解密并写入历史"]
    G --> H

路径探测:候选地址与实际路由是两回事

把网络层想象成快递:通讯录里存着对方的家庭住址、公司地址和老家地址,这些是"可以尝试投递的地方";今天包裹实际走了哪条路,是另一回事。Syzygy 严格区分这两者:候选地址回答"可以尝试哪里",认证连接回答"实际连到谁、走哪条路"。

候选来自几个发现源:局域网内的多播发现(mDNS)、节点间互相观察到的地址(包括 VPN 虚拟网卡的地址)、公网分布式记录(mainline),以及中继服务地址。中继是什么:当双方都在严格的 NAT 后面、直连打不通时,公网上的转发节点替两边搬运流量,好比两个人都不肯开门,快递由前台代为转交。中继是兜底路径而非首选,一旦直连建立,数据不再经过它。顺带分清两个词:局域网描述地址所在的环境,直连描述会话最终采用的路径,VPN 地址承载的会话同样可以是直连。设备身份是节点公钥,地址只是拨号线索,一个未认证的地址能连通不代表它可信。

探测是消耗资源的动作,Syzygy 给它定了硬预算:全局并发 8 个、每台对端 2 个、每轮最多尝试 8 个候选、每台对端 8 秒预算。同一台对端的探测请求合并为单次飞行,界面反复刷新不会叠加探测;网络切换时旧的探测结果按代际作废,不会用咖啡店的旧数据覆盖家里网络的健康状态。

排序只认证据。只有认证连接上报的实际路径才算数,排名区分"已选定直连""已选中继""其他"与"无遥测"几档;猜测地址段属于哪种网络只是启发式,不会被提升成实际路径。RTT 历史保存在内存里,保留 2 小时、最多 720 个采样点,重启即清空。诊断数据的导出只包含诊断所需的字段,不导出密钥或原始私密内容,报告里会明确标注遥测缺失与启发式推断。诊断里的 RTT 是控制层往返耗时,不是 ICMP ping,也不是"从复制到对端可粘贴"的全程耗时,目前系统还没有覆盖剪贴板交付全程的分布式追踪,诊断界面不会假装它有。

连接的复用与释放也有明确规则。传输基于 QUIC,控制信令、正文传输与群组广播各走各自的协议连接,连接池按设备加协议两个维度复用;网络端点的生命周期归节点运行时所有,重启节点时旧端点必须完全释放,端口才能重新绑定。这些规则决定了两个用户可感知的行为:诊断信息里能看到每类连接的实际路径;节点关闭后立刻重启时,端口的重新可用可能需要一瞬间,这是释放流程的一部分而非卡死。

同步状态机:摘要、正文与三种状态

同步分两层。摘要先行,正文跟进:摘要包含内容类型、预览与时间,体积小、到达快,让对端列表立刻显示"有新内容";正文与文件体积大,按该设备的模式与阈值决定是自动到达还是等待手动下载。对"先知道、后拿全"这个次序的坚持,是大文件场景下流量与体验平衡的关键。

Syzygy P2P 同步的两层结构:摘要先行,正文按策略跟进
Syzygy P2P 同步的两层结构:摘要先行,正文按策略跟进

摘要与正文分离:先花很小的流量让对端"知道",再按阈值决定正文何时到位,超大内容始终保留手动决定权。

每条远端内容在本地有三种投影状态:pending(只知道有这条,还没取正文)、syncing(正在取)、local(已落库,可粘贴可预览)。队列里的任务分四类:拉取摘要、物化正文、物化文件条目、下载文件。每类任务独立记录状态:排队中、执行中、延期、被阻塞、成功、失败、已取消,全部持久化,应用重启后任务还在,重试带退避与预算,取消会记录类型化的原因(用户取消或被手动抢占)。"被阻塞"值得单独一提:低功耗模式、离线、计量网络这类环境门禁不满足时,任务不是失败,而是挂起等待条件恢复。

stateDiagram-v2
  [*] --> 排队中: 创建任务
  排队中 --> 执行中: 规划器派发
  执行中 --> 成功: 内容完整且校验通过
  执行中 --> 失败: 超过预算或重试上限
  执行中 --> 延期: 退避等待后重试
  延期 --> 排队中
  执行中 --> 被阻塞: 环境门禁不满足
  被阻塞 --> 排队中: 条件恢复
  执行中 --> 已取消: 用户取消或被抢占
  成功 --> [*]
  失败 --> [*]
  已取消 --> [*]

调度权在后台规划器手里,界面只表达意图,不直接派发网络请求。规划器统一管去重、预算、优先级与重试,这避免了两个常见问题:UI 层重复发请求,以及"看起来在同步"但没人记录任务状态。手动下载是一种触发指令,与实时/按需两种模式平行存在:任何模式下,超阈值内容都可以手动下载,模式只决定自动下载的阈值,实时模式默认 50MB(可调 0–200MB),按需模式默认 5MB(可调 0–200MB)。大文件在弱网下的手动下载会进入后台队列慢慢传输,不阻塞前台操作。

Syzygy 单台设备的同步配置:模式、阈值、补抓条数与优先级
Syzygy 单台设备的同步配置:模式、阈值、补抓条数与优先级

按设备配置:阈值、离线补抓条数与优先级都是每台对端一份,而不是全局一刀切。

离线重连的追赶同样有上限:只补抓每台信任设备最近的 N 条摘要,默认 3 条、可调 1–10 条;手动点击"立即同步"也是同样的规则。这是有意为之,重连不该变成历史洪泛,尤其是移动端,流量和电量都要省着用。另有一个如实记录的实现差距:多设备并发时,优先级影响调度与展示顺序,但当前实现是在向各设备发起预取之后才对返回结果排序,"高优先级必然先发出请求"这句话当前说不满,文档里没有把它写成承诺。

离线、冲突与"到底同步好了没有"

剪贴板同步天然回避了经典的多端编辑冲突:每条内容是记录,不是文档。事件身份由来源设备加序号构成,也是数据库主键,跨端同步按身份幂等;两台设备各自复制产生的内容互不覆盖,相同内容按内容哈希共享同一份存储。没有合并,也就没有冲突标记。编辑加工的派生内容也遵守同一套身份规则:在操作台里解码、改写后贴回的内容是一条新的独立事件,并记录它的输入来源,本地保留这张派生关系图。

"同步完成"的判定标准只有一条:内容是否真实落在本地存储。传输按内容寻址进行,数据经哈希校验,校验不通过的写不进来;下载任务失败会自动重试,界面上短暂的失败提示不代表最终结果,以设备本地是否有完整文件为准。队列还有字节预算控制(自动或无上限),弱网环境下载大文件会排队慢慢传,不影响前台操作。预算的意义在于把"同步"约束在一个可预期的资源包络里,任何设备都不该因为后台同步被流量或磁盘意外击穿。

排障清单

把前文的机制对应到常见症状:

症状先检查机制
发起配对被拒角色组合与席位额度;授权服务可达性角色矩阵拒绝免费电脑、免费手机互配;新建配对需要实时授权
配对成功但不同步四个前提逐项过:角色允许、云端授权通过、双向本地信任、网络连通同步执行要求全部满足,缺一不产生"半配对"
内容只有摘要不自动下载该设备的模式与阈值;实时 50MB、按需 5MB,超阈值即等手动下载模式只管自动阈值,手动下载随时可用
重连后"缺了内容"离线补抓上限,默认 3 条(1–10 可调)追赶同步有意限量,防止历史洪泛
诊断显示走了中继直连未建立时的正常回退;中继只见密文排序只认认证连接的实际路径证据
RTT 数字与体感不符RTT 是控制层往返探测,不是剪贴板交付耗时诊断不报告尚未实现的端到端交付时间
下载短暂失败后恢复等待自动重试完成重试是传输层的正常行为,以本地落库为准
节点重启后立刻重启报端口不可用稍候重试端点生命周期归节点运行时,旧端点完全释放后端口才能重绑
诊断里没有历史 RTT重启后 RTT 窗口清空,属设计行为RTT 序列保存在内存,保留 2 小时、最多 720 点

设备与同步的设置都在 Peers(设备)界面完成,被信任的设备可以随时调整阈值、降低优先级或直接屏蔽;屏蔽立即生效,对端失去同步与访问资格。配对与信任的变化只改变授权关系,任何一端已经落库的本地历史都不会因此被删除,清理数据走本地垃圾箱。本文的机制细节若有与实际行为不符之处,以应用内的实际状态为准;更多使用层面的操作指引见安装与首次运行指南与产品第一篇:你好,Syzygy。配对与路径只是地基,同步之上还有一整层值得拆的内容,将在后续文章里继续。

常见问题

配对成功但内容不同步,先检查什么?

同步生效要同时满足四个条件:设备角色组合被允许、云端授权验证通过、双方完成本地信任确认、网络连通。缺任何一个都不会同步,也不会出现半配对状态,按这四项逐个排查即可。

中继服务器能看到我的剪贴板内容吗?

不能。正文在捕获时就用 XChaCha20-Poly1305 加密,落盘和传输的都是密文,密钥只通过配对令牌交换一次。中继搬运的只是它读不懂的字节,发现广播同样走密文通道。

设备离线期间的剪贴板历史会全部同步过来吗?

不会。重新上线只补抓每台信任设备最近几条摘要,默认 3 条,可在 1–10 条之间调整。这是有意设计的上限,避免重连时把离线期间的全部历史灌进设备。

直连和中继会自动切换吗?

会。排序只依据认证连接上报的实际路径证据,直连可用时优先使用,直连打不通时回退中继,中继搬运的也只是密文。

如何彻底停止与某台设备的同步?

在 Peers 界面对该设备开启屏蔽或删除信任。屏蔽立即生效,对端即刻失去同步与访问资格;本地节点的其余能力不受影响。