DEV Community

Rajesh S
Rajesh S

Posted on

I built an open source Bitcoin AML forensics tool in TypeScript that runs on Android

ChainTrail — Open Source Bitcoin AML Forensics
I spent the last few weeks building ChainTrail, a real-time Bitcoin
transaction monitoring and AML forensics platform. The interesting part?
I built and run the entire thing on my Android phone using Termux.

What it does

ChainTrail connects to the live Bitcoin mempool via WebSocket and lets
investigators trace fund flows across the blockchain graph.

Live demo: https://cary-uncompensated-joanie.ngrok-free.dev

Key features

  • Live mempool stream via mempool.space WebSocket
  • Transaction graph tracer using PostgreSQL recursive CTEs
  • Interactive D3.js graph visualization
  • 1,258+ threat intel entries (WannaCry, Lazarus Group, Silk Road, OFAC)
  • ML risk scoring 0-100 with feature extraction
  • Address clustering using Union-Find heuristic
  • Pattern detection: layering, smurfing, fan-out, peeling chain
  • Case management with PDF report export
  • Ethereum support with Tornado Cash detection
  • Webhook alerts via Telegram and Discord

Tech stack

  • Backend: Node.js + TypeScript + Express
  • Database: PostgreSQL (recursive CTEs for graph traversal)
  • Cache: Redis
  • Frontend: Next.js + Tailwind + D3.js
  • Data: mempool.space WebSocket

**The interesting part — graph traversal

**
The core of ChainTrail is a PostgreSQL recursive CTE that walks the
Bitcoin transaction graph:


sql
WITH RECURSIVE tx_graph AS 
![ ](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5wfcjbr368zg7k70ow5o.jpg)(
  SELECT i.from_address, t.txid, o.value_satoshi, 1 AS hop,
         ARRAY[i.from_address] AS path
  FROM tx_inputs i
  JOIN transactions t ON t.txid = i.txid
  JOIN tx_outputs o ON o.txid = t.txid
  WHERE o.to_address = $1

  UNION ALL

  SELECT i.from_address, t.txid, o.value_satoshi, g.hop + 1,
         g.path || i.from_address
  FROM tx_graph g
  JOIN tx_inputs i ON ...
  WHERE g.hop < $2
    AND NOT (i.from_address = ANY(g.path))
)
SELECT * FROM tx_graph ORDER BY hop;
This lets you trace any Bitcoin address N hops backward to find the
origin of funds — identifying mixers, exchanges, and sanctioned wallets
along the way.
**ML Risk Scoring**
Each address gets a risk score 0-100 based on extracted features:
Direct threat intel match → +40 to +100
Proximity to bad actor (hop distance) → decays with distance
Fan-out pattern (sending to many addresses) → +20
Fan-in pattern (receiving from many) → +15
Known exchange → -30 (reduces false positives)
Cluster risk level → +5 to +50

**What I learned**
PostgreSQL recursive CTEs are surprisingly powerful for graph
traversal — no graph database needed
mempool.space WebSocket pushes ~6 new transactions every few seconds
Union-Find is the right data structure for address clustering
Running a full stack on Android is actually practical for development
**What's next**
OFAC sanctions auto-sync from US Treasury
Better ML model trained on labeled data
Lightning Network support
Deploy to cloud for production use
Links
GitHub: https://github.com/rajeshselvam02/chaintail
Live demo: https://cary-uncompensated-joanie.ngrok-free.dev
Pricing: https://chaintail-landing.vercel.app
Built with ❤️ by Rajesh Selvam
---
Enter fullscreen mode Exit fullscreen mode

Top comments (0)