Memproof policies are declarative YAML files that define how the system handles every memory operation. The policy engine evaluates rules in priority order — the first matching rule wins.
Policy File Structure
Every policy file has five top-level keys:
version: 0.1.0 # Semantic version of your policy
mode: enforce # "enforce" applies rules; "audit" logs but does not block
defaults: # Fallback behaviors
on_policy_miss: deny
on_adapter_error: quarantine
require_idempotency: true
risk_thresholds: # Score boundaries for risk levels
low_max: 0.30
medium_max: 0.60
high_max: 0.80
critical_max: 1.00
rules: [] # Ordered list of policy rules
Rule Anatomy
Each rule is a dictionary with the following fields:
- id: approve_high_risk_mutations # Unique identifier
description: Require approval for high risk mutations.
enabled: true # Toggle without removing
priority: 200 # Lower number fires first
action: require_approval # allow | deny | require_approval | quarantine
reason_codes: [HIGH_RISK_MUTATION] # Machine-readable codes for audit trail
match: all # "all" = AND, "any" = OR
when: # List of conditions
- field: operation_type
operator: in
value: [remember, update, forget]
- field: risk_level
operator: in
value: [high, critical]
Rules are sorted by priority at load time. A rule with priority 50 is evaluated before priority 200. The first match short-circuits evaluation.
Available Fields
Conditions can reference these fields from the evaluation context:
| Field | Type | Example Values |
|---|
operation_type | string | remember, update, forget, search, get |
risk_level | string | low, medium, high, critical |
risk_score | float | 0.0 - 1.0 |
scope.tenant_id | string | "acme-corp" |
scope.project_id | string | "proj-123" |
scope.agent_id | string | "agent-alpha" |
scope.subject_id | string | "user-456" |
context.source | string | "langgraph", "mcp", "api" |
content.contains_pii | bool | true, false |
content.contains_secret | bool | true, false |
content.length | int | 0 - N |
Condition Operators
The policy engine supports 10 operators:
| Operator | Description | Example |
|---|
eq | Equals | { field: risk_level, operator: eq, value: critical } |
neq | Not equals | { field: context.source, operator: neq, value: api } |
in | Value is in list | { field: operation_type, operator: in, value: [search, get] } |
nin | Value not in list | { field: context.source, operator: nin, value: [langgraph, mcp] } |
gt | Greater than | { field: risk_score, operator: gt, value: 0.8 } |
gte | Greater than or equal | { field: content.length, operator: gte, value: 1000 } |
lt | Less than | { field: risk_score, operator: lt, value: 0.3 } |
lte | Less than or equal | { field: content.length, operator: lte, value: 500 } |
contains | Substring match | { field: scope.tenant_id, operator: contains, value: acme } |
regex | Regular expression match | { field: scope.agent_id, operator: regex, value: "^agent-.*" } |
Match Modes
all (default) — Every condition must be true (logical AND).
any — At least one condition must be true (logical OR).
Default Behaviors
| Key | Values | Purpose |
|---|
on_policy_miss | allow, deny, quarantine, require_approval | Action when no rule matches |
on_adapter_error | quarantine, deny | Action when the backend adapter fails |
Setting on_policy_miss: allow means any operation not explicitly matched by a rule will be permitted. In production, deny is the safer default.
Common Patterns
Allow Trusted Reads
- id: allow_safe_search
description: Allow read/search from trusted runtimes.
priority: 100
action: allow
reason_codes: [SAFE_READ_PATH]
match: all
when:
- field: operation_type
operator: in
value: [search, get]
- field: context.source
operator: in
value: [langgraph, openai_sessions, mcp]
Block Cross-Tenant Operations
- id: deny_cross_tenant_ops
description: Deny operations with missing tenant scope.
priority: 50
action: deny
reason_codes: [CROSS_TENANT_SCOPE_MISMATCH]
match: any
when:
- field: scope.tenant_id
operator: eq
value: ""
Require Approval for High Risk
- id: approve_high_risk
description: Require human approval for high/critical mutations.
priority: 200
action: require_approval
reason_codes: [HIGH_RISK_MUTATION]
match: all
when:
- field: operation_type
operator: in
value: [remember, update, forget]
- field: risk_level
operator: in
value: [high, critical]
Quarantine PII from Untrusted Sources
- id: quarantine_pii
description: Quarantine writes with PII from untrusted sources.
priority: 160
action: quarantine
reason_codes: [SENSITIVE_UNTRUSTED_SOURCE]
match: all
when:
- field: operation_type
operator: in
value: [remember, update]
- field: context.source
operator: nin
value: [langgraph, openai_sessions, mcp]
- field: content.contains_pii
operator: eq
value: true
Start with the example policy at examples/memproof.yaml and customize it for your threat model. Keep cross-tenant denial at the lowest priority number so it fires before anything else.