Cerberus Graph is a high-performance Synchronous Fraud Detection Engine designed to intercept transactions before authorization.
Unlike asynchronous monitoring systems that alert on fraud post-factum, Cerberus sits in the critical path of the payment gateway. It leverages Go's concurrency primitives to enforce a "Defense in Depth" strategy, querying graph relationships, statistical velocity, and ML models in parallel.
Architectural Goal: To answer the question "Is this transaction fraudulent?" in under 200ms by analyzing the user's hidden connections in a Graph Database.
The system consumes transaction requests from Kafka and orchestrates three parallel checks using the Scatter-Gather pattern:
- Question: "Did this user spend >$500 in the last 10 minutes?"
- Impl: Uses sliding window counters in Redis. Extremely fast (sub-millisecond).
- Question: "Is this card linked to a device that was used by a known fraudster?"
- Impl: Cypher queries designed for depth-limited traversal (2-hops).
- Optimization: Uses read-replicas for the graph to handle high throughput.
- Question: "Does the transaction metadata look suspicious?"
- Impl: Loads an ONNX model directly in Go (or calls a gRPC sidecar) to score the transaction probability.
Cerberus creates a custom stream processing topology in Go (without heavy frameworks like Flink), giving us fine-grained control over memory and threads.
- Ingestion: A Kafka Consumer Group reads the
tx-pendingtopic. - Fan-Out: For each transaction, a Goroutine spawns 3 sub-routines (The 3 Heads).
- Aggregation: Results are collected via channels. If any "Head" returns a
RISK_CRITICALsignal, the transaction is blocked immediately (fail-fast). - Decision: The final verdict is produced to
tx-authorizedortx-rejectedtopics.
- Core Engine: Go (Golang)
- Messaging: Apache Kafka (Redpanda for dev)
- Graph DB: Neo4j (Cypher)
- Cache/State: Redis (Pipeline execution)
- ML Runtime: ONNX Runtime / TensorFlow Lite
Detecting a "Money Mule" ring by checking if the recipient is connected to known bad actors:
// Check if the recipient shares a DeviceID with a blacklisted user
MATCH (recipient:User {id: $to_user})
MATCH (recipient)-[:USED_DEVICE]->(d:Device)<-[:USED_DEVICE]-(bad:User)
WHERE bad.status = 'FRAUD'
RETURN count(bad) > 0 as is_fraud_ring