# Flowsint Graph Database Schema: Complete Technical Reference

> Explore Flowsint's graph database schema for investigation data. Learn how GraphNode and GraphEdge types, Neo4j, and Pydantic models structure your data for visual rendering.

- Repository: [reconurge/flowsint](https://github.com/reconurge/flowsint)
- Tags: api-reference
- Published: 2026-06-05

---

**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`](https://github.com/reconurge/flowsint/blob/main/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)](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 the `compute_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"`) or `null`.
- `nodeImage`: URL string for an image displayed on the node, or `null`.
- `nodeFlag`: Visual flag color (`null` or one of red, orange, blue, green, yellow) mapped to Tailwind CSS classes.
- `nodeShape`: One of `"circle"`, `"square"`, `"hexagon"`, or `"triangle"` from the `NodeShape` union 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 origin `GraphNode`.
- `target`: String ID referencing the destination `GraphNode`.
- `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:

1. Extracts the Pydantic class name to populate `nodeType`.
2. Uses the model's primary field value (e.g., `domain.domain`) as the `id`.
3. Invokes `compute_label()` to generate the `nodeLabel`.
4. Serializes all model fields into the `nodeProperties` dictionary.
5. Applies default values for visual fields like `nodeSize` and `nodeFlag`.

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:

```typescript
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:

```python
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`](https://github.com/reconurge/flowsint/blob/main/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.ts`](https://github.com/reconurge/flowsint/blob/main/flowsint-app/src/types/graph.ts) define the canonical schema for all investigation data.
- Python enrichers use `create_node()` and `create_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`](https://github.com/reconurge/flowsint/blob/main/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.