# 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.

Source: https://usezn.com/docs/install/windows/

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

| | 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](https://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:

```powershell
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 `.cmd` versions: `npm.cmd`, `npx.cmd` and `zn.cmd` (for example `npm.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:

```powershell
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:

```powershell
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:

```bash
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:

```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`](https://usezn.com/docs/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 `.cmd` or `.bat` program (for example a CLI installed with npm) starts through the system `cmd.exe` only. zn refuses a program path that contains `%`, `!` or `"`, because `cmd.exe` would expand them.
- Approving a `.cmd` or `.bat` file approves that file, not the programs it starts by name: an npm shim runs the `node` found on your `PATH`. Prefer approving the program itself, or use the `secrets_http_request` tool for web APIs.
- Programs are found the way Windows finds them, through `PATHEXT`: an npm install puts a script called `claude` next to `claude.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 run` wrapping 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 as `zn 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:** `PreToolUse` let calls through when the hook could not run. Update Claude Code or install Git for Windows.
- **A hook that cannot run** (for example, `node` is missing) leaves tool calls unchecked in Claude Code and Codex, and blocks prompts in Claude Code. [What each agent supports](https://usezn.com/docs/agents/).
- 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:

```powershell
npx.cmd -y zn-gate check
```

It should show **OK** for each agent you set up. [What the output means](https://usezn.com/docs/check/).

## Further reading

- [Install zn-gate](https://usezn.com/docs/install/): all agents and the one-command setup.
- [Keep your .env values in the vault](https://usezn.com/docs/quickstart/#2-keep-your-env-values-in-the-vault)
