在清理磁盘空间时例行检查,发现 ~/.zcode 占用了超过 700MB。间歇排查后确认,登录后智谱 AI 编程桌面应用(ZCode)会静默打包整个工作区,包括完整 .git 历史、LFS 资源缓存、reflogs 和全局应用配置,加密并直接上传到阿里云 OSS(Aliyun OSS)。更讽刺的是,加密用的 RSA 公钥由服务器动态下发,而私钥仅在云端,用户自己磁盘上那数百 MB 密文无法解密,客户端本身也不能。
正文
深入 ZCode:悄悄把你的整个 Git 历史传到云端
起因是一次清理磁盘空间的例行检查:~/.zcode 占了 700MB 以上。断断续续翻查之后,我确认了一件相当离谱的事:
只要你处于登录状态,ZCode(智谱官方的 AI 编程桌面应用)就会悄悄把你的整个工作区——完整的 .git 历史、LFS 资源缓存、reflog 以及全局应用配置——打包加密,直接上传到阿里云 OSS。
更讽刺的是:加密用的 RSA 公钥由服务器现给,私钥只存在云端。 你磁盘上那几百兆密文,你解不开,ZCode 客户端自己也解不开。
下面是完整的排查记录、证据链,以及一条能永久关掉它的防御命令。
起点:一个卡在 pending 里的 313MB 压缩包
~/.zcode 是 ZCode 的数据根目录,体积大致是:
cli/:约 257MB(会话数据库、执行日志)computer-use/:约 130MB(内置应用和运行时依赖)v2/checkpoints/:约 303MB(主要嫌疑人)
在 v2/checkpoints/ 里,我发现了一个 313MB 的 .enc 文件和一份状态元数据:工作区路径指向 /Users/ferstar/myprojects/<某商业项目>,lastCompressedSize 中 encryptedSizeBytes 为 313070842、workspaceSizeBytes 为 345549173,kind 是 baseline,failureCount 为 564。
事情很清楚:
- 客户端扫描了我正在做的商业项目,排除
node_modules等目录,把剩下的 345MB 打包成 313MB 的加密归档,标记为baseline(完整快照); - 它记录了 564 次上传失败,把它留在本地
pending/目录里等待下次重试。
该仓库总计 10GB,去掉依赖后剩下的 345MB 几乎全是核心知识产权。
传到哪儿:从日志到 asar 逆向
日志里没有明确的上传地址,于是我拆开了客户端的 app.asar。还原出的上传流程是:
- 客户端 POST
/api/v1/snapshot/upload-credential到zcode.z.ai; - 服务端返回
snapshot_id、RSA 公钥、max_size、OSS 表单凭证和回调; - 客户端本地打包 tar.gz、用 AES-256-CTR 加密、用 RSA-OAEP 包裹密钥;
- 客户端把 tar.gz.enc POST 直传 OSS;
- OSS 回调通知服务端确认收到。
整条链路分两段:
- 向协调服务取凭证:客户端请求
https://zcode.z.ai(代码里的VITE_ZCODE_ENDPOINT_ORIGIN)。服务器返回 OSS 表单签名(policy、x-oss-signature)、动态 Object Key、体积上限,以及本轮加密用的 RSA 公钥; - 直接向 OSS 发 POST 表单:本地归档并流式加密后,客户端绕过 ZCode 自己的应用服务器,用 HTTP POST 表单把
tar.gz.enc直接发到阿里云 OSS。随后 OSS 回调智谱后端注册该快照。
检查活动套接字印证了这一点:运行中的 ZCode 进程与 zcode.z.ai 的 IP 端点以及两个阿里云 OSS 存储节点保持着长连接。
最讽刺的部分:密钥属于服务器
加密实现用的是教科书式的信封加密,代码里写着 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(文件清单)以明文存在本地。对一个 42411 个文件的快照做拆解:
| 内容 | 体积 | 占比 | 含有的信息 |
| --- | --- | --- | --- |
| .git/lfs/ | 196.1 MB | 56.8% | LFS 缓存——历史上下载过的全部二进制资源和大体积媒体 |
| .git/objects/ | 102.2 MB | 29.6% | 完整提交历史对象库(commits、trees、blobs) |
| .git/logs/ | 0.6 MB | 0.2% | reflog——本地分支历史和未推送的操作痕迹 |
| 源码与文档 | 约 46.2 MB | 13.4% | src/、配置文件、内部文档 |
光 .git 目录就占了载荷的 86.6%。
一旦传上去,云端拿到的远不止你当前的工作副本,而是这个仓库从第一天起的完整谱系:
- 后续提交里已删除的历史 API key 和敏感配置;
- 尚未推送的本地分支名(会泄露未发布的功能计划);
.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 的隐私政策明确写了会收集「对话中提交的文本、文件和代码」——这对给模型喂上下文来说是常规做法。
但整份政策、FAQ 和更新日志里,对静默打包上传整个工作区和完整 Git 历史只字未提。
最接近的一句是模板化的表述:「优化计划默认关闭,未经同意输入不会被用于训练」。
防御:删是打地鼠,锁住目录才行
我第一次发现 pending 包时,直接删掉了。半小时内它又重新抓取——一个全新的 313MB 归档,重试计数从 564 涨到 565。上传器发现文件没了,就再打一个。手动删除是打地鼠。
最干净有效的办法是在文件系统层面设置不可变标志,让内核拒绝写入:
macOS
rm -rf ~/.zcode/v2/checkpoints
mkdir -p ~/.zcode/v2/checkpoints
chflags uchg ~/.zcode/v2/checkpoints
验证:应输出「Operation not permitted」
touch ~/.zcode/v2/checkpoints/test
Linux
rm -rf ~/.zcode/v2/checkpoints
mkdir -p ~/.zcode/v2/checkpoints
sudo chattr +i ~/.zcode/v2/checkpoints
验证:应输出「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 提交历史。
第二是架构姿态。如果真是为用户侧恢复或同步设计的,解密密钥就该属于用户。密钥独归服务器、隐私政策零披露、后台上传停不下来、删掉还顽固重打包——这看着不像备份,更像采集。
工具是工具,但边界得用户自己划。如果软件不让你关掉它,就用操作系统内核把它锁进笼子里。
评论
社区弥漫着对相关中国 AI 供应商及其客户端强烈的不信任,多数人指其借免费推广暗中窃取用户数据并违规扒窃竞品,少数人则质疑自动沙箱与权限机制本身的有效性。
认为中国 AI 供应商及其客户端恶意窃取数据
- 多位用户指出绝不信任相关厂商的工具与推理端点,并认为免费 ZCode 推广实为诱饵,后台会吸走用户私密文件,甚至通过境外壳账号违规训练或扒窃竞品(较多人)。 > 这些公司的行为规模大且傲慢,显然不把违反规则放在眼里,使用者务必当心(@bbor)
客户端会暗中尝试读取用户磁盘上的敏感文件
- 有开发者发现 GLM 和 DeepSeek 模型频繁试图访问 dotfiles 与 .gitignore 中列出的文件,另有用户指出上传内容竟用用户无法持有的密钥加密,证实后台有隐蔽传输(较多人)。 > 我在自写工具里发现这类模型总想读取隐藏文件和忽略列表里的文件,虽被权限提示拦下(@philbo)
自动权限与沙箱机制本身就不可靠
- 部分用户认为不应信任任何新 harness,自动模式下的权限分类器不过是模型在猜测意图,甚至连沙箱都能被客户端以被拦截为由绕开,使隔离措施形同虚设(少数人)。 > 自动模式的权限判断只是模型在猜对错,连沙箱都能绕开,让人怀疑沙箱意义何在(@ectoloph)
有价值的补充
- 早先 Grok Code 事件本应成为不信任新测试框架的教训,但似乎无人吸取(@denysvitali)。
- 因 Anthropic 威胁情报报告网页侧边栏实验导致媒体低估了相关丑闻曝光度,是一种反直觉的传播现象(@bbor)。
- 讽刺厂商借口提升负零点三个百分点的召回率而窃取全部数据,折射出社区对窃取借口的嘲弄情绪(@alansaber)。