Skip to main content
DB
Back homeUse case

Fraud & risk

Score every event against state that is current, enrich it with the reference data that was true at the time, and show afterwards what a decision saw.

The problem

Where the usual stack breaks

  1. 01

    Fraud and risk scoring live or die on one property: the state you score against has to be current. A velocity check that reads a rolling total maintained somewhere else, on its own schedule, is not a check. It is a delay before the fraud clears.

  2. 02

    Most stacks build in exactly that delay: the write goes to one system, the feature store updates asynchronously, and the scorer reads a stale snapshot. The gap is small in time and large in money.

  3. 03

    You want per-account state kept from the same writes you score, events enriched with reference data at processing time, and a way to reproduce the state a decision was based on.

Architecture

How NYXDB does it

Payments land in one table; a transformation keeps per-account totals current and a LOOKUP JOIN stamps each payment with its region’s risk weight as it is processed; every accepted version is kept, so an audit can reread the state at any commit. A committed write is visible to the very next statement on the node.

  1. 01

    Write

    A payment is in the write-ahead log before the acknowledgement comes back.

  2. 02

    Fold

    Running totals per account are always current, maintained by a grouped CREATE TRANSFORMATION.

  3. 03

    Enrich

    Each payment is stamped with the reference data that was true when it was processed, with LOOKUP JOIN; a later change never rewrites history.

  4. 04

    Audit

    Reread the state exactly as the decision saw it, with AS OF COMMIT and the sequence from the write receipt.

Examples

In practice

The examples use the same two tables on every page. See the SQL reference for full syntax.

Activity per balance, kept current by a transformation
CREATE TRANSFORMATION balance_activity INTO balances ATTRIBUTE GROUP activity AS
SELECT
chain_id,
recipient AS address,
token,
COUNT(*) AS transfer_count,
MAX(transferred_at) AS last_transfer_at,
MAX(_index) AS activity_index
FROM transfers
GROUP BY chain_id, recipient, token;

Capabilities

What you get out of the box

Current per-account state

Transformations keep totals current from the same writes the scorer reads.

Maintained aggregates

Counts, sums, minimums, maximums and averages stay current, maintained by transformations.

Enrichment at processing time

Each event carries the reference value that was current when it was processed, with LOOKUP JOIN.

Auditable state

Every accepted version is kept, so you can reread a table exactly as it was at the moment that mattered, with AS OF COMMIT.

Streaming scores

Follow the risk table as it changes, with STREAM SELECT.

Indexed lookups

Point lookups on non-key columns are served by POSTINGS and BLOOM indexes.

Learn more

Score against state that is current

Run a node, fold payments per account and enrich them with a lookup.