---
title: "Quantitative Cyber Risk Analysis: Structural Vulnerability & Criticality Scoring via PageRank"
subtitle: "Modeling Cascading Operational Dependencies and Single Points of Failure"
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. </br></br>
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. </br></br>
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. </br></br>
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."
author: Patrick Lefler
date: "2026-07-30"
format:
html:
code-fold: true
code-copy: true
code-overflow: wrap
code-tools: true
code-summary: "Display code"
df-print: kable
embed-math: true
embed-resources: true
fig-align: center
fig-height: 6
fig-width: 10
highlight-style: arrow
lightbox: true
linkcolor: "#0166CC"
number-sections: false
page-layout: full
smooth-scroll: true
theme: sandstone
toc: true
toc-depth: 3
toc-location: right
toc-title: "Contents"
execute:
echo: true
warning: false
message: false
html-math-method: mathjax
knitr:
opts_chunk:
comment: "#>"
---
## 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.
::: {.callout-note icon="false"}
## A 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.
```{r setup}
# 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.
```{r data-scaffolding}
# 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.
```{r}
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.
```{r solver-validation}
# 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))
)
```
```{r Validation-assert}
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))
```
## 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.
```{r visnetwork-interactive}
#| 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.
```{r power-iteration-residual-decay-plot}
#| 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.
```{r dependency-matrix-topology-plot}
# 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$).
```{r spof-simulation}
# 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** </br> 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.
```{r systematic-stress-shift-plot}
# 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)** </br> 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.
```{r contagion-sensitivity-plot}
# 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.
```{r executive-scorecard}
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
)
```
## 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: </br>
**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.</br>
**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.</br>
**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
```{r}
#| echo: false
sessioninfo::session_info()
```
------------------------------------------------------------------------
*Rendered with [Quarto](https://quarto.org/) and R. Core packages: `gt`, `igraph`, `Matrix`, `patchwork`, `scales`, `sessioninfo`, `tidygraph`, `tidyverse`, `visNetwork`*