Deepmox / Programming

learning path

koala73/worldmonitor

Real-time global intelligence dashboard. AI-powered news aggregation, geopolitical monitoring, and infrastructure tracking in a unified situational awareness interface

5 chapters 5 audio lessons 5 videos 3 free previews Fresh topic

Start here

1. E00_The_500_Feed_Lie

The 500-Feed Lie

Real-time intelligence dashboards fail not at the algorithm but at the data plumbing — and most of the open-source projects in this space have the priority backwards.

I want to start with a moment. A few months ago I was watching an M4.7 earthquake register on WorldMonitor's seismology panel — light shaking, somewhere off the Honshu coast — at the same moment the upstream feed I would normally have used was returning 503s. The dashboard didn't go dark. The marker appeared, color-coded, with a confidence halo, and the panel's freshness indicator read "stale — 4 minutes, served from cache". Behind that single green marker was a chain of decisions that I now believe is the most underrated architectural choice in the open-source intelligence space: a panel that doesn't need the upstream API to render, a system that admits when its data is stale, and a pipeline that already knew the feed was degraded because the freshness tracker had been watching.

That is the whole series in miniature. A real-time dashboard is not a UI problem. It is a plumbing problem, and most projects in this category — whether built by journalists, by hedge funds, or by well-meaning open-source maintainers — get that priority wrong. They build the panel. They retrofit the data. They discover, somewhere around version 2, that the algorithm they wanted is impossible because the inputs are unreliable, the cache thrashes, and the LLM hallucinates over empty headlines.

WorldMonitor — the project I want to walk you through over the next four chapters — is one of the few that reverses the order. It is a TypeScript single-page application with 500+ curated news feeds, a dual map engine (3D globe plus WebGL flat map), a Country Instability Index scoring 31 Tier-1 nations in real time, and a finance radar covering 29 stock exchanges. But the headline features are downstream of a much quieter achievement: a data pipeline that survives when feeds go down, a scoring system that explicitly decouples from media volume, and a deployment topology that pushes 38 bootstrap datasets in a single Redis round trip rather than 38.

The series-level takeaway, stated once at the top: *If you cannot show the user what you don't know, you cannot show them what you do.* WorldMonitor's intelligence is a byproduct of its plumbing — and the plumbing is the product.

The cognitive shift I want to leave you with, by the end of this series, is a re-ranking of priorities. You will not learn to copy WorldMonitor's map. You will not memorize its scoring coefficients. What I want is for you to start asking, on every dashboard project you touch from now on: *Where is the cache? What happens when this dependency goes down? What is the fallback? Who is the source of truth for the score?* Those questions are plumbing questions, and the intelligence that falls out of them is the *consequence* of getting them right.

I should be honest about a bias here. I came to this codebase expecting to write a teardown — another framework-on-framework SPA with a thin intelligence veneer, another "AI dashboard" that turns out to be a chat wrapper around a list of RSS feeds. I was wrong in a specific way that I want to share with you now, because it is the bias I think most senior engineers share when they read the README.

The README says "real-time global intelligence dashboard". I read that as marketing. It is not marketing. The CII v8 server endpoint returns scores from a methodologically explicit formula

7m / Article + audio + video

2. E01_Why_Vanilla_Won

Why Vanilla TypeScript Beat React at Map Scale

Vanilla TypeScript is not asceticism. For a 56-layer interactive map with 104 panel subclasses, no virtual DOM is the only architecture that survives the bundle, the WebView, and the discriminated-union workload.

I want to start with a snippet. This is the shape of the Panel base class from src/components/Panel.ts (paraphrased — the real implementation goes deeper into lifecycle hooks and DOM sanitization helpers):

class Panel {
  content: HTMLElement;             // stable outer container, never replaced
  render(): void { /* subclass */ } // subclass fills in
  setContent(html: string): void {
    clearTimeout(this._t);
    this._t = setTimeout(() => {
      this.replaceContentSafely(html);  // sanitized + 150ms debounce
    }, 150);
  }
  destroy(): void { /* subclass */ }
}

That is the foundation under 104 panel subclasses — earthquakes, AIS vessels, ACLED protests, USNI fleet, market signals, country instability scores, hotspot escalation, prediction markets, infrastructure exposure. Every single one of them extends Panel, calls setContent(html) whenever state changes, and relies on event delegation from a stable outer container. There is no React. There is no Vue. There is no Svelte. There is no Solid, no Preact used as a hidden runtime, no adapter. The architecture document states this as a deliberate, defended choice — and after tracing the codebase for a week, I now think the framework rejection is not aesthetic minimalism. It is load-bearing engineering.

