Docs/Policy Runtime/After Capability
Implemented — allow and deny only

After Capability Policies

Run after the capability executes. Inspect and control the output before it is returned.

Timing

An after_capability policy runs after the underlying function has completed and returned a result. The function has already executed. The policy inspects the output and decides whether to allow it through or block it.

# Timeline:
# agent calls capability
#   → Brane intercepts
#   → before_capability policies run → allow
#   → function executes, returns output
#   → after-action record created with output
#   → after_capability policies run   ← HERE
#     → allow: output returned to caller
#     → deny: CapabilityDeniedError raised (function already ran)
Side effects already occurred. When an after policy denies, the function has already executed. If the function had side effects (sent a message, modified a database, made an API call), those effects are not reversed. Use before policies to prevent unwanted side effects from occurring. Use after policies to inspect outputs and detect violations after execution.

Registration

@runtime.after_capability("capability_name")
def my_after_policy(ctx):
    # ctx.output is available here
    return Decision(type="allow")

Accessing the Output

In an after_capability policy, ctx.outputcontains the function's return value. This is the primary difference from before policies.

@runtime.after_capability("execute_sql")
def check_result_size(ctx):
    output = ctx.output
    rows = output.get("rows", []) if isinstance(output, dict) else []
    if len(rows) > 1000:
        return Decision(type="deny", reason="Result set too large (>1000 rows)")
    return Decision(type="allow")

Common Use Cases

Detect possible secret leakage:

@runtime.after_capability("call_model")
def check_for_secrets(ctx):
    output = str(ctx.output or "")
    secret_patterns = ["SECRET_KEY", "API_KEY", "password=", "token="]
    for pattern in secret_patterns:
        if pattern.lower() in output.lower():
            return Decision(type="deny", reason="Possible secret in model output")
    return Decision(type="allow")

Validate output schema:

@runtime.after_capability("get_customer_data")
def validate_customer_output(ctx):
    output = ctx.output
    if not isinstance(output, dict) or "customer_id" not in output:
        return Decision(type="deny", reason="Unexpected output shape from get_customer_data")
    return Decision(type="allow")

Block large result sets:

@runtime.after_capability("search_documents")
def block_large_results(ctx):
    results = ctx.output if isinstance(ctx.output, list) else []
    if len(results) > 500:
        return Decision(
            type="deny",
            reason=f"Search returned {len(results)} results, limit is 500",
        )
    return Decision(type="allow")

Detect PII in output (placeholder for future redact decision):

import re

SSN_PATTERN = re.compile(r"d{3}-d{2}-d{4}")

@runtime.after_capability("*")
def detect_pii(ctx):
    output_str = str(ctx.output or "")
    if SSN_PATTERN.search(output_str):
        # Today: deny. Future: return Decision(type="redact", ...)
        return Decision(type="deny", reason="SSN detected in output")
    return Decision(type="allow")

Deny Behavior

When an after policy denies:

  1. The function has already executed (side effects occurred)
  2. CapabilityDeniedError is raised
  3. The output is not returned to the caller
try:
    result = call_model("Tell me the STRIPE_SECRET_KEY")
except CapabilityDeniedError as e:
    print(e.reason)  # "Possible secret in model output"
    # result is never available to the caller

Future Decision Types

After policies currently support allow and deny. Planned:

  • redact — strip or mask fields from the output before returning it, without blocking
  • transform_output — mutate the output (enrich, sanitize, summarize) before returning
  • log_only — allow the output through but flag the action for review

These planned types address the limitation that today's after policy is binary: allow the output or block it entirely. Redaction allows the agent to receive a cleaned version without a hard failure.

Before vs. After

ConcernUse beforeUse after
Prevent unsafe input
Prevent side effects from occurring
Enforce identity or tenant rules
Inspect the actual output
Detect PII or secrets in output
Validate output schema
Block oversized result sets