7 min read

World Monitor: A Live Global Intelligence Dashboard (GitHub, Scanned)

A live dashboard of world news, conflict, markets and infrastructure on a 3D globe or flat map.

World Monitor logo
✅
Scan: safe. Nothing malicious. Two things to know: the AGPL licence obliges you to share changes if you serve a modified copy, and the hosted site runs Umami and Vercel analytics, which a self-hosted copy fetches but does not report to. Scanned Oct 28, 2026; the full report is below.

World Monitor gathers public signals about the state of the world into one screen. Curated news feeds from global and regional outlets are summarised into AI briefs, and a 3D globe (globe.gl) or a WebGL flat map (deck.gl) carries layers for conflict, military flights, shipping chokepoints, cyber threats, climate events, energy and infrastructure. Panels track stock exchanges, commodities, crypto and prediction markets, and a Country Instability Index scores 31 countries with approximate 24-hour movement. The same codebase builds tech, finance, commodity, energy and good-news variants, a Tauri desktop app, a terminal dashboard (npx worldmonitor), SDKs and an MCP server for AI agents.

It comes from Elie Habib and grew unusually fast: created in January 2026, it has about 89,700 stars, 171 contributors, more than 9,000 commits and 50 releases, the latest (v2.11.6) on 25 October 2026, with commits landing daily. The hosted site at worldmonitor.app is free to browse, and a Pro plan sells API keys for the REST API, CLI and MCP tools.

The code is AGPL-3.0. You can self-host, fork and use it commercially, but if you run a modified copy as a service you have to publish your changes; the README offers a separate commercial licence for teams that cannot. The app starts with no environment variables, and AI summaries can run on a local Ollama model, so API keys are only needed for the feeds that require them.

  • Repository: github.com/koala73/worldmonitor
  • Licence: AGPL-3.0 (GNU Affero General Public License v3.0)
  • Language: TypeScript. Stars: 89.7K. Forks: 13.8K. Last push: Oct 28, 2026.
  • Scan: safe, Oct 28, 2026, commit 91c50b1

Who it is for

Analysts, journalists and researchers who follow geopolitics or markets and want one situational view, OSINT hobbyists, and developers who want a large, well-tested open-source dashboard or an agent-ready source of world data.

Getting started

1. Try the terminal dashboard without installing anything (no key needed)

npx worldmonitor

2. Or clone the web app (Node.js and npm)

git clone https://github.com/koala73/worldmonitor.git && cd worldmonitor

3. Install the dependencies

npm install

4. Start it, then open http://localhost:3000

npm run dev

Nothing is required to start; some feeds need their own credentials, listed in .env.example. A self-hosted copy still loads its analytics script from abacus.worldmonitor.app, although the tracker only records visits on the official worldmonitor.app hostnames.

Safety scan

We cloned koala73/worldmonitor at commit 91c50b1 on Oct 28, 2026 and ran the checks described on the GitHub Tools page: credential patterns, decode-and-execute code, install-time scripts, committed binaries, risky CI workflows, every host the code talks to, known vulnerabilities in pinned dependencies, and project hygiene. A person read every hit. This is what we found.

  • A large codebase: 8,945 files and about 2.4 million lines, mostly JavaScript and TypeScript. The one secret hit is a fake ghp_ token in tests/railway-reconcile-manifest.test.mjs, written to prove that token-like names are rejected. The 37 bare-IP URLs are all tests using documentation and reserved ranges (TEST-NET, 93.184.216.34) to prove the webhook and proxy guards refuse them.
  • The four api.telegram.org hits are the alert feature sending to the bot token you configure, and the curl | sh hit is a hint message in the build prerequisite check suggesting rustup. The root postinstall hook runs a local script that writes an inventory facts file, with no network access; src-tauri/build.rs is the standard one-line Tauri build script.
  • 3,657 locked packages across 12 lockfiles, with 24 known advisories: none critical, and the only two high ones (mysql2 and deepmerge-ts) are in docker/umami/runtime, the project's own analytics server, not the dashboard. The main package-lock has five moderate or low; most of the Rust desktop findings are notices that old crates are unmaintained.
  • Telemetry, read from the source: Sentry error reporting is off unless you set VITE_SENTRY_DSN (empty in .env.example), Umami is limited to the worldmonitor.app hostnames, and Vercel Web Analytics only reports on a Vercel deployment. The network hosts are the news, market and data sources the panels read.
  • 53 workflows. contributor-trust.yml uses pull_request_target, but the scanner's checkout match is a warning comment: the job never checks out PR code, it only looks up the author's name and adds a label. All 19 third-party actions are pinned to commits. Security policy, Dependabot, CodeQL, licence, contributing guide and code of conduct all present.

What the scanner counted

CheckResult
Secrets1 candidate found and read; see the notes above.
Suspicious code5 pattern hits found and read; every one is listed under the raw findings.
Install-time code2 npm lifecycle scripts. 1 Cargo build script. 2 installer scripts
Committed binariesNone.
CI workflows53 workflows. 1 uses pull_request_target, 1 of which check out the pull request head. 0 of 19 third-party actions pinned to a tag rather than a commit.
Network hosts40 distinct hosts referenced from source; most often worldmonitor.app, www.worldmonitor.app, news.google.com, api.worldmonitor.app. 37 URLs to a bare IP address, listed under the raw findings.
Known vulnerabilities24 advisories across 3,657 pinned packages: 0 critical, 2 high, 11 moderate, 1 low, 10 unrated. blog-site/package-lock.json: 329 packages, 0 advisories; consumer-prices-core/package-lock.json: 236 packages, 0 advisories; demos/verification-desk/package-lock.json: 49 packages, 0 advisories; docker/umami/runtime/pnpm-lock.yaml: 252 packages, 6 advisories; integrations/zapier/package-lock.json: 940 packages, 0 advisories; package-lock.json: 1,795 packages, 5 advisories; pro-test/package-lock.json: 603 packages, 5 advisories; scripts/package-lock.json: 342 packages, 2 advisories; sdk/rust/Cargo.lock: 132 packages, 1 advisories; src-tauri/Cargo.lock: 525 packages, 11 advisories; workers/api-cors-preflight/package-lock.json: 91 packages, 0 advisories; workers/railway-reconcile-control/package-lock.json: 91 packages, 0 advisories.
Project hygieneHas security policy, automated dependency updates, CodeQL, licence file, contributing guide.
OpenSSF ScorecardNot scored: the project is not in Scorecard's weekly index.

The raw findings

Every hit the scanner wrote out, with a link to the exact line at the scanned commit. Secrets candidates are redacted.

