What's new in zn-gate 1.13.0: zn-gate connect, zn-gate report and a GitHub Action fix

ReleasesJohn Silver9 min read

zn-gate 1.13.0 adds zn-gate connect, which turns on the optional cloud check with an analysis-only API key kept in your system keychain, and zn-gate disconnect, which can also revoke that key. It adds zn-gate report, a local summary of what zn-gate did in the last days. It also fixes the default version of the GitHub Action, keeps the audit log in one chain when several programs write to it, and lets you turn off color with NO_COLOR. zn-gate still needs no account and sends nothing by default. Update with npm install -g zn-gate@1.13.0 and pip install -U zn-gate.

How do I turn on the optional cloud check?

Run this in your own terminal:

npx -y zn-gate connect

The cloud check is optional. zn-gate works fully on your computer without it. When you turn it on, the text of analyze_prompt calls (and zn-gate analyze), with detected secrets hidden first, is also checked by the Galvanize 2 classifier at usezn.com. Hooks, file and secret protection and every other command stay local.

What connect does:

  1. It only runs in a terminal you type in. If an AI agent starts it, it refuses.
  2. It opens usezn.com in your browser, where you create an analysis-only API key.
  3. You paste the key at a hidden prompt. It is never taken from the command line.
  4. It keeps the key in your system keychain, or in the zn vault when there is no keychain and the vault is unlocked (zn unlock), and never in a file.

It accepts only a key limited to analyzing text. Such a key cannot read your usage or manage keys, so a leaked copy can do little, and a full-access key is refused. zn-gate status shows whether a key is connected and where it comes from, without showing it. A key in ZN_API_KEY or --key only takes part when you also set ZN_CLOUD=1. The saved key is only ever sent to https://api.usezn.com. The full guide, with what is sent and what to do if something fails, is in the connect documentation.

How do I disconnect and revoke the key?

zn-gate disconnect

It removes the saved key from this computer and asks whether to also revoke it on usezn.com, so it stops working everywhere. The default is no. Only an explicit yes sends anything: one request with the key to https://api.usezn.com. In your own terminal, --revoke skips the question and --no-revoke never revokes. If the revoke request fails, the local copy is still removed and the command points you to the API keys page of usezn.com so you can revoke the key yourself.

Like connect, disconnect runs only in your own terminal. From a script or an AI agent it refuses and changes nothing. The hooks also refuse commands that ask the system keychain for zn's own entries, so the direct ways for an agent to read the saved key are closed. A program running as you can still ask the keychain, as for any entry. That is why the key is limited to analyzing text.

What does zn-gate report show?

npx -y zn-gate report

It prints a short summary of the last 7 days on this computer:

  • blocked actions, by agent;
  • secrets hidden, by type;
  • new MCP servers zn-gate started protecting;
  • tools of protected MCP servers that changed and wait for your approval;
  • the busiest day.

Use --since with a period from 1d to 90d to change the range, and --json for scripts. The report reads only local files, writes nothing and sends nothing. It ends with one line about zn Team; --no-team-line hides it, and it does not appear once the computer has joined a Team. See the report documentation.

Is there a security fix?

Yes. Some provider token formats were not redacted by the library API, the MCP tool checks, the text sent to the cloud analysis and the local evidence preview. They are now redacted the same way as in the Python package. The agent hooks were not affected. Update to zn-gate 1.13.0.

What else is new?

  • A zn API key (zn_ followed by 48 characters) is recognised by the scrubber and hidden like other provider keys.
  • Claude Code: a new UserPromptExpansion hook checks what a typed skill or command expands into. It blocks the expansion, with the reason and the next step, when the model would see a secret value, a private key or the lines of a .env file. Run zn-gate hooks install claude-code again to add it; the plugin has it already.
  • zn-gate check now tells you when the Claude Code hooks installed by an older zn-gate lack that check, and suggests the same command.
  • zn-gate init no longer installs the Claude Code plugin when Node.js is older than 20.12, which the plugin needs. It sets up zn's hooks for Claude Code instead and says so in one line.
  • The hooks check the code that shells and wrappers run from a string (sh -c, bash -lc, eval, pwsh -Command, cmd /c and more) as a command of its own, so every rule applies inside it.
  • zn audit hides the value after a credential flag in more forms of argument arrays, in its text, JSON and SARIF reports.
  • More automated tests for the MCP servers, zn run, the vault, zn team, reach, audit and the Python package. No change in behaviour.

What changed in the GitHub Action?

The version input now defaults to the version of the Action release itself (1.13.0 in this release). The 1.12.0 release of the Action defaulted to 1.10.0, so a workflow on usezn/zn@v1.12.0 without version installed zn-gate 1.10.0. If you use the Action, set version in your workflow to choose exactly what runs. The GitHub Actions guide has the inputs and a minimal audit example, and this release also updates the Marketplace listing text without changing behaviour.

