groundy
Agents & Frameworks

Does ZCode Upload Your Git History? Auditing the GLM Coding Agent

Forensic analysis shows ZCode silently uploads encrypted Git history to Aliyun OSS without user consent or a working opt-out, requiring filesystem-level containment.

Published 5 references
A weathered green ceramic archive case packed with layered files straddles an ivory partition. A black clasp locks the case, while a copper-colored ceramic key lies beyond the partition.
On this page10 sections

Yes, according to the strongest evidence available: a developer’s forensic investigation of ZCode, Z.ai’s official desktop client for the GLM coding models, found that a logged-in client silently packages the entire workspace, including complete Git history, encrypts it, and uploads it to Aliyun OSS, with no UI setting that stops it. The evidence is one researcher’s reverse engineering plus one secondary writeup, with no official vendor statement, advisory, or patch in the record as of 2026-09-19; the closest to a vendor response is an informal reply from a ZCode-team-affiliated account that tokenstead reads as confirmation of the mechanism. The practical consequence: keep ZCode off proprietary repositories until Z.ai documents the pipeline and ships a tested opt-out, and in the meantime control it at the filesystem and network layers, not in its settings panel.

What a logged-in ZCode packages and where it sends it

The primary account is ferstar’s forensic investigation, published 2026-09-17 (tokenstead dates it September 18) and widely circulated the following day. It began mundanely: the ~/.zcode directory had grown past 700MB, and digging into it turned up a 313MB encrypted archive stuck in a pending-upload state. Reconstructing the client’s packaged code, ferstar reports the following pipeline.

While logged in, ZCode packages the entire workspace: the complete .git history, LFS asset caches, reflogs, and global application configs. The client first calls https://zcode.z.ai (the VITE_ZCODE_ENDPOINT_ORIGIN constant in the code), and the server returns OSS form signatures, a dynamic object key, size limits, and an RSA public key for that encryption round. The client then POSTs the encrypted tar.gz.enc archive directly to Aliyun OSS, Alibaba’s object storage, which calls back to Zhipu’s backend. When ferstar inspected the running process, it held persistent HTTPS connections to zcode.z.ai endpoints plus two Aliyun OSS storage nodes.

The scope deserves emphasis. One snapshot’s local plaintext manifest inventoried 42,411 files, and ferstar’s breakdown attributes 86.6% of the packed content to .git. A separate repo_snapshot_extra_manifest hashes global ZCode config files such as settings.behavior.json and bundles them across workspaces with every snapshot. So this is not a diff of files the model read, or the current working tree. It is the repository’s history, including anything ever committed and later deleted from the working tree but still reachable through Git objects and reflogs.

Capture is also frequent and automatic. Per ferstar’s reading of the host assembly code, the capture/upload sidecar is instantiated unconditionally at startup, with no gating checks on user preferences; the only requirement is that the token provider can return a valid JWT. Triggers fire before every prompt (captureBeforePrompt) and at task completion under a repo-wiki-update tag, and one active session generated up to 62 capture events. Deleting the pending archive is futile: ferstar reports ZCode re-captured a fresh 313MB archive within half an hour, with the retry counter climbing past 564.

Why you cannot decrypt your own snapshot

The encryption is, in ferstar’s words, textbook envelope encryption, and that is precisely the problem. The content is encrypted with AES-256-CTR under an ephemeral symmetric key, which is then wrapped with RSA-OAEP-SHA256 using a public key the server hands down during credential negotiation. The corresponding private key never touches the user’s machine. Ferstar tried unwrapping the envelope key with every local private key on the system and failed, as expected.

This inverts the usual reassurance. “Encrypted in transit and at rest” normally protects the user from third parties. Here the encryption protects the snapshot from the user. You can watch the archive leave your machine, and you cannot inspect what is in it beyond what the plaintext manifest happens to list. Whoever holds the private key, meaning Zhipu’s backend, decides what the snapshot is for.

That makes the disclosure gap the sharper issue. ZCode’s privacy policy does disclose collecting “text, files, and code submitted during conversations,” but ferstar’s review of the policy, FAQs, and changelogs found no mention of packaging and uploading entire workspaces or Git histories. The closest line is a template statement that the experience-optimization program is off by default, which, as the next section shows, does not govern this pipeline anyway. Meanwhile, Z.ai’s own GLM Coding Plan page, across its $12.6, $56, and $117.6 per month tiers, lists “Default data privacy” as an included plan feature. That marketing claim has not been verified against the observed behavior, and the tension between the two is exactly what a vendor statement needs to resolve.

The settings that do not stop it

ZCode’s UI offers two relevant-looking toggles, and ferstar cross-referenced both against the code. Neither gates the pipeline.

  • “Optimize Experience” (optimizeAgentExperienceEnabled) only controls authorization to use your data for model training.
  • “Repo Snapshot Indexing” (repoSnapshotIndexingEnabled) only controls server-side indexing of snapshots after upload.

