摘要

  • RFC 8620 中的 state 代表一个账户内某类全部数据的服务器状态;值变化后,客户端应丢弃缓存或取得精确差异。
  • StateChange 只提示哪些账户和数据类型的状态已经移动。它本身没有行为主体、对象前后值、原因、可证明顺序或邮箱结果。

一条要求继续查询的通知

Neil Jenkins 与 Chris Newman 在 RFC 8620 中解决的是同步成本问题。移动端或网页客户端若每次都下载整个账户,只为确认本地副本是否过期,会浪费网络与电量。因此 Foo/get 返回一个较短的 state 字符串。相关类型的数据一旦变化,字符串必须变化;数据未变时,服务器通常应当返回同一字符串。

这个字符串的范围很清楚:它代表账户中该类型的全部数据,不只是某次响应里出现的对象。客户端收到不同值后,不能从字符串推断细节。规范要求它放弃这一类型的缓存对象,或者调用 Foo/changes 取得确切变化。状态值是缓存一致性的边界,而不是一段事件叙述。

因此,新的状态值不能自然带出更大的结论。它没有指出谁发起请求、哪一项策略批准了操作、对象原来的字段为何、后来字段为何、事件为何发生,也没有保存一条可长期复核的时间序列。它可以说“需要同步”;若要说“某人以某种授权修改了某邮箱”,证据必须来自执行和记录该动作的层。

差异列表也不是完整审计

/changes 进一步体现了这个分工。客户端提交之前的 sinceState,服务器返回 oldState、newState,并可列出新建、更新、销毁的标识符。这是让缓存追上服务器的合适工具。

但标识符列表仍然不是天然的审计账本。要证明主体,需要认证身份、关联请求和授权决定;要证明内容,需要相关字段的前后表示;要证明顺序和可追责性,需要时间语义、完整性保护和保存规则。服务商可以围绕 JMAP 建立这些记录,却不能把这些未出现的事实塞进一个状态字符串。

这一区别在邮箱操作中尤其重要。控制台看到 Email 或 Mailbox 的状态变化,容易把“客户端应取回差异”写成“某个邮箱操作已经被证明”。前一句可能完全正确;后一句需要对象级和主体级的额外证据。

一次推送可以压缩多个变化

StateChange 将账户映射到自上次推送以来已经变化的数据类型状态。客户端把新值与本地值比较,必要时请求差异。RFC 8620 的示例允许服务器把涉及两个账户的数个变化合并为一个推送对象。

这种合并正是推送效率的来源,也限定了它的含义。一条通知不必对应一个事件、一个用户、一封邮件或一条完整顺序。若把它贴上“审计”标签,就把压缩的同步信号误当成了未被提供的事件记录。

问题 有用的 JMAP 证据 仍需的证据
缓存是否最新? 相同的 state 对这个窄问题不需要其他证据
客户端要补哪些对象? /changes 的标识符 必要时取得对象内容
谁修改了邮箱? StateChange 单独不能证明 身份、请求与授权决定
收件人或用户得到了什么结果? 通用状态不能证明 投递、邮箱或用户边界的观察

Jenkins 所参与的架构价值,在于它不把同步信号包装成更大的保证。严谨的运行方式应把状态证据留给同步,把审计证据留给审计。

来源