Safe Haven 中文文档 下载 App

Safe Haven 索引同步用 BITS 协议做增量传输,七天才拉一次全量

BITS 是什么

BITS(Background Incremental Transfer System,后台增量传输系统)是 Safe Haven 客户端和服务器之间同步应用目录的协议。它的核心目标是最小化数据开销——避免每次同步都下发整份 index.json。

两种同步模式

Safe Haven 客户端向服务器同步 endpoint 发送一个 since 参数(Unix 时间戳),服务器根据这个参数决定返回什么:

从未同步或 timestamp 超过 7 天

返回全量索引——整份 index.json,包含所有应用的完整数据。这是冷启动场景,只有第一次或长时间没同步才会触发。

timestamp 在 7 天以内

返回增量补丁——服务器遍历 7 天内的 changelog,计算出这期间发生了哪些变化,只返回变化的部分。

changelog 怎么维护

服务器在 S3 存储桶里和 index.json 同路径维护一个 index_changelog.json 文件。这是一个持久化的变更日志,每次 index.json 有变化时服务器会:

  1. 往 changelog 末尾追加一个事件记录:哪个包名变了、哪个包名被删了、变化的时间戳
  2. 每次写 changelog 时,自动删除7 天前的事件(滚动修剪)

这样 changelog 永远只保留最近 7 天的事件,大小可控。

补丁怎么计算

客户端带 since: 1715763600(假设是 4 小时前的时间戳)来同步:

  1. 服务器遍历 changelog 里所有 timestamp > 1715763600 的事件
  2. 对同一个包名的多条事件做折叠——只保留最后一次的净效果(比如一个应用先被更新再被删除,折叠后就是"移除")
  3. 产出两个数组:

补丁结构示例:

{
  "action": "patch",
  "timestamp": 1715850000,
  "removes": [
    "com.example.oldapp"
  ],
  "updates": [
    {
      "packageName": "com.example.updatedapp",
      "name": "Updated App Name",
      "summary": "A short summary",
      "trustLevel": "verified_source",
      "versions": [
        {
          "versionName": "2.1.0",
          "versionCode": 42,
          "apkPath": "apps/com.example.updatedapp/42/app.apk",
          "sha256": "ab12cd...",
          "scannedAt": 1715849000
        }
      ]
    }
  ]
}

补丁的过滤规则

全量索引和增量补丁都会过滤:

客户端怎么处理补丁

客户端收到补丁后:

  1. 把 updates 里的应用合并进本地索引(按 packageName 匹配,有就更新,没有就新增)
  2. 把 removes 里的包名从本地索引里删除
  3. 更新本地的同步时间戳,存为下次同步的 since 参数

为什么用 7 天窗口

7 天是一个工程折中:

同步走的是什么通道

Safe Haven 的同步端点跑在 Cloudflare Workers 上——这是 Safe Haven 后端的三大组件之一(Routing Backend)。Workers 从 S3 存储桶读取 index.json 和 index_changelog.json,做增量计算,返回结果。

好处:

和其他应用商店的同步对比

商店同步方式特点
Safe HavenBITS 七滚动增量明确的 since 时间戳和 changelog 机制
F-Droid全量索引 + 缓存不做增量,每次拉完整 index-v1.json
Google Play黑盒(Protobuf)客户端做请求,增量由 Google 决定

F-Droid 客户端每次同步都拉全量 index-v1.json——文件不大(压缩后几 MB),但机制上比 Safe Haven 简单。Safe Haven 在增量这块做得更精细,代价是后端维护 changelog 的复杂度。