Buckets:
| # Policy | |
| ## Purpose | |
| Policies control whether an operation on a named resource is allowed. They may be authored in configuration files, but policy evaluation is its own runtime concern. | |
| The first policy consumer is provider availability: | |
| ```text | |
| action: provider.use | |
| resource: provider ID, such as openai or company-ai | |
| ``` | |
| Provider configuration and provider policy remain separate: | |
| - `providers` describes endpoints, options, and model overrides. | |
| - `experimental.policies` determines whether an operation using a provider is allowed. | |
| A provider can be correctly configured and have valid credentials while policy still denies its use. | |
| ## Goals | |
| - Replace legacy `enabled_providers` and `disabled_providers`. | |
| - Keep the default experience unchanged when users specify no policy. | |
| - Support wildcard matching for actions and resources. | |
| - Provide one small policy vocabulary that can later cover operations such as `plugin.load` or `mcp.connect`. | |
| - Let user policy override repository policy, and later allow organization-managed policy to override both. | |
| - Keep evaluation simple: matching statements are applied in order and the last match wins. | |
| ## Non-Goals | |
| - Policies do not configure endpoints, credentials, models, or provider options. | |
| - Policies do not make unusable resources usable. | |
| - Policies do not currently provide conditions, principals, approval prompts, or enforced configuration values. | |
| - This spec does not define how organization-managed policies are delivered. | |
| ## Statement Shape | |
| ```jsonc | |
| { | |
| "experimental": { | |
| "policies": [ | |
| { | |
| "effect": "deny", | |
| "action": "provider.use", | |
| "resource": "openai", | |
| }, | |
| ], | |
| }, | |
| } | |
| ``` | |
| ```ts | |
| interface PolicyInfo { | |
| effect: "allow" | "deny" | |
| action: string | |
| resource: string | |
| } | |
| ``` | |
| The `Policy` module owns the shared `Policy.Info` interface, `Policy.Effect` type, and evaluator. Domains define their supported typed statement schemas; for example, `Catalog.ProviderPolicy` fixes `action` to `"provider.use"`. The config schema gathers those domain-defined statement schemas into the accepted `experimental.policies` union because config files are one place statements can be authored while the capability is experimental. | |
| ## Matching | |
| Both `action` and `resource` use opencode's existing wildcard matching behavior. | |
| Examples: | |
| | Action | Resource | Matches | | |
| | -------------- | ----------- | ---------------------------------------------------------------------------- | | |
| | `provider.use` | `openai` | Only use of provider ID `openai` | | |
| | `provider.use` | `company-*` | Use of provider IDs such as `company-us` and `company-eu` | | |
| | `provider.*` | `*` | Any provider operation on any provider, if more actions are introduced later | | |
| No pattern-specific precedence exists. A specific resource does not automatically beat a wildcard resource. Written/evaluation order controls the result. | |
| ## Evaluation | |
| To evaluate an operation and resource: | |
| 1. Start with `allow`. | |
| 2. Consider every statement whose `action` and `resource` match the requested action and resource. | |
| 3. Each matching statement replaces the current decision with its `effect`. | |
| 4. The last matching statement determines the result. | |
| Conceptually: | |
| ```ts | |
| function evaluate(action: string, resource: string, fallback: Policy.Effect, statements: Policy.Info[]) { | |
| return ( | |
| statements.findLast( | |
| (statement) => Wildcard.match(action, statement.action) && Wildcard.match(resource, statement.resource), | |
| )?.effect ?? fallback | |
| ) | |
| } | |
| ``` | |
| Each caller supplies the default effect appropriate for its operation. Catalog provider use supplies `"allow"`, so no provider policy statements means normal behavior continues: otherwise usable providers are allowed. | |
| ## Ordering Within One Config Document | |
| Statements remain in the order written by the user. | |
| To deny all providers except Anthropic: | |
| ```jsonc | |
| { | |
| "experimental": { | |
| "policies": [ | |
| { | |
| "effect": "deny", | |
| "action": "provider.use", | |
| "resource": "*", | |
| }, | |
| { | |
| "effect": "allow", | |
| "action": "provider.use", | |
| "resource": "anthropic", | |
| }, | |
| ], | |
| }, | |
| } | |
| ``` | |
| Result: | |
| ```text | |
| provider.use / anthropic -> allow | |
| provider.use / openai -> deny | |
| ``` | |
| To allow internal providers except experimental ones: | |
| ```jsonc | |
| { | |
| "experimental": { | |
| "policies": [ | |
| { "effect": "deny", "action": "provider.use", "resource": "*" }, | |
| { "effect": "allow", "action": "provider.use", "resource": "company-*" }, | |
| { "effect": "deny", "action": "provider.use", "resource": "company-experimental-*" }, | |
| ], | |
| }, | |
| } | |
| ``` | |
| Result: | |
| ```text | |
| company-stable: allowed | |
| company-experimental-fast: denied | |
| openai: denied | |
| ``` | |
| ## Ordering Across Authored Config Documents | |
| Ordinary settings and policies have different precedence needs: | |
| - Ordinary settings are read forward, so location-specific settings override user-global settings. | |
| - Policies are read by reversing authored config documents, so user-global policy can override repository policy. | |
| - Statements inside each document keep their written order. | |
| At minimum, this means a repository cannot silently re-enable something the user denied globally. | |
| Project config: | |
| ```jsonc | |
| { | |
| "experimental": { | |
| "policies": [{ "effect": "allow", "action": "provider.use", "resource": "openai" }], | |
| }, | |
| } | |
| ``` | |
| User-global config: | |
| ```jsonc | |
| { | |
| "experimental": { | |
| "policies": [{ "effect": "deny", "action": "provider.use", "resource": "openai" }], | |
| }, | |
| } | |
| ``` | |
| Result: | |
| ```text | |
| provider.use / openai -> deny | |
| ``` | |
| The relative policy precedence of direct project files and `.opencode` files is intentionally deferred until `.opencode` configuration is reviewed. | |
| ## Organization-Managed Policy | |
| Organization-managed policy is not ordinary authored config. When implemented, managed statements must be appended after the reversed authored statements so they have final authority. | |
| ```text | |
| repository policy -> user-global policy -> organization-managed policy | |
| ``` | |
| Plugins must not be allowed to add, remove, or override policy statements. Plugins can contribute functionality or configured providers; policy determines whether opencode permits an operation through its managed execution paths. | |
| Provider policy is not a full sandbox for executable plugins. A denied provider must not be usable through the normal provider/model path, but arbitrary plugin code requires separate governance if that becomes a compliance requirement. | |
| ## Interaction With Provider Configuration | |
| ```jsonc | |
| { | |
| "providers": { | |
| "company-ai": { | |
| "endpoint": { | |
| "type": "openai/responses", | |
| "url": "https://ai.company.example/v1/responses", | |
| }, | |
| }, | |
| }, | |
| "experimental": { | |
| "policies": [ | |
| { "effect": "deny", "action": "provider.use", "resource": "*" }, | |
| { "effect": "allow", "action": "provider.use", "resource": "company-ai" }, | |
| ], | |
| }, | |
| } | |
| ``` | |
| The provider entry configures `company-ai`; the policy statements make it the only provider permitted for use. | |
| Provider policy applies regardless of how a provider becomes known or usable, including: | |
| - models.dev catalog data | |
| - environment credentials | |
| - saved accounts | |
| - built-in provider plugins | |
| - explicit provider configuration | |
| ## Applying Provider Policy | |
| Provider records and model overrides should be assembled before checking provider policy. Otherwise later provider loading could recreate a provider that was already filtered. | |
| Intended flow: | |
| 1. Build provider/model catalog entries. | |
| 2. Apply configured provider and model overrides. | |
| 3. Ask `Policy.Service` to evaluate `provider.use` for each provider ID. | |
| 4. Prevent denied providers from being selectable or used. | |
| Whether denied providers are removed entirely or retained as disabled records for diagnostics remains an implementation decision. | |
| ## Legacy Migration | |
| Legacy deny list: | |
| ```jsonc | |
| { | |
| "disabled_providers": ["openai", "google"], | |
| } | |
| ``` | |
| Equivalent v2 policy: | |
| ```jsonc | |
| { | |
| "experimental": { | |
| "policies": [ | |
| { "effect": "deny", "action": "provider.use", "resource": "openai" }, | |
| { "effect": "deny", "action": "provider.use", "resource": "google" }, | |
| ], | |
| }, | |
| } | |
| ``` | |
| Legacy allowlist: | |
| ```jsonc | |
| { | |
| "enabled_providers": ["anthropic", "openai"], | |
| } | |
| ``` | |
| Equivalent v2 policy: | |
| ```jsonc | |
| { | |
| "experimental": { | |
| "policies": [ | |
| { "effect": "deny", "action": "provider.use", "resource": "*" }, | |
| { "effect": "allow", "action": "provider.use", "resource": "anthropic" }, | |
| { "effect": "allow", "action": "provider.use", "resource": "openai" }, | |
| ], | |
| }, | |
| } | |
| ``` | |
Xet Storage Details
- Size:
- 8.7 kB
- Xet hash:
- b74dd75dcec251ad85909c6993a1216ccbad7a6b99b9e5608b95e6f281bb63fd
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.