> For the complete documentation index, see [llms.txt](https://docs.openiam.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.openiam.com/administration-guide/pbac/policy-model.md).

# The policy model

What a policy, a version and an enforcement mode are, which OpenIAM entities Cedar can see, and the attribute rules that decide whether a policy evaluates or errors.

## What this is, and when you need it

Read this before authoring anything. Every trap in PBAC comes from one of three places: what a version is, which entities Cedar can see, and how Cedar treats an attribute that is not there. None of them is guessable from the screen.

## The objects

| Object               | What it is                                                                                                                                                               |
| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **Policy**           | A named header — name, description, lifecycle status, enforcement mode — plus a history of versions. The header is what you activate; it holds no policy text of its own |
| **Version**          | The Cedar text. Versions are **immutable and append-only**: every edit creates a new one, and nothing ever modifies an existing version                                  |
| **Status**           | `DRAFT` → `ACTIVE` → `DEPRECATED`. Only `ACTIVE` policies are loaded by the decision engine; a `DRAFT` is never evaluated, whatever else is set on it                    |
| **Enforcement mode** | How much live traffic an `ACTIVE` policy governs: `SHADOW`, `PARTIAL` or `FULL` — see [Roll a policy out safely](/administration-guide/pbac/roll-out-a-policy.md)        |
| **Effect**           | Cedar's `permit` or `forbid`                                                                                                                                             |
| **Priority**         | Orders policies when the engine reports which ones determined a decision. Lower wins. Defaults to `500`. It does **not** override *forbid wins*                          |
| **Decision**         | `ALLOW` or `DENY`, recorded as a `PBAC_REQUEST` audit event                                                                                                              |

**Appending a version never changes which version is in force.** The *current* pointer moves only when you select a version and choose **Make current** — which is also how a rollback works. That is the one explicit step in the model, and it is deliberate: authoring a fix does not deploy it.

## What Cedar can see

OpenIAM exposes its identity model to Cedar under the `Openiam` namespace.

| Cedar entity                                                                    | Represents                                                                                                                                                                                                                                                                                    |
| ------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `Openiam::User::Human`                                                          | A user. The principal of every request, except a tool call on the MCP server made by an agent acting alone                                                                                                                                                                                    |
| `Openiam::Group`, `Openiam::Role`, `Openiam::Organization`, `Openiam::Resource` | Resource-side entities. A user can also *be* the resource — for policies about who may modify whom                                                                                                                                                                                            |
| `Openiam::AccessRight::"<id>"`                                                  | An action: one of the access rights defined in your deployment                                                                                                                                                                                                                                |
| `Openiam::Action::"Impersonate"`                                                | An action: permission to [impersonate a user](/administration-guide/identities/impersonate-a-user.md). Its entity type is `Openiam::Action`, so it can only be tested in a `when` clause — Cedar refuses to parse a policy whose head names an action of any other type than `Action`         |
| `Openiam::UserManagement::Action::"<id>"`                                       | An action: one of the Webconsole [user-administration actions](/administration-guide/identities/grant-user-administration.md), such as `VIEW_USER`. Test it in a `when` clause, for the same reason as impersonation                                                                          |
| `Openiam::Nhi`                                                                  | A non-human identity: the resource of the [NHI management actions](/administration-guide/nhi/access-policies.md), and from 2026.10.2 the principal when an AI agent calls the MCP server on its own behalf, with its service user's entitlements as parents                                   |
| `Openiam::McpTool::Action::"<tool>"`                                            | An action: a tool of the MCP server, from 2026.10.2. The tools are grouped by scope, so `action in Openiam::McpTool::Action::"openiam:mcp:identity:read"` matches every identity tool; see [Decide MCP tool calls with policies](/administration-guide/federation/mcp-server/mcp-policies.md) |
| `Openiam::McpTool::"<tool>"`                                                    | A tool of the MCP server as a resource, for the tools that read no particular user, organization or entitlement                                                                                                                                                                               |

**Context supplied on every decision:**

| Attribute                   | Type          | Note                                                                                                                                                                                                                                                                                                                        |
| --------------------------- | ------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `context.contentProviderId` | string        | Which content provider the request arrived through                                                                                                                                                                                                                                                                          |
| `context.timeOfDay`         | hour, 0–23    | **UTC**, not the user's local time or the server's                                                                                                                                                                                                                                                                          |
| `context.dayOfWeek`         | ISO-8601, 1–7 | Monday is `1`, Sunday is `7`                                                                                                                                                                                                                                                                                                |
| `context.mcp`               | record        | From 2026.10.2. Present on every decision with default values, and filled in on a tool call to the MCP server: the tool, the client, the agent record and its sponsor, the arguments' names and the target kind. The fields are listed under [Context](/administration-guide/federation/mcp-server/mcp-policies.md#context) |

{% hint style="warning" %}
**`context.timeOfDay` is UTC.** A policy reading "working hours are 9 to 17" enforces 9–17 UTC for everybody, wherever they are. For an organization outside UTC this is almost never what was intended, and it fails quietly — the policy works, it just governs the wrong hours. Offset the numbers deliberately, and write the intended local window into the version's change comment so the next person knows the values are shifted on purpose.
{% endhint %}

## The attribute rule that causes most errors

**Cedar treats reading an absent attribute as an evaluation error, not as false.** A policy that reads `principal.employeeTypeId` on a user who has no employee type does not evaluate to false — it errors, and an errored evaluation is not a decision.

Guard every optional attribute:

{% code overflow="wrap" %}

```
principal has employeeTypeId && principal.employeeTypeId == "CONTRACTOR"
```

{% endcode %}

The form builder adds these guards for you. Hand-written Cedar is where this bites.

Always present, and readable without a guard: the entitlement lists, and the login and email lists.

## Entitlements as attributes

A user's entitlements arrive as list attributes — `groupIds`, `roleIds`, `organizationIds`, `resourceIds`. Each list holds two kinds of entry:

* the plain entity id — `"g-1"` — meaning membership;
* one `"<entityId>::<rightId>"` entry per access right held on that entity.

So `principal.groupIds.contains("g-1::read")` asks *"does this user hold **read** on group g-1"*, while `principal.groupIds.contains("g-1")` asks only whether they are a member.

Membership can also be tested with `in`, which walks the entitlement graph including transitive parents:

{% code overflow="wrap" %}

```
principal in Openiam::Group::"g-1"
```

{% endcode %}

Use `in` when inherited membership should count, and `contains` when it should not. They are not interchangeable, and the difference is invisible until someone inherits their way past a policy.

## Custom attributes are Cedar tags

Tenant-defined attributes are not fields — they are tags, and their values are **always lists of strings**, never scalars:

{% code overflow="wrap" %}

```
principal.hasTag("department") && principal.getTag("department").contains("engineering")
```

{% endcode %}

Writing `principal.getTag("department") == "engineering"` compiles and never matches, because the left side is a list. This is the second most common authoring mistake after the missing `has` guard.

## What the form builder can and cannot express

The builder supports **one policy statement with conditions two levels deep** — an OR of AND-groups. That covers most policies, and everything it produces is valid Cedar.

It does not cover `unless`, several statements in one version, or deeper nesting. A version using those cannot be rendered in the form; the Form tab says so and leaves the text alone rather than showing a lossy approximation. Such policies are edited on the Cedar text tab from then on.

## Related

* [Create a policy](/administration-guide/pbac/create-a-policy.md) — the model above, applied
* [Roll a policy out safely](/administration-guide/pbac/roll-out-a-policy.md) — what `ACTIVE` actually means for live traffic
* [The access model](/administration-guide/access/access-model.md) — where `groupIds`, `roleIds` and access rights come from
* [PBAC reference](/administration-guide/pbac/pbac-reference.md) — caching, which decides how quickly a change takes effect
* [Decide MCP tool calls with policies](/administration-guide/federation/mcp-server/mcp-policies.md) — the MCP vocabulary above, in full

*Applies to OpenIAM 2026.9.2. Last reviewed 20 October 2026.*


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.openiam.com/administration-guide/pbac/policy-model.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
