Your AI coding tool packaged your git history. Twice.

ZCode packaged 345MB of a developer's workspace and tried to upload it 564 times. It had two privacy switches. Both were honest, and both were wired to the wrong end of the pipeline.

Share
A laptop open in a dark room
Photo by Sean on Unsplash

The MacBook Air had 256GB and not much of it left, which is how this got found at all.

On 18 September a developer went looking for space. Nothing dramatic, just the housekeeping everyone does when the disk warning appears. Down in the home directory sat a folder called .zcode, holding a little over 700MB.

ZCode is the coding agent from Z.ai, the company behind the GLM models. It had been installed, it had been useful, and it had been sitting there.

Inside .zcode/v2/checkpoints/ was a single encrypted archive of 313MB.

Nobody had asked for it. Nobody had been told about it. And the metadata beside it recorded something stranger than the file itself: 564 attempts to upload it.

What was in the ZCode package

Uncompressed, the snapshot came to roughly 345MB, and the proportions are the part worth sitting with.

Source code and documentation accounted for 13.4% of it. Everything else, 86.6%, was the .git directory. Commit history objects at 102.2MB. The LFS cache, which is where the large binary assets live, at 196.1MB. Reflogs at 0.6MB.

That distinction matters more than it looks. Your working tree is what you have now. Your git history is every decision you have ever reversed, every secret someone committed and then removed in the next commit, every branch name that gives away an unannounced client, and the full record of who touched what and when. Handing over a repository is not the same act as handing over the code in it.

Only one seventh of it was the code.

What the 345MB ZCode snapshot contained, uncompressed, in megabytes

LFS cache, the large binary assets

196.1

Commit history objects

102.2

Source code and documentation

46.1

Reflogs

0.6

The three teal bars are the .git directory, 86.6% of the archive. Handing over a repository is not the same act as handing over the code in it. Source: Ferstar's technical analysis of the 18 September 2026 snapshot.

A separate manifest hashed the global ZCode configuration across every workspace on the machine, which is how you find out what else somebody is working on without needing to read any of it.

One small public repository, 538 files, about 15KB encrypted, uploaded successfully. The large commercial project stayed in pending status. Not because anything stopped it. Because it exceeded a size limit.

What ZCode’s privacy settings actually controlled

Here is where the story stops being an anecdote about a badly behaved tool.

ZCode had privacy controls. Two of them, both visible, both plainly labelled. "Optimize Experience" governed whether your inputs could be used to train models. "Repo Snapshot Indexing" governed server-side indexing of your repository.

Ferstar, who found the archive, reported that neither setting prevented ZCode from creating or uploading snapshots. Ruohang Feng reproduced the investigation on his own machine the same day and found the indexing setting recorded as false 1,339 times while snapshots were still being created, with two workspaces where the git directory made up 93.9% and 98.5% of the snapshot.

Both toggles were honest about what they did. Neither of them touched capture.

Two switches. Four stages. They were wired to the last two.

Where each ZCode privacy control acted, against where the data actually moved

1. Capture, on your machine

Packages the workspace, 345MB, 86.6% of it the git directory. Runs at startup whenever a valid login token exists. Delete the archive and it rebuilds within half an hour.

No control. Not exposed in settings.

2. Upload, off your machine

Credentials requested from zcode.z.ai, then posted direct to Aliyun object storage. Encrypted with a public key from the server; the private key never reaches you.

No control. Not exposed in settings.

3. Indexing, on their servers

Server-side indexing of the repository you have already sent.

Controlled by "Repo Snapshot Indexing"

4. Training use

Whether your inputs feed model training.

Controlled by "Optimize Experience"

Both switches were labelled accurately. Both did what they said. Neither one sat at a stage where the data was still yours. Sources: Ferstar's write-up and Ruohang Feng's follow-up analysis, both 18 September 2026.

The capture component activated whenever the client could obtain a valid login token, and kept running regardless of what the settings said. The 18 September release, version 3.12.3, shipped a changelog covering model management and workspace changes with nothing at all about repository snapshots or their controls.

