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.

One formula covers every capability, every policy, every integration.

The Formula

Capability + AgentAction + PolicyContext -> Policy -> Decision
A Capability is anything an agent can use: a tool, a model, a memory store, a database, an MCP server, a secret, another agent. An AgentAction is one attempted use of that capability, capturing identity, environment, input, and trace context. A PolicyContext is the clean interface the policy author uses to inspect the action. A Policy is a function that takes a PolicyContext and returns a Decision. A Decision is what the runtime enforces.

The Lifecycle

  1. Capability is invoked: a function call, tool use, or API call
  2. Brane intercepts the invocation before the function executes
  3. Brane creates an AgentAction record capturing who, what, where, and with what
  4. Brane builds a PolicyContext, the clean developer-facing view of the action
  5. Matching before_capability policies evaluate the context in priority order
  6. Policy returns a Decision: allow or deny, with more types planned
  7. If denied, Brane raises CapabilityDeniedError; the function never executes
  8. If allowed, the original function executes and returns output
  9. Brane creates an after-action record with the output included
  10. Matching after_capability policies evaluate; they can inspect the output
  11. If an after policy denies, Brane raises CapabilityDeniedError after execution
  12. If after policy allows, Brane returns the output to the caller

Developer Example

from brane import Decision, Runtime

runtime = Runtime(agent_id="support-agent", environment="prod", tenant_id="acme")

@runtime.capability(name="refund_customer", type="tool", risk="high")
def refund_customer(customer_id: str, amount_usd: float):
    return {"refunded": True, "amount": amount_usd}

@runtime.before_capability("refund_customer")
def limit_refund_amount(ctx):
    amount = ctx.arg("amount_usd", 0)
    if amount > 100:
        return Decision(type="deny", reason=f"Refund ${amount} exceeds $100 limit")
    return Decision(type="allow")

refund_customer("cust_123", 50.00)   # allowed
refund_customer("cust_456", 250.00)  # denied
At the refund_customer("cust_456", 250.00) call, Brane:
  1. Intercepts before the function runs
  2. Creates an AgentAction for support-agent in prod using refund_customer with amount_usd=250
  3. Builds a PolicyContext wrapping that action
  4. Finds limit_refund_amount, which matches refund_customer
  5. Calls the policy; ctx.arg("amount_usd") returns 250, which is over the limit
  6. Returns Decision(type="deny")
  7. Raises CapabilityDeniedError. The actual refund function never runs.

Security Team Example

@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 is not allowed in prod without an approved workflow",
        )
    return Decision(type="allow")
ctx.is_prod checks the runtime environment. ctx.is_high_risk checks the capability’s risk field. Neither requires knowing which specific capability was called; the policy works across the entire governed surface.

Why This Is The Right Boundary

The control boundary for agent systems is not the text they produce. It is the capabilities they use. Policy on prompts and outputs leaves a gap: the agent can still call a tool, write a file, or query a database before the output is inspected. Policy on capability use catches the action at the right moment, before the consequence occurs. The formula works the same regardless of the agent framework. LangGraph tools, CrewAI tools, OpenAI Agents SDK function tools, and MCP tool calls all reduce to the same AgentAction against the same capability. One policy runtime governs all of them.