Local packaging and upload continue regardless of either setting. Ferstar’s summary is blunt: as long as you are logged in, the background pipeline is permanently active and no UI setting can turn it off.

This is the detail that should change how teams evaluate every coding harness, not just this one. A settings toggle is a claim about behavior; the only trustworthy verification is at layers the client cannot rewrite, namely the filesystem and the network. That principle generalizes into the audit procedure below.

An audit checklist that outlives this incident

The method behind ferstar’s finding is reproducible, and tokenstead’s guide distills it into two gating questions that apply to any coding agent before it gets repository access: what does the runtime transmit while you are logged in, and who can decrypt what it stores. Here is the procedure, expanded into a checklist.

1. Capture egress before first real use. Install the client in a sacrificial environment, log in with a throwaway account, and record connections during an ordinary session. Ferstar’s socket inspection found persistent HTTPS connections to zcode.z.ai plus two Aliyun OSS storage nodes, which is what a workspace-sync feature looks like on the wire. Any destination beyond the vendor’s API domain, particularly a cloud object store, demands an explanation.

2. Plant canary files. Place unique, greppable sentinel strings in the working tree, in an old commit reachable only through history, and in a reflog-only state. If a canary that exists only in Git history shows up in an outbound payload or a server-side artifact, the client is shipping history, not context.

3. Watch local staging directories. ZCode’s behavior was first visible as disk growth: 700MB in ~/.zcode, with encrypted archives accumulating in ~/.zcode/v2/checkpoints. Any agent that builds large local archives under its config directory is staging something. Inspect what triggers growth (prompt submission, task completion) and whether deletion sticks.

4. Cross-reference UI toggles against behavior. Flip every privacy-related switch, then re-run the egress capture. If the traffic pattern is unchanged, the toggle does not govern that traffic, whatever its label says. Where the client is packaged (Electron asar archives, in ZCode’s case), code inspection can confirm which flag gates which code path; where it is not, behavioral diffing is the fallback.

5. Check key custody. If the client encrypts before upload, determine who holds the decryption key. Server-delivered public keys with no local private counterpart mean the data is encrypted against you. A locally inspectable, locally decryptable archive is a different and more defensible design.

6. Read the policy against the traffic, not the other way around. ZCode’s policy discloses conversation content but says nothing about workspace snapshots. Absence of disclosure is not proof of absence in either direction; it is a flag that the documented data flows and the observed data flows need reconciliation before adoption.

This extends the standard advice for command-line tooling. GitHub’s CLI guidance already flags the lack of default audit trails as a CLI risk category and recommends testing commands in a safe environment before production. An agentic coding client is a CLI with standing credentials and a network connection, so the safe-environment test has to include a packet capture, not just a dry run. And as Groundy’s earlier threat model of repo-level agent config argued, the trust boundary for coding agents has been moving from the model to the filesystem and configuration the agent touches. This incident moves it one step further, to the client’s network behavior.

The one fix that held, and its cost

Because UI toggles are ineffective and deletion triggers re-capture, the mitigation that held in the reports is filesystem-level. Tokenstead’s recommended fix is to recreate ~/.zcode/v2/checkpoints and make it immutable at the kernel level: sudo chattr +i on Linux, chflags uchg on macOS. The capture pipeline then has nowhere to stage archives.

The trade-off is real. The checkpoints directory also backs ZCode’s checkpoint rollback feature, so the rollback UI stops working. And a filesystem block is version-fragile: a future build could relocate the staging path, which is why the block needs to be paired with egress monitoring or an allow-list that would catch a new destination. Treat this as a containment measure, not a fix. It is a reason the decision below is “not yet” rather than “never.”

Open weights, closed harness

A recurring reaction to the finding, noted by tokenstead, was surprise that an “open” model’s tooling would do this. That reaction rests on a category error. The GLM weights are open; Groundy’s coverage of the GLM-5.2 open-weight release documented genuine MIT licensing and self-hostable weights, and that remains true of the model artifacts. ZCode is something else: a closed, first-party harness from Z.ai, pitched as integration no third-party editor can match. The official ZCode site markets it as optimized for GLM-5.3 with GLM-5.3-Flash built in, offers bot control from WeChat, Feishu, and Telegram, and lists current Linux installers at v3.14.0. None of the code that implements the snapshot pipeline is open for inspection, which is why ferstar had to reconstruct it from packaged artifacts.

The consequence for procurement is that open weights lower the trust bar for the model and say nothing about the client. A team can self-host GLM-5.3-Flash through a third-party harness and inherit none of this behavior; the same model reached through ZCode carries the full reported pipeline. Notably, the GLM Coding Plan supports 20+ agent tools, including Claude Code alongside ZCode, and nothing in the current evidence suggests the reported behavior extends to the weights or to third-party harnesses reachable through the same plan. The trust decision has moved from “do we trust this model” to “do we trust this client’s network behavior,” and that second question is answered by egress auditing, not by license terms.

