Military intelligence has always been about connection: which commander controls which unit, which financier funds which cell, which infrastructure node enables which logistics chain. These are graph problems. The data that answers them — intercepted communications, agent reports, satellite imagery metadata, open-source registrations — arrives from a dozen separate collection systems, each naming the same real-world entity differently and storing relationships in incompatible formats. Knowledge graphs provide the structural layer that joins these fragments into a coherent, queryable picture. This article examines how graph databases and knowledge graphs are applied in military intelligence workflows — from the fundamental inadequacies of relational approaches, through ontology design aligned to PMESII, entity resolution across multi-INT sources, STIX object representation, graph database selection for classified environments, Cypher query patterns for link analysis, and integration with C2 and OSINT pipelines.

Why relational databases fail for intelligence link analysis

The canonical intelligence question is not "find all records where column X equals value Y." It is "starting from this entity, traverse its network to depth five, filtering by relationship type and confidence, and tell me which communities those entities belong to." Relational databases answer the first question well. They answer the second by chaining JOIN operations whose computational cost compounds at each hop.

A two-hop JOIN over a ten-million-row entity table is usually tractable, perhaps a few seconds with good indexes. A five-hop self-join over the same table requires four sequential JOIN operations, each multiplying the working set by the average fan-out factor. Against realistic intelligence data — where a person node might have fifty communications contacts, each with fifty of their own — the intermediate result set at depth five can exceed the memory of any server before filtering is even applied. The query optimizer cannot avoid this because the relationship itself does not exist in the database as a stored object: it is recomputed by matching key columns at query time. Every hop is a full recomputation.

The entity disambiguation problem compounds this further. A person appears in a SIGINT intercept as "Ahmed M." and in a HUMINT report as "Ahmad Mansour" and in a financial record as "A. Mansouri." In a relational schema, these are three separate rows in three separate tables unless a human analyst (or a deduplication pipeline) has explicitly linked them. Without that link, a JOIN that starts from the SIGINT record never traverses to the financial record, and the network the analyst sees is systematically incomplete. Relational databases have no native mechanism for expressing "these two rows are probably the same entity, with confidence 0.85." That probability must live outside the schema, in application logic that is usually manual and rarely consistent.

The JOIN explosion and entity disambiguation problem together explain why link analysis in traditional defense intelligence systems — built on relational databases — required dedicated analyst labor that a graph database for intelligence analysis can automate. The graph stores relationships as native edges with direct pointers between nodes, so traversal cost scales with the local neighborhood, not the global table size. Entity resolution is first-class: a merge operation collapses multiple records into one canonical node, and all edges that referenced either record now reference the canonical node. The graph is the data model designed for the problem domain.

Knowledge graph fundamentals for military use

A knowledge graph extends a property graph with a shared vocabulary — an ontology — that gives every node type and edge type a defined, agreed meaning. This shared vocabulary is what makes queries composable across sources: two separate ingestion pipelines that both use the node label MilitaryUnit and the edge type SUBORDINATE_TO produce graphs that can be traversed together. Without the ontology, every pipeline invents its own names, and the graph accumulates ten synonymous labels for the same concept.

For military intelligence, the PMESII framework provides a natural ontology skeleton. Each of the six dimensions — Political, Military, Economic, Social, Infrastructure, Information — becomes a family of node labels:

PMESII Dimension Node Labels Key Edge Types
Political Government, Official, PoliticalParty, Faction CONTROLS, ALLIED_WITH, OPPOSES
Military Unit, Commander, WeaponsSystem, Base COMMANDS, SUBORDINATE_TO, EQUIPPED_WITH, DEPLOYS_AT
Economic Organization, FinancialAccount, SupplyChain, Commodity FINANCES, TRANSACTS_WITH, SUPPLIES
Social Person, Group, Community MEMBER_OF, COMMUNICATES_WITH, RECRUITS
Infrastructure Facility, TransportLink, PowerGrid, NetworkNode CONNECTS, DEPENDS_ON, OPERATED_BY
Information MediaChannel, PsyopActor, NarrativeTheme AMPLIFIES, TARGETS, DISSEMINATES

Every node carries a required property set: a canonical identifier, a name, a classification label, a source reference, and a created timestamp. Every edge carries: type, source_ref, confidence (a float between 0 and 1), valid_from (the earliest time the relationship was observed), valid_until (null if still active), and classification. These required properties are what make the graph auditable: any inferred connection can be traced back to the observations that support it, with their collection metadata and confidence scores intact.

Cross-PMESII edges are often the highest-value intelligence targets. An Economic node financing a Military node crosses two dimensions; an Information node spreading a narrative on behalf of a Political node crosses two others. Queries that specifically search for cross-dimension paths — what Economic organizations finance this Military unit? — are structurally straightforward in a property graph because the edge exists as a native object connecting the two nodes.

