Skip to content

Build Independently confirmed3 publishers 3 min read Published Updated

ZCode encrypted a 313MB snapshot of a commercial repo to a key only its server holds

The researcher ferstar found the archive sitting in ~/.zcode, 86.6 percent of it .git history, after 564 failed attempts to push it straight to an Aliyun OSS bucket, and Zhipu confirmed the upload behavior the day the writeup went out.

The Engineer · Build desk

Send a correction

Illustration accompanying ZCode encrypted a 313MB snapshot of a commercial repo to a key only its server holds
Generated illustration

What happened

  • The researcher known as ferstar published on Sep 18, 2026 that Zhipu's ZCode desktop app packaged whole repositories and uploaded them to cloud storage, and Zhipu confirmed the upload behavior that day.
  • A plain-text state file in the app's checkpoints folder recorded 345,549,173 workspace bytes packed into a 313,070,842-byte encrypted archive labeled baseline, with a failureCount of 564.
  • The locally stored manifest covered 42,411 files, of which .git was 86.6 percent, dominated by 196.1MB of LFS objects and 102.2MB of commit history.
  • Reading the unpacked app.asar bundle, ferstar reconstructed an upload path in which the client posts the encrypted archive straight to Aliyun OSS without passing through Zhipu's own application servers.

Why it matters

  • What it limits constraint Detection has to happen at the network boundary rather than in the tool: the app's logs do not record upload URLs, and the payload leaves to an object-store host that a vendor-domain allowlist permits.
  • Who's exposed exposure Whoever receives one of these snapshots holds commit history, so credentials that a team rotated out years ago are live again in someone else's storage.
  • Who has to decide decision A team that finds one of these archives cannot decrypt it to establish what left, so the evidence has to be gathered before the upload, from directory growth and per-destination byte counts.
  • What it costs cost This class of audit is paid for per app and per version by whoever runs egress capture, because each Electron client has to be unpacked before anyone knows where it posts.

Look at the archive that failed. It is the informative one. The local state file records 345,549,173 bytes of workspace going in and 313,070,842 bytes of ciphertext coming out [4]. A tar.gz and an AES pass removed about nine percent [21]. The manifest explains it. LFS objects, commit objects and reflogs account for 298.9MB of the snapshot [22], and those bytes are already compressed. The 538-file public repository that did reach the server was mostly source text, and it went out at roughly 15KB [7].

So the size limit that the credential endpoint hands back [9] kept a commercial project on local disk. By the time ferstar looked the client had tried 564 times [4], and the archive was still on the machine [6].

ferstar calls the scheme standard envelope encryption, and says that is exactly the problem [18]. Contents go under a random AES-256-CTR key, and that key is wrapped with RSA-OAEP-SHA256 using a public key the server supplies at upload time [10]. The private half stays in the cloud. ferstar tried every private key on the machine against the envelope and none of them opened it [14]. As a backup format it has one unusual property: the owner of the data cannot restore from it. According to the writeup, a tool built for crash recovery or cross-device sync would keep the decryption key on the machine, the way Git and Time Machine data does [23].

The transfer path makes this hard to see from the network. The client asks zcode.z.ai for credentials at POST /api/v1/snapshot/upload-credential and gets back OSS form credentials, a size limit and the RSA public key [9]. The bytes then go to Aliyun OSS as an HTTP POST form [11]. OSS calls the backend so the upload gets recorded [12]. A live check on the running process found persistent connections to zcode.z.ai and to two Aliyun OSS nodes [13]. The app's logs did not include upload URLs, and ferstar read the endpoints out of the unpacked app.asar bundle [8].

An allowlist written around a vendor's API domain passes this upload, because the volume goes to an object-store host. The signal available to a defender is bytes per destination, and a data directory that has grown past 700MB [3].

A recipient gets history. Reflogs are only 0.6MB of the manifest, and they record activity on local branches that were never pushed [16]. ferstar lists secrets deleted or overwritten long ago but still present in history, unpushed branch names that expose unreleased product direction, and .git/config, which often carries internal GitLab hostnames and repository paths [19]. The client also builds a repo_snapshot_extra_manifest that hashes the global ZCode settings file and bundles it across workspaces into every snapshot [20]. The project totaled 10GB, and ferstar describes almost everything in the snapshot, once dependencies are excluded, as core intellectual property [5].

This rests on one researcher, one machine and one commercial project, plus Zhipu's confirmation of the upload behavior on the day of publication [2]. ferstar mapped each settings toggle against the code; the text available here stops before that mapping, so what switching snapshots off actually prevents is still unknown. The method transfers whether or not other clients behave this way: unpack the Electron bundle, read the upload code, then watch the data directory and the destination hosts.

What to watch

  • Whether Zhipu's confirmation turns into a change anyone can verify, in particular whether the RSA private key moves to the client or snapshots become opt-in.
  • Whether other Electron-based AI coding clients use the same credential-then-object-store upload path, which unpacking their app.asar would show.
  • Whether the 15KB snapshot the server accepted has a stated retention or deletion path on the OSS side.

Clarity's read

What the record supports and how the coverage leans. The claims behind it follow.

Reality

Evidence72
Adoption
Not scored
Hype gap+25
Incentives60
Confidence68

