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 toolsThe same
rules.yamldrives 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_label3. 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 3600Applications 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.yamlOpen 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 onlyNeither the agent's
/v1/logsendpoint nor the dashboard has authentication.
Keep both bound to127.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.arrow6. 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
| Stage | Tool | Command |
|---|---|---|
| Validate / backtest rules | blazerules CLI | blazerules validate · blazerules backtest |
| Ingest live events | blazerules_agent | blazerules_agent --input http|stdin|file_tail |
| Observe decisions | blazerules_dashboard | blazerules_dashboard --decision-log … |
| Batch replay / offline | blazerules CLI | blazerules eval … |
| In-process integration | Python SDK | blazerules.RuleEngine |
Where to go next
Updated 9 days ago