anomalyco / anomalyco/opencode

[FEATURE]: Replace order-dependent (first-match) permission evaluation with set-based inheritance (specifcity and precedence)

Open
#40,805 4 comments 1 reaction 1 assignee View on GitHub

@kitlangton is already working on this.

Since Aug 6, 2026.

Dominant language
TypeScript
Stars
209k
Forks
27.5k
PR merge metrics
PR metrics pending

Description

Feature hasn't been suggested before.
  • I have verified this feature I'm about to request hasn't been suggested before.
Describe the enhancement you want to request

OpenCode’s permission engine evaluates bash permissions using a sequential "last-match-wins" evaluation model driven by dictionary merging and rule array iteration.

While simple to implement, this approach creates severe operational friction, maintenance bloat, and safety risks when managing non-trivial CLI permissions across multiple sub-agents:

  1. No Grouping or Macro Capabilities: Security policies require explicitly listing every command, sub-command, and flag variation (e.g., repeating dozens of git, docker, npm, and rtk variations across every agent file).

  2. Fragile Rule Ordering: Evaluation relies strictly on document or dictionary insertion order rather than pattern specificity. Accidentally moving a catch-all pattern (git* or *) below a specific override (such as git checkout -- *) silently invalidates the finer-grained restriction.

  3. Ambiguous Inheritance Semantics: Agent inheritance via extends performs a shallow dictionary merge where child rules append after parent rules. Because there is no formal concept of additive delegation versus subtractive boundary enforcement, child agents overwrite parent keys based purely on position. This makes it impossible to predictably build either strict Default-Allow / Subtractive (Boundary) or Default-Deny / Additive (Capability) security topologies.

Security Risk: Host Execution Without Sandboxing

OpenCode agents execute bash commands directly on the host operating system without container isolation or OS-level sandboxing by default.

In an unsandboxed host environment, permission configuration errors carry severe consequences:

  • Unintended data loss (e.g., recursive directory deletion rm -rf *, uncommitted state discards via git checkout .*).

  • Non-self-contained system alterations (e.g., package manager modifications via apt, pip install, systemctl modifications, or privilege escalation via sudo).

  • Exfiltration or unexpected network side-effects via curl or ssh.

A permission engine that relies on fragile array ordering and ambiguous inheritance mechanics increases the risk of accidental host compromise.

Real-World Access Control Paradigms

Established access control systems handle permissions using explicit, deterministic evaluation rules rather than document ordering:

  • AWS IAM (Identity and Access Management): Uses Permissions Boundaries (Service Control Policies) for subtractive restriction alongside Identity Policies for additive capabilities. An explicit Deny in a boundary hard-caps child elevation.

  • Kubernetes RBAC: Additive / Least Privilege Model: Operations start denied by default. Roles grant specific capabilities (verbs on resources). Child/bound roles additively expand access without order-dependent array bugs.

  • Linux Security Modules (AppArmor / SELinux): Subtractive Confinement: Profiles strictly limit capabilities. Specific path rules automatically take precedence over broad wildcards regardless of line order in the profile file.

  • Network Firewalls & Policy Engines (Open Policy Agent / Rego): Evaluate ASTs where rule matching prioritizes Pattern Specificity over declaration sequence.

Proposed Solution

Redesign OpenCode's permission engine around a deterministic, multi-tiered resolution model that supports both additive and subtractive configuration styles.

1. Command Grouping (Macros / Aliases)

Allow defining reusable command arrays in the root opencode.json so agents can reference whole categories of actions rather than maintaining exhaustive flag lists.

2. Pattern Specificity Engine (Glob Trie / AST)

Eliminate line-order sensitivity. When a command is executed, match all applicable globs and resolve conflicts using pattern specificity. Exact subcommands (git branch --delete*) take precedence over broader subcommands (git branch*), which take precedence over global wildcards (git* or *).

3. Clear Rule Resolution Hierarchy