Secret candidates (1, redacted)
WhereRuleMatch
tests/railway-reconcile-manifest.test.mjs:132github-tokenghp_ab…789 (40 chars)
Pattern hits (5)
WhereRuleMatch
convex/http.ts:734exfil-hostawait fetch(`https://api.telegram.org/bot${botToken}/${method}`, {
scripts/check-build-prereqs.mjs:95download-piped-to-shellhint: "Install via rustup: curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh",
scripts/notification-relay.cjs:596exfil-hostconst res = await fetch(`https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/sendMessage`, {
scripts/seed-digest-notifications.mjs:1206exfil-host`https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/sendMediaGroup`,
scripts/seed-digest-notifications.mjs:1234exfil-host`https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/sendMessage`,
URLs to bare IP addresses (37)
WhereRuleMatch
server/__tests__/shipping-register-user-key.test.ts:114ip-literal-urlconst payload = { callbackUrl: 'https://93.184.216.34/hook', chokepointIds: ['suez'], alertThreshold: 50 };
server/__tests__/shipping-registration-storage.test.ts:58ip-literal-urlconst payload = { callbackUrl: 'https://93.184.216.34/hook', chokepointIds: ['suez'], alertThreshold: 50 };
src-tauri/sidecar/local-api-server.test.mjs:2101ip-literal-url'http://100.64.0.1/secret',
src-tauri/sidecar/local-api-server.test.mjs:2102ip-literal-url'http://198.18.0.1/secret',
src-tauri/sidecar/local-api-server.test.mjs:2103ip-literal-url'http://192.0.0.1/secret',
src-tauri/sidecar/local-api-server.test.mjs:2104ip-literal-url'http://192.0.2.1/secret',
src-tauri/sidecar/local-api-server.test.mjs:2105ip-literal-url'http://192.88.99.1/secret',
src-tauri/sidecar/local-api-server.test.mjs:2106ip-literal-url'http://198.51.100.1/secret',
src-tauri/sidecar/local-api-server.test.mjs:2107ip-literal-url'http://203.0.113.1/secret',
src-tauri/sidecar/local-api-server.test.mjs:2108ip-literal-url'http://240.0.0.1/secret',
src-tauri/sidecar/local-api-server.test.mjs:2216ip-literal-urlconst upstream = await fetch('https://93.184.216.34/data');
tests/notification-teams.test.mjs:220ip-literal-urlconst result = await teams.sendTeamsPayload('https://20.42.65.92/workflows/x/triggers/manual/paths/invoke', {}, { resolveHostname: resolvePublic });
tests/seed-cyber-provider-profiles.test.mjs:203ip-literal-urlurls: [{ url: 'https://192.0.2.44/payload', country: 'FR', date_added: new Date(NOW - 90 * DAY).toISOString() }],
tests/shipping-v2-handler.test.mjs:211ip-literal-urlcallbackUrl: 'https://93.184.216.34/wm',
tests/shipping-v2-handler.test.mjs:258ip-literal-urlcallbackUrl: 'https://93.184.216.34/wm',
tests/shipping-v2-handler.test.mjs:273ip-literal-urlcallbackUrl: 'https://93.184.216.34/wm',
tests/shipping-v2-handler.test.mjs:284ip-literal-urlcallbackUrl: 'https://93.184.216.34/wm',
tests/shipping-v2-handler.test.mjs:295ip-literal-urlcallbackUrl: 'https://93.184.216.34/wm',
tests/shipping-v2-handler.test.mjs:327ip-literal-urlcallbackUrl: 'https://93.184.216.34/wm',
tests/shipping-v2-handler.test.mjs:346ip-literal-urlcallbackUrl: 'https://93.184.216.34/wm',
tests/shipping-v2-handler.test.mjs:361ip-literal-urlcallbackUrl: 'https://93.184.216.34/wm',
tests/shipping-v2-handler.test.mjs:372ip-literal-urlcallbackUrl: 'https://93.184.216.34/wm',
tests/shipping-v2-handler.test.mjs:383ip-literal-urlawait registerWebhook(proCtx(), { callbackUrl: 'https://93.184.216.34/wm', chokepointIds: [], countries: [] });
tests/shipping-v2-handler.test.mjs:393ip-literal-url() => registerWebhook(proCtx(), { callbackUrl: 'https://93.184.216.34/wm', chokepointIds: [], countries: [], eventType: 'country.coup' }),
and 13 more
npm lifecycle scripts (2)
  • demos/verification-desk/package.json postinstall: node scripts/build-avatar.mjs && node scripts/install-deck.mjs
  • package.json postinstall: npm run inventory:facts
Installer scripts (2)
Worst known vulnerabilities (24 of 24)
AdvisorySeverityPackageSummary
GHSA-ggr8-5vv4-36mxhighdeepmerge-ts@7.1.5DeepmergeTS has stack exhaustion when merging recursive object graphs
GHSA-3f6p-5ww8-9rcrhighmysql2@3.15.3MySQL2: Auth Plugin Downgrade to mysql_clear_password Leaks Plaintext Credentials
GHSA-92pp-h63x-v22mmoderate@hono/node-server@1.19.11@hono/node-server: Middleware bypass via repeated slashes in serveStatic
GHSA-frvp-7c67-39w9moderate@hono/node-server@1.19.11Node.js Adapter for Hono: Path traversal in `serve-static` on Windows via encoded backslash (`%5C`)
GHSA-rgwj-5xj2-c3m3moderatemysql2@3.15.3MySQL2: Unbounded zlib inflate in compressed MySQL protocol handler allows decompression-bomb DoS
GHSA-5qjj-4xww-7phcmoderatevalibot@1.2.0Valibot: record() issue paths can make flatten() throw for inherited Object property names
GHSA-528h-pc64-c93xmoderatestream-json@1.9.2-worldmonitor.1stream-json: pick/ignore/filter/replace filters are O(depth²) on nested input - small crafted JSON blocks the event loop…
GHSA-hqr4-qq8f-hg3xmoderatestream-json@1.9.2-worldmonitor.1stream-json: JSONC parser and verifier re-scan the whole accumulated comment on every input chunk
GHSA-mjw6-4jj6-33hcmoderatestream-json@1.9.2-worldmonitor.1stream-json has a prototype pollution issue: Assembler writes this.current[this.key] on plain objects
GHSA-w5hq-g745-h8pqmoderateuuid@8.3.2uuid: Missing buffer bounds check in v3/v5/v6 when buf is provided
GHSA-w5hq-g745-h8pqmoderateuuid@11.1.0uuid: Missing buffer bounds check in v3/v5/v6 when buf is provided
GHSA-qwgh-2vcv-g2f7moderateblock-buffer@0.10.4block_buffer: panic corrupts inline buffer position
GHSA-wrw7-89jp-8q8gmoderateglib@0.18.5Unsoundness in `Iterator` and `DoubleEndedIterator` impls for `glib::VariantStrIter`
GHSA-gcqx-w48m-w2h4lowdeepmerge@4.3.1deepmerge mergeObject() allows prototype poisoning of returned objects
CVE-2026-111943unknownrustls-webpki@0.103.15Certificate verification may accept distrusted intermediates
RUSTSEC-2024-0370unknownproc-macro-error@1.0.4proc-macro-error is unmaintained
RUSTSEC-2025-0081unknownunic-char-property@0.9.0`unic-char-property` is unmaintained
RUSTSEC-2025-0075unknownunic-char-range@0.9.0`unic-char-range` is unmaintained
RUSTSEC-2025-0080unknownunic-common@0.9.0`unic-common` is unmaintained
RUSTSEC-2025-0100unknownunic-ucd-ident@0.9.0`unic-ucd-ident` is unmaintained
RUSTSEC-2025-0098unknownunic-ucd-version@0.9.0`unic-ucd-version` is unmaintained
RUSTSEC-2026-0371unknownzbus_names@3.0.0`zbus_names` is unmaintained
RUSTSEC-2026-0370unknownzvariant@4.2.0`zvariant` is unmaintained
RUSTSEC-2026-0372unknownzvariant_derive@4.2.0`zvariant_derive` is unmaintained
Workflows worth a look

By the numbers

Stars89.7K
Forks13.8K
Contributors171
Commits9,005
Open issues240
Open pull requests103
Releases50
Latest releasev2.11.6
LicenceAGPL-3.0
Main languageTypeScript
Project age9 months
Last pushOct 28, 2026
Tracked files8,945
Lines of code2.4M
Checkout size182 MB

Lines by language: JavaScript 913.9K, TypeScript 636.8K, JSON 520.3K, Markdown 159.4K, YAML 83.7K, CSS 40.4K.

Questions

Is World Monitor free?

Yes. The website and its variants are free to browse, the desktop app and terminal dashboard are free downloads, and the source is AGPL-3.0, so self-hosting costs nothing beyond your own server. A paid Pro plan sells API keys for the REST API, CLI and MCP server, and a commercial licence exists for teams that cannot meet the AGPL's terms.

Where does World Monitor's data come from?

From public and attributed upstream sources: news outlets and Google News, official statistics such as the World Bank, IMF and St. Louis Fed, market data, prediction markets, ADS-B flight data from Wingbits, and conflict, cyber and climate feeds. The project publishes a data sources catalogue with each provider's licence posture. AI summaries are machine-written, so check the linked articles before relying on them.

Yes. It shows aggregated public information about countries, markets and infrastructure, not individuals, so it suits journalism, risk analysis and teaching. Each upstream source keeps its own terms, which matter if you republish its data, and if you run a modified copy as a public service the AGPL requires you to publish your source code.


This post is part of GitHub Tools, where every repository is cloned and scanned before it is written up. The scan is a snapshot of one commit on one day; the repository has moved on since, so check it before you install.