I came to this analysis believing that React's component model — declarative state, virtual DOM diffing, hooks, the entire mental model — would be essential at this scale. The reasoning seemed obvious: a map with 56 layer types, hundreds of markers per layer, real-time updates every 10 seconds, and a panel grid that re-renders constantly. Surely you need a framework. Surely hand-rolled DOM management would collapse under the weight. The evidence from src/components/ and the architecture document pushed me hard in the other direction. Vanilla TypeScript is the right default for a system where the bundle weight is competing with intelligence data, the render model is wholesale subtree replacement rather than fine-grained reactive updates, and the runtime target includes WebKitGTK on Linux Tauri builds that have idiosyncratic behavior around drag-and-drop and autoplay.

Let me show you what the architecture is actually doing.

flowchart LR
  A[Panel subclass] -->|setContent html| B[debounced 150ms]
  B --> C[sanitized content replace]
  C --> D[event delegation<br/>on this.content]
  D -->|closest selector| E[handler]
  F[AppContext] -->|centralized mutable state| A
  A -->|URL state| G[urlState.ts<br/>250ms debounce]
  H[SmartPollLoop] -->|visible? in-viewport? tab-hidden?| A
  I[hydrate from /api/bootstrap] -->|getHydratedData| A

The diagram looks mundane until you understand what each piece is doing *instead of* what a framework would do. setContent(html) is not a render function — it is a 150-millisecond debounced sanitized content replacement. The panel is not diffing against a previous virtual tree. It is replacing the entire subtree. Event delegation works because the *outer* container survives the replacement; only its children change. Handlers attach to this.content (stable) and use event.target.closest('.selector') to find the intended element. The cost of "diffing" is zero, because the diff is: replace the entire subtree, let the browser rebuild layout. The cost of the framework — VDOM diffing, hook dependency tracking, memoization invalidation, re-render scheduling — is also zero, because none of it exists.

This pattern is enforced project-wide. Every panel does this. E2E tests have to re-query the DOM after each render cycle, because element references go stale when the subtree is replaced — and the architecture document calls this out explicitly. The constraint is documented as a feature, not a bug.

Now let me show you why this matters at scale.

Imagine you have a React app. You render 56 layer toggles, each with its own subscription to a WebSocket, each with its own panel state. Every state change triggers a re-render of the parent, which re-renders the children, which diff against the previous tree. For a small app this is invisible. For a dashboard with 500+ news items, 4,000 vessels in the AIS stream, and 31 country instability scores updating every five minutes, the framework tax becomes visible. React's runtime alone weighs more than the *entire* WorldMonitor application shell. The architecture document makes the claim with characteristic bluntness: "the entire application shell (panel system, routing, state management) compiles to less JavaScript than React's runtime alone".

The reason this matters is not bundle size puritanism. It is that the bundle is competing with the actual intelligence. The page loads dozens of data layers, two map renderers, ML models, and live video streams. Every kilobyte of framework overhead is a kilobyte the user does not get for a map ti

9m / Article + audio + video

3. E02_Scoring_Without_Media_Bubble

Scoring Without the Media Bubble

Country instability scoring must be architecturally hostile to media volume. The CII v8 formula's deliberate decoupling of news pressure from conflict scoring is the difference between an intelligence product and a sentiment meter.

I want to start with a counter-example. Consider two countries in the same week.

Country A: 50 protest events. 12 headlines on major wire services. CII: 78 (High). Strategic Risk panel: orange.

Country B: 8 protest events. 240 headlines on major wire services. CII: 41 (Normal). Strategic Risk panel: yellow.

If you built a scoring system that says "more headlines = more unstable", you would have ranked Country B above Country A. WorldMonitor's Country Instability Index v8 does the opposite. The reason is the specific architectural choice of decoupling news pressure from conflict pressure, and once you understand it, you start to see why most public "instability scores" are not scoring — they are counting.