Entity resolution across multi-INT sources

Intelligence data is collected by sources that were never designed to share a common entity identifier. A SIGINT system identifies a person by their phone number. A HUMINT report names them in prose. An IMINT analyst tags a vehicle in an imagery annotation. An open-source database registers an organization by its legal name in a foreign script. Getting all of these into one consistent graph requires entity resolution: deciding, for every pair of incoming records, whether they refer to the same real-world entity.

The resolution pipeline has three stages. The first is normalization: before any comparison can be made, identifiers must be brought to a canonical form. Phone numbers normalize to E.164 international format. Names transliterate to a common script (typically Latin) using a defined transliteration standard (BGN/PCGN for Slavic languages, ALA-LC for Arabic) and are then further normalized to a base form that strips honorifics and name-order variation. Coordinates convert to WGS84. A record whose phone number is stored as "0044-20-7946-0123" and another whose number is "+44 20 7946 0123" must both produce the same normalized key before any blocking or comparison step.

The second stage is blocking: a mechanism that limits which pairs of records are compared. Comparing every incoming record against every existing graph node is quadratic in complexity and operationally intractable at scale. Blocking strategies include sorted neighborhood (records sorted by a blocking key, compared only within a sliding window), inverted index blocking (records sharing at least one token in a phonetic name encoding are candidates), and MinHash locality-sensitive hashing for set-valued attributes like known aliases. Each strategy trades recall for performance: a missed blocking candidate is a missed entity match, so blocking keys should be tuned against a gold-standard test set of known duplicates.

The third stage is scoring and decision. Candidate pairs receive a similarity vector across multiple attributes: name similarity (Jaro-Winkler or Jaccard over name tokens), identifier overlap (shared phone, IMEI, address), geographic proximity, and temporal co-occurrence. A logistic regression or gradient-boosted classifier produces a merge confidence score. Pairs above a high threshold (typically 0.90) are merged automatically; pairs in the uncertainty band (0.65–0.90) are queued for analyst review; pairs below the lower threshold are left as separate nodes.

Critical discipline: Every merge must be logged with the evidence vector and confidence that justified it. Merges must be reversible. An incorrect merge — two different people collapsed into one node — fabricates network connections that do not exist, potentially directing collection and analysis against the wrong target. The merge log is not a nice-to-have audit artifact; it is a prerequisite for any intelligence product the graph supports to survive analytic review.

The multi-sensor fusion architecture that handles track correlation at the sensor layer uses many of the same probabilistic principles — gating on position, velocity, and identity attributes — but operates at millisecond latency over structured sensor messages. Entity resolution for knowledge graphs operates at much lower volume (millions of records rather than millions per second) but against far messier, less structured data. Both domains require the same discipline: explicit confidence, reversible decisions, and auditability.

Representing STIX objects in a graph store

STIX (Structured Threat Information eXpression) is the NATO and Five Eyes standard for sharing cyber threat intelligence. A STIX bundle consists of STIX Domain Objects (SDOs) — the entities: threat actors, campaigns, indicators, malware, vulnerabilities, attack patterns, infrastructure — and STIX Relationship Objects (SROs) that express directed relationships between them: "ThreatActor X uses Malware Y," "Campaign A targets Sector B." This is a native property graph. Every SDO becomes a node; every SRO becomes a directed edge.

The mapping is straightforward:

// STIX SDO → Neo4j Node
MERGE (n:StixObject {stix_id: $bundle.id})
SET n.type        = $sdo.type,
    n.name        = $sdo.name,
    n.confidence  = $sdo.confidence,
    n.modified    = datetime($sdo.modified),
    n.revoked     = coalesce($sdo.revoked, false),
    n.labels      = $sdo.labels,
    n.classification = $sdo.object_marking_refs[0]

// STIX SRO → Neo4j Edge
MATCH (src:StixObject {stix_id: $sro.source_ref})
MATCH (tgt:StixObject {stix_id: $sro.target_ref})
MERGE (src)-[r:STIX_REL {stix_id: $sro.id}]->(tgt)
SET r.relationship_type = $sro.relationship_type,
    r.confidence        = $sro.confidence,
    r.valid_from        = datetime($sro.start_time),
    r.valid_until       = datetime($sro.stop_time),
    r.modified          = datetime($sro.modified),
    r.revoked           = coalesce($sro.revoked, false)

Two STIX-specific complications require explicit handling. First, versioning: STIX updates an object by re-issuing the same id with a new modified timestamp. The MERGE-on-stix_id pattern above handles this correctly by overwriting properties in place. For workflows that need to reconstruct the intelligence picture as it stood at a specific moment — such as a post-incident review — a temporal versioning pattern is needed: instead of overwriting, create a new node version and chain versions with HAS_VERSION edges, storing the effective timestamp on each version node.

