Skip to content

VMax 兼容性与迁移策略

1. 目标与边界

本文定义 API、协议、数据库、配置和部署升级的兼容规则。首版不支持跨实例联邦、多设备同时在线或服务端聊天历史恢复,迁移设计不得暗中引入这些能力。

  • 来源 MUST:未知协议版本拒绝;协议升级通过能力协商建立新会话,不在原会话内降级或切换密码套件。
  • 来源 MUST:切换服务器不能复用旧实例身份;各 Profile 的身份、数据库、Keychain 和通知路由隔离。
  • 来源 MUST:数据库迁移可回滚或安全失败,并在 CI 与恢复演练中验证。
  • 已验证事实:当前没有 API、schema 或发布构建,尚无历史版本需要真实迁移;以下规则应在首个实现前固化到 CI。

2. 版本轴

VMax 至少有五个独立版本轴,禁止用单一“App 版本”替代:

  1. api_version:HTTP 资源与错误语义,例如 /v1
  2. protocol_version:E2EE 信封和会话能力。
  3. config_version/.well-known/vmax/config 签名配置结构。
  4. schema_version:PostgreSQL 迁移版本。
  5. backup_format_version:客户端备份包版本,跟随 ADR-007。
  6. job_version:PostgreSQL outbox 与 Redis 后台任务 envelope 版本。
  7. object_api_version:P1 S3 附件元数据与上传流程版本;MVP 不启用。

密码套件、canonical encoding、身份地址和备份格式的具体值均为待总控决策,本文只要求版本轴不可混用。

3. 兼容矩阵

实例发现响应公布支持的 API/协议范围和 feature flags。客户端连接前计算交集:

  • 没有共同 API 主版本:阻断连接,提供升级/联系管理员提示。
  • API 可用但协议无交集:允许查看实例信息,不允许创建身份或发消息。
  • 未知可选能力:忽略并保持基础功能;未知必需能力:拒绝。
  • 实例签名密钥或固定指纹变化:阻断,不自动迁移信任。
  • attachments=false:图片、视频、文件入口和上传调用全部禁用;这不是实例故障。

发布物必须维护 客户端版本 × API 版本 × 协议版本 × schema 版本 的测试矩阵,至少覆盖当前版与仍在支持窗口内的上一版。

4. OpenAPI 演进

4.1 /v1 内允许

  • 新增可选响应字段。
  • 新增客户端可忽略的枚举值,但只有 schema 明确允许 unknown 且客户端有安全默认行为时可用。
  • 新增端点或可选能力。
  • 放宽非安全边界的上限,同时保持客户端兼容。

4.2 /v1 内禁止

  • 删除/重命名字段、改变字段类型或单位。
  • 将可选字段改为必填、收紧已发布范围、改变状态码或错误码语义。
  • 复用旧字段承载不同安全语义。
  • 让旧客户端因未知值而静默降低认证、隐私或密码强度。

破坏性变化创建 /v2,并定义双版本期限、客户端最低版本和退役指标。未验证旧版流量归零前不得移除旧端点。

5. 协议升级

  • 每个会话固定 protocol_version 与密码套件;升级通过新会话建立并显示必要的安全状态变化。
  • 服务端只能根据实例允许列表接受/拒绝 opaque 信封版本,不参与密码套件降级协商。
  • 客户端对未知版本、篡改版本和不支持套件安全失败,不“尝试兼容”。
  • 协议版本冻结前必须具备 canonical encoding、跨语言向量、降级攻击、乱序/重复/重放和状态迁移测试。

协议编码和套件仍属密码线路,待总控决策;本文不得被引用为生产安全证明。

6. 数据库迁移

6.1 expand-contract

  1. 备份并验证可恢复性。
  2. 部署兼容新旧 schema 的代码。
  3. 添加新表/字段/索引,避免长事务和阻塞式全表重写。
  4. 分批回填并记录检查点;失败可恢复。
  5. 切换读写路径并观察错误、锁、复制延迟和磁盘。
  6. 支持窗口结束后再移除旧结构。

6.2 回滚边界

  • 代码回滚只有在旧二进制仍兼容新 schema 时允许。
  • 数据迁移不可逆时,必须提供 forward-fix 或从已验证备份恢复的 Runbook,不能伪称“一键回滚”。
  • 密文或协议状态不得在迁移中被重新编码,除非对应协议/编码 ADR 已冻结并有逐字节验证工具。