Perspective Coverage

3 publishers
Builder
Builder 43%
Operator
Operator 37%
Investor
Investor 20%
Why these scores

Claim ledger

Ranked by verification strength, evidence, and original report placement.

  1. [1]

    A security researcher known as ferstar documented that ZCode, an AI coding desktop app from Zhipu, packaged an entire repository including every commit and uploaded it to cloud storage without asking.

    Reported Supported Source: ferstar writeup at blog.ferstar.org, as reported by dev.toView cited source
  2. [2]

    The full writeup was published on Sep 18, 2026, and Zhipu confirmed the upload behavior the same day.

    Reported SupportedView cited source
  3. [3]

    The ~/.zcode data directory had grown past 700MB, and inside it was a 313MB encrypted .enc archive in a checkpoints folder next to a small state file.

    Reported SupportedView cited source
  4. [4]

    The state file recorded a workspacePath for a commercial project, encryptedSizeBytes of 313070842, workspaceSizeBytes of 345549173, kind "baseline" (a full snapshot), and failureCount 564.

    Reported SupportedView cited source
  5. [5]

    The project totaled 10GB, but after excluding dependencies, almost everything in that snapshot was core intellectual property.

    Reported SupportedView cited source
  6. [6]

    The 313MB archive never made it out; the upload kept failing because of its size.

    Reported SupportedView cited source
  7. [7]

    A smaller workspace, 538 files from a public repository, was compressed down to roughly 15KB and shows a status of accepted by the server, so at least one snapshot reached the cloud.

    Reported SupportedView cited source
  8. [8]

    The app logs contained no upload URLs, so the researcher unpacked the Electron client's app.asar bundle and read the code to reconstruct the flow.

    Reported SupportedView cited source
  9. [9]

    The client calls POST /api/v1/snapshot/upload-credential on zcode.z.ai, and the server responds with OSS form credentials, a size limit, and an RSA public key.

    Reported SupportedView cited source
  10. [10]

    The client packs the workspace into a tar.gz archive, encrypts it with AES-256-CTR, and wraps the symmetric key with RSA-OAEP-SHA256 using the server-supplied public key.

    Reported SupportedView cited source
  11. [11]

    The client uploads the encrypted archive directly to Aliyun OSS via an HTTP POST form, and the traffic never passes through Zhipu's own application servers.

    Reported SupportedView cited source
  12. [12]

    OSS fires a callback so the backend can record the upload.

    Reported SupportedView cited source
  13. [13]

    Live network checks found the running process holding persistent connections to zcode.z.ai plus two Aliyun OSS nodes.

    Reported SupportedView cited source
  14. [14]

    The private key exists only in the cloud; the researcher tried every local private key on the machine against the envelope and all attempts failed, so neither the user nor the ZCode client can open the 313MB ciphertext and only Zhipu's backend holds the key.

    Reported SupportedView cited source
  15. [15]

    The snapshot manifest is stored locally in plain text, and the researcher tallied a manifest covering 42,411 files.

    Reported SupportedView cited source
  16. [16]

    The manifest breakdown was .git/lfs at 196.1MB (56.8%), .git/objects at 102.2MB (29.6%), .git/logs at 0.6MB (0.2%) covering reflogs including unpushed local branch activity, and remaining source and docs at about 46.2MB (13.4%).

    Reported SupportedView cited source
  17. [17]

    The .git directory alone made up 86.6% of the snapshot.

    Reported SupportedView cited source
  18. [18]

    The writeup describes the scheme as standard envelope encryption and states that this is exactly the problem.

    Reported Supported Source: ferstar writeupView cited source
  19. [19]

    A recipient of the archive would get old secrets that were deleted or overwritten long ago but still exist in history, names of local branches never pushed that expose unreleased product direction, and .git/config, which often contains internal GitLab hostnames and repository paths.

    Reported SupportedView cited source
  20. [20]

    The code contained a repo_snapshot_extra_manifest that hashes the user's global ZCode settings file and bundles it across workspaces into every snapshot.

    Reported SupportedView cited source
  21. [21]

    The encrypted archive is about 90.6 percent of the workspace size, so the tar.gz plus AES step removed roughly nine percent of the bytes.

    Derived Supported
  22. [22]

    The three .git entries in the manifest sum to 298.9MB of the snapshot.

    Derived Supported
  23. [23]

    The writeup argues that if the goal were crash recovery or cross-device sync for the user's benefit, the decryption key would live on the user's machine, the way Git or Time Machine data does.

    Reported Contested Source: ferstar writeupView cited source

Sources

3 independent publishers whose own reporting we read for this story.

  1. dev.to

    1 article · September 20, 2026

    An AI Coding App Was Silently Uploading Your Entire Git History: Inside the ZCode Incident
  2. runtimewire.com

    1 article · September 20, 2026

    Z.ai open-sources ZCode after its coding agent uploaded Git histories
  3. tomshardware.com

    1 article · September 21, 2026

    Devs say Chinese AI company silently uploaded hundreds of megabytes of local workspace data, company apologizes — Z.AI, the firm behind the GLM models, didn’t ask for user consent and made 564 attempts to exfiltrate 313MB archive

Topics and entities

Topics

Entities