# How Cerbos evaluates requests

Understanding how Cerbos evaluates authorization requests helps you write correct policies and diagnose unexpected results.

## Default deny

Cerbos is deny-by-default. If no policy exists for the resource kind and version in the request, or if no rule within a matching policy grants access, the result is `EFFECT_DENY`. There is no concept of "no opinion" — the absence of an explicit allow is a deny.

## Policy lookup

When a request arrives, Cerbos looks up the resource policy matching the resource `kind` and `version` from the request.

If a `scope` is provided, Cerbos looks for the most specific scoped policy first, then walks up the scope hierarchy. For example, with scope `a.b.c`, Cerbos checks for policies in this order:

1. scope `a.b.c`
2. scope `a.b`
3. scope `a`
4. no scope

See [scoped policies](https://docs.cerbos.dev/cerbos/prerelease/policies/scoped_policies) for details on how scoped policy evaluation works.

## Rule matching

For each action in the request, Cerbos evaluates all rules in the matched policy. A rule matches when all of the following are true:

1. The rule’s `actions` match the requested action. Wildcards are supported and respect the `:` delimiter (e.g. `view:*` matches `view:public` but not `view`).
2. The rule’s `roles` or `derivedRoles` include at least one of the principal’s static roles or activated derived roles.
3. If the rule has a [condition](https://docs.cerbos.dev/cerbos/prerelease/policies/conditions), the condition evaluates to `true`.

## Conflict resolution

This is the most important part of the evaluation model.

|     |     |
| --- | --- |
|  | For a single role and action, if both ALLOW and DENY rules match, the result is **DENY**. Deny always takes precedence. |

However, if the principal has multiple roles and at least one of those roles evaluates to `EFFECT_ALLOW` for the action (with no `EFFECT_DENY` for that same role), the overall result is `EFFECT_ALLOW`. This prevents scenarios like an admin user being locked out because they also have a less-privileged role.

### Example: multiple roles

A principal has roles `["admin", "user"]`.

```yaml
rules:
  - actions: ["delete"]
    effect: EFFECT_ALLOW
    roles: ["admin"]
```

- Rule: ALLOW `admin` to `delete` — matches.
- No rule allows `user` to `delete`.
- Result: **EFFECT_ALLOW** — because the `admin` role has an allow.

### Example: deny overrides allow

A principal has role `["manager"]`.

```yaml
rules:
  - actions: ["approve"]
    effect: EFFECT_ALLOW
    roles: ["manager"]
    condition:
      match:
        expr: R.attr.status == "PENDING"

- actions: ["approve"]
    effect: EFFECT_DENY
    roles: ["manager"]
    condition:
      match:
        expr: R.attr.amount > 10000
```

If the resource has `status: "PENDING"` and `amount: 15000`, both rules match for the `manager` role. The result is **EFFECT_DENY** because deny takes precedence over allow for the same role.

## Derived roles

[Derived roles](https://docs.cerbos.dev/cerbos/prerelease/policies/derived_roles) are evaluated before resource policy rules. A principal is assigned derived roles based on the conditions in the derived roles policy. These derived roles can then be referenced in resource policy rules.

The derived roles policy must be imported into the resource policy via `importDerivedRoles` for it to take effect.

## Principal policies

[Principal policies](https://docs.cerbos.dev/cerbos/prerelease/policies/principal_policies) are evaluated **before** resource policies. When a principal policy matches the requesting principal, Cerbos evaluates its rules for the requested actions first. For each action where the principal policy produces an explicit `EFFECT_ALLOW` or `EFFECT_DENY`, that decision is final — the resource policy is not consulted for that action.

Actions the principal policy leaves undecided (no matching rule, or a matching rule whose condition did not hold) fall through to resource policy evaluation.

## Role policies

[Role policies](https://docs.cerbos.dev/cerbos/prerelease/policies/role_policies) apply as an **additive constraint** on top of resource policies. They do not produce `EFFECT_ALLOW` or `EFFECT_DENY` directly — instead, the `allowActions` list acts as an allow-list of actions permitted for that role on each resource. Any action not in the list is an implicit `EFFECT_DENY` for that role.

For an action to be allowed, it must be permitted by **both** the role policy and a matching [resource policy](https://docs.cerbos.dev/cerbos/prerelease/policies/resource_policies). A role policy can narrow access granted by a resource policy but cannot grant access the resource policy does not already allow — which is why a resource policy is always required.

If a role policy declares `parentRoles`, it inherits and narrows those roles' permissions recursively.

## Evaluation order summary

1. If a [principal policy](https://docs.cerbos.dev/cerbos/prerelease/policies/principal_policies) matches the requesting principal, evaluate it first. For each action with an explicit `EFFECT_ALLOW` or `EFFECT_DENY` from the principal policy, that is the final result and no further evaluation happens for that action.
2. For actions the principal policy did not decide, look up the resource policy for the resource kind and version (considering [scope](https://docs.cerbos.dev/cerbos/prerelease/policies/scoped_policies)).
3. Evaluate [derived roles](https://docs.cerbos.dev/cerbos/prerelease/policies/derived_roles) to determine which apply to the principal.
4. Evaluate all matching rules in the resource policy. If any [role policies](https://docs.cerbos.dev/cerbos/prerelease/policies/role_policies) apply to the principal’s roles, their `allowActions` lists further restrict which actions are permitted for those roles.
5. Apply conflict resolution: deny overrides allow for the same role; allow from any role wins across roles.
6. Any action without an explicit `EFFECT_ALLOW` is `EFFECT_DENY`.
