> ## Documentation Index
> Fetch the complete documentation index at: https://www.worldmonitor.app/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Strategic Risk

> Composite strategic risk scoring that fuses every intelligence module — including the Pentagon Pizza Index — into a unified server-side assessment.

The Strategic Risk system provides a composite dashboard for global risk triage. Its server-published headline score is a top-5 Country Instability Index (CII) roll-up, while the panel layers additional convergence, infrastructure, theater posture, breaking-news, sanctions, and radiation-watch context around that headline.

## Strategic Risk Overview

The Strategic Risk Overview displays the server-side Strategic Risk headline and nearby operational context from the browser panel.

### Server Score and Browser Fallback (0-100)

The displayed/server headline score comes from `GetRiskScores.strategicRisks[0]`.
The server derives it from the weighted top 5 CII countries by `combinedScore`,
not from the additive panel context formula:

```
topScores = top 5 countries by combinedScore
weights = [1.00, 0.85, 0.70, 0.55, 0.40]
weightedAvg = sum(topScores[i] * weights[i]) / sum(weights)
headline = round(weightedAvg * 0.70 + 15)
```

See
[CII Risk Scoring Methodology](/docs/methodology/cii-risk-scores#3-strategic-risk-roll-up)
for the exact top-5 window, scale factor, floor, and severity bands.

When cached server scores are unavailable, the browser can render a local
fallback overview that combines map-side convergence, infrastructure, theater
posture, breaking news, sanctions, radiation-watch signals, and the locally
available CII summary:

```
compositeScore =
    convergenceScore * 0.30
  + ciiRiskScore     * 0.50
  + infraScore       * 0.20
  + theaterBoost
  + breakingBoost
  + sanctionsScore
  + radiationScore
```

Those additive terms are local fallback context, not the server Strategic Risk
contract. `sanctionsScore` is capped at 10 from new sanctions entries,
largest-country entry volume, and sanctioned vessel/aircraft counts;
`radiationScore` is capped at 12 from radiation-watch spikes, elevated readings,
and corroborated observations, reduced by low-confidence or conflicting
observations.

### Risk Levels

The browser panel and the server API intentionally expose two related but
different level schemes for the same 0-100 headline score.

**Panel-visible display labels** use the same five CII-aligned bands as country
risk rows:

| Headline score | Panel label | Meaning |
| - | - | - |
| >=81 | **Critical** | Active crisis or major escalation |
| 66-80 | **High** | Significant instability requiring close monitoring |
| 51-65 | **Elevated** | Above-normal activity patterns |
| 31-50 | **Normal** | Baseline geopolitical activity |
| \<31 | **Low** | Unusually quiet period |

**Server/on-wire enum labels** remain the three-tier `StrategicRisk.level`
contract returned by `GetRiskScores`:

| Headline score | `StrategicRisk.level` | Meaning |
| - | - | - |
| 70-100 | `SEVERITY_LEVEL_HIGH` | Multiple converging crises |
| 40-69 | `SEVERITY_LEVEL_MEDIUM` | Heightened global tension |
| 0-39 | `SEVERITY_LEVEL_LOW` | Lower global strategic pressure |

API clients should treat `StrategicRisk.level` as the server severity enum and
the Strategic Risk panel label as a display mapping of `StrategicRisk.score`.

### Unified Alert System

Alerts from all modules are merged using **temporal and spatial deduplication**:

* **Time window**: Alerts within 2 hours may be merged
* **Distance threshold**: Alerts within 200km may be merged
* **Same country**: Alerts affecting the same country may be merged

When alerts merge, they become **composite alerts** that show the full picture:

```
Type: Composite Alert
Title: Convergence + CII + Infrastructure: Ukraine
Components:
  - Geographic Convergence: 4 event types in Kyiv region
  - CII Spike: Ukraine +15 points (Critical)
  - Infrastructure: Black Sea cables at risk
Priority: Critical
```

### Alert Priority

Alert priority is separate from the server Strategic Risk High/Medium/Low
bands. References to Critical and High CII below use the Country Instability
Index country-score bands documented in
[Country Instability Index](/docs/country-instability-index#instability-levels).

| Priority | Criteria |
| - | - |
| **Critical** | CII country Critical band (81-100), convergence has 4+ types or score ≥90, cascade critical impact, high-impact radiation/sanctions signal |
| **High** | CII country High band (66-80) or ≥30-point CII move, convergence has 3+ types or score ≥70, cascade affecting ≥5 countries |
| **Medium** | CII country Elevated band or ≥15-point CII move, convergence score ≥50, cascade medium impact or ≥3 countries |
| **Low** | Minor changes and low-impact events |

### Trend Detection

Country-level CII rows track approximate 24-hour movement through
`dynamicScore`:

* `dynamicScore` is a signed delta from -100 to 100 against the nearest valid prior CII snapshot.
* Positive values mean the score rose, negative values mean it fell, and `0` means stable or no valid prior snapshot.
* Trend labels use the server deadband: greater than `+1` is rising, less than `-1` is falling, and values from `-1` through `+1` remain stable.

The server-published global `StrategicRisk` headline is different: it currently
sets `trend` to stable while publishing the top-5 CII roll-up score. If cached
server scores are unavailable, the browser fallback computes its own
escalating/stable/de-escalating panel trend from local additive context; that
fallback trend is a UI continuity signal, not the authoritative server
Strategic Risk trend contract.

## Pentagon Pizza Index (PizzINT)

The dashboard integrates real-time foot traffic data from strategic locations near government and military facilities. This "Pizza Index" concept, tracking late-night activity spikes at restaurants near the Pentagon, Langley, and other facilities, provides an unconventional indicator of crisis activity.

### How It Works

The system aggregates percentage-of-usual metrics from monitored locations:

1. **Locations**: Fast food, pizza shops, and convenience stores near Pentagon, CIA, NSA, State Dept, and other facilities
2. **Aggregation**: Activity percentages are averaged, capped at 100%
3. **Spike Detection**: Locations exceeding their baseline are flagged

### DEFCON-Style Alerting

Aggregate activity maps to a 5-level readiness scale:

| Level | Threshold | Label | Meaning |
| - | - | - | - |
| **DEFCON 1** | ≥85% | Maximum Activity | Maximum readiness; crisis response active |
| **DEFCON 2** | 70% – 84% | High Activity | High activity; significant event underway |
| **DEFCON 3** | 50% – 69% | Elevated Activity | Elevated; above-normal operations |
| **DEFCON 4** | 25% – 49% | Above Normal | Increased vigilance |
| **DEFCON 5** | \<25% | Normal Activity | Normal peacetime operations |

### World Monitor Tension Pairs

The indicator also displays World Monitor tension scores computed independently from GDELT event exports:

| Pair | Monitored Relationship |
| - | - |
| USA ↔ Russia | Primary nuclear peer adversary |
| Russia ↔ Ukraine | Active conflict zone |
| USA ↔ China | Economic and military competition |
| USA ↔ Iran | Middle East regional tensions |

Each pair shows:

* **Current tension score** (World Monitor's normalized score)
* **7-day trend** (rising, falling, stable)
* **Percentage change** from previous period

This provides context for the activity levels. See the country-pair tension section below for scoring and data-availability rules.

### Backed by endpoints

| Surface | Endpoint | Notes |
| - | - | - |
| Pentagon Pizza Index + GDELT pairs | `GET /api/intelligence/v1/get-pizzint-status` | Reads pizza activity and, with `include_gdelt=true`, independently computed World Monitor tension pairs. |
| GPS/GNSS interference | `GET /api/intelligence/v1/list-gps-interference` | Reads normalized GPSJam Redis keys, with optional `region` filtering. |
| OREF alerts for CII context | `GET /api/intelligence/v1/list-oref-alerts` | Direct RPC for live or history OREF data; see [Israel Sirens](/docs/panels/oref-sirens) for the panel relay path. |
| Security advisories | `GET /api/intelligence/v1/list-security-advisories` | Redis-backed advisory feed used by CII boosts and floors. |
| Country energy profile | `GET /api/intelligence/v1/get-country-energy-profile` | Country-level energy context for country deep dives and strategic risk analysis. |
| Energy shock scenario | `GET /api/intelligence/v1/compute-energy-shock` | On-demand country + chokepoint product supply shock. |

### World Monitor country-pair tensions

Tensions use GDELT event exports, not PizzINT's GPR scale. The materializer counts
both actor directions for US–Russia, Russia–Ukraine, US–China, and US–Iran.
China–Taiwan and US–Venezuela did not meet the initial 20 conflict events/day
measurement threshold and are excluded. No GDELT query API is used.

For each UTC day, conflict intensity is the sum of
`NumMentions × max(0, −GoldsteinScale)` for events in QuadClass 3 or 4.
The daily bucket also retains total events, conflict events, and tone sum/count
(mean tone is their quotient). Events are assigned to their export cohort's UTC
day; the current partial day is excluded from scoring.

The score is the percentile of the last seven completed days' mean intensity
against the pair's 90 completed daily intensities:
`100 × (days below the mean + 0.5 × days equal to it) / 90`.
A non-flat history's maximum scores 100; a flat history scores 50.
This measures relative event intensity, not the probability of conflict.
The last seven days must contain at least 140 conflict events (20 × 7).
All 90 days must have all 96 export cohorts. Missing or partial days do not
count as quiet days. During initial history collection, or when these conditions
fail, the stored `insufficientPairs` lists the pair; the API omits its numeric
score and the panel shows **Insufficient data**.

Change is `100 × (recent seven-day mean − preceding seven-day mean) / preceding mean`.
The trend rises above +10%, falls below −10%, and is stable at the boundaries.
A zero preceding mean with positive recent intensity has no finite percentage
change, so that pair is also withheld. Two zero means give a zero change.

The independent `gdelt:bulk:dyad-tension:v1` snapshot retains 90 completed days
plus today's bucket, with a 92-day TTL and its own replay cursor. Health checks
`seed-meta:gdelt:bulk:dyad-tension` at a 45-minute budget for the 15-minute worker.
The API withholds snapshots older than 45 minutes even though history remains
stored. Responses that include tensions bypass HTTP caches, and the browser does
not retain tension responses for cache fallback. Healthy ingestion does not mean
that the 90-day scoring history is ready.
The probe stays pending until the first successful data and metadata publication
sets its durable activation marker. It then enforces freshness permanently.
Pizza outages do not hide available tensions.

Each worker run also repairs history. It walks the past days in the window
that are missing or lack any of their 96 cohorts, newest first, and rebuilds
up to two of them from that day's GDELT export files. A rebuilt day replaces
the stored day. It is never added to it, so the partial first day of
collection counts once. Only days before the replay cursor's day are repaired,
so the live merge never adds to a rebuilt day. The worker downloads a day only
when the GDELT master file list names all 96 of its exports, and checks each
file against the listed size and MD5. A day with an upstream gap is skipped
without downloading it and does not count toward the two. A day whose download
or verification fails is recorded in the snapshot's `repairFailures` and
skipped for six hours, so it cannot hold the run's slots while older days wait.
A later successful rebuild clears its record. The repair has a 60-second budget
per run. When the budget runs out, the worker aborts in-flight downloads and
starts no new ones. A failure or an overrun keeps the days that finished and
never delays or fails the live publication. An empty window fills in about 45
runs, about 11 hours.

## Related Assets

News clusters are automatically enriched with nearby critical infrastructure. When a story mentions a geographic region, the system identifies relevant assets within 600km, providing immediate operational context.

### Asset Types

| Type | Source | Examples |
| - | - | - |
| **Pipelines** | 88 global routes | Nord Stream, Keystone, Trans-Siberian |
| **Undersea Cables** | 86 major cables | TAT-14, SEA-ME-WE, Pacific Crossing |
| **AI Datacenters** | Epoch AI registry | Azure East US, GCP Council Bluffs |
| **Military Bases** | 226 installations | Ramstein, Diego Garcia, Guam |
| **Nuclear Facilities** | 100+ sites | Power plants, weapons labs, enrichment |

### Location Inference

The system infers the geographic focus of news stories through:

1. **Keyword matching**: Headlines are scanned against hotspot keyword lists (e.g., "Taiwan" maps to Taiwan Strait hotspot)
2. **Confidence scoring**: Multiple keyword matches increase location confidence
3. **Fallback to conflicts**: If no hotspot matches, active conflict zones are checked

### Distance Calculation

Assets are ranked by Haversine distance from the inferred location:

```
d = 2r × arcsin(√(sin²(Δφ/2) + cos(φ₁) × cos(φ₂) × sin²(Δλ/2)))
```

Up to 3 assets per type are displayed, sorted by proximity.

### Example Context

A news cluster about "pipeline explosion in Germany" would show:

* **Pipelines**: Nord Stream (23km), Yamal-Europe (156km)
* **Cables**: TAT-14 landing (89km)
* **Bases**: Ramstein (234km)

Clicking an asset zooms the map to its location and displays detailed information.

## Server-Side Risk Score API

Strategic risk and Country Instability Index (CII) scores are pre-computed server-side rather than calculated in the browser. This eliminates the "cold start" problem where new users would see no data while the system accumulated enough information to generate scores.

### How It Works

The `GetRiskScores` RPC handler (`get-risk-scores.ts`):

1. Fetches recent protest/riot/battle/explosion/civilian-violence data from ACLED.
2. Fetches auxiliary sources from Redis, including UCDP conflicts, outages, climate, cyber threats, fires, GPS jamming, Iran events, OREF alerts, advisories, displacement, classified news summaries, aviation alerts, earthquakes, sanctions, and military/AIS CII aggregates.
3. Computes CII v8 scores for 31 Tier-1 countries using the shared coefficient table documented in [CII Risk Scoring Methodology](/docs/methodology/cii-risk-scores).
4. Derives Strategic Risk from the weighted top 5 CII scores.
5. Caches results in Redis with versioned live and stale keys tied to the current CII formula version.

### CII Score Calculation

Each country's score combines a static baseline (40%) with a dynamic event score (60%), plus supplemental boosts and floors.

**Baseline Risk** (0-50 points): Static geopolitical risk reflecting structural fragility. The canonical per-country values live in [CII Risk Scoring Methodology](/docs/methodology/cii-risk-scores#2-per-country-baselinerisk-and-eventmultiplier).

**Event Score** blends four sub-components:

| Sub-component | Weight | Scoring |
| - | - | - |
| Unrest | 25% | ACLED protests/riots, protest fatalities, high-severity unrest, and outage severity |
| Conflict | 30% | Weighted ACLED battles/explosions/civilian violence, fatalities, Iran strike severity, and OREF alert pressure |
| Security | 20% | Military flights, military vessels, aviation closures/delays, and GPS/GNSS jamming |
| Information | 25% | Classified news headlines and country-attributed threat summaries |

**Floors** (minimum score guarantees):

| Floor type | Threshold | Trigger |
| - | - | - |
| UCDP active war | >= 70 | 2-year Tier-1 UCDP total deaths > 1000 or event count > 100 |
| UCDP minor conflict | >= 50 | 2-year Tier-1 UCDP event count > 10 below the war thresholds |
| Advisory do-not-travel | >= 60 | Live advisory feed when present; otherwise embedded State Department fallback table |
| Advisory reconsider | >= 50 | Live advisory feed when present; otherwise embedded State Department fallback table |

**Supplemental Boosts**: Advisory boost (+15/+10/+5), OREF blend boost for IL (+15 active + history tiers), climate (+15 max), cyber (+12 max), fires (+8 max), displacement (+20 max), news urgency (+5 max), earthquakes (+25 max), sanctions (+14 max), and AIS disruptions (+10 max).

Advisory outputs expose both `advisoryLevel` and `advisoryProvenance` on each
`CiiScore`. `advisoryProvenance` is `live` when the score used the seeded
advisory feed, `fallback` when it used the embedded State Department fallback
table, and `absent` when no advisory level affected boosts or floors.

### Event Significance Multipliers

Events in some countries carry more global significance than others:

| Multiplier | Countries | Rationale |
| - | - | - |
| 3.0x | North Korea | Any visible unrest is highly unusual |
| 2.0-2.5x | China, Russia, Iran, Saudi Arabia, Cuba | Authoritarian states suppress protests |
| 1.5-1.8x | Taiwan, Pakistan, Myanmar, Venezuela, UAE | Regional flashpoints |
| 1.0-1.2x | Mexico, Turkey | Moderate significance |
| 0.5-0.8x | UK, France, Germany, Poland, Ukraine, Syria, Yemen, Israel, India, Brazil | Protests are routine or events already captured by floors |
| 0.3x | US | Very high observability and high-volume domestic reporting require stronger dampening |

### Strategic Risk Derivation

The composite strategic risk score is computed as a weighted average of the top 5 CII scores:

```
Weights: [1.0, 0.85, 0.70, 0.55, 0.40] (total: 3.5)
Strategic Risk = (Σ CII[i] × weight[i]) / 3.5 × 0.7 + 15
```

The top countries contribute most heavily, with diminishing influence for lower-ranked countries.

### Data Sources

| Source | Redis Key | Used For |
| - | - | - |
| ACLED | Fetched live via API | Protests, riots, battles, explosions, civilian violence, fatalities |
| UCDP | `conflict:ucdp-events:v1` | War/minor conflict floors |
| Outages | `infra:outages:v1` | Unrest outage boost |
| Climate | `climate:anomalies:v2` | Climate severity boost |
| Cyber | `cyber:threats-bootstrap:v2` | Severity-weighted cyber boost |
| Fires | `wildfire:fires:v1` | Severity-weighted wildfire boost |
| GPS Jamming | `intelligence:gpsjam:v2` | Security score |
| Military CII | Relay/seeded military aggregate | Security score and AIS disruption boost |
| Aviation Alerts | Seeded aviation disruption data | Security score |
| Iran Events | `conflict:iran-events:v1` | Strike boost with severity weighting |
| OREF Alerts | `relay:oref:history:v1` | IL conflict boost and blend boost |
| News Threats | Classified headline summaries | Information score and news urgency boost |
| Displacement | Humanitarian displacement aggregate | Displacement boost |
| Earthquakes | Earthquake feed | Earthquake boost |
| Sanctions | Sanctions country aggregate | Sanctions boost |
| Security advisories | `intelligence:advisories:v1` plus embedded fallback table | Advisory boost/floor with `advisoryProvenance` |

### Fallback Behavior

When upstream data is unavailable (API errors, rate limits):

1. **Stale cache**: Return the latest versioned stale risk-score payload when available.
2. **Baseline fallback**: Return scores using static baselines and available floors when no usable live or stale payload exists.

The RPC exposes the fallback state on `GetRiskScoresResponse`: stale-cache
responses set `degraded=true` and `stale=true`; cold baseline-only responses set
`degraded=true` and `stale=false`. Fresh/cache-hit responses set both flags to
`false`.

The relay CII warm-ping loop is active in `scripts/ais-relay.cjs`: it calls
`/api/intelligence/v1/get-risk-scores` every 8 minutes, lets the RPC handler
refresh live/stale cache state, and writes `seed-meta:intelligence:risk-scores`
for health monitoring when scores are returned. That seed-meta `recordCount`
is real-time signal-density coverage, not raw feed availability: it counts the
score-relevant Tier-1 conflict (ACLED or UCDP), news, and cyber signal families
present in the CII refresh. Quiet-but-fresh real-time feeds can therefore produce
`COVERAGE_PARTIAL`; source-specific freshness remains the feed-heartbeat view.
