ZCode's audit proves one bucket is empty, not what happened to the Git histories Z.ai's coding app uploaded
Z.ai's ZCode desktop app packed whole workspaces, Git history included, and sent them to Alibaba Cloud under a key only Z.ai held. The audit Z.ai cites shows one bucket is empty now, not who read the code before it was deleted.
By Parminder Kumar Sharma · · 19 min read

An empty bucket, three days after the data was said never to be stored
On 18 September, Z.ai told ZCode users that repository data uploaded by its coding app was "destroyed immediately" after use and "not stored" (Z.ai statement as reported by IT Home). On 21 September it published the results of two third-party checks. The China Academy of Information and Communications Technology (CAICT) found the zcode-prod Alibaba Cloud OSS bucket "in a zero-data state". NSFOCUS found that "all data objects" in that bucket, "as well as the bucket itself, have been deleted" (Z.ai on X, 21 September).
Put those two statements side by side. If uploads really were destroyed on arrival, nobody would need to delete a bucket full of objects three days later. Deleting objects means there were objects to delete. That is our inference, not something Z.ai has said, but it is the plain reading of its own two statements.
Here is what the audit findings do not establish. They do not say what the bucket held or for how long. They do not say whether anyone decrypted or read what was in it, or whether copies, derived Wiki pages, indexes or logs exist somewhere else. They do not say whether the private key that could decrypt the archives still exists. The full reports have not been published: Z.ai says they "will be released soon". What exists today is a two-paragraph summary written by the company that commissioned the work.
Z.ai, also known as Zhipu, did fix the client quickly. Version 3.14.0, released on 19 September, lists "Fixed an issue with abnormal uploads in the repository wiki" (ZCode changelog). The company also open-sourced the app under Apache 2.0 (zai-org/ZCode). Both are real steps. But an audit that shows a bucket is empty today does not answer the question a UK organisation should be asking, which is what happened to its code between upload and deletion.
What ZCode actually did
The behaviour came to light through a paying subscriber. The developer who writes as ferstar went looking for why ~/.zcode was using more than 700MB and found a 313MB encrypted file waiting to be uploaded. He published his analysis on 18 September (ferstar). That same day, database engineer Ruohang Feng repeated the investigation on his own Mac and confirmed it independently (Vonng). Both examined version 3.12.3, which Z.ai released on 17 September. Neither says how far back the behaviour goes. Feng says he did not check earlier versions.
Put together, their accounts describe this pipeline:
- Trigger. A snapshot was captured before every prompt (
captureBeforePrompt) and when a task finished (taggedrepo-wiki-update). The upload component started unconditionally at launch. All it needed was a valid login token. - Permission from the server. On each prompt the client asked
zcode.z.aifor upload credentials. The server sent back a snapshot ID, a size limit, signed OSS form fields and an RSA public key. Feng puts it bluntly: whether anything was collected was decided by the server, not by any setting on the laptop. - Pack and encrypt. The client made a tar.gz of the workspace, leaving out a few directories such as
node_modules. It encrypted the archive with AES-256-CTR under a one-off key, then wrapped that key with the server's RSA public key (RSA-OAEP-SHA256). - Upload. The client sent the ciphertext straight to Alibaba Cloud OSS as a form upload, bypassing Z.ai's own application servers. OSS then called back to Z.ai's backend to register the snapshot.
What went into the archive is what matters. The file list is written to disk in plaintext even though the archive itself is encrypted. ferstar's snapshot held 42,411 files, and 86.6% of its bytes were under .git: 56.8% LFS cache, 29.6% object store and 0.2% reflogs. Only 13.4% was the current source and documentation. In one of Feng's snapshots .git was 93.9% of the bytes, and in another 98.5%.
Feng also found why history got through untouched. The file filter is a chain of rules applied in order, and the rule that lets .git in comes before every exclusion. So neither the filename-based key filter nor the 1MB size cap ever touched anything under .git. A credential committed once and deleted the next day still sits in .git/objects, and it would have gone out with the rest. Feng's repository also had .git/filter-repo and .git/lost-found directories. In other words, the snapshot included the evidence of an earlier clean-up and the dangling objects that clean-up left behind.
The settings did not do what their names suggested. Two toggles looked relevant. According to ferstar's reading of the code, "Optimise Experience" only controlled whether data could be used for model training, and "Repo Snapshot Indexing" only controlled whether the server indexed snapshots it had already received. Neither stopped the packing or the upload. Feng's logs from 13 to 18 September show the snapshot-indexing field 1,339 times, and every time it was false. All four of his snapshots were created in that same window.
What left the machine, on the record
Much of the coverage says ZCode "uploaded" a 313MB commercial project. It did not. The two researchers were careful to keep "packed" and "uploaded" separate, and so should anyone assessing their own exposure.
What the two public investigations and one corporate complaint establish about data that actually left a machine. Sources: ferstar (18 to 21 September), Ruohang Feng (18 September), Reuters via Devdiscourse (21 September).
| Evidence | What it establishes | What it does not establish |
|---|---|---|
| ferstar: 313MB archive of a commercial project, 564 failed attempts | It was packed for upload. His router's connection records show it never left his network | That any commercial code of his reached Z.ai |
| ferstar: 538-file public repository, about 15KB encrypted | The server accepted it. At least one snapshot left his machine | What the server did with it after receipt |
| Feng: 405MB workspace packed to 7.4MB, no .git | Accepted by the server, based on a field written only after an upload is acknowledged | Whether it is still stored, or who accessed it |
| Feng: two Git-heavy snapshots with credentials granted | The server issued upload credentials, so workspace identifiers reached it | Whether the payloads finished uploading |
| Feng: 1.07GB snapshot, 102 failures | It exceeded a size limit that the server sets | That the repository was packed 102 separate times |
| Chengming Technology: six workspaces with passwords and staff data | It was claimed in a formal letter | Anything: Reuters reports the firm retracted it on 21 September, citing "wrong evidence" |
| Z.ai: "some users were affected" | Uploads happened. Z.ai no longer disputes that | How many users, workspaces or bytes. Not stated |
The last two rows show why care is needed. Taiyuan Chengming Technology's letter, which alleged that database passwords and employee personal data had been uploaded, was widely reported in China on 20 September. Reuters then reported that the company withdrew its statement on 21 September, saying it had "wrong evidence" (Reuters via Devdiscourse). We have not been able to reach the retraction itself. Either way, the letter should not be treated as evidence of what was taken. The researchers' evidence is narrower but checkable: some workspaces were accepted by the server, and the pipeline was built to include Git history. For a security team that is enough to act on.
The company's account, and where it does not match the code
Z.ai's first statement went to its user community at 17:44 Beijing time on 18 September, about seven hours after ferstar committed his post at 10:35. It blamed "codebase indexing", a feature for local indexes, session checkpoint restore and Repo Wiki. It said Wiki generation "may" trigger an upload, and that the feature was on by default early on. IT Home carried the statement that evening (IT Home). The second statement, on X on 21 September, added the audit results and a confirmation that "no such data is retained and that it has never been used for model training" (Z.ai on X).
Z.ai's claims set against what the researchers found and what the public record shows. Sources: IT Home (18 September), Z.ai on X (21 September), ferstar, Ruohang Feng, the ZCode changelog and privacy policy (fetched 21 September).
| Z.ai said | What the record shows | Resolved? |
|---|---|---|
| Wiki generation "may" trigger an upload | Capture ran before every prompt as well as on task completion | No. The statement understates the trigger |
| Data destroyed immediately, not stored (18 Sep) | A bucket with objects that needed deleting (21 Sep) | No. The two statements conflict |
| Indexing supports session checkpoint restore | The open-source checkpoint code runs on local Git and stores metadata under ~/.zcode | Checkpoints never needed an upload, per ferstar |
| The issue "has been fixed" (18 Sep) | The client fix shipped in 3.14.0 on 19 Sep | Fixed a day later, at least on the client side |
| Never used for model training | Neither researcher found evidence either way | Not verifiable from outside |
| Privacy practices unchanged | The policy still covers code "submitted to us through conversation", effective 15 June | The policy has not been updated |
One fact in Feng's analysis gets less attention than it should. Because the client asked the server for permission before every capture, Z.ai could stop collection from its side at any time, without shipping a client update. Feng's machine last received upload credentials at 11:55 on 18 September, about 80 minutes after ferstar committed his post, and nothing new appeared after that. Feng says he cannot tell whether the server was deliberately switched off. That design cuts both ways. It means Z.ai could turn collection off at once. It also means the only thing that ever decided whether your code left the building was a decision on Z.ai's servers.
A friendly name is not a control
At almost every step of this story, a reassuring label sat on top of a mechanism that did something else.
- "Codebase indexing" sounds local and harmless. The mechanism packed whole working trees plus their entire history and sent them to object storage.
- "Repo Snapshot Indexing: off" sounds like the feature is disabled. In practice it controlled server-side indexing of uploads that still happened.
- "Optimise Experience: off" sounds like a telemetry opt-out. It governed training use, not transmission.
- "Encrypted" sounds protective. Here the encryption kept the data from the user, not from the vendor. The machine that made the archive could not open it, and the only key was on Z.ai's server. Feng's point is that the first test of a backup is whether its owner can restore it, and this one fails that test.
- "Zero-data state" sounds final. It describes one bucket at one moment in time.
- "Open-sourced" sounds like full transparency. What was released is the current, cleaned-up code, not the history that shows how the uploader worked.
- "Security issue" and "vulnerability", which is how Reuters and Z.ai itself framed it, suggest a bug somebody found and exploited. By Z.ai's own account this was a feature that was switched on by default. As TNW put it, a bug gets patched, but a default is something somebody chose.
What an audit of deletion can and cannot prove
Proving that data was deleted means proving a negative, and every deletion audit runs into that limit. An assessor can inspect what is in front of them: the state of a named store at the time of inspection, the configuration around it, any logs the client hands over, and the code of a client build. An assessor cannot see a copy they were never shown. Unless the report says otherwise, they cannot rebuild the history of a system from a single visit after the incident. ferstar made the same point: checking a bucket after 20 September cannot show what happened to data uploaded before 18 September.
What the published audit summary answers, set against the questions a data owner needs answered. Source: Z.ai's 21 September statement on X, which is the only published account of the findings.
| Question | Answered by the summary? | What would answer it |
|---|---|---|
| Is the zcode-prod bucket empty now? | Yes (CAICT), and deleted (NSFOCUS) | Already answered, subject to the full report |
| Does client 3.14.0 still contain an upload path? | Yes: none was found | Our code search of the public repository agrees |
| What did the bucket hold, from when, for how long? | No | Object inventory and OSS access logs across the whole period |
| Was any archive decrypted, and by what? | No | Key-use logs and backend processing records |
| Were copies made to other buckets, regions or backups? | No | Replication and backup configuration, account-wide |
| Do Wiki pages, indexes or logs derived from the code survive? | No | Inventory of derived data and caches |
| Has the private key been destroyed? | No | Evidence the key was destroyed, which would make any stray ciphertext unreadable |
| Were other stores, other versions or other products involved? | No | A published scope statement |
The last point matters most. Z.ai's scheme was envelope encryption, which means the archives are only as sensitive as the private key. If that key is verifiably destroyed, any ciphertext left in a forgotten replica or backup becomes unreadable noise. This technique is known as crypto-shredding, and it is one of the few deletion claims an outsider can reason about with confidence. Neither statement mentions the key.
The same applies to the plaintext. By Z.ai's own account, archives were decrypted and processed to generate Repo Wiki pages. Wherever that happened, plaintext code existed at least briefly, together with whatever that pipeline produced and logged. The audit summary is about a storage bucket. It says nothing about the processing pipeline.
Who did the audit, and what that means. CAICT is a research institute affiliated with China's Ministry of Industry and Information Technology. NSFOCUS is a listed Chinese security company. Z.ai commissioned both after the fact. Commissioned reviews are normal, and neither firm is shown to have done anything wrong. But when a client commissions a review, the client usually sets the scope, and neither the scope nor the method has been published. Until the reports appear, the findings are Z.ai's summary of what two firms told it.
What open-sourcing proves. We checked the repository ourselves. It was created on 20 September at 12:01 UTC and has two commits: an empty initial commit, then a single "feat: open source" commit adding 1,033,262 lines. Issues are disabled and there is no SECURITY.md. A code search for repoSnapshot and rsa-oaep returns nothing, which matches the claim that the uploader has been removed. One harmless leftover remains: a list of network-telemetry path labels in networkTelemetryAggregator.ts still includes "snapshot" and "upload-credential". That is a label for counting requests, not a pipeline. The repository proves what the current code does. It cannot show what earlier builds did, and ferstar notes that the desktop client can receive hot updates. Nothing on the record shows that the binary on a developer's machine is reproducibly built from this source.
The repository's own NOTICE.md is worth reading for another reason. It says the shared agent execution adapter "does not provide a default operating system sandbox", and that the standalone CLI running a non-interactive --prompt task defaults to yolo mode unless --mode is set (NOTICE.md, our translation). That is candid, and it tells you how much the agent itself can reach on a developer's machine.
Separating method from accusation
The evidence supports a clear finding about method. A default-on pipeline, gated by the server and not by the user, collected whole repositories including history, sent them to vendor-controlled storage under a vendor-only key, went beyond the scope described in the privacy policy, and ignored the settings a user would reasonably have thought controlled it.
It does not support claims about purpose. Feng says in terms that an upload does not establish training use, and that he draws no conclusions about motive. Nothing on the public record shows Z.ai used the data to train models, and Z.ai denies it. Claims of espionage or deliberate theft go further than any evidence we have seen. The one corporate complaint that alleged serious data loss has, according to Reuters, been retracted.
On interests: Zhipu is listed in Hong Kong. Jiemian News, via 36Kr, reported its shares down more than 4% on 21 September. The audit firms were paid by the company they assessed. Both researchers were paying subscribers, and Feng discloses that he received referral credits. None of this changes the evidence. It is context for how to weigh it.
Nor is this unique to one vendor or one country. Feng and TNW both cite xAI's Grok Build, which in July was reported to upload whole repositories while a privacy toggle had no effect. The category problem is a coding agent: a background process with read access to the whole disk and network access, used under terms written for a chat window.
The UK angle: this is a data question before it is a China question
For a UK organisation, the question is not whether Z.ai is trustworthy in the abstract. The question is whether your code, and any personal data inside it, went to a third party without authorisation. Several things follow from that.
Who you would be dealing with. ZCode's English privacy policy names the controller as Jingsheng Hengxing Technology Pte. Ltd in Singapore, and says data is "generally processed in Singapore" (ZCode privacy policy). The Chengming letter noted the same mismatch between a Singapore network endpoint and a Beijing contracting entity. No source states which region the OSS bucket was in.
Why Git history is personal data. Every Git commit records an author name and email address. Repositories often also contain test fixtures, seed data, logs or customer exports that somebody committed and later deleted. The ICO defines a personal data breach as "a breach of security leading to the accidental or unlawful destruction, loss, alteration, unauthorised disclosure of, or access to, personal data". Notifiable breaches must be reported "not later than 72 hours after becoming aware of it" (ICO).
We are not saying every affected organisation has a notifiable breach. For many, the risk will come down to names and work emails, and the right decision may be to record the assessment and not notify. But the clock starts when you become aware, and the only way to judge the risk is to know what was packed and what was accepted. That evidence is on the developer's laptop, not in Z.ai's audit.
Contracts and intellectual property. Client code held under a non-disclosure agreement, and third-party code under licence, may also carry duties to tell the owner. That is a question for legal, but it needs the same inventory.
What to do, in the order worth doing it
Take this with you
Actions for organisations whose developers may have used ZCode, or any third-party coding agent
- Find it. Use EDR, MDM or software inventory to list every endpoint with the ZCode desktop app or a ~/.zcode directory, on macOS, Linux and Windows. Include personal devices used for work under your BYOD policy.
- Preserve before you remove. Copy the ~/.zcode/v2/checkpoints directory, especially the plaintext manifests and state files, into your evidence store before anyone deletes the app or the archives.
- Separate packed from accepted. Use the state files to mark each workspace as accepted by the server, credentials granted, or failed and pending. Only accepted snapshots are confirmed to have left the machine.
- Scan full history for accepted repositories. Run a secret scanner over every ref and unreachable object, not only the working tree. Rotate every credential that has ever appeared in history, including ones already deleted.
- Make and record the data protection call. Check commit authors and any personal data ever committed. Decide within 72 hours of awareness whether it is notifiable, and write down the reasoning either way.
- Write to the controller. Ask for a list of snapshots tied to your accounts, confirmation of deletion including derived Wiki data and logs, and a statement on whether the decryption key has been destroyed. Use UK GDPR access rights where they apply.
- Update or remove. If ZCode stays, make sure clients are on 3.14.0 or later and watch for hot updates. If policy says remove, do it after evidence is preserved.
- Control egress for all coding agents. Allowlist the endpoints each agent needs and alert on direct object-storage uploads from developer machines that do not match an approved service.
- Change procurement. Before approving any coding agent, require a documented data-flow statement, a local control that provably stops uploads, keys the user holds if the vendor keeps any copies, verifiable deletion, and a published vulnerability disclosure route.
The first three steps can be scripted. The commands below only read local files, and they work on macOS and Linux. The field names come from the researchers' published state files. File names may differ between versions, so search by content, not by name.
# List ZCode checkpoint records for the current user (read-only)
ls -la ~/.zcode/v2/checkpoints 2>/dev/null
# State files the researchers describe: accepted uploads record lastAcceptedManifestHash,
# pending ones record failureCount
grep -rl "lastAcceptedManifestHash" ~/.zcode/v2/checkpoints 2>/dev/null
grep -rl "failureCount" ~/.zcode/v2/checkpoints 2>/dev/null
# Preserve the whole directory before any removal
tar -czf "zcode-evidence-$(hostname)-$(date +%Y%m%d).tar.gz" -C ~ .zcode/v2/checkpoints
The question that exposes the gap
Z.ai has done more than many vendors would. It admitted the uploads, fixed the client within a day, published the code and brought in outside firms. The weakness is in what those firms were asked to show. An empty bucket is evidence about the present. What data owners need is evidence about the past: what was received, what was decrypted, what was produced from it, and whether the one key that could open any surviving copy still exists.
So the question for Z.ai, and for every coding-agent vendor your developers use, is this: if your own audit can only show that a bucket is empty today, what evidence can you give me that nobody read my code before it was deleted, and have you destroyed the key?
Sources
- PrimaryOriginal researcher disclosure, 18 September 2026 with 19 and 21 September updates: pipeline, encryption, manifest breakdown, toggles, what was and was not uploaded, open-source repository reviewferstaraccessed 2026-09-21
- PrimaryIndependent reproduction on ZCode 3.12.3, 18 September 2026: accepted and pending snapshots, .git filter ordering, server-gated collection, toggle log countsRuohang Feng (Vonng)accessed 2026-09-21
- PrimaryZ.ai's 21 September 2026 statement: open-sourcing, no-retention and no-training claims, CAICT and NSFOCUS findings, full reports to followZ.ai (ZCode on X)accessed 2026-09-21
- PrimaryOpen-source ZCode repository: creation time, two commits, licence, issues disabled, code search for the removed upload pathZ.ai (GitHub)accessed 2026-09-21
- PrimaryNOTICE.md: statement that the agent execution adapter has no default OS sandbox and the CLI prompt-mode defaultZ.ai (GitHub)accessed 2026-09-21
- PrimaryZCode changelog: 3.12.3 released 17 September, 3.14.0 released 19 September with the repository wiki upload fixZ.aiaccessed 2026-09-21
- PrimaryZCode privacy policy, effective 15 June 2026: collection scope, Singapore controller, processing locationZ.aiaccessed 2026-09-21
- PrimaryUK GDPR personal data breach definition and 72-hour reporting ruleInformation Commissioner's Officeaccessed 2026-09-21
- Reported byReport of Z.ai's 18 September 2026 community statement: codebase indexing, Repo Wiki upload, destroyed immediately, default-onIT Homeaccessed 2026-09-21
- Reported byChinese text of Z.ai's 21 September 2026 statement and audit findingsIT Homeaccessed 2026-09-21
- Reported byReuters report, 21 September 2026: Z.ai statements, Chengming Technology retraction, auditor descriptionsReuters via Devdiscourseaccessed 2026-09-21
- Reported byReport of Chengming Technology's letter, 20 September 2026, including the Singapore entity pointTechWeb via Tencent Newsaccessed 2026-09-21
- Reported byTimeline of Z.ai's three responses and Zhipu share price move on 21 SeptemberJiemian News via 36Kraccessed 2026-09-21
- Reported byAnalysis of the vendor-only key and the bug-versus-default distinctionTNWaccessed 2026-09-21


