Thunderbolt 加密数据线上格式完整解析:__enc: : 背后的设计
【免费下载链接】thunderboltAI You Control: Choose your models. Own your data. Eliminate vendor lock-in.项目地址: https://gitcode.com/GitHub_Trending/thund/thunderbolt
Thunderbolt(thund/thunderbolt)是一款主打"AI You Control"的开源 AI 助手,支持端到端加密多设备同步。本文将拆解 Thunderbolt 端到端加密在同步链路上使用的线上数据格式__enc:<iv>:<ciphertext>:它由哪几段组成、为什么这样设计,以及新手如何阅读相关源码,帮你真正理解"零知识同步"背后的工程细节。
一句话看懂:Thunderbolt 端到端加密同步
先建立整体认知:Thunderbolt 的加密同步属于零知识架构——所有用户数据在客户端加密后才上传,服务器只保存密文和包裹好的密钥,即使服务器被攻破或被迫配合,也读不到你的聊天内容。
整个密钥体系可以概括为三个角色:
| 概念 | 作用 |
|---|---|
| 内容密钥 CK | 一把 AES-256-GCM 密钥,加密同一用户的所有数据,所有设备共用 |
| 设备密钥对 | 每台设备一对 ECDH P-256 + ML-KEM-768(后量子)密钥,私钥永不出设备 |
| 设备信封 | 用混合加密为特定设备包裹的 CK,只有该设备能解开 |
完整的密钥分层设计详见官方架构文档 docs/architecture/e2e-encryption.md。
线上格式拆解:三段式密文的每一部分
当开启端到端加密后,同步通道(PowerSync 同步链路)上传/下载的每一条加密列的值,都会被写成这样的字符串:
__enc:<iv-base64>:<ciphertext-base64>逐段解释:
__enc:前缀—— 格式标识符。它的作用不只是"看起来像密文",而是权威信号:下载端只要看到这个前缀就执行解密,无需查询配置。iv(Base64 编码)—— 12 字节的随机初始化向量,由加密时随机生成。AES-GCM 要求每次加密使用不同的 IV,防止相同明文产出相同密文。ciphertext(Base64 编码)—— 用 AES-256-GCM 加密后的密文,末尾自带 16 字节的认证标签,能检测任何篡改。
选择 Base64 而非 hex 的原因也很实际:Base64 比 hex 短约 25%,在同步 JSON 里传输更省流量,且对文本数据库完全友好。
加密原语全部集中在 src/crypto/primitives.ts:encrypt()负责生成随机 IV 并输出 Base64 的 iv/ciphertext,decrypt()负责还原。
编解码核心:codec 的防御性设计
真正拼装与拆解这个格式的是编解码器 src/db/encryption/codec.ts,它对新手特别值得细读,因为里面全是"防御性编程"的细节:
- encode 侧:如果输入已经以
__enc:开头,直接原样返回,防止"密文再加密"的双重加密事故;如果内容密钥 CK 缺失但加密已完成配置,会直接抛错拒绝写入明文,宁可失败也不泄露。 - decode 侧:找不到分隔符、没有 CK、解密失败时都降级返回原始值而不是崩溃——同步链路不能因为单条数据出错就整体中断。
- CK 缓存失效机制:CK 惰性加载自 IndexedDB 并缓存在内存;登出时通过
BroadcastChannel向主线程、SharedWorker、其他标签页广播失效消息,确保过期的密钥绝不会被再次使用。
上传与下载:两条路径,同一个格式
格式之所以简单稳定,是因为上传和下载共用同一个契约:
上传路径(加密后入库到同步队列):src/db/encryption/upload-encoder.ts 中的encodeForUpload()只加密配置表里声明过的列,PUT/PATCH 走加密、DELETE 直接放行。
下载路径(解密后写入本地 SQLite):src/db/powersync/middleware/EncryptionMiddleware.ts 采用数据驱动策略——它扫描整条记录的所有字符串字段,凡以__enc:开头的都解密,不看配置。
这个不对称是有深意的:桌面端旧版本自带的加密列清单可能落后于新版本新增的加密列,但只要认前缀,旧客户端依然能正确解密新数据。格式即接口,前缀即契约。
哪些列被加密由单一事实来源 src/db/encryption/config.ts 中的encryptedColumnsMap声明,涵盖聊天标题、消息内容、任务、模型配置、提示词、技能等几乎所有用户创作内容——而设备 ID、排序字段这类非用户内容则保持明文。
为什么格式里不需要密钥版本号?
细心的你会发现__enc:里只有 IV 和密文,没有算法或版本字段。这并非疏忽:
- 算法由内容密钥 CK 的类型决定(固定 AES-256-GCM + 12 字节 IV),密钥类型不会中途更换;
- 真正需要版本化的地方是设备信封(src/crypto/primitives.ts 中
wrapCK()组装的信封首字节就是版本号0x01),因为混合密钥封装(ECDH + ML-KEM-768)更可能有协议演进; - 数据面格式保持稳定,把演进压力留给密钥面,是典型的"少变协议、多变密钥"分层思路。
类似的验证机制还能在金丝雀文件里看到:src/crypto/canary.ts 用同一个 CK 加密一个固定前缀 + 随机秘密,存到服务端,用于校验恢复密钥(24 词 BIP-39 助记词)是否正确。
新手阅读路线图
如果你想在本地仓库中亲手验证这套格式,建议按以下顺序阅读(若需拉取仓库:git clone https://gitcode.com/GitHub_Trending/thund/thunderbolt):
| 顺序 | 文件 | 看点 |
|---|---|---|
| 1️⃣ | docs/architecture/e2e-encryption.md | 全局概念、Wire Format 定义、用户流程 |
| 2️⃣ | src/crypto/primitives.ts | AES-256-GCM 加解密、混合密钥包裹 |
| 3️⃣ | src/db/encryption/codec.ts | __enc:格式的拼装与拆解、CK 缓存 |
| 4️⃣ | src/db/encryption/config.ts | 加密列清单(单一事实来源) |
| 5️⃣ | src/db/encryption/upload-encoder.ts | 上传前加密 |
| 6️⃣ | src/db/powersync/middleware/EncryptionMiddleware.ts | 下载时按前缀解密 |
| 7️⃣ | src/db/encryption/codec.test.ts | 用测试验证你对格式的理解 |
相关文档还包括同步管道集成说明 docs/architecture/powersync-sync-middleware.md。
总结
__enc:<iv>:<ciphertext>这个看似简单的三段式字符串,浓缩了 Thunderbolt 端到端加密的几个核心设计取舍:前缀即契约(数据驱动解密,兼容新旧版本)、格式稳定密钥演进(版本放在信封层而非数据层)、处处防御(防双重加密、失败降级、密钥失效广播)。对于想理解零知识同步如何落地的新手来说,这份源码是一份难得的、可直接运行验证的范本。
【免费下载链接】thunderboltAI You Control: Choose your models. Own your data. Eliminate vendor lock-in.项目地址: https://gitcode.com/GitHub_Trending/thund/thunderbolt
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考