> ## 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.

# On Behalf Of

> How actor and principal permissions are evaluated together

## Overview

The `onBehalfOf` field in evaluation requests enables **delegated authorization**. When provided, Bedrock evaluates permissions for both the actor (who's performing the action) and the principal (who they're acting for).

## Prerequisite: a Delegation Grant

Delegation is **gated**. Before any permissions are checked, an **active delegation grant** authorizing the actor to act for the principal in the scope must exist—otherwise the request is denied with `NO_DELEGATION_GRANT`. The "both have permission → allowed" logic below applies **only once a grant is in place**.

```bash theme={null}
curl -X POST 'https://api.example.com/delegation-grants' \
  -H 'x-api-key: brk_YOUR_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
    "scopeId": "scope_engineering",
    "actorSubjectId": "subject_agent",
    "principalSubjectId": "subject_jane",
    "validFrom": "2026-01-01T00:00:00Z",
    "validUntil": "2026-12-31T23:59:59Z"
  }'
```

The temporal window (`validFrom`/`validUntil`) is checked against a **trusted server clock**—a forged `context.now` cannot revive an expired grant.

## Evaluation Input

```typescript theme={null}
interface BedrockEvaluateInput {
  actor: BedrockSubjectRef;        // Required: who is performing the action
  onBehalfOf?: BedrockSubjectRef;  // Optional: who they're acting for
  scopeId: string;
  action: string;
  resource?: BedrockResourceRef;
  context?: Record<string, unknown>;
}

interface BedrockSubjectRef {
  subjectId: string;
  subjectType: SubjectTypeEnum | string;
}
```

## How Evaluation Works

### Without onBehalfOf

Only the actor's permissions are checked:

```typescript theme={null}
const decision = await bedrock.evaluate({
  actor: { subjectId: "subject_jane", subjectType: "user" },
  scopeId: "scope_engineering",
  action: "read",
  resource: { resourceType: "document" }
});

// Checks: Does Jane have "document:read" in scope_engineering?
```

### With onBehalfOf

Both actor AND principal must have the permission:

```typescript theme={null}
const decision = await bedrock.evaluate({
  actor: { subjectId: "subject_agent", subjectType: "agent" },
  onBehalfOf: { subjectId: "subject_jane", subjectType: "user" },
  scopeId: "scope_engineering",
  action: "read",
  resource: { resourceType: "document" }
});

// Checks:
// 0. Is there an active delegation grant (agent -> Jane) in scope_engineering?
// 1. Does the agent have "document:read" in scope_engineering?
// 2. Does Jane have "document:read" in scope_engineering?
// 3. All must pass (grant + both legs) — the allow is the intersection
```

## Decision Output

The decision includes delegation details:

```typescript theme={null}
interface BedrockDecision {
  allowed: boolean;
  matches: BedrockPermissionMatch[];
  explanation?: string;
  
  // Delegation fields
  usedDelegation?: boolean;        // Was onBehalfOf provided?
  delegationId?: string;           // Reference for audit
  evaluatedActor?: BedrockSubjectRef;
  evaluatedOnBehalfOf?: BedrockSubjectRef;
}
```

### Example Response

```typescript theme={null}
{
  allowed: true,
  matches: [
    { permission: {...}, sourceRoleIds: ["role_agent_reader"] },
    { permission: {...}, sourceRoleIds: ["role_editor"] }
  ],
  explanation: "Delegated permission read:document granted. Actor and principal both allowed.",
  usedDelegation: true,
  evaluatedActor: { subjectId: "subject_agent", subjectType: "agent" },
  evaluatedOnBehalfOf: { subjectId: "subject_jane", subjectType: "user" }
}
```

## Permission Scenarios

### Scenario 1: Both Have Permission ✅

```
Agent: Has "document:read" via "Agent Reader" role
Jane:  Has "document:read" via "Editor" role

Result: ALLOWED
```

### Scenario 2: Only Actor Has Permission ❌

```
Agent: Has "document:read" via "Agent Reader" role
Jane:  Does NOT have "document:read" (Viewer role only has "document:list")

Result: DENIED
Reason: Principal lacks required permission
```

### Scenario 3: Only Principal Has Permission ❌

```
Agent: Does NOT have "document:read" (no role assignment)
Jane:  Has "document:read" via "Editor" role

Result: DENIED
Reason: Actor lacks required permission
```

### Scenario 4: Neither Has Permission ❌

```
Agent: No "document:read" permission
Jane:  No "document:read" permission

Result: DENIED
Reason: Neither actor nor principal has permission
```

## Scope Considerations

Both legs are evaluated at the **same `scopeId`**, each composed per that scope's [`permissionMode`](/concepts/scope-types). Under the default `override` mode, each subject needs a membership at that exact scope; under `inherit`/`additive`, ancestor grants are pulled in.

```typescript theme={null}
const decision = await bedrock.evaluate({
  actor: { subjectId: "subject_agent", subjectType: "agent" },
  onBehalfOf: { subjectId: "subject_jane", subjectType: "user" },
  scopeId: "scope_engineering",
  action: "read",
  resource: { resourceType: "document" }
});

// Both the agent and Jane are resolved at scope_engineering;
// ancestor grants count only if that scope's type is inherit/additive.
```

## Overrides Apply to Both

Scope overrides affect both actor and principal:

```bash theme={null}
# Disable "write" permission in production
curl -X POST 'https://api.example.com/scope-overrides/permissions' \
  -d '{
    "childScopeId": "scope_production",
    "permissionId": "perm_write",
    "state": "inactive"
  }'
```

```typescript theme={null}
// Even if both have "write" normally, it's disabled in production
const decision = await bedrock.evaluate({
  actor: { subjectId: "subject_agent", subjectType: "agent" },
  onBehalfOf: { subjectId: "subject_jane", subjectType: "user" },
  scopeId: "scope_production",
  action: "write",
  resource: { resourceType: "document" }
});

// Result: DENIED (override applies)
```

## Conditional Permissions

Conditions are evaluated for both actor and principal:

```bash theme={null}
# Role-permission edge with a condition (conditions live on the edge, not the permission)
{
  "roleId": "role_reader",
  "permissionId": "perm_doc_read",
  "condition": {
    "==": [{"var": "subject.meta.department"}, "engineering"]
  }
}
```

```typescript theme={null}
// Agent has meta.department = "platform"
// Jane has meta.department = "engineering"
// Document has tags.departments = ["engineering"]

const decision = await bedrock.evaluate({
  actor: { subjectId: "subject_agent", subjectType: "agent" },
  onBehalfOf: { subjectId: "subject_jane", subjectType: "user" },
  scopeId: "scope_org",
  action: "read",
  resource: { resourceId: "resource_eng_doc" }
});

// Agent condition: "platform" in ["engineering"] = false
// Result: DENIED (actor fails condition)
```

## Best Practices

<AccordionGroup>
  <Accordion title="Always use onBehalfOf for user-initiated agent actions">
    When a user triggers an agent action, include their identity as the principal.
  </Accordion>

  <Accordion title="Give agents minimal permissions">
    Agents should have the minimum permissions needed. Delegation ensures they can't exceed user permissions.
  </Accordion>

  <Accordion title="Log both actor and principal">
    For audit trails, always log both the actor and principal from the decision.
  </Accordion>

  <Accordion title="Consider agent-specific roles">
    Create roles specifically for agents with appropriate permission sets.
  </Accordion>
</AccordionGroup>

## Next Steps

<Card title="Agent Delegation" icon="arrow-right" href="/delegation/agent-delegation">
  Patterns for AI agent delegation
</Card>
