Prismatic Graph

Prismatic Graph

290 files Storage

Overview

Prismatic Graph provides the graph database integration layer built on KuzuDB, an embedded graph database optimized for analytical workloads. It enables relationship-centric queries across the platform's entity data, powering the knowledge graph, entity relationship mapping, and graph-based intelligence analysis.

Graph databases excel where relational databases struggle: traversing deep relationship chains, finding shortest paths between entities, detecting communities, and uncovering hidden connections. For an intelligence platform like Prismatic, graph queries are essential for connecting the dots across OSINT sources, company registries, and threat intelligence. The choice of KuzuDB as the embedded graph engine provides sub-100ms query performance for multi-hop traversals without the operational overhead of a standalone graph database server.

The module serves as the backbone of the platform's entity resolution pipeline, maintaining a unified graph of entities -- companies, persons, domains, IP addresses, certificates -- connected by typed, directional relationships. When a new entity is discovered through any OSINT source, it is linked into the graph with edges representing ownership, directorship, hosting, DNS resolution, and other relationship types. This interconnected view enables analysts to discover non-obvious connections that would be invisible in tabular data.

Architecture

PrismaticGraph
+-- Schema          # Graph schema definitions (node types, edge types, properties)
+-- Query           # Cypher query interface with parameterized execution
+-- Analytics       # Graph algorithms (centrality, community detection, PageRank)
+-- Ingestion       # Data loading from OSINT and registry sources
+-- KuzuAdapter     # Low-level KuzuDB driver with connection pooling
+-- Visualization   # Graph data export for rendering (D3.js, Cytoscape)

Data Ingestion Pipeline

OSINT Sources --> Entity Extraction --> Graph Ingestion --> KuzuDB
    |                                                      |
    +-- ARES companies ----------------------------------------+
    +-- Justice.cz statutory bodies ---------------------------+
    +-- Shodan host-domain relationships ----------------------+
    +-- Censys certificate-domain links -----------------------+
    +-- Sanctions entity connections ---------------------------+

The ingestion pipeline follows an idempotent merge strategy: when an entity or relationship already exists in the graph, the ingestion process updates properties and timestamps rather than creating duplicates. This ensures graph integrity even when multiple OSINT sources provide overlapping data about the same entities.

Key Modules

ModuleResponsibility
PrismaticGraphPublic facade: query/1, shortest_path/1, ingest/2
PrismaticGraph.ApplicationOTP application entry point with connection pool supervision
PrismaticGraph.SchemaDSL for defining graph node types, edge types, and property constraints
PrismaticGraph.QueryCypher query builder with parameterized execution and result mapping
PrismaticGraph.AnalyticsGraph algorithms including centrality, community detection, PageRank
PrismaticGraph.IngestionIdempotent entity and relationship loading from external sources
PrismaticGraph.KuzuAdapterLow-level KuzuDB driver with connection management
PrismaticGraph.VisualizationGraph data export in formats suitable for frontend rendering
PrismaticGraph.SchemaEvolutionSchema migration management for graph structure changes

Entity Relationship Modeling

# Define graph schema
PrismaticGraph.Schema.define do
  node :company do
    property :ico, :string
    property :name, :string
    property :country, :string
  end

  node :person do
    property :name, :string
    property :role, :string
  end

  node :ip_address do
    property :address, :string
    property :asn, :integer
  end

  node :domain do
    property :name, :string
    property :registrar, :string
  end

  node :certificate do
    property :fingerprint, :string
    property :issuer, :string
    property :valid_until, :datetime
  end

  edge :owns, from: :person, to: :company
  edge :directs, from: :person, to: :company
  edge :hosts, from: :ip_address, to: :company
  edge :resolves_to, from: :domain, to: :ip_address
  edge :related_to, from: :company, to: :company
  edge :secures, from: :certificate, to: :domain
end

Cypher Query Interface

# Find all companies directed by a specific person
{:ok, companies} = PrismaticGraph.query("""
  MATCH (p:Person {name: 'Jan Novak'})-[:DIRECTS]->(c:Company)
  RETURN c.name, c.ico
""")

# Find connections between two entities (up to 4 hops)
{:ok, paths} = PrismaticGraph.query("""
  MATCH path = (a:Company {ico: '12345678'})-[*1..4]-(b:Company {ico: '87654321'})
  RETURN path
""")

# Community detection - find clusters of related companies
{:ok, clusters} = PrismaticGraph.query("""
  MATCH (c1:Company)-[:RELATED_TO]-(c2:Company)
  WHERE c1.country = 'CZ'
  RETURN c1.name, collect(c2.name) as related
  ORDER BY size(related) DESC
  LIMIT 20
""")

# Shortest path between entities
{:ok, shortest} = PrismaticGraph.shortest_path(
  from: {:company, ico: "12345678"},
  to: {:company, ico: "87654321"}
)

Graph Analytics

Graph analytics algorithms operate on the full graph to compute structural properties that reveal patterns invisible to direct queries. These algorithms run as background tasks under OTP supervision, with results cached in ETS for fast lookup.