So a careful user, the kind who opens settings before typing anything, who reads both descriptions and switches both off, would have concluded they had control. They would have had control over training. They would have had control over indexing. They would have had no control over whether their git history left the machine.

Two details that turn this from a bug into a design

Rows of servers in a data centre
Racks like these are where the 313MB went, by direct upload to object storage rather than through the vendor's own servers. Photo by Kevin Ache on Unsplash.

The archive was encrypted. AES-256-CTR for the contents, with the symmetric keys wrapped using RSA-OAEP-SHA256 against a public key supplied by the server. Standard envelope encryption, competently done.

The private key never touches your machine.

Read that again with the scene in mind. A developer finds a 313MB file in their own home directory, on their own laptop, containing their own repository, and cannot open it.

The encryption was not protecting the archive from an attacker. It was protecting it from the person it was taken from. Whatever was in there, only Z.ai could say, a point The Next Web put more sharply than I can on 20 September: only Z.ai can say it was deleted either.

And the uploads did not go to Z.ai's own servers. The client requested credentials from zcode.z.ai, then posted directly to Aliyun object storage, which means the traffic you would look for if you were watching for it is not the traffic that carried the data.

Then the second detail. Delete the pending archive and ZCode built another 313MB package within half an hour.

That's not an accident. Something in that system treats the absence of the snapshot as a fault to be corrected. A person removing their own files from their own disk was modelled as a problem, and the software fixed it.

Why the toggles were in the wrong place

I want to be careful here, because the easy version of this piece is that a company behaved badly and you should not install its software. That version is cheap and it teaches nothing, because the same shape shows up in products built by people with entirely good intentions. Including, if I'm honest, some of mine.

Think about where a privacy setting actually gets implemented.

A toggle that governs use is a flag on a record. The data has already arrived, it already sits in a table, and somewhere a job checks a boolean before including it in a training set or an index. That is an afternoon of work. It is testable, it is revertible, and it does not require anyone to touch the ingestion path.

A toggle that governs capture is architecture. It has to sit at the very start of the pipeline, before the thing exists, and every downstream feature that assumed the data would be there has to cope with it not being there. The snapshot feature stops working. The indexing that makes the assistant fast stops having anything to index. Someone has to decide what the product does for a user who has switched it off, and that decision is a product decision, not a privacy one.

So the cheap control gets built, it gets labelled accurately, and it goes on the settings screen. And from the settings screen, a use-stage control and a capture-stage control look exactly alike. Both are a switch with a sentence under it. Nothing in the interface tells you which stage of the pipeline you are standing at.

The claim I will defend: a consent control placed at the point of use is not a weaker version of one placed at the point of capture, it is a control over a different thing entirely, and the user cannot tell them apart from the outside.

The version of this I have shipped

Hands typing on a laptop with code on the screen
The decision that matters is made months before the incident, by people writing an accurate label on the cheaper option. Photo by Rahul Mishra on Unsplash.

I've built systems that hold other people's data, including payment monitoring across chains and compliance tooling, and I've been in the room for the conversation this piece is really about.

It goes like this. Someone asks for a privacy setting. The team scopes it. Capture-stage is three weeks and breaks two features. Use-stage is two days and breaks nothing. Nobody in that room is being dishonest. The label that gets written is accurate. And the thing that ships is the cheap one, because the expensive one would have required rethinking why the data was being collected in the first place, and that question was settled long ago by somebody who has left.

The gap between those two options is where almost every privacy failure I have seen actually lives. Not in a decision to deceive. In a decision about scope, made under delivery pressure, by people who then wrote an honest label on the smaller thing.

Which is why the specific lesson from ZCode is not about Z.ai. When you evaluate a tool, asking whether it has privacy settings gets you nowhere. What you need is the stage of the pipeline they act on, and nothing on the settings screen will tell you. You find out by reading the client, watching the network, or waiting for someone else to run out of disk space.

What happened next, and what it does not prove

