Start typing to search the documentation.

Docs navigation

Policies

Policies decide whether OpenCode may perform an action on a named resource. They are authored under policies and can also be delivered by a connected OpenCode Console workspace.

Policies are separate from permissions. Permissions are per-agent tool rules that can allow, deny, or ask. Policies are binary, never prompt, and only ever tighten what permissions and providers would otherwise allow.

Configure

Deny one provider everywhere by adding a statement to opencode.jsonc:

{
  "$schema": "https://opencode.ai/config.json",
  "policies": [{ "action": "provider.use", "resource": "openai", "effect": "deny" }],
}

A denied provider disappears from the catalog and model selection even when it has valid credentials.

Earlier releases read policies from experimental.policies. That key still works and merges into policies:

{
  "experimental": {
    "policies": [{ "action": "provider.use", "resource": "openai", "effect": "deny" }],
  },
}

Statements

Each statement has three fields:

FieldValuesMeaning
actionprovider.use, tool.use, integration.connect, integration.use, mcp.use, skill.use, plugin.useThe operation being controlled
resourcestring or wildcard patternWhat the statement applies to; depends on the action
effectallow, denyThe decision when this statement matches

Statements that fail validation are dropped with a warning in the server log and the rest of the list still applies. Check the log after editing a deny.

Matching

Resources use the same whole-value wildcards as permissions: * matches zero or more characters and ? matches one.

{
  "policies": [{ "action": "provider.use", "resource": "company-*", "effect": "deny" }],
}

This denies company-us and company-eu. A pattern ending in * also matches the value without arguments, so shell:git push * covers git push on its own.

Order

When several statements match, the last one wins. There is no specificity rule, so put broad statements first and exceptions after them. If nothing matches, the action is allowed.

{
  "policies": [
    { "action": "provider.use", "resource": "*", "effect": "deny" },
    { "action": "provider.use", "resource": "anthropic", "effect": "allow" },
  ],
}

Only Anthropic remains available.

Providers

provider.use controls whether a provider is usable at all. The resource is the provider ID: anthropic, openai, opencode, or a custom ID from providers.

{
  "providers": {
    "company-ai": {
      "package": "@opencode/ai/providers/openai-compatible",
      "settings": { "baseURL": "https://ai.company.example/v1" },
    },
  },
  "policies": [
    { "action": "provider.use", "resource": "*", "effect": "deny" },
    { "action": "provider.use", "resource": "company-ai", "effect": "allow" },
  ],
}

The providers entry configures the endpoint; the statements make it the only provider OpenCode will use. Policy applies however a provider became known: catalog data, environment variables, saved accounts, built-in plugins, or configuration.

Use provider.use instead of the V1 enabled_providers and disabled_providers lists. V1 files still load; see Migrate from V1.

Tools

tool.use statements hard-deny a permission check. The resource is <action>:<value>, matched against every resource the tool checks.

{
  "policies": [
    { "action": "tool.use", "resource": "shell:git push *", "effect": "deny" },
    { "action": "tool.use", "resource": "edit:*.env", "effect": "deny" },
    { "action": "tool.use", "resource": "webfetch:*", "effect": "deny" },
  ],
}

A denied check fails with Blocked by configuration policy instead of prompting. It applies after agent rules and saved approvals, so it overrides an ask and an “Allow always” approval alike.

