> ## Documentation Index
> Fetch the complete documentation index at: https://docs.bedrock.quarry-systems.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Permission Evaluation

> How Bedrock decides if an action is allowed

## Overview

When your application asks "Can this subject do this action?", Bedrock's evaluation engine processes the request through a series of steps to produce a decision.

## The Evaluation Input

Every permission check requires an evaluation input:

```typescript theme={null}
interface BedrockEvaluateInput {
  actor: BedrockSubjectRef;        // Who is performing the action
  onBehalfOf?: BedrockSubjectRef;  // Optional: acting on behalf of another subject
  scopeId: string;                 // Where the action is happening
  action: string;                  // What action is being performed
  resource?: BedrockResourceRef;   // What resource is being accessed
  context?: Record<string, unknown>; // Additional context for conditions
  includeResourceTags?: boolean;   // Auto-load resource tags (default: true)
}
```

### Basic Example

```typescript theme={null}
const input = {
  actor: { subjectId: "subject_jane", subjectType: "user" },
  scopeId: "scope_engineering",
  action: "write",
  resource: { resourceType: "document", resourcePattern: "*" }
};

const decision = await bedrock.evaluate(input);
// { allowed: true, matches: [...], explanation: "..." }
```

## Evaluation Steps

The engine works through several legs to reach a decision—roughly in this order:

1. **Reachability gate** *(when resource-scope gating is enabled)* — if a concrete resource is not reachable in the requested scope, deny outright.
2. **Delegation grant** *(when `onBehalfOf` is set)* — an active delegation grant authorizing the actor to act for the principal in this scope must exist, or the request is denied (`NO_DELEGATION_GRANT`) before any RBAC runs.
3. **Resource policies** — policies targeting the resource (and any collections it matches) are evaluated **before** RBAC, sorted by priority; a matching `allow` or `deny` decides. Policies can grant access beyond RBAC, and a policy `deny` can override an ownership grant.
4. **Role-based permissions (RBAC)** — resolve the subject's grants, composed across the scope chain per the request scope's [`permissionMode`](/concepts/scope-types) (default `override` = that scope only), apply [scope overrides](/concepts/overrides), match action + resource pattern, and evaluate any [conditions](/concepts/conditional-permissions) fail-closed.
5. **Ownership** — if RBAC did not grant and a concrete resource is present, an active `owns(subject, resource, role)` row grants that owner-role's matching permissions (also routed through overrides and fail-closed conditions).
6. **Resource hierarchy** — still denied? If the resource has ancestors linked with `cascade: 'inherit'`, a parent resource can grant access.

When acting on behalf of another subject, the **actor leg** and the **principal leg** are each evaluated this way and the results intersected (see [Delegation](#delegation-on-behalf-of)).

<Note>
  These are not all strict priority tiers—delegation runs only for on-behalf-of
  requests, and the hierarchy fallback runs only when direct RBAC denies **and** a
  concrete resource is present. A resource-policy `deny` is the strongest single
  signal for locking down a sensitive resource.
</Note>

## The Decision Output

```typescript theme={null}
interface BedrockDecision {
  allowed: boolean;                    // Final result
  matches: BedrockPermissionMatch[];   // Permissions that contributed
  explanation?: string;                // Human-readable reason
  
  // Policy evaluation (new)
  evaluatedPolicy?: BedrockResourcePolicy;  // Policy that decided (if any)
  decidedByPolicy?: boolean;           // Was decision made by a policy?
  
  // Resource context
  evaluatedResource?: BedrockResource; // Resolved resource
  evaluatedResourceType?: BedrockResourceType;
  inheritedFrom?: string;              // Ancestor resource ID if inherited
  resourceTags?: BedrockTag[];         // Tags on the resource
  
  // Actor context
  evaluatedActor?: BedrockSubjectRef;  // Echo of actor
  evaluatedOnBehalfOf?: BedrockSubjectRef; // Echo of principal
  usedDelegation?: boolean;            // Was onBehalfOf used?
  delegationId?: string;               // Delegation reference if applicable
  
  evaluatedContext?: Record<string, unknown>; // Final context used
}
```

## Step-by-Step Example

Let's trace through a real evaluation:

### Setup

```
Organization: Acme Corp
├── Team: Engineering
│   └── Project: Backend API
│       └── Environment: Production

Roles at Acme Corp:
- Admin (permissions: read, write, delete, manage)
- Editor (permissions: read, write)
- Viewer (permissions: read)

Jane has:
- Membership in Production (memberships are scope-specific—she needs one
  at the exact scope she is being evaluated in)
- Role assignment: Editor

Override:
- "write" permission disabled in Production
```

### Evaluation Request

```typescript theme={null}
const input = {
  actor: { subjectId: "subject_jane", subjectType: "user" },
  scopeId: "scope_production",  // Production environment
  action: "write",
  resource: { resourceType: "document", resourcePattern: "*" }
};
```

### Evaluation Trace

```
1. Resolve Actor: subject_jane
   └── Look up memberships at scope_production (exact scope requested)
   └── Found membership_jane_prod at scope_production

2. Collect Roles: 
   └── role_editor (via membership_jane_prod)

3. Gather Permissions:
   └── perm_read (document:read:*)
   └── perm_write (document:write:*)

4. Apply Overrides:
   └── Found: perm_write disabled in scope_production
   └── Removing perm_write from effective permissions

5. Match Action & Resource:
   └── Looking for action="write", resourceType="document"
   └── No matching permissions (perm_write was removed)

6. Evaluate Conditions:
   └── N/A (no matches)

7. Return Decision:
   └── allowed: false
   └── explanation: "No permission for write:document in scope scope_production"
```

## Delegation (On Behalf Of)

Agents or services can act on behalf of users:

```typescript theme={null}
const input = {
  actor: { subjectId: "subject_agent", subjectType: "agent" },
  onBehalfOf: { subjectId: "subject_jane", subjectType: "user" },
  scopeId: "scope_engineering",
  action: "read",
  resource: { resourceType: "document", resourcePattern: "*" }
};
```

When `onBehalfOf` is provided, delegation is **co-authorization**, not authority-borrowing:

1. An **active delegation grant** authorizing the actor to act for the principal in this scope must exist—otherwise the request is denied (`NO_DELEGATION_GRANT`) before any permission check.
2. The actor must have the permission (the **actor leg**).
3. The principal (`onBehalfOf`) must also have the permission (the **principal leg**).
4. The allow is the **intersection**—both legs must pass, and neither party can exceed the other. See [Delegation](/delegation) for how to create grants.

```typescript theme={null}
const decision = await bedrock.evaluate(input);
// decision.usedDelegation = true
// decision.evaluatedActor = { subjectId: "subject_agent", ... }
// decision.evaluatedOnBehalfOf = { subjectId: "subject_jane", ... }
```

## Resource-Based Evaluation

When evaluating against a specific resource:

```typescript theme={null}
const input = {
  actor: { subjectId: "subject_jane", subjectType: "user" },
  scopeId: "scope_engineering",
  action: "read",
  resource: {
    resourceId: "resource_doc_123",  // Specific resource
    // OR
    externalResourceId: "my-doc-123",
    resourceType: "document"
  },
  includeResourceTags: true  // Load tags for conditional evaluation
};
```

The engine will:

1. Look up the resource
2. Load its tags (if `includeResourceTags` is true)
3. Include resource data in the evaluation context
4. Evaluate any conditional permissions against this context

## Conditional Evaluation

Conditions are attached to the **role-permission edge** (`condition`), not to the permission itself, and are evaluated against the context:

```typescript theme={null}
// Role-permission edge with a condition
{
  "roleId": "role_editor",
  "permissionId": "perm_read_document",
  "condition": {
    "in": [{"var": "subject.meta.department"}, {"var": "tags.departments"}]
  }
}

// Evaluation input
const input = {
  actor: { subjectId: "subject_jane", subjectType: "user" },
  scopeId: "scope_org",
  action: "read",
  resource: { resourceId: "resource_finance_report" },
  context: {
    subject: {
      meta: { department: "Finance" }
    }
  }
};

// Resource has tag: departments = ["Finance", "Accounting"]
// Condition evaluates: "Finance" in ["Finance", "Accounting"] = true
// Result: allowed = true
```

## Context Variables

The evaluation context includes:

| Variable                       | Source                                                                | Example                   |
| ------------------------------ | --------------------------------------------------------------------- | ------------------------- |
| `subject.id`                   | Actor                                                                 | `"subject_jane"`          |
| `subject.subjectType`          | Actor                                                                 | `"user"`                  |
| `subject.externalId`           | Actor                                                                 | `"user-123"`              |
| `subject.meta.*`               | Actor metadata                                                        | `subject.meta.department` |
| `resource.id`                  | Resource                                                              | `"resource_123"`          |
| `resource.externalResourceId`  | Resource                                                              | `"my-doc-123"`            |
| `resource.resourceType`        | Resource type key                                                     | `"document"`              |
| `resource.resourceTypeId`      | Resource type ID                                                      | `"rt_doc"`                |
| `resource.ownerScopeId`        | Owning scope                                                          | `"scope_eng"`             |
| `resource.displayName`         | Resource                                                              | `"Q3 Report"`             |
| `tags.*`                       | Resource tags (identifier→label)                                      | `tags.classification`     |
| `tagList`                      | Resource tags (array of objects)                                      | `tagList[].identifier`    |
| `<your keys>`                  | Custom `context` keys, spread at **top level** (not under `context.`) | `ip`, `region`            |
| `time.hour` / `time.dayOfWeek` | Trusted server clock (caller-supplied `time`/`now` is **ignored**)    | `time.hour`               |

<Note>
  The resolved `subject`, `resource`, `tags`, and `tagList` overwrite any
  same-named keys you pass in `context`. There is no `resource.meta` or
  `resource.ownerId`—custom resource data must be supplied via tags or your own
  context. See [Conditional Permissions](/concepts/conditional-permissions) for
  the full context shape.
</Note>

## Effective Permissions

Get all permissions a subject has in a scope. The engine exposes `listEffectivePermissions(subjectId, scopeId)` with **positional** arguments:

```typescript theme={null}
interface EffectivePermissionSummary {
  resourceType: string;
  action: string;
  permissions: BedrockPermission[];
  sourceRoles: BedrockRole[];
}

const summary = await bedrock.listEffectivePermissions("subject_jane", "scope_engineering");

// [
//   { resourceType: "document", action: "read", permissions: [...], sourceRoles: [...] },
//   { resourceType: "document", action: "write", permissions: [...], sourceRoles: [...] }
// ]
```

## Debugging Decisions

The decision includes debugging information:

```typescript theme={null}
const decision = await bedrock.evaluate(input);

console.log(decision.explanation);
// "Permission write:document granted via roles: role_editor"

console.log(decision.matches);
// [{ permission: {...}, sourceRoleIds: ["role_editor"] }]

console.log(decision.evaluatedContext);
// { subject: {...}, resource: {...}, ... }
```

## Performance Considerations

<AccordionGroup>
  <Accordion title="Cache membership lookups">
    Membership and role data changes infrequently. Cache it per-request or with short TTLs.
  </Accordion>

  <Accordion title="Prefer effective permissions for many checks">
    When you need many actions for one subject and scope (e.g. rendering a UI), call `listEffectivePermissions(subjectId, scopeId)` once instead of many single `evaluate` calls.
  </Accordion>

  <Accordion title="Minimize context size">
    Only include context data needed for your conditional permissions.
  </Accordion>

  <Accordion title="Pre-compute effective permissions">
    For UI rendering, fetch effective permissions once rather than checking each action individually.
  </Accordion>
</AccordionGroup>

## API Reference

<CardGroup cols={2}>
  <Card title="Evaluation API" icon="gears" href="/api-reference/evaluation">
    Evaluate permission checks
  </Card>

  <Card title="Scope Overrides" icon="sliders" href="/api-reference/scope-overrides/get-role-overrides">
    View and manage overrides
  </Card>
</CardGroup>

## Next Steps

<CardGroup cols={2}>
  <Card title="User Governance" icon="user-shield" href="/guides/user-governance">
    Apply these concepts to user authorization
  </Card>

  <Card title="Agent Governance" icon="robot" href="/guides/agent-governance">
    Apply these concepts to AI agent authorization
  </Card>
</CardGroup>