Second, revocation: a STIX object with revoked: true should be excluded from traversals but not deleted, to preserve the audit trail. The convention is to set revoked: true on the node and add a WHERE NOT n.revoked filter to every traversal query by default. A revoked edge that asserted a relationship between two threat actors must remain in the database so an analyst can see that the assertion was made and later retracted — the retraction is itself intelligence.

STIX also defines Sighting objects, which record that an SDO was observed in a specific context. Sightings map to edges connecting the observed object to an observing identity, with sighting_count and first/last_seen properties. This is the mechanism that links abstract threat intelligence — "this malware family" — to a specific operational observation: "this malware was seen on this network at this time."

Graph database selection for military environments

Choosing a graph database for classified military deployment is not primarily a performance decision. Several non-functional constraints dominate:

Engine Air-gap Query language Native GDS algorithms Notes
Neo4j Enterprise Yes (offline license) Cypher / openCypher GDS library: PageRank, Louvain, Dijkstra, WCC Most analyst tooling targets Cypher; strong accreditation track record in Five Eyes deployments
TigerGraph Yes (on-prem bundle) GSQL Built-in: centrality, community, path Higher throughput for batch analytics; GSQL steeper learning curve for analysts
Amazon Neptune No (cloud service) openCypher / Gremlin / SPARQL Neptune ML for graph neural nets Suitable for TS/SCI cloud tenancies only; not for true air-gap
JanusGraph Yes (open source) Gremlin Via Spark/Hadoop backend Fully open-source; runs over HBase, Cassandra, or RocksDB; best for edge-deployed nodes with embedded storage

For forward-deployed or edge-of-network intelligence nodes — an exploitation team with a laptop and a tactical network — embedded graph stores backed by RocksDB (JanusGraph + EmbeddedGraph) or Neo4j's embedded mode offer the lowest footprint. For garrison analytical networks with multi-analyst concurrent access, Neo4j Enterprise's clustered deployment or TigerGraph's distributed architecture provide the throughput needed for simultaneous link-analysis sessions. Amazon Neptune is appropriate only where the classification enclave is already hosted in a sovereign cloud tenancy at the required classification level.

A critical operational consideration is the multi-level security (MLS) enforcement layer. The graph database itself is rarely MLS-aware: it stores data and executes queries without inherently understanding classification labels. The enforcement layer sits in the query gateway — a middleware component that rewrites every incoming Cypher query to add a WHERE clause filtering to the requesting user's authorized classification set. This must be implemented carefully: a user cleared at SECRET must not be able to infer the existence of TOP SECRET edges through timing differences or error messages, even if they cannot see the edge content itself.

Query patterns for link analysis

Link analysis in Cypher reduces to a small vocabulary of patterns that compose into complex analytic questions. The following examples assume Neo4j and the GDS library, but the patterns translate to GSQL or Gremlin with minor syntax changes.

Shortest path between two entities of interest:

// Find shortest path, filtering to high-confidence, non-revoked edges
MATCH (a:Entity {id: $seedA}),
      (b:Entity {id: $seedB}),
      p = shortestPath((a)-[*1..6]-(b))
WHERE ALL(r IN relationships(p)
      WHERE r.confidence >= 0.6
        AND NOT coalesce(r.revoked, false)
        AND (r.valid_until IS NULL OR r.valid_until > datetime()))
RETURN p, length(p) AS hops
ORDER BY hops ASC

K-hop neighborhood expansion from a seed, with time window:

// 3-hop neighborhood active during a 30-day operational window
MATCH (seed:Entity {id: $seedId})-[r*1..3]-(neighbor)
WHERE ALL(rel IN r
      WHERE rel.confidence >= 0.5
        AND rel.valid_from <= $windowEnd
        AND (rel.valid_until IS NULL OR rel.valid_until >= $windowStart))
RETURN DISTINCT neighbor, labels(neighbor) AS types
LIMIT 500

Community detection with Louvain (run on in-memory graph projection):

// Project a confidence-weighted subgraph, run Louvain, write community IDs back
CALL gds.graph.project(
  'intel-graph',
  ['Entity'],
  {COMMUNICATES_WITH: {properties: ['confidence']}})

CALL gds.louvain.write('intel-graph', {
  writeProperty: 'communityId',
  relationshipWeightProperty: 'confidence'
}) YIELD communityCount, modularity

// Then surface the seed's community peers
MATCH (seed:Entity {id: $seedId})
MATCH (peer:Entity {communityId: seed.communityId})
WHERE peer.id <> $seedId
RETURN peer ORDER BY peer.degreeCentrality DESC LIMIT 50

Temporal link analysis — activity cadence over time:

