All Projects
In ProductionSolo Engineer & ArchitectFlagship · Advanced

CRIMSON

Intelligence-grade criminal network analysis.

CRIMSON turns raw investigative data into a living, queryable intelligence graph — giving analysts the ability to surface hidden criminal networks in minutes, not weeks.

Live Demo
CRIMSON

The Story

Why this exists.

The Problem

Law enforcement analysts deal with thousands of unstructured data points — call records, witness statements, transaction logs — that are manually cross-referenced in spreadsheets. Connections are missed. Investigations stall.

Why It Matters

A single missed link in a criminal network can mean an investigation fails, a suspect walks free, or a network grows. Automated graph analysis with AI-assisted entity resolution changes the equation entirely.

The Solution

CRIMSON ingests structured and unstructured data, runs entity resolution to deduplicate identities, builds a persistent graph database, and exposes a query interface powered by Gemini AI. Analysts can ask natural-language questions and get graph-backed answers.

Product Features

What it does.

Live Criminal Network Graph

Interactive force-directed graph showing entities (persons, organizations, locations, events) and their weighted relationships. Zoom, filter, and cluster by connection strength.

Live Criminal Network Graph

AI Entity Resolution

Gemini AI extracts named entities from unstructured text — reports, transcripts, messages — and resolves duplicates using embedding similarity. A single person mentioned under five aliases becomes one node.

AI Entity Resolution

Natural Language Query

Analysts type plain English queries — 'Show everyone linked to X within 2 hops' — and the system translates to graph traversal queries, returning visual + tabular results.

Case Management

Organise entities and relationships into cases. Each case has an audit trail, analyst notes, and export to PDF/JSON for court submission.

Real-Time Ingestion

Webhook-based ingestion pipeline allows live data feeds from partner systems. New entities and edges appear in the graph within seconds.

System Architecture

How every layer connects.

Data flows: ingestion → AI extraction → graph storage → query layer → analyst UI

FrontendNext.js 14 + TypeScriptApp shell, graph visualisation (D3 Force Graph), case management UI
AI PipelinePython + FastAPIEntity extraction, embedding generation, NL-to-graph query translation
Graph DBPostgreSQL + pgvectorEntity + edge storage, similarity search on embeddings
AI ModelGemini 1.5 ProNER, entity resolution, natural language interface
Searchpgvector + cosine simSemantic deduplication of entity records
AuthSupabase Auth + RLSRole-based access — Admin, Analyst, Read-Only
DeploymentVercel + RenderNext.js on Vercel edge, FastAPI on Render

Technical Deep Dive

Under the hood.

Entity Resolution Pipeline

Raw text arrives via webhook or manual upload. The FastAPI service chunks the text, sends it to Gemini for NER, then generates sentence embeddings (text-embedding-004) for each extracted entity. A cosine similarity check against existing records (threshold 0.91) determines whether to create a new node or merge with an existing one. This reduces duplicate nodes by ~73% compared to string matching alone.

Graph Query Translation

Analysts write plain English. The system uses a structured prompt chain: (1) classify intent, (2) extract parameters (entity names, hop depth, relationship types), (3) generate a parameterised SQL query. Results are returned as both raw JSON and a subgraph for visualisation.

-- 2-hop network around an entity
WITH RECURSIVE network AS (
  SELECT id, name, 0 AS depth
  FROM entities WHERE name ILIKE $1
  UNION ALL
  SELECT e.id, e.name, n.depth + 1
  FROM entities e
  JOIN edges ed ON ed.target_id = e.id
  JOIN network n ON n.id = ed.source_id
  WHERE n.depth < $2
)
SELECT DISTINCT * FROM network;

Security & Access Control

Every entity and case is scoped by organisation_id with Supabase Row Level Security. Analysts only see their organisation's data. All API calls are logged to an append-only audit table for full traceability.

Engineering Decisions

Challenge → Decision → Result.

Graph database — Neo4j vs. PostgreSQL with recursive CTEs

Decision

PostgreSQL + recursive CTEs

Why

Dataset < 500K nodes doesn't justify Neo4j costs. PostgreSQL recursive CTEs with proper indexing handle 3-hop traversals in < 80ms.

Result

Saved ~$400/month, zero additional infrastructure.

Entity deduplication — string matching vs. embedding similarity

Decision

Embedding similarity with pgvector

Why

String matching fails on nicknames, transliterations, abbreviations. Cosine similarity threshold 0.91 handles all gracefully.

Result

73% reduction in duplicate nodes on real test data.

Graph rendering — Cytoscape.js vs. D3 Force Graph

Decision

D3 Force Graph (react-force-graph)

Why

Cytoscape.js is 320KB and opinionated. D3 Force Graph gives full physics/label control at 1/3 the size.

Result

2,000+ nodes render at 60fps.

Performance & Scale

By the numbers.

91%Entity Resolution Accuracyvs. ground truth test set
< 80msGraph Query Latency3-hop traversal, 50K nodes
1,200 entities/minIngestion Throughputvia webhook pipeline
73%Duplicate Reductionvs. string matching baseline
60fpsGraph Render2,000 nodes, 5,000 edges

Deployment & Infrastructure

Production setup.

Deployment

Next.js on Vercel Edge with ISR. FastAPI on Render with a persistent worker dyno for the ingestion queue. PostgreSQL on Supabase with PgBouncer connection pooling.

CI/CD

GitHub Actions — lint + type-check on PR, Playwright smoke tests on merge, auto-deploy to Vercel and Render on green CI.

Monitoring

Vercel Analytics for frontend. Render metrics for API latency and memory. Supabase dashboard for DB query times and RLS audit.

Challenges & Failures

What broke — and how I fixed it.

Challenge

Gemini API rate limits stalling the ingestion pipeline under load.

Fix

Exponential backoff queue with Redis-backed job list, bursts smoothed over 30-second windows.

Challenge

Force graph layout thrashing when new nodes were added live.

Fix

Froze the simulation on new-node events, inserted at fixed position, then unfroze.

Challenge

Analysts accidentally merging unrelated entities with similar names.

Fix

Added a confirmation modal with side-by-side entity comparison and confidence score.

What I Learned

Key takeaways.

Future Roadmap

Where this is going.

v2.0 — Q4 2026

  • Temporal graph analysis — track network evolution over time
  • Multi-jurisdiction federation with privacy-preserving queries
  • Fine-tuned NER model on criminal investigation corpora

v3.0 — 2027

  • Mobile analyst app (React Native) for field use
  • OSINT API integration (Shodan, Maltego)
  • Anomaly detection — auto-flag unusual network patterns

Screenshots

The product.

CRIMSON network graph

Live criminal network graph — 847 entities

AI entity extraction

Entity extraction pipeline output

NL query interface

Natural language → graph traversal result

Technology Stack

Built with.

Frontend

Next.js 14TypeScript

Backend

PythonFastAPI

Database

PostgreSQLpgvector

AI/ML

Gemini 1.5 Pro

Auth / DB

Supabase

Visualisation

D3 Force Graph

Deployment

VercelRender

Ready to dive in?

This isn't just a project.
It's a system I designed,
engineered, and shipped.

Live Demo ← All Projects