the short version
- The ZCode capture sidecar is instantiated at host level outside the agent tool loop, so no permission prompt or in-app toggle stops it; the only requirement is a valid JWT.
- The .git directory was 86.6 percent of one 313MB archive built from a 345MB, 42,411-file workspace, so the payload is the repository's full lineage rather than the working tree.
- Envelope encryption with a server-supplied RSA-OAEP public key means the archive on your own disk cannot be decrypted by you or by the ZCode client; ferstar tried every local private key and failed.
- Deleting the pending archive triggered a fresh 313MB repack within half an hour; the mitigation that held was making ~/.zcode/v2/checkpoints immutable with chattr +i or chflags uchg. Z.ai says the issue is now fixed.
On September 18, 2026, a developer going by ferstar published a reverse-engineering walkthrough showing that the ZCode GLM agent uploads git history from any logged-in workspace: the client packs the entire directory, including .git objects, the LFS cache, reflogs and global app configs, encrypts it, and POSTs it to Aliyun OSS, Alibaba Cloud's object storage. The captured example was a 313MB encrypted archive built from a 345MB commercial workspace of 42,411 files, with the .git directory accounting for 86.6 percent of the payload and 564 failed upload attempts logged while the researcher investigated. The archive is encrypted against its own user: the RSA private key that unwraps the payload key lives only in Z.ai's cloud.
ZCode is the AI coding desktop app from Z.ai, the Beijing-headquartered company that publishes the open-weight GLM family, including GLM-5.3-Flash. Several commenters in the thread reaction assumed ZCode was open source because GLM is. It is not: the weights are open, the harness is closed, and it launched in July 2026 pitched as first-party integration no third-party editor can match. A Z.ai executive, asked on X whether ZCode would include "any sort of spyware," answered that the company would not implement "anything beyond what's listed" on the ZCode website, and workspace snapshotting is not listed on the ZCode website.
Why ZCode uploads git history at login
The trigger is authentication, not usage. According to the reconstruction from the client's app.asar, the host assembly instantiates the capture sidecar unconditionally at startup, with no gating on user preferences; the only requirement is that the token provider can produce a valid JWT. Session logs from a single active session showed 62 capture events, fired before every prompt and on task completion. The pipeline is: request credentials from zcode.z.ai, receive OSS form signatures, an object key, a size cap and a per-round RSA public key; pack the workspace to tar.gz; encrypt; POST the archive directly to Aliyun OSS, which callbacks to Z.ai's backend to register the snapshot.
The agent's own tool surface, captured in OrcaPromptVault alongside a 131KB system prompt, has 31 tools, and none of them are snapshot, upload or telemetry tools. The exfiltration path sits outside the tool loop, so tool-level permission gating never sees it. Across 131KB of captured instructions there is no mention of Aliyun, OSS, uploads or privacy. The capture also includes a ReadSessionContext tool that reads other persisted ZCode sessions on demand by session ID, so session content is both locally persisted and cloud-captured.
What is inside the 313MB archive
The packaging manifest is stored locally in plaintext, and it is specific. For the 42,411-file snapshot, it breaks down as:
- .git/lfs/ - 196.1 MB, 56.8 percent
- .git/objects/ - 102.2 MB, 29.6 percent
- .git/logs/ - 0.6 MB, 0.2 percent
- Source code and docs - 46.2 MB, 13.4 percent
The .git directory alone is 86.6 percent of the payload. A git object store is not a snapshot of the working tree; it is the complete lineage of the repository since day one. API keys deleted in a later commit are in there. Unpushed branch names that reveal unreleased product plans are in there, along with internal hostnames and repo paths from .git/config. For a security review, the threat model is years of engineering history leaving the machine, not the files a developer had open.
Envelope encryption that locks out the owner
ZCode uses envelope encryption. The payload is encrypted with a symmetric key using AES-256-CTR, and that key is wrapped with an RSA-OAEP public key delivered by the server during upload-credential negotiation. The corresponding private key exists only in Z.ai's cloud. ferstar attempted to unwrap the archive with every private key on the local system and failed, which means the 313MB ciphertext sitting on the user's own disk cannot be decrypted by the user or by the ZCode client itself.
A key that only the server can use serves exactly one purpose: making sure the server can read your code whenever it wants.
The in-app controls do not change this. Cross-referenced against the code, optimizeAgentExperienceEnabled ("Optimize Experience") only controls whether data is authorized for model training, and repoSnapshotIndexingEnabled ("Repo Snapshot Indexing") only controls whether the server indexes uploaded snapshots. Local packaging and upload continue under both. ZCode's privacy policy discloses collection of "text, files, and code submitted during conversations" - the standard inference-context line - and across policy, FAQ and changelog ferstar found no mention of packaging and uploading entire workspaces and git histories.
How to detect, block and audit the egress
Detection has three signals a team can check without reverse-engineering the client. Network: the running client maintained persistent connections to zcode.z.ai and two Aliyun OSS nodes during the test, so an egress allowlist or proxy log separates the credential negotiation from the form POST to OSS. Filesystem: the packaging manifest is written locally in plaintext, though the post does not give its path, and the capture writes into ~/.zcode/v2/checkpoints, where the pending archive was 313MB for a 345MB workspace in the one case measured. Logs: the 564 failed upload attempts and the 62 capture events in one session were both recorded locally, so a session's capture count is checkable after the fact.
Deleting the pending archive does not work; the client re-packaged a fresh 313MB archive within half an hour, with the retry counter incrementing. The mitigation that held in ferstar's testing was kernel-level: make the checkpoints directory unwritable.
# Linux
rm -rf ~/.zcode/v2/checkpoints
mkdir -p ~/.zcode/v2/checkpoints
sudo chattr +i ~/.zcode/v2/checkpoints
# macOS
rm -rf ~/.zcode/v2/checkpoints
mkdir -p ~/.zcode/v2/checkpoints
chflags uchg ~/.zcode/v2/checkpoints
# restore: chattr -i / chflags nouchgThe trade is that the checkpoint rollback UI stops working - the feature whose template ("Workspace rewind applied. rewindId, checkpointId, strategy, restoredFiles") appears five times in the captured system prompt, and which required uploading the code in the first place. Chat, autocomplete and tool calls work normally.
What practitioners are pushing back on
The sharpest objection on Hacker News was terminological: one commenter argued that the write-up conflates "git history" in the git log sense with the git repository itself, and claimed that another coding tool sends their own git log to Anthropic's servers daily. A reply pointed out that nobody has 300MB of commit messages. The manifest supports that reading: 196.1MB of the payload is the LFS asset cache and 102.2MB is the object store, against 0.6MB of reflogs. Other commenters on Hacker News were blunt about the coverage itself, calling the secondary write-up a bad-faith AI paraphrase of ferstar's original post at blog.ferstar.org, which is reason enough to read the forensics directly rather than the summary.
The broader discussion, on V2EX and Hacker News-adjacent channels, split along one line: agents upload code fragments during tool calls all the time, with consent; this is a full repository plus its entire history, without consent, encrypted so only the vendor can read it. The most quoted response came from Petri Kuittinen, whose own AI agent is open sourced with security documentation: "My advice has been and continues to be: do NOT trust closed source AI harnesses."
Z.ai's statement and the July precedent
Z.ai issued a statement on September 18, translated from Chinese and circulated via Sina Finance and the Hacker News discussion, attributing the uploads to ZCode's "codebase indexing" feature: when the Repo Wiki feature generates Wiki pages it "may trigger an upload of repository data," the uploaded data is "destroyed immediately and is not stored," the feature was enabled by default in its early launch period, and the issue "has now been fixed." Three promises came with the apology: open-sourcing the ZCode codebase, inviting third-party evaluators to review the system, and a compensating weekly quota reset for all users. The statement confirms that repository data left machines by default. It does not explain why toggles verified in the client code did not stop the upload, how long uploaded data persisted before the destroy policy, or what non-repository files were caught in home-directory cases such as the one attributed to A Green Being.
There is a July 2026 precedent with a different signature. Grok Build users found full git commit histories and untouched repository data going to Google Cloud by default, with upload events firing across 186 turns of a single conversation; one user who ran Grok in their home directory found SSH keys, a password manager database, documents and photos in the upload path, discovered only by reading the tool's own logs. Peter Dedene reported that a kill-switch setting returned disable_codebase_upload: true for his account while, per the same report, eight private repositories were independently found uploaded anyway. Grok's uploads were unencrypted and logged in the tool's own unified.json, and xAI shipped a kill switch after the fact. ferstar's post reads ZCode as the opposite signature - encrypted with a key only the server holds, toggles verified in code not to stop it, and a retry counter that increments when the local capture is deleted - and calls that deliberate rather than careless.
What is still unknown after the fix
Nobody outside Z.ai can enumerate the payload, because nobody outside Z.ai can decrypt it. The promised open-source release of the ZCode codebase is the only thing that would make the upload path verifiable and answer what else was in the archive; until it lands, the fix is a vendor assertion. Z.ai went public on the Hong Kong Stock Exchange in January 2026, and its official X account had not responded to ferstar's post as of publication, with the most visible reply coming from an account affiliated with the ZCode team: "hey I am sorry to let you find it." ferstar's summary thread passed 276,000 views within 13 hours, and FeiZ's Chinese-language alert thread drew another 63,800.
The operational lesson generalizes past this one app. Two checks belong in any harness review before it touches private code: what the runtime transmits when you are logged in, and who can decrypt what it stores. A locally-running open-weight model wrapped in a cloud-phoning closed harness is not local, and the tool permission model will not tell you, because the capture never ran as a tool.
Questions this raises
does ZCode upload my git repository
According to ferstar's September 18, 2026 reverse-engineering walkthrough, yes. The client packs the entire workspace including .git objects, the LFS cache, reflogs and global app configs into a tar.gz, encrypts it, and POSTs it to Aliyun OSS. The trigger is authentication rather than usage, so it fires as long as the token provider can produce a valid JWT.
can I turn off ZCode workspace snapshot uploads
The in-app settings do not stop it. optimizeAgentExperienceEnabled only controls whether data is authorized for model training, and repoSnapshotIndexingEnabled only controls whether the server indexes uploaded snapshots; local packaging and upload continue under both. The mitigation that held in ferstar's testing was making ~/.zcode/v2/checkpoints unwritable at the kernel level, which disables the checkpoint rollback UI but leaves chat, autocomplete and tool calls working.
why can't I decrypt the ZCode archive on my own disk
ZCode uses envelope encryption: the payload is encrypted with a symmetric key under AES-256-CTR, and that key is wrapped with an RSA-OAEP public key the server delivers during upload-credential negotiation. The matching private key exists only in Z.ai's cloud. ferstar tried every private key on the local system and failed, so neither the user nor the ZCode client can read the 313MB ciphertext.
These daily notes are drafted by a model I run and operate myself - the same kind of pipeline this site is about - from sources published in the previous 24 hours, and every one lists what it read. The longer essays, the talks and the preprint are mine, written by hand.
