docs(mintlify): add Route Explorer + Scenario Engine workflow pages (#3211)

* docs(mintlify): add Route Explorer + Scenario Engine workflow pages

Checkpoint for review on the IA refresh (per plan
docs/plans/2026-04-19-001-feat-docs-user-facing-ia-refresh-plan.md).

- docs/docs.json: link Country Resilience Index methodology under
  Intelligence & Analysis so the flagship 222-country feature is
  reachable from the main nav (previously orphaned). Add a new
  Workflows group containing route-explorer and scenario-engine.
- docs/route-explorer.mdx: standalone workflow page. Who it is for,
  Cmd+K entry, four tabs (Current / Alternatives / Land / Impact),
  inputs, keyboard bindings, map-state integration, PRO gating
  with free-tier blur + public-route highlight, data sources.
- docs/scenario-engine.mdx: standalone workflow page. Template
  categories (conflict / weather / sanctions / tariff_shock /
  infrastructure / pandemic), how a scenario activates on the map,
  PRO gating, pointers to the async job API.

Deferred to follow-up commits in the same PR:
  - documentation.mdx landing rewrite
  - features.mdx refresh
  - maritime-intelligence.mdx link-out to Route Explorer
  - Panels nav group (waits for PR 2 content)

All content grounded in live source files cited inline.

* docs(mintlify): fix Route Explorer + Scenario Engine review findings

Reviewer caught 4 cases where I described behavior I hadn't read
carefully. All fixes cross-checked against source.

- route-explorer (free-tier): the workflow does NOT blur a numeric
  payload behind a public demo route. On free tier, fetchLane()
  short-circuits to renderFreeGate() which blurs the left rail,
  replaces the tab area with an Upgrade-to-PRO card, and applies a
  generic public-route highlight on the map. No lane data is
  rendered in any tab. See src/components/RouteExplorer/
  RouteExplorer.ts:212 + :342.
- route-explorer (keyboard): Tab / Shift+Tab moves focus between the
  panel and the map. Direct field jumps are F (From), T (To), P
  (Product/HS2), not Tab-cycling. Also added the full KeyboardHelp
  binding list (S swap, ↑/↓ list nav, Enter commit, Cmd+, copy URL,
  Esc close, ? help, 1-4 tabs). See src/components/RouteExplorer/
  KeyboardHelp.ts:9 and RouteExplorer.ts:623.
- scenario-engine: the SCENARIO_TEMPLATES array only ships templates
  of 4 types today (conflict, weather, sanctions, tariff_shock).
  The ScenarioType union includes infrastructure and pandemic but
  no templates of those types ship. Dropped them from the shipped
  table and noted the type union leaves room for future additions.
- scenario-engine + api-scenarios: the worker writes
  status: 'done' (not 'completed') on success, 'failed' on error;
  pending is synthesised by the status endpoint when no worker
  record exists. Fixed both the new workflow page and the merged
  api-scenarios.mdx completed-response example + polling language.
  See scripts/scenario-worker.mjs:421 and
  src/components/SupplyChainPanel.ts:870.

* docs(mintlify): fix third-round review findings (real IDs + 4-state lifecycle)

- api-scenarios (template example): replaced invented
  hormuz-closure-30d / ["hormuz"] with the actually-shipped
  hormuz-tanker-blockade / ["hormuz_strait"] from scenario-
  templates.ts:80. Listed the other 5 shipped template IDs so
  scripted users aren't dependent on a single example.
- api-scenarios (status lifecycle): worker writes FOUR states,
  not three. Added the intermediate "processing" state with
  startedAt, written by the worker at job pickup (scenario-
  worker.mjs:411). Lifecycle now: pending → processing →
  done|failed. Both pending and processing are non-terminal.
- scenario-engine (scripted use blurb): mirror the 4-state
  language and link into the lifecycle table.
- scenario-engine (UI dismiss): replaced "Click Deactivate"
  with the actual × dismiss control on the scenario banner
  (aria-label: "Dismiss scenario") per
  src/components/SupplyChainPanel.ts:790. Also described the
  banner contents (name, chokepoints, countries, tagline).
- api-shipping-v2: while fixing chokepoint IDs, also corrected
  "hormuz" → "hormuz_strait" and "bab-el-mandeb" → "bab_el_mandeb"
  across all four occurrences in the shipping v2 page (from
  PR #3209). Real IDs come from server/_shared/chokepoint-
  registry.ts (snake_case, not kebab-case, not bare "hormuz").

* docs(mintlify): fix fourth-round findings (banner DOM, webhook TTL refresh)

- scenario-engine: accurate description of the rendered scenario
  banner. Always-present elements are the ⚠ icon, scenario name,
  top-5 impacted countries with impact %, and dismiss ×. Params
  chip (e.g. '14d · +110% cost') and 'Simulating …' tagline are
  conditional on the worker result carrying template parameters
  (durationDays, disruptionPct, costShockMultiplier). The banner
  never lists affected chokepoints by name — the map and the
  chokepoint cards surface those. Per renderScenarioBanner at
  src/components/SupplyChainPanel.ts:750.
- api-shipping-v2 (webhook TTL): register extends both the record
  and the owner-index set's 30-day TTL via atomic pipeline
  (SET + SADD + EXPIRE). rotate-secret and reactivate only
  extend the record's TTL — neither touches the owner-index set,
  so the owner index can expire independently if a caller only
  rotates/reactivates within a 30-day window. Re-register to keep
  both alive. Per api/v2/shipping/webhooks.ts:230 (register
  pipeline) and :325 (rotate setCachedJson on record only).

* docs(mintlify): fix PRO auth contract (trusted origin ≠ PRO)

- api-scenarios: 'X-WorldMonitor-Key (or trusted browser origin)
  + PRO' was wrong — isCallerPremium() explicitly skips
  trusted-origin short-circuits (keyCheck.required === false) and
  only counts (a) an env-valid or user-owned wm_-prefixed API key
  with apiAccess entitlement, or (b) a Clerk bearer with role=pro
  or Dodo tier ≥ 1. Browser calls work because premiumFetch()
  injects one of those credentials per request, not because Origin
  alone authenticates. Per server/_shared/premium-check.ts:34 and
  src/services/premium-fetch.ts:66.
- usage-auth: strengthened the 'Entitlement / tier gating' section
  to state outright that authentication and PRO entitlement are
  orthogonal, and that trusted Origin is NOT accepted as PRO even
  though it is accepted for public endpoints. Listed the two real
  credential forms that pass the gate.

* docs(mintlify): fix stale line cite (MapContainer.activateScenario at :1010)

Greptile review P2: prose cited MapContainer.ts:1004 but activateScenario
is declared at :1010. Line 1004 landed inside the JSDoc block.

* docs(mintlify): finish PR 1 — landing rewrite, features refresh, maritime link-out

Completes the PR 1 items from docs/plans/2026-04-19-001-feat-docs-user-
facing-ia-refresh-plan.md that were deferred after the checkpoint on
Route Explorer + Scenario Engine + CRI nav. No new pages — only edits
to existing pages to point at and cohere with the new workflow pages.

- documentation.mdx: landing rewrite. Dropped brittle counts (344
  news sources, 49 layers, 24 CII countries, 31+ sources, 24 typed
  services) in favor of durable product framing. Surfaced the
  shipped differentiators that were invisible on the landing
  previously: Country Resilience Index (222 countries, linked to
  its methodology page), AI daily brief, Route Explorer,
  Scenario Engine, MCP server. Kept CII and CRI as two distinct
  country-risk surfaces — do not conflate.
- features.mdx: replaced the 'all 55 panels' Cmd+K claim and the
  stale inventory list with family-grouped descriptions that
  include the panels this audit surfaced as missing (disease-
  outbreaks, radiation-watch, thermal-escalation, consumer-prices,
  latest-brief, forecast, country-resilience). Added a Workflows
  section linking to Route Explorer and Scenario Engine, and a
  Country-level risk section linking CII + CRI. Untouched
  sections (map, marker clustering, data layers, export, monitors,
  activity tracking) left as-is.
- maritime-intelligence.mdx: collapsed the embedded Route Explorer
  subsection to a one-paragraph pointer at /route-explorer so the
  standalone page is the canonical home.

Panels nav group remains intentionally unadded; it waits on PR 2
content to avoid rendering an empty group in Mintlify.
This commit is contained in:
Elie Habib
2026-04-19 18:39:36 +04:00
committed by GitHub
parent 1f66b0c486
commit d1a4cf7780
9 changed files with 280 additions and 59 deletions

View File

@@ -15,30 +15,32 @@ This service is documented inline (not yet proto-backed). Proto migration is tra
Returns the catalog of pre-defined scenario templates. Cached `public, max-age=3600`. Returns the catalog of pre-defined scenario templates. Cached `public, max-age=3600`.
**Response**: **Response** — abbreviated example using one of the live shipped templates (`server/worldmonitor/supply-chain/v1/scenario-templates.ts`):
```json ```json
{ {
"templates": [ "templates": [
{ {
"id": "hormuz-closure-30d", "id": "hormuz-tanker-blockade",
"name": "Strait of Hormuz closure (30 days)", "name": "Hormuz Strait Tanker Blockade",
"affectedChokepointIds": ["hormuz"], "affectedChokepointIds": ["hormuz_strait"],
"disruptionPct": 100, "disruptionPct": 100,
"durationDays": 30, "durationDays": 14,
"affectedHs2": ["27"], "affectedHs2": ["27", "29"],
"costShockMultiplier": 1.8 "costShockMultiplier": 2.10
} }
] ]
} }
``` ```
Other shipped templates at the time of writing: `taiwan-strait-full-closure`, `suez-bab-simultaneous`, `panama-drought-50pct`, `russia-baltic-grain-suspension`, `us-tariff-escalation-electronics`. Use the live `/templates` response as the source of truth — the set grows over time.
## Run a scenario ## Run a scenario
### `POST /api/scenario/v1/run` ### `POST /api/scenario/v1/run`
Enqueues a job. Returns `202 Accepted` with a `jobId` the caller must poll. Enqueues a job. Returns `202 Accepted` with a `jobId` the caller must poll.
- **Auth**: `X-WorldMonitor-Key` (or trusted browser origin) + PRO - **Auth**: PRO entitlement required. Granted by either (a) a valid `X-WorldMonitor-Key` (env key from `WORLDMONITOR_VALID_KEYS`, or a user-owned `wm_`-prefixed key whose owner has the `apiAccess` entitlement), **or** (b) a Clerk bearer token whose user has role `pro` or Dodo entitlement tier ≥ 1. A trusted browser Origin alone is **not** sufficient — `isCallerPremium()` in `server/_shared/premium-check.ts` only counts explicit credentials. Browser calls work because `premiumFetch()` (`src/services/premium-fetch.ts`) injects one of the two credential forms on the caller's behalf.
- **Rate limits**: - **Rate limits**:
- 10 jobs / minute / user - 10 jobs / minute / user
- Global queue capped at 100 in-flight jobs; excess rejected with `429` + `Retry-After: 30` - Global queue capped at 100 in-flight jobs; excess rejected with `429` + `Retry-After: 30`
@@ -46,7 +48,7 @@ Enqueues a job. Returns `202 Accepted` with a `jobId` the caller must poll.
**Request**: **Request**:
```json ```json
{ {
"scenarioId": "hormuz-closure-30d", "scenarioId": "hormuz-tanker-blockade",
"iso2": "SG" "iso2": "SG"
} }
``` ```
@@ -82,11 +84,20 @@ Enqueues a job. Returns `202 Accepted` with a `jobId` the caller must poll.
### `GET /api/scenario/v1/status?jobId=<jobId>` ### `GET /api/scenario/v1/status?jobId=<jobId>`
Returns the job result or `{ status: "pending" }` while the worker is still running. Returns the job's current state as written by the worker, or a synthesised `pending` stub while the job is still queued.
- **Auth**: same as `/run` - **Auth**: same as `/run`
- **jobId format**: `scenario:{unix-ms}:{8-char-suffix}` — strictly validated to guard against path traversal - **jobId format**: `scenario:{unix-ms}:{8-char-suffix}` — strictly validated to guard against path traversal
**Status lifecycle**:
| `status` | When | Additional fields |
|---|---|---|
| `pending` | Job enqueued but worker has not picked it up yet. Synthesised by the status handler when no Redis record exists. | — |
| `processing` | Worker dequeued the job and started computing. Written by the worker at job pickup. | `startedAt` (ms epoch) |
| `done` | Worker completed successfully. | `completedAt`, `result` (scenario-specific payload) |
| `failed` | Worker hit a computation error. | `failedAt`, `error` (string) |
**Pending response (`200`)**: **Pending response (`200`)**:
```json ```json
{ {
@@ -95,13 +106,19 @@ Returns the job result or `{ status: "pending" }` while the worker is still runn
} }
``` ```
**Completed response (`200`)** — shape is scenario-specific; always includes: **Processing response (`200`)**:
```json ```json
{ {
"jobId": "scenario:1713456789012:a1b2c3d4", "status": "processing",
"scenarioId": "hormuz-closure-30d", "startedAt": 1713456789500
"iso2": "SG", }
"status": "completed", ```
**Done response (`200`)** — the worker writes the result directly to Redis; the status endpoint returns it verbatim:
```json
{
"status": "done",
"completedAt": 1713456890123, "completedAt": 1713456890123,
"result": { "result": {
"costShockPct": 14.2, "costShockPct": 14.2,
@@ -111,6 +128,18 @@ Returns the job result or `{ status: "pending" }` while the worker is still runn
} }
``` ```
**Failed response (`200`)**:
```json
{
"status": "failed",
"error": "computation_error",
"failedAt": 1713456890123
}
```
Poll loop: treat `pending` and `processing` as non-terminal; only `done` and `failed` are terminal. Both pending and processing can legitimately persist for several seconds under load.
**Errors**: **Errors**:
| Status | `error` | Cause | | Status | `error` | Cause |

View File

@@ -38,7 +38,7 @@ GET /api/v2/shipping/route-intelligence?fromIso2=AE&toIso2=NL&cargoType=tanker&h
"hs2": "27", "hs2": "27",
"primaryRouteId": "ae-to-eu-via-hormuz-suez", "primaryRouteId": "ae-to-eu-via-hormuz-suez",
"chokepointExposures": [ "chokepointExposures": [
{ "chokepointId": "hormuz", "chokepointName": "Strait of Hormuz", "exposurePct": 100 }, { "chokepointId": "hormuz_strait", "chokepointName": "Strait of Hormuz", "exposurePct": 100 },
{ "chokepointId": "suez", "chokepointName": "Suez Canal", "exposurePct": 100 } { "chokepointId": "suez", "chokepointName": "Suez Canal", "exposurePct": 100 }
], ],
"bypassOptions": [ "bypassOptions": [
@@ -82,7 +82,7 @@ Registers a webhook for chokepoint disruption alerts. Returns `201 Created`.
```json ```json
{ {
"callbackUrl": "https://hooks.example.com/shipping-alerts", "callbackUrl": "https://hooks.example.com/shipping-alerts",
"chokepointIds": ["hormuz", "suez", "bab-el-mandeb"], "chokepointIds": ["hormuz_strait", "suez", "bab_el_mandeb"],
"alertThreshold": 60 "alertThreshold": 60
} }
``` ```
@@ -101,7 +101,7 @@ Registers a webhook for chokepoint disruption alerts. Returns `201 Created`.
- `subscriberId` — `wh_` prefix + 24 hex chars (12 random bytes). - `subscriberId` — `wh_` prefix + 24 hex chars (12 random bytes).
- `secret` — raw 64-char lowercase hex (32 random bytes). There is no `whsec_` prefix. Persist it — the server never returns it again except on rotation. - `secret` — raw 64-char lowercase hex (32 random bytes). There is no `whsec_` prefix. Persist it — the server never returns it again except on rotation.
- **TTL**: 30 days on the record and the owner-index set (re-register or rotate to extend). - **TTL**: 30 days on both the subscriber record and the per-owner index set. Only **re-registration** refreshes both, via an atomic pipeline (`SET` record with `EX`, `SADD` + `EXPIRE` on the owner index). `rotate-secret` and `reactivate` refresh the record's TTL only — they do not touch the owner-index set's expiry, so the owner index can expire independently if a caller only ever rotates or reactivates within a 30-day window. Re-register to keep both alive.
- Ownership is tracked via SHA-256 of the caller's API key (never secret — stored as `ownerTag`). - Ownership is tracked via SHA-256 of the caller's API key (never secret — stored as `ownerTag`).
Auth: `X-WorldMonitor-Key` (forceKey: true) + PRO. Returns `401` / `403` otherwise. Auth: `X-WorldMonitor-Key` (forceKey: true) + PRO. Returns `401` / `403` otherwise.
@@ -116,7 +116,7 @@ Lists the caller's registered webhooks (filtered by the SHA-256 owner tag of the
{ {
"subscriberId": "wh_...", "subscriberId": "wh_...",
"callbackUrl": "https://hooks.example.com/...", "callbackUrl": "https://hooks.example.com/...",
"chokepointIds": ["hormuz", "suez"], "chokepointIds": ["hormuz_strait", "suez"],
"alertThreshold": 60, "alertThreshold": 60,
"createdAt": "2026-04-19T12:00:00Z", "createdAt": "2026-04-19T12:00:00Z",
"active": true "active": true
@@ -158,7 +158,7 @@ X-WM-Event: chokepoint.disruption
{ {
"subscriberId": "wh_...", "subscriberId": "wh_...",
"chokepointId": "hormuz", "chokepointId": "hormuz_strait",
"score": 74, "score": 74,
"alertThreshold": 60, "alertThreshold": 60,
"triggeredAt": "2026-04-19T12:03:00Z", "triggeredAt": "2026-04-19T12:03:00Z",

View File

@@ -86,11 +86,19 @@
"signal-intelligence", "signal-intelligence",
"ai-intelligence", "ai-intelligence",
"country-instability-index", "country-instability-index",
"methodology/country-resilience-index",
"geographic-convergence", "geographic-convergence",
"strategic-risk", "strategic-risk",
"algorithms" "algorithms"
] ]
}, },
{
"group": "Workflows",
"pages": [
"route-explorer",
"scenario-engine"
]
},
{ {
"group": "Map Layers", "group": "Map Layers",
"pages": [ "pages": [

View File

@@ -1,28 +1,32 @@
--- ---
title: "Introduction" title: "Introduction"
description: "World Monitor is an open-source, real-time global intelligence dashboard that aggregates news, markets, military activity, infrastructure data, and AI-powered analysis into a single map interface." description: "World Monitor is an open-source, real-time global intelligence dashboard that aggregates news, markets, military and maritime activity, infrastructure data, and AI-powered analysis into a single map-first interface."
--- ---
World Monitor brings together 344 curated news sources, 49 interactive map layers, and AI-powered analysis into a situational awareness platform. It runs five specialized variants from a single codebase, each tailored to a different domain: geopolitics, technology, finance, commodities, and positive global trends. World Monitor is a real-time situational-awareness platform for people who need to understand what is happening in the world beyond traditional news headlines. The core surface is a 3D globe (or flat map) with toggleable data layers for conflict, military, maritime, aviation, infrastructure, and natural events; a panel system for synthesised intelligence and markets; a daily AI brief; and two flagship workflows (Route Explorer and Scenario Engine) for reasoning about supply-chain disruption. It ships as five specialised variants from one codebase geopolitics, technology, finance, commodities, and positive global trends — each pre-configured for its audience.
The platform is designed for journalists, security analysts, researchers, and anyone who needs to understand what is happening in the world beyond traditional news headlines. The platform is designed for journalists, security analysts, researchers, PRO subscribers running operational workflows, and anyone who needs a faster read on a complex global situation than open-web searching allows.
## What you can do ## What you can do
- **Monitor global events in real time** on a 3D globe or flat map with 49 toggleable data layers covering military flights, naval vessels, satellites, earthquakes, wildfires, cyber threats, and more - **Monitor global events in real time** on a 3D globe or flat map with toggleable data layers for military flights, naval vessels, satellites, earthquakes, wildfires, cyber threats, disease outbreaks, radiation, and more
- **Read AI-generated intelligence briefs** that synthesize hundreds of headlines into actionable summaries, with source attribution and confidence scoring - **Read an AI-generated daily intelligence brief** that synthesises the day's headlines into a structured, source-attributed summary — viewable in the dashboard, as a public share link, or as a social-ready image carousel
- **Track country stability** through the Country Instability Index (CII), which scores 24 countries in real time using conflict data, social unrest indicators, and news velocity - **Screen country-level risk two ways**: the [Country Instability Index](/country-instability-index) for a high-frequency stress signal on a curated country set, and the [Country Resilience Index](/methodology/country-resilience-index) for a 222-country resilience score across 5 domains and 13 dimensions, refreshed every 6 hours
- **Analyze financial signals** including market data, prediction markets, central bank rates, commodity prices, and Gulf economy indicators - **Plan shipments with [Route Explorer](/route-explorer)** — a full-screen keyboard-first workflow that resolves chokepoint exposures, alternative corridors, land routes, and country-level impact for any origin-destination-commodity combination
- **Run disruption scenarios with the [Scenario Engine](/scenario-engine)** — pre-built conflict, weather, sanctions, and tariff-shock scenarios that paint impact across chokepoints, sectors, and countries directly on the map
- **Analyse financial signals** including market quotes, commodity prices, prediction markets, central-bank rates, EU macro indicators, and Gulf-economy dashboards
- **Connect your own agent** via the [MCP server](/mcp) — Claude, Cursor, and other MCP clients can call curated WorldMonitor tools over OAuth 2.1 or a direct API key
- **Run entirely in your browser** with optional offline AI capabilities via ONNX Runtime Web, keeping your data on your device - **Run entirely in your browser** with optional offline AI capabilities via ONNX Runtime Web, keeping your data on your device
## Quick links ## Quick links
- **New here?** Start with [Getting Started](/getting-started) for installation and setup - **New here?** Start with [Getting Started](/getting-started) for installation and setup
- **Want the quick tour?** [Features & Interface](/features) walks through the map, layers, panels, and Cmd+K
- **Want to understand the system?** Read [Architecture](/architecture) for how the pieces fit together - **Want to understand the system?** Read [Architecture](/architecture) for how the pieces fit together
- **Looking for specific features?** See [Features & Interface](/features) for the full capability list - **Building an integration?** Start with the [Quickstart](/usage-quickstart) and browse the [API Reference](/api/ConflictService.openapi.yaml)
- **Interested in the data?** Check [Data Sources](/data-sources) for all 31+ sources and collection methods - **Connecting an AI agent?** Set up the [MCP server](/mcp)
- **Interested in the data sources?** See [Data Sources](/data-sources)
- **Want to contribute?** Read [Contributing](/contributing) for code style and PR process - **Want to contribute?** Read [Contributing](/contributing) for code style and PR process
- **Building on the API?** Browse the [API Reference](/api/) for all 24 typed services
## License ## License

View File

@@ -234,24 +234,35 @@ This prevents background tabs from consuming bandwidth while preserving user pre
- Polymarket integration for event probability tracking - Polymarket integration for event probability tracking
- Correlation analysis with news events - Correlation analysis with news events
## Workflows
Two full-screen, keyboard-first workflows complement the map + panels experience:
- **[Route Explorer](/route-explorer)** — plan a shipment between any two countries. Four tabs (Current / Alternatives / Land / Impact) resolve chokepoint exposure, bypass corridors, land alternatives, and per-country import exposure for the selected HS2 commodity. PRO-gated.
- **[Scenario Engine](/scenario-engine)** — run pre-built disruption scenarios (conflict, weather, sanctions, tariff shocks) and watch the impact resolve across chokepoints, sectors, and countries on the map and in the Supply Chain panel. PRO-gated.
## Country-level risk
Two complementary country-scoring surfaces:
- **[Country Instability Index](/country-instability-index)** — a high-frequency stress score that blends conflict, unrest, news velocity, and security signals for a curated country set.
- **[Country Resilience Index](/methodology/country-resilience-index)** — a 222-country, 0-100 resilience score across 5 domains and 13 dimensions, refreshed every 6 hours and sourced from official/authoritative providers with transparent imputation.
CII and CRI answer different questions and are not interchangeable: CII is short-horizon stress; CRI is structural resilience plus live shock exposure.
## Search (Cmd+K) ## Search (Cmd+K)
Universal command palette for navigating the entire application. All 55 panels, map views, layer toggles, and country briefs are searchable: Universal command palette for navigating the entire application. Every panel, map view, layer toggle, workflow, and country brief is searchable:
- **Map navigation**: Jump to any region (Global, MENA, Europe, Asia-Pacific, Americas, Africa, Oceania) - **Map navigation**: jump to any region (Global, MENA, Europe, Asia-Pacific, Americas, Africa, Oceania)
- **Layer presets**: Military, Finance, Infrastructure, Intel, All, None, Minimal - **Layer presets and individual layers**: flip presets (Military, Finance, Infrastructure, Intel, All, None, Minimal) or toggle individual layers by name (AIS, flights, conflicts, cables, fires, GPS jamming, satellites, etc.)
- **Individual layers**: 30+ toggleable layers (AIS, flights, conflicts, cables, fires, GPS jamming, satellites, etc.) - **Panels by family**: Intelligence and correlation panels (AI Insights, Deduction, Strategic Posture, Live Intelligence, Intel Feed, Escalation / Economic / Disaster correlation), regional and world news feeds, markets (quotes, commodities, crypto, sector heatmap, ETF/flow trackers, prediction markets, Gulf economies), analysis (Country Instability, Country Resilience, Strategic Risk, Infrastructure Cascade, Trade Policy, Supply Chain, Economic Indicators), tracking (fires, UCDP events, displacement, climate anomalies, security advisories, population exposure, disease outbreaks, radiation watch, thermal escalation), consumer prices, and utilities (Latest Brief, AI Forecasts, Webcams, World Clock, Airline Intel, Telegram Intel, OREF Sirens, Layoffs, My Monitors)
- **All panels**: Every panel is searchable by name and keywords, including: - **Workflows**: open Route Explorer or activate a Scenario Engine template directly from the palette
- Intelligence: AI Insights, AI Forecasts, Strategic Posture, Live Intelligence, Intel Feed, Deduction - **Country briefs**: search any country name to open its intelligence brief or navigate the map
- Correlation: Force Posture, Escalation Monitor, Economic Warfare, Disaster Cascade - **Time range**: filter events by 1h, 6h, 24h, 48h, or 7 days
- News: Live News, World News, regional feeds (US, Europe, Middle East, Africa, Latin America, Asia-Pacific) - **View controls**: dark/light mode, fullscreen, settings, refresh all data
- Markets: Markets, Commodities, Crypto, Sector Heatmap, BTC ETF Tracker, Stablecoins, Market Radar, Gulf Economies
- Analysis: Country Instability, Strategic Risk, Infrastructure Cascade, Trade Policy, Supply Chain, Economic Indicators The full panel inventory lives in the app itself — Cmd+K surfaces what's available in your current variant rather than a fixed list.
- Tracking: Fires, UCDP Events, Displacement, Climate Anomalies, Security Advisories, Population Exposure
- Other: Webcams, World Clock, Tech Readiness, Airline Intel, Telegram Intel, Israel Sirens, Layoffs, My Monitors
- **Country briefs**: Search any country name to open its intelligence brief or navigate the map
- **Time range**: Filter events by 1h, 6h, 24h, 48h, or 7 days
- **View controls**: Dark/light mode, fullscreen, settings, refresh all data
## Data Export ## Data Export

View File

@@ -6,19 +6,7 @@ The Ships layer provides real-time vessel tracking and maritime domain awareness
## Route Explorer ## Route Explorer
The Route Explorer lets users plan shipments between any two countries by querying real-time maritime route intelligence. Open it via **CMD+K** and search for "route". Route Explorer is the keyboard-first workflow for planning a shipment between any two countries and seeing chokepoint exposure, bypass corridors, land alternatives, and per-country impact — all on top of this maritime layer. See the dedicated [Route Explorer](/route-explorer) page for the full workflow, tab contents, keyboard bindings, and tier gating.
**Features:**
- **Country pair + HS2 product picker** with typeahead search across 197 port-clustered countries and 50 HS2 chapters
- **Current route tab** showing the primary maritime lane, chokepoint exposures ranked by severity, estimated transit days and freight ranges
- **Alternatives tab** with ranked bypass sea routes including cost deltas, transit deltas, and war risk tiers
- **Land tab** showing overland corridor options where available, with honest empty states for unmodeled regions
- **Impact tab** showing strategic-product trade data, lane value at risk, dependency flags, and resilience scores
- **Map integration** highlighting the selected route with bypass arc overlays
- **Keyboard-first design** with digit-based tab switching (1-4), picker shortcuts (F/T/P/S), and a cheat sheet (?)
- **URL state sharing** via `?explorer=from:CN,to:DE,hs:85`
PRO users get full route intelligence. Free users see a public route highlight on the map with details blurred behind an upgrade prompt.
## Chokepoint Monitoring ## Chokepoint Monitoring

99
docs/route-explorer.mdx Normal file
View File

@@ -0,0 +1,99 @@
---
title: "Route Explorer"
description: "Plan a shipment between any two countries and see the chokepoints, bypass options, and country-level impact on a full-screen, keyboard-first map workflow."
---
Route Explorer is a full-screen workflow for reasoning about how a specific shipment moves through the world: which maritime chokepoints it crosses, what alternative routes exist, what land corridors could substitute, and which countries take the brunt of the disruption if a chokepoint closes. It is the fastest way to ask "if this lane is disrupted, what actually happens?"
## Who it is for
- **Shippers, traders, and freight desks** evaluating lane risk for a specific origin-destination-commodity combination.
- **Analysts** comparing baseline routing to plausible detours under disruption.
- **Policy and risk teams** looking at country-level import dependency and exposure.
## Opening Route Explorer
- **Cmd / Ctrl + K → "Route Explorer — plan a shipment"** (`src/config/commands.ts:254`).
- Or use the dedicated action in the command palette; the workflow opens as a full-screen modal over the map.
Route state is encoded in the URL (`src/components/RouteExplorer/url-state.ts`), so any configuration — origin, destination, HS2 commodity, cargo type, active tab — can be copied, shared, or bookmarked.
## The four tabs
Route Explorer is organized as four tabs, labelled in `src/components/RouteExplorer/RouteExplorer.ts`:
### 1. Current
The baseline route between the selected origin and destination, rendered on the map with chokepoint-crossing events highlighted. Shows which chokepoints the lane touches, live disruption score on each, and the live war-risk tier attached to the primary chokepoint.
### 2. Alternatives
Alternative maritime corridors if the primary lane is degraded — for example, Cape of Good Hope as a detour around the Suez + Bab-el-Mandeb system. Each alternative shows added transit days and an added-cost multiplier, filtered to corridors that are actually suitable for the selected cargo type.
### 3. Land
Land-corridor substitutions where a maritime lane has a credible rail or road counterpart (e.g. ChinaEurope via Middle Corridor). Useful for thinking through land-sea rebalancing rather than purely maritime detours.
### 4. Impact
Flips the question from "where does my cargo go" to "who is most exposed if this lane breaks." Ranks countries by import dependency on the selected HS2 chapter through the selected chokepoint.
## Inputs
All four tabs share a single input bar:
| Input | Values |
|---|---|
| **From country** | Any country; ISO-3166-1 alpha-2 under the hood. |
| **To country** | Same. |
| **HS2 commodity** | 2-digit HS chapter (e.g. `27` — mineral fuels). Default guesses from cargo type. |
| **Cargo type** | `container`, `tanker`, `bulk`, `roro` — filters bypass corridors to those that carry the cargo. |
Every change triggers a debounced re-fetch (~250ms) so holding a key or scrubbing through options feels live.
## Keyboard-first
Route Explorer is built for keyboard use — all inputs and tabs are reachable without the mouse. The full binding list is shown in the in-workflow help overlay (press `?`) and lives in `src/components/RouteExplorer/KeyboardHelp.ts`:
| Key | Action |
|---|---|
| `Esc` | Close the active picker first, then the panel |
| `Tab` / `Shift+Tab` | Move focus between the panel and the map |
| `F` | Jump to the From picker |
| `T` | Jump to the To picker |
| `P` | Jump to the Product (HS2) picker |
| `S` | Swap From ↔ To |
| `1` `4` | Switch tabs (Current / Alternatives / Land / Impact) |
| `↑` / `↓` | Navigate the ranked list in the left rail |
| `Enter` | Commit the highlighted selection |
| `Cmd+,` | Copy the shareable URL |
| `?` | Show this help overlay |
## Map integration
While the workflow is open, the underlying map reflects your selections in real time:
- The primary route is highlighted.
- Bypass corridors render as dashed overlays when the Alternatives tab is active.
- The viewport zooms to fit the active route set.
When the workflow closes, the map restores its prior state.
## Tier & gating
Route Explorer is **PRO**. Free-tier visitors can open the workflow and fill in the input bar, but they hit a hard gate at fetch time — the left rail is blurred, the tab area is replaced with an "Unlock route intelligence" card and an **Upgrade to PRO** button, and no numeric payload is rendered in any tab. The underlying map, visible behind the modal, shows a generic public-route highlight as a preview of what PRO exposes.
The gate path is implemented by `renderFreeGate()` in `src/components/RouteExplorer/RouteExplorer.ts:342`; a `route-explorer:free-cta-click` analytics event fires if the Upgrade button is clicked. Clicking Upgrade routes into the Pro checkout (or falls back to the Pro marketing page).
## Data behind Route Explorer
- **Route graph** — `/api/supply-chain/v1/get-route-explorer-lane` (generated from `proto/worldmonitor/supply_chain/v1/`). Returns primary route id, chokepoint exposures, bypass corridors, war-risk tier, and disruption score.
- **Country impact** — `/api/supply-chain/v1/get-route-impact` for the Impact tab.
- **Chokepoint status** — live dry-bulk congestion, AIS density, dark-ship events, and recent incident feeds.
For the API-level contract, see the [Supply Chain](/api/SupplyChainService.openapi.yaml) and [Shipping v2](/api-shipping-v2) reference pages. For the underlying maritime layer on the map, see [Maritime Intelligence](/maritime-intelligence).
## Related workflows
- [Scenario Engine](/scenario-engine) — if Route Explorer answers "what is this lane today," Scenario Engine answers "what happens if a specific disruption event plays out."
- [Maritime Intelligence](/maritime-intelligence) — the layer-level view of vessels, chokepoints, and corridors on the main map.

75
docs/scenario-engine.mdx Normal file
View File

@@ -0,0 +1,75 @@
---
title: "Scenario Engine"
description: "Run pre-built supply-chain disruption scenarios — conflicts, sanctions, tariff shocks, and weather events — and see which chokepoints, sectors, and countries are exposed, directly on the map."
---
Scenario Engine turns WorldMonitor's live supply-chain graph into an interactive what-if tool. Instead of asking "what is the state of this lane today," you pick a named disruption scenario — a Hormuz closure, a Panama drought, a tariff shock on semiconductors — and the engine resolves the downstream impact on chokepoints, HS2 sectors, and countries, then paints the result onto the existing map.
## Who it is for
- **Supply-chain and commodity desks** stress-testing routing assumptions against a named event.
- **Risk and policy teams** translating a geopolitical or environmental scenario into concrete country exposure.
- **Leadership** building talking-tracks around "if X happens, what breaks first?"
## Opening the engine
Scenario Engine lives inside the **Supply Chain** panel on the main dashboard. Each pre-built scenario template renders as a trigger button; clicking a scenario starts an async job and activates the visual overlay once results land.
You can also drive it programmatically — see [Scenarios API](/api-scenarios) for the `/templates`, `/run`, and `/status` endpoints.
## Scenario templates
Templates are defined in `server/worldmonitor/supply-chain/v1/scenario-templates.ts`. Each template has a `type` drawn from a small, curated set so scenarios are browsable by category rather than a free-form list.
The currently shipped types are:
| Type | What it models |
|---|---|
| `conflict` | Chokepoint closure or degradation driven by an active conflict event (Taiwan Strait full closure, Suez + Bab-el-Mandeb simultaneous, Hormuz tanker blockade). |
| `weather` | Climatic disruption — e.g. the Panama Canal 50% drought scenario. |
| `sanctions` | Targeted trade restrictions (e.g. Russia / Baltic grain suspension). |
| `tariff_shock` | A sudden tariff action and its cost pass-through (e.g. US tariff escalation on electronics). |
Each template declares the chokepoints it affects (IDs from the chokepoint registry), a duration in days, affected HS2 sectors, and a cost-shock multiplier. Run them as-is — there are no sliders in v1. The `ScenarioType` union leaves room for `infrastructure` and `pandemic` categories, but no templates of those types ship today.
## What you get back
A completed scenario returns:
- **Affected chokepoints** — which ones go red on the map.
- **Impact ranking** — the top affected countries by ISO-2, ordered by exposure, with per-country import-value impact against the selected HS2 sectors.
- **Sector call-outs** — which HS2 chapters take the biggest hit.
- **A summary card** injected into the Supply Chain panel that stays visible until you deactivate the scenario.
The UI is state-driven, not modal — activating a scenario sets a `scenarioState` on every map renderer (deck.gl, globe, SVG fallback) so chokepoint colors and country choropleths reflect the disruption until you deactivate. This is coordinated by `MapContainer.activateScenario` at `src/components/MapContainer.ts:1010`, which is explicitly PRO-gated.
## Tier & gating
Scenario Engine is **PRO**. Free users see the trigger buttons but are blocked at activation: a `scenario-engine` gate-hit event is logged and the map is not repainted. The `/api/scenario/v1/run` endpoint itself also enforces PRO at the edge (`api/scenario/v1/run.ts`).
Rate limits on the API side — 10 jobs / minute / user, with a global queue cap at 100 in-flight — are documented in [Scenarios API](/api-scenarios#run-a-scenario).
## Run it yourself
The workflow is inherently async — the edge function enqueues a job, a Railway worker computes the impact, and the result is polled back:
1. Open the Supply Chain panel.
2. Click a scenario trigger button (the template name).
3. The button disables while the job runs (typically 5-30 s).
4. When the result lands, the map repaints, and a scenario banner is prepended to the panel. The banner always shows: a ⚠ icon, the scenario name, the top 5 impacted countries with per-country impact %, and a **×** dismiss control. When the scenario's result payload includes template parameters (duration, disruption %, cost-shock multiplier), the banner additionally renders a chip row (e.g. `14d · +110% cost`) and a tagline line such as *"Simulating 14d / 100% closure / +110% cost on 1 chokepoint. Chokepoint card below shows projected score; map highlights disrupted routes."* The affected chokepoints themselves are highlighted on the map and on the chokepoint cards rather than listed by name in the banner.
5. Click the **×** dismiss control on the banner (aria-label: "Dismiss scenario") to clear the scenario state — the map repaints to its baseline and the panel re-renders without the projected score and red-border callouts.
For scripted use, see [`POST /api/scenario/v1/run`](/api-scenarios#run-a-scenario) — enqueue, then poll `/status` until the response has a terminal status (`"done"` on success, `"failed"` on error). Non-terminal states are `"pending"` (queued) and `"processing"` (worker started); both can persist for several seconds. See the [status lifecycle table](/api-scenarios#poll-job-status) for the full contract.
## Data behind Scenario Engine
- **Scenario templates** — `server/worldmonitor/supply-chain/v1/scenario-templates.ts`. Additions require a proto-side change; not user-configurable today.
- **Job queue** — Redis list `scenario-queue:pending`; worker results land at `scenario-result:{jobId}`.
- **Chokepoint registry** — the same registry that backs live chokepoint status and Route Explorer, ensuring scenario results visually align with the rest of the product.
- **Trade / impact data** — HS2 import exposure per country, sourced from the supply-chain cache set.
## Related workflows
- [Route Explorer](/route-explorer) — run a specific lane against *today's* state.
- [Scenarios API](/api-scenarios) — the underlying HTTP contract.
- [Supply Chain](/api/SupplyChainService.openapi.yaml) — the broader service that backs the Supply Chain panel.

View File

@@ -77,7 +77,14 @@ Server-side verification uses `jose` with a cached JWKS — no round-trip to Cle
## Entitlement / tier gating ## Entitlement / tier gating
Valid key ≠ access. Every PRO-gated endpoint also checks the caller's entitlement via `isCallerPremium(req)` before returning data. **Valid key ≠ PRO.** Authentication and entitlement are orthogonal. Every PRO-gated endpoint runs a separate `isCallerPremium(req)` check (`server/_shared/premium-check.ts`) that **does not** accept a trusted browser Origin as proof of PRO, even though it accepts Origin for anonymous/public access.
`isCallerPremium` returns true only when one of these is present:
- A valid `X-WorldMonitor-Key` (env-allowlisted from `WORLDMONITOR_VALID_KEYS`, or a user-owned `wm_`-prefixed key whose Convex record has the `apiAccess` entitlement), **or**
- A Clerk `Authorization: Bearer …` token whose user has role `pro` or Dodo entitlement tier ≥ 1.
From the browser, `premiumFetch()` (`src/services/premium-fetch.ts`) handles this by injecting one of those credentials on every request. Desktop app uses `WORLDMONITOR_API_KEY` from the runtime config. Server-to-server callers must send the header explicitly.
| Tier | Access | | Tier | Access |
|------|--------| |------|--------|