# ZCode 内部揭秘：将您的 Git 历史记录静默上传至云端

- 来源：Hacker News 热门（buzzing.cc 中文翻译）
- 作者：csmantle
- 发布时间：2026-09-18 22:22
- AIHOT 分数：77
- AIHOT 标记：同事件
- AIHOT 链接：https://aihot.news/items/cmu72r58y0o5xrowk1txg8msk
- 原文链接：https://blog.ferstar.org/en/posts/zcode-silent-workspace-snapshot-upload

## 正文

我不是英语母语者；这篇文章由 AI 翻译。

事情始于一次清理磁盘空间时的例行检查：~/.zcode 占用了超过 700MB。在断断续续地深入排查后，我确认了一件相当离谱的事：

只要你处于登录状态，ZCode（智谱官方的 AI 编程桌面应用）就会悄悄地把你的整个工作区——完整的 .git 历史记录、LFS 资源缓存、reflog 以及全局应用配置——打包、加密，然后直接上传到阿里云 OSS。

更讽刺的是：用于加密的 RSA 公钥是由服务器动态下发的，而私钥只存在于云端。你无法解密就躺在你自己磁盘上的那几百兆密文，ZCode 客户端本身也做不到。

以下是完整的调查记录、证据链，以及一条能永久关闭它的单行防御措施。

起点：一个卡在待处理状态的 313MB 归档文件 #

~/.zcode 是 ZCode 的数据根目录。体积构成大致如下：

cli/：约 257MB（会话数据库、执行日志）

computer-use/：约 130MB（打包的应用及运行时依赖）

v2/checkpoints/：约 303MB（主要嫌疑对象）

在 v2/checkpoints/ 内部，我发现了一个 313MB 的 .enc 文件，旁边还有一个状态元数据文件：

{ "workspacePath": "/Users/ferstar/myprojects/<a commercial project>", "lastCompressedSize": { "encryptedSizeBytes": 313070842, "workspaceSizeBytes": 345549173 }, "kind": "baseline", "failureCount": 564 }

事情的经过很直白：

客户端扫描了我正在进行的商业项目，排除了 node_modules 和其他几个文件，然后把剩余的 345MB 打包成一个 313MB 的加密归档文件，标记为 baseline（完整快照）；

它记录了 564 次上传失败尝试，最终让它停留在本地 pending/ 目录中，等待下一次重试。

该仓库总计 10GB；减去依赖项后，剩余的 345MB 几乎全是核心知识产权。

它去了哪里：从日志到 asar 逆向工程 #

日志中没有明确的上传 URL，所以我拆开了客户端的 app.asar。重建后的上传流程如下：

sequenceDiagram participant C as ZCode client participant S as zcode.z.ai participant O as Aliyun OSS C->>S: POST /api/v1/snapshot/upload-credential S-->>C: snapshot_id + RSA public key + max_size + OSS form credentials + callback C->>C: tar.gz pack → AES-256-CTR encrypt → RSA-OAEP wrap key C->>O: PostObject direct upload of tar.gz.enc O->>S: callback confirms receipt

该流水线分两个阶段运行：

向协调器请求凭证：客户端调用 https://zcode.z.ai（代码中为 VITE_ZCODE_ENDPOINT_ORIGIN）。服务器返回 OSS 表单签名（policy、x-oss-signature）、一个动态 Object Key、大小限制，以及本轮加密所用的 RSA 公钥；

直接以表单 POST 上传至 OSS：在本地完成归档和流式加密后，客户端绕过 ZCode 自己的应用服务器，通过 HTTP POST 表单tar.gz.enc直接上传至阿里云 OSS。随后 OSS 回调智谱后端以登记该快照。

检查活动套接字证实了这一点：运行中的 ZCode 进程维持着到 zcode.z.ai IP 端点以及两个阿里云 OSS 存储节点的持久 HTTPS 连接。

最讽刺的部分：密钥属于服务器#

该加密实现采用的是教科书式的信封加密：

keyId: String(i.encryption.key_version), keyWrapAlgorithm: "rsa-oaep-sha256", publicKeySpkiPem: Ylt(i.encryption.public_key)

内容通过 AES-256-CTR 使用临时对称密钥进行加密；

该对称密钥使用服务器提供的公钥，通过 RSA-OAEP-SHA256 进行封装。

关键问题在于那把公钥：它是在凭证协商过程中由服务器下发的，而对应的私钥永远不会触及你的机器。用我系统上所有本地私钥来解封信封密钥都失败了，这在意料之中。

换句话说：你硬盘上那 313MB 的密文，你或客户端都无法打开。只有智谱的后端持有解锁它的密钥。

如果这个功能真的是为用户侧回滚或跨设备同步而构建的，密钥本应保存在本地（就像 Git 或 Time Machine 那样）。一个只有服务器才能使用的密钥只有一个用途：确保服务器随时都能读取你的代码。

打包了什么：近 90% 是 .git #

尽管密文是加密锁定的，但打包过程中生成的 Manifest（文件清单）以明文形式保存在本地。拆解一份包含 42,411 个文件的快照：

