# `Beamlet.Policy`
[🔗](https://github.com/aaronrussell/beamlet/blob/v0.1.0/lib/beamlet/policy.ex#L1)

A policy decides what a token's requests may do on your beamlet.

Declare policies in config, by name:

    config :beamlet,
      policies: [
        explorer: [
          tools: [:eval],
          allow: [{Host.Code, except: [remove: 1]}],
          deny: [Host.HTTP]
        ]
      ]

Then give one to a token, with `beamlet tokens.create laptop
--policy explorer` or on the consent page when a chat client
connects. A token with no policy named gets `default`.

A policy keeps an agent to what you meant it to do. It is a
guardrail, not a sandbox: code set on escaping it may succeed.

## Keys

Every policy starts from `default` and changes only what it names.

* `:tools` - The MCP tools the token gets: `:eval`, `:define` or
  both. `:define` brings the `patch` tool with it. Replaces the
  default's list, which has both.
* `:rules` - Relaxes the rules on the code the token submits.
  There are two, `allow_defmacro` and `allow_dynamic_dispatch`,
  both off unless set to `true`.
* `:allow` - Modules the token's code may call. A module alone
  grants all of it. `{Mod, only: [fun: 1]}` grants just the
  functions listed, and `{Mod, except: [fun: 1]}` all but those.
  An entry replaces whatever the default grants for that module,
  so `allow: [Kernel]` brings back `apply/2` and the rest the
  default holds back.
* `:deny` - Modules the token's code may not call at all. A
  module in both `allow` and `deny` is denied.

Tools and grants are separate. Without the `except`, `explorer`
above could not define modules but could still delete them with
`Host.Code.remove/1`.

Modules agents define need no `allow`. Every token can call them,
whichever token defined them.

## Applying and reading

Every module and function a policy names must exist, so a typo
stops the boot instead of granting nothing. A change takes a
restart. A policy in the data dir's `config.exs` replaces any
policy of the same name declared elsewhere
(`Beamlet.Config.Provider`).

`beamlet policies.show explorer` prints what a policy allows. Agent
code reads its own with `Host.Code.print_policy/0`.

> #### Relaxed rules reach every token {: .warning}
>
> `allow_defmacro` lets the token write macros. A macro writes code
> into every module that uses it, so other tokens end up running
> code this token wrote.
>
> `allow_dynamic_dispatch` lets code compute what it calls, as in
> `mod.fun()` with `mod` a variable. Beamlet cannot check such a
> call, so any module is within reach. Other tokens reach it too,
> through the modules this token defines.
>
> Give either only to a token you would trust with the whole
> beamlet.

# `entry`

```elixir
@type entry() :: :all | {:only, [fa()]} | {:except, [fa()]}
```

A module's grant: everything, a closed list, or all but.

# `fa`

```elixir
@type fa() :: {atom(), arity()}
```

A function name/arity pair, the grants' granularity.

# `grants`

```elixir
@type grants() :: %{required(module()) =&gt; entry()}
```

The grant table. Absence means denied.

# `t`

```elixir
@type t() :: %Beamlet.Policy{
  grants: grants(),
  name: String.t(),
  rules: %Beamlet.Policy.Rules{
    allow_defmacro: term(),
    allow_dynamic_dispatch: term()
  },
  tools: [tool()]
}
```

A built policy.

# `tool`

```elixir
@type tool() :: :define | :eval
```

An MCP tool a policy may grant.

# `build`

```elixir
@spec build(String.t() | atom(), keyword()) :: {:ok, t()} | {:error, String.t()}
```

Builds a declared policy from its document, on top of the default.

The name is a string or atom. The document is a keyword list with
any of `tools`, `rules`, `allow` and `deny`, as the moduledoc
describes. An error names the policy and the key at fault.

# `default`

```elixir
@spec default() :: t()
```

The policy Beamlet ships: both tools, strict rules, the curated grants.

# `render`

```elixir
@spec render(t()) :: String.t()
```

Renders the policy as an agent or operator reads it: name and
tools, the rules in force, the deliberate denials it has not
re-granted with their reasons, and the modules granted only in
part.

The only listable answer to "what is disallowed", since everything
absent from the grants is denied: the rendering covers the denials
that carry teaching copy rather than the unbounded rest.

# `tool_list`

```elixir
@spec tool_list(t()) :: [:define | :eval | :patch]
```

The MCP tools a policy puts in a token's tool list, in name order.

`define` is two tools in one: a policy granting it lists the
`define` and `patch` tools, since both write modules through the
same pipeline under the same limit.

---

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