zn-gate 1.4.1 and what leaves your machine

John Silver4 min read

We promise that secrets and personal data never leave your machine. For users of zn-gate 1.4.0 who had set ZN_API_KEY, that promise was not true. This post explains what the package did, what our API stored, what we changed in 1.4.1 and what you should do. The short version: upgrade, and if you ran 1.4.0 with an API key, rotate any credential that could have appeared in tool output.

What 1.4.0 did

zn-gate checks three kinds of text: prompts, tool-call arguments and tool results (the contents of files your agent reads, the output of commands it runs). All three were evaluated by the local rules first. In 1.4.0 and earlier, if the local rules did not block the text and ZN_API_KEY was set, the Node package then sent that text to api.usezn.com for a second opinion from the Galvanize-60M classifier. It did so without redacting secrets, and it did so for tool results and tool arguments, not only prompts.

The README said the package was local-first and that secret redaction was applied. The redaction function existed in the code, but no executable path called it. We found this during an internal review of the published package on 2026-10-11.

Who is affected:

  • Users of the npm package (the MCP server, zn-gate shield, zn-gate analyze, zn-gate test or the library) with ZN_API_KEY set and ZN_LOCAL_ONLY not set to true.
  • Users of the Python package with ZN_API_KEY set: only analyze() and zn-gate analyze sent text, without redaction. guard, check_tool_call, check_tool_result, sanitize_tool_result and the framework integrations were always local.
  • Users who never set ZN_API_KEY, or who set ZN_LOCAL_ONLY=true, are not affected. Nothing left the machine.

What our API stored

The text travelled over TLS and was evaluated by the classifier. Two routes also stored the first 1,000 characters of each input in our evidence table, without an expiry: the legacy /prod/analyze route and the MCP analyze route. The current /v30/analyze route, which zn-gate uses by default, did not store input for paying tiers; it kept a redacted, truncated copy only for the free Contributor tier, which opts into that. Access logs record route and status only, never content.

On 2026-10-11 we changed the API to store only a SHA-256 hash and the length of the input, and we deleted the stored input text from the evidence table for every record that had it: 1,281 records, dated between August 22 and September 12, 2026.

What 1.4.1 changes

  • Tool results, tool-call arguments and tool descriptions are evaluated only locally, under every configuration: MCP server, shield proxy and library.
  • The cloud is opt-in. Prompt analysis queries the cloud only if you set both ZN_CLOUD=1 and ZN_API_KEY. ZN_API_KEY alone enables nothing. ZN_LOCAL_ONLY=true still overrides everything.
  • Redaction runs on the real code path. check_tool_result always returns content passed through the secret redactor; check_tool_call returns sanitized arguments; shield forwards tool output with secrets redacted. When the cloud is enabled, prompt text goes through the redactor before it is sent.
  • zn-gate status prints what your configuration sends, and the self-test never uses the network.
  • New tests mock the network and count requests: tool output with fake secrets comes back redacted and makes zero requests, even with ZN_API_KEY and ZN_CLOUD=1 set. Against the 1.4.0 code, seven of those eight tests fail.

The README now has a "What leaves your machine" table in all three packages, and the same table is on this site.

What to do

  1. Upgrade: npx -y zn-gate@latest (or npm install -g zn-gate@1.4.1) and pip install -U zn-gate.
  2. If you ran 1.4.0 or earlier with ZN_API_KEY set, assume any credential that appeared in tool output or tool arguments during that time could have been transmitted, and rotate it.
  3. Run npx -y zn-gate status to see what your configuration sends now.

The advisory is published on GitHub as GHSA-r4h2-qcf2-8227, with the full list of affected code paths in the 1.4.1 changelog.

Why we are writing this up

Nobody reported this to us; we found it ourselves. We could have shipped 1.4.1 with a one-line changelog entry. We are publishing the details because the whole point of zn-gate is that you can trust what it does with your data, and that trust has to survive the days when we get it wrong. If you have questions, or you think something here is still not precise enough, write to security@usezn.com.

— John Silver

Share