Skip to main content

Risk Engine

The Risk Engine produces a numerical score (0.0 to 1.0) and a categorical risk level for every memory operation. This assessment is computed before the Policy Engine runs, so policy rules can branch on risk_score and risk_level. The current scorer is memproof-baseline-v1.

Risk Factors

Each operation is evaluated across up to five factors. Every factor produces a contribution value between 0.0 and 1.0.
1

Operation Type

Different operation types carry different inherent risk. Read operations are low-risk; destructive operations are higher.
OperationContribution
get0.05
search0.05
remember0.30
update0.40
forget0.50
2

PII Detection

Content is scanned against regex patterns for personally identifiable information. If PII is detected, a factor with contribution 0.60 is added.Detected patterns include: Social Security numbers, email addresses, credit card numbers, and phone numbers.
3

Secrets Detection

Content is scanned for embedded credentials and secrets. If a secret is detected, a factor with contribution 0.70 is added.Detected patterns include: API key assignments, bearer tokens, and keys matching the sk- prefix pattern.
4

Source Trust

The context.source field is compared against a set of trusted sources: langgraph, openai_sessions, and mcp.
SourceContribution
Trusted (known adapter)0.05
Untrusted (custom/unknown)0.40
5

Scope Anomaly

If the tenant_id or project_id is missing from the operation scope, a factor with contribution 0.70 is added. This flags operations that lack proper organizational context.

Scoring Formula

The final score is computed in two steps: Step 1: Weighted average
weighted_avg = sum(factor.contribution for each factor) / count(factors)
Step 2: Floor boost from maximum factor
final_score = max(weighted_avg, max_individual_contribution * 0.8)
The floor boost ensures that a single high-risk factor (like a detected secret at 0.70) cannot be diluted below 80% of its contribution by low-risk factors. The final score is clamped to a maximum of 1.0.
The floor boost is a deliberate design choice. A memory operation that contains an API key should never score “low risk” just because all other factors are clean.

Worked Example

Consider a remember operation from a trusted source (langgraph) that contains an email address:
FactorContribution
operation_type (remember)0.30
content_pii (email)0.60
source_trust (trusted)0.05
weighted_avg = (0.30 + 0.60 + 0.05) / 3 = 0.3167
max_individual = 0.60
floor_boost = 0.60 * 0.8 = 0.48
final_score = max(0.3167, 0.48) = 0.48
Result: score = 0.48, level = medium.

Risk Levels

The score maps to one of four risk levels using configurable thresholds:
LevelScore RangeDefault Threshold
low0.00 — 0.30low_max: 0.30
medium0.31 — 0.60medium_max: 0.60
high0.61 — 0.80high_max: 0.80
critical0.81 — 1.00critical_max: 1.00
Thresholds can be customized in the policy YAML under risk_thresholds:
risk_thresholds:
  low_max: 0.25
  medium_max: 0.50
  high_max: 0.75
  critical_max: 1.00

Content Flags

In addition to the scored risk assessment, the Risk Engine produces boolean content flags that are passed to the Policy Engine as condition fields:
flags = risk_engine.assess_content_flags(content)
# {"contains_pii": True, "contains_secret": False}
These map directly to the content.contains_pii and content.contains_secret fields available in policy rule conditions.

How Risk Feeds Into Policy

The RiskAssessment and content flags are passed to the Policy Engine as part of the evaluation context. Policy rules can use any combination of risk data:
# Block critical-risk operations
- id: block-critical
  priority: 10
  when:
    - field: risk_level
      operator: eq
      value: critical
  action: deny
  reason_codes: [CRITICAL_RISK]

# Require approval for high-risk writes
- id: approve-high-risk-writes
  priority: 20
  match: all
  when:
    - field: risk_score
      operator: gte
      value: 0.6
    - field: operation_type
      operator: in
      value: [remember, update]
  action: require_approval
  reason_codes: [HIGH_RISK_WRITE]

RiskAssessment Model

Every operation response includes the full risk assessment:
result = await mp.remember(content="...", scope={...}, context={...})

assessment = result.risk_assessment
assessment.score    # 0.48
assessment.level    # RiskLevel.medium
assessment.scorer   # "memproof-baseline-v1"
assessment.factors  # [RiskFactor(name="operation_type", contribution=0.3, ...), ...]
Each RiskFactor in the factors list includes:
FieldTypeDescription
namestringFactor identifier (e.g., operation_type, content_pii)
contributionfloatScore contribution, 0.0 to 1.0
descriptionstringHuman-readable explanation
evidencestring or nullSpecific pattern matched (e.g., “Email address”)
Use the factors list in logging and dashboards to understand exactly why an operation scored the way it did.