Use zn-gate on Windows
Install zn-gate on Windows from PowerShell or Git Bash, where its files live (%USERPROFILE%\.zn), how the agent hooks run on Windows, the minimum versions and the known limits.
zn-gate runs natively on Windows. This page covers what differs from macOS and Linux: installing from PowerShell or Git Bash, where zn keeps its files, how the agent hooks start, which versions you need and what is not covered.
What you need
| Item | Version | Why |
|---|---|---|
| Node.js | 18 or later (LTS); 20.12 or later for the Claude Code plugin | Runs zn-gate and every hook. It must be on your PATH |
| Claude Code | 2.1.295 or later; 2.1.299 or later recommended on Windows | 2.1.295 is the minimum for the plugin. See the known limits for 2.1.299 |
| Git for Windows | Any recent version | Needed to add the Claude Code plugin from GitHub. Claude Code also runs hook commands with Git Bash |
Install Node.js from nodejs.org, open a new terminal and check node --version. The native Claude Code installer does not include Node.js: without it, Claude Code reports at session start that node was not found, prompts are blocked and tool calls run unchecked.
Install in PowerShell
Run this yourself in a normal PowerShell window, with no arguments:
npx.cmd -y zn-gate init
It finds Claude Code, Codex CLI, Cursor and OpenCode, shows the setup for each one, runs each step after you confirm it, and ends with a check.
If PowerShell says "running scripts is disabled on this system": the default execution policy blocks npm.ps1 and npx.ps1. Pick one:
- Use the
.cmdversions:npm.cmd,npx.cmdandzn.cmd(for examplenpm.cmd install -g zn-gate). - Run the commands from Command Prompt.
- Allow local scripts for your account only:
Set-ExecutionPolicy -Scope CurrentUser RemoteSigned.
For Claude Code, the plugin is the recommended route:
claude plugin marketplace add usezn/zn
claude plugin install zn-gate@usezn
For Codex CLI, Cursor or OpenCode, install zn-gate and then its hooks:
npm.cmd install -g zn-gate
zn.cmd hooks install codex # or: cursor, opencode
Install in Git Bash
Git Bash is not affected by the PowerShell execution policy, so the commands are the same as on macOS and Linux:
npx -y zn-gate init
One agent at a time: npm install -g zn-gate, then zn hooks install codex (or cursor, opencode).
The Python package, for agents you build yourself, installs into a virtual environment. In PowerShell:
python -m venv .venv
.venv\Scripts\pip install zn-gate
Where zn keeps its files
zn keeps its own state in a .zn folder in your user profile: %USERPROFILE%\.zn (for example C:\Users\you\.zn). Set the ZN_HOME environment variable to use another folder.
| What | Where |
|---|---|
| The vault (encrypted secrets) | %USERPROFILE%\.zn\vault\ (secrets.age) |
| Session files while the vault is unlocked | %USERPROFILE%\.zn\run\ |
| The vault's local audit log | %USERPROFILE%\.zn\audit.jsonl |
The log that zn-gate evidence reads |
%USERPROFILE%\.zn\evidence.jsonl |
| Optional hook settings | %USERPROFILE%\.zn\config.json |
The vault's key is kept in the Windows Credential Manager. When there is no keychain, zn asks for a passphrase instead (zn unlock --backend passphrase). zn does not set Unix file modes on Windows: the permissions of your user profile folder apply.
Where each agent keeps the configuration that zn hooks install writes:
| Agent | On Windows |
|---|---|
| Claude Code | %USERPROFILE%\.claude\settings.json (or the folder in CLAUDE_CONFIG_DIR) |
| Codex CLI | %USERPROFILE%\.codex\hooks.json (or the folder in CODEX_HOME) |
| Cursor | %USERPROFILE%\.cursor\hooks.json |
| OpenCode | plugins\zn-gate.js in %USERPROFILE%\.config\opencode (or in the folder under XDG_CONFIG_HOME) |
Agents cannot read or change %USERPROFILE%\.zn: zn blocks it, also when a command writes it as $env:USERPROFILE\.zn or %USERPROFILE%\.zn.
Organization policy files that can turn hooks off live in C:\Program Files\ClaudeCode (Claude Code) and %ProgramData%\OpenAI\Codex (Codex). zn-gate check and zn hooks install read them and tell you when one stops zn.
How the hooks run on Windows
Every hook is a short Node.js process: the agent starts node, which runs zn-gate's hook script with the event on its standard input.
| Agent | How the hook starts |
|---|---|
| Claude Code, plugin | node runs the plugin's launcher, which checks the pinned zn-gate files first |
Claude Code, zn hooks install |
Claude Code runs the command with Git Bash (PowerShell only when Git Bash is not installed). zn writes the path of node and of the hook script in single quotes, with forward slashes, so a path with spaces or special characters is never interpreted |
| Codex CLI | The Windows command goes through cmd.exe and starts powershell.exe -NoProfile -NonInteractive with the same node call |
| Cursor | On Windows Cursor usually wraps the command in PowerShell. zn writes the same powershell.exe form, which cmd.exe, PowerShell and Git Bash all run alike |
| OpenCode | The plugin starts node with an argument list, with no shell |
In our CI the plugin adds a median of 51 to 90 ms per hook call on Windows, on top of the 42 to 92 ms the hook takes without the plugin.
The vault's background process talks to the hooks through a Windows named pipe (a Unix socket on macOS and Linux). Each connection has to prove it knows the session key kept in %USERPROFILE%\.zn\run\. The vault's confirmation dialog, the Credential Manager and zn secret set --clipboard start the system Windows PowerShell 5.1, found through the system itself and not through the SystemRoot variable, which an agent could change. Without it, the dialog is not shown (approvals are refused) and the Credential Manager stops with an error that suggests zn unlock --backend passphrase.
Running commands with zn run
- A
.cmdor.batprogram (for example a CLI installed with npm) starts through the systemcmd.exeonly. zn refuses a program path that contains%,!or", becausecmd.exewould expand them. - Approving a
.cmdor.batfile approves that file, not the programs it starts by name: an npm shim runs thenodefound on yourPATH. Prefer approving the program itself, or use thesecrets_http_requesttool for web APIs. - Programs are found the way Windows finds them, through
PATHEXT: an npm install puts a script calledclaudenext toclaude.cmd, and zn picks the one Windows can run. - A PowerShell command that already runs through
zn-gate run -- ...with a secret placeholder is left alone by the hook. Before 1.12.0 it was refused.
Known limits
- WSL: not tested. We have not run zn-gate inside Windows Subsystem for Linux, so this page says nothing about it.
- Claude Code is the route we test on Windows, including its PowerShell tool.
- Codex CLI: we ran the hook command by hand from PowerShell and from
cmd.exe. It is not part of our automated tests. - Cursor and OpenCode: not tested on Windows. For OpenCode, the
zn runwrapping is written for bash: with PowerShell as OpenCode's shell, a command that has a secret marker fails instead of running (no value is exposed). zn-gate check: on Windows it does not start a hook installed aszn hook <agent>. It checks zn's rules for that agent instead and says so in its output.- Claude Code before 2.1.299, without Git for Windows:
PreToolUselet calls through when the hook could not run. Update Claude Code or install Git for Windows. - A hook that cannot run (for example,
nodeis missing) leaves tool calls unchecked in Claude Code and Codex, and blocks prompts in Claude Code. What each agent supports. - A shell, PowerShell or Monitor command longer than 100 KB is blocked, because zn cannot check it within the time the agent waits for the hook.
Check that it works
Open a new terminal and run:
npx.cmd -y zn-gate check
It should show OK for each agent you set up. What the output means.
Further reading
- Install zn-gate: all agents and the one-command setup.
- Keep your .env values in the vault