Skip to main content

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:
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

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 evaluates permissions in a specific order, with earlier steps taking precedence:
┌─────────────────────────────────────────────────────────────┐
│  1. RESOURCE POLICIES (highest priority)                    │
│     ├── Get policies targeting the specific resource        │
│     ├── Get collections matching the resource               │
│     ├── Get policies targeting those collections            │
│     ├── Sort by priority (highest first)                    │
│     ├── Evaluate subject and context conditions             │
│     └── If a policy matches → return allow/deny immediately │
└─────────────────────────────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────┐
│  2. RESOURCE HIERARCHY INHERITANCE                          │
│     ├── If resource has parents with cascade: inherit       │
│     ├── Check ancestor permissions recursively              │
│     └── Return if any ancestor grants permission            │
└─────────────────────────────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────┐
│  3. ROLE-BASED PERMISSIONS                                  │
│     ├── Resolve actor → get memberships in scope hierarchy  │
│     ├── Collect role assignments for memberships            │
│     ├── Gather permissions from assigned roles              │
│     ├── Apply scope overrides (role, permission, role-perm) │
│     ├── Match action & resource pattern                     │
│     ├── Evaluate conditional permissions (JSON Logic)       │
│     └── Return decision with matching permissions           │
└─────────────────────────────────────────────────────────────┘
Resource policies are evaluated first and can short-circuit the entire evaluation. This makes them ideal for explicit allow/deny rules on sensitive resources.

The Decision Output

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 Engineering
- Role assignment: Editor

Override:
- "write" permission disabled in Production

Evaluation Request

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
   └── Found membership in scope_engineering (parent of production)

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

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: "Permission 'write' is disabled in this scope"

Delegation (On Behalf Of)

Agents or services can act on behalf of users:
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:
  1. The actor must have the permission
  2. The principal (onBehalfOf) must also have the permission
  3. Both must pass for the action to be allowed
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:
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

Permissions with logic fields are evaluated against the context:
// Permission with condition
{
  "action": "read",
  "resourceType": "document",
  "logic": {
    "in": [{"var": "subject.meta.department"}, {"var": "resource.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:
VariableSourceExample
subject.idActor"subject_jane"
subject.typeActor"user"
subject.meta.*Actor metadatasubject.meta.department
resource.idResource"resource_123"
resource.typeResource type"document"
resource.tags.*Resource tagsresource.tags.departments
context.*Custom contextcontext.hour, context.ip

Bulk Evaluation

Evaluate multiple permissions at once:
const inputs = [
  { actor, scopeId, action: "read", resource: { resourceType: "document" } },
  { actor, scopeId, action: "write", resource: { resourceType: "document" } },
  { actor, scopeId, action: "delete", resource: { resourceType: "document" } }
];

const decisions = await bedrock.evaluateBulk(inputs);
// [{ allowed: true }, { allowed: true }, { allowed: false }]

Effective Permissions

Get all permissions a subject has in a scope:
interface EffectivePermissionSummary {
  resourceType: string;
  action: string;
  permissions: BedrockPermission[];
  sourceRoles: BedrockRole[];
}

const summary = await bedrock.getEffectivePermissions({
  subjectId: "subject_jane",
  scopeId: "scope_engineering"
});

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

Debugging Decisions

The decision includes debugging information:
const decision = await bedrock.evaluate(input);

console.log(decision.explanation);
// "Allowed via role 'Editor' which grants 'document:write:*'"

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

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

Performance Considerations

Membership and role data changes infrequently. Cache it per-request or with short TTLs.
When checking multiple permissions, use evaluateBulk instead of multiple single calls.
Only include context data needed for your conditional permissions.
For UI rendering, fetch effective permissions once rather than checking each action individually.

API Reference

Next Steps