Flowsint Graph Database Schema: Complete Technical Reference
Flowsint stores investigation data as a property graph using GraphNode and GraphEdge TypeScript types, where Python enrichers serialize Pydantic models into Neo4j-backed nodes and edges with visual rendering metadata.
Flowsint (reconurge/flowsint) implements a property graph model to represent cybersecurity investigations. The Flowsint graph database schema defines exactly how entities like domains, IPs, and emails become nodes, and how their relationships become edges in both the frontend visualization and the backend Neo4j database. Understanding these core types is essential for developing custom enrichers or extending the visualization layer.
Core Schema Types in flowsint-app/src/types/graph.ts
The canonical type definitions live in [flowsint-app/src/types/graph.ts](https://github.com/reconurge/flowsint/blob/main/flowsint-app/src/types/graph.ts), establishing the strict contract between Python enrichers and the React frontend. This file also contains supporting enums like NodeShape and flagColors that control visual presentation.
The GraphNode Interface
Each entity in an investigation becomes a GraphNode with fields optimized for both data storage and force-graph rendering:
id: Unique identifier derived from the Pydantic model's primary field.nodeType: String matching the Python class name (e.g.,"Domain","Ip").nodeLabel: Human-readable label generated by thecompute_label()method.nodeProperties: Dictionary ({ [key: string]: any }) containing all entity attributes from the Pydantic model.nodeSize: Number controlling the visual size in the force-graph layout.nodeColor: Optional CSS color string for node styling.nodeIcon: Key from the Lucide icon set (e.g.,"globe","server") ornull.nodeImage: URL string for an image displayed on the node, ornull.nodeFlag: Visual flag color (nullor one of red, orange, blue, green, yellow) mapped to Tailwind CSS classes.nodeShape: One of"circle","square","hexagon", or"triangle"from theNodeShapeunion type.nodeMetadata: System-level dictionary tracking creation time, source enricher, and provenance.x,y: Canvas coordinates populated by the layout engine.val?: Optional numeric value used by the force-graph for dynamic sizing.neighbors?,links?: Runtime-populated adjacency arrays used by the visualization engine but never persisted to storage.
The GraphEdge Interface
Relationships between entities are modeled as GraphEdge objects with directional semantics:
source: String ID referencing the originGraphNode.target: String ID referencing the destinationGraphNode.id: Unique edge identifier.label: Relationship type string must follow UPPER_SNAKE_CASE convention (e.g.,"RESOLVES_TO","HAS_SUBDOMAIN").date?: Optional ISO-8601 timestamp for temporal analysis.caption?: Descriptive text for UI tooltips.type?: Classification string for frontend filtering.weight?: Number controlling visual thickness or algorithmic importance.confidence_level?: Reliability score as number or string.
How Enrichers Map Python Models to Graph Structures
The framework automates the translation between Python Pydantic models and the Flowsint graph database schema through the enricher base class. When an enricher calls self.create_node(domain), the framework performs the following mapping:
- Extracts the Pydantic class name to populate
nodeType. - Uses the model's primary field value (e.g.,
domain.domain) as theid. - Invokes
compute_label()to generate thenodeLabel. - Serializes all model fields into the
nodePropertiesdictionary. - Applies default values for visual fields like
nodeSizeandnodeFlag.
For relationships, calling self.create_relationship(source_obj, target_obj, "LABEL") automatically extracts the id values from both Python objects, assigns the UPPER_SNAKE_CASE label, and generates a unique id for the GraphEdge.
Working with the Schema
TypeScript Node and Edge Creation
Frontend components import types directly from the graph module to ensure type safety:
import { GraphNode, GraphEdge } from '@/types/graph';
// Investigation entity node
const ipNode: GraphNode = {
id: '192.0.2.1',
nodeType: 'Ip',
nodeLabel: '192.0.2.1',
nodeProperties: { ip: '192.0.2.1', reputation: 'high', asn: 64496 },
nodeSize: 12,
nodeColor: '#3b82f6',
nodeIcon: 'network',
nodeImage: null,
nodeFlag: 'red',
nodeShape: 'hexagon',
nodeMetadata: { created_at: '2026-05-15T12:00:00Z', enricher: 'shodan_enricher' },
x: 0,
y: 0,
};
// Relationship edge
const resolvesEdge: GraphEdge = {
source: ipNode.id,
target: 'example.com',
id: 'edge-192-0-2-1-example-com',
label: 'RESOLVES_TO',
date: '2026-05-15T12:00:00Z',
confidence_level: 0.95,
weight: 3
};
Python Enricher Integration
Enrichers interact with the graph schema through high-level methods without manual JSON construction:
from flowsint_enrichers.base import BaseEnricher
class DnsEnricher(BaseEnricher):
def postprocess(self) -> None:
# domain and ip are Pydantic models
self.create_node(domain) # Generates GraphNode
self.create_node(ip) # Generates GraphNode
self.create_relationship(domain, ip, "RESOLVES_TO") # Generates GraphEdge
The create_node() and create_relationship() methods handle all Pydantic reflection and ID resolution internally, ensuring the resulting objects conform to the TypeScript definitions in graph.ts.
Backend Storage Implementation
While the TypeScript types define the frontend contract, the actual Flowsint graph database schema persists in Neo4j. Migration scripts in neo4j-migrations/*.cypher define indexes and constraints that store GraphNode and GraphEdge payloads as property graph elements. The nodeProperties and nodeMetadata dictionaries map directly to Neo4j node properties, while edges store their respective fields as relationship attributes.
Summary
- GraphNode and GraphEdge in
flowsint-app/src/types/graph.tsdefine the canonical schema for all investigation data. - Python enrichers use
create_node()andcreate_relationship()to automatically map Pydantic models to these types. - Nodes support rich visual properties including Lucide icons, Tailwind flag colors, and configurable shapes.
- Relationship labels must use UPPER_SNAKE_CASE format (e.g.,
"HAS_SUBDOMAIN"). - The schema is backed by Neo4j, with TypeScript types ensuring frontend-backend consistency.
Frequently Asked Questions
What file contains the official Flowsint graph type definitions?
The canonical TypeScript interfaces are located in flowsint-app/src/types/graph.ts within the reconurge/flowsint repository. This file exports GraphNode, GraphEdge, and related enums like NodeShape that control visual presentation.
How does Flowsint generate unique identifiers for graph nodes?
The framework derives the id field from the primary value of the Pydantic model being enriched. For example, a Domain model with the value example.com receives id: "example.com", while an IP model uses the IP string itself, ensuring deterministic IDs across enrichment runs.
What format must relationship labels follow in Flowsint edges?
All relationship labels must conform to UPPER_SNAKE_CASE convention (e.g., "RESOLVES_TO", "REGISTRANT_OF"). This standardization occurs in the label field of GraphEdge and ensures consistency in the Neo4j backend and frontend visualization filtering.
Where is Flowsint's graph data physically stored?
While the TypeScript types define the data shape, the actual graph data persists in a Neo4j database. Migration scripts in the neo4j-migrations directory define the physical schema, indexes, and constraints that store the JSON payloads generated by the TypeScript GraphNode and GraphEdge structures.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →