Live demo · Rules engine

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.

Up to 12 rules of 240 characters · 30 decisions a minuteOpen in new tab

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.

Getting started

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 model

Rules 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.