🔶 ZCode,这款 GLM 编程 Agent 会静默上传你的 Git 历史
英文标题:🔶 ZCode, the GLM coding agent, silently uploads your Git history
逆向工程研究者 ferstar 称,Z.ai 的 AI 编程桌面应用 ZCode 在登录状态下会将整个工作区打包、加密并上传至阿里云 OSS,其中包括完整 `.git` 历史、LFS 缓存、reflog 和全局应用配置。其捕获样本显示,一个 345MB 的商业工作区被打包为 313MB 加密归档,涉及 42,411 个文件,且 `.git` 目录占载荷的 86.6%。研究指出,归档使用由 Z.ai 服务端掌握私钥的信封加密机制,用户及本地客户端均无法解密已生成的密文。该事件凸显:即便开发者在本地运行开放权重模型,围绕模型的闭源 Agent harness、桌面应用和更新链路仍可能构成关键的数据与信任风险。
2.2 KB
1.8 KB
1.7 KB
42.9 KB
中文
2026 年 9 月 18 日,一名使用 ferstar 化名的开发者发布了对 ZCode 的逆向工程解析。ZCode 是总部位于北京的 Z.ai 推出的 AI 编程桌面应用;Z.ai 也是 GLM 开放权重模型家族的开发者——这些模型在本地 AI 社区的各类本地设备上运行,包括本站追踪的 GLM-5.3-Flash。该发现比多数隐私丑闻看上去更糟:只要应用处于登录状态,它就会静默打包用户的整个工作区——包括完整的 .git 历史、LFS 资产缓存、reflog 和全局应用配置——将其加密后上传至阿里云 OSS,即 Alibaba Cloud 的对象存储服务。研究者自己的抓包显示:一个来自 345MB 商业工作区的 313MB 加密归档,包含 42,411 个文件;研究者调查期间,日志还记录了 564 次上传失败尝试。
如果你在本地运行 GLM,发布权重的公司与开发者可能在其上层使用的运行时并不是同一回事——讨论串的反应表明,这种混淆确实存在:数名评论者因为 GLM 是开放的,便假定 ZCode 也是开源的。但事实并非如此。权重是开放的,harness 则是闭源的;它是 Z.ai 为自家模型打造的 harness,并以第一方集成能力为卖点,声称没有第三方编辑器能够匹敌。
这则消息在数小时内以中英文传播开来:ferstar 的帖子浏览量超过 276,000 次;FeiZ 的中文警示帖(“暂时禁用 ZCode……仍然应尽可能使用开源 Agent”)又获得了 63,800 次浏览。被引用最多的回应来自 Petri Kuittinen,他自己的 AI Agent 已开源并配有安全文档:“我的建议一直是、也仍然是:不要信任闭源 AI harness。”
将一个可疑目录变成完整事件的关键细节,是加密密钥。ZCode 使用信封加密:载荷由对称密钥加密,该密钥再由 RSA-OAEP 公钥封装。该公钥在上传凭据协商期间由服务器下发;相应私钥仅存在于 Z.ai 的云端。ferstar 尝试使用本地系统中的每一把私钥解封归档,但均告失败。存放在用户自己磁盘上的 313MB 密文,用户无法解密,ZCode 客户端本身也无法解密。
ferstar 在帖子中的结论是:“一把只有服务器能够使用的密钥,只有一个用途:确保服务器想读你的代码时随时都能读。”
被打包的内容
打包清单以明文形式本地存储,内容很具体。对于一个包含 42,411 个文件的快照:
| 内容 | 大小 | 占比 |
|---|---|---|
| .git/lfs/ | 196.1 MB | 56.8% |
| .git/objects/ | 102.2 MB | 29.6% |
| .git/logs/ | 0.6 MB | 0.2% |
| 源代码和文档 | 46.2 MB | 13.4% |
仅 .git 目录就占载荷的 86.6%。
一个包含 42,411 个文件的快照的载荷构成:.git 目录占加密归档的 86.6%。 ::: 这很重要,因为 Git 对象存储并不是工作树的快照——它是该仓库自第一天起的完整沿革。后来某次提交中删除的 API 密钥仍在其中。暴露未发布产品计划的未推送分支名称仍在其中。.git/config 中的内部主机名和仓库路径仍在其中。一次被捕获的归档包含的是多年的工程历史,而非你当时打开的文件。根据客户端的 app.asar 重建出的上传管线:客户端向 zcode.z.ai 请求凭据,后者返回 OSS 表单签名、对象键、大小上限和每轮使用的 RSA 公钥;客户端将工作区打包为 tar.gz,以 AES-256-CTR 加密,封装对称密钥,并将归档直接 POST 至阿里云 OSS;OSS 随后回调 Z.ai 后端以登记该快照。测试期间,运行中的客户端持续连接至 zcode.z.ai 和两个阿里云 OSS 节点。:::figure /images/articles/zcode-git-upload/upload-flow.svg 重建出的 ZCode 快照上传流程:从协调器获取凭据、本地打包和加密、直接以表单 POST 至阿里云 OSS、回调登记。由 ferstar 根据客户端 app.asar 重建。
这些开关无法阻止它
自然的做法是打开设置。ferstar 将 UI 开关与代码进行了交叉比对:
- “优化体验”(
optimizeAgentExperienceEnabled)仅控制是否授权将数据用于模型训练。快照捕获和上传仍会继续。 - “仓库快照索引”(
repoSnapshotIndexingEnabled)仅控制服务器是否为已上传的快照建立索引。本地打包和上传仍会继续。
宿主程序集会在启动时无条件实例化捕获 sidecar,不受用户偏好设置控制——唯一要求是 token 提供方能够生成有效 JWT。会话日志显示,单个活跃会话中发生了 62 次捕获事件,分别在每次提示词之前以及任务完成时触发。
第二个来源佐证了这一机制。OrcaPromptVault 是一个公开收集 AI harness 提示词捕获记录的项目,其中保存着来自 ZCode 的 131KB 系统提示词和 31 个工具的能力表面。检查点/回退功能已接入系统提示词——模板“Workspace rewind applied. rewindId, checkpointId, strategy, restoredFiles”出现了五次。这是快照管线面向用户的一端,也是文件系统锁会禁用的功能。
该 Agent 的完整工具能力表中,不包含任何快照、上传或遥测工具。数据外传管线不是 Agent 工具;它是位于工具循环之外、由宿主层实例化的 sidecar。这也是为什么没有任何权限设置能阻止它,以及为什么 Agent 本身永远看不到它。在捕获的 131KB 指令中,没有提及阿里云、OSS、上传或隐私。
这次捕获还增加了 ferstar 未提及的一项细节:ZCode 内置 ReadSessionContext 工具,可按会话 ID 按需读取其他已持久化的 ZCode 会话。结合宿主层的快照 sidecar,会话内容既被本地持久化,也会被捕获至云端。
泄露系统提示词中的检查点模板(出现五次)以及 Agent 的 31 个工具能力表;其中不包含快照、上传或遥测工具。
隐私政策没有提及此事
ZCode 的隐私政策称,该工具收集“在对话期间提交的文本、文件和代码”——这是每款 AI 编程工具都会作出的标准推理上下文披露。在该政策、FAQ 和更新日志中,ferstar 都未发现任何关于打包并上传整个工作区和 Git 历史的说明。最接近的表述,是一项称优化计划默认关闭的模板化声明。
使情况更糟的背景
ZCode 于 2026 年 7 月推出,其发布宣传直接建立在信任之上。在 Claude Code 隐藏遥测争议发生数周后,Z.ai 将这款 harness 定位为 Anthropic Claude Code 的竞争对手,并将开放权重描述为摆脱 kill-switch 问题的出路。一名 Z.ai 高管在 X 上被问及 ZCode 是否会包含“任何形式的间谍软件”时回答,公司不会实施“ZCode 网站所列内容之外的任何东西”。
工作区快照并未列在 ZCode 网站上。
Z.ai 于 2026 年 1 月在香港证券交易所上市。截至发稿,公司的官方 X 账号尚未回应 ferstar 的帖子。最引人注目的回复来自一个与 ZCode 团队有关联的账号:“嘿,很抱歉让你发现了它。”这看起来是在确认该机制,而非否认。ferstar 的帖子在 13 小时内浏览量突破 276,000;V2EX 和 HN 相关渠道中的讨论大多围绕一个分歧展开:Agent 在工具调用时经常会在取得同意后上传代码片段。这里则是在未经同意的情况下上传整个仓库及其全部历史,并且经过加密后只有供应商能够读取。
有效的防御措施
删除待上传归档并不起作用:客户端会在半小时内重新打包一个新的 313MB 归档,重试计数器随之递增。有效的修复方法是文件系统级别的操作:在内核层面使 checkpoints 目录不可写。
Linux:
rm -rf ~/.zcode/v2/checkpoints
mkdir -p ~/.zcode/v2/checkpoints
sudo chattr +i ~/.zcode/v2/checkpoints
macOS:
rm -rf ~/.zcode/v2/checkpoints
mkdir -p ~/.zcode/v2/checkpoints
chflags uchg ~/.zcode/v2/checkpoints
代价是:检查点回退 UI 将停止工作——而这项功能本来就要求上传你的代码。聊天、自动补全和工具调用仍可正常运行。可使用 chattr -i 或 chflags nouchg 恢复。
这对“本地”意味着什么
在本地运行开放权重模型是其核心卖点:你的模型、你的硬件、无需按 token 付费、不会被供应商随时关停。ZCode 事件将这一问题推进到了模型层之外。模型周边的运行时——harness、桌面应用、更新管线——同样是信任边界的一部分;一个运行在本地、但被持续向云端通信的 harness 包裹的模型,并不算本地。
由此可以得出两个检查问题,它们适用于这一领域的所有 harness,而不仅是 ZCode:当你处于登录状态时,运行时会传输什么?它存储的内容由谁能够解密?Tokenstead 正是因此而追踪 Agent harness 及其遥测行为;如果 Z.ai 以修复、披露变更或声明作出回应,本文将更新。
来源:
English / 原文
On September 18, 2026, a developer going by ferstar published a reverse-engineering walkthrough of ZCode, the AI coding desktop app from Z.ai, the Beijing-headquartered company behind the GLM family of open-weight models - the same models running on local rigs all over the local-AI community, including GLM-5.3-Flash, tracked on this site. The finding reads worse than most privacy scandals: whenever the app is logged in, it silently packages the user’s entire workspace - complete .git history, LFS asset cache, reflogs, and global app configs - encrypts it, and uploads the archive to Aliyun OSS, Alibaba Cloud’s object storage. The researcher’s own capture: a 313MB encrypted archive built from a 345MB commercial workspace, 42,411 files, with 564 failed upload attempts logged while the researcher investigated.
If you run GLM locally, the company that publishes the weights is not the same thing as the runtime a developer might use on top of them - and the thread reaction showed the confusion is live: several commenters assumed ZCode was open source because GLM is. It is not. The weights are open; the harness is closed, and it is Z.ai’s harness for its own models, pitched as first-party integration no third-party editor can match.
The story spread in both languages within hours: ferstar’s post passed 276,000 views, and FeiZ’s Chinese-language alert thread (“disable ZCode for now… it’s still best to use open-source agents as much as possible”) drew another 63,800. The most quoted response came from Petri Kuittinen, whose own AI agent is open sourced with security documentation: “My advice has been and continues to be: do NOT trust closed source AI harnesses.”
The detail that turned a suspicious directory into a story: the encryption key. ZCode uses envelope encryption - the payload is encrypted with a symmetric key, and that key is wrapped with an RSA-OAEP public key. The public key is delivered by the server during upload-credential negotiation. The corresponding private key lives only in Z.ai’s cloud. ferstar attempted to unwrap the archive with every private key on the local system and failed. The 313MB ciphertext sitting on the user’s own disk cannot be decrypted by the user or by the ZCode client itself.
ferstar’s conclusion, from the post: “A key that only the server can use serves exactly one purpose: making sure the server can read your code whenever it wants.”
What gets packed
The packaging manifest is stored locally in plaintext, and it is specific. For a 42,411-file snapshot:
| Content | Size | Share |
|---|---|---|
| .git/lfs/ | 196.1 MB | 56.8% |
| .git/objects/ | 102.2 MB | 29.6% |
| .git/logs/ | 0.6 MB | 0.2% |
| Source code and docs | 46.2 MB | 13.4% |
The .git directory alone is 86.6 percent of the payload.
Payload breakdown of one 42,411-file snapshot: the .git directory is 86.6 percent of the encrypted archive. ::: That matters because a git object store is not a snapshot of your working tree - it is the complete lineage of the repository since day one. Deleted-in-a-later-commit API keys are in there. Unpushed branch names that reveal unreleased product plans are in there. Internal hostnames and repo paths from .git/config are in there. A captured archive is years of engineering history, not the files you had open. The upload pipeline, reconstructed from the client’s app.asar: the client requests credentials from zcode.z.ai, which returns OSS form signatures, an object key, a size cap, and a per-round RSA public key; the client packs the workspace to tar.gz, encrypts with AES-256-CTR, wraps the symmetric key, and POSTs the archive directly to Aliyun OSS, which callbacks to Z.ai’s backend to register the snapshot. The running client maintained persistent connections to zcode.z.ai and two Aliyun OSS nodes during the test. :::figure /images/articles/zcode-git-upload/upload-flow.svg The reconstructed ZCode snapshot upload flow: credentials from the coordinator, local packing and encryption, direct form POST to Aliyun OSS, callback registration. Reconstructed from the client app.asar by ferstar.
The toggles do not stop it
The natural move is opening settings. ferstar cross-referenced the UI switches against the code:
- “Optimize Experience” (
optimizeAgentExperienceEnabled) only controls whether data is authorized for model training. Snapshot capture and upload continue. - “Repo Snapshot Indexing” (
repoSnapshotIndexingEnabled) only controls whether the server indexes uploaded snapshots. Local packaging and upload continue.
The host assembly instantiates the capture sidecar unconditionally at startup, with no gating on user preferences - the only requirement is that the token provider can produce a valid JWT. Session logs showed 62 capture events from a single active session, triggered before every prompt and on task completion.
A second source corroborates the mechanism. OrcaPromptVault, a public collection of captured AI harness prompts, holds a 131KB system prompt and a 31-tool surface from ZCode. The checkpoint/rewind feature is wired into the system prompt - the template “Workspace rewind applied. rewindId, checkpointId, strategy, restoredFiles” appears five times. This is the user-facing tip of the snapshot pipeline, the feature the filesystem lock disables.
The agent’s complete tool surface contains zero snapshot, upload, or telemetry tools. The exfiltration pipeline is not an agent tool; it is a host-level sidecar instantiated outside the tool loop. That is why no permission setting stops it, and why the agent itself never sees it. Across 131KB of captured instructions there is no mention of Aliyun, OSS, uploads, or privacy.
The capture adds a detail ferstar did not mention: ZCode ships a ReadSessionContext tool that reads other persisted ZCode sessions on demand by session ID. Combined with the host-level snapshot sidecar, session content is both locally persisted and cloud-captured.
The leaked system prompt’s checkpoint template (appears five times) and the agent’s 31-tool surface, which contains no snapshot, upload, or telemetry tools.
The privacy policy does not mention it
ZCode’s privacy policy states the tool collects “text, files, and code submitted during conversations” - the standard inference-context disclosure every AI coding tool makes. Across the policy, FAQ, and changelog, ferstar found no mention of packaging and uploading entire workspaces and git histories. The closest line is a template statement about the optimization program being off by default.
The context that makes it worse
ZCode launched in July 2026, and its launch pitch ran directly on trust. Z.ai positioned the harness against Anthropic’s Claude Code weeks after the Claude Code hidden-telemetry controversy, with open weights positioned as the escape from the kill-switch problem. A Z.ai executive, asked on X whether ZCode would include “any sort of spyware,” answered that the company would not implement “anything beyond what’s listed” on the ZCode website.
Workspace snapshotting is not listed on the ZCode website.
Z.ai went public on the Hong Kong Stock Exchange in January 2026. The company’s official X account had not responded to ferstar’s post as of publication. The most visible reply came from an account affiliated with the ZCode team - “hey I am sorry to let you find it” - which reads as confirmation of the mechanism, not a rebuttal of it. ferstar’s tweet passed 276,000 views within 13 hours, and discussion threads on V2EX and HN-adjacent channels split mostly along one line: agents upload code fragments during tool calls all the time, with consent. This is a full repository plus its entire history, without consent, encrypted so only the vendor can read it.
The defense that works
Deleting the pending archive does not work: the client re-packaged a fresh 313MB archive within half an hour, retry counter incrementing. The fix that holds is filesystem-level. Make the checkpoints directory unwritable at the kernel level:
Linux:
rm -rf ~/.zcode/v2/checkpoints
mkdir -p ~/.zcode/v2/checkpoints
sudo chattr +i ~/.zcode/v2/checkpoints
macOS:
rm -rf ~/.zcode/v2/checkpoints
mkdir -p ~/.zcode/v2/checkpoints
chflags uchg ~/.zcode/v2/checkpoints
The trade: the checkpoint rollback UI stops working - a feature that required uploading your code in the first place. Chat, autocomplete, and tool calls work normally. Restore with chattr -i or chflags nouchg.
What it means for local
Running open weights locally is the pitch: your model, your hardware, no per-token bill, no vendor switch-off. The ZCode story sharpens the point past the model layer. The runtime around the model - the harness, the desktop app, the update pipeline - is part of the trust surface, and a locally-running model wrapped in a cloud-phoning harness is not local.
Two checks follow from this, and they apply to every harness in this space, not only ZCode: what does the runtime transmit when you are logged in, and who can decrypt what it stores. Tokenstead tracks agent harnesses and their telemetry behavior for exactly this reason; this piece will be updated if Z.ai responds with a fix, a disclosure change, or a statement.
Sources: