Quantitative Cyber Risk Analysis: Structural Vulnerability & Criticality Scoring via PageRank

Modeling Cascading Operational Dependencies and Single Points of Failure

Author

Patrick Lefler

Published

July 30, 2026

Abstract
Enterprise technology risk assessments typically rely on subjective questionnaires that treat every dependency as equal weight. This project replaces that approach with Google’s PageRank algorithm, adapted to score structural criticality across a 25-node synthetic enterprise IT topology spanning four architectural tiers: foundational core, data persistence, microservices, and ingress/perimeter gateways.

A custom vectorized power-iteration solver computes stationary criticality scores and converges in 23 cycles to within 5.44 × 10⁻¹¹ of the igraph reference implementation, confirming numerical stability. The results show risk is not evenly distributed: two assets, the credential vault (15.27%) and the identity access manager (13.79%), account for nearly 30% of total systemic weight, exceeding the 8% single-point-of-failure threshold (p*ᵢ > 2/n) defined for this network.

Two shock simulations test that concentration under stress. A targeted node-elimination scenario for the top three Tier-0 assets computes a Rank Distortion Index showing how criticality redistributes when a foundational system fails; eliminating the credential vault shifts roughly nine percentage points of stress onto identity access management alone. A damping-factor sweep from 0.50 to 0.99 shows that as dependency friction rises, Tier-0 assets absorb an increasing share of systemic risk while storage and messaging layers stay flat.

These findings anchor a governance scorecard that sorts all 25 assets into three vulnerability classes, each paired with a specific architecture mitigation: multi-region replication, circuit breakers, or standard telemetry. The framework gives boards and risk committees a reproducible, quantitative basis for prioritizing resilience investment, replacing subjective risk matrices with a ranked, auditable measure of where cascading failure actually originates.

Introduction

Imagine you are managing a massive international airport. You have ticket counters, baggage carousels, security checkpoints, fueling trucks, runway lights, and air traffic control.

If one of the mens restrooms is closed for cleaning in Terminal 2, some travelers might be annoyed, but planes still take off. But if the main radar system or the jet-fuel pumping station loses power, everything grinds to a halt.

This project solves that exact problem—not for airports, but for large computer networks and tech companies like Netflix, banks, or hospital systems. It uses math originally designed by Google to automatically find the most dangerous “hidden bottlenecks” before they crash the whole system.

NoteA Short History of Google PageRank

PageRank started in 1996 as a research project at Stanford, created by PhD students Larry Page and Sergey Brin. They built on an earlier prototype called BackRub, which began by crawling the web from Stanford’s homepage (Wikipedia; History of Google). Inspired by academic citations, they believed a page’s importance could be measured by counting and weighing the links to it, with each link serving as a vote of confidence. In January 1998, they filed a patent called “Method for Node Ranking in a Linked Database,” which was assigned to Stanford. In April 1998, they published the key paper “The Anatomy of a Large-Scale Hypertextual Web Search Engine” (HowStuffWorks; Wikipedia). Later that year, they launched Google. PageRank helped Google quickly overtake competitors like AltaVista. Today, Google uses many ranking signals, but PageRank remains a basic, though less important, part of the system. Its patents expired in 2019.

Why Google PageRank Algorithm?

Google’s PageRank algorithm measures the importance of an entity by looking at the significance of the systems that connect to it, not just the number of connections. While it was first used to rank web pages based on the credibility of incoming links, this approach can also help with enterprise risk management by finding hidden structural “keystones” in a complex technology ecosystem. Instead of using subjective questionnaires, the algorithm uses math to show how operational reliance and stress move through layers of dependencies. It can reveal how an outage in a key service, like an identity access manager or credential vault, can spread and disrupt client-facing applications.

PageRank was chosen instead of other network metrics because simply counting connections, known as degree centrality, treats a link to a minor reporting tool the same as a link to a core revenue system, which hides real vulnerabilities. Other methods, like betweenness centrality, assume failures only move along the shortest path, not through all possible communication channels. Traditional eigenvector metrics do not work well with directed, layered software systems. PageRank uses a “damping factor” to balance predictable dependency chains with random, unexpected shocks. This helps turn complex technology networks into a clear, percentage-based risk scorecard that leaders can use right away to make decisions about investments and resilience.

Environmental Setup

This initialization block loads the core R libraries required for data manipulation, sparse matrix algebra, network graph modeling, and executive-level visualization. It integrates specialized packages such as igraph and tidygraph for dependency graph mechanics alongside visNetwork and gt for interactive charting and risk scorecard rendering. Additionally, it defines a standardized, publication-grade ggplot2 theme to ensure consistent and reproducible visual formatting throughout the analysis.

Display code
# Core Data Science & Matrix Operations
library(tidyverse)
library(Matrix)

# Network Analysis & Graph Algorithms
library(igraph)
library(tidygraph)

# Visualizations & Executive Presentation
library(ggplot2)
library(scales)
library(patchwork)
library(visNetwork)
library(gt)

# Set global theme for reproducible figures
theme_set(
  theme_minimal(base_size = 12) +
    theme(
      plot.title = element_text(face = "bold", size = 14),
      plot.subtitle = element_text(color = "#555555", size = 11),
      panel.grid.minor = element_blank(),
      legend.position = "bottom"
    )
)

Data Scaffolding

The data scaffolding block constructs the synthetic enterprise IT topology by defining 25 representative infrastructure nodes classified across four architectural tiers with baseline criticality ratings. It maps out the directed operational dependency relationships in an edge list, explicitly specifying which perimeter gateways and microservices rely on underlying foundational core and data persistence layers. Finally, it joins these service relationships to numerical node identifiers, establishing the relational structure required for downstream graph modeling and matrix operations.

Display code
# Define 25 Infrastructure Nodes with Tier Classifications
nodes_df <- tibble(
  id = 1:25,
  name = c(
    # Tier 0: Foundational Core
    "Cloud_IAM", "Root_DNS", "IdP_Auth_Okta", "Secrets_Vault", "Kafka_Bus",
    # Tier 1: Data & Persistence
    "Core_Postgres_DB", "Redis_Session_Cache", "S3_Asset_Store", "Ledger_DB", "Elastic_Search",
    # Tier 2: Microservices
    "Payment_Execution_API", "Order_Routing_Svc", "Fraud_Engine", "Settlement_API", "User_Profile_Svc",
    "Notification_Gateway", "Invoicing_API", "KYC_Verification_API", "Risk_Scoring_Engine", "Inventory_Svc",
    # Tier 3: Ingress / Perimeter
    "Edge_Cloudflare_CDN", "Ingress_WAF_Gateway", "Web_Client_Gateway", "Mobile_API_Gateway", "Partner_B2B_Gateway"
  ),
  tier = c(
    rep("Tier 0: Foundational Core", 5),
    rep("Tier 1: Data & Persistence", 5),
    rep("Tier 2: Microservices", 10),
    rep("Tier 3: Ingress / Perimeter", 5)
  ),
  crit_rating = c(
    rep("Mission Critical", 5),
    rep("High", 5),
    rep("Medium-High", 10),
    rep("Medium", 5)
  )
)

# Define Directed Operational Dependencies (Source relies on Target: from -> to)
edges_df <- tibble(
  from_name = c(
    # Perimeter relying on Ingress & Auth
    "Edge_Cloudflare_CDN", "Edge_Cloudflare_CDN",
    "Ingress_WAF_Gateway", "Ingress_WAF_Gateway", "Ingress_WAF_Gateway",
    "Web_Client_Gateway", "Web_Client_Gateway", "Web_Client_Gateway",
    "Mobile_API_Gateway", "Mobile_API_Gateway", "Mobile_API_Gateway",
    "Partner_B2B_Gateway", "Partner_B2B_Gateway", "Partner_B2B_Gateway",
    
    # Microservices relying on Core Tier 0 & Data Tier 1
    "User_Profile_Svc", "User_Profile_Svc", "User_Profile_Svc", "User_Profile_Svc",
    "Order_Routing_Svc", "Order_Routing_Svc", "Order_Routing_Svc", "Order_Routing_Svc",
    "Inventory_Svc", "Inventory_Svc", "Inventory_Svc",
    "Fraud_Engine", "Fraud_Engine", "Fraud_Engine", "Fraud_Engine",
    "Risk_Scoring_Engine", "Risk_Scoring_Engine", "Risk_Scoring_Engine",
    "KYC_Verification_API", "KYC_Verification_API", "KYC_Verification_API",
    "Payment_Execution_API", "Payment_Execution_API", "Payment_Execution_API", "Payment_Execution_API",
    "Settlement_API", "Settlement_API", "Settlement_API",
    "Invoicing_API", "Invoicing_API", "Invoicing_API",
    "Notification_Gateway", "Notification_Gateway",
    
    # Data Layer relying on Vault & IAM
    "Core_Postgres_DB", "Core_Postgres_DB",
    "Ledger_DB", "Ledger_DB",
    "Redis_Session_Cache", "S3_Asset_Store", "Elastic_Search",
    "Kafka_Bus", "Kafka_Bus"
  ),
  to_name = c(
    # Ingress mappings
    "Ingress_WAF_Gateway", "Root_DNS",
    "Web_Client_Gateway", "Mobile_API_Gateway", "Partner_B2B_Gateway",
    "IdP_Auth_Okta", "User_Profile_Svc", "Order_Routing_Svc",
    "IdP_Auth_Okta", "User_Profile_Svc", "Payment_Execution_API",
    "IdP_Auth_Okta", "Order_Routing_Svc", "Payment_Execution_API",
    
    # Microservice dependencies
    "IdP_Auth_Okta", "Core_Postgres_DB", "Redis_Session_Cache", "Cloud_IAM",
    "Inventory_Svc", "Fraud_Engine", "Kafka_Bus", "Core_Postgres_DB",
    "Core_Postgres_DB", "Redis_Session_Cache", "Secrets_Vault",
    "Risk_Scoring_Engine", "Elastic_Search", "Redis_Session_Cache", "Kafka_Bus",
    "Core_Postgres_DB", "Elastic_Search", "Secrets_Vault",
    "Secrets_Vault", "S3_Asset_Store", "Core_Postgres_DB",
    "Secrets_Vault", "Fraud_Engine", "Ledger_DB", "Kafka_Bus",
    "Ledger_DB", "Secrets_Vault", "Kafka_Bus",
    "Ledger_DB", "S3_Asset_Store", "Notification_Gateway",
    "Kafka_Bus", "Redis_Session_Cache",
    
    # Data layer internal dependencies
    "Secrets_Vault", "Cloud_IAM",
    "Secrets_Vault", "Cloud_IAM",
    "Secrets_Vault", "Cloud_IAM", "Cloud_IAM",
    "Secrets_Vault", "Cloud_IAM"
  )
) %>%
  left_join(nodes_df %>% select(id, name), by = c("from_name" = "name")) %>%
  rename(from = id) %>%
  left_join(nodes_df %>% select(id, name), by = c("to_name" = "name")) %>%
  rename(to = id)