# Calculate centrality (most connected entities)
{:ok, central} = PrismaticGraph.Analytics.degree_centrality(:company)
# => [%{name: "Holding Corp", degree: 47}, %{name: "Bank A", degree: 38}, ...]

# Detect communities using label propagation
{:ok, communities} = PrismaticGraph.Analytics.detect_communities(:company, :related_to)

# Find bridge entities (connecting otherwise separate groups)
{:ok, bridges} = PrismaticGraph.Analytics.betweenness_centrality(:person)

# PageRank for entity importance
{:ok, ranked} = PrismaticGraph.Analytics.pagerank(:company)
AlgorithmUse CaseComplexity
Degree CentralityMost connected entitiesO(V + E)
Betweenness CentralityBridge entities between groupsO(V * E)
PageRankEntity importance rankingO(V + E) per iteration
Label PropagationCommunity detectionO(V + E)
Shortest PathConnection discoveryO(V + E)
Connected ComponentsIsolated subgraph identificationO(V + E)

Graph Schema Evolution

As intelligence requirements evolve, the graph schema must adapt to represent new entity types and relationship categories. The SchemaEvolution module manages these changes while preserving existing data:

defmodule PrismaticGraph.SchemaEvolution do
  @spec migrate(atom(), keyword()) :: {:ok, MigrationResult.t()} | {:error, term()}
  def migrate(migration_name, opts \\ []) do
    dry_run = Keyword.get(opts, :dry_run, false)

    with {:ok, migration} <- load_migration(migration_name),
         {:ok, plan} <- compute_migration_plan(migration),
         :ok <- validate_backward_compatibility(plan) do
      if dry_run do
        {:ok, %MigrationResult{plan: plan, applied: false}}
      else
        apply_migration(plan)
      end
    end
  end
end

Connection Pool Management

The KuzuDB adapter manages a connection pool to ensure efficient resource utilization under concurrent query loads. Each LiveView dashboard session and background analytics task acquires connections from the pool rather than creating direct database handles:

Pool ParameterDefaultProductionNotes
Pool size520Connections per KuzuDB instance
Checkout timeout5,000ms10,000msMaximum wait for available connection
Idle timeout30,000ms60,000msConnection recycling threshold
Queue target50ms100msTarget wait time in checkout queue

OSINT Intelligence Graph

Data from ARES, Justice.cz, Shodan, Censys, and other OSINT sources flows into the graph, creating a unified intelligence knowledge base.

# Ingest company data from Czech registries
PrismaticGraph.Ingestion.ingest_company(ares_data, justice_data)

# Ingest infrastructure relationships
PrismaticGraph.Ingestion.ingest_infrastructure(shodan_data, censys_data)

# Query across data sources
{:ok, result} = PrismaticGraph.query("""
  MATCH (c:Company {ico: '12345678'})-[:HOSTS]-(ip:IPAddress)-[:HOSTS]-(other:Company)
  RETURN other.name, ip.address
""")

NABLA Compliance

NABLA AxiomGraph EnforcementImplementation
Provenance MandatoryEvery node and edge carries source and timestamp metadataIngestion pipeline attaches provenance to all graph elements
Signal PluralityMulti-source entity verification before graph insertionCross-source validation required for high-confidence entities
Source IndependenceEach OSINT source ingests independentlySeparate ingestion workers per source prevent cross-contamination
Contradiction PreservationConflicting relationship data preserved as parallel edgesMultiple edges with different sources maintained
Time DecayEntity freshness tracked through last-observed timestampsStale entities flagged for re-verification

Testing

Schema tests verify node type, edge type, and property constraint definitions. Query tests verify Cypher execution, parameterization, and result mapping against known graph fixtures. Analytics tests verify algorithm correctness using small, manually-verified graph structures with known centrality and community properties.

Integration tests exercise the full pipeline from OSINT data ingestion through graph construction and querying. Property-based tests generate random graph structures to verify algorithm correctness invariants. Schema evolution tests verify migration safety using backward compatibility checks against existing graph data.

Integration Points

ApplicationRelationship
Prismatic StorageKuzuDB adapter via Graphable trait
Prismatic OSINT CoreEntity data ingestion from OSINT sources
Prismatic PerimeterAttack surface relationship mapping
Prismatic WebGraph visualization in LiveView dashboards
Prismatic Czech AutocrawlerCzech registry entity relationship data

Performance

MetricValueNotes
4-hop traversal< 100msKuzuDB embedded engine
Shortest path< 50msBidirectional BFS
Community detection< 5sFull graph, 100K+ nodes
Entity ingestion10K+ entities/sBatch mode with merge
Total nodes100K+Across all entity types
Total edges500K+All relationship types
Schema migration< 30sBackward-compatible changes

Telemetry events: [:prismatic, :graph, :query_executed], [:prismatic, :graph, :entity_ingested], [:prismatic, :graph, :analytics_computed].


Connect & Contribute

Created by Tomáš Korcak (korczis) | Open Source under GHL