Write the rule.
Chalk brings the data.
A fictional fintech's risk team decides payouts, logins and card transactions with rules. Each rule names the features it needs, and the rules engine asks Chalk for exactly those at decision time. Edit a rule, or switch on one that needs data no rule used before, and watch Chalk compute it on demand.
Pick an event, then change the rules
Choose an event from the stream to see which rules fired, every feature value each rule pulled from Chalk, and the query the engine sent. Then switch on takeover_model for a login, or edit a rule to use a feature no rule asked for yet, and run it again. Decisions run live against a Chalk environment; Quillfeather Pay, its users, merchants and history are made up.
The rules decide. Chalk computes.
Rules engines are good at logic and bad at data. Every new rule tends to need a new signal, and every new signal tends to need a new pipeline. Here, the rule asks and Chalk answers.
Rules declare their data
A rule is one expression over named features. The engine parses it before it runs, so it knows exactly which features each enabled rule references.
Chalk computes on demand
The engine sends one query whose outputs are only those features. Chalk plans just the resolvers they need: windowed aggregations, lookups, enrichments and a model.
Decide in one pass
Each rule is evaluated against the returned values, and the most severe action wins. The demo shows every value, where it came from and what Chalk ran.
Features in Chalk, logic in your rules
Define the signals your rules use as Chalk features: aggregations over event history, lookups, enrichments and model scores. The rules engine keeps a map from rule names to feature names and requests only the ones its enabled rules reference.
In this demo the engine is a few hundred lines of Python on a Chalk scaling group, and the features are a small Chalk project. In the decisions you run, Chalk typically executes in 30 to 250 milliseconds.
from chalk import DataFrame, Windowed, windowed
from chalk.features import _, features, has_many, has_one
@features
class User:
id: str
signup_at: datetime
payouts: DataFrame[Payout] = has_many(lambda: Payout.user_id == User.id)
logins: DataFrame[Login] = has_many(lambda: Login.user_id == User.id)
account_age_days: float
# Windowed aggregations, computed at query time over the user's history.
avg_payout: Windowed[float] = windowed(
"30d", expression=_.payouts[_.amount, _.ts > _.chalk_window].mean(),
)
payout_count: Windowed[int] = windowed(
"1d", "7d", expression=_.payouts[_.ts > _.chalk_window].count(), default=0,
)
@features
class RiskEvent:
id: str
user_id: str
amount: float
device_id: str
ip: str
user: User = has_one(lambda: User.id == RiskEvent.user_id)
is_new_device: bool # derived from the user's login history
geo_velocity_kmh: float # derived from the last login's location
ip_risk: float # enrichment from a network-intelligence lookup
ato_risk_score: float # model-backed: an account-takeover modelRules that can use any signal
Keep the rules your risk team owns, and give them every signal you can define.
No feature pipeline per rule
A new rule that needs new data needs a Chalk feature, not a new job, table or cache. Every rule can use every feature as soon as it is deployed.
Only what is needed
Unused features are never computed. A login decision never scans payout history; a payout decision never calls the IP-reputation lookup.
Windows at decision time
Counts and averages over the last ten minutes, day or ninety days are computed against the moment of the decision, not a nightly snapshot.
Models are features too
A rule can threshold a model score the same way it thresholds a count. Chalk computes the model's inputs and the score in the same query.
Safe to edit
Rules are a restricted expression language: comparisons, arithmetic and boolean logic over known features. No code runs, and sizes are capped.
Explainable decisions
Every decision carries the values each rule saw and the resolver that produced them, so an analyst can see exactly why a payout was held.
Give your rules the data they ask for
Talk to an engineer about feeding your decision rules with features computed at decision time.