Skip to content

bug: user-level permission config overridden by project-level config with missing rules #31

@terisuke

Description

@terisuke

Bug Description

User-level permission config at ~/.config/opencode/opencode.jsonc is not effective when a project has .opencode/opencode.jsonc with minimal/missing permission rules.

Steps to Reproduce

  1. Create user-level config at ~/.config/opencode/opencode.jsonc:
{
  "permission": {
    "read": { "*": "allow" },
    "bash": { "*": "ask", "supabase *": "allow", "git *": "allow" }
  }
}
  1. Have a project with .opencode/opencode.jsonc containing:
{
  "permission": {
    "edit": { "packages/opencode/migration/*": "deny" }
  }
}
  1. Start OpenCode in the project directory
  2. Run a read operation or supabase db query command

Expected Behavior

User-level read/bash permissions should be respected since the project config doesn't specify them.

Actual Behavior

Both read and bash (e.g., supabase *) still prompt for permission approval.

Root Cause Analysis

Config loading hierarchy (from src/config/paths.ts):

  1. System-managed (highest priority)
  2. Project-level .opencode/opencode.jsonc
  3. User-level ~/.config/opencode/opencode.jsonc (lowest priority)

When configs are merged, the project-level config takes priority. If the project config has a permission key (even with minimal rules like just edit), it may replace rather than deep-merge the user-level permission object, causing user-level read/bash rules to be lost.

Evidence from src/agent/agent.ts:102-109:

const user = Permission.fromConfig(cfg.permission ?? {})

Here cfg.permission is the already-merged config. If project-level permission overwrites user-level permission during config merge (shallow merge on the permission key), the user's read/bash rules are gone.

Additionally, evaluate.ts:10 uses findLast() semantics:

const match = rules.findLast(
  (rule) => Wildcard.match(permission, rule.permission) && Wildcard.match(pattern, rule.pattern),
)

This returns the last matching rule, not the most specific one, making rule ordering critical and unintuitive.

Proposed Fix

  1. Use deep merge for the permission object across config layers (user → project → system)
  2. OR: Change config priority so user-level overrides project-level for permission rules (user knows best)
  3. Consider using specificity-based matching instead of findLast()

Environment

  • OpenCode: built from source (dev branch)
  • macOS 15.4 (Darwin 25.4.0)
  • Config locations verified: ~/.config/opencode/opencode.jsonc + .opencode/opencode.jsonc

🤖 Generated with Claude Code

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions