Rule DSL

Write BlazeRules rule files in YAML: schema, fields, lookups, decisions, and the condition tree.

A BlazeRules rule file is a single YAML document. The engine compiles it once into an immutable execution plan and reuses that plan for every batch. The following sections define the document from top-level metadata through individual rules.

Complete ruleset structure

A rule file has a small set of top-level keys. Most are optional; ruleset is required.

KeyRequiredPurpose
schema_versionYesYAML format version. Use "2.1".
fieldsOptionalType hints for input fields. Omit them and BlazeRules infers types from the first batch.
lookupsOptionalNamed CSV sets used by in_lookup / not_in_lookup. See Lookups.
decisionsOptionalDefault action and precedence ladder. See Decisions & Scoring.
rulesetYesThe rules themselves: name, version, and a list of rules.
📘

The agent has one extra key

A top-level instances: block configures the multi-instance ingest agent (blazerules_agent). It is not part of rule evaluation. See Production YAML Guide for a complete multi-instance file and CLI & Agent for agent runtime behavior.

Minimal example

The smallest useful file declares a version, an optional field hint, and one rule:

schema_version: "2.1"

fields:
  amount: {type: float32, nullable: false}

ruleset:
  name: My Ruleset
  version: "1.0.0"
  rules:
    - id: high_amount_emulator
      action: review
      severity: HIGH
      weight: 25
      conditions:
        and:
          - {field: amount, op: gt, value: 1000}
          - {field: device_type, op: eq, value: emulator}

Fields are optional hints

The fields: block declares each input field's type and, optionally, nullability and allowed values. Undeclared fields are inferred from the first evaluated batch (see schema inference in Core Concepts).

📘

Why give hints at all?

Hints keep types stable across batches, catch field-name errors early, and pin categorical fields to known values. Inference reduces initial configuration; production rulesets should hint fields where ambiguity or drift is unacceptable.

Field type hints use these column types:

float32 · float64 · int32 · int64 · categorical · entity_key · timestamp_ms · boolean · string

A few real declarations from the canonical sample file:

fields:
  event_id: {type: string, nullable: false}
  card_token: {type: entity_key, nullable: false}
  amount: {type: float32, nullable: false}
  account_age_days: {type: int32}
  country_code:
    type: categorical
    values: [US, GB, IN, DE, BR, CN, RU]
  device_type:
    type: categorical
    values: [ios, android, web, emulator]
  merchant_bin: {type: int64}
  ip_address: {type: string}
  event_ts_ms: {type: timestamp_ms}
  optional_note: {type: string, nullable: true}

The entity_key type marks the field used to group an entity's history for Windows (here, card_token).

Per-rule fields

Each entry in ruleset.rules defines one rule. Common fields are:

FieldPurpose
idUnique identifier for the rule. Appears in winning_rule_ids on the result.
actionOne of approve, flag, review, block, score.
labelOptional custom decision label emitted instead of the action name (see below).
severityOne of LOW, MEDIUM, HIGH, CRITICAL.
weightInteger added to the record's score when the rule matches.
conditionsThe condition tree (below).

A rule also supports priority, reason_code, and shadow. These fields control verdict selection and staged rollout. See Decisions & Scoring.

Custom decision labels

action must be one of the five built-ins and sets scoring/risk-band behavior. Add an optional label to emit a custom decision string while keeping the action's semantics:

decisions:
  precedence: [approve, score, flag, review, bot_block, block]
rules:
  - id: bot_traffic
    action: block
    label: bot_block
    conditions: {field: bot_score, op: gt, value: 0.9}

The label surfaces everywhere the decision does — result.decisions, grouped_decision_indices(), the agent decision log, and the dashboard. List custom labels in decisions.precedence to control how they rank against the built-ins.

The condition tree

conditions is a tree. A leaf is a single test on one field; branches combine leaves with boolean logic.

A leaf usually looks like {field, op, value} or {field, op, values}:

- {field: amount, op: gt, value: 1000}
- {field: country_code, op: in, values: [US, GB]}

Some operators take extra keys instead of value — for example cross-field operators use other_field:, bitfield operators use mask:, lookups use lookup:, and geo operators use lat_field / lon_field / other_lat_field / other_lon_field. The full list lives in the Operator Reference.

Branches nest with and, or, and not:

conditions:
  and:
    - {field: amount, op: gt, value: 1000}
    - or:
        - {field: country_code, op: not_in, values: [US, GB]}
        - {field: device_type, op: eq, value: emulator}

SQL and expression forms

Two forms express richer logic inside a leaf.

A sql: leaf accepts a SQL-style boolean expression, including any_match(...) over arrays of objects:

- sql: "amount > 100 AND account_age_days >= 0"
- sql: "any_match(items, x -> x.price > 100 AND x.category = 'electronics')"

The sql: grammar supports:

  • Comparisons>, <, >=, <=, = (or ==), != (or <>). String values compare only with =/!=.
  • Boolean logicAND, OR, NOT, and parentheses for grouping.
  • field IS NULL / field IS NOT NULL.
  • field IN (a, b, ...) / field NOT IN (...).
  • field BETWEEN lo AND hi — numeric, inclusive bounds.
  • field LIKE 'pat' / ILIKE% at the start and/or end maps to ends-with / starts-with / contains. In the current parser, ILIKE uses the same case-sensitive mapping as LIKE; YAML ci_eq provides case-insensitive equality. A pattern with no % maps to substring contains, not exact equality.
  • any_match(path, alias -> expr) — true if any element of the array at path satisfies expr; reference element fields as alias.field (dotted paths allowed for nesting).

Field names may use dots for nested access (e.g. payment.amount). String literals use single or double quotes. Expressions have a bounded nesting depth; extremely deep parenthesization is rejected as a parse error.

An expr: leaf computes arithmetic (add, sub, mul, div) and then compares the result with an op / value:

- op: gt
  expr:
    op: div
    left: amount
    right: available_credit
  value: 0.8

Derived forms are YAML rule leaves, not SQL function calls in the current parser: use ML Scoring for model_score, Vector Similarity for vector_distance, and Windows for velocity aggregates.

Nested YAML vs SQL for the same logic

The condition tree and a sql: leaf can express the same predicate. Here is amount > 1000 AND country_code in [US, GB] written both ways:

conditions:
  and:
    - {field: amount, op: gt, value: 1000}
    - {field: country_code, op: in, values: [US, GB]}

Related documentation


Did this page help you?