Algorithmic Implementation: Vectorized Power Iteration

The calculation engine is at the heart of the project. It turns the complicated web of technology dependencies into clear, standardized risk percentages for each system. The engine works by running repeated simulations of how operational reliance moves through the organization until the scores settle into a steady ranking of structural importance. This automated process removes guesswork and provides leaders with reliable data to help them decide how to allocate cybersecurity and infrastructure resilience budgets.

Display code
compute_custom_pagerank <- function(nodes, edges, alpha = 0.85, tol = 1e-9, max_iter = 500) {
  n <- nrow(nodes)
  
  # Step 1: Construct Adjacency Matrix A (from -> to implies from depends on to)
  A <- matrix(0, nrow = n, ncol = n)
  for (k in 1:nrow(edges)) {
    A[edges$to[k], edges$from[k]] <- 1 # A[i, j] = 1 means link j -> i
  }
  
  # Step 2: Column-normalize to construct stochastic transition matrix M
  out_degrees <- colSums(A)
  M <- matrix(0, nrow = n, ncol = n)
  
  for (j in 1:n) {
    if (out_degrees[j] > 0) {
      M[, j] <- A[, j] / out_degrees[j]
    } else {
      # Dangling node redistribution
      M[, j] <- rep(1 / n, n)
    }
  }
  
  # Step 3: Initialize uniform probability vector
  p <- rep(1 / n, n)
  residual_log <- numeric(max_iter)
  
  # Step 4: Execute Power Iteration
  for (iter in 1:max_iter) {
    p_next <- alpha * (M %*% p) + rep((1 - alpha) / n, n)
    
    # L1 residual error
    residual <- sum(abs(p_next - p))
    residual_log[iter] <- residual
    p <- as.vector(p_next)
    
    if (residual < tol) {
      residual_log <- residual_log[1:iter]
      break
    }
  }
  
  list(
    criticality_vector = p,
    iterations = length(residual_log),
    residuals = residual_log,
    transition_matrix = M,
    adjacency_matrix = A
  )
}

# Run custom solver
custom_solution <- compute_custom_pagerank(nodes_df, edges_df, alpha = 0.85)

Solver Validation: Custom Engine vs. igraph::page_rank

The validation table serves as an independent quality check, comparing our custom risk calculation engine to the industry-standard igraph library to show its accuracy and reliability. The scores are almost identical, with differences smaller than one-billionth of a percent, which shows the algorithm is numerically stable after just 23 calculation cycles. This benchmark gives executive leadership clear proof that credential storage (Secrets_Vault at 15.27%) and access management (Cloud_IAM at 13.79%) are our most critical points of failure. By confirming the model’s accuracy, the table assures the board that capital allocation and cyber resilience budgets are based on reliable, audited risk metrics.

Display code
# Instantiate directed igraph object
ig_graph <- graph_from_data_frame(
  d = edges_df %>% select(from_name, to_name),
  vertices = nodes_df %>% select(name, tier, crit_rating),
  directed = TRUE
)

# Compute PageRank via igraph
igraph_solution <- page_rank(
  ig_graph,
  directed = TRUE,
  damping = 0.85,
  algo = "prpack"
)$vector

# Merge solutions for side-by-side precision testing
validation_df <- nodes_df %>%
  mutate(
    p_custom = custom_solution$criticality_vector,
    p_igraph = igraph_solution[name],
    abs_delta = abs(p_custom - p_igraph),
    rank_custom = rank(-p_custom),
    rank_igraph = rank(-p_igraph)
  ) %>%
  arrange(rank_custom)

# Display Validation Scorecard
validation_df %>%
  select(rank_custom, name, tier, p_custom, p_igraph, abs_delta) %>%
  slice_head(n = 10) %>%
  gt() %>%
  tab_header(
    title = "Algorithm Validation: Custom Power Iteration vs. igraph",
    subtitle = "Benchmarking 10-9 Convergence Tolerance on Stationary Criticality Vectors"
  ) %>%
  cols_label(
    rank_custom = "Rank",
    name = "Asset Name",
    tier = "Tier",
    p_custom = "Custom Solver (p*)",
    p_igraph = "igraph Reference",
    abs_delta = "Absolute Difference (|Δ|)"
  ) %>%
  fmt_number(columns = c(p_custom, p_igraph), decimals = 6) %>%
  fmt_scientific(columns = abs_delta, decimals = 2) %>%
  tab_style(
    style = cell_text(weight = "bold"),
    locations = cells_body(columns = c(p_custom, rank_custom))
  )
Algorithm Validation: Custom Power Iteration vs. igraph
Benchmarking 10-9 Convergence Tolerance on Stationary Criticality Vectors
Rank Asset Name Tier Custom Solver (p*) igraph Reference Absolute Difference (|Δ|)
1 Secrets_Vault Tier 0: Foundational Core 0.152724 0.152724 3.63 × 10−11
2 Cloud_IAM Tier 0: Foundational Core 0.137918 0.137918 5.44 × 10−11
3 Kafka_Bus Tier 0: Foundational Core 0.054528 0.054528 4.90 × 10−12
4 Core_Postgres_DB Tier 1: Data & Persistence 0.051971 0.051971 1.31 × 10−12
5 Redis_Session_Cache Tier 1: Data & Persistence 0.049502 0.049502 4.09 × 10−12
6 IdP_Auth_Okta Tier 0: Foundational Core 0.047327 0.047327 6.36 × 10−12
7 Ledger_DB Tier 1: Data & Persistence 0.035811 0.035811 3.77 × 10−12
9 Payment_Execution_API Tier 2: Microservices 0.033003 0.033003 7.60 × 10−12
9 Order_Routing_Svc Tier 2: Microservices 0.033003 0.033003 7.60 × 10−12
9 User_Profile_Svc Tier 2: Microservices 0.033003 0.033003 7.60 × 10−12
Display code
max_discrepancy <- max(validation_df$abs_delta)
cat(sprintf("Validation Status: SUCCESS\nMaximum Absolute Discrepancy: %.2e\nTotal Iterations to Convergence: %d\n", 
            max_discrepancy, custom_solution$iterations))
