The Problem
Cyber‑security analysts need a way to capture, enrich, and visualise relationships among disparate OSINT artefacts (domains, IPs, social handles, wallets, etc.) without relying on third‑party SaaS platforms that expose sensitive data. Building a self‑hosted graph‑based investigation stack from scratch is costly in time and expertise.
What This Does
flowsint delivers a complete, containerised stack that stores entities in a Neo4j graph, enriches them via a library of Python enrichers, exposes CRUD and search APIs through FastAPI, and visualises the graph in a React SPA.
- Core data model and Pydantic schemas live in
flowsint-types/src/flowsint_types/(e.g.flowsint_base.py,registry.py). - Enrichment logic resides in
flowsint-enrichers/(e.g.dockertool.py,registry.py). - Graph persistence and service layer are in
flowsint-core/– the hub classEnricherBase(flowsint-core/src/flowsint_core/core/enricher_base.py) orchestrates scans, creates nodes/relationships and logs messages. - The HTTP layer (
flowsint-api/app/main.py, route modules underflowsint-api/app/api/routes/) forwards requests to the core services. - The UI (
flowsint-app/src/…) renders the graph and provides an editor for custom analyses.
How It Is Wired
Execution begins in the core entry point flowsint-core/src/flowsint_core/core/enricher_base.py:execute (line 412). That function invokes a cascade of 124 internal functions, the most widely referenced being:
query– 78 call sites (Neo4j driver wrapper inflowsint-core/src/flowsint_core/core/graph/connection.py).Neo4jGraphRepository– 68 call sites (graph repository underflowsint-core/src/flowsint_core/core/graph/repository.py).GraphService– 42 call sites (service wrapper inflowsint-core/src/flowsint_core/core/graph/service.py).
A typical scan follows the resolved path:
execute → scan → get_secret (network request) → Domain (type resolution) → create_node → postprocess → create_relationship → log_graph_message
scanmakes an outbound HTTP call (requests.get) and a cryptographic hash (hashlib.md5) on the target email.create_nodeandcreate_relationshipissue Cypher queries viaquery, writing to Neo4j (80 functions read/write the DB).postprocesslogs messages and updates theDomainmodel, which is defined inflowsint-types/src/flowsint_types/flowsint_base.py.
The API layer (flowsint-api/app/main.py) creates the FastAPI app, mounts routes such as routes/sketches.py (which call create_sketch, list_sketches, etc.), and injects the core services. The frontend (flowsint-app/src/components/sketches/graph/index.tsx) consumes these endpoints, rendering the graph via the GraphService API.
Import‑cycle analysis shows 23 modules in circular dependencies, the most prominent being flowsint-app/src/components/analyses/editor/* and flowsint-app/src/components/sketches/graph-viewer.tsx. These cycles increase the blast radius of any change because a modification can trigger recompilation across many files (e.g., flowsint-app/src/components/ui/button.tsx is imported by 16 other components but imports none itself, making it a stable leaf).
How To Use It
# Clone the upstream repo (the fork you will work on)
git clone https://github.com/moses-y/flowsint.git
cd flowsint
# Build and start all services (Docker + Make)
make prod # uses docker-compose.prod.yml, builds API and UI images
# or for development
make dev # uses docker-compose.dev.yml
Configuration: copy .env.example to .env and supply a Neo4j connection string (NEO4J_URI, NEO4J_USER, NEO4J_PASSWORD). The API reads these variables in flowsint-api/app/utils.py.
Run the UI at http://localhost:5173/register. The FastAPI server listens on port 8000 (exposed by the compose file). No additional CLI scripts are required.
Real‑World Use
A SOC analyst can script a nightly job that invokes the enrichers via the API:
import requests
resp = requests.post(
"http://localhost:8000/enrichers/domain",
json={"domain": "example.com"},
headers={"Authorization": "Bearer <token>"}
)
print(resp.json())
The service resolves DNS, pulls WHOIS data, creates a Domain node, and links it to its IP and ASN nodes in Neo4j, instantly visualisable in the web UI.
Code Health & Issues
- HIGH – Pin GitHub Actions to commit SHAs (
.github/workflows/*). - HIGH – Remove privileged mode and host networking from
docker-compose.dev.yml. - HIGH – CI does not run the test suite; add a test step to the workflow.
- MEDIUM – Enable Dependabot/Renovate (
.github/dependabot.yml). - MEDIUM – Pin the API Docker base image by digest (
flowsint-api/Dockerfile). - MEDIUM – Add a non‑root USER to the API image.
- LOW – Set
timeout-minuteson workflow jobs. - LOW – Add repository convention files (
.editorconfig,.gitattributes, formatter config).
Static analysis also flagged 54 high‑severity “hub module” risks (e.g., flowsint-types/src/flowsint_types/flowsint_base.py imported by 39 modules) and 23 import‑cycle members that should be refactored to reduce coupling.
The Bottom Line
flowsint provides a functional, self‑hosted OSINT graph platform with clear module boundaries, but the codebase contains several high‑impact coupling points and security‑hardening gaps in its Docker and CI configurations. It is suitable for teams comfortable with Python, React, and Docker who can invest in tightening the dependency cycles and CI pipeline.