客户端把工作区加密上传后,为什么你解不开这份本地密文

2026 年 9 月 18 日,开发者 ferstar 在清理磁盘时发现:登录态下的 AI 编程客户端会把整个工作区打成加密包,再尝试直传对象存储。包是密文,用来包密钥的 RSA 公钥却由服务端当场下发,对应私钥从不落地。于是出现一句反直觉的话:磁盘上那几百兆「已加密」文件,你解不开,客户端也解不开。9 月 21 日厂商宣布开源并拆除上传链路,但「加密上传」四个字本身并没有因此变成零知识。

先记结论:「已加密」只说明打包后的字节不是工作区明文。它不能说明你持有解密能力,也不能说明云端读不到内容。信封加密里,对称密钥用服务端下发的公钥包一层,私钥留在云端,设计目标就是让云端能解开。能当场核对的是三件事:这份密文你自己能不能打开、Network 里有没有工作区明文、界面开关关掉之后是否仍出现新的待传包。不要把「本地做了 AES」读成「只有我能解密」。

一句话先回答搜索意图

传输层加密和「你拥有密钥」不是同一层。客户端可以在本机用 AES 把工作区打成密文,再把密文传到对象存储;若包密钥的公钥来自服务端,私钥从未进入这台电脑,这份文件就不是给你恢复用的备份。它是一张只有云端能拆的信封。链路窃听者看不到明文,持有私钥的服务端看得到。

这篇文章给刚看到「客户端加密上传工作区」新闻、想分清「密文在磁盘上」和「零知识」的人,以及要把仓库或密钥交给同事、又不愿把解密权交给厂商的开发者。它不代替厂商通报,也不写成某一款编辑器的卸载指南。你要带走的是:信封加密把密钥放在谁手里、清单明文已经写出了多大范围、「立即销毁」证明不了什么,以及你怎么在本机对照「这份密文我能不能解开」。

这次拆解把哪几层写清楚了

2026 年 9 月 18 日,ferstar 公开了调查记录。起因不是抓包竞赛,而是清理磁盘:本机 ~/.zcode 占了七百多兆,其中 v2/checkpoints/ 里有一份约 313 MB 的 .enc,旁边的状态文件写明工作区约 345 MB、kind 为 baseline、failureCount 为 564。仓库本身约 10 GB,去掉依赖后剩下的 345 MB,几乎都是项目核心文件。

他把客户端里的上传流程还原成两步。第一步,客户端向 zcode.z.ai 请求 POST /api/v1/snapshot/upload-credential,服务端返回快照编号、对象存储表单签名、大小上限,以及这一轮的 RSA 公钥。第二步,本机打 tar.gz,用一次性对称密钥做 AES-256-CTR,再用 RSA-OAEP-SHA256 包住这把对称密钥,最后把 tar.gz.enc 以表单 POST 直传到阿里云 OSS,由存储回调厂商后端登记。业务服务器不经手这份密文文件,但登记权和私钥仍在云端。

范围也不只是当前工作树。本地明文清单列出 42,411 个文件:.git/lfs/ 约 196.1 MB(56.8%),.git/objects/ 约 102.2 MB(29.6%),.git/logs/ 约 0.6 MB,源码与文档约 46.2 MB(13.4%)。.git 合计 86.6%。上传若成功,云端拿到的是提交历史、已删除但仍留在对象库里的密钥、未推送的本地分支名,以及 .git/config 里的内网主机。另有一份跨工作区的额外清单,会给全局配置做哈希并一并带走。

9 月 18 日 17:44,厂商在用户社区发情况说明,承认上传来自默认开启的代码库索引,用于会话检查点、版本回退和 Repo Wiki,并称 Wiki 生成后数据立即销毁。9 月 21 日《每日经济新闻》报道:ZCode 宣布开源;中国信通院评测称 zcode-prod 存储桶为零数据;绿盟科技称桶与对象已删除;客户端 v3.14.0 已拆除快照生成与上传链路。ferstar 补充:那份 313 MB 商业项目因超过大小限制、重试 564 次,并未传出局域网;另一份 538 个文件的公开仓库工作区,压缩加密后约 15 KB,状态为服务端已接受。所以「有没有东西离开过这台机器」——至少那一份小的,离开过。

