An unhandled error has occurred. Reload 🗙

Capability-Based Security for LLMs

Security ModelsCapabilities

Capability-Based Security for LLMs

When it comes to securing AI agents, most teams reach for what they know: Role-Based Access Control (RBAC). It's the standard for web applications, enterprise systems, and cloud infrastructure. But RBAC was designed for human users with predictable behavior patterns—not for AI agents that generate actions dynamically, operate autonomously, and need permissions that change based on context.

Capability-based security offers a fundamentally better model for AI authorization. Instead of asking "What role does this agent have?" it asks "What specific capabilities does this agent hold right now?"

This shift from identity-based to token-based authorization is what makes AI agents both powerful and safe.

Understanding RBAC: Why It Works for Humans

Role-Based Access Control is simple and elegant. You define roles, assign permissions to roles, and assign roles to users.

# Traditional RBAC
roles:
  customer_support:
    permissions:
      - read_customers
      - create_tickets
      - update_tickets

  sales_rep:
    permissions:
      - read_customers
      - create_quotes
      - update_opportunities

  admin:
    permissions:
      - read_customers
      - create_customers
      - delete_customers
      - manage_users
      - system_config

users:
  alice:
    roles: [customer_support]
  bob:
    roles: [sales_rep]
  carol:
    roles: [admin]

RBAC works for humans because:

  1. Roles are stable - Alice is always a support rep during her shift
  2. Permissions are predictable - Support reps always need the same access
  3. Context rarely changes - Alice's permissions don't depend on the time of day, the customer she's helping, or how many tickets she's processed today
  4. Humans self-regulate - Alice won't read every customer record just because she can

Why RBAC Fails for AI Agents

AI agents break every assumption RBAC relies on.

Problem 1: Agents Need Dynamic Permissions

RBAC assigns static permissions at deployment time. But AI agents need permissions that change based on:

  • Who they're helping: Access only the current customer's data
  • What they're doing: Different constraints for reads vs. writes
  • When they're acting: Tighter limits outside business hours
  • How much they've done: Rate limits based on cumulative usage
# RBAC can't express this:
"Agent can read customer data, but ONLY for the customer
 currently being served, ONLY during business hours,
 ONLY if the agent hasn't exceeded 100 reads today,
 and ONLY columns that aren't sensitive."

# RBAC gives you:
permissions: ["read_customers"]
# Which means: read ANY customer, ANY time, ANY column, unlimited

Problem 2: Roles Are Too Coarse

RBAC groups permissions into roles. But agents often need unique combinations that don't fit neatly into roles.

Example: A customer support agent that can:

  • Read customer data (like a support rep)
  • Process refunds under $500 (like a billing agent)
  • Send emails to company domains (like a communications agent)
  • Cannot modify customer records (unlike a support rep)
  • Cannot process refunds over $500 (unlike a billing agent)

With RBAC:

# Option 1: Create a custom role (role explosion)
roles:
  ai_support_limited_billing_restricted_email:
    permissions: ["read_customers", "refund_under_500", "email_company_only"]

# Option 2: Assign multiple roles (over-permission)
agent_roles: ["support", "billing", "communications"]
# But now the agent has ALL permissions from ALL three roles

Neither option works well. Custom roles lead to role explosion. Multiple roles lead to over-permissioning.

Problem 3: No Constraint Enforcement

RBAC says "you can do this." It doesn't say "you can do this, but only under these conditions."

# RBAC
permission: process_refund  # No limit on amount, frequency, or conditions

# What you actually need:
permission: process_refund
constraints:
  maxAmount: 500
  maxPerDay: 10
  requireApproval: amount > 100
  onlyForCustomer: "{{session.customer_id}}"
  notDuring: "maintenance_window"

RBAC has no mechanism for constraints.

Problem 4: No Delegation Model

In multi-agent systems, agents need to delegate specific capabilities to other agents. RBAC has no concept of delegation.

