Skip to main content
zcodez.aizhipuai coding privacyai coding securitygit historylocal-firstair-gaprotate secrets

ZCode tried to upload your Git history. What actually happened.

Bodega One7 min read
Share
Quick answer

A paying subscriber found that ZCode, Z.ai's desktop coding app (v3.12.3), packaged his workspace, full .git history included, encrypted it with a key only the server can open, and tried to upload it to Alibaba Cloud storage before every prompt. Turning off both privacy toggles did not stop the packaging or upload attempts. To be precise: his 313 MB commercial repo never actually left his network, but a small public repo did upload, and another user reproduced it. Z.ai says it is fixed and open-sourced the client. Fair warning, we build a local-first AI coding tool, so we are not neutral. But the pattern holds either way: you cannot opt out of an upload that never happens.

On September 18, 2026, a developer who goes by ferstar published a teardown titled “Inside ZCode: Silently Uploading Your Entire Git History to the Cloud.” He is not a hacker. He is a paying GLM Coding plan subscriber who noticed ~/.zcode eating 700 MB while he was freeing disk space on a 256 GB MacBook Air, and then kept pulling the thread. This is the verified version, the parts that are still open, and what to do about it.

What is ZCode?

ZCode is the official AI coding desktop app from Zhipu, the company behind the GLM models, which operates as Z.ai. It launched around July 1, 2026 as a harness for GLM-5.2 and got a lot of attention on Hacker News. A harness is the app around the model: it reads your files, runs tools, manages sessions, and talks to servers. That layer is where this story lives.

What did the researcher find?

While logged in, ZCode 3.12.3 packaged the workspace into an archive, encrypted it, and POSTed it, or tried to, straight to Alibaba Cloud OSS storage, without any prompt or notice. The details, from his write-up:

  • What went into the archive. The workspace as a tar.gz, including the full .git directory, the LFS cache, reflogs, and hashes of global ZCode config files.
  • A key you cannot use. The archive was encrypted with AES-256-CTR. The key was wrapped with RSA-OAEP using a public key sent by the server. The private key stays server-side, so neither the user nor the client can decrypt the local copy.
  • Direct upload. The client fetched credentials from zcode.z.ai/api/v1/snapshot/upload-credential, then POSTed the archive to Alibaba Cloud (Aliyun) OSS, which called back to Zhipu's backend.
  • Constant triggers. A captureBeforePrompt hook ran before every prompt, and a second trigger fired on task completion. He logged up to 62 capture events in a single session.

The snapshot he measured, his commercial repo, which turned out too big to upload, was 42,411 files. Here is where the bytes were:

Part of the snapshotSizeShare
.git/lfs196.1 MB56.8%
.git/objects102.2 MB29.6%
.git/logs0.6 MB0.2%
Source and docs~46.2 MB13.4%

Add the first three rows and .git is 86.6% of the payload. The working tree was the small part.

Why is Git history worse than the files you can see?

Because history keeps everything you already deleted, including secrets you thought were gone. A key removed in a later commit still sits in .git/objects. Unpushed branch names, internal hostnames in .git/config, and old experiments all travel with it. Most people scrub the working tree and forget the history exists. An agent that only needed the current files had no reason to ship the past.

Why didn't the privacy toggles stop it?

Because they controlled training and indexing, not whether the snapshot was packaged and sent. In his tests, “Optimize Experience” only governed training authorization, and “Repo Snapshot Indexing” only governed whether the server indexed what it received. With both off, packaging and upload attempts continued. The background process started unconditionally and needed only a valid login token. Deleting the files did not help either: when he removed a pending archive, a fresh one was packed within about 30 minutes.

This is the same gap we covered in the Grok Build CLI incident: a toggle is a promise about one thing, and the network traffic is another. The privacy policy he archived (last updated June 15, 2026) mentions code submitted in conversations. It says nothing about workspace snapshots or Git history.

Did his private repo actually get uploaded?

No. The 313 MB archive of his commercial project never left his network, and it is important to say so plainly. It failed against a size limit 564 times, and his router logs confirm nothing got out. A separate small public-repo workspace of 538 files, about 15 KB compressed and encrypted, was accepted by the server. Another user reproduced it on Windows, with small workspaces that appear to have been accepted.

So the accurate reading is: uploads happened, the pipeline started for any logged-in user on 3.12.3, and a big repo was blocked by a size cap rather than by any decision to protect it. The proof is of behavior, not of a specific private repo being leaked.

What did Z.ai say, and what does it prove?

Z.ai said the uploads served codebase indexing, and that the issue is fixed, but the fix and the audits do not answer every question. Here is their response, as fairly as we can summarize it:

  • On September 18, Z.ai attributed it to “codebase indexing” used for local indexes, session checkpoint restore, and Repo Wiki. It said generating Wiki pages in the cloud “may” trigger an upload, that data was destroyed immediately after Wiki generation and not stored, and that it was on by default early on.
  • Version 3.14.0 removed the upload pipeline. The credential endpoint now returns 404. Version 3.14.3 shipped September 23.
  • On September 21, Z.ai published github.com/zai-org/ZCode under Apache-2.0. The researcher audited it: the upload pipeline is absent, and checkpoints are purely local Git diffs stored under ~/.zcode/checkpoints. That sits awkwardly with the claim that checkpoint restore needed uploads.
  • Per Z.ai's summary, CAICT found the zcode-prod bucket held zero data and NSFOCUS found the bucket and its objects deleted. Full reports are promised.