内容 大小 占比 包含的信息

.git/lfs/ 196.1 MB 56.8% LFS 缓存——所有曾下载过的二进制资源和大体积媒体文件

.git/objects/ 102.2 MB 29.6% 完整的提交历史对象存储（提交、树、blob）

.git/logs/ 0.6 MB 0.2% reflog——本地分支历史与未推送的操作痕迹

源代码与文档 约 46.2 MB 13.4% src/、配置文件、内部文档

仅 .git 目录就占了整个数据载荷的 86.6%。

一旦上传，云端收到的远不止你当前的工作树——它得到的是你仓库自第一天起的完整谱系：

在后续提交中已被删除的历史 API 密钥和敏感配置；

未推送的本地分支名称（会泄露尚未发布的功能计划）；

在 .git/config 中配置的内部 GitLab 主机名和仓库路径。

此外，还有一个名为 repo_snapshot_extra_manifest 的额外清单文件会对你的全局 ZCode 配置文件（例如 settings.behavior.json）进行哈希处理，并在每次快照时将它们跨工作区打包。

关于开关的真相：UI 开关并不能阻止它 #

自然的反应是去检查设置把它关掉。我将 UI 选项与代码库进行了交叉比对：

开关 你以为它会做什么 它实际做了什么

优化体验（optimizeAgentExperienceEnabled） 禁用遥测 / 数据收集 仅控制数据是否被授权用于模型训练。快照捕获和上传仍在运行

仓库快照索引（repoSnapshotIndexingEnabled） 禁用快照功能 仅控制服务器是否对上传的快照建立索引。本地打包和上传仍不间断进行

查看宿主程序集代码就一目了然：捕获/上传 sidecar 在启动时被无条件实例化。没有任何基于用户偏好的门控if检查；唯一的要求是tokenProvider能够返回一个有效的 JWT。

归根结底：只要你处于登录状态，这条后台流水线就永久处于活跃状态，没有任何 UI 设置能将其关闭。

捕获触发发生在两个时间点：captureBeforePrompt（每次提示词之前）以及任务完成并标记为repo-wiki-update时。在会话日志中，单个活跃会话最多产生了 62 次捕获事件。

隐私政策怎么说 #

查阅 ZCode 的隐私政策，其中明确声明会收集“对话过程中提交的文本、文件和代码”——这是为 LLM 提供上下文的常规做法。

然而，纵观整份政策、FAQ 和更新日志，没有任何一处提及会静默打包并上传整个工作区和完整的 Git 历史记录。

最接近的表述是那条通用模板声明：“优化程序默认关闭，未经同意输入内容不会被用于训练”。

防御：删除是打地鼠；锁定目录 #

当我第一次发现那个待处理的软件包时，我直接把它删掉了。不到半小时，它又重新捕获了——一个全新的 313MB 归档文件，重试计数器从 564 跳到了 565。当上传方发现文件不见了，它就会重新打包一个新的。手动删除就是打地鼠。

最干净、最有效的解决方案是在文件系统层面设置不可变标志，在内核层面拒绝写入权限：

macOS #

# Wipe and lock the checkpoints directory rm -rf ~/.zcode/v2/checkpoints mkdir -p ~/.zcode/v2/checkpoints chflags uchg ~/.zcode/v2/checkpoints # Verify: should output "Operation not permitted" touch ~/.zcode/v2/checkpoints/test

Linux #

# Wipe and lock the checkpoints directory rm -rf ~/.zcode/v2/checkpoints mkdir -p ~/.zcode/v2/checkpoints sudo chattr +i ~/.zcode/v2/checkpoints # Verify: should output "Operation not permitted" touch ~/.zcode/v2/checkpoints/test

影响与回滚 #

结果：每当捕获逻辑尝试磁盘 I/O 时，都会被内核阻止。没有本地产物，上传流水线就没有东西可发送；

权衡：“检查点回滚 / 时间线”UI 功能将无法使用（该功能本来就一直要求上传你的代码）。正常聊天、自动补全和工具执行不受影响。日志中被吞掉的 I/O 错误是无害的；

恢复方法：运行 chflags nouchg ~/.zcode/v2/checkpoints（macOS）或 sudo chattr -i ~/.zcode/v2/checkpoints（Linux）。

结语 #

使用 AI 工具时，模型推理不可避免地需要代码上下文——这一点大家都接受。但这一行为显然在两个方面越了界：

第一，数据范围。推理发送的是与任务相关的上下文；而快照外泄的却是整个代码仓库，连同多年的 Git 提交历史。

第二，架构姿态。如果这真的是为用户侧恢复或同步而设计，解密密钥本应归用户所有。而加密密钥完全由服务器持有、隐私政策中零披露、后台上传无法阻止、删除后仍顽固地重新打包——这看起来远不像备份，更像是数据收集。

工具就是工具，但用户必须自己划定边界。如果软件不让你关掉它，那就用操作系统内核把它锁进笼子里。

相关