7. 部署滚动升级

  • API/Worker 版本需要在混合运行窗口兼容相同 schema 和 outbox 语义。
  • 先迁移数据库,再部署兼容 API,再部署 Worker;清理旧 schema 最后执行。
  • Worker 任务必须版本化;旧任务由新 Worker 可安全识别、重试或送入隔离队列。
  • 健康检查分别提供 liveness、readiness、dependency;迁移中不就绪实例应被摘流,但不能触发任务丢失。

7.1 PostgreSQL outbox 与 Redis 队列升级

  • 先部署能读取旧、新 job_version 的 Worker,再让 API/dispatcher 产生新版本任务,最后停止旧版本消费。
  • PostgreSQL 保存任务业务状态;Redis 只保存可重建的投递。升级不得把 Redis backlog 当作唯一迁移来源。
  • 未知 job 版本进入有界隔离队列并告警,不能确认成功、静默丢弃或无限热循环。
  • Redis 清空/更换集群后,reconciler 从 outbox_events 重建未完成任务;重建前后用稳定 event_id 去重。
  • 队列库变更需要双读/双发布仅限短迁移窗口,并防止两个 Worker 对同一非幂等副作用重复执行。

7.2 S3 能力启用

S3 兼容存储已经选入总体架构,但媒体附件是 P1。MVP 发布始终声明 attachments=false,对象存储缺失不阻断核心 readiness。P1 启用顺序为:部署 bucket/lifecycle 和凭据、执行独立对象 schema 迁移、部署兼容服务端、打开实例能力、最后由兼容客户端展示入口。回退按相反顺序先关闭能力,不能先删除仍被引用的密文对象。

8. Profile 与实例迁移

切换服务器创建或导入目标实例专属身份,旧实例保留为独立 Profile 或经用户明确确认删除。禁止:

  • 静默复制身份私钥到新实例。
  • 复用旧实例 token、数据库、推送路由或固定指纹。
  • 把相同显示 ID 当成跨实例同一身份。
  • 将服务端备份描述为可恢复客户端明文历史。

身份地址映射和备份导入冲突规则分别跟随 ADR-003 与 ADR-007,待总控决策

9. 数据保留策略迁移

  • 缩短 TTL:只影响新信封,还是同时提前删除旧信封,需在管理员操作前明确显示影响。
  • 延长 TTL:默认只影响新信封,不能复活已过期或已 ACK 数据。
  • 降低配额:现有超限队列不得随意丢弃;建议阻止新入队并让其自然拉取/过期。
  • 幂等窗口不能在仍支持的客户端重试期内缩短到 24 小时以下。

以上默认行为属于 ADR-009 的待总控决策,应通过迁移测试后接受。

10. 验证与发布门槛

  • OpenAPI breaking-change 检查与生成代码 diff。
  • N-1 客户端连接新服务端、新客户端连接 N-1 服务端的契约测试。
  • 数据库从空库、上一版数据量级、失败中断和备份恢复四条路径演练。
  • 游标、幂等记录、ACK、outbox 在混合版本和重启后不丢不重。
  • 升级前后统计信封行数/字节、待清理数、幂等冲突、过期积压和 Push 失败,且指标不含敏感标识。
  • Redis 队列升级验证任务不丢、允许重复但副作用幂等;S3 关闭的 MVP 环境必须完整跑通文本消息回归。
  • 未完成跨版本、恢复和降级攻击测试前,不得宣布协议或发布兼容性已达标。

11. 待总控决策清单

决策选项建议验证
API 支持窗口仅当前版 / 当前+上一主版本首版至少当前+上一主版本的迁移测试,实际期限结合发布节奏双版本流量回放与退役告警
schema 工具Go migrate、Atlas、其他选择可审计 SQL、支持锁超时与 CI 回放的方案空库/升级/中断/重复执行
游标格式与轮换签名 JSON / 加密二进制版本化 opaque token,不暴露主键篡改、过期、密钥轮换
协议状态迁移原地升级 / 新建会话新建会话,避免同会话换套件跨版本向量和降级测试
备份版本兼容仅当前 / 多版本读取跟随 ADR-007,不在服务端线路冻结恢复旧备份、篡改和冲突测试
Redis 队列实现Go 库 / Redis Streams / 最小自建优先成熟且许可可接受的实现,PG outbox 保底清空、重启、重复、毒任务、滚动升级
S3 提供方MinIO、云 S3、其他兼容实现使用最小兼容子集,不写死供应商;MVP 关闭P1 再做兼容矩阵、生命周期与一致性测试

系统初步设计 · 待决事项不代表批准 · 安全方案尚未完成独立审计