Resolve overlapping permission decisions using a deterministic 3-step evaluation order:

  1. Specificity First: The rule matching the most specific glob pattern always wins (e.g., rm -rf / beats @destructive-git-safe or *).
  2. Locality / Inheritance Scope: When glob patterns are identical in specificity, the most local rule wins (e.g., a child agent's rule for rm * overrides an inherited parent or global rule for rm *).
  3. Configurable Verb Precedence (Fallback): When rules conflict at the exact same specificity and scope level, apply the policy's resolution strategy (e.g., Deny > Ask > Allow in strict mode, or Allow > Ask > Deny in permissive mode).
4. Explicit Inheritance Modes (mode: additive vs mode: subtractive)

To remove ambiguity, formalize permission inheritance in child agents using an explicit mode directive:

  • mode: additive (Capability Grants / Default-Deny):

    • The parent establishes the baseline bounds.
    • The child uses mode: additive to widen access (e.g., elevating @destructive-git-safe from ask $\rightarrow$ allow).
  • mode: subtractive (Guardrails / Default-Allow):

    • The parent provides broad access.
    • The child uses mode: subtractive to narrow access (e.g., downgrading @destructive from allow $\rightarrow$ deny).
  • Enforceable Boundaries (sealed rules):

    • Rules declared in opencode.json or parent agents using sealed: true (or a seal: block) create a permission ceiling/floor that child agents cannot override, regardless of inheritance mode or local specificity.
Proposed Configuration Examples
Root Configuration (opencode.jsonc)

Define macro groups, baseline defaults, and un-overrideable hard boundaries:

{
  "$schema": "https://opencode.ai/config.json",
  "permission": {
    // Reusable command groups
    "groups": {
      "@read-only": [
        "rtk git status*", "rtk git log*", "rtk git diff*", "rtk ls*", "rtk rg*", "pwd"
      ],
      "@mutation": [
        "bun build*", "bun install*", "rtk cargo build*", "mkdir *", "touch *"
      ],
      "@destructive-git-safe": [
        "cp *", "rtk git add*", "git rm*", "mv *", "rm *"
      ],
      "@destructive": [
        "apt *", "sudo *", "rtk git checkout -- *", "git clean*", "rm -rf *"
      ]
    },

    "bash": {
      "default": "deny",

      // Universal baseline permissions
      "rules": {
        "@read-only": "allow",
        "@mutation": "deny",
        "@destructive-git-safe": "deny",
        "@destructive": "deny"
      },

      // Sealed boundaries that no agent or sub-agent can override
      "seal": {
        "sudo *": "deny",
        "rm -rf /": "deny"
      }
    }
  }
}

Additive Inheritance Example: Building Capabilities Top-Down

In an additive setup, the base agent starts restricted, and child agents selectively elevate capabilities.

Base Agent (.opencode/agents/adhoc.yaml)
name: adhoc
description: Ad-hoc runner with intermediate interactive capabilities
mode: subagent

permission:
  bash:
    mode: additive # Explicitly state that rules in this block elevate privileges
    rules:
      "@mutation": allow
      "@destructive-git-safe": ask
      "@destructive": ask

Child Agent (.opencode/agents/coder.yaml)

coder extends adhoc. Because it specifies mode: additive, it additively expands git-safe operations from ask to allow, while specific pattern overrides (git push --force*) protect critical workflows regardless of declaration order.

name: coder
description: Primary development agent
mode: subagent
extends: adhoc

permission:
  bash:
    mode: additive # Widens permissions inherited from 'adhoc'
    rules:
      # Capability Elevation: Elevates '@destructive-git-safe' from 'ask' -> 'allow'
      "@destructive-git-safe": allow

      # Specific glob override (Specificity Engine ensures this beats '@destructive-git-safe')
      "git push --force*": ask

Subtractive Inheritance Example: Applying Guardrails Bottom-Up

In a subtractive setup, a parent agent starts with broad permissions, and specialized sub-agents narrow the blast radius.

Base Autonomous Agent (.opencode/agents/power-user.yaml)
name: power-user
description: High-privilege agent for automated environment setup
mode: subagent

permission:
  bash:
    mode: additive
    rules:
      "@mutation": allow
      "@destructive-git-safe": allow
      "@destructive": allow

Specialized Restricted Child (.opencode/agents/reviewer.yaml)

reviewer inherits broad power from power-user, but uses mode: subtractive to enforce narrow security guardrails for safe code review.

name: reviewer
description: Code review sub-agent constrained to safe operations
mode: subagent
extends: power-user

permission:
  bash:
    mode: subtractive # Narrows/restricts permissions inherited from 'power-user'
    rules:
      # Restricts mutations to interactive prompts
      "@mutation": ask

      # Hard-denies destructive actions regardless of parent 'allow'
      "@destructive-git-safe": deny
      "@destructive": deny

Expected Impact
  • Determinism: Eliminates hidden bugs caused by TOML key sorting or JSON rule array placement.
  • Flexibility: Cleanly supports both Default-Deny additive capability workflows and Default-Allow subtractive boundary workflows via explicit mode declarations.
  • Maintainability: Reduces agent frontmatter definitions by 80–90% through reusable command groups.
  • Host Safety: Guarantees that explicit system safety rules (e.g., sudo *, rm -rf /) sealed at the global layer cannot be accidentally bypassed by sub-agent extensions.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.