#> Validation Status: SUCCESS
#> Maximum Absolute Discrepancy: 5.44e-11
#> Total Iterations to Convergence: 23

Structural Network & Convergence Visualizations

Interactive Cyber Dependency Map

This interactive network graph shows how different parts of the enterprise architecture depend on each other. Each node is color-coded by its architectural tier, and arrows show the direction of operational dependencies. The size of each node reflects its PageRank criticality score, making it easy to spot key points of failure such as Secrets_Vault and Cloud_IAM. Users can click or hover over nodes to see which assets rely on them and to explore possible paths for cascading failures.

Display code
#| fig-width: 11
#| fig-height: 8

vis_nodes <- nodes_df %>%
  mutate(
    criticality = custom_solution$criticality_vector,
    label = name,
    value = criticality * 200, # Size proportional to structural importance
    group = tier,
    title = paste0(
      "<div style='font-family:sans-serif;'>",
      "<b>Asset:</b> ", name, "<br>",
      "<b>Classification:</b> ", tier, "<br>",
      "<b>PageRank Criticality:</b> ", round(criticality, 5), "<br>",
      "<b>Structural Rank:</b> #", rank(-criticality),
      "</div>"
    )
  )

vis_edges <- edges_df %>%
  mutate(arrows = "to")

visNetwork(vis_nodes, vis_edges, main = "Enterprise Cyber Infrastructure Dependency Mesh") %>%
  visGroups(groupname = "Tier 0: Foundational Core", color = list(background = "#d9534f", border = "#b52b27")) %>%
  visGroups(groupname = "Tier 1: Data & Persistence", color = list(background = "#f0ad4e", border = "#d58512")) %>%
  visGroups(groupname = "Tier 2: Microservices", color = list(background = "#0275d8", border = "#014c8c")) %>%
  visGroups(groupname = "Tier 3: Ingress / Perimeter", color = list(background = "#5cb85c", border = "#3d8b3d")) %>%
  visOptions(highlightNearest = list(enabled = TRUE, degree = 1, hover = TRUE), nodesIdSelection = TRUE) %>%
  visPhysics(solver = "forceAtlas2Based", forceAtlas2Based = list(gravitationalConstant = -40)) %>%
  visLayout(randomSeed = 101)

Convergence Rate & Transition Matrix Sparsity

This diagnostic section evaluates the mathematical stability of the scoring engine alongside the structural density of the underlying technology architecture. The residual decay plot confirms the efficiency of the Power Iteration algorithm by showing exponential error decay toward the strict ‭\(10^{-9}\)$10^{-9}$$10^{-9}$$10^{-9}$‬ convergence tolerance. Complementing this, the dependency matrix heatmap visualizes network sparsity, pinpointing where operational call volumes concentrate heavily into foundational Tier-0 assets.

Power Iteration Residual Decay Plot

The Power Iteration Residual Decay plot shows how the risk engine’s calculation error changes over time. It quickly stabilizes, and the error drops sharply with each round. After just 23 cycles, the error is almost zero. This gives executive leadership clear evidence that our final infrastructure rankings are accurate, stable, and reliable for making decisions about capital allocation and cyber resilience.

Display code
#| label: convergence-sparsity-plots
#| fig-width: 11
#| fig-height: 8

# Panel A: Residual Convergence Trajectory
conv_df <- tibble(
  Iteration = 1:length(custom_solution$residuals),
  Residual = custom_solution$residuals
)

p1 <- ggplot(conv_df, aes(x = Iteration, y = Residual)) +
  geom_line(color = "#0275d8", linewidth = 1) +
  geom_point(color = "#014c8c", size = 1.8) +
  scale_y_log10(labels = trans_format("log10", math_format(10^.x))) +
  labs(
    title = "Power Iteration Residual Decay",
    subtitle = "Log10 L1-Norm Residual ||p(k+1) - p(k)|| per Step",
    x = "Iteration Step",
    y = "Residual Error (Log Scale)"
  )

p1

Dependency Matrix Topology Heatmap

The Dependency Matrix Topology heatmap visualizes all directed operational linkages across the infrastructure, where red cells indicate an active dependency from a calling service (X-axis) to an underlying asset (Y-axis). The dense horizontal clustering across top rows—particularly Secrets_Vault, Cloud_IAM, and Core_Postgres_DB—reveals where disparate microservices converge on the same foundational systems. These results matter because they expose systemic single points of failure, providing empirical justification for where engineering teams must prioritize multi-region redundancy, asynchronous circuit breakers, and zero-trust ring-fencing to prevent cascading outages.

Display code
# Panel B: Adjacency / Dependency Matrix Heatmap (FIXED)
adj_mat <- custom_solution$adjacency_matrix
rownames(adj_mat) <- nodes_df$name
colnames(adj_mat) <- nodes_df$name

M_tidy <- as.data.frame(as.table(adj_mat)) %>%
  rename(To = Var1, From = Var2, Connected = Freq) %>%
  mutate(
    # Preserve topological hierarchy on the axes
    To = factor(To, levels = rev(nodes_df$name)),
    From = factor(From, levels = nodes_df$name)
  )

p2 <- ggplot(M_tidy, aes(x = From, y = To, fill = factor(Connected))) +
  geom_tile(color = "white", linewidth = 0.2) +
  scale_fill_manual(
    values = c("0" = "#f8f9fa", "1" = "#d9534f"),
    guide = "none"
  ) +
  labs(
    title = "Dependency Matrix Topology",
    subtitle = "Directed Structural Linkages (Red = Direct Reliance)",
    x = "Calling Dependent Service (From)",
    y = "Underlying Critical Asset (To)"
  ) +
  theme(
    axis.text.x = element_text(angle = 45, vjust = 1, hjust = 1, size = 8),
    axis.text.y = element_text(size = 8)
  )

p2

Cyber Shock Scenarios & Single Point of Failure (SPOF) Analysis

To measure systemic fragility, we execute targeted failure simulations:

  1. Targeted Outage Simulation (Node Elimination): We sever all operational availability of top Tier-0 assets (Secrets_Vault, Cloud_IAM, and IdP_Auth_Okta) and compute the Rank Distortion Index (RDI) across remaining systems: ‭\[\text{RDI}(v_i) = \sqrt{\sum_{j \neq i} \left( p^*_{\text{baseline}}(j) - p^*_{-i}(j) \right)^2}\]‬‭‬‭‬‭‬‭‬
  2. Contagion Damping Sensitivity (‭\(\alpha\)‬-Sweep): Sweeping ‭\(\alpha \in [0.50, 0.99]\)‬‭‬‭‬‭‬‭‬ ‭‬‭‬ to model operational modes from asynchronous resilience (‭\(\alpha \to 0.50\)‬‭‬) to rigid, blocking cascades (‭\(\alpha \to 0.99\)‬‭‬).
Display code
# Function to simulate node failure and recompute network stationary state
simulate_node_knockout <- function(target_node_name, nodes, edges, baseline_p) {
  target_id <- nodes %>% filter(name == target_node_name) %>% pull(id)
  
  # Remove all edges linked to the knocked out asset
  shocked_edges <- edges %>%
    filter(from != target_id, to != target_id)
  
  # Re-solve Markov stationary state
  shock_res <- compute_custom_pagerank(nodes, shocked_edges, alpha = 0.85)
  shock_p <- shock_res$criticality_vector
  
  # Zero out the removed node and re-normalize among surviving nodes
  shock_p[target_id] <- 0
  shock_p <- shock_p / sum(shock_p)
  
  # Calculate Rank Distortion Metric (Euclidean shift)
  rdi <- sqrt(sum((baseline_p - shock_p)^2))
  
  tibble(
    name = nodes$name,
    tier = nodes$tier,
    baseline_score = baseline_p,
    shock_score = shock_p,
    delta_score = shock_p - baseline_p,
    shock_target = target_node_name,
    rdi = rdi
  )
}

# Run SPOF Knockout on Top Critical Assets
baseline_p <- custom_solution$criticality_vector
shock_vault <- simulate_node_knockout("Secrets_Vault", nodes_df, edges_df, baseline_p)
shock_iam   <- simulate_node_knockout("Cloud_IAM", nodes_df, edges_df, baseline_p)
shock_idp   <- simulate_node_knockout("IdP_Auth_Okta", nodes_df, edges_df, baseline_p)

all_shocks <- bind_rows(shock_vault, shock_iam, shock_idp)

Post-Shock Rank Distortion & Stress Shifts

This section evaluates enterprise infrastructure resilience by simulating targeted component outages and parameter perturbations to quantify structural risk migration. By measuring reallocated criticality scores (‭\(\Delta p^*\)‬) and sweeping the contagion damping parameter (‭\(\alpha\)‬), it reveals how operational stress dynamically shifts to surviving systems. These simulations demonstrate that systemic risk is non-static, pinpointing second-order bottlenecks that become critical during an active outage.

Systemic Stress Shift: Secrets_Vault Outage
This chart illustrates the reallocation of operational criticality (‭\(\Delta p^*\)‬) across surviving infrastructure nodes following a simulated failure of Secrets_Vault. Cloud_IAM absorbs the vast majority of redirected systemic stress (‭\(\Delta p^* \approx +0.09\)‬‭‬‭‬), while Core_Postgres_DB and Kafka_Bus experience secondary compounding load. This reveals that an outage in secrets management immediately concentrates risk onto identity access layers, requiring joint architectural hardening across both Tier-0 assets.

Display code
# Visualize top 10 impacted nodes when Secrets_Vault fails
p_shock1 <- shock_vault %>%
  filter(name != "Secrets_Vault") %>%
  arrange(desc(abs(delta_score))) %>%
  slice_head(n = 10) %>%
  ggplot(aes(x = reorder(name, delta_score), y = delta_score, fill = delta_score > 0)) +
  geom_col(width = 0.7) +
  coord_flip() +
  scale_fill_manual(values = c("TRUE" = "#d9534f", "FALSE" = "#0275d8"), guide = "none") +
  labs(
    title = "Systemic Stress Shift: Secrets_Vault Outage",
    subtitle = "Change in Relative Criticality (Δp*) Across Surviving Assets",
    x = "Infrastructure Node",
    y = "Criticality Shift (Δp*)"
  )

p_shock1

