๐Ÿ›ก๏ธ Interven
Reference

Policy DSL Reference

Complete reference for the Interven policy language โ€” every match key, condition, action, and combinator.

The Interven policy language is a JSON / YAML DSL. Each policy has a name and one or more rules; each rule has a match, condition, action, and priority.

This page is the authoritative reference. For the conceptual primer + common recipes, see Policies.

Structure

{
  "name": "policy name (unique per env)",
  "description": "free text",
  "env_names": ["production"],        // optional; omit = applies to all envs
  "rules": [
    {
      "rule_id": "stable-id-for-audit",
      "priority": 10,
      "match": { /* match keys */ },
      "condition": { /* conditions */ },
      "action": "ALLOW | DENY | SANITIZE | REQUIRE_APPROVAL | OPEN_INCIDENT"
    }
  ]
}

Match keys

match says which scans the rule applies to. Empty {} = every scan. All keys are ANDed together; multi-value fields are ORed within themselves.

KeyTypeExample
tool_namestring"slack", "github", "custom_proxy"
tool_namesstring[]["slack", "discord", "teams"]
operationsstring[]["create_pr"], ["method:POST"], ["category:issues"], ["*"]
verbsstring[]["READ"], ["WRITE", "ADMIN"]
agent_idsUUID[]Limit to specific agents
agent_runtime_typesstring[]["langchain", "langgraph"]
org_patternsstring[]GitHub-style ["acme/*"]
repo_patternsstring[]["my-org/secrets-*"]
channel_patternsstring[]Slack ["#alerts*", "#prod-*"]
region_patternsstring[]AWS ["us-*"], ["eu-*"]
account_patternsstring[]AWS ["12345*"]
aws_tagsobject{"Environment": "prod"} โ€” all must match
principal_patternsstring[]["@gmail.com$"], ["*@external.example"]
methodsstring[]["POST", "DELETE"] (HTTP verbs, raw)
url_patternsstring[]URL prefix or glob (["https://api.slack.com/*"])

Operation reference

The operations array accepts three forms:

FormMeaningExample
Bare op nameExact match"create_pr", "post_message"
category:XAll ops in a category"category:issues", "category:write"
method:XAll ops with this HTTP verb"method:POST", "method:DELETE"
*All operations for the toolUseful with tool_name

Available categories per tool are in the Tools catalog.

Conditions

condition says when a matched scan triggers the rule. Empty {} = always. All keys are ANDed.

KeyTypeUse case
has_data_classstring[]Body matches a classifier โ€” ["SECRETS"], ["PII"], ["PHI"], ["INTERNAL"]
body_contains_anystring[]Body text has ANY of these substrings (case-insensitive)
body_contains_allstring[]Body has EVERY substring
body_path_equalsobjectJSON-path-style: {"path": "$.amount", "value": 0}
body_path_gt / _gte / _lt / _lteobjectNumeric: {"path": "$.amount", "value": 10000}
body_size_bytes_gtnumberRequest body size threshold
is_external_principalboolTrue if the action targets an entity outside your org
is_new_destinationboolFirst time this agent has touched this resource
is_volume_burstboolBurst detector: โ‰ฅ5ร— normal rate in last 10 min
risk_score_gtnumber0.0โ€“1.0 โ€” combine with other rules for tiered actions
trust_score_ltnumberAgent trust below threshold (default 0.6)
threat_intel_matchboolURL or IP matched one of the 6 feeds
daily_budget_exceededstringNamed budget that has gone over its day's allocation
time_of_day_outsideobject{"start": "09:00", "end": "18:00", "tz": "Asia/Dubai"} โ€” true outside the window

body_path_* examples

For body content like {"amount": 14500, "recipient": "..."}:

{
  "body_path_gt": { "path": "$.amount", "value": 10000 }
}

The path syntax is JSONPath subset: $.field, $.nested.field, $.items[*].field.

Combining conditions

{
  "condition": {
    "has_data_class": ["PII"],
    "is_external_principal": true,
    "trust_score_lt": 0.7
  }
}

All three must be true for the rule to fire. There's no top-level OR in the DSL โ€” use multiple rules with the same action instead.

Actions

ActionEffect
ALLOWForward to the upstream. Trace logged as ALLOW.
DENYBlock. Trace logged. Reason codes from the rule + classifier.
SANITIZERun the classifier's redaction pass + forward the redacted body. Trace logged as SANITIZE.
REQUIRE_APPROVALPause the agent; create an approval record. See Approvals.
OPEN_INCIDENTSame as DENY plus auto-open an incident. Use for critical-severity policies.

You can chain side effects with action modifiers (optional fields next to action):

ModifierEffect
escalate_to"slack", "discord", etc. โ€” force-notify even if the channel's default filter wouldn't include this event
tag"tagname" โ€” adds a tag to the trace for later filtering
incident_severityLOW / MEDIUM / HIGH / CRITICAL โ€” used by OPEN_INCIDENT
reason_codeOverride the default reason code with a custom string (visible in Activity)

Priority & precedence

Within a single policy: lower priority number wins (priority 10 evaluates before priority 20).

Across policies: each policy's matching rules are collected, then strictness escalates. DENY beats SANITIZE beats REQUIRE_APPROVAL beats ALLOW. So if any matching rule says DENY, the decision is DENY โ€” regardless of priority.

OPEN_INCIDENT is treated as DENY for decision purposes plus the incident side effect.

Wildcards & globs

  • Globs in *_patterns fields use shell-glob syntax: * matches anything, ? matches one char, [abc] matches a set.
  • Regex is NOT supported; if you need it, use body_contains_any with multiple substrings or split into multiple rules.
  • Case-insensitive matching is the default for body text; case-sensitive for URLs and identifiers.

Versioning & idempotency

POST /v1/policies/apply is idempotent on name + env_name. Re-applying the same policy creates a new policy_version row (audit trail preserved) but the active policy with that name in that environment gets the latest config.

To roll back: re-apply the old version's JSON. Or use the Console's "Version history โ†’ Restore" button.

Example: full healthcare HIPAA pack rule

{
  "name": "P-HIPAA-1: Deny PHI egress to chat",
  "env_names": ["production"],
  "rules": [
    {
      "rule_id": "phi-egress-chat-deny",
      "priority": 10,
      "match": {
        "tool_names": ["slack", "discord", "teams"],
        "operations": ["post_message", "send_message"]
      },
      "condition": {
        "has_data_class": ["PHI"]
      },
      "action": "DENY",
      "reason_code": "PHI_EGRESS_CHAT_BLOCKED"
    }
  ]
}

Validating policies offline

npm i -g @interven/policy-cli

interven-policy validate policies/*.yaml
# -> reports any DSL errors before you push

CI integration: add interven-policy validate to your linter step.

YAML vs JSON

The DSL accepts either. YAML is recommended for git-tracked policies:

name: "P-HIPAA-1: Deny PHI egress to chat"
env_names: ["production"]
rules:
  - rule_id: phi-egress-chat-deny
    priority: 10
    match:
      tool_names: [slack, discord, teams]
      operations: [post_message, send_message]
    condition:
      has_data_class: [PHI]
    action: DENY
    reason_code: PHI_EGRESS_CHAT_BLOCKED

See also