Recipe: End-to-End with CLI, Agent, and Dashboard

Use the blazerules CLI, blazerules_agent, and blazerules_dashboard together, from rule validation to live decisions.

The three executables that ship with pip install blazerules (and the native
archives) are designed to work together: the blazerules CLI vets and
batch-evaluates rules, blazerules_agent ingests live events and writes
compact decision logs, and blazerules_dashboard reads those logs for a
read-only view. This recipe wires all three into one workflow around a single
rules.yaml.

📘

One ruleset, three tools

The same rules.yaml drives every stage. The CLI proves it before you ship,
the agent runs it against live traffic, and the dashboard shows what it did.

1. Author the ruleset

schema_version: "2.1"
fields:
  event_id: {type: string, nullable: false}
  amount: {type: float32, nullable: false}
  country: {type: categorical, nullable: false}
ruleset:
  name: payments
  version: "1.0.0"
  rules:
    - id: high_amount
      action: flag
      severity: HIGH
      conditions: {field: amount, op: gt, value: 1000}
    - id: blocked_country
      action: block
      severity: CRITICAL
      conditions: {field: country, op: eq, value: XX}

2. Vet the rules with the CLI (before anything runs live)

Validate the ruleset compiles and, optionally, push a sample through it so schema
binding and type issues surface now rather than in production:

blazerules validate --rules rules.yaml --sample sample.ndjson
# {"ok":true,"ruleset_version":"1.0.0","conflicts":0,"subsumptions":0,"dead_rules":0,"sample_records":3,"sample_skipped":0}

Compare a candidate ruleset against the current one over historical Parquet to
see what would change before you promote it:

blazerules backtest \
  --rules-a rules.yaml \
  --rules-b rules-candidate.yaml \
  --path history/day-1.parquet \
  --label-column fraud_label

3. Ingest live events with the agent

Run blazerules_agent to accept events over HTTP and write a compact decision
log. This is the long-running process.

blazerules_agent \
  --rules rules.yaml \
  --input http --host 127.0.0.1 --port 9480 \
  --output ndjson --output-path decisions.ndjson \
  --dedupe-key event_id --dedupe-ttl-seconds 3600

Applications POST NDJSON to /v1/logs:

curl -s http://127.0.0.1:9480/v1/logs \
  -H 'content-type: application/x-ndjson' \
  --data-binary $'{"event_id":"e1","amount":2500,"country":"US"}\n{"event_id":"e2","amount":10,"country":"XX"}\n'
# {"ok":true,"instance":"default"}

Each decision line looks like:

{"ts_ms":1782150000000,"instance":"default","batch_row":0,"decision":"FLAG","score":40.0,"risk_band":"MEDIUM","winning_rule_id":"high_amount"}

4. Watch decisions in the dashboard

Point blazerules_dashboard at the same decision log (and the ruleset, for the
visualizer). It serves a local read-only UI plus JSON endpoints.

blazerules_dashboard \
  --host 127.0.0.1 --port 9470 \
  --decision-log decisions.ndjson \
  --rules rules.yaml

Open http://127.0.0.1:9470, or query the JSON directly:

curl -s http://127.0.0.1:9470/api/summary
curl -s 'http://127.0.0.1:9470/api/decisions?limit=50'

Trusted network only

Neither the agent's /v1/logs endpoint nor the dashboard has authentication.
Keep both bound to 127.0.0.1 (the default) or behind your own authenticated
proxy. See Deployment and Security.

5. Batch replay and offline jobs with the CLI

The same rules.yaml runs offline over files or s3:// inputs with blazerules eval — useful for replaying a day of traffic or producing decisions for a
downstream job. It supports the same formats and output shapes as the Python API:

# NDJSON file → grouped routing indices
blazerules eval --rules rules.yaml --input ndjson --path replay.ndjson --output grouped-decisions

# Parquet → an Arrow IPC stream of per-row decisions
blazerules eval --rules rules.yaml --input parquet --path day.parquet \
  --output arrow-ipc --output-path decisions.arrow

6. Or embed the same ruleset in Python

For in-process integration, the Python SDK evaluates the identical rules.yaml
and returns results in memory:

import blazerules

engine = blazerules.RuleEngine()
engine.load_rules("rules.yaml")
result = engine.evaluate_ndjson(open("replay.ndjson", "rb").read())
print(result.n_matched, dict(result.match_counts))
print(result.grouped_decision_indices())

Where each tool fits

StageToolCommand
Validate / backtest rulesblazerules CLIblazerules validate · blazerules backtest
Ingest live eventsblazerules_agentblazerules_agent --input http|stdin|file_tail
Observe decisionsblazerules_dashboardblazerules_dashboard --decision-log …
Batch replay / offlineblazerules CLIblazerules eval …
In-process integrationPython SDKblazerules.RuleEngine

Where to go next


Did this page help you?