Contagion Sensitivity (α-Sweep)
This plot tracks how steady-state criticality scores (‭\(p^*\)‬) respond as the damping factor ‭\(\alpha\)‬ increases from 0.50 to 0.95, representing the transition from loosely coupled, asynchronous workflows to rigid, cascading operational dependencies. The foundational Tier-0 assets—Secrets_Vault and Cloud_IAM—display steep upward trajectories, capturing an increasingly dominant share of systemic risk as dependency friction intensifies. In contrast, underlying storage and messaging layers (Core_Postgres_DB, Kafka_Bus, Redis_Session_Cache) maintain flat, stable criticality profiles across all operational regimes.

Display code
# Visualize Alpha Contagion Sensitivity Sweep
alpha_seq <- seq(0.50, 0.95, by = 0.05)
alpha_sweep_results <- map_dfr(alpha_seq, function(a) {
  res <- compute_custom_pagerank(nodes_df, edges_df, alpha = a)
  tibble(
    alpha = a,
    name = nodes_df$name,
    criticality = res$criticality_vector
  )
})

top_assets <- validation_df %>% slice_head(n = 5) %>% pull(name)

p_shock2 <- alpha_sweep_results %>%
  filter(name %in% top_assets) %>%
  ggplot(aes(x = alpha, y = criticality, color = name)) +
  geom_line(linewidth = 1.1) +
  geom_point(size = 2) +
  scale_color_brewer(palette = "Set1") +
  labs(
    title = "Contagion Sensitivity (α-Sweep)",
    subtitle = "Criticality Sensitivity across Varying Operational Friction Parameters",
    x = "Damping Factor (α)",
    y = "Stationary Score (p*)",
    color = "Top Assets"
  )

p_shock2

Enterprise Risk Governance & Decision Rules

The Enterprise Risk Governance Scorecard uses PageRank-based systemic weights to create a practical hierarchy, sorting infrastructure assets into three vulnerability levels: Extreme (Systemic SPOF), Elevated (Cluster Bottleneck), and Standard Operational Risk. Most structural risk is concentrated in Tier-0 components, especially Secrets_Vault (15.27%), Cloud_IAM (13.79%), and Kafka_Bus (5.45%). Together, these make up over a third of the network’s total operational risk. Core data storage systems like Core_Postgres_DB (5.20%), Redis_Session_Cache (4.95%), and Ledger_DB (3.58%) are key cluster bottlenecks. These act as aggregation points and need automated scaling and DNS failover to stop local issues from affecting the wider system.

The other 18 microservices and perimeter gateways have lower systemic weights, ranging from 1.84% to 3.30%. This shows that issues at the perimeter do not automatically put the core of the enterprise at risk. By connecting Markov centrality scores to clear engineering actions, the framework sets up transparent, data-driven rules for managing risk and allocating resources. This helps leaders apply targeted controls, like required multi-region replication and non-blocking circuit breakers for the most critical points of failure, so that cyber resilience budgets are used where they have the most impact.

Display code
final_governance_table <- validation_df %>%
  mutate(
    criticality_pct = p_custom * 100,
    rdi_exposure = case_when(
      rank_custom <= 3 ~ "Extreme (Systemic SPOF)",
      rank_custom <= 7 ~ "Elevated (Cluster Bottleneck)",
      TRUE ~ "Standard Operational Risk"
    ),
    governance_action = case_when(
      rank_custom == 1 ~ "Mandatory Multi-Region Active-Active Replication; Zero-Trust Ring-Fencing",
      rank_custom == 2 ~ "Asynchronous Circuit Breakers & In-Memory Fallback Caching",
      rank_custom <= 5 ~ "Automated Dynamic Read-Replica Scaling & DNS Auto-Failover",
      TRUE ~ "Standard Telemetry & High-Availability Service Mesh"
    )
  ) %>%
  select(rank_custom, name, tier, criticality_pct, rdi_exposure, governance_action)

final_governance_table %>%
  gt() %>%
  tab_header(
    title = "Enterprise Technology Infrastructure Risk Governance Scorecard",
    subtitle = "PageRank-Driven Criticality Tiers and Prescriptive Mitigation Actions"
  ) %>%
  cols_label(
    rank_custom = "Rank",
    name = "Asset Component",
    tier = "Topology Tier",
    criticality_pct = "Systemic Weight (%)",
    rdi_exposure = "Vulnerability Class",
    governance_action = "Prescriptive Architecture Action"
  ) %>%
  fmt_number(columns = criticality_pct, decimals = 2) %>%
  data_color(
    columns = criticality_pct,
    direction = "column",
    palette = c("#ffffff", "#fce4e4", "#d9534f")
  ) %>%
  tab_options(
    table.font.size = 11,
    heading.title.font.size = 14,
    heading.subtitle.font.size = 12
  )
