{"slug": "kavach-final-round-a-fraud-investigator-that-shows-its-work-on-tigergraph", "title": "Kavach, final round: a fraud investigator that shows its work, on TigerGraph", "summary": "Team PixelPaws, made up of Subhojyoti Maity and Arni Bachhar, built Kavach, a card-fraud investigation agent that runs on TigerGraph and keeps the LLM out of the decision path: 45 read-only installed GSQL queries serve as the agent's tools, and the model only writes summaries and SAR narratives. The system fuses vector search over closed-case notes with graph traversal in a fused_precedent query, which ranked different precedents than plain vector search on 13 of 13 cases with connected precedent, and uses label propagation (ring_wcc) to surface fraud rings, recovering 14 of 16 cards in the HHF-006 ring with no detector input. The agent ran against 650,938 transactions and about 3.6M edges on a TigerGraph 4.2.5 Savanna workspace, with MCP access restricted to an allowlist of installed queries.", "body_md": "*TigerGraph x Hacker House Goa 2026, final round. Team PixelPaws: Subhojyoti Maity and Arni Bachhar.*\n\nIn the first round we built Kavach (\"shield\"), an agent that investigates card-fraud alerts on a TigerGraph graph. The final round raised the bar: 18 new alerts, 650,938 transactions (July 2016 to January 2017), the bank's 5,585 closed cases, a written Fraud Policy v1.0, and 14 challenges that read like a bank's audit checklist. This post covers what we built and how TigerGraph carries the weight.\n\nA bank's fraud model flags a transaction. Someone then has to decide: is it fraud, how far does it go, which other cards are involved, what should the bank do, and must it file a suspicious activity report (SAR)? Wrong one way, the fraud keeps running. Wrong the other, an innocent customer gets blocked.\n\nIt is tempting to paste the alert into an LLM and ask for a verdict. For a bank, that is the wrong design:\n\nSo in Kavach, **tools and policy decide, and the model only writes prose.**\n\nEvery alert goes through nine fixed steps, in the same order every time:\n\n`gemini-3.8-flash`), which writes the summary and SAR narrative. Every id in its text is checked; on failure a template is used and recorded.`written_to_graph: true`.\nA harness enforces hard bounds (80 tool calls per case, 90 s per call, 15 minutes per case) and fails closed. Tools sit behind one registry with three lanes: TigerGraph installed queries (`--backend tg`), the TigerGraph MCP server (`--backend mcp`) and a local DuckDB mirror (`--backend duck`).\n\nThe graph lives on a Savanna workspace (TigerGraph 4.2.5) at the smallest size, TG-00: 13 vertex types (from Customer, Card and Transaction to ClosedCase, InvestigationCase, CaseEvent, Approval and Detector), 650,938 transactions and about 3.6M edges, with counts checked against the source files.\n\n**Installed GSQL queries are the agent's tools.** There are 45 installed queries, all read-only. Each tool is one query with typed parameters, so \"who else paid this recipient email domain?\" is a traversal with accumulators, not a prompt. On the TigerGraph lane there is no fallback to DuckDB: a graph failure is an incident.\n\n**Vector search fused with traversal.** Closed-case notes, policy sections and investigation summaries carry 384-d embeddings searched with `vectorSearch()`. The interesting query is `fused_precedent`. It walks from the case card to its recent transactions, to the rare elements they carry (a device profile, recipient email domain or billing region used by 25 or fewer cards), to the other cards using them, to every closed case on those cards, then runs `vectorSearch(..., {candidate_set: X})` over exactly that set. The fused top 5 differed from plain vector search on 13 of 13 cases with connected precedent, and from plain traversal on 9 of 13. On two legitimate alerts (HHF-014, HHF-016), traversal alone ranked five confirmed frauds first; the fused query ranked five cleared cases first.\n\n**Connected components for rings.** `ring_wcc` runs label propagation for weakly connected components over a card graph whose edges are rare elements shared by atypical transactions in a 30-day window. Without the \"atypical\" restriction, the rules percolated into one 600 to 1,100 card component on every alert. With it, 8 of the 9 legitimate alerts stay single cards, and with no detector input it recovers 14 of the 16 cards in the HHF-006 ring.\n\n**MCP, restricted to an allowlist.** On the MCP lane the only MCP tool called is `tigergraph__run_installed_query`, and only for query names on an allowlist built from our own read-only GSQL files. Any other name raises `PermissionError` before a call. No GSQL, upsert or schema tool is reachable, and the LLM never sees a tool.\n\n**Write-back, read-back, hash chains.** Each investigation is upserted as an InvestigationCase with edges to its card, transactions, devices and similar closed cases. Edges use `vertex_must_exist`, so a link to a missing id is refused instead of creating an empty vertex. Case events and approvals are written insert-only with `new_vertex_only`; approvals form a sha256 hash chain, and the graph copy is read back and compared by count, chain hash and digest. All 18 answers carry `written_to_graph: true` only after that read-back.\n\nA miner searches seven generic pattern families (258 grid points) over the 5,585 closed cases in 24 seconds, without reading the analysts' notes. It only proposes patterns that separate confirmed fraud from both normal traffic and cleared customers, with a significance test corrected for the whole search.\n\nSearching blind, it put both of the bank's undocumented typologies on its shortlist: the just-under-$500 four-purchase burst and an anonymous-proxy device ring. It also proposed one detector nobody wrote: a fan-in spike where a rarely used recipient email domain is suddenly paid by three or more cards in one day (8 confirmed-fraud cases, 0 cleared, lift 18).\n\nNothing joins the toolkit until a human with the L2 role approves it. An analyst token was refused first and logged as denied; the lead then approved it with the L2 token. Here is what that detector changes on HHF-006, where the analyst asked what several affected cards have in common:\n\n| arm | HHF-006 decision | \n|---|---|\n| no grown detector, no typologies | uncertain, p 0.42; decline, case, monitor, escalate; no report | \n| typologies only | fraud, p 0.96, card_not_present_fraud; block and case; no ring, no report | \n| full system | fraud, p 0.88, undocumented ring: 3 transactions ($967.68) on the case card, 16 connected cards paying recipient domain sc.rr.com; BLOCK_CARD (L1), CREATE_CASE, FILE_REPORT (L2), MONITOR_CONNECTED_CARDS, ESCALATE_TO_ANALYST | \n\nIt changes no other case in the pack, so it cannot act against a legitimate customer elsewhere. On TigerGraph it runs through 13 installed GSQL mirrors and gives the identical decision.\n\n`denied` record in the append-only chain, and the route uses the server's own exposure, so a caller cannot lower it.\nEach has a fault-injection drill (`KAVACH_FAULT=graph:down`, `tool:any:5`, `llm:garbage`).\n\nOur 18 answers: 7 fraud, 9 legitimate, 2 uncertain.\n\n`kavach attest --all` re-runs every attested claim: 0 failing.`ring_wcc` confirms rather than changes the six ring answers today. It also places a legitimate alert's card (HHF-013) in the same component as the HHF-006 ring, which a human reviews.\nWhat worked first time:\n\n`vectorSearch()` with a `candidate_set` taken from a traversal. Asking for k equal to the set size returns a distance for every candidate; the ranking matched our local cosine to 4 decimals on all 18 cases.`MapAccum<STRING, MinAccum<INT>>` and a `WHILE ... LIMIT` loop compiled and ran on the first attempt.`new_vertex_only` and `vertex_must_exist` are exactly the append-only and no-invention guarantees an audit trail needs.`CREATE OR REPLACE QUERY` without `INSTALL` works as a quick compile check before the slow install.\nWhere we lost time:\n\nOn our wish list: an offline GSQL lint, nullable attributes, and a bulk vector loader.\n\nThanks for reading. Every number above has a command in the repo that reproduces it.", "url": "https://wpnews.pro/news/kavach-final-round-a-fraud-investigator-that-shows-its-work-on-tigergraph", "canonical_source": "https://dev.to/subhojyoti_maity/kavach-final-round-a-fraud-investigator-that-shows-its-work-on-tigergraph-52b7", "published_at": "2026-10-31 08:13:17+00:00", "updated_at": "2026-10-31 08:19:52.505885+00:00", "lang": "en", "topics": ["ai-agents", "ai-tools", "artificial-intelligence", "mlops"], "entities": ["TigerGraph", "Kavach", "Team PixelPaws", "Subhojyoti Maity", "Arni Bachhar", "Hacker House Goa 2026", "Gemini", "DuckDB"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/kavach-final-round-a-fraud-investigator-that-shows-its-work-on-tigergraph", "markdown": "https://wpnews.pro/news/kavach-final-round-a-fraud-investigator-that-shows-its-work-on-tigergraph.md", "text": "https://wpnews.pro/news/kavach-final-round-a-fraud-investigator-that-shows-its-work-on-tigergraph.txt", "jsonld": "https://wpnews.pro/news/kavach-final-round-a-fraud-investigator-that-shows-its-work-on-tigergraph.jsonld"}}