# Manager agent needs to delegate "read_customer" to worker agent
# But only for customer #12345, only for 10 minutes

# RBAC: Assign the worker agent the same role
# Problem: Worker now has ALL permissions of that role, permanently

# Capabilities: Delegate a specific, scoped, time-limited token
# Worker gets exactly what it needs, nothing more

Problem 5: Revocation Is All-or-Nothing

With RBAC, you revoke an entire role. You can't revoke a single permission without changing the role definition (affecting all agents with that role).

# Agent misbehaves with email sending
# RBAC: Revoke "support" role → Agent loses ALL support permissions
# Capability: Revoke email capability → Agent keeps other permissions

Capability-Based Security: A Better Model

Capability-based security replaces roles with tokens that embody specific permissions.

Core Concepts

1. A capability is an unforgeable token that grants specific access

capability_token:
  id: "cap_abc123"
  agent: "support-bot-01"
  granted: "2026-02-15T09:00:00Z"
  expires: "2026-02-15T17:00:00Z"

  capabilities:
    - action: read_customer
      resource: "customer://id:{{session.customer_id}}"
      constraints:
        columns: ["name", "email", "order_history"]
        maxRows: 50

    - action: process_refund
      resource: "order://customer:{{session.customer_id}}/*"
      constraints:
        maxAmount: 500
        maxPerDay: 10
        requireApproval: "amount > 100"

    - action: send_email
      resource: "email://domain:@company.com"
      constraints:
        maxPerHour: 20
        blockSensitiveData: true

2. Capabilities are context-aware

The same agent gets different capabilities based on context:

# Morning shift: Full capabilities
morning_token = act.issue_token(
    agent="support-bot",
    context={"shift": "morning", "customer_id": "12345"},
    policy="support-full"
)

# After hours: Read-only capabilities
afterhours_token = act.issue_token(
    agent="support-bot",
    context={"shift": "after_hours", "customer_id": "12345"},
    policy="support-readonly"
)

# High-risk customer: Enhanced restrictions
highrisk_token = act.issue_token(
    agent="support-bot",
    context={"shift": "morning", "customer_id": "vip_99", "risk": "high"},
    policy="support-restricted"
)

3. Capabilities can be delegated with attenuation

When one agent delegates to another, the delegated capability can only be equal or more restricted—never more permissive.

# Manager agent has broad capabilities
manager_cap = {
    "action": "read_customer",
    "resource": "customer://region:US/*",
    "constraints": {"maxRows": 1000}
}

# Manager delegates to worker with restrictions
worker_cap = manager_cap.attenuate({
    "resource": "customer://region:US/state:CA/*",  # More restricted
    "constraints": {"maxRows": 100},  # More restricted
    "expires": "10m"  # Time-limited
})

# Worker CANNOT escalate beyond what manager delegated

4. Capabilities are independently revocable

# Revoke ONLY the email capability
act.revoke_capability(token_id="cap_abc123", action="send_email")
# Agent can still read customers and process refunds

# Revoke ALL capabilities for an agent
act.revoke_all(agent="support-bot-01")
# Instant, global effect

RBAC vs. Capabilities: Side-by-Side Comparison

| Aspect | RBAC | Capability-Based (ACT) | |--------|------|----------------------| | Permission model | Role → Permissions | Token → Specific capabilities | | Granularity | Action-level | Action + Resource + Constraints | | Context awareness | None (static) | Full (dynamic per-request) | | Delegation | Not supported | Built-in with attenuation | | Revocation | All-or-nothing (role) | Per-capability granularity | | Constraint support | None | Amounts, rates, time, conditions | | Scaling | Role explosion | Policy composition | | Audit trail | Role-level | Action-level with full context | | Time-based | Manual role changes | Built-in expiration |

Real-World Scenarios

Scenario 1: E-commerce Customer Support

RBAC approach:

