The Permission service manages tool-use approval: rules in opencode.json configure whether tools are allowed, denyed, or ask the user before execution, with later rules overriding earlier ones. When a tool requires approval, Permission publishes an Asked event, suspends execution on a Deferred, and resumes only after the user replies via Replied event or the session scope finalizes.
The Permission service lives in packages/opencode/src/permission/index.ts and manages tool-use permission requests: asking for approval, receiving user replies, and listing pending requests.[1] The Permission node export in packages/opencode/src/permission/index.ts declares EventV2Bridge.node as its only layer dependency: LayerNode.make({ service: Service, layer: layer, deps: [EventV2Bridge.node] }).[1]
The evaluate function in packages/opencode/src/permission/index.ts finds the LAST matching rule across all provided rulesets (using findLast), so later, more-specific rules take precedence over earlier ones; if no rule matches, it defaults to action: "ask" with pattern: "*".[1] Permission.ask in packages/opencode/src/permission/index.ts evaluates each pattern against the active ruleset in order: deny immediately returns a DeniedError; allow continues; any ask result suspends execution via an Effect Deferred until the user replies.[1] When Permission.ask suspends, packages/opencode/src/permission/index.ts publishes a Permission.Event.Asked event via EventV2Bridge before suspending and publishes Permission.Event.Replied after the user responds.[1] When a session's permission Deferred scope finalizes, all still-pending permission requests for that session are automatically failed with RejectedError and the pending map is cleared.[1]
The disabled helper in packages/opencode/src/permission/index.ts maps tool names to permission categories before evaluating rules: edit, write, and apply_patch map to the "edit" permission key; list_mcp_resources, list_mcp_resource_templates, and read_mcp_resource map to the "read" permission key; all other tools use their tool name directly.[1] disabled in packages/opencode/src/permission/index.ts marks a tool as disabled only when a matching rule has both pattern: "*" AND action: "deny" — partial-pattern denies do not hide the tool from the UI.[1] visibleTools in packages/opencode/src/permission/index.ts returns a filtered copy of a tools record, excluding any tools that disabled marks as globally denied, and is the canonical way to apply permission rules before presenting tools to a user.[1]
fromConfig in packages/opencode/src/permission/index.ts converts a ConfigPermissionV1.Info map into a flat PermissionV1.Rule[]: a string value is treated as an action applied to pattern: "*", while an object value maps each key as a specific pattern to an action.[1] The expand helper in packages/opencode/src/permission/index.ts resolves ~/, ~, $HOME/, and $HOME prefixes in permission patterns to the OS home directory at config-load time.[1] Tool permissions in opencode.json support wildcard patterns — for example, "mymcp_*": "ask" — allowing bulk permission assignment to all tools from an MCP server.[2]
Sources