ZCode vs Grok Build: same pattern, different posture

This is the second time in roughly two months that a coding agent has been caught uploading repository history by default. In July 2026, Grok Build was found uploading full Git commit histories and untouched repo data to Google Cloud; Groundy’s analysis of the Grok CLI uploads covered that case at the time. The comparison is instructive, because the differences matter more than the similarity.

DimensionGrok Build (July 2026)ZCode (September 2026, as reported)
Upload destinationGoogle CloudAliyun OSS
VisibilityLogged openly in the tool’s own unified.jsonNo user-facing log reported; found via disk growth
EncryptionNot encrypted in the reported flowAES-256-CTR/RSA-OAEP envelope; private key server-side only
Opt-outKill switch shipped by xAI after the factNo working UI toggle; filesystem-level block is the reported fix
Exposure beyond workspaceOne home-directory user reportedly had SSH keys and a password-manager database in the upload pathWorkspace-scoped plus global app configs, per the manifest analysis
Vendor responseKill switch shipped after disclosureNo official statement, advisory, or patch as of 2026-09-19; only an informal reply from a ZCode-team-affiliated account

Per tokenstead’s framing, Grok’s uploads looked like an open accident: visible in its own logs, tied to normal context-sync phases across 186 turns in one session, and remediated under pressure. ZCode’s pipeline, as ferstar describes it, is encrypted against the user, unlogged, gated behind no preference, and undisclosed in the policy. Whether that difference reflects intent is interpretation, not measurement; what is measurable is that the ZCode design leaves the user with less visibility and less recourse. The Grok precedent also carries one encouraging signal: disclosure pressure worked there. A vendor-facing demand for documentation and a tested opt-out is a reasonable ask, not a futile one.

One more piece of context from tokenstead’s account: ZCode launched in July 2026, and when a Z.ai executive was asked on X about “any sort of spyware,” the response was that the company would not implement “anything beyond what’s listed” on the ZCode website. Workspace snapshotting is not listed there. The most visible reply to ferstar’s post came from an account affiliated with the ZCode team, “hey I am sorry to let you find it,” which tokenstead reads as confirmation of the mechanism, not a rebuttal; Z.ai’s official X account had not responded as of that writeup’s publication. Ferstar’s post passed 276,000 views and a Chinese-language alert thread urging users to disable ZCode drew another 63,800 within hours, per tokenstead, so the question is now public and an official vendor answer is overdue.

What to gate before ZCode touches proprietary code

The verdict, bounded by the evidence: keep ZCode off proprietary repositories until Z.ai publicly documents the snapshot pipeline and ships a tested opt-out. For teams that must evaluate it sooner, the interim posture that held in the reports is containment: run it only on repos whose full history you could tolerate leaving your control, apply the immutable-checkpoints block with the rollback-feature trade-off understood, and put egress monitoring or an allow-list on the host so a changed staging path or new destination is visible. Before any coding harness gets repo access, run the audit above: capture egress while logged in, plant canaries in history, diff toggle states against traffic, and establish who can decrypt what it stores.

Now the limitation, and it is substantial. Every substantive finding here traces to one researcher’s reverse engineering of one ZCode build on one machine. Tokenstead’s guide reports and contextualizes ferstar’s work rather than independently reproducing it, and says it will be updated if Z.ai responds with a fix, a disclosure change, or a statement. No vendor advisory, patch, or third-party verification exists in the available evidence as of 2026-09-19. Vendor installers now list v3.14.0, and whether that build behaves identically is unverified; exact payload scope and any undocumented opt-out flag are likewise open. The two writeups even disagree on the publication date, and ferstar’s post is AI-translated, so its wording should be quoted with care. Vendor documentation may yet contradict the findings, and a missing policy passage is not proof of what the servers do with the data. What the evidence does settle is narrower and still actionable: the reported pipeline is technically specific, internally consistent, and without an official answer, and the burden of proof now sits with the vendor. Until Z.ai discharges it, the audit checklist is the adoption gate, for ZCode and for every closed client that asks for your repositories.

Frequently Asked Questions

Does ZCode’s privacy policy disclose the upload of Git history?

ZCode’s privacy policy does disclose collecting “text, files, and code submitted during conversations,” but ferstar’s review of the policy, FAQs, and changelogs found no mention of packaging and uploading entire workspaces or Git histories. The closest line is a template statement that the experience-optimization program is off by default, which, as the next section shows, does not govern this pipeline anyway.

References

Follow the links in the article for context. The supporting material is collected here for further reading.

  1. ZCode Silent Git History Uploadtokenstead.aiAccessed
  2. ZCode Silent Workspace Snapshot Uploadblog.ferstar.orgAccessed
  3. Z.ai Subscribez.aiAccessed
  4. What is a CLIgithub.comAccessed
  5. ZCode Official Sitezcode.z.aiAccessed

Join the discussion

Share a useful perspective or ask a question about this article.

Discussion guidelinesComments privacy