[BankWeave logo: a blue B whose bowls are split by one thread]
BankWeave
[CI]
[Release]
[Live]
[MCP: hosted HTTP]
[API image]
[MCP image]
[FalkorDB Cloud]
[License: MIT]
Many banks, one ring, every claim proven.
[Each bank sees only its piece; FalkorDB's consortium graph sees the ring; agents prove every claim]
BankWeave is a team of AI agents that investigates money-laundering alerts across several
banks, with FalkorDB as its shared brain. It clears innocent alerts fast, exposes laundering
rings that no single bank can see, and backs every conclusion with a graph path you can click
through. It also knows how many independent reasons it has for each belief, notices when one
of them dies, and refuses to file a report it can't prove.
- Tracks: 01 Agents That Act on Connected Data (primary), 02 Agent Memory and Coordination, Best blog post
- Live deployment: https://bankweave.vercel.app (UI on Vercel) · API https://bankweave-api.onrender.com/docs (Render) · graph: FalkorDB Cloud (ap-south-1)
- MCP Registry: published as
io.github.LSUDOKO/bankweave (v3.0.0, streamable HTTP)
- Use it from Claude:
claude mcp add --transport http bankweave https://bankweave-api.onrender.com/mcp, or as a stdio package: claude mcp add bankweave -- docker run -i --rm ghcr.io/lsudoko/bankweave-mcp (17 read-only FalkorDB tools, each returning the Cypher it ran; docs/MCP.md)
- Proof in FalkorDB's own browser: screenshots and a recording of the ring, PageRank, card taps, SAR provenance and vector search running on our Cloud instance
- Understand it in five minutes: the 3D explainer (eight scenes, each one FalkorDB call) and the graph explorer (twelve presets, run live)
- Demo video: watch on YouTube, on the landing page or download the MP4 (narrated, captions burned in): a whiteboard explainer of the problem, the problem film's close, then the live product on FalkorDB Cloud, the hardware edge, MCP and FalkorDB's own browser. Build it yourself:
video/build.py --long
- Blog post:
docs/BLOG.md
Running now on real data. The production instance on FalkorDB Cloud holds IBM's labelled AML
transactions for 6 banks: 3,024 accounts, 24,990 transfers, 36 graphs (6 bank graphs, the
consortium graph with 10,178 nodes and 37,448 edges, 27 case graphs, agent memory and the typology
knowledge base). The agents have triaged 1,208 alerts: 1,113 cleared with a stated reason and
95 escalated into cases, each case with claims, evidence, the Cypher behind every piece of
evidence, a provenance-gated report and a signed Merkle seal.
BankWeave is an independent project built for FalkorDB Graph Hacks. It is not a FalkorDB product and is
unrelated to FalkorDB's QueryWeaver or Weights & Biases Weave.
The problem
- Alert fatigue. Rule-based transaction monitoring flags single signals without context;
industry sources put 85-95% of alerts as false positives.
- Fragmented visibility. A bank only sees transactions that touch it. Laundering schemes
spread across institutions, so each bank's slice looks innocent.
- Unprovable reports. A suspicious activity report (SAR) has to justify itself, and today
that justification is assembled by hand.
What BankWeave does
- Loads IBM's public AMLworld laundering dataset into one isolated FalkorDB graph per bank plus a
consortium graph that links them with salted, hashed account ids.
- A team of agents (Orchestrator, Triage, Investigator, Skeptic, Reporter with a separate
Auditor, Archivist) works the alert queue through a task blackboard stored in the graph,
calling native FalkorDB algorithms as tools (also served over MCP).
- Innocent alerts are cleared in milliseconds with a reason; suspicious ones become cases,
each in its own
case_<id> graph, where claims are linked to evidence and the query that
produced it.
- A trust layer built from three 2026 papers keeps the agents honest (below).
- Humans approve every action. Everything is measured against IBM's labels on a scoreboard.
What's new in 3.0
Proof in the official FalkorDB Browser
The same data the agents use, opened in FalkorDB's own browser (browser.falkordb.com, connected
to our FalkorDB Cloud instance: FalkorDB v4.22, 36 graphs, about 81 MB). Every query below is
read-only and was typed into the browser; the timings are the engine's own RT readout. The
connection bar with the instance hostname is cropped out.
[Running BankWeave's queries in the FalkorDB Browser]
Hosted MCP, called live (no install, plain HTTP):
The top account matches row 1 of the PageRank query in the FalkorDB Browser above.
What's new in 2.0
The card terminals, enclave and security key run as software device twins (the ESP32 terminal in
Wokwi), producing real EMV/ISO 8583 data, real signatures, real key exchange and real graph writes
through the same API a physical device would call.
Screenshots from the live site
Captured on https://bankweave.vercel.app against the Render API and FalkorDB Cloud
(ap-south-1), agents on Groq's free tier; the mode badge reads "Live · LLM + FalkorDB".
Measured, with the evidence highlighted. The landing page's "The numbers, not the pitch"
shows these captures of the live Evaluation page:
Hardware trust layer (device twins, real cryptography)
Banks don't let software act alone: terminals produce the device evidence, HSMs and enclaves
guard identity hashing, and a human signs off with a security key. BankWeave runs all three as device twins
in software and wires each into FalkorDB.
Recorded on the real tools. The sketch in hardware/wokwi/ running in the Wokwi ESP32
simulator: it joins Wokwi-GUEST, Card A is tapped on the Mumbai POS, the board posts it over
HTTPS to the live API, which answers 00 APPROVED, writes the CARD_TXN edge into FalkorDB
Cloud and returns the ISO 8583 field 55 printed on the serial monitor. EMVLab.org then decodes
that exact TLV (amount, currency, IFD serial D0006301, ATC, cryptogram, PAN).
The Hardware Lab on the live site:
What each device twin produces:
External verifiers: EMVLab TLV decoder ·
EMVLab cryptogram tool ·
WebAuthn.me debugger ·
Chrome virtual authenticator ·
Wokwi ESP32 · OpenSanctions ·
FalkorDB Cloud.
Why FalkorDB is essential
Turn FalkorDB off and BankWeave can't do any of its core work:
FalkorDB features in use: multigraph (per-bank, consortium, per-case, procedures, typology KB
graphs in one instance), native algorithms (algo.wcc, algo.labelPropagation,
algo.pageRank, algo.betweenness, algo.HarmonicCentrality, algo.SPpaths, algo.SSpaths,
algo.maxFlow, algo.bfs, algo.MSF), full-text indexes with Double Metaphone phonetic
matching and English stemming, a vector index (HNSW) on behaviour profiles, JavaScript
UDFs (FalkorDB's FLEX library and our own aml_temporal), GRAPH.COPY sandboxes,
GRAPH.PROFILE/GRAPH.EXPLAIN, GRAPH.MEMORY USAGE, ACLs scoped to one graph, and
the Redis protocol for live pub/sub.
Proof: every FalkorDB feature BankWeave uses
Proof from the live FalkorDB Cloud instance
Screenshots from FalkorDB Browser connected to the production instance the live site uses
(docs/img/cloud/):
Proof from local runs on the IBM data
Architecture
[Architecture: Next.js console, MCP clients and card terminals into the FastAPI service and agent worker; Cypher and algo.* into FalkorDB Cloud]
The LLMs plan, choose tools and write prose. Cypher and algo.* do the computation. With no
API key the agents run a deterministic policy (labelled as such in the UI), so the system is
always runnable.
Graph model
[Graph model: bank graphs, the consortium graph and case graphs, with their labels and relationships]
Full schema: docs/GRAPH_MODEL.md. In short:
consortium: Account (hashed id, bank, degrees, PageRank, component, community, motif
score, pass-through stats, 16-dim behaviour vector) -[:TRANSFERS]-> Account, a collapsed
-[:FLOWS]-> edge per account pair, the identity layer (Person/Company -[:OWNS]->;
USES_DEVICE, USES_IP, REGISTERED_AT), Alert, Decision, blackboard Task, case mirrors.
bank_<id>: the bank's own Accounts plus ExternalAccount stubs; only transfers touching
the bank.
case_<id>: Claim -[:SUPPORTED_BY|CHALLENGED_BY]-> Evidence {origin, cypher},
Claim -[:DERIVED_FROM]-> Claim, Action -[:JUSTIFIED_BY]-> Claim,
SAR -[:CONTAINS]-> Sentence -[:CITES]-> Evidence|Claim, Evidence -[:SUPERSEDES]-> Evidence,
Case -[:HAD_EVENT]-> Event -[:NEXT]-> Event.
Cypher queries and algorithms
Every query the product runs is listed in docs/QUERIES.md, generated from
the code. A few that carry the product:
Things FalkorDB doesn't ship natively, done in plain Cypher (and said so): degree
(precomputed), time-ordered cycles (iterative time-respecting expansion, one query per hop),
3-cycles (motif score), eigenvector centrality (PageRank used instead in SPNSA).
Agents and MCP tools
[The agent pipeline and the live counts on FalkorDB Cloud]
Hosted MCP (read-only, no install): claude mcp add --transport http bankweave https://bankweave-api.onrender.com/mcp.
It serves 17 read-only tools over Streamable HTTP from the same Render API; every tool runs through
GRAPH.RO_QUERY and returns the graph, Cypher, parameters and time. Install lines for Claude
Desktop and Cursor, the tool table and a real transcript are in docs/MCP.md.
The full local MCP server (python -m mcp_server.server, stdio or --http PORT) exposes all 34 tools (32 agent tools plus cypher_readonly and list_graphs),
including trace_flow, find_hub, find_bridges, money_at_risk, typology_patterns,
passthrough_check, clue_expand, hunt_regions, resolve_entity, behavior_neighbors,
kyc_lookup, kb_query, echo_check, invalidate_evidence, premise_check, plus
cypher_readonly and list_graphs. Every result includes the Cypher it ran.
Claude Desktop config:
The trust layer
[Echo-proof confidence, cascading invalidation and the provenance gate, with measured results]
- Every
Evidence carries an origin (e.g. attr:D00063, flow:a->b, typ:cycle:a).
Confidence is computed over distinct origins; a 200-unit building address is down-weighted.
The UI shows "N paths, K independent sources" and the naive figure beside it.
- Superseding evidence never deletes it. Dependents move along a validity lattice
(
active < needs_verification < historical_only < superseded); actions resting on stale
evidence need an explicit override note to approve.
- The SAR is drafted from a closed bundle. Code checks run first (cited ids exist, are active,
belong to the case); a different model then judges entailment per sentence. It publishes
only at provenance fidelity >= 0.95, after at most 3 repairs; otherwise it writes a
divergence report with the Investigator's and Skeptic's positions side by side. A
recommendation can't rest on a claim below 0.5 echo-proof confidence.
Memory and coordination (Track 02)
- Shared state: the blackboard is
Task nodes; claiming is one atomic query (verified with
6 concurrent workers, each task done exactly once).
- Kill and resume: workers heartbeat; stale claims are reset and any worker continues from
the graph. Demo control has a Kill button; the case page has "Resume from graph".
- Episodic: each case's
Event timeline (HAD_EVENT, NEXT), replayable in the UI.
- Semantic: claims and evidence with validity states and history (
beliefs_as_of).
- Procedural: the
procedures graph stores tool sequences that led to approved outcomes.
- Cross-case recall: claim summaries mirrored into the consortium (
Account <-[:ABOUT]- Claim).
- Isolation: per-bank and per-case graphs in one FalkorDB instance.
Evaluation
[Measured results: E2, E1, E7 and FalkorDB Cloud timings]
E1: alert triage (IBM HI-Small, 20-bank consortium)
4,000 alerted accounts from the later half of the period (129 illicit, base rate 3.2%); weights and thresholds from the earlier half only.
eval/results/E1_alert_triage.json, commit 49f9388, HI-Small_Trans.csv (20 banks, graph prefix 'ibm_')
E2: no bank can see the ring alone
Across 36 laundering groups that span banks (on average 3.5 banks each), the best-placed single bank holds 43.8% of the ring's accounts and holds a whole ring in 0.0% of cases. It does observe 95.7% of the laundering transfers, but most counterparties are other banks' customers it can't investigate. The consortium graph holds 100% of every ring.
eval/results/E2_consortium_vs_single_bank.json, commit 49f9388, HI-Small_Trans.csv (20 banks, graph prefix 'ibm_')
E3: hunt mode (SPNSA reproduction, k=200, r=1)
Base rate in this graph: 2.5%. We did not reproduce the paper's ranking: with its parameters the degree penalty inverts the motif score on our 20-bank slice (account-level AUC 0.3743, vs 0.638 without the penalty), so degree imbalance wins here. All feeds beat random.
eval/results/E3_spnsa_hunt.json, commit cc7037a, HI-Small_Trans.csv (20 banks, graph prefix 'ibm_')
E4: pass-through matching
708 accounts flagged; 4.8% of them illicit vs a 2.5% base rate. Chain-deduplicated value: 74,961,854,871 (IBM amounts are in many currencies, so this is a raw sum, not a USD figure).
eval/results/E4_passthrough.json, commit 154672b, HI-Small_Trans.csv (20 banks, graph prefix 'ibm_')
E5: clue-guided recovery
From 2 clue members of 12 laundering groups: best round 2 reaches precision 33.3%, recall 3.4%. This is weak; the graph-signal version (no GNN) mostly finds the clue's immediate partners.
eval/results/E5_clue_expansion.json, commit 706ae01, HI-Small_Trans.csv (20 banks, graph prefix 'ibm_')
E6: trust layer on the agents' real cases
Over 28 cases the agents built from the live IBM slice:
- Echo: 27.8% of 510 evidence items repeated an origin already seen; 37 of 101 claims had more supporting paths than independent sources. Mean confidence 41.7% echo-proof vs 48.8% counting paths.
- Provenance gate: 25 of 28 SARs published, 3 ended as divergence reports; mean provenance fidelity 0.988; 26 needed at least one repair round.
- Cascading invalidation: affected-node recall 100.0% and precision 100.0% over 50 scripted dependency graphs.
- Stale approvals: 0 actions approved on overturned evidence without an override.
eval/results/E6_trust_layer.json, commit f3cff38, IBM_SLICE_Trans.csv (6 banks, graph prefix '')
E7: speed (one local machine)
Consortium graph: 28,307 accounts, 1,315,597 transfers.
eval/results/E7_performance.json, commit b8a1292, ibm_
Deployment (live)
Render's free plan sleeps after 15 minutes idle; the first request wakes it (about a minute) and
the agents resume from the graph blackboard. A GitHub Actions job pings /health every 10 minutes
to keep it warm, and while it wakes the main pages stay available from captured real API responses
(scripts/capture_snapshots.py), labelled on screen.
FalkorDB Cloud's free tier has 100 MB and noeviction. Since 2.0 the graphs load in compact form
(KYC_ARGS=--compact, no unused txn_id edge index, no id indexes on ~100-node case graphs),
/health reports used memory, and scripts/cloud_reseed.sh rebuilds the cloud graphs while keeping
pinned cases. Old case graphs are pruned automatically only when CAPACITY_PRUNE=true.
Optional environment variables: OPENSANCTIONS_API_KEY (live sanctions screening),
HELIUS_API_KEY (Solana wallet lookups), EVM_RPC_URL, OTEL_EXPORTER_OTLP_ENDPOINT and
OTEL_EXPORTER_OTLP_HEADERS (export agent spans to Langfuse, Jaeger or any OTLP collector),
DOSSIER_SIGNING_KEY (hex Ed25519 seed for dossier signatures), PINNED_CASES, CAPACITY_PRUNE.
Run it locally
Prebuilt API image: docker pull ghcr.io/lsudoko/bankweave-api:latest (published on every release).
Open http://localhost:3001, go to Demo control and press Start agents (the agents begin triaging alerts
and opening cases; the dashboard updates live).
IBM data for evaluation: scripts/download_ibm.sh (Kaggle token) or the public mirror
hf.co/datasets/eexzzm/IBM-Transactions-for-Anti-Money-Laundering-HI-Small-Trans, then
GRAPH_PREFIX=ibm_ scripts/seed_all.sh data/raw/HI-Small_Trans.csv --top-n 20 and
GRAPH_PREFIX=ibm_ eval/run_all.sh.
Tests: cd backend && uv run pytest (55 tests, against the local FalkorDB).
Hardware: the ESP32 terminal in hardware/wokwi/ runs in Wokwi and posts card taps over HTTPS to
POST /hardware/pos-tap (see its README).
Data
- IBM Transactions for Anti-Money Laundering (AMLworld, HI-Small), the standard labelled AML
benchmark, which IBM generated with a multi-agent world model because real bank transactions
cannot be published: 5,078,345 transactions, 5,177 labelled laundering transfers, 8 laundering
typologies. Labels are used only in
eval/. The live instance runs a 6-bank slice of it
(3,024 accounts, 24,990 transfers).
- Identity layer (
backend/app/services/kyc_synth.py, seed 42): people,
name variants, devices, IPs, addresses and a Postgres KYC table. Generation rule, reported so
leakage can be audited: attempt members share a device with P=0.40, innocent accounts with
P=0.03; 6% of innocent accounts share a family address/IP; one 200-unit building (A-77) houses
about 15 accounts including some attempt members.
- Sample dataset for local setup (
data/demo, seed 42): IBM-format transactions with innocent
background and 16 cross-bank laundering schemes in the 8 IBM shapes.
- No personal data of real people anywhere, as AML compliance requires for a public project.
AI tools disclosure
Built with help from Claude Code (Anthropic), which wrote much of the code under the
team's direction; every module was reviewed and is understood by the team. At runtime the
agents use free-tier LLMs through Groq (writer qwen/qwen3.8-27b, auditor openai/gpt-oss-120b,
a different and larger model from a different vendor), with OpenRouter free models as fallback.
Scope and design notes
- Data is IBM's AMLworld benchmark plus a documented identity layer; connecting a bank's own feed
means loading it in the same graph model (
scripts/seed_all.sh takes any IBM-format CSV).
- Without IBM's Patterns file, laundering attempts are approximated as connected groups of
laundering transfers (stated in each result file).
- Clue-guided recovery uses graph signals, not the paper's GNN.
- Consortium ids are salted hashes. The ECDH-PSI handshake is real X25519 arithmetic; in this
deployment both banks' sides run in one service, and in production each side runs at its own bank.
- The enclave, card terminals and VaultKey run as software device twins (the ESP32 in Wokwi) with
real cryptography (HMAC, SHA-384 PCRs, ECDSA P-384/P-256, Ed25519, X25519); physical HSMs,
terminals and keys plug into the same endpoints. The EMV application cryptogram (9F26) is an HMAC.
- The SAR export is FinCEN-style XML for review, not a BSA E-Filing submission.
- Sanctions screening uses live OpenSanctions with an API key and a bundled sample list without one;
name-only matches are confirmed with date of birth or country.
- The threshold explorer on the Evaluation page runs on the live slice, which overlaps the
model's training data, so its numbers are optimistic; the held-out result is E1's table.
License
MIT, see LICENSE.