Skip to main content

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

Policy File Structure

Policies are defined in a YAML file with four top-level sections:
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]
FieldPurpose
versionSemver string tracked in every PolicyDecision
modeDeployment mode (monitor, enforce, strict)
defaults.on_policy_missFallback action when no rule matches
rulesOrdered list of policy rules

Rule Anatomy

Each rule has these fields:
- 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]
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.

Match Modes

ModeBehavior
allAll conditions must be true (logical AND). This is the default.
anyAt least one condition must be true (logical OR).

Available Fields

These fields are available in rule conditions:
FieldTypeSource
operation_typestringremember, update, forget, search, get
risk_levelstringlow, medium, high, critical
risk_scorefloat0.0 to 1.0
scope.tenant_idstringFrom MemoryScope
scope.project_idstringFrom MemoryScope
scope.agent_idstringFrom MemoryScope
scope.subject_idstringFrom MemoryScope
context.sourcestringlanggraph, openai_sessions, mcp, or custom
content.contains_piibooleanPII detected in content
content.contains_secretbooleanSecret/credential detected in content
content.lengthintegerCharacter length of the content

Condition Operators

The engine supports 10 operators:
OperatorDescriptionExample
eqEqualsrisk_level eq "critical"
neqNot equalsoperation_type neq "search"
inValue in listoperation_type in ["forget", "update"]
ninValue not in listcontext.source nin ["langgraph", "mcp"]
gtGreater thanrisk_score gt 0.8
gteGreater than or equalcontent.length gte 10000
ltLess thanrisk_score lt 0.3
lteLess than or equalrisk_score lte 0.5
containsString containsscope.tenant_id contains "prod"
regexRegex matchscope.project_id regex "^proj-[a-z]+"

Actions

Each rule produces one of four actions:

allow

The operation proceeds to the adapter and is committed to the memory backend.

deny

The operation is blocked immediately. A PolicyDeniedError is raised with the matched rule ID.

require_approval

The operation is paused and sent to the Approval Broker. It proceeds only if approved.

quarantine

The operation payload is stored in the quarantine store for later review. A QuarantinedError is raised.

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.
defaults:
  on_policy_miss: allow   # or deny, require_approval, quarantine
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.

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

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.