From opencode-dev
Author OpenCode enterprise governance — the `experimental.policies` provider allow/deny layer and the `permission` per-tool gate — for `opencode.json` / `.opencode/`. Use when configuring which LLM providers OpenCode may use, migrating off `disabled_providers`/`enabled_providers`, locking down a fleet, or setting tool permissions (`allow`/`ask`/`deny`) for `bash`, `edit`, `read`, `external_directory`, `doom_loop`, etc. Trigger on mentions of OpenCode policies, `experimental.policies`, `provider.use`, `permission` config, OpenCode `.env` access, doom-loop / external-directory guards, per-agent permission overrides, or "restrict OpenCode to Anthropic only".
How this skill is triggered — by the user, by Claude, or both
Slash command
/opencode-dev:policies What governance do you want? (e.g. 'allow only Anthropic provider', 'deny edit but allow docs', 'why can OpenCode still read .env?')What governance do you want? (e.g. 'allow only Anthropic provider', 'deny edit but allow docs', 'why can OpenCode still read .env?')The summary Claude sees in its skill listing — used to decide when to auto-load this skill
Two **separate** governance layers in OpenCode config. Don't conflate them:
Two separate governance layers in OpenCode config. Don't conflate them:
| Layer | Key | Governs | Values |
|---|---|---|---|
| Policies | experimental.policies (array) | Whether OpenCode may use a resource — today only LLM providers | effect: allow|deny |
| Permissions | permission (top-level) | What tools may do in a session — per tool, per pattern | allow | ask | deny |
OpenCode's API moves fast and predates the model's training cutoff — before writing policies code, read
references/policies.rst+references/permissions.rstAND re-check https://opencode.ai/docs/policies/ and https://opencode.ai/docs/permissions/ for drift. The official config schema ishttps://opencode.ai/config.json. Policies are still underexperimental..
Verbatim docs live in references/. This file is the delta a fresh model gets
wrong — read it before authoring, then verify against the references.
experimental.policies)The traps that bite, in order of how often a fresh model gets them wrong:
experimental., not a top-level policies key.
{ "experimental": { "policies": [ … ] } }. A bare top-level "policies" is
silently ignored.provider.use is the ONLY action that exists today. Policies govern
LLM providers, not tools/commands/permissions. You cannot deny a tool or a
slash command with a policy — that's the permission layer (Layer 2). If you
catch yourself writing "action": "tool.use" or "bash", you're in the wrong
layer.opencode.json cannot re-enable a provider the global config denied. This is
the enforcement guarantee an enterprise relies on.allow. Policies are a denylist by default. To run
an allowlist ("only Anthropic"), you must put a broad deny * FIRST, then
allow the specific providers.deny * then
allow anthropic.disabled_providers / enabled_providers.
Don't emit those legacy keys — translate them:
disabled_providers: [openai, google] → one deny provider.use per id.enabled_providers: [anthropic, openai] → deny * then allow each id.Statement shape (all three fields required):
{ "effect": "deny", "action": "provider.use", "resource": "openai" }
resource is a provider id or wildcard (* = zero-or-more chars, ? = one char),
e.g. "company-*". See assets/policies.json for the allowlist template.
{
"$schema": "https://opencode.ai/config.json",
"experimental": {
"policies": [
{ "effect": "deny", "action": "provider.use", "resource": "*" },
{ "effect": "allow", "action": "provider.use", "resource": "anthropic" }
]
}
}
If you flipped the order (allow anthropic then deny *), last-match-wins means
deny * wins for every provider including Anthropic — nothing works. Broad first.
permission)Per-tool / per-pattern gate over tool actions in a session. Three resolved
states: allow (run silently), ask (prompt), deny (block).
There is no single default. A fresh model assumes "everything defaults to allow" — wrong on three counts:
| Key | Default | Why it surprises |
|---|---|---|
| most tools | allow | the baseline |
doom_loop | ask | fires when the same tool repeats 3× with identical input |
external_directory | ask | fires when a tool touches a path outside the project cwd |
read of .env | deny | *.env + *.env.* deny, *.env.example allow |
So "why can't OpenCode read .env?" / "why did it pause on the 3rd identical call?"
are defaults, not bugs. To let OpenCode read a secret file you must explicitly
allow it under read; to silence the loop guard set doom_loop: "allow".
edit permission covers edit, write, and the patch tool (named
apply_patch in OpenCode's built-in tool list; the /docs/permissions/ page
refers to it loosely as "patch"). There is no separate write or patch
permission key."*" first, exceptions
after. { "bash": { "*": "ask", "git *": "allow", "rm *": "deny" } }."grep *" matches
grep foo file.txt; bare "grep" matches only the literal word and blocks the
call. Commands with arguments require the explicit *.~ / $HOME expand at the start of a pattern.read, edit, glob, grep, bash, task, skill, lsp, question, webfetch, websearch, external_directory, doom_loop. lsp is
non-granular (no object form). See references/permissions.rst for what each
matches on."permission": "allow".Agent-level permission rules beat the top-level permission block. This is a
separate axis from policies — don't assume the policy global>project rule carries
over to permissions; it doesn't. Override via agent.<name>.permission in JSON, or
permission: in a markdown agent's frontmatter:
---
description: Code review without edits
mode: subagent
permission:
edit: deny
bash: ask
webfetch: deny
---
Only analyze code and suggest changes.
See assets/permission.json for a JSON template with bash, edit, and a
per-agent block.
When OpenCode prompts: once (this request only), always (future matching
requests, current session only — not persisted), reject.
experimental.policies; tool lock-down → permission.
Never provider.use for a tool.deny * FIRST, then allow <id> (default is allow; last match wins).disabled_providers/enabled_providers; emit policies instead..env already denied, doom_loop
external_directory already ask.edit key = edit + write + apply_patch.https://opencode.ai/config.json and re-check the live docs.npx claudepluginhub nq-rdl/agent-extensions --plugin opencode-devCreates structured, bite-sized implementation plans from specs or requirements before writing code. Useful for breaking down multi-step tasks into testable steps with file structure and task boundaries.