信封加密为什么不等于你能解密

AES-256-CTR 加 RSA-OAEP 是教科书里的信封:内容用短命对称密钥加密,对称密钥再用公钥包一层。算法名字看起来很硬,决定「谁能读」的却是公钥从哪来、私钥落在哪。调查记录写明:公钥在申请上传凭证时由服务端下发,对应私钥从不进入这台电脑;用本机已有私钥去拆这层信封,没有成功。客户端自己也解不开磁盘上那份密文。

这和「给我自己做一份可恢复备份」是反着的。Time Machine、本机 Git 对象、你用口令加密后再拷走的文件,解密材料都在你这边:口令、本地密钥,或你能打开的钥匙串。服务端独占私钥时,设计目标换成了:传输途中第三者看不见,到达后云端能拆开。ferstar 的原话把用途写死了——一把只有服务端能用的钥匙,服务的是服务端读你的代码,不是给你做回滚。

上海交通大学数据法律研究中心何渊对《每日经济新闻》说得更窄:加密是安全措施,不等于匿名化,也不能替代告知和最小必要;服务提供者仍掌握解密能力时,加密后的数据仍可能被恢复和处理。「使用后立即删除」只降低长期留存,回答不了最初收集是否必要。把这两句放在一起:算法强度证明不了密钥归属,销毁声明证明不了收集范围。

不要用生产仓库、真实云密钥或客户代码去「复现上传」做实验。核对的是本机是否已经出现待传密文、状态文件里的路径和失败次数,以及你自己能不能解开这份文件。把活仓库再送一遍,不会让私钥回到你手里。

清单明文已经把范围写出来了

密文打不开,不等于范围只能猜。打包时生成的文件清单留在本地,而且是明文。42,411 条记录把「当前源码」和「仓库一生」拆开了:当前树大约只占 13.4%,其余是 LFS 缓存、完整对象库和 reflog。当前工作区里已经删掉的 API 密钥,只要曾经提交过,仍可能在 .git/objects 里。未推送的分支名会暴露还没发布的功能。内网 Git 主机名写在 .git/config,不需要打开密文也能从清单侧看见它被收进包。

界面上的两个开关,名字像总闸,代码里不是。调查记录对照后的结果是:「优化体验」只控制可否用于模型训练;「仓库快照索引」只控制云端是否建索引。打包和直传在登录 JWT 有效时仍会走。捕获点写在每次发 Prompt 之前,以及任务结束时的 repo-wiki-update;单次活跃会话最多记到 62 次捕获。手动删掉待传包后约半小时,会打出一份新的 313 MB 文件,失败计数从 564 变成 565。删除文件不是关闭开关。

9 月 18 日归档的隐私政策,页面仍写最后更新于 2026-06-15,收集范围写的是对话里提交的文本、文件和代码,没有写「登录后自动打包完整工作区与 Git 历史」。政策能支持的结论,是厂商声明了会话提交;它不能支持:全量快照已经写进你点过同意的那一页。开关和隐私政策都要对照实际产物:本地有没有新的 .enc,状态文件里的 workspacePath 是不是你正在写的商业项目。

「已加密」和「立即销毁」各能证明什么

把新闻里的句子拆成「能支持 / 不能支持」,比跟着形容词走要稳。密文在磁盘上,只能支持:打包后的字节不是目录树明文。立即销毁,只能支持:厂商声称 Wiki 生成后不再长期放那份对象。开源后的仓库里搜不到 /api/v1/snapshot/upload-credential,只能支持:当前公开树里这条链路被拆掉了。存储桶被评测为零数据,只能支持:评测当时那个桶是空的。

