# Upgrading zn-gate

What to do when you update zn-gate: when to run zn hooks install again, why Codex asks you to trust the hooks again, what to do after Node.js is upgraded or moved with nvm, how to check with zn-gate check, and what each release since 1.8.0 asks you to do.

Source: https://usezn.com/docs/zn-gate/upgrade/

Run every command on this page in your own terminal, not through an AI agent: zn's hooks refuse `zn hooks install` and a few other commands when an agent starts them.

## The short version

1. Update the package: `npm install -g zn-gate` (and `pip install -U zn-gate` if you use the Python package). If you only ever run `npx -y zn-gate ...`, there is nothing to update.
2. Look up your old and new versions in [the table below](#what-each-release-asks-you-to-do). Most releases need nothing more.
3. Run `zn-gate check`. Every agent that is set up should say **OK**.

If `zn-gate check` says an agent is not protected, it also prints the command that fixes it.

## Run `zn hooks install <agent>` again when a release changes the hook

The hooks of `zn hooks install` (Claude Code, Codex CLI, Cursor) are written into the agent's own settings, so a new zn-gate does not rewrite them by itself. When a release adds or changes a hook, its [release notes](https://github.com/usezn/zn/blob/main/CHANGELOG.md) say so and name the agent. Run the install again for that agent:

```bash
zn hooks install claude-code   # or codex, cursor, opencode
```

Running it again is safe: it updates zn's own entries and leaves the rest of your settings alone. From 1.13.0, `zn-gate check` also tells you when the Claude Code hooks it finds lack a hook that your zn-gate has.

Other cases:

- **The zn-gate plugin for Claude Code** updates through Claude Code's plugin manager (`/plugin` inside Claude Code). Start a new Claude Code session afterwards. The plugin installs the zn-gate release it is pinned to, so you do not run `zn hooks install` for it. Do not keep both the plugin and the hooks of `zn hooks install claude-code`: both would run. Remove the hooks with `zn hooks uninstall claude-code`.
- **OpenCode** uses a small plugin file that zn-gate writes. Run `zn hooks install opencode` again when a release says so.
- **Project installs** (`zn hooks install <agent> --project`) write files you can commit (`.claude/settings.json`, `.codex/hooks.json`, `.cursor/hooks.json`, `.opencode/plugins/zn-gate.js`). They call `zn` from your PATH, so they follow the zn-gate you have installed. Run the install again in the project when a release changes the hook, and commit the changed file.

## Codex asks you to trust the hooks again

Codex runs a hook only after you trust it, and it asks again when a hook is new or has changed. After `zn hooks install codex` (the first time and after any release that changes the hook), open Codex and approve the hooks with `/hooks`. Until you do, Codex does not run the new or changed hook, so zn is not checking that part. `zn-gate check` shows Codex as protected only when the hook works.

For automation that already verifies its own hooks, `codex exec --dangerously-bypass-hook-trust` skips the question for one run. Do not use it for everyday work.

## Node.js was upgraded, moved or removed (nvm, fnm, Homebrew)

A user install of the hooks writes the paths of the Node.js and of the zn-gate files it was run from. A global npm package belongs to one Node.js version, so `nvm use`, `nvm uninstall`, a Homebrew Node.js upgrade or a new Node.js install can leave the hook pointing at something that is gone. When the hook cannot start:

- Prompts: Claude Code 2.1.295 or later, Cursor and OpenCode block them instead of letting them through unchecked. Codex CLI lets them through.
- Tool calls: in Claude Code and Codex CLI they go ahead unchecked.

To fix it, with the Node.js you want to keep:

1. Run `npx -y zn-gate check`. It names each agent that is not protected and the command that fixes it.
2. Usually that is `npm install -g zn-gate`, then `zn hooks install <agent>`. If you use Codex, trust the changed hooks in `/hooks`.
3. With the zn-gate plugin for Claude Code, fix Node.js (the plugin needs 20.12 or later) and start a new Claude Code session.
4. If you need your agent right away, turn zn off for it until it is fixed: `npx -y zn-gate hooks uninstall <agent>` (the `zn` command may be gone with the old Node.js), or `claude plugin disable zn-gate@usezn` for the plugin. Turn it back on with `zn hooks install <agent>` or `claude plugin enable zn-gate@usezn`, then run `zn-gate check` again.

## Check that it works

```bash
zn-gate check
```

It sets up a temporary folder with a `.env` file that holds a fake key and an ordinary file, asks each agent's installed hook to read both, and counts an agent as protected only when the `.env` file is blocked and the ordinary file is allowed. Nothing is sent and no setting changes. [What the output means](https://usezn.com/docs/check/).

## What each release asks you to do

Versions from 1.8.0 on. "No action" means that updating the package is enough. For the full list of changes, see the [changelog](https://github.com/usezn/zn/blob/main/CHANGELOG.md).

| Version | Date | What to do |
|---|---|---|
| 1.13.1 | 2026-10-20 | Only if you use the Python LangChain or LlamaIndex integrations: update with `pip install -U zn-gate`. A blocked prompt, tool call or tool output now stops the LangChain run, and with `mask_secrets=True` a tool output that contains a secret stops it too. `ZnGuardCallbackHandler(on_block="return")` now raises `ValueError`: wrap the tool with `guard_langchain_tool(tool, on_block="return")` instead (see [LangChain, CrewAI and LlamaIndex](https://usezn.com/docs/integrations/frameworks/)). The npm package has no changes. If you wrap your tools with `guard_langchain_tool`, create the handler with `mask_secrets=False`. |
| 1.13.0 | 2026-10-19 | If you installed Claude Code hooks with `zn hooks install claude-code`, run it again to add the hook that checks what a typed skill or command expands into (`zn-gate check` tells you; the plugin has it already). On a Node.js older than 20.12, `zn-gate init` sets up hooks instead of the plugin; if you have the plugin there, `check` gives the way out. In a GitHub workflow that uses the Action without `version`, set `version` to choose what runs: the 1.12.0 Action defaulted to 1.10.0. |
| 1.12.0 | 2026-10-18 | No action. If you verify release signatures, the signer workflow is now `build-release.yml` (see [verify a release](https://usezn.com/docs/zn-gate/verify-release/)). |
| 1.11.0 | 2026-10-17 | No action. A CI job that fails when `zn-gate scan` finds secrets can start failing on tokens of the new types (for example Square, Discord, Grafana or Notion tokens): look at each finding and rotate any real token. |
| 1.10.0 | 2026-10-16 | If a script runs `zn unlock` with the system keychain, it now needs an interactive terminal (`--passphrase-stdin` with the passphrase backend still works from a pipe). |
| 1.9.1 | 2026-10-16 | No action. A CI job that fails on `zn-gate scan` findings can start failing on more secret types. With Node.js older than 18, the `zn-gate` and `zn` commands stop with a message that says how to install Node.js 18 or later. |
| 1.9.0 | 2026-10-16 | If a script runs `zn-gate init` without options to wrap MCP servers, change it to `zn-gate init --wrap-mcp`: plain `init` is now a guided setup that runs only in your own terminal. |
| 1.8.2 | 2026-10-15 | No action. |
| 1.8.1 | 2026-10-15 | Update if you use the agent hooks; no other step. |
| 1.8.0 | 2026-10-15 | Run `zn unlock` once after updating: a vault still running from an older version keeps secrets as markers and tokens in the hooks until it restarts. Python 3.8 and 3.9 are no longer supported (pip there keeps installing 1.7.1). In a zn Team organization with Warn rules, do not go back to 1.7.1. |

If you are updating from a version older than 1.8.0, go through the rows of every release in between. Older releases are in the [changelog](https://github.com/usezn/zn/blob/main/CHANGELOG.md).
