Skip to main content
← Back to All Posts

Capability Leases for AI Agents That Need Temporary Access

May 6, 2026•11 min read•Security & Reliability
Capability Leases for AI Agents That Need Temporary Access

A lot of agent demos quietly assume the dangerous part away. The model can run the deployment script, hit the production database, or call the billing API because the tool is just there.

That works right up until a prompt is malformed, a tool output gets weird, or a retry loop keeps reusing access that should have expired ten minutes ago.

The safer pattern is not never giving the agent power. It is giving the agent power briefly, for one reason, with a receipt. Capability leases are short-lived grants that let an agent do one bounded thing, then disappear.

Why this matters

Permanent tool permissions are easy to wire and hard to trust. In production systems, the real problem is not whether the model is smart. It is whether access survives longer than the task that justified it.

  • deploy approvals for CI agents
  • temporary write access to a ticketing system
  • one-off production read queries during incident response
  • short admin windows for migration or rollback helpers

Architecture or workflow overview

Visual plan
  • Hero: dark banner with lease artifact, policy check, and revoke steps
  • Diagram: approval service issuing a short-lived lease token through a policy engine
  • Terminal visual: grant and revoke audit events
  • Comparison table: permanent permissions vs session scopes vs capability leases
Human approval
or policy gate
→
Lease issuer
and policy service
→
Agent run
uses scoped tool
flowchart LR
    U[Human approval or policy gate] --> P[Policy service]
    P --> L[Lease issuer]
    L --> A[Agent run]
    A --> T[Scoped tool call]
    T --> O[Audit log]
    L --> X[Auto expiry or revoke]
    X --> T
  1. The agent requests a capability for a specific action.
  2. A policy service checks actor, task, scope, and time budget.
  3. The lease issuer returns a short-lived token or signed document.
  4. The tool verifies the lease before every privileged operation.
  5. The lease expires or gets revoked, and the tool stops honoring it.

Implementation details

1) Define the lease like a real contract

A lease should be inspectable and boring. If you cannot explain it to an auditor in one minute, it is too loose.

{
  "lease_id": "lease_01hxqj4k6v9",
  "subject": "agent-run-88421",
  "tool": "deploy.prod",
  "allowed_actions": ["release:create", "release:status"],
  "resource_constraints": {
    "service": "payments-api",
    "environment": "staging"
  },
  "issued_at": "2026-05-06T12:00:00Z",
  "expires_at": "2026-05-06T12:12:00Z",
  "approved_by": "human:anirudh",
  "reason": "promote already-built artifact for smoke test",
  "signature": "base64-ed25519-signature"
}

Useful fields: the subject ties access to one run, allowed_actions narrows the tool surface, resource_constraints prevent environment drift, and expires_at forces cleanup even if the worker crashes.

What I would not do: issue a generic admin=true lease and hope downstream code behaves.

2) Verify at the tool boundary, not only in the orchestrator

Central policy helps, but the tool still needs to fail closed. Otherwise an old token can leak through a forgotten side path.

import { verifyLeaseSignature, isExpired } from './lease-crypto';
import type { Lease } from './types';

export async function authorizeLease(
  lease: Lease,
  action: string,
  target: { service: string; environment: string }
) {
  if (!verifyLeaseSignature(lease)) throw new Error('invalid lease signature');
  if (isExpired(lease.expires_at)) throw new Error('lease expired');
  if (!lease.allowed_actions.includes(action)) throw new Error('action denied');
  if (lease.resource_constraints.service !== target.service) throw new Error('service denied');
  if (lease.resource_constraints.environment !== target.environment) throw new Error('environment denied');

  return true;
}

3) Keep the issuance path small and observable

Lease issuance tends to sprawl because teams pack in too many escape hatches. I prefer one small endpoint with explicit defaults.

from datetime import datetime, timedelta, timezone

MAX_TTL_SECONDS = 900


def issue_lease(request, approver_id):
    ttl = min(request.get("ttl_seconds", 300), MAX_TTL_SECONDS)
    now = datetime.now(timezone.utc)

    lease = {
        "lease_id": new_lease_id(),
        "subject": request["subject"],
        "tool": request["tool"],
        "allowed_actions": request["allowed_actions"],
        "resource_constraints": request["resource_constraints"],
        "issued_at": now.isoformat(),
        "expires_at": (now + timedelta(seconds=ttl)).isoformat(),
        "approved_by": approver_id,
        "reason": request["reason"],
    }

    write_audit_event("lease.issued", lease)
    return sign_lease(lease)

The important design choice is the max TTL. Teams almost always start too long. Five to fifteen minutes is enough for most dangerous tasks.

4) Show the operator what changed

If the human cannot see what was granted, approval becomes theater.

$ clawctl lease inspect lease_01hxqj4k6v9
subject: agent-run-88421
scope: deploy.prod -> release:create,release:status
resource: payments-api / staging
approved-by: human:anirudh
expires-in: 08m14s
status: active

$ clawctl lease revoke lease_01hxqj4k6v9
status: revoked
reason: rollout completed
PatternGood atMain riskMy take
Permanent tool credentialsSimplicityHidden blast radius, stale accessFine for read-only low-risk tools, bad default for write paths
Session-scoped credentialsSlightly better containmentSessions outlive tasks, accidental reuseBetter than permanent, still too broad for risky actions
Capability leasesNarrow, explainable accessMore plumbing and policy workBest default for dangerous tools

What went wrong and the tradeoffs

Failure mode 1: the lease outlived the queue

One ugly bug pattern is when a worker retries a job after a long delay, but the old lease is still cached somewhere. The job looks resumable and suddenly performs a stale action.

Fix: bind the lease to both a run ID and a monotonic attempt ID, then reject mismatches.

Failure mode 2: humans approve scopes they cannot parse

If the approval card says “Grant elevated tool access,” people will click yes because they want the task finished.

Fix: show exact action verbs, target resource, TTL, and reason. Small text details matter here.

Failure mode 3: revocation exists on paper but not in the tool

I have seen systems add a revoke button while the downstream tool never checks the revocation ledger after first validation.

Fix: either make leases short enough that revocation is rare, or check revocation on every state-changing operation.

Security and reliability concerns
  • Signed leases are better than opaque booleans in memory.
  • Clock skew matters if TTLs are short, so keep servers NTP-sane.
  • Audit logs need append-only or tamper-evident storage if these leases gate production writes.
  • If a tool fans out to sub-tools, decide whether the lease can delegate. My default is no.

Practical checklist

  • [ ] Scope the lease to a run, not a whole assistant identity
  • [ ] Limit actions to verbs the human can understand
  • [ ] Add explicit resource constraints like environment, repo, or service
  • [ ] Set a hard max TTL and keep it short
  • [ ] Verify the lease at the privileged tool boundary
  • [ ] Log issuance, use, expiry, and revoke events
  • [ ] Show approval details before the human clicks yes
  • [ ] Refuse delegation unless you have a strong reason to allow it

Conclusion

Capability leases add friction, but it is the useful kind. They turn “the agent can do this” into “the agent can do this, for this reason, until this time, and we can prove it.” For real systems, that is a much better deal.

AI AgentsSecurityAccess ControlToolingReliability
Browse more posts