> ## Documentation Index
> Fetch the complete documentation index at: https://memproof.kyberon.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Policy Engine

> YAML-based rule evaluation for deterministic memory governance decisions

# Policy Engine

The Policy Engine is a deterministic rule evaluator. It takes the operation type, risk assessment, scope, context, and content flags as input, and evaluates a list of YAML-defined rules in priority order. The **first matching rule wins**, producing a `PolicyDecision` with an action and reason codes.

<Note>
  Deterministic means the same input combined with the same policy version always produces the same decision. There is no randomness, no ML model, and no external calls.
</Note>

## Policy File Structure

Policies are defined in a YAML file with four top-level sections:

```yaml theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
version: "1.0.0"
mode: enforce          # monitor | enforce | strict

defaults:
  on_policy_miss: allow   # action when no rule matches

rules:
  - id: block-secrets
    description: Block any memory containing detected secrets
    priority: 10
    match: all
    when:
      - field: content.contains_secret
        operator: eq
        value: true
    action: deny
    reason_codes: [SECRET_DETECTED]
```

| Field                     | Purpose                                          |
| ------------------------- | ------------------------------------------------ |
| `version`                 | Semver string tracked in every `PolicyDecision`  |
| `mode`                    | Deployment mode (`monitor`, `enforce`, `strict`) |
| `defaults.on_policy_miss` | Fallback action when no rule matches             |
| `rules`                   | Ordered list of policy rules                     |

## Rule Anatomy

Each rule has these fields:

```yaml theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
- id: require-approval-for-deletes
  description: Require human approval for any delete operation
  enabled: true              # optional, defaults to true
  priority: 20               # lower number = higher priority
  match: all                 # "all" (AND) or "any" (OR)
  when:
    - field: operation_type
      operator: eq
      value: forget
  action: require_approval
  reason_codes: [DELETE_REQUIRES_APPROVAL]
```

<Tip>
  Rules are evaluated in ascending priority order. A rule with `priority: 10` is evaluated before a rule with `priority: 20`. Use gaps (10, 20, 30) so you can insert rules later without renumbering.
</Tip>

## Match Modes

| Mode  | Behavior                                                        |
| ----- | --------------------------------------------------------------- |
| `all` | All conditions must be true (logical AND). This is the default. |
| `any` | At least one condition must be true (logical OR).               |

## Available Fields

These fields are available in rule conditions:

| Field                     | Type    | Source                                           |
| ------------------------- | ------- | ------------------------------------------------ |
| `operation_type`          | string  | `remember`, `update`, `forget`, `search`, `get`  |
| `risk_level`              | string  | `low`, `medium`, `high`, `critical`              |
| `risk_score`              | float   | 0.0 to 1.0                                       |
| `scope.tenant_id`         | string  | From `MemoryScope`                               |
| `scope.project_id`        | string  | From `MemoryScope`                               |
| `scope.agent_id`          | string  | From `MemoryScope`                               |
| `scope.subject_id`        | string  | From `MemoryScope`                               |
| `context.source`          | string  | `langgraph`, `openai_sessions`, `mcp`, or custom |
| `content.contains_pii`    | boolean | PII detected in content                          |
| `content.contains_secret` | boolean | Secret/credential detected in content            |
| `content.length`          | integer | Character length of the content                  |

## Condition Operators

The engine supports 10 operators:

| Operator   | Description           | Example                                   |
| ---------- | --------------------- | ----------------------------------------- |
| `eq`       | Equals                | `risk_level eq "critical"`                |
| `neq`      | Not equals            | `operation_type neq "search"`             |
| `in`       | Value in list         | `operation_type in ["forget", "update"]`  |
| `nin`      | Value not in list     | `context.source nin ["langgraph", "mcp"]` |
| `gt`       | Greater than          | `risk_score gt 0.8`                       |
| `gte`      | Greater than or equal | `content.length gte 10000`                |
| `lt`       | Less than             | `risk_score lt 0.3`                       |
| `lte`      | Less than or equal    | `risk_score lte 0.5`                      |
| `contains` | String contains       | `scope.tenant_id contains "prod"`         |
| `regex`    | Regex match           | `scope.project_id regex "^proj-[a-z]+"`   |

## Actions

Each rule produces one of four actions:

<CardGroup cols={2}>
  <Card title="allow">
    The operation proceeds to the adapter and is committed to the memory backend.
  </Card>

  <Card title="deny">
    The operation is blocked immediately. A `PolicyDeniedError` is raised with the matched rule ID.
  </Card>

  <Card title="require_approval">
    The operation is paused and sent to the Approval Broker. It proceeds only if approved.
  </Card>

  <Card title="quarantine">
    The operation payload is stored in the quarantine store for later review. A `QuarantinedError` is raised.
  </Card>
</CardGroup>

## The `on_policy_miss` Fallback

When no rule matches an operation, the `defaults.on_policy_miss` action is used. The decision will have `reason_codes: ["DEFAULT_POLICY"]` and an empty `matched_rule_ids` list.

```yaml theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
defaults:
  on_policy_miss: allow   # or deny, require_approval, quarantine
```

<Warning>
  In production, consider setting `on_policy_miss: deny` to enforce a deny-by-default posture. Any operation not explicitly covered by a rule will be blocked.
</Warning>

## Policy Schema Validation

Policies are validated against a JSON Schema at load time. If a `memproof-policy.schema.json` file is found in the `schemas/` directory, it is used automatically:

```python theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
from memproof.policy.engine import load_policy, validate_policy

# Load and validate
config = load_policy("./memproof.yaml", schema_path="./schemas/memproof-policy.schema.json")

# Validate a dict without loading
validate_policy(policy_data, schema_path="./schemas/memproof-policy.schema.json")
```

## Example: Multi-Rule Policy

```yaml theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
version: "1.0.0"
mode: enforce
defaults:
  on_policy_miss: allow

rules:
  - id: block-secrets
    description: Block writes containing secrets
    priority: 10
    match: all
    when:
      - field: content.contains_secret
        operator: eq
        value: true
    action: deny
    reason_codes: [SECRET_DETECTED]

  - id: quarantine-pii-from-untrusted
    description: Quarantine PII from untrusted sources
    priority: 20
    match: all
    when:
      - field: content.contains_pii
        operator: eq
        value: true
      - field: context.source
        operator: nin
        value: [langgraph, openai_sessions, mcp]
    action: quarantine
    reason_codes: [PII_UNTRUSTED_SOURCE]

  - id: approve-deletes
    description: Require approval for delete operations
    priority: 30
    match: all
    when:
      - field: operation_type
        operator: eq
        value: forget
    action: require_approval
    reason_codes: [DELETE_REQUIRES_APPROVAL]

  - id: block-critical-risk
    description: Block any operation with critical risk
    priority: 40
    match: all
    when:
      - field: risk_level
        operator: eq
        value: critical
    action: deny
    reason_codes: [CRITICAL_RISK]
```

In this example, a delete operation containing a secret would match `block-secrets` (priority 10) before reaching `approve-deletes` (priority 30), because rules are evaluated in ascending priority order.