CII v8 is the formula that produces a 0–100 instability score for each of 31 Tier-1 countries, refreshed every five minutes by the server endpoint GET /api/intelligence/v1/get-risk-scores. The endpoint is the source of truth; the browser renders a fallback only if the cached scores are unavailable. The methodology is published. The coefficients are explicit. The floors are auditable. This is not an LLM. It is an algorithm with a methodology page.

Here is the formula, in two pieces:

flowchart TB
  subgraph baseline["Baseline layer (40%)"]
    BL[per-country baselineRisk<br/>0-50 editorial]
  end
  subgraph event["Event layer (60%)"]
    U[Unrest 25%]
    C[Conflict 30%]
    S[Security 20%]
    I[Information 25%]
  end
  subgraph boosts["Supplemental boosts (additive, capped)"]
    B1[Climate: +15]
    B2[Cyber: +12]
    B3[Wildfire: +8]
    B4[Travel advisory: +15]
    B5[OREF blend Israel: +25]
    B6[Displacement: +20]
    B7[Earthquake: +25]
    B8[Sanctions: +14]
    B9[News urgency: +5]
    B10[AIS disruption: +10]
  end
  subgraph floors["Floors (lower bounds)"]
    F1[UCDP war: ≥70]
    F2[UCDP minor: ≥50]
    F3[Do-not-travel: ≥60]
    F4[Reconsider-travel: ≥50]
  end
  BL --> CS
  U --> CS
  C --> CS
  S --> CS
  I --> CS
  CS[combinedScore] --> BM{+ boosts}
  BM --> CL[clamp 0-100]
  CL --> MAX{floor wins}
  MAX --> SC[final CII 0-100]
  F1 --> MAX
  F2 --> MAX
  F3 --> MAX
  F4 --> MAX
  B1 --> BM
  B2 --> BM
  B3 --> BM
  B4 --> BM
  B5 --> BM
  B6 --> BM
  B7 --> BM
  B8 --> BM
  B9 --> BM
  B10 --> BM

Two layers. The baseline is an editorial coefficient — a per-country number from 0 to 50 reflecting structural fragility (Venezuela gets a different number than Uruguay). The event layer is a weighted blend of four components: Unrest (25%), Conflict (30%), Security (20%), Information (25%). The blend is combinedScore = baselineRisk × 0.40 + eventScore × 0.60. Then ten supplemental boosts add on top, and four floors guarantee a minimum score if the data says so.

Now look at Country A vs Country B through this formula. Country A has 50 protests. If A is a democracy with high baseline observability, the Unrest score uses logarithmic dampening (log(protestCount)), so 50 protests in a country where protests are routine produce a moderate Unrest score. Country B has 8 protests in an authoritarian state where public protest is rare and dangerous — the Unrest score is linear, so 8 events with fatalities produce a higher score *per event* than Country A's 50.

The Information score captures news pressure but only contributes 25% of the event layer. Even if Country B had 1,000 headlines, the Information score could not, by itself, push the country into High territory without commensurate Unrest, Conflict, or Security signal. This is the decoupling — and it is the entire reason the score is not a sentiment meter.

The design intent is explicit. The architecture document quotes the intelligence tradecraft behind it: "Democracies experience routine protests that don't indicate instability (France's yellow vest movement, US campus protests). Authoritarian states rarely see public protest, so each event is significant. The CII uses log(protestCount) for democracies and linear scaling for authoritarian states, preventing democratic noise from drowning genuine authoritarian unrest signals."

I want to spend a moment on the floors, because they are the part most coverage of "instability scores" omits.

A floor is a lower bound. If a country qualifies, its score cannot be lower than the floor value, regardless of what the events are saying. There are four:

  • UCDP active war: floor at 70. If the Uppsala Conflict Data Program has classified the country as active war (total deaths > 1,000 or event count > 100 in a 2-year recency window), the CII is pinned at ≥70.
  • UCDP minor conflict: floor at 50. Same source, lower threshold (events > 10).
  • State Department do-not-travel: floor at 60.
  • State Department reconsider-travel: floor at 50.

Why floors? Because data gaps are not peace. If a country is in active conflict and the news pipeline goes quiet, the absence of headlines is not a calming signal — it is a coverage gap. The floor forces the score to remain high until the conflict is genuinely over, as classified by an externa

9m / Article + audio + video

Premium chapters

4. E03_Thirty_Eight_Channels_One_Round_Trip
Available after upgrade / 11m
5. E04_The_Dashboard_With_Zero_Keys
Available after upgrade / 10m