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

# Resource Policies

> Fine-grained allow/deny access control on resources and collections

## What are Resource Policies?

**Resource Policies** provide fine-grained access control at the resource level. Unlike role-based permissions that apply broadly, policies target specific resources or collections with precise allow/deny rules.

Policies are evaluated **before** role-based permissions, giving you a powerful override mechanism.

## Policy Properties

| Property                   | Type                       | Description                                                              |
| -------------------------- | -------------------------- | ------------------------------------------------------------------------ |
| `id`                       | `string?`                  | Unique identifier                                                        |
| `scopeId`                  | `string`                   | Scope where policy is defined                                            |
| `target`                   | `PolicyTarget`             | Resource or collection to target                                         |
| `actions`                  | `string[]`                 | Actions this applies to (`["*"]` for all)                                |
| `effect`                   | `'allow' \| 'deny'`        | Policy effect                                                            |
| `priority`                 | `number`                   | Selects the winning group; deny beats allow within a group (default `0`) |
| `subjectCondition`         | `Record<string, unknown>?` | JSON Logic to match the actor                                            |
| `contextCondition`         | `Record<string, unknown>?` | JSON Logic to match request context                                      |
| `validFrom` / `validUntil` | `string?`                  | ISO datetime — the policy is inactive outside this window                |