这些句子都盖不住另一面。密文不能支持你能恢复工作区。立即销毁不能支持销毁前无人持有私钥,也不能支持收集范围等于你勾过的选项。扁平化成两次提交的开源仓库,不能用来对照旧版 sidecar 是怎么写的。事后清空的桶,不能倒推 9 月 18 日之前已接受的对象被谁解过、复过制。ferstar 写过:检查点回滚若真需要全量上云,和「生成后立即销毁」在时间上是拧着的;开源树里的检查点实现,是本机 Git diff,元数据写在 ~/.zcode/checkpoints/ 的 JSON,并不依赖那条上传链路。

你看见的说法 能支持的结论 不能支持的结论
磁盘上是 .enc,算法写着 AES-256 打包后的字节不是工作区明文 只有你能解密,云端读不到内容
RSA 公钥在申请凭证时由服务端下发 拆信封的私钥不在这台电脑 这是给你做跨设备恢复的备份
关掉「优化体验」和「仓库索引」 训练授权或云端索引可能停了 打包和直传一定一起停了
官方称 Wiki 生成后立即销毁 厂商声明不长期留存该对象 最初打包范围等于你点过的开关
评测称存储桶为零数据 评测当时那个桶是空的 更早收下的对象从未被解密或复制
313 MB 那份失败了 564 次 这一份商业项目快照未传出 没有任何工作区离开过任何用户的机器

当场能核对哪几层

口号不能当证据。下面只核对应由你控制的三层:本机有没有待传产物、请求有没有带出工作区明文、这份密文你自己能不能打开。不要向生产仓库再灌一次「测试上传」。

  1. 先看本机数据目录,而不是先看宣传页。AI 编程客户端常见的检查点或缓存目录里,若出现成百兆的 .enc 和一份写着 workspacePath、encryptedSizeBytes、failureCount 的状态文件,说明打包已经发生。路径对得上你正在写的项目,比「我们只传对话上下文」更硬。
  2. 打开开发者工具或系统网络观察,勾选 Preserve log。只看请求行和主机名:有没有申请上传凭证的接口,有没有直连对象存储的表单 POST。对照的是「密文是否离开这台机器」,不是把仓库再送一遍。请求体里若出现源码、口令或 .pem 明文,这一层已经失败。
  3. 问一句能落地的话:这份本地密文,我用自己的口令或本机私钥能不能打开。打不开,且厂商也说客户端解不开,密钥归属就不在你这边。打得开,才能谈「这是给我自己的备份」。
  4. 关掉所有名字像「隐私 / 索引 / 优化」的开关,等一个会话周期,再看检查点目录有没有新的待传包、失败计数有没有继续加。开关文案证明不了总闸;新文件才证明。
  5. 需要自己还能解开的文件时,换一条计算边界写清楚的通道。打开 文件加密盒,选一份可丢弃的金丝雀文件,文件名写成 canary-fp-20260922-vendor-key.txt,口令只留在这台电脑。打开 Network,勾选 Preserve log,点加密。请求行和请求体里不应出现文件内容、口令或完整文件名业务字段;输出是 .lock / .enc,单文件不超过 5 GB。用同一口令在本机解密,金丝雀应还原。你能解开,是因为口令在你手里,不是因为算法名字里有 AES。

第五步不是让文件加密盒去替代编程客户端。它只提供一个对照:同样是「本机加密」,密钥派生和 AES-256-GCM 留在当前标签页,文件和口令默认不作为业务数据上传。密文你带得走,也解得开。上一篇写过的中转站问题是另一层——HTTPS 只保护到代理这一跳;这一篇多出来的层是:密文离开之后,拆信封的人是谁。

完整口令和密文不要走同一条聊天。文件加密盒的 .lock 可以拷到网盘,口令应另走已经核实过的渠道,或把短口令放进阅后即焚链接的 # 后面。把钥匙和锁贴在同一条消息里,信封对「能看见这条消息的人」已经没了。

三种把「本地加密」读过头的情况

第一,把「在本机调用了 AES」读成「只有我能解密」。调用发生在本机,只说明明文打包时还在这台电脑。公钥从服务端来、私钥不落地时,解密权在云端。算法列表、密钥长度、CTR 还是 GCM,都回答不了「私钥在谁的进程里」。

