Metadata-Version: 2.4
Name: brane-core
Version: 0.1.0a0
Summary: Local-first capability policy runtime for AI agents
Author: Brane
Requires-Python: >=3.11
Description-Content-Type: text/markdown

# Brane Core

Brane Core is a local-first capability policy runtime for Python agent tools.
It lets a developer declare capabilities, write central policies, intercept tool
calls, allow or deny actions, and inspect local audit events.

This alpha is intentionally small: sync Python callables, local memory/JSONL
audit, and allow/deny policy decisions.

## Install

```bash
pip install -e .
```

## Quickstart

```python
import brane

runtime = brane.Runtime(
    agent_id="support-agent",
    environment="prod",
    audit_path=".brane/audit/events.jsonl",
)

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

@runtime.before_capability("refund_customer")
def refund_policy(ctx):
    if ctx.arg("amount_usd") > 100:
        return brane.deny("Refund above $100 requires approval")
    return brane.allow()

refund_customer("cus_123", 50)
refund_customer("cus_123", 500)
```

The second call raises `brane.CapabilityDeniedError` and writes an audit event
with the action id, capability, policy, decision, and outcome.

## Decorator Capabilities

Use `@runtime.capability(...)` when you own the Python function.

```python
@runtime.capability(name="send_email", type="tool", risk="medium")
def send_email(to: str, body: str):
    return {"status": "sent"}
```

Brane registers the capability and returns an enforcing wrapper. The original
function is available as `send_email.__brane_original__` for adapter code that
needs direct execution after creating an explicit `InterceptRequest`.

## Wrapper Capabilities

Use `runtime.wrap_capability(...)` when the function comes from another package
or framework.

```python
def web_search(query: str):
    return {"results": []}

safe_web_search = runtime.wrap_capability(
    web_search,
    name="web_search",
    type="tool",
    risk="low",
)
```

## Policy Decorators

Policies receive a `PolicyContext`. They can inspect the normalized action,
capability metadata, runtime metadata, input arguments, output, tenant,
principal, environment, effect, side effects, and scopes.

```python
@runtime.before_capability("*", priority=0)
def tenant_boundary(ctx):
    input_tenant = ctx.arg("tenant_id")
    if input_tenant and ctx.tenant_id and input_tenant != ctx.tenant_id:
        return brane.deny("Cross-tenant action blocked")
    return brane.allow()
```

`before_capability` policies run before execution when the interception request
stage is `before_capability`. `after_capability` policies run after execution
when the request stage is `after_capability`.

## Audit Logs

Use `audit_path` for JSONL audit:

```python
runtime = brane.Runtime(
    agent_id="support-agent",
    audit_path=".brane/audit/events.jsonl",
)
```

Or use an in-memory sink for tests and demos:

```python
audit = brane.InMemoryAuditSink()
runtime = brane.Runtime(agent_id="support-agent", audit=audit)
```

Supported alpha outcomes:

- `denied_before_execution`
- `executed`
- `denied_after_execution`
- `execution_failed`

## Run Examples

```bash
python agents/support_refund_agent.py
python agents/enterprise_ops_agent.py
python agents/planner_driven_agent.py
```

The planner-driven example is the clearest alpha demo: a fake planner emits
runtime action plans, a generic executor routes them through Brane, and central
policies block large refunds, cross-tenant actions, billing ledger reads, and
unsafe customer-visible emails.

## Alpha Limitations

- Sync callables only
- Allow/deny only
- Local audit only
- No approvals yet
- No cloud yet
- No LangGraph adapter yet
- No async yet
- No secret broker yet