What happens if the audit log is damaged?

The audit log (~/.zn/evidence.jsonl) is now checked for damage before anything is added to it. A log with unreadable bytes or a cut-off last line is reported as corrupted, with the line number, by evidence --verify (non-zero exit), evidence and evidence --export, instead of failing with a Python error. A damaged log is never used to start a new chain. The event is not written, a warning is shown, and your command or agent carries on as usual.

To recover, move the damaged file aside and the next event starts a new chain:

mv ~/.zn/evidence.jsonl ~/.zn/evidence.damaged.jsonl

On Windows, in cmd: move %USERPROFILE%\.zn\evidence.jsonl %USERPROFILE%\.zn\evidence.damaged.jsonl; in PowerShell: Move-Item $env:USERPROFILE\.zn\evidence.jsonl $env:USERPROFILE\.zn\evidence.damaged.jsonl. The old file stays for review.

Several programs can write to the log at once, for example two agents in one project, or the Node.js and Python packages together. Each write now takes a short lock (an evidence.jsonl.lock file next to the log), so the records stay in one chain. If the lock stays busy for 2 seconds, or the log cannot be written (no permission, full disk), the event is skipped with a warning and the decision is unaffected. A lock left by a crashed program is cleared after 10 seconds. The Node.js and Python packages can also share one log that holds non-ASCII text, such as accented letters or CJK, and logs written by earlier versions still verify.

What else is fixed?

  • The hook no longer refuses a command that writes a file from a here-document (cat > file <<'EOF') when the body holds a * or another glob character, and it checks a 100 KB body in a few milliseconds instead of tens of milliseconds. A here-document body is read as data only in a plain data command such as cat or tee. Bodies that a shell, an interpreter or xargs runs are still refused, and so is every read of .env or a credential file on the command line.
  • The command line output of the Node.js and Python packages has no color codes when NO_COLOR is set to any non-empty value, when TERM=dumb, or when the output is not a terminal. With NO_COLOR or TERM=dumb it also leaves out symbols and box-drawing lines. Status words such as ALLOWED and BLOCKED keep their meaning, and exit codes and the JSON, SARIF and agent hook outputs do not change.
  • The hooks refuse a .env file reached through a Windows root, a network share or an extended-length path (type %CD%\.env, more \\server\share\.env, gc \\?\C:\path\.env), because zn cannot read such a path to check whether the file holds values.
  • The rule that protects zn's key no longer mistakes a module named cloud-keys for zn's own cloud-key module, and it now also recognises module names written with escape sequences or split by quotes.
  • The scrubber no longer reports a masked example (an Anthropic key or a bearer value made mostly of one repeated character) as a secret.
  • zn-gate reach --remember shows the date of the previous run only when it is a plain date, so an edited memory file cannot put other text into the report.

Frequently asked questions

What is new in zn-gate 1.13.0?

zn-gate connect and zn-gate disconnect, to turn the optional cloud check on and off with an analysis-only API key kept in your system keychain or vault. zn-gate report, a local summary of the last days. The scrubber recognises zn API keys, the hooks check what a typed skill expands into, a damaged audit log is reported instead of crashing, and the GitHub Action default version is fixed. The audit log keeps one chain when several programs write to it, and the command line output can be plain text with NO_COLOR.

How do I update to zn-gate 1.13.0?

Run npm install -g zn-gate@1.13.0 for the Node package and pip install -U zn-gate for the Python package. If Claude Code uses zn's hooks instead of the plugin, run npx -y zn-gate@1.13.0 hooks install claude-code to add the new hook; npx -y zn-gate@1.13.0 check tells you when it is missing. If you use the Claude Code plugin, update it from the plugin directory once 1.13.0 is listed there.

Do I need an account to use zn-gate 1.13.0?

No. zn-gate still needs no account and sends nothing by default. zn-gate connect is optional: only after you run it, the text of analyze_prompt calls (with detected secrets hidden first) is also checked by the Galvanize 2 classifier at usezn.com.

Does zn-gate report send my data anywhere?

No. It reads only local files, writes nothing and sends nothing. Sharing usage statistics with zn is a separate choice that is off unless you turn it on.

Which version does the GitHub Action install if I leave out version?

From 1.13.0, each release of the Action defaults to its own zn-gate version. The 1.12.0 release of the Action defaulted to 1.10.0, so a workflow on usezn/zn@v1.12.0 without version installed zn-gate 1.10.0. Set version in your workflow to choose exactly what runs.


The full list of changes is in the 1.13.0 release notes. zn-gate is MIT, runs locally and needs no account: start with npx -y zn-gate report, or see Install for each agent.