第二,把「直传对象存储、不经业务服务器」读成「厂商看不见」。文件可以不经过应用服务器,登记回调和私钥仍在厂商一侧。跳过业务机房,减少的是应用日志里的明文副本,不是解密权。中转站那篇写过 HTTPS 挡不住应用层代理;这里挡不住的是「持有私钥的那一端」。

第三,把「立即销毁 / 开源 / 存储桶已空」读成「从未收集」。销毁降低留存,开源让你核对当前树,空桶说明评测当时没有对象。它们都不自动等于:历史对象没有被解过,或当时的开关已经覆盖全量 Git。需要写进交接说明的,是本机是否还在生成待传包,以及你自己能不能解开将要外发的密文,而不是新闻标题里的「已整改」。

常见问题

磁盘上已经是密文,是不是就等于本机零知识?

不是。密文只说明打包后的字节不是明文工作区。若用来包密钥的 RSA 公钥由服务端当场下发,对应私钥从不落地,你和客户端都解不开这份文件。这是给云端读的信封,不是给你自己恢复用的备份。

官方说上传后立即销毁,还能再核对什么?

「立即销毁」降低的是长期留存。它回答不了最初打包范围是否超出你点过的开关,也回答不了销毁前谁握着私钥。事后清空存储桶,不能倒推 9 月 18 日之前已经收下的对象被谁解过、复过制。

界面上的「优化体验」「仓库索引」关掉,上传是不是一起停?

在被拆解的旧客户端里,这两项分别管「可否用于训练」和「云端是否建索引」,都不切断打包和直传。要看的是有没有独立的「停止生成快照」开关,以及关掉之后本地是否仍出现新的待传密文。

要把一份自己还能解开的文件交给同事,该走哪条通道?

先在当前标签页用 AES-256-GCM 加密,口令只告诉已经核实过的接收方。打开 文件加密盒 即可,单文件不超过 5 GB,输出 .lock / .enc,文件和口令默认不上传,打开即用,无需账号。短秘密改走 阅后即焚,密钥放在链接的 # 后面,形态是 s.html?id={id}#{key}。工单或日志里若还叠着手机号、证件号,先在 隐私清洗 做规则脱敏,原文不作为业务数据上传。

需要自己能解开时,改走打开即用的通道

若你希望用一个把计算边界写清楚的页面来练习「密文我能解开、口令不进 Network」,打开 FastPwd 的 文件加密盒 即可,无需注册。选上面那份金丝雀,在当前标签页做 AES-256-GCM,再按第五节对照:请求里不应出现文件内容和口令,同一口令应能在本机还原。输出 .lock / .enc 可以拷到网盘;口令不要写进同一条聊天,也不要写进任何 AI 对话框。

还要把仓库片段交给云端模型时,先假定接收端看得到你粘贴的全文。中转站会在 HTTPS 终止处留下明文调用包,上一篇已经写过。这一篇多问一句:客户端若先「加密」再上传,拆信封的私钥在不在你这边。两层都过不了,就不要把活的云密钥、SSH 私钥或客户数据放进工作区再交给会打包的工具。规则脱敏只能掩码常见字段,整段密钥应改走阅后即焚或根本不外发。

下次再看见「我们本地加密后再上传」这类说明,记住三件事。第一,问私钥或口令在谁手里,不要只问算法名字。第二,问清单覆盖到不算当前文件的 .git 历史没有。第三,用一份你自己能解开的密文做对照,而不是用销毁声明做对照。能写进交接说明的,只能是这三句,不能是「磁盘上是密文,所以云端看不见」。

延伸阅读

网盘上传前给压缩包设密码,挡不住文件名,也不是本机 AES-GCM,见 网盘上传前,压缩包密码能挡住什么,为什么它不是本机 AES-GCM。HTTPS 只加密到中转站这一跳,挡不住 Prompt 里的 SSH 密钥,见 中转站 6TB 调用包里为什么会出现 SSH 密钥,HTTPS 挡不住哪一层。页面上的「本地加密」怎么用 Network 自己核对,见 浏览器本地加密怎么核对明文没有离开本机。