# `LivePanels.Canvas.Policy`
[🔗](https://github.com/livepanels/livepanels/blob/v0.1.0/lib/livepanels/canvas/policy.ex#L1)

Application policy contract for canvas commands.

Runtime authorization verifies LivePanels invariants such as payload shape,
actor role, item ownership, leases, and declared capabilities. A configured
policy module can reject otherwise valid commands with application rules and
attach decision metadata to accepted commands.

Configure a policy on the canvas runtime:

    on_mount {LivePanels.Canvas, pubsub: MyApp.PubSub, policy: MyApp.CanvasPolicy}

Policy modules implement `authorize/1`:

    defmodule MyApp.CanvasPolicy do
      @behaviour LivePanels.Canvas.Policy

      @impl true
      def authorize(%LivePanels.Canvas.Policy.Request{action: :remove_item} = request) do
        if MyApp.Workflows.locked?(request.canvas_id, request.item_ids, request.actor) do
          {:error, :workflow_locked}
        else
          :ok
        end
      end

      def authorize(_request), do: :ok
    end

Returning `:ok` accepts the command. Returning `{:ok, metadata}` accepts the
command and stores metadata under `:policy` in command metadata. Returning
`{:error, reason}` or `{:halt, reason}` rejects the command.

Policy callbacks run in the reducer/coordinator command path behind a timeout
boundary. Configure `timeout_ms:` when command acceptance checks need a
different bound:

    on_mount {LivePanels.Canvas,
      pubsub: MyApp.PubSub,
      policy: {MyApp.CanvasPolicy, timeout_ms: 2_000}}

A timeout rejects the LivePanels command. Policy callbacks should be bounded
acceptance checks, not application write transactions or side-effect hooks.

# `metadata`

```elixir
@type metadata() :: map() | keyword()
```

# `result`

```elixir
@type result() :: :ok | {:ok, metadata()} | {:error, term()} | {:halt, term()}
```

# `t`

```elixir
@type t() :: %LivePanels.Canvas.Policy{
  module: module() | nil,
  opts: map(),
  timeout_ms: timeout_ms()
}
```

# `timeout_ms`

```elixir
@type timeout_ms() :: pos_integer() | :infinity
```

# `authorize`

```elixir
@callback authorize(LivePanels.Canvas.Policy.Request.t()) :: result()
```

---

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