PatternBlocks
shell:*Every shell command
read:*/.ssh/*Reading SSH material
external_directory:*Any access outside the Location or worktree
subagent:generalLaunching one subagent
github_delete_repository:*One MCP tool
*Everything not already denied

A tool.use statement with allow never grants access. It only lifts an earlier broader deny, after which the agent’s own rules decide.

{
  "policies": [
    { "action": "tool.use", "resource": "shell:*", "effect": "deny" },
    { "action": "tool.use", "resource": "shell:git status *", "effect": "allow" },
  ],
}

git status falls back to the agent’s shell rule; every other command is blocked.

Integrations

An integration is anything you connect in /connect: the OpenCode Console (opencode), a provider such as anthropic, a web search service such as exa, or a well-known config origin such as https://ai.acme.com. Signing in to an MCP server is not affected by integration policies; use mcp.use to control MCP servers.

integration.connect controls which integrations /connect lists and which can be connected. The resource is the integration ID.

integration.use controls everything an integration supplies: its providers and models, its web search, and the MCP servers and config a Console workspace or well-known origin delivers. The resource is <integration>#<account>, where the account is the connected organization or account ID, such as opencode#org_01M3CAP8H43Z49CER6NT0Y3ZF1. Without a connected account it is the bare integration ID: the signed-out free tier is opencode, an ANTHROPIC_API_KEY environment variable is anthropic, and a provider from opencode.json is its provider ID.

This MDM policy requires one Console workspace and nothing else:

{
  "policies": [
    { "action": "integration.connect", "resource": "*", "effect": "deny" },
    { "action": "integration.connect", "resource": "opencode", "effect": "allow" },
    { "action": "integration.use", "resource": "*", "effect": "deny" },
    { "action": "integration.use", "resource": "opencode#org_01M3CAP8H43Z49CER6NT0Y3ZF1", "effect": "allow" },
  ],
}

/connect offers only the Console so members can sign in. The free tier, personal Console accounts, other organizations, environment API keys, and providers from opencode.json are all unavailable. The workspace’s providers, MCP servers, and web search work, and changing them in the Console needs no new MDM profile.

Only managed policies can withhold the MCP servers and config an integration delivers; its own policies keep applying either way.

A provider the connected Console workspace supplies can’t be changed by user, project, or remote configuration: its settings, headers, models, and package come only from the Console and managed configuration. Other providers stay configurable as usual.

MCP servers, skills, and plugins

ActionResource
mcp.useAn MCP server by its configured name
skill.useA skill by its ID
plugin.useA plugin by unversioned package name or absolute path
{
  "policies": [
    { "action": "mcp.use", "resource": "*", "effect": "deny" },
    { "action": "mcp.use", "resource": "github", "effect": "allow" },
    { "action": "plugin.use", "resource": "opencode-acme-cli", "effect": "deny" },
  ],
}

A denied MCP server or skill is removed, and a denied plugin is not loaded, however it was configured.

Upgrading from integration.use

Before OpenCode 2.3.0, integration.use covered MCP servers, skills, and plugins through an mcp:, skill:, or plugin: prefix. Prefixed statements still load and are converted to mcp.use, skill.use, or plugin.use.

A statement without one of those prefixes, including a bare *, now applies to integrations instead. A broad deny such as

{ "action": "integration.use", "resource": "*", "effect": "deny" }

no longer blocks MCP servers, skills, or plugins. Instead it blocks every provider, including the free tier, unless a later statement allows an integration. To keep the old meaning, replace it with:

{
  "policies": [
    { "action": "mcp.use", "resource": "*", "effect": "deny" },
    { "action": "skill.use", "resource": "*", "effect": "deny" },
    { "action": "plugin.use", "resource": "*", "effect": "deny" },
  ],
}

Exceptions move the same way: integration.use mcp:github becomes mcp.use github, and skill:team:review becomes skill.use team:review.

Clients older than 2.3.0 ignore integration.connect, integration.use (in its new meaning), mcp.use, skill.use, and plugin.use, so require 2.3.0 or newer wherever you rely on them.

Precedence

Ordinary settings let the nearest configuration win. Policies reverse that: statements from broader configuration are evaluated later, so they override narrower ones. Managed configuration and the connected Console always outrank user and project files.

AuthoritySource
1 (highest)macOS managed preferences (/Library/Managed Preferences/ai.opencode.managed.plist)
2System opencode.json(c) (/Library/Application Support/opencode, /etc/opencode, %ProgramData%\opencode)
3Connected OpenCode Console workspace
4Global ~/.config/opencode/opencode.json(c)
5Direct opencode.json(c); outer directories beat inner ones
6 (lowest).opencode/opencode.json(c); outer directories beat inner ones

A repository cannot re-enable a provider you deny globally:

~/.config/opencode/opencode.jsonc
{
  "policies": [{ "action": "provider.use", "resource": "openai", "effect": "deny" }],
}
project/opencode.jsonc
{
  "policies": [{ "action": "provider.use", "resource": "openai", "effect": "allow" }],
}

openai stays denied. Within one file, statements keep their written order.

Console

A workspace on the OpenCode Console compiles its Providers and Tools policies into regular policies statements in the workspace configuration. OpenCode merges that configuration like a configuration file that ranks right below managed preferences and the system file, so an organization deny cannot be lifted by a repository or user allow, and an organization allow lifts a lower-authority deny. Managed preferences and the system file still override the workspace.

Denials from these statements name the workspace:

Blocked by Acme's policy

Statements follow the connected account.

EventResult
Console policy changesApplied on the next fetch, about once a minute
Switch to another workspaceStatements replaced with that workspace’s
DisconnectStatements cleared
Console unreachableLast statements for that connection stay in effect

The Console plugin and the policy plugin cannot be disabled from plugins; see plugin control.