role: customer_support
permissions: [read_customers, read_orders, create_tickets, process_refunds]
# Problems:
# - Can read ANY customer (not just current one)
# - Can process ANY refund amount
# - No rate limits
# - Same permissions 24/7

Capability approach:

token:
  capabilities:
    - action: read_customer
      resource: "customer://id:{{session.customer_id}}"
      constraints: {excludeColumns: ["ssn", "payment_info"]}

    - action: read_order
      resource: "order://customer:{{session.customer_id}}/*"
      constraints: {maxRows: 50}

    - action: process_refund
      resource: "order://customer:{{session.customer_id}}/*"
      constraints: {maxAmount: 500, maxPerDay: 5, requireApproval: "amount > 100"}

  expires: "8h"
  context: {shift: "day", tier: "standard"}

Scenario 2: Financial Analysis Agent

RBAC approach:

role: financial_analyst
permissions: [read_accounts, read_transactions, generate_reports]
# Problems:
# - Can see ALL accounts (compliance violation)
# - No limit on data volume
# - Can generate unlimited reports

Capability approach:

token:
  capabilities:
    - action: read_account
      resource: "account://portfolio:{{agent.assigned_portfolio}}"
      constraints:
        columns: ["id", "name", "balance", "type"]
        excludeColumns: ["tax_id", "beneficiary_ssn"]

    - action: read_transaction
      resource: "transaction://account:{{agent.assigned_accounts}}/*"
      constraints:
        dateRange: "last_90_days"
        maxRows: 10000

    - action: generate_report
      resource: "report://portfolio:{{agent.assigned_portfolio}}"
      constraints:
        maxPerDay: 10
        format: ["pdf", "csv"]
        classification: "internal_only"

  expires: "1d"
  context: {compliance_level: "SOX", reviewer: "[email protected]"}

Scenario 3: Multi-Agent Research System

RBAC approach:

# All agents get the same role
role: researcher
permissions: [web_search, read_documents, write_documents]
# Problems:
# - All agents have same permissions
# - No isolation between agents
# - No delegation tracking

Capability approach:

# Each agent gets specific capabilities
coordinator_token:
  capabilities:
    - action: web_search
      constraints: {maxQueries: 100, safeDomains: true}
    - action: delegate
      constraints: {maxDelegationDepth: 2, attenuationRequired: true}

researcher_token:
  capabilities:
    - action: web_search
      constraints: {maxQueries: 20}  # Attenuated from coordinator
    - action: read_document
      resource: "doc://project:{{project.id}}/*"

writer_token:
  capabilities:
    - action: write_document
      resource: "doc://project:{{project.id}}/drafts/*"
      constraints: {maxLength: 10000, requireReview: true}

Implementing Capability-Based Security with ACT

Step 1: Define Your Capability Policies

# policies/support-agent.yaml
policy:
  name: "customer-support-standard"
  version: "2.0"

  capabilities:
    read_customer:
      resources: ["customer://id:{{session.customer_id}}"]
      constraints:
        columns: ["name", "email", "phone", "order_history"]
        maxRows: 50
        rateLimit: "200/hour"

    create_ticket:
      resources: ["ticket://agent:{{agent.id}}/*"]
      constraints:
        maxOpen: 10
        requiredFields: ["subject", "description", "priority"]

    send_email:
      resources: ["email://domain:@company.com"]
      constraints:
        maxPerHour: 20
        maxRecipients: 5
        blockSensitiveData: true

  circuit_breaker:
    maxViolations: 3
    window: "5m"
    suspendDuration: "30m"

  audit:
    logAll: true
    retainDays: 90

Step 2: Issue Capability Tokens

from act_sdk import ACT

act = ACT(api_key=os.getenv("ACT_API_KEY"))