<Note>
  A resource policy has **no** `name` or `description` field. If you send them they are
  silently dropped (don't rely on them). Express intent with `priority`, `target`, and
  conditions.
</Note>

## Policy Targets

### Target a Specific Resource

```json theme={null}
{
  "target": {
    "kind": "resource",
    "resourceId": "resource_confidential_report"
  }
}
```

### Target a Collection

```json theme={null}
{
  "target": {
    "kind": "collection",
    "collectionId": "collection_finance_docs"
  }
}
```

## Creating Policies

### Allow Policy on a Resource

```bash theme={null}
# Allow finance team to read the Q4 report
curl -X POST 'https://api.example.com/resource-policies' \
  -H 'Content-Type: application/json' \
  -d '{
    "scopeId": "scope_org",
    "name": "Finance Q4 Report Access",
    "target": {
      "kind": "resource",
      "resourceId": "resource_q4_report"
    },
    "actions": ["read"],
    "effect": "allow",
    "subjectCondition": {
      "==": [{"var": "subject.meta.department"}, "finance"]
    }
  }'
```

### Deny Policy on a Collection

```bash theme={null}
# Deny all access to archived documents
curl -X POST 'https://api.example.com/resource-policies' \
  -d '{
    "scopeId": "scope_org",
    "name": "Block Archived Documents",
    "target": {
      "kind": "collection",
      "collectionId": "collection_archived"
    },
    "actions": ["*"],
    "effect": "deny",
    "priority": 100
  }'
```

### Policy with Context Condition

```bash theme={null}
# Only allow access during business hours
curl -X POST 'https://api.example.com/resource-policies' \
  -d '{
    "scopeId": "scope_org",
    "name": "Business Hours Only",
    "target": {
      "kind": "resource",
      "resourceId": "resource_production_db"
    },
    "actions": ["write", "delete"],
    "effect": "allow",
    "contextCondition": {
      "and": [
        {">=": [{"var": "time.hour"}, 9]},
        {"<=": [{"var": "time.hour"}, 17]}
      ]
    }
  }'
```

## Policy Effects

### Allow

Grants access if the policy matches:

```bash theme={null}
{
  "effect": "allow",
  "actions": ["read", "update"],
  "subjectCondition": {
    "==": [{"var": "subject.meta.role"}, "manager"]
  }
}
```

### Deny

Blocks access if the policy matches. Deny policies typically have higher priority:

```bash theme={null}
{
  "effect": "deny",
  "priority": 100,
  "actions": ["delete"],
  "subjectCondition": {
    "!=": [{"var": "subject.meta.role"}, "admin"]
  }
}
```

## Priority and Evaluation Order

Policies resolve by **priority group**, not first-match:

1. All matching policies (action matches, temporal window active, subject/context conditions pass) are collected.
2. Only the **highest-priority group** among the matches decides.
3. Within that group, **`deny` beats `allow`** (deny-overrides)—it is **not** "first match wins".
4. If no policy matches, **role-based permissions** are checked.

A lower-priority `deny` does **not** override a higher-priority `allow`: priority selects the group, and `deny` wins only *within* the winning group.

```bash theme={null}
# High priority deny (evaluated first)
{
  "name": "Block External IPs",
  "effect": "deny",
  "priority": 100,
  "contextCondition": {
    "!": {"in": [{"var": "ip"}, ["10.0.0.0/8", "192.168.0.0/16"]]}
  }
}

# Lower priority allow (evaluated second)
{
  "name": "Allow Finance Team",
  "effect": "allow",
  "priority": 50,
  "subjectCondition": {
    "==": [{"var": "subject.meta.department"}, "finance"]
  }
}
```

## Subject Conditions

Match based on the actor making the request:

```bash theme={null}
# Only admins
{
  "subjectCondition": {
    "==": [{"var": "subject.meta.role"}, "admin"]
  }
}

# Specific department
{
  "subjectCondition": {
    "==": [{"var": "subject.meta.department"}, "engineering"]
  }
}

# Clearance level
{
  "subjectCondition": {
    ">=": [{"var": "subject.meta.clearanceLevel"}, 3]
  }
}

# Multiple conditions
{
  "subjectCondition": {
    "and": [
      {"==": [{"var": "subject.meta.department"}, "finance"]},
      {">=": [{"var": "subject.meta.level"}, 2]}
    ]
  }
}
```

## Context Conditions

Match based on request context:

```bash theme={null}
# Business hours
{
  "contextCondition": {
    "and": [
      {">=": [{"var": "time.hour"}, 9]},
      {"<=": [{"var": "time.hour"}, 17]}
    ]
  }
}

# Approved IP ranges
{
  "contextCondition": {
    "in": [{"var": "ip"}, ["10.0.0.0/8", "192.168.1.0/24"]]
  }
}

# Specific device types
{
  "contextCondition": {
    "in": [{"var": "deviceType"}, ["desktop", "laptop"]]
  }
}
```

## Combined Conditions

Use both subject and context conditions:

```bash theme={null}
# Finance team during business hours from office IPs
curl -X POST 'https://api.example.com/resource-policies' \
  -d '{
    "scopeId": "scope_org",
    "name": "Finance Office Access",
    "target": {
      "kind": "collection",
      "collectionId": "collection_financial_data"
    },
    "actions": ["read", "update"],
    "effect": "allow",
    "subjectCondition": {
      "==": [{"var": "subject.meta.department"}, "finance"]
    },
    "contextCondition": {
      "and": [
        {">=": [{"var": "time.hour"}, 9]},
        {"<=": [{"var": "time.hour"}, 17]},
        {"in": [{"var": "ip"}, ["10.0.0.0/8"]]}
      ]
    }
  }'
```

## Evaluation Flow

When `BedrockEngine.evaluate()` is called:

```
1. Get the resource being accessed
2. Find policies targeting this resource directly
3. Find collections matching this resource, and policies targeting those collections
4. Keep only policies that MATCH: action matches, the validFrom/validUntil window is
   active, and subjectCondition + contextCondition (if present) pass
5. Among the matches, take the highest `priority` value
6. In that highest-priority group: if ANY policy is `deny` → deny; otherwise → allow
7. If no policy matched → continue to role-based evaluation
```

## Decision Output

When a policy decides the outcome:

```typescript theme={null}
const decision = await bedrock.evaluate({
  actor: { subjectId: "subject_jane" },
  scopeId: "scope_org",
  action: "read",
  resource: { resourceId: "resource_q4_report" },
  context: { time: { hour: 14 } }
});

// If decided by policy:
decision.allowed          // true or false
decision.decidedByPolicy  // true
decision.evaluatedPolicy  // { id: "policy_123", name: "Finance Access", ... }
```

## Common Patterns

### Owner-Only Access

`resource.ownerId` is **not** exposed in the condition context (see the [roadmap](/roadmap)), so a policy condition can't compare it to the actor. For owner-based access today, use a resource-**ownership** grant (`owns(subject, resource, role)`) instead of a policy, or surface the owner's subject id as a resource **tag** and match `tags.<identifier>`.

### Deny All Except Admins

```bash theme={null}
{
  "name": "Admin Override",
  "effect": "allow",
  "priority": 1000,
  "actions": ["*"],
  "subjectCondition": {
    "==": [{"var": "subject.meta.role"}, "admin"]
  }
}

{
  "name": "Deny Everyone Else",
  "effect": "deny",
  "priority": 999,
  "actions": ["*"]
}
```

### Temporary Access Window

```bash theme={null}
{
  "name": "Maintenance Window",
  "effect": "allow",
  "actions": ["write", "delete"],
  "contextCondition": {
    "and": [
      {">=": [{"var": "time.hour"}, 2]},
      {"<=": [{"var": "time.hour"}, 4]}
    ]
  }
}
```

### Geographic Restrictions

```bash theme={null}
{
  "name": "US Only",
  "effect": "deny",
  "actions": ["*"],
  "contextCondition": {
    "!": {"==": [{"var": "country"}, "US"]}
  }
}
```

## Best Practices

<AccordionGroup>
  <Accordion title="Use deny policies sparingly">
    Prefer allow policies with specific conditions. Deny policies can be hard to debug.
  </Accordion>

  <Accordion title="Set appropriate priorities">
    Use a consistent priority scheme. Example: deny=100+, allow=50, default=0.
  </Accordion>

  <Accordion title="Test policies thoroughly">
    Verify both allow and deny cases before deploying.
  </Accordion>

  <Accordion title="Document policy intent">
    Use clear names and descriptions explaining why the policy exists.
  </Accordion>

  <Accordion title="Prefer collections over individual resources">
    Policies on collections are more maintainable than many individual resource policies.
  </Accordion>
</AccordionGroup>

## Related Concepts

<CardGroup cols={2}>
  <Card title="Resource Collections" icon="layer-group" href="/resources/resource-collections">
    Define dynamic resource groups for policies
  </Card>

  <Card title="Conditional Permissions" icon="code" href="/concepts/conditional-permissions">
    JSON Logic conditions on role-based permissions
  </Card>

  <Card title="Evaluation" icon="gears" href="/concepts/evaluation">
    How policies fit into the evaluation flow
  </Card>
</CardGroup>
