feat(sync): 实现设备认证与对象版本协议原型

This commit is contained in:
2026-09-07 15:45:37 +08:00
parent f193f699b2
commit 7fffbcd55a
17 changed files with 1444 additions and 0 deletions
+43
View File
@@ -0,0 +1,43 @@
# Sync Protocol v1
状态:服务端协议原型已实现,生产运维与桌面客户端尚未验收。入口为 `server sync/sync_server`;运行时 `/openapi.json` 是字段约束来源。不能将 SQLite Fixture 结果称为 PostgreSQL / MinIO 验收。
## 身份与数据边界
生产 CLI 只接受 `postgresql+psycopg`,账号通过 `python -m sync_server create-user` 交互初始化。无开放注册、默认密码或内置共享账号。设备由每次登录注册;Access Token 有效 900 秒,Refresh Token 30 天,数据库只保存摘要。刷新轮换使旧会话立即失效。注销删除当前会话;设备撤销使该设备所有会话与上传立即不可访问。社区与 Sync 身份完全独立。
普通读写须带 `Authorization: Bearer …`。访问对象也必须验证 Vault 所有者和设备;摘要不是访问凭证。服务器可见明文,**不支持 E2EE**、多人共享和 CRDT。
## 接口
所有业务接口位于 `/sync/v1`。错误为 `{error:{code,details}}`;校验错误不回显输入。无正文与凭据访问日志。
| 方法与路径 | 行为 |
| --- | --- |
| GET `/handshake?protocol=1` | 限额、协议与保留策略;不兼容为 426 |
| POST `/auth/sessions` | username/password/device_name,登录失败限流 |
| POST `/auth/refresh` | refresh_token,轮换会话 |
| DELETE `/auth/sessions` | 注销 |
| GET `/devices`DELETE `/devices/{id}` | 列出及撤销当前用户设备 |
| POST、GET `/vaults` | 创建、列出当前用户 Vault |
| POST `/vaults/{id}/uploads` | content_hash/size,预留配额,一小时上传期 |
| GET、PUT、DELETE `/vaults/{id}/uploads/{upload}` | 查询 offset、按 offset 写入、取消 |
| POST `/vaults/{id}/uploads/{upload}/complete` | 长度和 SHA-256 复核,写入对象存储 |
| POST `/vaults/{id}/revisions` | 幂等提交与 CAS |
| GET `/vaults/{id}/changes` | cursor/limit/boundary,一致分页边界 |
| GET `/vaults/{id}/history/{file_id}` | before/limit 倒序历史 |
| GET `/vaults/{id}/objects/{hash}` | 经鉴权读取与摘要复核 |
上传分块最大 1 MiB,对象最大 100 MiB。offset 冲突为 409 `UPLOAD_OFFSET`,响应给出已确认偏移;客户端查询偏移后续传。完成请求响应丢失时重新预申请同一摘要即可确认对象已存在。未完成上传不允许提交 Revision。
提交字段:operation_id、file_id、base_revision、path、operation (`put`/`delete`)、content_hash、size。device_id 从会话取得,客户端不能冒充。移动使用同一 file_id、新 path 及当前对象;恢复历史使用历史摘要、新 operation_id 和当前 base_revision,生成新 Revision。删除必须携带当前基线、空摘要和 size=0。
Vault 行锁内执行幂等键验证、CAS、路径检查、序列分配及历史和当前元数据写入。相同请求重试返回原结果;改变请求内容或设备重用同一幂等键为 409 `IDEMPOTENCY_REUSED`。基线不匹配返回 `REVISION_CONFLICT` 及当前 Revision,客户端必须保留本地内容并显示冲突。
路径须为 NFC,拒绝 Windows 保留名、控制符、穿越、反斜杠、绝对路径、尾随点/空格与 `.ainote`/`.git`。大小写折叠后检查同名及文件/目录前缀冲突。文件身份不由路径推导。
## 保留与未交付边界
当前历史和 tombstone **永久保留**,游标不主动过期;不启用 GC,以免缺少恢复演练时删除历史对象。配额包含全部历史对象,未完成上传预留额度。通知尚未实现,客户端必须主动按 cursor 拉取。上传过期清理、对象 GC、限速指标、完整备份恢复工具与生产负载验收仍未交付。`/ready` 目前只检查数据库 schema,不证明 S3 就绪。
验证向量见 `server sync/tests/test_protocol.py`:双设备 CAS、幂等重放、移动与删除、历史恢复、稳定分页、隔离、撤销、刷新、配额、路径冲突、摘要与断点。
@@ -0,0 +1,34 @@
# 第三阶段实施与验收记录
基线:`f193f69`;分支:`feat/phase3-completion`。范围以[第三阶段实施规划](../architecture/第三阶段实施规划.md)为准。**第三阶段尚未完整交付,不可作为发布候选。**
## 决策与状态
| 项目 | 决策 / 当前状态 | 依据与恢复 |
| --- | --- | --- |
| 同步入口 | 冻结 `server sync/`,生产 PostgreSQL + S3 | SQLite + 磁盘仅用于隔离协议测试 |
| Sync v1 | CAS、幂等、设备撤销、对象上传和历史已实现 | [契约](../contracts/Sync-v1契约.md),完整生产验收待执行 |
| 历史保留 | 永久保留 Revision、tombstone 和历史对象 | 暂不 GC,容量达到配额时拒绝上传;可逆地增加保留策略 |
| Host | `frontend/src-tauri/`Rust 文件日志及 outbox | Tauri 与 Sidecar 生命周期、完整交互待验收 |
| 平台隔离 | 未证明 OS 沙箱的第三方进程默认禁用 | 能力许可不等同 OS 沙箱,不以用户点击绕过 |
| 发布 | 不启用自动更新与安装包分发 | 签名、迁移、恢复、三平台实机尚未通过 |
## 已执行验证
- Sync v118 项隔离测试通过,Windows / Python 3.13.9 / SQLite / 磁盘对象 Fixture。包括双线程并发 CAS;不是 PostgreSQL 多 Worker 压测。
- 依赖:后端和前端 frozen 安装成功;Sync 独立 uv.lock 已生成。
- 真实模型、个人 Vault、真实音频和凭据未作为测试输入。
## 完整范围跟踪
| 里程碑 | 状态 | 未满足退出条件 |
| --- | --- | --- |
| M0 | 进行中 | 社区签名契约、平台原型、负责人/预算确认、完整 Host 原型 |
| M1 | 待开始 | 原生编辑完整闭环、Sidecar、Stronghold 迁移、菜单与多窗口 |
| M2 | 待开始 | 社区与安装升级事务、真实包全生命周期、安全隔离 |
| M3 | 进行中 | PostgreSQL/MinIO 运行与备份恢复、运维和负载门禁 |
| M4 | 待开始 | Rust 上传/拉取循环、冲突 UI、两台实机离线同步 |
| M5 | 待开始 | 七类社区、OCR、质量专项和主题/WebView 交互矩阵 |
| M6 | 待开始 | 安装更新签名、升级迁移、三平台和性能门禁 |
本机未发现 Docker 命令;Compose 尚未运行。真实厂商调用需获准账号与费用上限,音频质量需有授权且带标注的数据,当前均未执行。不能以这些外部条件缺失解释尚未实现的普通工程范围。