def create_agent_session(agent_id, customer_id, shift):
    """Issue a capability token for an agent session."""
    token = act.issue_token(
        agent=agent_id,
        policy="customer-support-standard",
        context={
            "session.customer_id": customer_id,
            "agent.id": agent_id,
            "shift": shift
        },
        ttl="8h"
    )
    return token

Step 3: Validate Actions Against Capabilities

def execute_agent_action(token, action, resource, params):
    """Execute an action only if the agent has the capability."""
    validation = act.validate(
        token=token,
        action=action,
        resource=resource,
        context=params
    )

    if validation.allowed:
        result = perform_action(action, resource, params)
        return {"status": "success", "data": result}
    else:
        log_capability_violation(token, action, resource, validation)
        return {"status": "denied", "reason": validation.reason}

Step 4: Handle Delegation

def delegate_capability(parent_token, child_agent, capabilities, ttl):
    """Delegate specific capabilities to a child agent."""
    # ACT ensures delegation can only attenuate, never escalate
    child_token = act.delegate(
        parent_token=parent_token,
        child_agent=child_agent,
        capabilities=capabilities,
        ttl=ttl  # Must be <= parent's remaining TTL
    )

    # Log the delegation chain
    log_delegation(
        parent=parent_token.agent_id,
        child=child_agent,
        capabilities=capabilities,
        expires=child_token.expires
    )

    return child_token

Migration Guide: From RBAC to Capabilities

Phase 1: Audit Existing Roles

# Document what each role actually allows
for role in existing_roles:
    print(f"Role: {role.name}")
    print(f"  Permissions: {role.permissions}")
    print(f"  Agents using: {role.assigned_agents}")
    print(f"  Actual usage: {analyze_audit_logs(role)}")
    print(f"  Over-permissioned: {identify_unused_permissions(role)}")

Phase 2: Create Equivalent Policies

For each role, create ACT policies that match actual usage (not granted permissions):

# Old RBAC role
role: customer_support
permissions: [read_customers, read_orders, create_tickets, 
              update_tickets, process_refunds, send_emails]

# New ACT policy (based on actual usage analysis)
policy: customer-support-v1
capabilities:
  read_customer:
    resources: ["customer://id:{{session.customer_id}}"]
    constraints: {maxRows: 50}
  read_order:
    resources: ["order://customer:{{session.customer_id}}/*"]
  create_ticket:
    resources: ["ticket://agent:{{agent.id}}/*"]
  process_refund:
    resources: ["order://customer:{{session.customer_id}}/*"]
    constraints: {maxAmount: 500, maxPerDay: 5}
  send_email:
    resources: ["email://domain:@company.com"]
    constraints: {maxPerHour: 20}

Phase 3: Deploy in Monitor Mode

Run ACT alongside RBAC without blocking:

# Monitor mode: validate but don't block
validation = act.validate(token, action, resource, context)
if not validation.allowed:
    log_would_have_blocked(action, resource, validation.reason)
# Still execute via RBAC

# After 2 weeks, review logs:
# - False positives (legitimate actions that would be blocked)
# - True positives (dangerous actions that should be blocked)

Phase 4: Switch to Enforcement

Once policies are tuned, enable enforcement:

# Enforcement mode: validate and block
validation = act.validate(token, action, resource, context)
if validation.allowed:
    execute(action, resource)
else:
    block_and_log(action, resource, validation.reason)

Conclusion

RBAC was built for a world of human users with predictable behavior. AI agents live in a different world—one of dynamic actions, autonomous decisions, and context-dependent permissions.

Capability-based security provides what AI agents need:

  • Dynamic permissions that adapt to context
  • Fine-grained control at the action + resource + constraint level
  • Secure delegation with automatic attenuation
  • Granular revocation without affecting other capabilities
  • Complete audit trails for every capability used

The transition from RBAC to capabilities isn't just a security upgrade—it's a fundamental shift that makes AI agents governable, auditable, and safe for production.


Implement capability-based security for your AI agents Get Started with ACT →

Related articles: