Files
NotesAgentic/docs/contracts/Sync-v1契约.md
T

98 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Sync Protocol v1
状态:服务端协议原型已实现,生产运维与桌面客户端尚未验收。入口为 `server sync/sync_server`;运行时 `/openapi.json` 是字段约束来源。不能将 SQLite Fixture 结果称为 PostgreSQL / MinIO 验收。
## Rust 上传客户端增量
Workspace schema 3 新增持久绑定、上传作业与远端 heads。首次向已验证为空的远端绑定后,队列保留每次本地操作;大正文转入按摘要命名的 spool,清除已物化 outbox 的正文副本。提交基线来自远端确认值,首次发送时冻结,超时重试不得重算。确认响应逐字段核对后,heads、作业与 outbox 同事务更新。解绑封存旧绑定与队列,重新绑定从当前文件快照生成新操作,旧回调不能修改新绑定。
Rust HTTP 客户端已实现握手、登录、空远端复核、1 MiB 分块上传、查询 offset 续传、complete 和 Revision 提交。默认 HTTPS,测试 HTTP 必须显式启用;不跟随重定向,令牌不进入 URL,响应有大小限制。真实本地 HTTP Fixture 已验证 20 次编辑形成 20 个正确远端基线、同一提交重复 100 次无重复 revision。此 Fixture 使用 SQLite/磁盘对象,不替代生产 PostgreSQL/MinIO 验收。
此增量尚未开放桌面 Sync capabilityStronghold 会话接入、冲突解决与 UI、附件故障矩阵及数据分类仍在实施。
Workspace schema 4 增加持久 inbox、分页 boundary 和冲突记录,并为文件移动/删除日志记录 local/remote 来源。下载先验证长度与摘要,再登记 inbox;文件操作通过 Workspace journal 应用后才推进 cursor。进程在文件提交与 cursor 提交之间重启时使用操作回执去重。远端新增保留 file_id,远端移动/删除不回流 outbox。本机历史提交回放仅确认操作,不回退新编辑。本地待上传修改或摘要不符产生冲突,保存本地与远端内容引用后推进接收游标;同一文件保留最新待处理冲突,旧冲突记录仍保留。空本地库可以绑定已有远端并仅拉取。
真实本地 HTTP 集成已验证第二设备拉取 20 个历史 revision、稳定 file_id、零回流 outbox、本机旧历史不覆盖新编辑,以及离线同改时保留本地内容和远端冲突。重启边界测试验证文件提交前后 cursor 都不会提前推进。冲突解决事务、完整附件故障矩阵和真实双机 UI 验收尚未完成。
## 身份与数据边界
生产 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、幂等重放、移动与删除、历史恢复、稳定分页、隔离、撤销、刷新、配额、路径冲突、摘要与断点。
## 逻辑任务记录 v1
普通文件传输增加保留命名空间 `opennexus-records/v1/tasks/task_<32位小写十六进制>.json`。文件 Revision 的 file_id 与任务业务 id 分开;记录仅包含 `schema=1``kind=task``id``data`。data 白名单为 title、description、status、note_id、due_at_ms、created_at_ms、updated_at_ms。时间采用 UTC Unix 毫秒,due_at_ms/note_id 可为空;status 为 todo/in_progress/done/cancelled。任务状态只表示任务状态,不启动目标设备上的 Agent 或后台作业。
Host 在写入 journal、捕获外部修改和上传前验证记录;未知字段、未知 schema/kind、非法路径或时间拒绝。记录不允许 api_key、token、environment、permissions 等附加字段。标题最多 4096 字节、说明最多 256 KiB、记录最多 1 MiBCore list 响应最多 4 MiB。用户写入标题/说明的正文仍是用户内容,不按关键字审查正文。
桌面 Task CRUD 经 Host records broker,操作重放返回原始记录且同 ID 不同字段拒绝;Core 不以全局 SQLite 作为任务来源。已明确归属于当前 Vault 的旧 Task 表在首次访问时逐条迁移,完成后设置所有权标记,来源表保留;未分配 Vault 的全局旧数据不猜测归属。文件身份采纳时,任务链接通过 Host 别名解析,并在上传队列物化前将规范化引用写成新的逻辑记录。
用户 Skill/配置、可选对话等仍须逐类定义白名单与适配器;不得用复制任意 JSON/SQLite 代替。
## 主题与编辑器偏好记录 v1
主题设置使用 `opennexus-records/v1/theme-settings/appearance.json`kind 为 theme_settings、id 为 appearance;编辑器偏好使用 `opennexus-records/v1/preferences/editor.json`kind 为 preferences、id 为 editor。两者沿用 schema/id/kind/data 信封、文件 CAS、幂等操作和冲突保留规则。
主题白名单为已安装主题标识、编辑字体与字号、行高、代码配色、六级标题字体大小与字重;偏好白名单为恢复上次库的布尔偏好、自动保存间隔、语言、编辑模式、行宽、拼写检查、Markdown 格式与最多 20 项格式预设。Rust 拒绝未知嵌套字段并限制字符串、枚举、数量和数值范围。不传输实际 Vault 路径、凭据、权限、环境变量或自定义主题程序包。目标设备缺少主题时使用内置浅色主题并显示提示。
前端按 Vault 和记录类别持久保存草稿、基线摘要与操作 ID;重启恢复原草稿,CAS 冲突必须选择保留本地或采用 Workspace 版本。选择期间出现新编辑时保留新编辑。远端应用不产生回流。轮询空库不自动创建设置;初始上传和合并预览显式写入当前偏好,空库下载不预写设置。
## 侧栏布局记录 v1
`opennexus-records/v1/layout/sidebars.json` 使用 schema=1、kind=layout、id=sidebars。data 仅包含 primaryExpanded 布尔值、workspaceWidth 与 chatWidth 两个 200–520 的像素数值;允许小数以保留指针拖动结果。未知字段、窗口路径或越界宽度均拒绝。记录使用相同的 Vault 草稿、CAS 和冲突解决机制,初始上传/合并准备包含当前布局。
本机已有三项侧栏 localStorage 值仍作为初始偏好读取并保存,不删除来源。组件共享布局状态,远端值立即反映在侧栏;窗口可用空间不足时仅收窄渲染宽度,不改写同步偏好,恢复空间后恢复偏好宽度。布局记录不含本机窗口坐标、显示器信息、已打开文件路径或执行权限。本版本未支持的旧客户端不能被视作已验证兼容,跨版本发布兼容验收仍需覆盖新增记录类别。
## 工作区人设记录 v1
桌面端使用 `opennexus-records/v1/persona/default.json`schema=1、kind=persona、id=default。data 白名单为 version02^531 整数)、name(最多 128 Unicode 标量)、system_prompt(最多 16000 Unicode 标量)、dialogue_pairs(最多 20 对,每对仅 user/assistant,各最多 8000 Unicode 标量);整条记录仍受 1 MiB 字节限制。人设正文是用户内容,不自动赋予权限或启动任务。未知字段一律拒绝。
Core 通过绑定 Vault 的 workspace.persona.get/write RPC 读写;Host 先校验 Vault、路径和数据,再执行现有 CAS journal。设置 HTTP DTO 增加 revision 内容摘要,供表单保存时作为 expected;此摘要不写入逻辑 data。整数 version 用作显示版本,不能代替摘要 CAS。重复 operation_id 与同一输入返回持久回执,回执包含所提交记录的实际摘要。过期摘要以 PERSONA_VERSION_CONFLICT 返回,表单保留错误状态。
桌面运行的聊天/Agent 获取当前 Vault 人设,缺失记录得到空人设,不回退全局 SQLite。Web 模式保持原来的全局存储。旧全局数据保留,不自动复制到任意 Vault;用户可通过下述预览流程明确选择导入当前 Vault。表单加载时保留 Vault 身份,切换 Vault 后禁止提交旧表单。头像仍只在本机保存,不在此记录内。独立设备 UI、导入中断故障矩阵与跨版本兼容需另行验收;本机两个实际客户端的同改收敛已另有测试证据。
旧全局人设导入使用只读 GET `/api/settings/persona/legacy`,仅桌面模式提供,并先向 Host 验证当前 Vault。返回 available 与旧人设内容,不返回旧记录 revision,也不写入任何 Workspace 记录。UI 展示预览后,用户可以点击“填入当前表单”;这只替换可编辑内容,保留当前目标的 version/revision。最终保存沿用正常人设 PUT、Host CAS 与 journal。取消预览/关闭表单不会导入,切换 Vault 后迟到响应被丢弃,旧 SQLite 来源始终保留。没有自动清除或把读取行为作为迁移完成标记。
逻辑记录的“另存副本并采用远端”可将原始记录内容导出为 `attachments/*.txt`;文本副本作为普通附件同步,拥有独立 file_id,不作为活动设置自动应用。副本目标必须属于同步白名单;若目标本身位于记录命名空间,还必须与原始记录 kind/id/schema 一致。这些检查在持久化冲突决定之前完成,因此无效路径不占用该冲突的解决选择。