erlik-graph
by malkreide
README.md
# Erlik Graph




[](https://github.com/malkreide/erlik-graph/actions/workflows/ci.yml)
> An OSINT link-analysis graph with one shared transform core and two front-ends: a visual FastAPI + Cytoscape app **and** an MCP server for LLM-driven investigation.
[π©πͺ Deutsche Version](README.de.md)
Part of the **[Erlik](#the-erlik-portfolio)** portfolio of open-source AI OSINT tools.
## Overview
Erlik Graph implements the classic link-analysis workflow β **Entities** (nodes) connected by **Transforms** (functions that take one entity and return related ones) on an interactive **Graph** β as a small, hackable Python project.
The point is the architecture: transform logic is written **once** as plain Python functions and exposed through **two adapters** that share the same graph store:
- **FastAPI + Cytoscape** β a visual, clickable investigation graph. *You* drive it, every step is deterministic and auditable.
- **MCP** β the same transforms as tools an LLM agent (e.g. Claude) calls autonomously to enrich and follow leads.
Add a transform once, and it appears in both.
## Features
- π **Entity/Transform/Graph** model with automatic node de-duplication
- π§© **13 transforms out of the box** β DNS (A/MX/NS/TXT), reverse DNS, certificate-transparency subdomains (crt.sh), RDAP/WHOIS, Wayback Machine, IP geolocation, HIBP breaches, Gravatar, Shodan services, username enumeration across 10 platforms
- π₯οΈ **Visual graph** front-end (Cytoscape.js), click a node β run applicable transforms β the graph grows
- π€ **MCP adapter** exposing every transform as a tool for LLM-driven OSINT
- β»οΈ **Shared core** β one `@transform` decorator, both adapters pick it up automatically
- π **Pluggable storage** β in-memory `networkx` by default, or a shared **Neo4j** backend so the MCP and FastAPI adapters read/write the *same* graph (Claude enriches, you inspect it live in the browser)
## Prerequisites
- Python 3.11+
- Optional: a `SHODAN_API_KEY` for the Shodan transform and a `HIBP_API_KEY` for the breach transform
- Optional: a running **Neo4j** instance to share one graph across both adapters
## Installation
```bash
git clone https://github.com/malkreide/erlik-graph.git
cd erlik-graph
python -m venv .venv
# Windows: .\.venv\Scripts\Activate.ps1 | Unix: source .venv/bin/activate
pip install -r requirements.txt
```
## Usage
### Variant A β visual graph (FastAPI)
```bash
python -m uvicorn erlik_graph.adapters.api_server:app --reload
```
Open http://127.0.0.1:8000 β add a seed entity β click a node β run a transform β the graph expands.
### Variant B β MCP (LLM-driven)
Register the server (see [.mcp.json](.mcp.json)) with your MCP client. The transforms appear as tools (`domain_to_subdomains`, `username_to_profiles`, `get_graph`, β¦) and the agent decides which leads to follow.
```jsonc
{
"mcpServers": {
"erlik-graph": {
"command": "python",
"args": ["-m", "erlik_graph.adapters.mcp_server"],
"cwd": "/path/to/erlik-graph"
}
}
}
```
## Configuration
| Variable | Purpose | Required |
|---|---|---|
| `SHODAN_API_KEY` | Enables the `ipv4_to_services` transform | No (transform returns a hint if unset) |
| `HIBP_API_KEY` | Enables the `email_to_breaches` transform (Have I Been Pwned) | No (transform returns a hint if unset) |
| `ERLIK_GRAPH_BACKEND` | Graph backend: `memory` (default) or `neo4j` | No |
| `NEO4J_URI` | Bolt URI when the backend is `neo4j` | Only for `neo4j` (default `bolt://localhost:7687`) |
| `NEO4J_USER` / `NEO4J_PASSWORD` | Neo4j credentials | Only for `neo4j` |
### Shared graph via Neo4j
By default each process keeps its own in-memory graph, so the MCP and FastAPI adapters don't see each other's data. Point both at Neo4j to share **one** graph β the intended "Claude enriches, you inspect visually" loop:
```bash
export ERLIK_GRAPH_BACKEND=neo4j
export NEO4J_URI=bolt://localhost:7687
export NEO4J_USER=neo4j
export NEO4J_PASSWORD=your-password
# Adapter 1 β the LLM enriches through MCP
python -m erlik_graph.adapters.mcp_server
# Adapter 2 β you watch the same graph grow in the browser
python -m uvicorn erlik_graph.adapters.api_server:app --reload
```
Swap in a different store by subclassing `BaseGraphStore` and returning it from `create_store()`.
## Adding a transform
1. Write a function decorated with `@transform(name, input_type, description)` in a file under `erlik_graph/transforms/`.
2. Import that file in `erlik_graph/transforms/__init__.py`.
It now appears in **both** adapters automatically.
```python
@transform("domain_to_ipv4", "domain", "Resolves a domain to its IPv4 addresses.")
def domain_to_ipv4(value: str, properties: dict) -> list[Entity]:
return [Entity(type="ipv4", value=ip, link_label="resolves_to")
for ip in query(value, "A")]
```
## Project Structure
```
erlik_graph/
βββ core/ Data model, registry, graph stores
β βββ entity.py Entity / Edge, de-dup key
β βββ registry.py @transform decorator + registry
β βββ base_store.py BaseGraphStore β shared expand() logic
β βββ graph_store.py in-memory networkx store
β βββ neo4j_store.py shared Neo4j-backed store
β βββ factory.py create_store() β picks backend from env
βββ transforms/ the actual logic β this is where the system grows
β βββ dns_transforms.py
β βββ rdns_transforms.py
β βββ crtsh_transforms.py
β βββ whois_transforms.py
β βββ wayback_transforms.py
β βββ geo_transforms.py
β βββ breach_transforms.py
β βββ gravatar_transforms.py
β βββ shodan_transforms.py
β βββ username_transforms.py
βββ adapters/
β βββ api_server.py FastAPI endpoints
β βββ mcp_server.py MCP tools
βββ web/index.html Cytoscape front-end
tests/ offline pytest suite (run in CI)
.github/workflows/ci.yml install + import check + pytest on 3.11 / 3.12
```
## The Erlik Portfolio
**Erlik** β named after the lord of the underworld in Turkic-Mongolic (Tengrist) mythology β is a growing family of small, focused, open-source AI OSINT tools. Each tool lives in its **own** repository and shares the `erlik` topic for discoverability. `erlik-graph` is the link-analysis flagship; further tools (scouts, enrichers, monitors) join the portfolio as separate repos.
## Legal Notice
For use only against systems and data you are authorized to investigate. When aggregating personal data, comply with the GDPR and applicable law.
## Changelog
See [CHANGELOG.md](CHANGELOG.md).
## License
MIT License β see [LICENSE](LICENSE).
## Author
Hayal Γzkan Β· [@malkreide](https://github.com/malkreide)
This server cannot be deployed
Maintenance
ActivityStale
ResponsivenessNo issues