Search fraud-ring connections with Chalk graph resolvers
This demo shows how to run PII normalization and graph queries on Chalk to inspect an applicant’s connections to labelled rings and high-risk accounts. Select a fictional applicant and inspect the matches, then explore the code to see how it works.
Compare an ordinary shared address with a risky connection
Use the applicant examples to separate normalized identity links from the scoring policy applied to them.
Try this experiment · expected results
1Start with a normalization-only match
Select Only normalization sees it and compare raw and normalized values.
Expected: The graph reveals links that exact matching on the submitted strings would miss.
2Remove one matching attribute
After the query finishes, untick a phone, email, or address and compare the graph and score.
Expected: The result shows which attribute carries the connection; shared contact data alone does not establish fraud.
The fictional book contains 29,967 people. Normalization groups 28,986 phone strings into 25,745 numbers and 26,659 addresses into 20,235.
Different-looking contact details can belong to the same fraud ring. Chalk normalizes the values, follows shared contacts, and returns the connections behind this fictional application’s score.
| Use | Raw, as submitted | Normalized by Chalk |
|---|---|---|
| (500) 555-0154 | +15005550154 Digits only, country code 1, E.164 | |
| milo.brindlewick+apply@example.com | milo.brindlewick@example.com Lowercase, drop +tag, fold mbx.test into mailbox.test, drop dots there | |
| 901 Thimbleberry Road, Lower Quillbrook, ZZ 00052 | 901 thimbleberry rd lower quillbrook zz 00052 Lowercase, fold punctuation, USPS abbreviations, units as apt building: 901 thimbleberry rd lower quillbrook zz 00052 |
Brightwater Lending, its customers and applicants are fictional: phones are in the reserved 555-01xx range, emails use example and .test domains, and the streets are invented. The population is stored as Vortex, SpiralDB's open columnar format, in a Chalk Volume, and loaded from there into a real Neo4j database running as a Chalk scaling group; it is not read through SpiralDB itself. Every decision above is a real Chalk online query against a branch that holds these feature definitions, and each applicant shows a recorded run of the same query instantly while the live one runs. The approve, review and decline labels come from the example’s hand-written policy; they are not validated fraud predictions or real lending decisions.
Built on Chalk
Chalk runs the normalization, graph resolver and scoring expressions in one online query. The example defines which identifiers match, how links are weighted and which scores produce approve, review or decline.
Reuse normalization expressions
The applicant and stored population use the same Chalk feature expressions for phones, email aliases and addresses. The example’s matching rules determine which raw values collapse together.
Graph context from Python
A Chalk Python resolver calls Neo4j with Cypher and returns graph results as features. The query combines them with the example’s scoring expressions.
Rules in the feature definitions
Chalk evaluates the example’s weights and thresholds as native feature expressions. The application defines the policy, and the query returns its score alongside the supporting features.
Return the matching subgraph
The query returns graph features and the subgraph alongside the decision. The application shows which normalized values link the applicant to the accounts it found.
Run it on your own
You will need uv, the Chalk CLI and a Chalk environment. Clone the example, log in, deploy the features to a branch, normalize and upload the population, then deploy the graph and set FRAUD_GRAPH_URL.
The population is synthetic. Adapt the normalization rules to your identifiers and review the graph traversal, link weights and decision thresholds against your own labelled cases before using the results in an onboarding workflow.
git clone https://github.com/chalk-ai/fraud-ring-search
cd fraud-ring-search
# Features, normalization and scoring on a branch.
chalk apply --branch fraud-rings
# Normalize the population with the same Chalk expressions, store it as Vortex in a Chalk Volume.
./build_population.py normalize --branch fraud-rings
./build_population.py upload
# Neo4j loads the Vortex tables and serves the PII graph read-only, inside your cluster.
./deploy_graph.py
chalk secret set FRAUD_GRAPH_URL=http://<graph service>:7474
chalk query --branch fraud-rings --in applicant.id=APP-106 \
--out applicant.decision --out applicant.risk_scoreBuild identity graph features on Chalk
Identity graphs, device signals and bureau data: talk to an engineer about turning them into features your onboarding decisions can act on.