Keep API keys out of OpenCode
What OpenCode's model can see, what zn-gate's OpenCode plugin blocks or scrubs before and after each tool call and on your prompts, what it does not cover, how to set it up and a short test with fake values.
OpenCode sends to its model whatever enters the conversation: the prompts you type, the files the agent reads, the output of shell commands and the results of MCP tools. An API key in any of those can reach the model. zn-gate installs a plugin in OpenCode that checks these points. This page explains what the plugin does according to zn-gate's documentation, what it cannot do, and gives you a test to run yourself.
Where a key can reach the model
zn-gate's OpenCode plugin looks at three moments in a session:
- A prompt you type. A secret pasted into it can be blocked before it is sent. OpenCode shows this as an error.
- A tool call, before it runs. Reads of
.envfiles with values, of credential files and of zn-gate's own~/.znfolder can be blocked, whichever tool is used. Writes to~/.znand to the agents' hook and plugin configuration can be blocked too. - The output of a tool, before the model sees it. Secret values in it are replaced with markers. This covers every tool, MCP tools included, and the preview OpenCode shows in its terminal interface.
The plugin has no check on the model's final answer. And OpenCode keeps its own copy of the session locally, so what zn-gate does to what the model sees does not clean everything OpenCode stores (see the limits below).
What zn-gate checks in OpenCode
zn hooks install opencode writes a small plugin, zn-gate.js, into OpenCode's plugins folder. This table follows compat.md in the zn-gate repository, for the current published version.
| Point | What zn-gate does in OpenCode |
|---|---|
Reads of .env files with values, credential files and ~/.zn |
Blocked in all tools. |
Writes to ~/.zn and to the agents' hook and plugin configuration |
Blocked (write, edit, apply_patch and shell). |
| Tool output | Scrubbed in every tool before the model sees it, and in the terminal preview. |
| A secret pasted into a prompt | Blocked. OpenCode shows it as an error. |
A command that uses __ZN_<NAME>__ markers |
Rewritten automatically to run through zn run. OpenCode's own permission rules still apply. |
The secrets MCP server |
Optional. zn hooks install opencode prints the snippet to add to opencode.json. |
| zn-gate's end-to-end run with OpenCode | Yes: 0 values sent to the model provider. |
A rewrite does not approve anything, and OpenCode's own permission rules still apply.
zn-gate does not restore a detected value into a command in OpenCode: the markers stay as they are, because OpenCode keeps the arguments a tool ran with in the session, where the model can read them.
What zn-gate does not cover in OpenCode
- If the zn plugin cannot run, OpenCode calls and messages are blocked rather than let through. A project install names
znon the PATH, so on a machine without zn the plugin cannot run and calls are blocked there. - A secret with no known format that is not in the vault or in a
.envfile can get through the scrubber. - The model sees the wrapped command in its own history, including the local paths of
nodeand zn (no secret value). - Live output that OpenCode stores while a command is still running stays in its local database, because it is stored before the plugin can scrub it.
- A shell command or a patch longer than 100 KB is blocked, because zn-gate cannot check it in the time OpenCode waits for the plugin.
- On Windows, this has not been tested. The command wrapping is written for bash: with PowerShell as OpenCode's shell, a command with a marker fails instead of running (no value is exposed).
- OpenCode's documentation lists no setting that turns plugins off without a sign in the agent, so zn-gate has nothing to detect there (checked by zn-gate on 2026-10-17).
Set it up
You need Node.js 18 or later. If you already installed zn-gate for OpenCode, update it and run the second line again:
npm install -g zn-gate
zn hooks install opencode
Restart OpenCode. Then run zn-gate check in a terminal. It should show OK for OpenCode (what the output means). zn-gate check confirms zn's rules for OpenCode but does not start OpenCode. The full steps are on the OpenCode install page.
To keep real values out of your .env altogether, move them into zn-gate's vault. Your .env then holds markers such as __ZN_STRIPE_KEY__:
zn unlock
zn env shadow .env
More in Scrubber. To undo the plugin: zn hooks uninstall opencode.
Test it on your own machine
This test uses fake values. Parts of it follow what the documentation says should happen and have not been run end to end by us on your version of OpenCode, so check each step yourself.
-
In a new empty folder, create a
.envfile with fake values only:EXAMPLE_API_KEY=sk-test-EXAMPLE-not-a-real-key EXAMPLE_URL=https://example.com -
Install the plugin (
zn hooks install opencode), restart OpenCode in that folder, and runzn-gate checkin another terminal. -
Ask OpenCode: "Read the file .env and tell me what it contains." According to compat.md, zn-gate blocks reads of
.envfiles with values in all tools, so you should see a message from zn-gate instead of the values. -
Paste into the prompt the AWS documentation example key
AKIAIOSFODNN7EXAMPLE. If zn-gate recognizes the format, OpenCode should show an error and not send the prompt. If it is not blocked, the format may not be one zn-gate detects. -
Optional: run
zn unlockandzn env shadow .env, open the file and check that it holds markers. Ask OpenCode to read it again and compare. In zn-gate's own end-to-end run, OpenCode got the markers and not the values. -
Remove the sample folder when you are done. To remove the plugin:
zn hooks uninstall opencode.
Only your own results count here. If a step behaves differently from what this page says, tell us and include your OpenCode version.
Frequently asked questions
Can OpenCode's model see my API keys? Anything the agent reads, runs or receives can reach the model: files, shell output, MCP results and prompts. zn-gate's plugin checks those points, with the limits above.
Does zn-gate stop OpenCode from reading .env? Its plugin blocks reads of .env files with values in all tools.
Does it check the model's answer? No. The plugin checks your prompts, the tool calls and the tool output.
What if the plugin breaks? OpenCode calls and messages are blocked until you fix it. Run zn-gate check to see what to do.
How do I undo it? zn hooks uninstall opencode, then restart OpenCode.
Related pages
Sources
- zn-gate, per-agent compatibility and the OpenCode plugin: compat.md
- zn-gate, OpenCode install page: /docs/install/opencode/
- OpenCode, plugins documentation: https://opencode.ai/docs/plugins