> ## Documentation Index
> Fetch the complete documentation index at: https://afk.arpan.sh/llms.txt
> Use this file to discover all available pages before exploring further.

# Security Model

> Security boundaries, policy engine, and production hardening.

AFK implements security through **four boundaries** — policy engine, tool runtime, A2A/MCP bridges, and sandbox. Each boundary enforces least-privilege defaults and requires explicit opt-in for elevated permissions.

## Security boundaries

```mermaid theme={null}
flowchart TB
    subgraph B1["Boundary 1: Policy Engine"]
        Policy["Policy Rules"]
        Approval["Human Approval"]
    end

    subgraph B2["Boundary 2: Tool Runtime"]
        Validation["Arg Validation"]
        Sanitize["Output Sanitization"]
        Sandbox["Sandbox Profiles"]
    end

    subgraph B3["Boundary 3: A2A / MCP"]
        Auth["Authentication"]
        AuthZ["Authorization"]
        RateLimit["Rate Limiting"]
    end

    subgraph B4["Boundary 4: Agent Sandbox"]
        CostLimit["Cost Limits"]
        StepLimit["Step Limits"]
        TimeLimit["Time Limits"]
    end

    Agent --> B1
    B1 --> B2
    Agent --> B3
    Agent --> B4
```

<AccordionGroup>
  <Accordion title="Boundary 1: Policy Engine" icon="gavel">
    Gate tool calls and agent actions with configurable rules.

    ```python theme={null}
    from afk.agents import PolicyEngine, PolicyRule

    policy = PolicyEngine(rules=[
        PolicyRule(
            rule_id="deny-admin",
            condition=lambda e: e.tool_name and "admin" in e.tool_name,
            action="deny",
            reason="Admin tools are disabled",
        ),
        PolicyRule(
            rule_id="approve-writes",
            condition=lambda e: e.tool_name and "write" in e.tool_name,
            action="request_approval",
            reason="Write operations need human approval",
        ),
    ])

    runner = Runner(policy_engine=policy)
    ```

    **Actions:** `allow` (default), `deny`, `request_approval`, `request_user_input`
  </Accordion>

  <Accordion title="Boundary 2: Tool Runtime" icon="wrench">
    Every tool call passes through validation, policy checks, and output sanitization.

    ```python theme={null}
    runner = Runner(
        config=RunnerConfig(
            sanitize_tool_output=True,        # Strip prompt injection vectors
            tool_output_max_chars=8000,       # Truncate oversized responses
        ),
    )
    ```

    **Sandbox profiles** are configured at the runner level, not per-tool:

    ```python theme={null}
    class CodeArgs(BaseModel):
        code: str

    @tool(args_model=CodeArgs, name="run_code", description="Execute code.")
    def run_code(args: CodeArgs) -> dict:
        # Execution constraints are enforced by the runner and policy engine
        ...
    ```
  </Accordion>

  <Accordion title="Boundary 3: A2A / MCP Bridges" icon="network-wired">
    External communication requires authentication and per-caller authorization.

    ```python theme={null}
    from afk.agents import A2AServiceHost, APIKeyA2AAuthProvider

    auth = APIKeyA2AAuthProvider(
        keys={"system-a": "token-abc"},
        server_secret="hmac-secret-for-key-hashing",
    )

    server = A2AServiceHost(
        agents={"analyzer": agent},
        runner_factory=lambda: Runner(),
        auth_provider=auth,
    )
    ```
  </Accordion>

  <Accordion title="Boundary 4: Agent Sandbox" icon="shield">
    Hard limits prevent runaway agents.

    ```python theme={null}
    from afk.agents import FailSafeConfig

    agent = Agent(
        ...,
        fail_safe=FailSafeConfig(
            max_steps=10,
            max_tool_calls=5,
            max_total_cost_usd=0.50,
            max_wall_time_s=60.0,
        ),
    )
    ```
  </Accordion>
</AccordionGroup>

## Default posture

AFK defaults to **least privilege**:

