# 智谱 ZCode，你打包上传我的代码仓库干什么？

LLMS 索引： [llms.txt](/llms.txt)

---

我用智谱的 ZCode 也就几天，说实话，体验不差。

GLM 5.3 这代模型是真能打，Coding Plan 便宜，自研的 Agent Zcode 也挺好用。
国产 AI 里难得有个让人想主动掏钱的产品，前两天还写了一篇，准备过两天写一篇完整的体验，夸一夸它。

![blog.webp](blog.webp)

结果，就在今天 9 月 18 日凌晨，ferstar 发了一篇博客《[扒一扒 ZCode 静默上传全量 Git 历史的骚操作](https://blog.ferstar.org/posts/zcode-silent-workspace-snapshot-upload/)》。
他清磁盘时发现 ~/.zcode 占了七百多兆，一路扒下去，找出一条工作区快照打包，包含完整的 .git 历史，整个打包加密上传到阿里云 OSS 去，本地没有解密密钥。

我第一反应是不信，这种事听着太蠢了，Grok 两个月前刚翻过车，被开发者骂得一塌糊涂。但是这个博客写的非常详细，又让我不得不信。

所以我立刻在自己的 Mac 上，让 Claude 按他的路线把取证重跑了一遍。结论：属实。而且不是 “会”，是 “已经”。

查下来，我的机器上有四个工作区的快照相关记录。其中一份留下了上传被接受的状态；
另外两份快照的文件清单里，.git 的字节占比分别达到 93.9% 和 98.5%，客户端也已经取得了上传凭证。

![claude-zcode.webp](claude-zcode.webp)

> 老冯之前还比较奇怪，为什么这几天流量跑那么快，我准备挂一个流量监控好好看看到底是什么应用干的。


--------

## 它到底干了什么

本次检查的对象，是 macOS 上的 ZCode 3.12.3，时间为 2026 年 9 月 18 日。下面涉及本机行为的结论，都限定在这个版本和这次检查范围内。

在客户端代码中，可以找到与发送 prompt、任务结束有关的快照触发入口。
相关流程会向 zcode.z.ai 申请上传凭证，取得服务端提供的加密公钥、
大小限制和 OSS 表单凭证，再按筛选规则扫描、打包工作区文件。

文件经过压缩和 AES 加密，对称密钥再由 RSA 公钥封装。
后续上传路径将密文提交到 OSS，并通过回调向后端登记。

我在使用过程中，没有看到明确告诉我快照范围和上传去向的提示，也没有找到一个能够明确关闭这条上传路径的设置。
在 `~/.zcode/v2/checkpoints/` 下，本机留下了这些记录：

| 工作区 | 明文大小 | 加密大小 | 状态                       |
|--------|----------|----------|----------------------------|
| silo   | 229 MB   | 211 MB   | 已取得上传凭证，正文待传   |
| mc     | 232 MB   | 229 MB   | 已取得上传凭证，正文待传   |
| pgnls  | 405 MB   | 7.4 MB   | 已被服务端接受，离机       |
| pgdoc  | 1.53 GB  | 1.07 GB  | 超出体积上限，失败计数 102 |

![claude-analyze.webp](claude-analyze.webp)

pgnls 的状态文件里写入了 lastAcceptedManifestHash。按本次代码检查找到的写入路径，这个字段对应上传获得确认后的分支。
因此据此可以推断：这份工作区快照已经上传，并由客户端记录为被接受。

但我没有直接查看服务端存储，不能进一步判断对象现在是否仍然保留、谁访问过、做过什么处理。
这份快照装的是 PostgreSQL 翻译文件，不含 .git。真正包含大量 Git 对象的 silo 和 mc，是否上传完成，仍然未知。

pgdoc 则留下了一笔奇怪的记录：加密输出大小为 1,073,774,608 字节，比 1 GiB 多 32,784 字节。
按本次代码分析，大小阈值由服务端下发，检查发生在客户端，失败计数已经达到 102。

这说明存在大小限制相关的失败和重试状态，但我没有逐次执行记录，不能说它把整个仓库完整打包了 102 遍，更不能拿这个数字去计算上传了多少流量。

事实到哪儿，就写到哪儿。

![traffic.png](traffic.png)



--------

## Key 你也打包啊？

我一开始看到 Claude 报告以为 PGSTY RPM 签名私钥也进了包。要真是这样，事情就大了。

老冯做的都是 Build in Public 的开源项目，真把我仓库打包走了，对我来说倒也可以接受，只要别碰我的密钥就行。

![key-upload.webp](key-upload.webp)

把 silo 的 78,141 个 blob 对象全量扫完（含悬空对象、不设体积上限）之后，确认没有：
仓库里那个 `buildscripts/pgsty-rpm-signing-key.asc` 是 PGP **公钥**，私钥从来没有被提交过。mc 的 20,356 个 blob 同样干净。

但有意思的地方恰恰在这儿：那个公钥文件，3,139 字节，确实躺在 silo 快照的 manifest 里。
它能进去，是因为文件名后缀叫 `.asc` —— 不含 token、不含 secret、后缀不是 .pem/.key/.p12/.pfx，**从密钥过滤器的缝里漏了过去**。

我不打算拿一个公钥，冒充供应链安全事故。毕竟 silo 这个仓库它还只是打包完，没有上传。但这不意味着后面的疑问消失了。

公钥漏出去不要紧，公钥本来就是拿来分发的。要紧的是这道过滤器薄到什么程度，你得自己去数它那张表有几行。
我的签名私钥没有在本次检查中被检出，是一回事；快照为什么把 .git 纳入范围，是另一回事。
不能因为我没有把要紧东西放进去，就算你替我守住了边界。

我只是运气好。那条放行逻辑摆在那儿：谁要是往仓库里提交过一次私钥，哪怕当天就 `git rm` 掉、哪怕后来跑过 filter-repo 重写历史，
它都会随 .git/objects 原样出去。问题不是“有没有出事”，是**这个设计让出不出事只取决于运气**。



--------

## 你越线了

用 AI 写代码，模型推理需要上下文，这个我知道。

我做开源项目，公开代码本来就是给人看的。我并不反对把必要的代码交给模型处理，也不排斥在明确授权的前提下参与模型改进。

我反对的是 **不告而取、拿全部、不给关**。 —— 数据范围扩大了，我却没有清楚地知道；我想拒绝，又找不到明确对应的控制。


**第一，范围。**
: 推理需要的是当前任务相关的文件，快照拿的是整个仓库的全部历史。我这份 silo 快照 9,619 个文件里 8,323 个属于 .git，字节占比 93.9%。ferstar 那份 .git 占 86.6%。这东西装的不是你的代码，是你代码的族谱。

更要命的是代码逻辑。文件筛选是一条顺序判定链，.git 的放行排在所有排除规则之前——密钥过滤、1 MB 体积上限，对 .git 下的任何东西永远不生效。所以一个 137 MB 的 packfile 能整包带走，而任何曾经提交进 Git 历史的凭据，哪怕你早就从工作区删掉，也会随 .git/objects 原样上传。

我的 silo 仓库里还有 .git/filter-repo 和 .git/lost-found 目录。前者说明这个仓库做过历史重写——通常正是为了清除敏感数据；后者存放悬空对象。也就是说，你刻意清理掉的东西，恰恰会被这个快照带走。

**第二，钥匙。**
: 加密是真的，信封加密也是教科书式的。问题是，本次检查看到的是：加密公钥由服务端提供；在本地留存材料中，我没有找到由用户持有、可以独立恢复这份密文的解密密钥。
你硬盘上那几百兆密文，你打不开，ZCode 客户端自己也打不开，只有服务端能解。
如果这功能真是给用户做回滚或者跨设备同步，钥匙理应在用户手里，像 Git 和 Time Machine 那样。
ferstar 的判断是：这不像备份，更像采集。我同意，而且要补一句：备份的第一原则是数据所有者能恢复，一个所有者解不开的备份，在定义上就不是备份。
我的疑问是，用户到底被要求信任什么，以及是否知道自己正在作出这个选择。

**第三，开关。** 
: 设置里有一项 “仓库快照索引”，我的机器上一直是关的。9 月 13 日到 18 日的日志里，这个字段出现 1,339 次，全是 false，一次 true 都没有。而那四份快照全造于这几天里。
查代码就明白了：这个字段在真正执行采集的 host 包里只出现两次，一次是迁移赋值，一次是设置项名称列表，都不在采集路径上。
采集入口的唯一条件是 sidecar 实例存在，而这个实例是无条件构造的。全量检索 app.asar 也没有任何可以禁用它的环境变量。

更准确地说，决定权根本不在本地。客户端每次 prompt 都无条件向服务端申请上传凭证，服务端发就采、不发就不采。
这个请求没有缓存、不写日志、失败无提示。你的电脑上没有任何一个开关能否决它，对面想什么时候采就什么时候采。



--------

## 隐私政策怎么写的

ZCode 隐私政策今年 6 月 15 日生效。收集范围的原文是这么写的：[z](https://zcode.z.ai/en/privacy)

> text, files (including but not limited to uploads and inputs you provide in the form of text, images, audio, video, configuration parameters, shell commands, and similar formats), and code **submitted to us through conversation**.

> 你**通过对话**提交给我们的文本、文件与代码。

这是推理上下文，各家都一样，没问题。它还写了优化计划默认关闭，用户主动加入前不会把输入用于训练。

关键在最后那三个词：**through conversation**。后台快照不是通过对话提交的。这不是"条款留了个口子"，是这个行为压根不在条款描述的范围之内。



--------

## 几句公道话

批评也要讲证据边界，我没证实的东西如实列出：
密文内容我没有解开，快照里装了什么是按明文 manifest 逐条认定的，不是解密后看到的
—— 解密私钥在智谱服务端，本地没有解密条件。

silo 和 mc 是否传完未知。能确认的是客户端已经拿到了上传凭证，至少工作区标识已经到了服务端。
服务端按什么判据决定采不采、采集范围如何界定，我没有抓包观察，不做推断。

上传不等于训练。两份取证都只证明了传输和接受，没有证明用途。

还有一点该承认：全局配置采集设了排除表，credentials.json 和 OAuth 路径在列，凭据文件本身没有被打包。
虽然这个过滤条件很粗糙，还是把我的签名公钥和一堆上游的测试密钥打进了包。但万幸，最核心的 —— 我的签名私钥保住了。 
否则我都不敢想重新签名 Pigsty 十多万个 RPM/DEB 包有多麻烦。 


--------

## 这件事的本质

判断一个 AI 工具是否尊重你，只要问三个问题：数据范围是不是最小必要？钥匙在谁手里？能不能关？

我之前写过，本地 AI 算的是政治账不是经济账，原则是该公开的当广告使劲喂，该保密的一个字节不出局域网。这条原则有一个前提：你得知道哪些字节出去了。ZCode 干掉的正是这个前提。

Agent 类桌面客户端，是一个有权读你全部硬盘、有权连网、常驻后台的进程。审计它，应该按这个标准，而不是按一个聊天窗口。

天下没有白吃的午餐，用 ZCode 多给 50% 的额度，显然是希望你用这个 Harness。
大家不是没有数据换算力的预期，你去拿上下文训练，我们也不是不知道或者不允许，
但打包把整个仓库拿走，那就完全是另一个级别的事情了。



--------

## grok 也翻过同样的车

这事不是没有先例。今年 7 月，安全研究者 cereblab 对 xAI 的 Grok Build 命令行做了抓包：哪怕提示词明确写着"回复 OK，不要读任何文件"，
它仍然把整个仓库打成 git bundle 上传到 Google Cloud Storage，克隆回来能原样恢复一个从未被读取的文件和全部提交历史；
关闭"改进模型"开关对此毫无影响。 [github](https://gist.github.com/cereblab/dc9a40bc26120f4540e4e09b75ffb547)

据该记录，事发后 xAI 在服务端关停了上传，加了退出选项，马斯克公开承诺删除此前上传的数据。 [github](https://gist.github.com/cereblab/dc9a40bc26120f4540e4e09b75ffb547)

那是 7 月中旬。ZCode 从 3.0 一路迭代到 3.12.3，检查点回滚、项目知识库、仓库 Wiki 一个个功能上线。更早的版本是什么状态我没有验证，但至少在我检查的 3.12.3 里，这条链路还在跑。
全球开发者圈刚为同一件事骂完一轮，两个月后国产头部厂商的官方客户端还在做同一件事。为什么没有在那时候复查一遍自己的链路，这个问题该由智谱来回答。


-----

## 我的处置

第一，主力开发环境停用 ZCode。先限制相关程序联网，保全本地记录，再卸载。重要仓库不再让它碰。我无法信任作出这样行为的 Agent。

第二，它再也不会碰任何真正重要的仓库。我会用一台隔离开发机运行，把剩下的额度快速干杂活烧完，做做翻译，用完拉倒，我也不会再用或者推荐它了。

第三，给还在用的朋友几条建议：清掉 ~/.zcode/v2/checkpoints/*/pending/ 下的密文，那是唯一你能自主控制的环节，
服务端一恢复发凭证它就会续传；打开 manifests/ 明文清单，看看到底哪些文件进了包，重点看 .git 而不是工作区；

历史里出现过的凭据，全部轮换，密钥过滤只看文件名，对 Git 历史无效；
要彻底阻断，用 ferstar 给的方案锁死 checkpoints 目录（macOS 用 chflags uchg，Linux 用 chattr +i），
代价是回滚功能不可用。hosts 屏蔽不推荐，登录接口同域，会一起干掉。

第四，这更加坚定了老冯在《[本地 AI：算的是政治账，不是经济账](/ai/local-ai-movement/)》提到的。在数据安全面前，白送白给的 Token 一文不值。
我依然感谢智谱开发并开源了 GLM 5.3，但我宁可买一台 M5 Ultra 在本地跑，或者继续用更贵的 Codex/Fable 而不再会用它的 API 做任何重要工作，哪怕再便宜。



------

## 给智谱的四句话

**第一，请确认并解释这条快照机制。** 它服务于哪个功能，何时触发，为什么需要包含 `.git`，不同版本和账号是否存在差异。

**第二，请给用户一个明确、真正对应上传行为的选择。** 我的建议是默认关闭，在清楚展示范围后由用户开启。依赖它的功能有哪些、关闭后影响什么，也一起写明白。

**第三，请说明已经收到的数据如何处理。** 包括存储和处理区域、访问权限、保留期限、是否用于其他目的，以及用户怎样查询、申请删除并获得可核对的处置说明。

**第四，请把产品行为和说明对齐。** 设置、首次触发提示、隐私政策和更新日志，应当让用户形成一致的预期，而不是分别说着不同的事情。

GLM 是我愿意继续评价和使用的模型，Coding Plan 也是我付过钱的产品。但模型值得夸，不代表客户端这件事就可以含糊过去。


--------

## 披露

我是 GLM Coding Plan 的付费用户，也曾分享推广链接并因此获得赠金。

![glm53.webp](glm53.webp)

本文本机部分来自 2026 年 9 月 18 日对 macOS 上 ZCode 3.12.3 的检查，涉及 `~/.zcode/v2/checkpoints/` 下的明文清单、
状态文件，以及客户端代码的静态分析；检查借助 Claude 完成。外部资料在相应位置注明来源。

还有一个时间点值得记下来：我这台机器上最后一次成功取得上传凭证是 9 月 18 日 11:55。
此后到我写完这篇文章为止，checkpoints 目录没有再新增任何东西。是服务端主动停了，还是别的原因，我没有抓包，不做判断。

本文没有解密快照，没有直接检查服务端存储，也没有据此推断所有版本、平台和用户都存在相同行为。

对于已经观察到的行为，我提出批评；对于尚未证实的用途和动机，我不下结论。


--------

## 附录

> 原文：《[扒一扒 ZCode 静默上传全量 Git 历史的骚操作](https://blog.ferstar.org/posts/zcode-silent-workspace-snapshot-upload/)》 

![blog-full.webp](blog-full.webp)

> 报告

![report.webp](report.webp)
