# `Nous.Permissions`
[🔗](https://github.com/nyo16/nous/blob/v0.17.1/lib/nous/permissions.ex#L1)

Tool-level permission policy engine.

Controls which tools can be used, which require user approval,
and which are denied entirely. This operates at the tool level —
for message-level input filtering, see `Nous.Plugins.InputGuard`.

## Quick Start

    # Use a preset policy
    policy = Nous.Permissions.default_policy()

    # Check tool access
    Nous.Permissions.blocked?(policy, "bash")
    #=> false

    Nous.Permissions.requires_approval?(policy, "bash")
    #=> true

    Nous.Permissions.requires_approval?(policy, "file_read")
    #=> false

    # Filter a tool list
    tools = [bash_tool, read_tool, write_tool]
    allowed = Nous.Permissions.filter_tools(policy, tools)

## Custom Policies

    policy = %Nous.Permissions.Policy{
      mode: :default,
      deny_names: MapSet.new(["bash"]),
      deny_prefixes: ["web_"],
      approval_required: MapSet.new(["file_write", "file_edit"])
    }

# `blocked?`

```elixir
@spec blocked?(Nous.Permissions.Policy.t(), String.t()) :: boolean()
```

Checks if a tool name is blocked by the policy.

Matches against deny_names (exact, case-insensitive) and
deny_prefixes (prefix match, case-insensitive).

## Examples

    iex> policy = %Nous.Permissions.Policy{deny_names: MapSet.new(["bash"]), deny_prefixes: ["web_"]}
    iex> Nous.Permissions.blocked?(policy, "bash")
    true
    iex> Nous.Permissions.blocked?(policy, "web_fetch")
    true
    iex> Nous.Permissions.blocked?(policy, "file_read")
    false

# `build_policy`

```elixir
@spec build_policy(keyword()) :: Nous.Permissions.Policy.t()
```

Creates a policy from keyword options.

## Options

  * `:mode` — `:default`, `:permissive`, or `:strict`
  * `:deny` — list of tool names to block
  * `:deny_prefixes` — list of prefixes to block (e.g. `["web_"]`)
  * `:approval_required` — list of tool names requiring approval
  * `:allow_unattended_execute` — when `true`, `:permissive` mode also drops
    the approval gate for `category: :execute` tools (e.g. `bash`). Defaults to
    `false`: even under `:permissive`, execute-class tools still require
    approval so one config choice can't turn an LLM into unattended RCE.

## Examples

    policy = Nous.Permissions.build_policy(
      mode: :default,
      deny: ["bash"],
      deny_prefixes: ["web_"],
      approval_required: ["file_write"]
    )

# `default_policy`

```elixir
@spec default_policy() :: Nous.Permissions.Policy.t()
```

Returns the default permission policy.

Read and search tools are open. Write and execute tools require approval.

# `filter_tools`

```elixir
@spec filter_tools(Nous.Permissions.Policy.t(), [Nous.Tool.t()]) :: [Nous.Tool.t()]
```

Filters a list of `Nous.Tool` structs, removing blocked tools.

## Examples

    tools = [bash_tool, read_tool, write_tool]
    policy = Nous.Permissions.build_policy(deny: ["bash"])
    filtered = Nous.Permissions.filter_tools(policy, tools)
    # Returns [read_tool, write_tool]

# `partition_tools`

```elixir
@spec partition_tools(Nous.Permissions.Policy.t(), [Nous.Tool.t()]) ::
  {[Nous.Tool.t()], [Nous.Tool.t()]}
```

Partitions tools into {allowed, blocked} based on the policy.

## Examples

    {allowed, denied} = Nous.Permissions.partition_tools(policy, tools)

# `permissive_policy`

```elixir
@spec permissive_policy() :: Nous.Permissions.Policy.t()
```

Returns a permissive policy where all tools are open.

# `requires_approval?`

```elixir
@spec requires_approval?(Nous.Permissions.Policy.t(), String.t()) :: boolean()
```

Checks if a tool requires approval under the policy.

## Examples

    iex> policy = Nous.Permissions.strict_policy()
    iex> Nous.Permissions.requires_approval?(policy, "file_read")
    true

    iex> policy = Nous.Permissions.permissive_policy()
    iex> Nous.Permissions.requires_approval?(policy, "bash")
    false

# `requires_approval?`

```elixir
@spec requires_approval?(Nous.Permissions.Policy.t(), String.t(), atom() | nil) ::
  boolean()
```

Checks if a tool requires approval, accounting for its `category`.

Identical to `requires_approval?/2` except that `category: :execute` tools
(e.g. `bash`) are NOT auto-approved under `:permissive` mode unless the policy
sets `allow_unattended_execute: true`. This keeps the single `:permissive`
switch from silently turning an LLM into unattended remote-code-execution: the
per-tool `requires_approval` flag and the policy already compose, but a custom
execute-class tool that relies on the policy for its gate would otherwise be
unguarded under `:permissive`.

`category` is the tool's declared `Nous.Tool` category (`:execute`, `:write`,
`:read`, …) or `nil`.

# `strict_policy`

```elixir
@spec strict_policy() :: Nous.Permissions.Policy.t()
```

Returns a strict policy where all tools require approval.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
