feat(policy): native expiration / single-use bounds for approved policy grants (expires_at, max_uses)
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 8.7k
- Forks
- 1.3k
- Avg merge
- 2d 11h
- Merged PRs (30d)
- 253
Description
Summary
OpenShell has no native way to make an approved policy grant time-bounded or single-use. Once a draft/chunk is approved and applied, the resulting allow rule stays in effect until a client explicitly calls the API to revoke it. There is no expires_at, ttl, or max_uses on the grant itself, so OpenShell will neither auto-expire nor one-shot-limit it.
Motivation
We are integrating an external policy-recommendation service with OpenShell (the external service produces a recommendation; OpenShell remains the enforcement authority). Many of these grants should be tightly bounded in time and usage — e.g. "allow this sandbox one egress call to an approved external endpoint, valid for the next 10 minutes."
Today we emulate that with apply-then-revoke: approve/apply the rule, then issue a follow-up API call to remove it after the window or after a single use. It works, but it pushes all single-use/expiry/replay enforcement into the external service and leaves a gap — nothing at the OpenShell layer bounds the grant to exactly one use or a fixed lifetime before the external caller revokes it.
Broader use cases:
- Just-in-time, least-privilege grants that auto-expire.
- Single-use grants scoped to one action (submit one envelope, run one query).
- Break-glass / exception approvals that should lapse automatically.
- Any third-party policy advisor that wants OpenShell to enforce the time/use bound rather than trusting the advisor to call back and revoke.
Proposed capability
Add optional expiration / usage bounds to an approved policy grant (the draft/chunk or its resulting rule):
expires_at(absolute) orttl(relative): the grant auto-lapses (reverts to prior state) when it passes.max_uses: the grant is consumed after N matching requests (default unlimited).- optional
nonce/ idempotency key: prevents replay of the same grant.
Enforced server-side, so the grant is bounded even if the external service never calls back. Expiry vs. manual revocation should be distinguishable in status/events.
Prior art
#2591 — feat(sandbox): add server-managed expiration leases applies the same server-managed TTL/expiration-lease pattern at the sandbox level (a reconciler auto-terminates expired sandboxes). This request is the policy-grant analog: the same lease model, but the grant auto-reverts instead of the sandbox being deleted.
Current workaround
Apply-then-revoke from the external decision layer: our decision store tracks expiry/nonce/uses and issues a follow-up deny/drop API call. Functional, but the guarantee lives outside OpenShell and is subject to a revoke race.
Acceptance criteria (rough)
- A grant can be created with
expires_at/ttland/ormax_uses(and optionallynonce). - OpenShell auto-reverts/denies once the bound is hit, with no external call required.
- Status and events distinguish auto-expiry from manual revocation.
Related: #2591 (sandbox expiration leases); the draft/chunk approval flow (ApproveDraftChunk, policy reload).
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by reading the server-managed expiration lease pattern in #2591, then trace the draft/chunk approval flow at ApproveDraftChunk and policy reload. The work is complete when grants support the proposed expiration, usage, and optional nonce bounds, automatically revert or deny server-side, and distinguish auto-expiry from manual revocation in status and events.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- api, authorization, security
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100