Enterprise Technology Infrastructure Risk Governance Scorecard
PageRank-Driven Criticality Tiers and Prescriptive Mitigation Actions
Rank Asset Component Topology Tier Systemic Weight (%) Vulnerability Class Prescriptive Architecture Action
1.0 Secrets_Vault Tier 0: Foundational Core 15.27 Extreme (Systemic SPOF) Mandatory Multi-Region Active-Active Replication; Zero-Trust Ring-Fencing
2.0 Cloud_IAM Tier 0: Foundational Core 13.79 Extreme (Systemic SPOF) Asynchronous Circuit Breakers & In-Memory Fallback Caching
3.0 Kafka_Bus Tier 0: Foundational Core 5.45 Extreme (Systemic SPOF) Automated Dynamic Read-Replica Scaling & DNS Auto-Failover
4.0 Core_Postgres_DB Tier 1: Data & Persistence 5.20 Elevated (Cluster Bottleneck) Automated Dynamic Read-Replica Scaling & DNS Auto-Failover
5.0 Redis_Session_Cache Tier 1: Data & Persistence 4.95 Elevated (Cluster Bottleneck) Automated Dynamic Read-Replica Scaling & DNS Auto-Failover
6.0 IdP_Auth_Okta Tier 0: Foundational Core 4.73 Elevated (Cluster Bottleneck) Standard Telemetry & High-Availability Service Mesh
7.0 Ledger_DB Tier 1: Data & Persistence 3.58 Elevated (Cluster Bottleneck) Standard Telemetry & High-Availability Service Mesh
9.0 Payment_Execution_API Tier 2: Microservices 3.30 Standard Operational Risk Standard Telemetry & High-Availability Service Mesh
9.0 Order_Routing_Svc Tier 2: Microservices 3.30 Standard Operational Risk Standard Telemetry & High-Availability Service Mesh
9.0 User_Profile_Svc Tier 2: Microservices 3.30 Standard Operational Risk Standard Telemetry & High-Availability Service Mesh
11.0 Elastic_Search Tier 1: Data & Persistence 3.24 Standard Operational Risk Standard Telemetry & High-Availability Service Mesh
12.0 Fraud_Engine Tier 2: Microservices 3.24 Standard Operational Risk Standard Telemetry & High-Availability Service Mesh
13.0 S3_Asset_Store Tier 1: Data & Persistence 2.88 Standard Operational Risk Standard Telemetry & High-Availability Service Mesh
14.5 Root_DNS Tier 0: Foundational Core 2.62 Standard Operational Risk Standard Telemetry & High-Availability Service Mesh
14.5 Ingress_WAF_Gateway Tier 3: Ingress / Perimeter 2.62 Standard Operational Risk Standard Telemetry & High-Availability Service Mesh
17.0 Web_Client_Gateway Tier 3: Ingress / Perimeter 2.58 Standard Operational Risk Standard Telemetry & High-Availability Service Mesh
17.0 Mobile_API_Gateway Tier 3: Ingress / Perimeter 2.58 Standard Operational Risk Standard Telemetry & High-Availability Service Mesh
17.0 Partner_B2B_Gateway Tier 3: Ingress / Perimeter 2.58 Standard Operational Risk Standard Telemetry & High-Availability Service Mesh
19.0 Inventory_Svc Tier 2: Microservices 2.54 Standard Operational Risk Standard Telemetry & High-Availability Service Mesh
20.0 Risk_Scoring_Engine Tier 2: Microservices 2.53 Standard Operational Risk Standard Telemetry & High-Availability Service Mesh
21.0 Notification_Gateway Tier 2: Microservices 2.36 Standard Operational Risk Standard Telemetry & High-Availability Service Mesh
23.5 Settlement_API Tier 2: Microservices 1.84 Standard Operational Risk Standard Telemetry & High-Availability Service Mesh
23.5 Invoicing_API Tier 2: Microservices 1.84 Standard Operational Risk Standard Telemetry & High-Availability Service Mesh
23.5 KYC_Verification_API Tier 2: Microservices 1.84 Standard Operational Risk Standard Telemetry & High-Availability Service Mesh
23.5 Edge_Cloudflare_CDN Tier 3: Ingress / Perimeter 1.84 Standard Operational Risk Standard Telemetry & High-Availability Service Mesh

Insights & Conclusion

This governance framework replaces subjective risk matrices with mathematical precision, establishing exactly where operational disruptions will trigger catastrophic domino effects across the enterprise. To ensure resilient oversight and high-impact technology budgeting, the board and senior leadership should focus on three core takeaways:

Systemic Risk is Heavily Concentrated (The 8% Single Point of Failure Rule): Rather than operational risk being evenly distributed across all 25 systems, nearly 30% of total enterprise vulnerability resides in just two foundational hubs: our security credential storage (Secrets_Vault at 15.27%) and identity access management (Cloud_IAM at 13.79%). Any asset exceeding an 8% systemic dependency threshold (‭\(p_i^* > 2/n\)‬‭‬‭‬) is formally designated a Systemic Single Point of Failure (SPOF), triggering mandatory multi-region redundancy and dedicated cyber ring-fencing.

Mandatory Circuit Breakers to Halt Cascade Failures: Customer-facing and internal microservices must be engineered with automated “circuit breakers.” If a core upstream service experiences latency or an outage, dependent applications must degrade gracefully into safe fallback modes rather than stalling and crashing the entire platform in a chain reaction.

Traffic Isolation for Core Data Hubs: Primary transaction and ledger databases act as structural collection sinks for the entire business. Background reporting and non-essential analytical workloads must be physically isolated to dedicated database copies, ensuring high-volume background tasks never lock or disrupt live client transaction processing.

By tying technology investments directly to these structural dependency scores, leadership can ensure capital is deployed where it delivers the greatest reduction in systemic enterprise risk.

Session Information

