The Trust Surface Is the Runtime, Not the Weights
On September 18, 2026, a developer going by ferstar published a reverse-engineering breakdown of ZCode, the AI coding desktop application from Z.ai the Beijing-headquartered company that also publishes the GLM open-weight model family. The finding is clean enough that it requires almost no editorializing: whenever ZCode is logged in, the client silently packages the user’s entire workspace complete .git history, LFS asset cache, reflogs, and global app configs encrypts the bundle, and uploads it to Aliyun OSS, Alibaba Cloud’s object storage. The encryption key lives exclusively on Z.ai’s servers. The user cannot decrypt the archive sitting on their own disk.
ferstar’s test workspace: 42,411 files, 345MB, producing a 313MB encrypted archive with 564 failed upload attempts logged during investigation. The client re-packaged a fresh archive within thirty minutes of the researcher deleting the pending file. That is not a one-time sync bug. That is a persistent, retry-hardened exfiltration pipeline.
I want to be precise about what makes this story different from the ordinary category of “AI tool uploads your code.” The mechanism here has a property that most coverage has glossed over: the encryption is specifically designed to prevent the user from reading their own data. Envelope encryption with a server-side RSA private key means Z.ai can read your archive whenever they choose. You cannot. That architectural choice is not a side effect. It is the point.
What Actually Gets Packaged
The packaging manifest is stored in plaintext locally, and ferstar published the breakdown for one 42,411-file snapshot:
| Content | Size | Share of Archive |
|---|---|---|
.git/lfs/ |
196.1 MB | 56.8% |
.git/objects/ |
102.2 MB | 29.6% |
.git/logs/ |
0.6 MB | 0.2% |
| Source code and docs | 46.2 MB | 13.4% |
The .git directory alone constitutes 86.6 percent of the payload. This distinction is not semantic. A .git object store is not a snapshot of your working tree it is the complete lineage of the repository from the first commit. Every branch you created and deleted is in there. Every commit message is in there. Every file you added and then removed including credentials you rotated, internal hostnames you changed, API keys from a service you deprecated is in the object store. Unpushed branches that reveal unreleased features are in there. The .git/config file contains remote URLs, submodule paths, and signing configuration. Reflogs record every HEAD movement for weeks.
Capturing the working tree gets the code you have open today. Capturing the .git directory gets the organization’s engineering history since the repository was created. These are not equivalent threats. The second is substantially worse.
The Upload Pipeline, Reconstructed
ferstar reconstructed the upload flow from the client’s app.asar archive the standard Electron packaging format, which means the client-side code is readable without any binary reversing. The pipeline has four stages:
First, the client authenticates to zcode.z.ai, which returns OSS form signatures, an object key, a per-session size cap, and critically a per-round RSA public key. Second, the client packs the workspace to tar.gz and encrypts it with AES-256-CTR, using a randomly generated symmetric key. Third, that symmetric key is wrapped with the RSA public key received in step one envelope encryption. Fourth, the client POSTs the encrypted archive directly to Aliyun OSS, which callbacks to Z.ai’s backend to register the snapshot.
The RSA private key lives in Z.ai’s cloud. ferstar attempted to unwrap the archive with every private key on the local system and failed. The 313MB ciphertext on the researcher’s own disk is undecryptable without Z.ai’s cooperation. As ferstar summarized: “A key that only the server can use serves exactly one purpose: making sure the server can read your code whenever it wants.”
During the test, the running client maintained persistent connections to zcode.z.ai and two Aliyun OSS nodes. The session logs showed 62 capture events from a single active session triggered before every prompt and on task completion. This is not a once-per-session backup. Every prompt sends a fresh snapshot.
The Settings Toggles Do Not Stop It
The natural response when you suspect a tool is oversharing is to open Settings and disable the relevant option. ferstar cross-referenced the UI switches against the actual code paths, and the result is worth quoting directly.
“Optimize Experience” (optimizeAgentExperienceEnabled) controls only whether data is authorized for model training. Snapshot capture and upload continue regardless of this setting.
“Repo Snapshot Indexing” (repoSnapshotIndexingEnabled) controls only whether the server indexes the already-uploaded snapshots. Local packaging and upload continue regardless of this setting.
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 meaning any logged-in session triggers the pipeline, without any additional user action or consent dialog.
I have spent enough time reading closed-source harness code to recognize what this architecture is doing. These two toggles exist to give the user a sense of control. They control downstream use of what has already been captured. They do not control capture. The distinction is the entire story.
The Agent Never Sees It
A secondary source corroborates the architecture. OrcaPromptVault, a public collection of captured AI harness system prompts, holds a 131KB system prompt and a 31-tool surface from ZCode. The agent’s complete tool surface contains zero snapshot tools, zero upload tools, and zero telemetry tools. Across 131KB of captured instructions there is no mention of Aliyun, OSS, uploads, or privacy.
The exfiltration pipeline is not an agent tool. It is a host-level sidecar instantiated outside the tool loop. This matters for a specific reason: when AI safety researchers and enterprise security teams audit AI coding agents, they often focus on what tools the agent has access to. ZCode’s architecture puts the most sensitive capability full repository exfiltration in a layer that agent-level auditing will never see.
This is not accidental. Putting the snapshot pipeline in the agent tool surface would make it visible to anyone inspecting the system prompt or monitoring tool calls. Putting it in the host sidecar makes it invisible to both the agent and to prompt-level security reviews. The captured system prompt even shows the user-facing feature that uses snapshots checkpoint/rewind, with a template appearing five times without any mention of the underlying upload mechanism. The feature is disclosed. The mechanism is not.
ZCode also ships a ReadSessionContext tool that reads other persisted ZCode sessions on demand by session ID. Combined with the host-level snapshot sidecar, session content is both locally persisted and cloud-captured, and the agent can retrieve sessions by ID. This is not a privacy-by-design product.
What the Privacy Policy Says
ZCode’s privacy policy states the tool collects “text, files, and code submitted during conversations” the standard inference-context disclosure that every AI coding tool makes. Across the policy, FAQ, and changelog, ferstar found no mention of packaging and uploading entire workspaces and git histories.
This is the legal problem that outlasts any technical fix. “Text, files, and code submitted during conversations” describes what you type into the chat interface and what you paste into context. It does not describe the automatic packaging of your entire repository including years of git history, triggered before every prompt, without any user submission action. The disclosure is accurate for one data collection mechanism and silent on another.
ZCode launched in July 2026, and its launch pitch ran directly on trust. Z.ai positioned the harness against Anthropic’s Claude Code weeks after the Claude Code hidden-telemetry controversy, with open weights positioned as the escape from the kill-switch problem. 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. Workspace snapshotting is not listed on the ZCode website.
Z.ai went public on the Hong Kong Stock Exchange in January 2026. The most visible reply from an account affiliated with the ZCode team was “hey I am sorry to let you find it” which reads as confirmation, not rebuttal. That response passed without any denial of the mechanism.
Open Weights Do Not Equal a Safe Runtime
The thread reaction on HN and V2EX revealed the confusion that makes this category of vulnerability recurring. Multiple commenters assumed ZCode was open source because GLM is. It is not. Several more expressed surprise that a tool built on open weights could have a closed, cloud-phoning harness. These are two different things, and conflating them is the conceptual error that ZCode’s launch pitch was designed to exploit.
I have seen this conflation in conversations with engineers who have otherwise excellent security instincts. The mental model goes: “I’m running an open-weight model locally, so I control the stack.” This is true for the model. It is not automatically true for the application that loads, prompts, and wraps that model.
The GLM weights are open. Anyone can download them, inspect them, quantize them, run them on local hardware without any network calls. The ZCode harness is closed source, phones home before every prompt, and sends your entire git history to Alibaba Cloud storage where only Z.ai holds the decryption key. These two facts coexist without contradiction. The weights are open. The runtime is not.
This distinction matters increasingly because the value proposition of local AI is often articulated at the model layer open weights, local inference, no vendor lock-in while the actual user-facing products are built at the harness layer, where none of those properties automatically apply. The harness decides what context gets sent where, how authentication works, what telemetry runs, and who can read what. Users who chose GLM because it runs locally chose the model. They did not necessarily choose ZCode’s harness, and the harness is doing something the model documentation never disclosed.
| Layer | ZCode Situation | User Control |
|---|---|---|
| Model weights (GLM) | Open, Apache 2.0 | Full can download, run offline |
| ZCode harness | Closed source, Electron | None binary only |
| Snapshot pipeline | Runs unconditionally on login | None settings toggles don’t gate capture |
| Encrypted archive | On Aliyun OSS, server-side key | None user cannot decrypt own data |
| Checkpoint/rewind feature | Disclosed in UI and system prompt | Partial disabling kills the feature |
| Upload mechanism | Not in privacy policy, not on website | None not disclosed |
The Fix That Works, and Why Official Fixes Probably Won’t
Deleting the pending archive does not work: the client re-packages within thirty minutes. The fix that holds is filesystem-level. On Linux, chattr +i on the checkpoints directory makes it immutable at the kernel level the client cannot write to it even as root. On macOS, chflags uchg achieves the same effect for unprivileged writes.
The trade-off is explicit: the checkpoint rollback UI stops working, because that feature requires the upload in the first place. Chat, autocomplete, and agent tool calls work normally without the snapshot pipeline. This is a useful data point. The feature that requires sending your git history to Z.ai’s cloud is one specific feature checkpoint/rewind not the core coding assistant functionality. The pipeline is architecturally separable from the assistant’s core value.
This matters for evaluating whatever fix Z.ai ships. If a future ZCode update adds a toggle that actually stops upload, the question to ask is: does this toggle gate the capture sidecar, or does it gate indexing on the server side? The current “Repo Snapshot Indexing” toggle already demonstrates that Z.ai is comfortable shipping controls that stop downstream use while continuing upstream collection. Any fix that does not gate the sidecar instantiation itself ideally with user-visible, auditable network traffic is not a fix.
The correct fix would be: stop unconditional sidecar instantiation. Require explicit user opt-in to the checkpoint feature. Document the upload mechanism in the privacy policy. Change the encryption architecture so users can read their own archives. None of these changes are technically difficult. They are policy decisions, not engineering problems.
The Broader Harness Trust Problem
ZCode is not an isolated incident. It is a well-documented example of a structural problem that applies to every closed-source AI coding harness currently in production.
The AI coding tool category has converged on a specific architecture: a model or API backend for inference, an Electron or native desktop harness for the user interface and agent orchestration, and a cloud backend for features like sync, history, and team collaboration. In this architecture, the harness is the trust surface. The harness decides what to send, when, to whom, and with what encryption properties. The model whether frontier or open-weight is downstream of all of these decisions.
Enterprise security teams have developed rigorous processes for evaluating SaaS products: data processing agreements, SOC 2 audits, penetration testing, network traffic analysis. Most of these processes were designed for web applications that make HTTPS calls you can observe in a browser’s network tab. They were not designed for Electron applications with embedded node runtimes that can make network calls outside the browser sandbox, read arbitrary filesystem paths, and package data before any observable network event occurs.
The ZCode sidecar runs before every prompt, packages data before the prompt is sent, and delivers the archive directly to a third-party storage provider (Aliyun OSS) via a signed form POST. A corporate network monitoring solution watching for outbound calls to zcode.z.ai would see credential negotiation traffic. The actual payload goes to Aliyun OSS endpoints. The two hops are not obviously connected at the network level without deep packet inspection and correlation.
I have talked with security engineers at three different companies who evaluated AI coding tools for enterprise deployment over the past year. All three used network traffic analysis as a primary signal. None of them had instrumented for the specific pattern ZCode uses pre-prompt packaging to a third-party endpoint via signed form credentials obtained from the primary backend. This is a coverage gap that the ZCode disclosure has now made visible.
What Due Diligence Actually Requires
After reading ferstar’s breakdown, I ran through the checklist I would want any team deploying an AI coding harness to complete. The ZCode case makes the gaps in standard checklists clear.
Network traffic analysis is necessary but not sufficient. You need to capture traffic from the client during a full work session not just the setup phase. Capture should cover all connections the Electron process opens, not just calls to the primary vendor domain. Map destination IP ranges against cloud providers; signed form POSTs to object storage endpoints may not obviously connect to the primary vendor without correlation.
Source availability is a signal, not a guarantee. ZCode is built on GLM, which is open. The harness is not. For any tool where the harness is closed, you are extending trust to the vendor’s binary, not to auditable code. This is the same trust model as any closed-source application, and it should be evaluated the same way with the recognition that AI harnesses have unusually broad filesystem access by design.
Opt-in vs. opt-out is not the right question. The right question is whether capture can be gated off at all. ZCode’s “Repo Snapshot Indexing” toggle demonstrates that vendors can ship toggles that control downstream use while leaving upstream collection intact. The question for any sync or checkpoint feature is: does disabling it stop the collection, or does it stop the use of what was already collected?
Encryption architecture matters more than the presence of encryption. AES-256-CTR is strong encryption. It provides no meaningful protection when the user cannot hold the decryption key. Server-side-only key custody means the vendor can read the data whenever they want. If a vendor describes their storage as “encrypted,” the follow-up question is: who holds the key, and can the user verify this independently?
The .git directory is the most sensitive thing in most workspaces, and it is rarely treated as such in security reviews. Secrets management tooling protects against committing credentials to main. It does not protect against an agent harness that packages the full object store before you have a chance to clean the history. Any tool that has read access to your workspace has read access to your git history, deleted files and all.
The Community Response Tells You Something
ferstar’s tweet hit 276,000 views within thirteen hours. FeiZ’s Chinese-language alert thread reached 63,800. The V2EX discussion ran long. The community response split on a specific line: multiple commenters argued that agents upload code fragments during tool calls all the time, with consent, and this is no different.
It is different on three axes that matter. First, scope: a tool call uploads the context the agent needs for a specific task. ZCode uploads the complete git history of the entire workspace, including content the user never submitted to the agent. Second, consent: tool calls are initiated by the user asking the agent to do something. ZCode’s snapshot pipeline runs before every prompt, without a user action triggering it. Third, decryptability: tool call data goes to the inference API, which uses it to respond. ZCode’s snapshots go to object storage with server-side-only key custody. The vendor can read them. The user cannot.
The “agents upload code all the time” framing is technically accurate and strategically misleading. It conflates inference-time context with persistent, undisclosed repository archiving. The conflation is doing work: it normalizes the ZCode behavior by association with a different, more acceptable category of data sharing.
Petri Kuittinen, whose own AI agent ships with public security documentation, put the distilled conclusion simply: “My advice has been and continues to be: do NOT trust closed source AI harnesses.” This is correct, and the ZCode case is the clearest demonstration of why that advice is not paranoia.
Why Electron Makes This Category Hard to Audit
ZCode is an Electron application. That architectural choice deserves more attention than it typically receives in security discussions, because it defines the threat model in ways that do not apply to web-based tools or native applications written in memory-safe languages.
Electron bundles Chromium and Node.js into a single desktop application. The Node.js runtime has full access to the local filesystem, can spawn arbitrary child processes, open arbitrary network connections, and execute arbitrary JavaScript. The browser sandbox that protects against cross-site scripting in a web app does not exist in the same form here. An Electron app running with default configuration can read files, write files, and make network calls without any of the permission prompts that a browser extension or a mobile app would require.
The app.asar file that ferstar unpacked is the standard Electron application bundle — a tar-like archive containing the application’s JavaScript source. It is readable without tools beyond the standard asar utility. This is actually the favorable case: ferstar could read the source. A more adversarial implementation would use native modules compiled to binary, which would require actual reverse engineering. ZCode’s snapshot logic was findable because the Electron JavaScript layer is not obfuscated. That should not be mistaken for a security property.
The Node.js process that the Electron main process uses for the capture sidecar runs outside the renderer’s security context. Renderer-process isolation, which limits what JavaScript can do in the UI layer, does not constrain the main process or its child processes. Security reviews that inspect the renderer — examining what the agent’s tool surface can do, what the UI can request — are looking at a subset of the application’s actual capabilities. The main process and its sidecars are a different layer entirely, and they are where ZCode put the pipeline that matters.
This is not a ZCode-specific architectural quirk. Cursor, Windsurf, Void, and every other Electron-based coding assistant has the same main-process/renderer split. The security property that a user can inspect in the agent’s tool surface or in the system prompt is a property of the renderer layer. It says nothing about what the main process is doing in parallel. Teams auditing these tools need to instrument at the process level, not the UI level.
What Z.ai Needs to Do
Z.ai has not responded publicly as of publication. The most visible reply from an affiliated account reads as confirmation of the mechanism. A response that stops short of a full remediation plan is not adequate given the severity of what was disclosed.
A minimum-adequate response addresses four things. First, disclose the snapshot mechanism in the privacy policy with specificity not “files submitted during conversations” but a description of what gets packaged, when, and where it goes. Second, change the encryption architecture to give users access to their own data user-held keys, or at minimum, an option for encryption where the user controls the key. Third, gate the sidecar instantiation on explicit user opt-in, not just downstream use toggles. Fourth, publish a timeline for any changes, with a statement about what happens to archives already on Aliyun OSS.
I am not expecting Z.ai to do all of this quickly. The encryption architecture in particular requires non-trivial engineering. But the disclosure and the opt-in gating are policy decisions that can be shipped in days, not months. The absence of either in the current release suggests that the current behavior is intended, not incidental.
There is also a broader obligation that goes beyond Z.ai’s immediate response. Z.ai positioned GLM explicitly as an alternative for engineers who wanted out of the US frontier model ecosystem — no Anthropic, no OpenAI, local inference, Chinese open weights. That pitch attracted a specific audience: engineers at companies with sensitive intellectual property who were willing to move away from frontier models in exchange for local control. That audience is exactly the population whose git histories are most valuable to an adversary with server-side decryption keys. The positioning made the trust argument; the architecture contradicted it in the most direct way possible.
Falsifiable Predictions
If Z.ai ships a “fix” within two weeks, it will gate indexing, not capture. The privacy policy will be updated to mention “workspace snapshots” but will not change the encryption architecture. I give this 70 percent probability.
If the fix does not gate capture at the sidecar level, a second researcher will demonstrate within sixty days that the archive is still being built and uploaded despite the updated toggle. The retry logic ferstar documented is robust enough that a superficial toggle will have observable gaps.
The ZCode disclosure will be cited in at least two enterprise AI tool security evaluation frameworks within the next six months. The specific gap it exposes pre-prompt packaging to third-party storage endpoints via credential-signed form POSTs is reproducible in any Electron-based harness, and security teams now have a documented case to reference when pushing vendors for transparency.
Open-weight model adoption in enterprises will not meaningfully slow down because of ZCode. What will slow down is the assumption that “uses open weights” implies “safe to deploy without network traffic analysis.” That assumption was always wrong. ZCode made it visibly wrong in a way that is hard to argue with. That is, in a narrow sense, useful.
At least one other Electron-based AI coding harness will have a comparable disclosure within twelve months. The ZCode case follows a pattern: a new harness enters the market with a privacy pitch, an engineer with the right tools takes thirty minutes to unpack the app.asar, and the gap between the pitch and the code becomes visible. The gap exists not because these teams are uniquely bad actors but because Electron gives any developer broad filesystem and network access, and shipping fast does not leave much time for asking whether every capability that technically works is a capability that should be used without explicit consent.
The harness layer is where AI coding tools will be won and lost over the next two years, and it is where the most significant security decisions are being made without adequate disclosure. ZCode is the clearest documented case to date. It will not be the last. The engineers who understand that distinction — that the weights are open and the runtime is not — will make better decisions about what runs in their development environment. The engineers who conflate the two will keep getting surprised.




Discussion