Production YAML Guide

Learn the practical structure of a BlazeRules YAML file, then copy a full multi-instance production example.

This guide is the practical version of the rule reference. It shows how to write a YAML file that a production process can load directly.

For smaller copy-ready examples, see YAML Pattern Reference. For complete files organized by deployment style, see Complete Rule Files. The E-commerce Rule Example shows custom decision labels, model channels, lookups, and two independently deployed rulesets.

The file layout

A complete YAML file can contain both rule semantics and local agent wiring:

SectionUsed byPurpose
schema_versionengineRule file compatibility version. Use "2.1".
fieldsengineOptional type hints. Omit most fields and let BlazeRules infer them from the first batch.
lookupsengineNamed CSV lookup sets for in_lookup / not_in_lookup.
decisionsengineDefault decision and precedence.
rulesetengineRule metadata and rules.
instancesblazerules_agent onlyLocal input/output instances for HTTP, file tail, or stdin.

instances is ignored by the in-process Python and C++ rule engine. The agent reads it to start multiple inputs that each load rules and write decisions.

Field hints

Field hints are optional. Add them when stable typing is required before the first batch arrives.

fields:
  event_id: {type: string, nullable: false}
  card_token: {type: entity_key, nullable: false}
  amount: {type: float32, nullable: false}
  country_code:
    type: categorical
    values: [US, GB, IN, DE]
  event_ts_ms: {type: timestamp_ms, nullable: false}

Allowed field types:

float32, float64, int32, int64, categorical, entity_key, timestamp_ms, boolean, string.

Use values: only for an intentionally closed categorical set. Other fields may remain inferred.

Lookups

Lookups are named files resolved relative to the YAML file. For S3-hosted YAML, use exact s3://bucket/key paths or relative keys under the same prefix.

lookups:
  blocked_merchants:
    type: string_set
    path: lookups/blocked_merchants.csv
  risky_bins:
    type: int_set
    path: lookups/risky_bins.csv
  vpn_ranges:
    type: ipv4_cidr_set
    path: lookups/vpn_ranges.csv

Supported lookup types:

TypeCSV columnUsed with
string_setvalueSTRING, CATEGORICAL, ENTITY_KEY
int_setvalueinteger numeric fields
ipv4_cidr_setcidrdotted IPv4 STRING, numeric IPv4 INT32, or numeric IPv4 INT64 fields

Decisions

Decision policy turns rule matches into one final per-row decision.

decisions:
  default: APPROVE
  precedence: [APPROVE, SCORE, FLAG, REVIEW, BLOCK]

Rules can set action, weight, priority, severity, and reason_code. precedence is ordered weakest to strongest; a later label outranks an earlier label. Risk bands are output fields with the engine's fixed LOW / MEDIUM / HIGH thresholds, not YAML-configured ranges. Production routing should normally consume result.grouped_decision_indices() or compact decision logs instead of scanning Python strings one row at a time.

Condition examples by family

Numeric and range

- {field: amount, op: gt, value: 1000}
- {field: amount, op: between_including, value: [100, 5000]}
- {field: amount, op: gt_field, other_field: historical_avg_amount}

Categorical and null handling

- {field: country_code, op: in, values: [US, GB, IN]}
- {field: optional_note, op: is_empty}
- {field: description, op: is_not_empty}

Strings and regex

- {field: user_agent, op: contains, value: Mobile}
- {field: transaction_description, op: regex, value: "payment|checkout"}
- {field: user_agent, op: not_regex, value: "bot|crawler"}

Arrays, flags, and nested arrays of objects

- {field: tags, op: contains_any, values: [vip, trusted]}
- {field: signal_flags, op: flags_any, mask: 4}
- array_any:
    path: items
    where:
      and:
        - {field: price, op: gt, value: 100}
        - {field: category, op: eq, value: electronics}

Inside array_any, field names are scoped to the same array element. The example only matches when one item has both price > 100 and category = electronics.

Network, temporal, and geo

- {field: ip_address, op: ip_in_subnet, value: "10.0.0.0/8"}
- {field: event_ts_ms, op: within_last, value: 86400}
- {field: event_ts_ms, op: day_of_week_in, values: [1, 2, 3, 4, 5]}
- op: distance_gt
  lat_field: billing.lat
  lon_field: billing.lon
  other_lat_field: shipping.lat
  other_lon_field: shipping.lon
  value: 50

Lookups, windows, SQL, ML, and vectors

- {field: merchant_id, op: in_lookup, lookup: blocked_merchants}
- window:
    entity_field: card_token
    function: count
    duration_seconds: 600
    op: gt
    value: 3
- sql: "amount > 1000 AND country_code IN ('US', 'GB')"
- model_score:
    model: fraud_logreg
    features: [amount, account_age_days, merchant_risk_score]
    op: gt
    value: 0.8
- vector_distance:
    dims: [embedding_0, embedding_1, embedding_2, embedding_3]
    metric: cosine
    reference: [0.1, 0.2, 0.3, 0.4]
    op: gt
    value: 0.7

model_score.features and vector_distance.dims must be numeric fields. vector_distance uses one scalar field per dimension. For metric: cosine, op: gt means “more similar.” day_of_week_in uses 0..6, where 0 is Sunday and 6 is Saturday.

Complete minimal production YAML

This single file can be loaded by Python/C++ as rules, and by blazerules_agent as a multi-instance local runtime.

schema_version: "2.1"

fields:
  event_id: {type: string, nullable: false}
  card_token: {type: entity_key, nullable: false}
  amount: {type: float32, nullable: false}
  country_code:
    type: categorical
    values: [US, GB, IN, DE]
  device_type:
    type: categorical
    values: [ios, android, web, emulator]
  event_ts_ms: {type: timestamp_ms, nullable: false}
  ip_address: {type: string}

lookups:
  blocked_merchants:
    type: string_set
    path: lookups/blocked_merchants.csv
  risky_bins:
    type: int_set
    path: lookups/risky_bins.csv
  vpn_ranges:
    type: ipv4_cidr_set
    path: lookups/vpn_ranges.csv

decisions:
  default: APPROVE
  precedence: [APPROVE, SCORE, FLAG, REVIEW, BLOCK]

ruleset:
  name: Payments Production Rules
  version: "2026.06.23"
  rules:
    - id: high_amount_emulator
      action: BLOCK
      severity: HIGH
      priority: 100
      weight: 60
      reason_code: HIGH_AMOUNT_EMULATOR
      conditions:
        and:
          - {field: amount, op: gt, value: 1000}
          - {field: device_type, op: eq, value: emulator}

    - id: blocked_merchant_or_vpn
      action: REVIEW
      severity: MEDIUM
      priority: 80
      weight: 40
      reason_code: MERCHANT_OR_NETWORK_RISK
      conditions:
        or:
          - {field: merchant.id, op: in_lookup, lookup: blocked_merchants}
          - {field: ip_address, op: in_lookup, lookup: vpn_ranges}

    - id: expensive_electronics_item
      action: FLAG
      severity: MEDIUM
      weight: 25
      conditions:
        array_any:
          path: items
          where:
            and:
              - {field: price, op: gt, value: 100}
              - {field: category, op: eq, value: electronics}

    - id: card_velocity_10m
      action: REVIEW
      severity: HIGH
      weight: 50
      conditions:
        window:
          entity_field: card_token
          function: count
          duration_seconds: 600
          op: gt
          value: 3

    - id: impossible_shipping_distance
      action: REVIEW
      severity: HIGH
      weight: 45
      conditions:
        op: distance_gt
        lat_field: billing.lat
        lon_field: billing.lon
        other_lat_field: shipping.lat
        other_lon_field: shipping.lon
        value: 500

instances:
  - name: payments-http
    rules: rules.yaml
    batch_size: 4096
    flush_ms: 50
    service: payments-api
    source: http-json
    input:
      type: http
      host: 127.0.0.1
      port: 9480
    output:
      type: ndjson
      path: decisions-payments.ndjson
    dedupe:
      enabled: true
      key_fields: [event_id]
      ttl_seconds: 86400

  - name: checkout-pod-tail
    rules: rules.yaml
    batch_size: 2048
    flush_ms: 250
    service: checkout
    source: pod-stdout
    input:
      type: file_tail
      path: /var/log/containers/checkout.log
    output:
      type: ndjson
      path: decisions-checkout.ndjson

  - name: replay-stdin
    rules: rules.yaml
    batch_size: 8192
    flush_ms: 1000
    service: replay
    source: stdin
    input:
      type: stdin
    output:
      type: stdout

Run the multi-instance file:

blazerules_agent --config rules.yaml
Python equivalent: load the rule semantics from the same file
import blazerules

engine = blazerules.RuleEngine()
engine.load_rules("rules.yaml")

payload = b'{"event_id":"e1","card_token":"c1","amount":1200,"device_type":"emulator","country_code":"US","event_ts_ms":1782150000000}\n'
result = engine.evaluate_ndjson(payload)
print(result.decisions)

Common production mistakes

  • Do not put credentials in YAML. Use environment variables, profiles, or platform secrets.
  • Do not use instances: as a replacement for Kafka partitioning. It is a local agent convenience.
  • Do not enable OutputDetail.BITMASKS for routing unless per-rule masks are required.
  • Do not rely on inferred types for critical fields that may drift between producers.
  • Keep lookup CSV paths stable. Missing lookup files fail rule activation.

Related pages


Did this page help you?