| Setting                  | Default                                                                      | Meaning                               |
| ------------------------ | ---------------------------------------------------------------------------- | ------------------------------------- |
| Tool policy              | `allow`                                                                      | Tools run unless explicitly denied    |
| Tool output sanitization | `True`                                                                       | Output is sanitized by default        |
| A2A authentication       | Required                                                                     | No unauthenticated A2A                |
| MCP authentication       | Required                                                                     | No unauthenticated MCP                |
| Cost limits              | None (<Icon icon="triangle-exclamation" iconType="solid" color="#f59e0b" />) | **You must set `max_total_cost_usd`** |
| Sandbox                  | None                                                                         | Tools run in the host process         |

<Warning>
  **Cost limits are not set by default.** Always configure `max_total_cost_usd`
  in production to prevent runaway spending.
</Warning>

## Production hardening checklist

| Area           | Action                                          | Status                                    |
| -------------- | ----------------------------------------------- | ----------------------------------------- |
| **Cost**       | Set `max_total_cost_usd` on all agents          | <Icon icon="square" iconType="regular" /> |
| **Cost**       | Set `max_steps` and `max_tool_calls`            | <Icon icon="square" iconType="regular" /> |
| **Policy**     | Add `deny` rules for admin/destructive tools    | <Icon icon="square" iconType="regular" /> |
| **Policy**     | Add `request_approval` for mutating operations  | <Icon icon="square" iconType="regular" /> |
| **Tools**      | Enable `sanitize_tool_output=True`              | <Icon icon="square" iconType="regular" /> |
| **Tools**      | Set `tool_output_max_chars`                     | <Icon icon="square" iconType="regular" /> |
| **Tools**      | Use sandbox profiles for code execution         | <Icon icon="square" iconType="regular" /> |
| **A2A/MCP**    | Configure auth providers with valid tokens      | <Icon icon="square" iconType="regular" /> |
| **A2A/MCP**    | Set per-caller agent access lists               | <Icon icon="square" iconType="regular" /> |
| **A2A/MCP**    | Enable rate limiting                            | <Icon icon="square" iconType="regular" /> |
| **Secrets**    | Store API keys in environment variables         | <Icon icon="square" iconType="regular" /> |
| **Secrets**    | Use secret scope isolation per tool call        | <Icon icon="square" iconType="regular" /> |
| **Monitoring** | Configure telemetry exporter (OTEL)             | <Icon icon="square" iconType="regular" /> |
| **Monitoring** | Set up alerts for error rate and cost anomalies | <Icon icon="square" iconType="regular" /> |

## Secret isolation

AFK recommends isolating secrets at the environment level. Use separate environment scopes and the runner's `ToolContext.metadata` to control which credentials are available to each tool:

```python theme={null}
from pydantic import BaseModel
from afk.tools import tool, ToolContext

class QueryArgs(BaseModel):
    sql: str

@tool(args_model=QueryArgs, name="query_db", description="Query the database.")
def query_db(args: QueryArgs, ctx: ToolContext) -> dict:
    db_url = ctx.metadata.get("db_url")  # ← Injected via runner context
    return execute_query(db_url, args.sql)
```

## Threat model overview

| Threat                  | Mitigation                                  |
| ----------------------- | ------------------------------------------- |
| **Prompt injection**    | Output sanitization, input validation       |
| **Runaway agents**      | Cost limits, step limits, wall time         |
| **Tool abuse**          | Policy engine, sandbox profiles             |
| **Unauthorized access** | A2A/MCP auth, per-caller authorization      |
| **Secret leakage**      | Secret scope isolation, output sanitization |
| **Cost explosion**      | `max_total_cost_usd`, circuit breakers      |

## Next steps

<CardGroup cols={2}>
  <Card title="Failure Policy Matrix" icon="table" href="/library/failure-policy-matrix">
    How errors flow through the system.
  </Card>

  <Card title="Building with AI" icon="hammer" href="/library/building-with-ai">
    Production playbook and anti-patterns.
  </Card>
</CardGroup>