// Count edge activations per calendar week for temporal pattern analysis
MATCH (a:Entity {id: $seedId})-[r:COMMUNICATES_WITH]-()
WHERE r.valid_from >= $rangeStart AND r.valid_from <= $rangeEnd
WITH r,
     date.truncate('week', r.valid_from) AS week
RETURN week,
       count(r) AS contact_count,
       avg(r.confidence) AS avg_confidence
ORDER BY week ASC

Temporal link analysis is the bridge to pattern of life analysis military tradecraft: when the cadence of a node's edge activations shifts — frequency drops, hours change, contact partners rotate — the graph surfaces that shift as a structural anomaly even before a purpose-built behavioral model is trained on the entity. The graph query is the first-pass detector; the behavioral model is the confirmatory layer.

A note on performance: community detection and centrality algorithms run over the full projected graph, not just a subgraph. For a graph with tens of millions of nodes, these algorithms must be scheduled as background batch jobs rather than interactive queries. The results — communityId and degreeCentrality written back as node properties — are then available for instant lookup in analyst queries without re-running the algorithm.

Integrating knowledge graphs with C2 and OSINT pipelines

A knowledge graph that is not connected to live data sources is a historical artifact rather than an operational tool. The integration architecture has three ingestion channels and two consumption endpoints.

CTI feed ingestion (STIX/TAXII). A TAXII client polls registered collection endpoints — allied CTI sharing platforms, national threat intelligence feeds — and processes incoming STIX bundles through the SDO-to-node / SRO-to-edge pipeline described above. Bundle processing is idempotent: MERGE on STIX id ensures that a re-delivered object updates properties rather than creating a duplicate node. New objects are entity-resolved against the existing graph before being committed; if a new STIX ThreatActor matches an existing non-STIX Person node through probabilistic identity resolution, the two are merged and all edges from both records are now traversable from the canonical node.

HUMINT report ingestion. Natural language reports from human sources are processed through an NLP pipeline that performs named entity recognition, relation extraction, and coreference resolution, then maps extracted entities and relationships to the graph schema. The NLP pipeline is not the graph: it is the transformation layer from prose to structured graph elements. Extracted entities go through the same entity resolution pipeline as any other source, with a typically lower base confidence (prose extraction is less reliable than a structured database record) that is propagated to the edges the HUMINT report supports.

SIGINT track ingestion. SIGINT observations — a communication between two endpoints at a specific time, a device appearing at a specific location — arrive as a streaming feed. Each observation creates or refreshes an edge in the graph: a COMMUNICATES_WITH edge between two person or device nodes, or an OBSERVED_AT edge between a device and a location. The edge's valid_from is the observation timestamp; valid_until is null unless the SIGINT feed explicitly closes the observation. High-volume streaming ingestion requires a queue-backed ingestor (Kafka or equivalent) that buffers observations and batches them into graph writes at a rate the database can absorb without blocking interactive queries.

The two consumption endpoints are the analyst workstation and the C2 common operational picture (COP).

The analyst workstation exposes a link-analysis interface where an analyst specifies a seed entity, selects relationship types and confidence thresholds, and expands a bounded neighborhood. The result is rendered as a force-directed graph layout with confidence encoded as edge opacity and community membership encoded as node color. Centrality scores determine node size. The analyst never sees the whole graph — only the bounded, filtered, query-driven subgraph that answers a specific question. A good interface provides the Cypher pattern in a sidebar so the analyst understands exactly what was queried and can refine it.

The C2 COP integration surfaces graph-derived entity profiles — a unit's known subordinates, an individual's community membership, a facility's supply dependencies — as popups on the tactical picture. When the operator clicks a tracked entity, the C2 system queries the knowledge graph for that entity's network context and displays the top five related entities by confidence and centrality. This transforms the knowledge graph from a standalone analytic tool into an active component of the operational picture: structured relationship intelligence, available in context, without requiring the operator to open a separate tool.

Architecture principle: The knowledge graph is not the visualization — it is the data layer. Invest in ingestion quality, entity resolution accuracy, and index performance. The visualization is the last mile. An analyst interface built on a poorly resolved graph with low-confidence edges will mislead faster and more confidently than any manual process it replaces. The graph earns operational trust through auditability: every displayed connection must be expandable to its underlying evidence, with source, collection time, and confidence attached.

Bring relationship intelligence into your operational picture

Corvus HEAD fuses multi-source intelligence into a queryable knowledge graph — entity resolution, link analysis, and C2-integrated network visualization built for analysts who need auditable, confidence-weighted answers, not hairballs.

Explore Corvus HEAD → Book a Briefing

This analysis was prepared by Corvus Intelligence engineers who build mission-critical intelligence and data-integration systems for defense and government organizations. Learn about our team →