Real-time risk scoring with JetGraph

Score every event against committed graph history, then write the outcome back. JetGraph supplies graph signals; your application owns the model.

The scoring loop

Event
  → Query historical graph
  → Generate signals (velocity, novelty, context, cardinality)
  → Score (your rules or model)
  → Record event
  → Propagate risk (optional FlagNode)

Querying before insert avoids reading your own uncommitted write and keeps novelty honest. Recording declines is required: otherwise velocity under-counts attackers you already blocked.

Signals the engine can produce in this loop

SignalAPI / ideaRole in a score
VelocityGetVelocityCountBurst / session volume
Noveltyedge existence / compact pair storeFirst-time counterparties
Neighbor risknode context after flagsContagion from known-bad nodes
NeighborhoodGetNeighbors / Cypher MATCHShared devices, IPs, merchants
CardinalityHyperLogLog GetNeighborCountFan-out of an identifier
WindowsQueryEdgeWindowAmount or count in a time range

Architecture relative to a feature store

A feature store is typically tabular, batch- or stream-updated, and keyed by entity ID. JetGraph is a live graph: features that are relationships stay relationships. Many stacks will still have a feature store for non-graph features (credit bureau, device ML). JetGraph replaces the “join five tables plus a nightly graph job” part of authorize-time scoring. Comparison: vs feature stores.

Technical sketch (Rust-shaped)

// Phase 1 — historical graph
let v1h = features.get_velocity_count(card, "TRANSACTS_AT", 3600).await?;
let is_new = !graph.edge_exists(card, merchant, "TRANSACTS_AT").await?;
let ctx = features.get_fraud_context(card).await?;

// Phase 2 — your score
let mut risk = 0.0;
if is_new { risk += 0.15; }
if v1h > 30 { risk += 0.25; }
risk += 0.5 * ctx.max_neighbor_fraud_score;

// Phase 3 — always insert; flag on decline
graph.create_edge(card, merchant, "TRANSACTS_AT", decision).await?;
if risk > 0.7 { features.flag_node(card, 0.85, "auto_decline").await?; }

Runnable-oriented versions live in code examples and Rust client docs. Cypher can fetch related context; feature APIs remain the O(1) path for velocity and contagion.