Credit where due: a same-day public response, a fix, an open-source release, and an extra quota reset for users. Still open: an empty bucket after the fact cannot show what happened to data uploaded before it was emptied. Nobody has said who held decryption keys for past snapshots. And the open-source repo is two flattened commits, which shows what the code does now, not what it did in 3.12.3.

What should you do if you used ZCode?

Update, rotate anything that ever lived in your Git history, and treat the toggles as training settings. In order:

  1. Update to 3.14.x or later. Versions before 3.14.0 still had the upload pipeline.
  2. Rotate secrets that ever existed in the history of a repo you opened with ZCode. Not just the current files. A key deleted three commits ago counts.
  3. Consider locking the checkpoints directory. The researcher still recommends wiping and locking ~/.zcode/v2/checkpoints, the path in the desktop client (macOS: chflags uchg, Linux: sudo chattr +i), since the desktop client can hot-update.
  4. Assume a privacy toggle is about training, not transmission, in any cloud tool, until a network capture says otherwise. Our Copilot opt-out guide makes the same point about a different vendor.

Why is the risk in the harness, not the model?

Because the model only sees what the app sends it, and the app decides what else leaves your machine. This is the second repo-upload incident this summer, after Grok Build CLI in July. Different vendors, different clouds, same shape: a coding agent quietly packaged a repository, history included, and tried to move it off the developer's machine, and a settings toggle did not mean what it appeared to mean. This is about app behavior, not about any one country or company. Any closed desktop agent that logs in to a service can do this. The secrets exposure in CI agents story is the same lesson from another angle.

How does a local-first setup change this?

With a local model, it makes this class of problem a non-event, because there is no server for a snapshot to go to. Bodega One Code is a local-first AI coding IDE and CLI. You bring your own model, with 10+ provider presets including fully local ones, and air-gap mode blocks network egress at the process level (the nine layers of enforcement are documented). With a local model in air-gap mode, a run cannot reach anyone's bucket.

Honest caveats, since this whole post is about not taking claims on faith:

  • Bodega One Code is not open source. So the claim is testable rather than trust-me: put it behind a proxy in air-gap mode and watch for egress. There should be none.
  • If you choose a cloud model provider, your prompts and the context the agent sends go to that provider. Air-gap plus a local model is what keeps everything on your machine.
  • It is free for everyone during the open beta, and beta software has bugs. Report them on Discord or GitHub.
  • If you want a fully open-source stack, opencode or Cline pointed at a local model through Ollama is a solid route.

Sources

Common questions

What did ZCode actually upload?
According to researcher ferstar, ZCode 3.12.3 packaged the open workspace into a tar.gz archive, including the full .git directory, LFS cache and reflogs, encrypted it, and tried to upload it to Alibaba Cloud OSS using credentials fetched from a Z.ai endpoint. In the snapshot he measured, which was too large and never left his network, .git made up 86.6% of the payload. A small public-repo snapshot did upload. Capture ran before every prompt and after task completion.
Did ZCode upload the whole 313 MB commercial repo?
No. The researcher is clear on this. The 313 MB archive of his commercial project failed to upload 564 times on size limits, and his router logs confirm it never left his network. A separate small public-repo workspace of 538 files did upload and was accepted by the server, and another user reproduced accepted uploads on Windows with small workspaces.
Did turning off the privacy toggles stop the upload?
No. Per the researcher, Optimize Experience only controlled training authorization, and Repo Snapshot Indexing only controlled whether the server indexed uploaded snapshots. With both off, packaging and upload attempts continued, because the background process started unconditionally and only needed a valid login token.
What did Z.ai say, and is it fixed?
Z.ai attributed the behavior to codebase indexing, session checkpoints and Repo Wiki generation, said uploaded data was destroyed immediately after use, and said it was fixed. Version 3.14.0 removed the upload pipeline and the credential endpoint now returns 404. Z.ai also published ZCode under Apache-2.0, and the researcher found no upload code in that release.
What should I do if I used ZCode?
Update to 3.14.x or later, rotate any secret that ever existed in the Git history of a repo you opened with ZCode, consider locking the checkpoints directory as the researcher suggests, and treat data toggles in any cloud tool as training settings rather than proof your code stays local.
How does a local-first tool avoid this class of problem?
If the model runs on your machine, there is no server for a workspace snapshot to go to. Bodega One Code has an air-gap mode that blocks network egress at the process level, and it is testable with a proxy. If you pick a cloud model provider, your prompts and the context the agent sends still go to that provider.

Written by the Bodega One team. We build Bodega One Code, the local-first AI IDE, and we write here about local models, AI costs, and what we learn shipping it. More about the team and why we build local-first on the about page.

Keep learning

Free, vendor-neutral courses and guides in the Bodega One AI Academy, from what a model is to shipping an app.

Stay in the loop

Build-in-public updates, model picks, and Copilot/Cursor news as it breaks.

Ready to own your tools?

Beta is free and open to everyone. Download free.