Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.brane.membranelabs.org/llms.txt

Use this file to discover all available pages before exploring further.

Run before the capability executes. This is the most important policy stage.

Timing

A before_capability policy runs after the call is intercepted but before the underlying function executes. If the policy denies, CapabilityDeniedError is raised and the function never runs. The consequence does not occur.
agent calls capability
  -> Brane intercepts
  -> AgentAction created
  -> PolicyContext built
  -> before_capability policies run
    -> allow: function executes
    -> deny: CapabilityDeniedError raised, function does not run

Registration

@runtime.before_capability("capability_name")
def my_policy(ctx):
    return Decision(type="allow")

What Is Available

In a before_capability policy, ctx.output is always None; the function has not run yet.
  • ctx.capability
  • ctx.arg("name")
  • ctx.agent_id, ctx.principal_id, ctx.tenant_id
  • ctx.is_prod, ctx.is_high_risk
  • ctx.agent_has_scope("scope")

Common Use Cases

Block unsafe SQL input:
@runtime.before_capability("execute_sql")
def read_only_sql(ctx):
    query = ctx.arg("query", "").lower().strip()
    if not query.startswith("select"):
        return Decision(type="deny", reason="Only SELECT queries are allowed")
    return Decision(type="allow")
Block high-risk tools in production:
@runtime.before_capability("*")
def block_high_risk_in_prod(ctx):
    if ctx.is_prod and ctx.is_high_risk:
        return Decision(
            type="deny",
            reason="High-risk capability use requires an approved workflow in prod",
        )
    return Decision(type="allow")
Enforce a financial limit:
@runtime.before_capability("refund_customer")
def refund_amount_limit(ctx):
    amount = ctx.arg("amount_usd", 0)
    limit = get_tenant_limit(ctx.tenant_id)
    if amount > limit:
        return Decision(type="deny", reason=f"Refund ${amount} exceeds limit of ${limit}")
    return Decision(type="allow")
Check scopes:
@runtime.before_capability("refund_customer")
def require_refund_scope(ctx):
    if not ctx.agent_has_scope("refunds:create"):
        return Decision(type="deny", reason="Agent requires refunds:create scope")
    return Decision(type="allow")

Deny Behavior

When any before policy returns Decision(type="deny"):
  1. The runtime stops evaluating remaining before policies
  2. The underlying function is not called
  3. CapabilityDeniedError is raised with the denial reason
from brane import CapabilityDeniedError

try:
    execute_sql("delete from customers")
except CapabilityDeniedError as e:
    print(e.reason)
    print(e.policy_name)
    print(e.action_id)

Policy Order

Multiple before policies on the same capability run in descending priority order. Deny wins: if any policy denies, the action is blocked regardless of other policies. Wildcard policies ("*") run alongside exact-match policies.

Future Decision Types

  • approval_required: pause until a human approves
  • transform_input: mutate the input before execution
  • route: redirect to a different capability or provider
  • sandbox: execute with restricted access
  • log_only: allow but flag for review