Zhipu's ZCode Agent Sent Whole Git Histories to Alibaba Cloud in Encrypted Snapshots
TL;DR
ZCode, the desktop coding agent from Z.ai (the Chinese lab formerly known as Zhipu, whose GLM weights you have probably run locally), was packaging entire workspaces, full .git directories included, encrypting them under a public key its own server issued, and posting them to Alibaba Cloud object storage. A developer found a 313 MB encrypted archive parked in ~/.zcode while clearing space on a base-spec MacBook Air. It held 42,411 files and 345 MB of content, 86.6% of which was Git history rather than code. Neither of the two privacy toggles in the UI stopped it.
Zhipu apologized within a day, attributed the uploads to a Repo Wiki indexing feature that shipped default-on, said the data is destroyed right after use, promised to open source the client and bring in third-party reviewers, and gave every user one extra weekly quota reset. An independent replay of the patched build found the fix flipped a default and pulled Repo Wiki server side, with the upload pipeline still sitting in the client.
A full disk, and 700 MB of someone else's business
The teardown comes from ferstar, who published on September 18 and updated it the next day. The trigger was mundane: a laptop running out of room, and a ~/.zcode directory that had quietly grown past 700 MB.
The breakdown was roughly 257 MB of cli/, 130 MB of computer-use/, and 303 MB of v2/checkpoints/. That last one is where things got interesting. Inside sat a single 313 MB encrypted archive in a pending state, waiting for a network it could reach.
Pending, in this case, means it had not gone anywhere yet. A smaller test proved the path worked end to end: a 538-file public repo compressed and encrypted down to about 15 KB, uploaded, and came back with a lastAcceptedManifestHash written into the client's state file. That field is the client recording that the object store accepted the object.
What was actually in the 313 MB
The manifest is the part that should bother you. This was not a diff, a file you had open, or the chunk of context the model needed. It was the repository.
The LFS cache alone was 196.1 MB, which means binary assets that no coding agent needs to read. .git/objects added 102.2 MB of loose and packed objects: every branch, every deleted file, every commit someone force-pushed over and assumed was gone. .git/logs carried the reflog. Global ZCode settings across workspaces went along for the ride.
A second sample, from a separate read-only verification collected in a follow-up analysis, came in at 747.9 MiB. Depending on the repo, .git ran between 86.6% and 98.9% of the payload.
The envelope you cannot open
The crypto is where this stops being sloppy telemetry and starts being a design decision. The client asks the server for credentials at POST /api/v1/snapshot/upload-credential, encrypts the archive with AES-256-CTR, then wraps that symmetric key with an RSA-OAEP-SHA256 public key the server hands it, and does a direct form POST to Aliyun OSS. The object store then calls back to Zhipu's backend to confirm receipt.
Envelope encryption is standard practice and usually good news. The direction is what matters here. Picture a safe deposit box where the bank supplies the box, keeps the only key, and assures you it never looks inside. That is the shape of this: the archive sitting on your own disk is ciphertext you have no way to decrypt, so you cannot audit what left your machine even after you find it.
Capture ran off two hooks, captureBeforePrompt and repo-wiki-update. One active session produced up to 62 capture events. The stuck 313 MB archive carried a failureCount of 564, though the follow-up analysis notes that counter is cumulative across tasks rather than 564 attempts on one file, with a default policy of three retries and 24-hour retention.
Two switches, neither of them the switch
ZCode ships with settings named optimizeAgentExperienceEnabled and repoSnapshotIndexingEnabled. Reading those, you would reasonably conclude that turning them off stops snapshots.
They govern downstream permissions instead. Per ferstar, "Optimize Experience" controls whether your data is authorized for model training, and "Repo Snapshot Indexing" controls whether the server indexes what it already received. The capture and upload sidecar started unconditionally.
The privacy policy mentions collecting text, files, and code submitted during conversations. It does not mention packaging and uploading whole workspaces and Git histories.
Zhipu's answer, inside a day
The post hit the Hacker News front page on the morning of September 18 UTC. Zhipu responded that evening Beijing time through its official user group, and IT Home and mainland outlets carried it within hours.
The company's account: the uploads came from a "code repository indexing" feature meant to support session checkpoint restore, version rollback, and Repo Wiki. Generating a Wiki page could trigger a repository upload, the data is destroyed immediately after the page is generated and not retained, and the feature was default-on at launch, which is how users got caught.
Zhipu says the issue is fixed, that it will open source the ZCode codebase and invite third-party reviewers to audit how the system behaves, and that it will publish review progress publicly. Compensation is one extra weekly quota reset for all users, which is the SaaS equivalent of a free dessert.
What the fix actually changed
This is the part worth reading twice. A third-party replay of 3.12.3 after the apology, using an isolated fake HOME, static analysis of the app.asar bundle, automation through the debug interface, and full traffic interception, found no snapshot production at all: zero checkpoints, zero encrypted files, zero credential requests.
It also found why. The default for repoSnapshotIndexingEnabled was flipped to false, legacy configs were force-migrated, and Repo Wiki was taken offline through server configuration. The upload pipeline code remained in the client, which means the behavior is re-enableable from the server rather than removed.
ferstar reports that by 3.14.0, the current build on the download page, the upload path is gone from the client and the credential endpoint returns 404. Those two findings are consistent if the code came out in the later build, and they are worth separating: one is a configuration change, the other is a code change, and only the second survives a server-side decision you do not control.
We have seen this exact movie
In July, a pseudonymous researcher published a mitmproxy capture of xAI's Grok Build CLI uploading 5.10 GiB of repository state from a 12 GB canary repo whose files the agent never read, with the opt-out toggle changing nothing. xAI disabled trace uploads by default two days later, deleted the uploaded coding data, and open sourced the client three days after that, upload code included.
Same arc, different flag: a closed harness, a capture path nobody documented, a toggle that governs something adjacent to what its label implies, and an apology plus an open-source promise once a packet capture or a disk audit makes it undeniable. ZCode is the official harness for GLM-5.3 and ships with GLM Coding plans that start at 94.4 yuan a month, so the affected population is not hobbyists poking at a beta.
If you ran ZCode, do this
- Check first, panic second. A community forensic tool, zcode-upload-forensics, reads your local state ledgers, manifests, encrypted artifacts, and pending folder, and produces a single-file HTML report with a confidence rating and explicit falsifiers. Verified against 3.11.2 on Windows and 3.7.7 on macOS.
- Assume secrets in history are exposed. If your repo ever had a key in an old commit, a reflog entry, or an LFS blob, treat it as shipped and rotate. Git history is exactly where old credentials go to be forgotten.
- Block the path if you keep the client. zcode-block-upload patches the endpoint string inside
app.asarwith an equal-length replacement so the archive never needs rebuilding. ferstar's lighter option is to make the checkpoint directory immutable withchflags uchgon macOS orchattr +ion Linux. - Update, then verify. 3.14.0 is the build where the upload path is reported gone. Update, then confirm with the forensic tool rather than the release notes.
Key Takeaways
- ZCode packaged whole workspaces including
.git, encrypted them with a server-issued RSA public key, and posted them to Aliyun OSS. One snapshot was 313 MB and 42,411 files, 86.6% of it Git history. - The two privacy toggles controlled training consent and server-side indexing, not capture or upload. The sidecar ran unconditionally.
- Because the private key lives on the vendor's server, the archive on your disk is ciphertext you cannot audit.
- Zhipu apologized on September 18, blamed a default-on Repo Wiki indexing feature, promised open source and third-party review, and gave users one weekly quota reset.
- An independent replay found the initial fix was a default flip plus a server-side shutdown of Repo Wiki, with the pipeline still shipped in the client. ferstar reports the code is gone in 3.14.0.
- This is the second closed coding harness caught doing this in three months, after Grok Build in July. Treat repository exfiltration as a default risk of closed agents, not an anomaly.
Sources: ferstar's teardown, Hacker News discussion, IT Home on Zhipu's response, Sohu on the apology and compensation, independent post-fix replay, zcode-upload-forensics, zcode-block-upload, ZCode downloads and plans