# `Ash.Policy.Policy`
[🔗](https://github.com/ash-project/ash/blob/v3.25.2/lib/ash/policy/policy.ex#L5)

Represents a policy on an Ash.Resource

# `error_message`

```elixir
@type error_message() ::
  String.t()
  | (subject(), error_message_context() -&gt; String.t() | Exception.t())
```

# `error_message_context`

```elixir
@type error_message_context() :: %{
  resource: Ash.Resource.t(),
  action: Ash.Resource.Actions.action() | nil,
  actor: Ash.actor() | nil,
  domain: Ash.Domain.t() | nil,
  tenant: Ash.ToTenant.t() | nil
}
```

# `subject`

```elixir
@type subject() :: Ash.Query.t() | Ash.Changeset.t() | Ash.ActionInput.t()
```

# `t`

```elixir
@type t() :: %Ash.Policy.Policy{
  __spark_metadata__: Spark.Dsl.Entity.spark_meta(),
  access_type: :strict | :filter | :runtime,
  bypass?: boolean(),
  condition: nil | Ash.Policy.Check.ref() | [Ash.Policy.Check.ref()],
  description: String.t() | nil,
  error_message: error_message() | nil,
  policies: [Ash.Policy.Check.t()]
}
```

# `evaluate`

```elixir
@spec evaluate(t(), map()) :: :authorized | :forbidden | :unknown
```

Evaluates a policy's check chain against already-computed facts and returns
its decision.

Walks checks in source order. The first decisive check fixes the decision:

  * `authorize_if X` with `X` true → `:authorized`
  * `authorize_unless X` with `X` false → `:authorized`
  * `forbid_if X` with `X` true → `:forbidden`
  * `forbid_unless X` with `X` false → `:forbidden`

Anything else leaves the state at `:unknown` and the walk continues. If no
check is decisive, the policy ends at `:unknown` — callers that need a
binary "did this policy deny?" should treat `:unknown` as `:forbidden`,
matching Ash's "if nothing authorized, the request is forbidden" rule.

Reads from the supplied `facts` map only. Strict checks are **not** invoked,
so this is safe to call from error-construction paths where rerunning a
strict check would surface unrelated errors (e.g. missing calculation
arguments). For pre-flight call sites that need lazy strict-check
evaluation, use the private `policy_fails_statically?/2` in the policy
authorizer.

# `expression`

```elixir
@spec expression(
  policies ::
    t() | Ash.Policy.FieldPolicy.t() | [t() | Ash.Policy.FieldPolicy.t()],
  check_context :: Ash.Policy.Check.context()
) :: Crux.Expression.t(Ash.Policy.Check.ref())
```

# `fetch_fact`

```elixir
@spec fetch_fact(
  facts :: map(),
  check :: Ash.Policy.Check.t() | Ash.Policy.Check.ref()
) ::
  {:ok, Crux.Expression.t(Ash.Policy.Check.ref())} | :error
```

# `fetch_or_strict_check_fact`

```elixir
@spec fetch_or_strict_check_fact(
  Ash.Policy.Authorizer.t(),
  Ash.Policy.Check.t() | Ash.Policy.Check.ref()
) ::
  {:ok, Crux.Expression.t(Ash.Policy.Check.ref()), Ash.Policy.Authorizer.t()}
  | {:error, Ash.Policy.Authorizer.t()}
```

# `responsible_for_forbidden`

```elixir
@spec responsible_for_forbidden([t() | Ash.Policy.FieldPolicy.t()], map()) ::
  {t(), :forbidden | :unknown} | nil
```

Returns the first non-bypass policy considered responsible for a forbidden
outcome, along with its computed state.

A policy is considered responsible if:

  * Its `condition` applies given the facts (no condition resolves to
    `{:ok, false}`),
  * It is not a bypass (bypasses don't deny on their own; an unsatisfied
    bypass just falls through to the next policy), and
  * Its `evaluate/2` decision is `:forbidden` (explicit deny) or `:unknown`
    (no check authorized).

Within those, explicit `:forbidden` is preferred over `:unknown`. Returns
`nil` when no non-bypass policy is responsible.

# `scenario_options`

```elixir
@spec scenario_options(check_context :: Ash.Policy.Check.context()) ::
  Crux.opts(Ash.Policy.Check.ref())
```

Default Options for Crux scenarios

# `solve`

```elixir
@spec solve(authorizer :: Ash.Policy.Authorizer.t()) ::
  {:ok, boolean() | [map()], Ash.Policy.Authorizer.t()}
  | {:error, Ash.Policy.Authorizer.t(), Ash.Error.t()}
```

---

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