Skip to content

[FEATURE]: Plugin API: race-safe, single-use permission admission #61645

Open

Description

@lloydie-t

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

We’re evaluating classifier-assisted permission recommendations in a plugin. We’re keeping it advisory-only because we haven’t established a supported way to bind a recommendation to current permissions at execution time.

Reviewing OpenCode 2.2.5, commit 9bbb8ce, we found:

  • packages/core/src/permission.ts:176–204 reads configured rules before awaiting permission hooks, without an apparent post-hook rule revalidation.
  • permission.ts:298–356 releases the selected pending request on an approving once reply without apparently re-evaluating its current rules. The subsequent revalidation loop protects other requests covered by an always approval.
  • packages/core/src/shell.ts:264–321 performs process spawning separately from permission preparation, without an apparent revision-bound admission capability.

These are source-review findings, not a reproduced exploit. We may have missed an existing host guarantee.

Recommendation: provide a host-owned, single-use permission admission mechanism that:

  1. Binds approval to the exact invocation, session, agent and complete permission-resource set.
  2. Invalidates pending approval when relevant policy, role, assignment or execution context changes.
  3. Rechecks authoritative configured denials and hard policies after asynchronous hooks or approval waits.
  4. Atomically validates and consumes approval at the execution-admission boundary.
  5. Preserves denials and falls back to the normal approval flow on missing, stale or invalid evidence—without creating persistent approval patterns.

Existing internal State.revision() machinery may provide part of the foundation, but we couldn’t identify a complete policy revision and compare-and-consume contract exposed to plugins.

Is there an existing supported mechanism that provides these guarantees? If not, would you consider exposing one?

We’re happy to share the detailed source assessment and proposed regression cases.

Activity

  1. opencodejr commented on Nov 4, 2026

    @opencodejr

    Thanks for the careful source review and the clear write-up.

    To restate the request: you'd like a host-owned, single-use permission admission mechanism. An approval would be bound to the exact invocation, session, agent and set of resources. It would become invalid when policy or context changes. Configured denies would be checked again after async hooks or approval waits. The approval would be validated and consumed atomically right before the tool runs. And when evidence is missing or stale, it would fall back to the normal approval flow without creating saved approvals.

    Your reading of packages/core/src/permission.ts matches the current code:

    • evaluateInput reads the configured and saved rules, then awaits the permission/evaluate hook and uses the effect it returns. Configured denies are not checked again afterwards.
    • reply releases the selected request on once/always without re-evaluating it. Only the other pending requests are checked again after an always reply.
    • The plugin API has no supported revision, lease or compare-and-consume contract.

    So as far as I can tell, no existing supported mechanism gives these guarantees today. A plugin can't add one either, because the check has to happen inside the host, right before the tool runs. Keeping your classifier advisory-only is the safe choice for now.

    Maintainers will consider this. Related issues:

    The regression cases you mentioned would be useful if you'd like to share them here.

  2. lloydie-t commented on Nov 4, 2026

    @lloydie-t
    Author

    Thanks for the clarification. Here are the proposed host-level regression cases. These are test proposals based on source review, not reproduced exploits or claims that the native guarantees have already been implemented/tested. Our plugin remains advisory-only.

    Use deterministic barriers and a fake/blocked spawner, with no real model requests or command execution:

    1. Policy changes during an asynchronous permission hook

      • Begin a check that initially permits the operation; pause an async hook before it returns an allow recommendation.
      • Introduce a matching configured denial, then release the hook.
      • Expect the current denial to win and the spawner to receive zero calls. Repeat for a hard-policy change and effective-agent/role restriction change.
    2. The selected pending request changes before once/always approval

      • Queue an ask, change the applicable rules to deny, then approve that same pending request.
      • Expect no admission despite the earlier request being approvable. Cover both the selected once and selected always request, not just other pending requests affected by an always save.
      • Retain existing regressions protecting those other requests. Unchanged, valid human approvals should continue to work. once must not create saved approval patterns.
    3. Policy changes between preparation and process admission

      • Pause after scanning/permission preparation but before the spawner accepts the operation. Change a covered revision and resume with the old admission.
      • Expect the stale admission to be rejected, with no spawn. Merely adding a second check earlier in preparation should not satisfy this test.
      • Also test the opposite ordering: when atomic admission precedes the policy update, document that the update governs subsequent admissions rather than implying retroactive revocation of a running process.
    4. Invocation/context changes invalidate the recommendation

      • Change the command, cwd, shell, effective agent, covered execution snapshot, resource aggregate, or approved assignment/delegation revision while a recommendation is pending.
      • Expect no reuse of the old admission: preserve a current deny, otherwise return to the normal approval flow.
      • Include external-directory checks as well as every scanner-produced shell resource; a safe prefix must not stand in for the complete operation. Any assignment/delegation record here is a requirement of the proposed mechanism, not an existing API we assume is available.
    5. Single-use and cross-call isolation

      • Run overlapping identical commands in different sessions/messages, including reused provider tool-call IDs and multiple permission evaluations within one tool call.
      • Deliver responses late, twice, or to the wrong evaluation, and attempt concurrent consumption of one admission.
      • Expect at most one admission for the exact owning host evaluation; no crossed response or copied/retired capability can authorize another operation.
    6. Cancellation, teardown and failure fallback

      • Cancel/close the session, dispose relevant registrations, restart the host, exhaust admission capacity, or fail the spawn after admission. Deliver a late response or attempt to reuse the old admission afterward.
      • Expect no usable leftover capability and no automatic retry grant. Also inject classifier timeout/unavailability, malformed output and model mismatch: none may create approval or weaken a known denial. Missing/partial revision or execution coverage must remain ineligible.

    The important acceptance boundary is current authoritative restriction checking plus single-use consumption at actual effect admission, not only at the permission-hook or reply endpoint. Existing internal State.revision() machinery looks potentially reusable, but revision coverage would need to include all relevant mutation paths; State.batch/uninterruptible effects alone should not be treated as a transaction over those paths.

    We have offline simulations of binding, replay and invalidation behavior. Those are not evidence of native atomicity. Binding reviewed script/path bytes to what actually executes is also a separate requirement; a policy admission token alone is not a filesystem/OS sandbox.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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