Elie Habib 12ea629a63 feat(supply-chain): Sprint C — scenario engine (templates, job API, Railway worker, map activation) (#2890)
* feat(supply-chain): Sprint C — scenario engine templates, job API, Railway worker, map activation

Adds the async scenario engine for supply chain disruption modelling:

- src/config/scenario-templates.ts: 6 pre-built ScenarioTemplate definitions
  (Taiwan Strait closure, Suez+BaB simultaneous, Panama drought, Hormuz blockade,
  Russia Baltic grain suspension, US electronics tariff shock) with costShockMultiplier
  and optional HS2 sector scoping. Exports ScenarioVisualState + ScenarioResult
  types (no UI imports, avoids MapContainer <-> DeckGLMap circular dep).

- api/scenario/v1/run.ts: PRO-gated edge function — validates scenarioId against
  template registry and iso2 format, enqueues job to Redis scenario-queue:pending
  via RPUSH. Returns {jobId, status:'pending'} HTTP 202.

- api/scenario/v1/status.ts: Edge function — validates jobId via regex to prevent
  Redis key injection, reads scenario-result:{jobId}. Returns {status:'pending'}
  when unprocessed, or full worker result when done.

- scripts/scenario-worker.mjs: Always-on Railway worker using BLMOVE LEFT RIGHT for
  atomic FIFO dequeue+claim. Idempotency check before compute. Writes result with
  24h TTL; writes {status:'failed'} on error; always cleans processing list in finally.

- DeckGLMap.ts: scenarioState field + setScenarioState(). createTradeRoutesLayer()
  overrides arc color to orange for segments whose route waypoints intersect scenario
  disruptedChokepointIds. Null state restores normal colors.

- MapContainer.ts: activateScenario(id, result) and deactivateScenario() broadcast
  ScenarioVisualState to DeckGLMap. Globe/SVG deferred to Sprint D (best-effort).

🤖 Generated with Claude Sonnet 4.6 via Claude Code (https://claude.com/claude-code) + Compound Engineering v2.49.0

Co-Authored-By: Claude Sonnet 4.6 (200K context) <noreply@anthropic.com>

* fix(supply-chain): move scenario-templates to server/ to satisfy arch boundary

api/ edge functions may not import from src/ app code. Move the authoritative
scenario-templates.ts to server/worldmonitor/supply-chain/v1/ and replace
src/config/scenario-templates.ts with a type-only re-export for src/ consumers.

* fix(supply-chain): guard scenario-worker runWorker() behind isMain check

Without the isMain guard, the pre-push hook picks up scenario-worker.mjs
as a seed test candidate (non-matching lines pass through sed unchanged)
and starts the long-running worker process, causing push failures.

* fix(pre-push): filter non-matching lines from seed test selector

The sed transform passes non-matching lines (e.g. scenario-worker.mjs)
through unchanged. Adding grep "^tests/" ensures only successfully
transformed test paths are passed to the test runner.

* fix(supply-chain): address PR #2890 review findings — worker data shapes + status PRO gate

Three bugs found in PR #2890 code review:

1. [High] scenario-worker.mjs read wrong cache shape for exposure data.
   supply-chain:exposure:{iso2}:{hs2}:v1 caches GetCountryChokepointIndexResponse
   ({ iso2, hs2, exposures: [{chokepointId, exposureScore}], ... }), not a
   chokepointId-keyed object. Worker now iterates data.exposures[], filters by
   template.affectedChokepointIds, and ranks by exposureScore (importValue does
   not exist on ChokepointExposureEntry). adjustedImpact = exposureScore x
   (disruptionPct/100) x costShockMultiplier.

2. [Medium] api/scenario/v1/status.ts was not PRO-gated, allowing anyone with
   a valid jobId to retrieve full premium scenario results. Added isCallerPremium()
   check; returns HTTP 403 for non-PRO callers, matching run.ts behavior.

3. [Low] Worker parsed chokepoint status cache as Array but actual shape is
   { chokepoints: [], fetchedAt, upstreamUnavailable }. Fixed to access
   cpData.chokepoints array.

* fix(scenario): per-country impactPct + O(1) route lookup in arc layer

- impactPct now reflects each country's relative share of the worst-hit
  country (0-100) instead of the flat template.disruptionPct for all
- Pre-build routeId→waypoints Map in createTradeRoutesLayer() so
  getColor() is O(1) per segment instead of O(n) per frame

* fix(scenario): rate limit, pipeline GETs, error sanitization, processing state, orphan drain

- Add per-user rate limit (10 jobs/min) + queue depth cap to run.ts
- Replace 594 sequential Redis GETs with single Upstash pipeline call
- Sanitize worker err.message to 'computation_error' in failed results
- Remove dead validateApiKey() calls (isCallerPremium covers this)
- Write processing state before computeScenario() starts
- Add SIGTERM handler + startup orphan drain to worker loop
- Validate dequeued job payload fields before use as Redis key fragments
- Fix maxImpact divide-by-zero with Math.max(..., 1)
- Hoist routeWaypoints Map to module level in DeckGLMap
- Add GET /api/scenario/v1/templates discovery endpoint
- Fix template sync comment to reference correct authoritative file

* docs(plan): mark Sprint C complete, record deferrals to Sprint D

- Sprint status table added: Sprints 0-2 merged, C ready to merge (#2890), A/B/D not started
- Sprint C checklist: 4 ACs checked off, panel UI + tariff-shock visual deferred
- Sprint D section updated to carry over Sprint C visual deferrals
- PR #2890 added to Related PRs

---------

Co-authored-by: Claude Sonnet 4.6 (200K context) <noreply@anthropic.com>
2026-04-10 14:44:14 +04:00

World Monitor

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

GitHub stars GitHub forks Discord License: AGPL v3 TypeScript Last commit Latest release

Web App  Tech Variant  Finance Variant  Commodity Variant  Happy Variant

Download Windows  Download macOS ARM  Download macOS Intel  Download Linux

Documentation  ·  Releases  ·  Contributing

World Monitor Dashboard


What It Does

  • 435+ curated news feeds across 15 categories, AI-synthesized into briefs
  • Dual map engine — 3D globe (globe.gl) and WebGL flat map (deck.gl) with 45 data layers
  • Cross-stream correlation — military, economic, disaster, and escalation signal convergence
  • Country Intelligence Index — composite risk scoring across 12 signal categories
  • Finance radar — 92 stock exchanges, commodities, crypto, and 7-signal market composite
  • Local AI — run everything with Ollama, no API keys required
  • 5 site variants from a single codebase (world, tech, finance, commodity, happy)
  • Native desktop app (Tauri 2) for macOS, Windows, and Linux
  • 21 languages with native-language feeds and RTL support

For the full feature list, architecture, data sources, and algorithms, see the documentation.


Quick Start

git clone https://github.com/koala73/worldmonitor.git
cd worldmonitor
npm install
npm run dev

Open localhost:5173. No environment variables required for basic operation.

For variant-specific development:

npm run dev:tech       # tech.worldmonitor.app
npm run dev:finance    # finance.worldmonitor.app
npm run dev:commodity  # commodity.worldmonitor.app
npm run dev:happy      # happy.worldmonitor.app

See the self-hosting guide for deployment options (Vercel, Docker, static).


Tech Stack

Category Technologies
Frontend Vanilla TypeScript, Vite, globe.gl + Three.js, deck.gl + MapLibre GL
Desktop Tauri 2 (Rust) with Node.js sidecar
AI/ML Ollama / Groq / OpenRouter, Transformers.js (browser-side)
API Contracts Protocol Buffers (92 protos, 22 services), sebuf HTTP annotations
Deployment Vercel Edge Functions (60+), Railway relay, Tauri, PWA
Caching Redis (Upstash), 3-tier cache, CDN, service worker

Full stack details in the architecture docs.


Flight Data

Flight data provided gracefully by Wingbits, the most advanced ADS-B flight data solution.


Data Sources

WorldMonitor aggregates 65+ external data sources across geopolitics, finance, energy, climate, aviation, cyber, military, infrastructure, and news intelligence. See the full data sources catalog for providers, feed tiers, and collection methods.


Contributing

Contributions welcome! See CONTRIBUTING.md for guidelines.

npm run typecheck        # Type checking
npm run build:full       # Production build

License

AGPL-3.0 for non-commercial use. Commercial license required for any commercial use.

Use Case Allowed?
Personal / research / educational Yes
Self-hosted (non-commercial) Yes, with attribution
Fork and modify (non-commercial) Yes, share source under AGPL-3.0
Commercial use / SaaS / rebranding Requires commercial license

See LICENSE for full terms. For commercial licensing, contact the maintainer.

Copyright (C) 2024-2026 Elie Habib. All rights reserved.


Author

Elie HabibGitHub

Contributors

Security Acknowledgments

We thank the following researchers for responsibly disclosing security issues:

  • Cody Richard — Disclosed three security findings covering IPC command exposure, renderer-to-sidecar trust boundary analysis, and fetch patch credential injection architecture (2026)

See our Security Policy for responsible disclosure guidelines.


worldmonitor.app  ·  docs.worldmonitor.app  ·  finance.worldmonitor.app  ·  commodity.worldmonitor.app

Star History

Star History Chart
Description
Mirrored from GitHub
Readme AGPL-3.0 382 MiB
Languages
TypeScript 49.1%
JavaScript 47%
CSS 2.9%
HTML 0.4%
Rust 0.3%
Other 0.1%