Z.ai responded the same day, 18 September, at 17:44. On 21 September the company open-sourced ZCode under Apache-2.0, publishing the desktop app, the web interface, the CLI, the backend services and the agent runtime, and said on X that it had completed the necessary remediation and apologised to users.

Open-sourcing the client is a real move and more than most companies do. It also proves less than it appears to.

The client tells you whether capture still happens. It cannot tell you whether the archives already uploaded were deleted, who at the company could read them, or what access controls sit on the storage they went to. Those are server-side facts and no amount of published client code contains them. The repository also went up without a security policy or a disclosure route, which is an odd omission for a release whose entire purpose is to restore trust.

The statement said remediation was complete. It did not say what was fixed.

Questions people are actually asking this week

Am I affected? Only if you installed ZCode and signed in. The capture ran when the client held a valid login token, so an installed-but-never-logged-in copy is not the case described. Other coding agents are not implicated by this reporting, which says nothing about whether they do something similar.

How do I check? Look for ~/.zcode and specifically ~/.zcode/v2/checkpoints/. An archive there that you did not create, and cannot open, is the artefact in question. More generally, checking the size of any AI tool's config directory takes about a minute and is the cheapest audit available to you.

Was the data that already uploaded deleted? Unknown. Z.ai said remediation was complete and did not say what was remediated. Open-sourcing the client lets you verify whether capture still happens; it cannot tell you anything about what sits on the server or who can read it.

Does open-sourcing fix it? It fixes the part you can inspect. The client is now readable, which is genuinely more than most vendors offer. Server-side deletion, access controls and retention remain unverifiable from published client code, and the repository went up without a security policy or disclosure route.

Should we ban AI coding agents? That is not what I take from this. The useful change is narrower: before approving a tool, ask which stage of its pipeline its privacy controls act on, and treat an unanswered question as an answer.

Marking my confidence

Established. The archive size of 313MB, the 564 logged upload attempts, the roughly 345MB uncompressed total, the 86.6% git share broken down as 102.2MB of commit objects, 196.1MB of LFS cache and 0.6MB of reflogs, the 538-file public repository that uploaded, the AES-256-CTR and RSA-OAEP-SHA256 envelope scheme with the private key held server-side, the direct posts to Aliyun object storage, the two settings and what each actually governed, version 3.12.3 and its changelog, the re-packaging within half an hour, the 18 September response at 17:44 and the 21 September Apache-2.0 release.

Not original. The discovery is Ferstar's, the reproduction and the settings analysis are Ruohang Feng's, and the observation that only Z.ai can verify its own deletion is The Next Web's. I have added no findings. What I have added is the argument about where in a pipeline consent controls sit and why the cheap placement wins.

Inferred. That the use-stage versus capture-stage split explains most privacy failures I have encountered rather than deception explaining them. That comes from the systems I have worked on, which is a real sample and a small one.

Guess. That within a year at least one major AI coding tool ships a settings screen that states, per control, which stage of the pipeline it acts on, and that it will be a smaller vendor doing it as a differentiator rather than a large one doing it as a standard. Low confidence on the timing.

What would change my mind: evidence that the capture here was a straightforward bug, in which case this is a defect story and not a design story, or a credible account from inside a comparable product showing the capture-stage control was built first and the use-stage one added later.

What I would actually check

Three things, in rising order of effort, and the first takes ten minutes.

Look at what your AI coding tools have written to disk. Check the sizes of their config and cache directories, and look for anything you did not create and cannot open. Disk usage is the cheapest telemetry you've got and almost nobody reads it, which is a habit I have complained about before.

For any tool your team has approved, find out what the privacy settings actually govern. Not what they are called. If the vendor documents the stage, that's a signal in itself. If they won't answer the question, that's also a signal.

And if you ship a consent control in your own product, write down which stage it acts on, in the interface, where the user reads it. It costs a sentence. It's the difference between an honest label and an honest control, and this week is a fairly expensive demonstration of the gap between those two.