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,:defineor both.:definebrings thepatchtool with it. Replaces the default's list, which has both.:rules- Relaxes the rules on the code the token submits. There are two,allow_defmacroandallow_dynamic_dispatch, both off unless set totrue.: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, soallow: [Kernel]brings backapply/2and the rest the default holds back.:deny- Modules the token's code may not call at all. A module in bothallowanddenyis 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
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.
Summary
Types
A module's grant: everything, a closed list, or all but.
A function name/arity pair, the grants' granularity.
The grant table. Absence means denied.
A built policy.
An MCP tool a policy may grant.
Functions
Builds a declared policy from its document, on top of the default.
The policy Beamlet ships: both tools, strict rules, the curated grants.
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 MCP tools a policy puts in a token's tool list, in name order.
Types
A module's grant: everything, a closed list, or all but.
A function name/arity pair, the grants' granularity.
The grant table. Absence means denied.
@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.
@type tool() :: :define | :eval
An MCP tool a policy may grant.
Functions
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.
@spec default() :: t()
The policy Beamlet ships: both tools, strict rules, the curated grants.
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.
@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.