#> ─ Session info ───────────────────────────────────────────────────────────────
#>  setting  value
#>  version  R version 4.5.2 (2025-10-31)
#>  os       macOS Tahoe 26.5.1
#>  system   aarch64, darwin20
#>  ui       X11
#>  language (EN)
#>  collate  en_US.UTF-8
#>  ctype    en_US.UTF-8
#>  tz       America/New_York
#>  date     2026-08-24
#>  pandoc   3.8.3 @ /Applications/RStudio.app/Contents/Resources/app/quarto/bin/tools/aarch64/ (via rmarkdown)
#>  quarto   1.8.26 @ /usr/local/bin/quarto
#> 
#> ─ Packages ───────────────────────────────────────────────────────────────────
#>  package      * version date (UTC) lib source
#>  cli            3.6.6   2026-04-09 [1] CRAN (R 4.5.2)
#>  digest         0.6.39  2025-11-19 [1] CRAN (R 4.5.2)
#>  dplyr        * 1.2.1   2026-04-03 [1] CRAN (R 4.5.2)
#>  evaluate       1.0.5   2025-08-27 [1] CRAN (R 4.5.0)
#>  farver         2.1.2   2024-05-13 [1] CRAN (R 4.5.0)
#>  fastmap        1.2.0   2024-05-15 [1] CRAN (R 4.5.0)
#>  forcats      * 1.0.1   2025-09-25 [1] CRAN (R 4.5.0)
#>  fs             1.6.6   2025-04-12 [1] CRAN (R 4.5.0)
#>  generics       0.1.4   2025-05-09 [1] CRAN (R 4.5.0)
#>  ggplot2      * 4.0.3   2026-04-22 [1] CRAN (R 4.5.2)
#>  glue           1.8.1   2026-04-17 [1] CRAN (R 4.5.2)
#>  gt           * 1.3.0   2026-01-22 [1] CRAN (R 4.5.2)
#>  gtable         0.3.6   2024-10-25 [1] CRAN (R 4.5.0)
#>  hms            1.1.4   2025-10-17 [1] CRAN (R 4.5.0)
#>  htmltools      0.5.8.1 2024-04-04 [1] CRAN (R 4.5.0)
#>  htmlwidgets    1.6.4   2023-12-06 [1] CRAN (R 4.5.0)
#>  igraph       * 2.2.1   2025-10-27 [1] CRAN (R 4.5.0)
#>  jsonlite       2.0.0   2025-03-27 [1] CRAN (R 4.5.0)
#>  knitr          1.50    2025-03-16 [1] CRAN (R 4.5.0)
#>  labeling       0.4.3   2023-08-29 [1] CRAN (R 4.5.0)
#>  lattice        0.22-7  2025-04-02 [1] CRAN (R 4.5.2)
#>  lifecycle      1.0.5   2026-01-08 [1] CRAN (R 4.5.2)
#>  lubridate    * 1.9.4   2024-12-08 [1] CRAN (R 4.5.0)
#>  magrittr       2.0.5   2026-04-04 [1] CRAN (R 4.5.2)
#>  Matrix       * 1.7-4   2025-08-28 [1] CRAN (R 4.5.2)
#>  patchwork    * 1.3.2   2025-08-25 [1] CRAN (R 4.5.0)
#>  pillar         1.11.1  2025-09-17 [1] CRAN (R 4.5.0)
#>  pkgconfig      2.0.3   2019-09-22 [1] CRAN (R 4.5.0)
#>  purrr        * 1.2.2   2026-04-10 [1] CRAN (R 4.5.2)
#>  R6             2.6.1   2025-02-15 [1] CRAN (R 4.5.0)
#>  RColorBrewer   1.1-3   2022-04-03 [1] CRAN (R 4.5.0)
#>  readr        * 2.1.5   2024-01-10 [1] CRAN (R 4.5.0)
#>  rlang          1.3.0   2026-07-05 [1] CRAN (R 4.5.2)
#>  rmarkdown      2.30    2025-09-28 [1] CRAN (R 4.5.0)
#>  rstudioapi     0.17.1  2024-10-22 [1] CRAN (R 4.5.0)
#>  S7             0.2.2   2026-04-22 [1] CRAN (R 4.5.2)
#>  sass           0.4.10  2025-04-11 [1] CRAN (R 4.5.0)
#>  scales       * 1.4.0   2025-04-24 [1] CRAN (R 4.5.0)
#>  sessioninfo    1.2.3   2025-02-05 [1] CRAN (R 4.5.0)
#>  stringi        1.8.7   2025-03-27 [1] CRAN (R 4.5.0)
#>  stringr      * 1.6.0   2025-11-04 [1] CRAN (R 4.5.0)
#>  tibble       * 3.3.1   2026-01-11 [1] CRAN (R 4.5.2)
#>  tidygraph    * 1.3.1   2024-01-30 [1] CRAN (R 4.5.0)
#>  tidyr        * 1.3.1   2024-01-24 [1] CRAN (R 4.5.0)
#>  tidyselect     1.2.1   2024-03-11 [1] CRAN (R 4.5.0)
#>  tidyverse    * 2.0.0   2023-02-22 [1] CRAN (R 4.5.0)
#>  timechange     0.3.0   2024-01-18 [1] CRAN (R 4.5.0)
#>  tzdb           0.5.0   2025-03-15 [1] CRAN (R 4.5.0)
#>  vctrs          0.7.3   2026-04-11 [1] CRAN (R 4.5.2)
#>  visNetwork   * 2.1.4   2025-09-04 [1] CRAN (R 4.5.0)
#>  withr          3.0.3   2026-06-19 [1] CRAN (R 4.5.2)
#>  xfun           0.54    2025-10-30 [1] CRAN (R 4.5.0)
#>  xml2           1.4.1   2025-10-27 [1] CRAN (R 4.5.0)
#>  yaml           2.3.10  2024-07-26 [1] CRAN (R 4.5.0)
#> 
#>  [1] /Library/Frameworks/R.framework/Versions/4.5-arm64/Resources/library
#>  * ── Packages attached to the search path.
#> 
#> ──────────────────────────────────────────────────────────────────────────────

Rendered with Quarto and R. Core packages: gt, igraph, Matrix, patchwork, scales, sessioninfo, tidygraph, tidyverse, visNetwork