| # Changelog |
|
|
| All notable changes to World Monitor are documented here. |
|
|
| ## [Unreleased] |
|
|
| ### Changed |
|
|
| - **Corporate intelligence is live; `get-company-enrichment` and |
| `list-company-signals` are no longer deprecated** (#5695). Both |
| `/api/intelligence/v1/get-company-enrichment` and |
| `/api/intelligence/v1/list-company-signals` previously returned an empty stub |
| (disabled in #3777 because the old domain-slug attribution heuristic |
| fabricated company intelligence — issues #3754/#3755). They now aggregate real |
| data behind authoritative SEC ticker→CIK resolution: EDGAR identity and recent |
| filings, Finnhub market profile and earnings surprises, and recent news |
| mentions. A company that does not resolve in the SEC registry returns an empty |
| envelope with `sources: []` rather than a guess. Verified lookup keys are |
| `ticker` and `name` — a `name` resolves solely when it identifies exactly one |
| filer. The legacy `domain` request field remains deprecated for v1 wire |
| compatibility and returns the same empty, non-attributing stub as before. |
| Legacy response fields `github`, `techStack`, `hackerNewsMentions`, and |
| `company.founded` likewise remain deprecated and empty. |
| **Added:** `market`, `earningsSurprises`, `newsMentions`, `company.cik`, |
| `company.ticker`, filing `url`/`items` on enrichment; `cik` on signals (empty |
| CIK distinguishes "no such company" from "resolved but quiet"); a `ticker` |
| query param on both. Two new endpoints ship alongside: |
| `/api/intelligence/v1/search-sec-filings` (EDGAR full-text search) and |
| `/api/intelligence/v1/list-material-events` (market-wide 8-K material-event |
| stream). All four are exposed through the new `get_company_intelligence` MCP |
| tool. Earnings surprises remain enrichment facts; they are not emitted as |
| time-ordered signals because Finnhub's `period` is a fiscal period end, not an |
| authoritative report timestamp. |
|
|
| - **CII formula `v8`** — fixed dead UCDP conflict-floor attribution. The server |
| scorer read non-existent `intensity_level` / `type_of_violence` fields from the |
| cached `conflict:ucdp-events:v1` feed (whose rows actually carry `violenceType`, |
| `deathsBest`, and `dateStart`), so UCDP never applied its war (floor 70) / minor |
| (floor 50) score floor and never counted toward `/api/health.riskScores` |
| realtime signal coverage. UCDP is now classified per country over a 2-year |
| trailing window (war when total deaths > 1000 or event count > 100; minor when |
| event count > 10), matching the frontend `deriveUcdpClassifications` heuristic. |
| The health coverage metric's conflict family is now satisfied by EITHER the |
| ACLED API path OR UCDP, so a thin/empty ACLED window no longer flips health to |
| `COVERAGE_PARTIAL` while UCDP is healthy. `combinedScore` values rise for active |
| UCDP conflict countries (e.g. UA/PK/MX gain a war floor); the risk-score cache |
| key family moved to `risk:scores:sebuf:v8` and emitted `methodology_version` is |
| now `v8`, so clients should re-baseline. |
| - **CII formula `v7`** — score attribution now changes for the second |
| gap-closure batch. Coordinate attribution resolves three-way bbox overlaps |
| before pairwise border rules, so Punggye-ri maps to KP instead of CN and |
| Lublin maps to PL instead of UA; the seed mirror uses the same resolver. |
| Targeted probes also cover Lhasa, Gaziantep, Kermanshah, Quetta/Islamabad, |
| Dammam, Tabuk, Ruili, Belogorsk, north Hokkaido, and Amman. Climate anomaly |
| boosts now cover the producer-emitted `Europe`, `East Asia`, and |
| `Latin America` zones, restoring KP/KR/JP/PL/DE/FR/GB/VE coverage, and future |
| unknown zones can fall back through anomaly coordinates when available. |
| `combinedScore` values may shift for affected records, the risk-score cache |
| key family moved to `risk:scores:sebuf:v7`, and emitted |
| `methodology_version` is now `v7`; clients should re-baseline. Remaining |
| coordinate attribution is still a rectangular approximation, not full |
| point-in-polygon border geometry. |
| - **CII formula `v6`** — score attribution now changes as one batch for country |
| text, coordinate, and climate anomaly inputs. The seed and server country |
| resolvers now agree on token/phrase matching, Rio Grande US/MX border |
| segments, the western Korean DMZ, RU/UA, IN/PK, CN/RU, and RU/JP bbox |
| overlaps; `north korean` maps to KP and `taiwanese` maps to TW while bare |
| `korean` remains ambiguous. Climate anomalies now feed the emitted producer |
| zones (`Ukraine`, `California`, `Amazon`, `Taiwan Strait`, `Caribbean`, etc.) |
| into CII and translate enum severities into the numeric climate boost scale. |
| `combinedScore` values may shift for affected records, the risk-score cache |
| key family moved to `risk:scores:sebuf:v6`, and emitted |
| `methodology_version` is now `v6`; clients should re-baseline. |
| - **CII formula `v5`** — `dynamicScore` is now a signed movement delta in the |
| range `-100..100`, derived from a valid CII snapshot from approximately 24 |
| hours earlier. Positive values mean rising risk, negative values mean falling |
| risk, and `0` means stable or no valid prior snapshot. The browser fallback |
| trend deadband now matches the server rule (`> 1` / `< -1`, an effective |
| two-point threshold for whole-point scores). `combinedScore` coefficients are |
| unchanged, the risk-score cache key family moved to `risk:scores:sebuf:v5`, |
| and emitted `methodology_version` is now `v5`; clients that previously treated |
| `dynamicScore` as a non-negative live score should re-baseline. |
| - **CII methodology `v4`** — country attribution and source inputs were tightened |
| for the Composite Instability Index. Text attribution now uses token |
| exact-match / phrase matching, known bounding-box overlaps route through |
| explicit heuristics, the earthquake seed uses the USGS 4.5-week |
| (`4.5_week`) feed, and sanctions scoring reads the full country-count map. |
| Client impact: `combinedScore` values may shift for affected countries, the |
| risk-score cache key family moved to `risk:scores:sebuf:v4`, and emitted |
| `methodology_version` is now `v4`. |
| - **CII formula `v3`** — conflict event activity now uses log-scaled calibration before the final component cap, preserving distance between moderate and extreme event volumes. The browser CII path now matches the server displacement log-ramp instead of the old `+4/+8` tiers, and browser news-alert pressure is no longer amplified by per-country `eventMultiplier`. Public `combinedScore` values may shift and `methodology_version` is bumped `v2` → `v3`; clients pinned on it should re-baseline. See `docs/methodology/cii-risk-scores.mdx` (#2457, methodology portions of #3726). |
| - **CII weights source of truth (#3789, foundation for #2457)** — `baselineRisk` and `eventMultiplier` now come from one shared coefficient table used by both server-side risk scoring and frontend client-side CII rendering. The previous 7-country frontend/server drift was resolved to the published server/API values for AF, EG, IQ, JP, KR, LB and QA. Server/API values were unchanged by this refactor — the v3 `methodology_version` bump above is from the calibration changes, not this source-of-truth move. |
| - **CII formula `v2`** — the Composite Instability Index `Security` component now scores military flights, military vessels and aviation disruptions in addition to GPS jamming (previously GPS-jamming-only — issue #3738). The composite blend gains `newsUrgencyBoost`, `earthquakeBoost`, `sanctionsBoost` and an AIS-disruption boost; `cyberBoost`/`fireBoost` are now severity-weighted. Public `combinedScore` values shift accordingly and `methodology_version` is bumped `v1` → `v2` — clients pinned on it should re-baseline. See `docs/methodology/cii-risk-scores.mdx` (#3864). |
|
|
| ### Added |
|
|
| - **Pro Business tier ($49.99/month · $499/year, #5604)** — a commercial-license plan between Pro and the API plans, covering client work, internal tools and reporting. Dashboard data export (CSV, JSON, and a new PDF report) is now an entitlement of the plans that carry data export — Pro Business, both API plans, and Enterprise — instead of being open to every visitor; the export control stays visible and explains what unlocks it. Dashboard tabs are capped when a tab is created (3 Free / 10 Pro / 25 Pro Business) and existing tabs are never pruned. MCP + SDK daily quotas are now plan-driven (50/day on Pro, 250/day on Pro Business). `/pro` moves to a five-plan grid with a personal/commercial axis and an Enterprise band. Pro and Pro Business are separate products, so a Pro subscriber upgrades by cancelling in the billing portal and starting the Pro Business checkout — the app walks through both steps. |
| - **Global inflation (all countries)** — the Consumer Prices panel gains a **World** tab surfacing IMF WEO official annual CPI inflation for every reporting economy (~195 countries), sorted highest-first, leading with year-over-year period-average inflation and an end-of-period secondary column, plus a country filter and severity colour bands. Reuses the already-hydrated `imfMacro` bootstrap bundle (no new network). Discoverable via CMD+K — typing "inflation", "global inflation", or "inflation by country" lands directly on the tab through the new `panel:consumer-prices@world` deep-link command. |
| - **Unified OpenAPI bundle** — `docs/api/worldmonitor.openapi.yaml` is now emitted alongside per-service specs, merging every WorldMonitor RPC into a single OpenAPI 3.1 document (190 operations). Powered by sebuf v0.11.1's origin-level bundle support ([SebastienMelki/sebuf#158](https://github.com/SebastienMelki/sebuf/issues/158)). Bumps `SEBUF_VERSION` in the Makefile from v0.7.0 to v0.11.1 — rerun `make install-plugins` after pulling. |
| - **Route Explorer**: standalone full-screen modal (CMD+K) for planning shipments between any two countries. Includes Current/Alternatives/Land/Impact tabs, keyboard-first navigation, URL state sharing, strategic-product trade data, dependency flags, and free-tier blur with public route highlight (#2980, #2982, #2994, #2996, #2997, #2998) |
| - US Treasury customs revenue in Trade Policy panel with monthly data, FYTD year-over-year comparison, and revenue spike highlighting (#1663) |
| - Security advisories gold standard migration: Railway cron seed fetches 24 government RSS feeds hourly, Vercel reads Redis only (#1637) |
| - CMD+K full panel coverage: all 55 panels now searchable (was 31), including AI forecasts, correlation panels, webcams, displacement, security advisories (#1656) |
| - Chokepoint transit intelligence with 3 free data sources: IMF PortWatch (vessel transit counts), CorridorRisk (risk intelligence), AISStream (24h crossing counter) (#1560) |
| - 13 monitored chokepoints (was 6): added Cape of Good Hope, Gibraltar, Bosporus Strait (absorbs Dardanelles), Korea, Dover, Kerch, Lombok (#1560, #1572) |
| - Expandable chokepoint cards with TradingView lightweight-charts 180-day time-series (tanker vs cargo) (#1560) |
| - Real-time transit counting with enter+dwell+exit crossing detection, 30min cooldown (#1560) |
| - PortWatch, CorridorRisk, and transit seed loops on Railway relay (#1560) |
| - R2 trace storage for forecast debugging with Cloudflare API upload (#1655) |
|
|
| ### Changed |
|
|
| - **Sebuf API migration (#3207)** — scenario + supply-chain endpoints migrated to the typed sebuf contract. RPC URLs now derive from method names; the five renamed v1 URLs remain live as thin aliases so existing integrations keep working: |
| - `/api/scenario/v1/run` → `/api/scenario/v1/run-scenario` |
| - `/api/scenario/v1/status` → `/api/scenario/v1/get-scenario-status` |
| - `/api/scenario/v1/templates` → `/api/scenario/v1/list-scenario-templates` |
| - `/api/supply-chain/v1/country-products` → `/api/supply-chain/v1/get-country-products` |
| - `/api/supply-chain/v1/multi-sector-cost-shock` → `/api/supply-chain/v1/get-multi-sector-cost-shock` |
|
|
| Aliases will retire at the next v1→v2 break ([#3282](https://github.com/koala73/worldmonitor/issues/3282)). |
|
|
| - `POST /api/scenario/v1/run-scenario` now returns `200 OK` instead of the pre-migration `202 Accepted` on successful enqueue. sebuf's HTTP annotations don't support per-RPC status codes. Branch on response body `status === "pending"` instead of `response.status === 202`. `statusUrl` is preserved. |
|
|
| ### Security |
|
|
| - CDN-Cache-Control header now only set for trusted origins (worldmonitor.app, Vercel previews, Tauri); no-origin server-side requests always reach the edge function so `validateApiKey` can run, closing a potential cache-bypass path for external scrapers |
| - **Shipping v2 webhook tenant isolation (#3242)** — `POST /api/v2/shipping/webhooks` (register) and `GET /api/v2/shipping/webhooks` (list) now enforce `validateApiKey(req, { forceKey: true })`, matching the sibling `[subscriberId]{,/[action]}` routes and the documented contract in `docs/api-shipping-v2.mdx`. Without this gate, a Clerk-authenticated pro user with no API key would fall through `callerFingerprint()` to the shared `'anon'` bucket and see/overwrite webhooks owned by other `'anon'`-bucket tenants. |
|
|
| ### Fixed |
|
|
| - Trade Policy panel WTO gate changed from panel-wide to per-tab, so Revenue tab works on desktop without WTO API key (#1663) |
| - Conflict-intel seed succeeds without ACLED credentials by accepting empty events when humanitarian/PizzINT data is available (#1651) |
| - Seed-forecasts crash from top-level @aws-sdk/client-s3 import resolved with lazy dynamic import (#1654) |
| - Bootstrap desktop timeouts restored (5s/8s) while keeping aggressive web timeouts (1.2s/1.8s) (#1653) |
| - Service worker navigation reverted to NetworkOnly to prevent stale HTML caching on deploy (#1653) |
| - Railway seed watch paths fixed for 5 services (seed-insights, seed-unrest-events, seed-prediction-markets, seed-infra, seed-gpsjam) |
| - PortWatch ArcGIS URL, field names, and chokepoint name mappings (#1572) |
|
|
| ## [2.6.1] - 2026-03-11 |
|
|
| ### Highlights |
|
|
| - **Blog Platform** — Astro-powered blog at /blog with 16 SEO-optimized posts, OG images, and site footer (#1401, #1405, #1409) |
| - **Country Intelligence** — country facts section with right-click context menu (#1400) |
| - **Satellite Imagery Overhaul** — globe-native rendering, outline-only polygons, CSP fixes (#1381, #1385, #1376) |
|
|
| ### Added |
|
|
| - Astro blog at /blog with 16 SEO posts and build integration (#1401, #1403) |
| - Blog redesign to match /pro page design system (#1405) |
| - Blog SEO, OG images, favicon fix, and site footer (#1409) |
| - Country facts section and right-click context menu for intel panel (#1400) |
| - Satellite imagery panel enabled in orbital surveillance layer (#1375) |
| - Globe-native satellite imagery, removed sidebar panel (#1381) |
| - Layer search filter with synonym support (#1369) |
| - Close buttons on panels and Add Panel block (#1354) |
| - Enterprise contact form endpoint (#1365) |
| - Commodity and happy variants shown on all header versions (#1407) |
|
|
| ### Fixed |
|
|
| - NOTAM closures merged into Aviation layer (#1408) |
| - Intel deep dive layout reordered, duplicate headlines removed (#1404) |
| - Satellite imagery outline-only polygons to eliminate alpha stacking blue tint (#1385) |
| - Enterprise form hardened with mandatory fields and lead qualification (#1382) |
| - Country intel silently dismisses when geocode cannot identify a country (#1383) |
| - Globe hit targets enlarged for small marker types (#1378) |
| - Imagery panel hidden for existing users and viewport refetch deadlock (#1377) |
| - CSP violations for satellite preview images (#1376) |
| - Safari TypeError filtering and Sentry noise patterns (#1380) |
| - Swedish locale 'avbruten' TypeError variant filtered (#1402) |
| - Satellite imagery STAC backend fix, merged into Orbital Surveillance (#1364) |
| - Aviation "Computed" source replaced with specific labels, reduced cache TTLs (#1374) |
| - Close button and hover-pause on all marker tooltips (#1371) |
| - Invalid 'satelliteImagery' removed from LAYER_SYNONYMS (#1370) |
| - Risk scores seeding gap and seed-meta key mismatch (#1366) |
| - Consistent LIVE header pattern across news and webcams panels (#1367) |
| - Globe null guards in path accessor callbacks (#1372) |
| - Node_modules guard in pre-push hook, pinned Node 22 (#1368) |
| - Typecheck CI workflow: removed paths-ignore, added push trigger (#1373) |
| - Theme toggle removed from header (#1407) |
|
|
| ## [2.6.0] - 2026-03-09 |
|
|
| ### Highlights |
|
|
| - **Orbital Surveillance** — real-time satellite tracking layer with TLE propagation (#1278) |
| - **Premium Finance Suite** — stock analysis tools for Pro tier (#1268) |
| - **Self-hosted Basemap** — migrated from CARTO to PMTiles on Cloudflare R2 (#1064) |
| - **GPS Jamming v2** — migrated from gpsjam.org to Wingbits API with H3 hexagons (#1240) |
| - **Military Flights Overhaul** — centralized via Redis seed + edge handler with OpenSky/Wingbits fallbacks (#1263, #1274, #1275, #1276) |
| - **Pro Waitlist & Landing Page** — referral system, Turnstile CAPTCHA, 21-language localization (#1140, #1187) |
| - **Server-side AI Classification** — batch headline classification moves from client to server (#1195) |
| - **Commodity Variant** — new app variant focused on commodities with relevant panels & layers (#1040, #1100) |
| - **Health Check System** — comprehensive health endpoint with auto seed-meta freshness tracking (#1091, #1127, #1128) |
|
|
| ### Added |
|
|
| - Orbital surveillance layer with real-time satellite tracking via satellite.js (#1278, #1281) |
| - Premium finance stock analysis suite for Pro tier (#1268) |
| - GPS jamming migration to Wingbits API with H3 hex grid (#1240) |
| - Commodity app variant with dedicated panels and map layers (#1040, #1100) |
| - Pro waitlist landing page with referral system and Turnstile CAPTCHA (#1140) |
| - Pro landing page localization — 21 languages (#1187) |
| - Pro page repositioning toward markets, macro & geopolitics (#1261) |
| - Referral invite banner when visiting via `?ref=` link (#1232) |
| - Server-side batch AI classification for news headlines (#1195) |
| - Self-hosted PMTiles basemap on Cloudflare R2, replacing CARTO (#1064) |
| - Per-provider map theme selector (#1101) |
| - Globe visual preset setting (Earth / Cosmos) with texture selection (#1090, #1076) |
| - Comprehensive health check endpoint for UptimeRobot (#1091) |
| - Auto seed-meta freshness tracking for all RPC handlers (#1127) |
| - Submarine cables expanded to 86 via TeleGeography API (#1224) |
| - Pak-Afghan conflict zone and country boundary override system (#1150) |
| - Sudan and Myanmar conflict zone polygon improvements (#1216) |
| - Iran events: 28 new location coords, 48h TTL (#1251) |
| - Tech HQs in Ireland data (#1244) |
| - BIS data seed job (#1131) |
| - CoinPaprika fallback for crypto/stablecoin data (#1092) |
| - Rudaw TV live stream and RSS feed (#1117) |
| - Dubai and Riyadh added to default airport watchlist (#1144) |
| - Cmd+K: 16 missing layer toggles (#1289), "See all commands" link with category list (#1270) |
| - UTM attribution tags on all outbound links (#1233) |
| - Performance warning dialog replaces hard layer limit (#1088) |
| - Unified error/retry UX with muted styling and countdown (#1115) |
| - Settings reorganized into collapsible groups (#1110) |
| - Reset Layout button with tooltip (#1267, #1250) |
| - Markdown lint in pre-push hook (#1166) |
|
|
| ### Changed |
|
|
| - Military flights centralized via Redis seed + edge handler pattern (#1263) |
| - Military flights seed with OpenSky anonymous fallback + Wingbits fallback (#1274, #1275) |
| - Theater posture computed directly in relay instead of pinging Vercel RPC (#1259) |
| - Countries GeoJSON served from R2 CDN (#1164) |
| - Consolidated duplicated market data lists into shared JSON configs (#1212) |
| - Eliminate all frontend external API calls — enforce gold standard pattern (#1217) |
| - WB indicators seeded on Railway, never called from frontend (#1159, #1157) |
| - Temporal baseline for news + fires moved to server-side (#1194) |
| - Panel creation guarded by variant config (#1221) |
| - Panel tab styles unified to underline pattern across all panels (#1106, #1182, #1190, #1192) |
| - Reduce default map layers (#1141) |
| - Share dialog dismissals persist across subdomains via cookies (#1286) |
| - Country-wide conflict zones use actual country geometry (#1245) |
| - Aviation seed interval reduced to 1h (#1258) |
| - Replace curl with native Node.js HTTP CONNECT tunnel in seeds (#1287) |
| - Seed scripts use `_seed-utils.mjs` shared configs from `scripts/shared/` (#1231, #1234) |
|
|
| ### Fixed |
|
|
| - **Rate Limiting**: prioritize `cf-connecting-ip` over `x-real-ip` for correct per-user rate limiting behind CF proxy (#1241) |
| - **Security**: harden cache keys against injection and hash collision (#1103), per-endpoint rate limits for summarize endpoints (#1161) |
| - **Map**: prevent ghost layers rendering without a toggle (#1264), DeckGL layer toggles getting stuck (#1248), auto-fallback to OpenFreeMap on basemap failure (#1109), CORS fallback for Carto basemap (#1142), use CORS-enabled R2 URL for PMTiles in Tauri (#1119), CII Instability layer disabled in 3D mode (#1292) |
| - **Layout**: reconcile ultrawide zones when map is hidden (#1246), keep settings button visible on scaled desktop widths (#1249), exit fullscreen before switching variants (#1253), apply map-hidden layout class on initial load (#1087), preserve panel column position across refresh (#1170, #1108, #1112) |
| - **Panels**: event delegation to survive setContent debounce (#1203), guard RPC response array access with optional chaining (#1174), clear stuck error headers and sanitize error messages (#1175), lazy panel race conditions + server feed gaps (#1113), Tech Readiness panel loading on full variant (#1208), Strategic Risk panel button listeners (#1214), World Clock green home row (#1202), Airline Intelligence CSS grid layouts (#1197) |
| - **Pro/Turnstile**: explicit rendering to fix widget race condition (#1189), invisible widget support (#1215), CSP allow Turnstile (#1155), handle `already_registered` state (#1183), reset on enterprise form error (#1222), registration feedback and referral code gen (#1229, #1228), no-cache header for /pro (#1179), correct API endpoint path (#1177), www redirect loop fix (#1198, #1201) |
| - **SEO**: comprehensive improvements for /pro and main pages (#1271) |
| - **Railway**: remove custom railpack.json install step causing ENOENT builds (#1296, #1290, #1288) |
| - **Aviation**: correct cancellation rate calculation and add 12 airports (#1209), unify NOTAM status logic (#1225) |
| - **Sentry**: triage 26 issues, fix 3 bugs, add 29 noise filters (#1173, #1098) |
| - **Health**: treat missing seed-meta as stale (#1128), resolve BIS credit and theater posture warnings (#1124), add WB seed loop (#1239), UCDP auth handling (#1252) |
| - **Country Brief**: formatting, duplication, and news cap fixes (#1219), prevent modal stuck on geocode failure (#1134) |
| - **Economic**: guard BIS and spending data against undefined (#1162, #1169) |
| - **Webcams**: detect blocked YouTube embeds on web (#1107), use iframe load event fallback (#1123), MTV Lebanon as live stream (#1122) |
| - **Desktop**: recover stranded routing fixes and unified error UX (#1160), DRY debounce, error handling, retry cap (#1084), debounce cache writes, batch secret push, lazy panels (#1077) |
| - **PWA**: bump SW nuke key to v2 for CF-cached 404s (#1081), one-time SW nuke on first visit (#1079) |
| - **Performance**: only show layer warning when adding layers, not removing (#1265), reduce unnecessary Vercel edge invocations (#1176) |
| - **i18n**: sync all 20 locales to en.json — zero drift (#1104), correct indentation for geocode error keys (#1147) |
| - **Insights**: graceful exit, LKG fallback, swap to Gemini 2.5 Flash (#1153, #1154) |
| - **Seeds**: prevent API quota burn and respect rate limits (#1167), gracefully skip write when validation fails (#1089), seed-meta tracking for all bootstrap keys (#1163, #1138) |
|
|
| ## [2.5.25] - 2026-03-04 |
|
|
| ### Changed |
|
|
| - **Supply Chain v2** — bump chokepoints & minerals cache keys to v2; add `aisDisruptions` field to `ChokepointInfo` (proto, OpenAPI, generated types, handler, UI panel); rename Malacca Strait → Strait of Malacca; reduce chokepoint Redis TTL from 15 min to 5 min; expand description to always show warning + AIS disruption counts; remove Nickel & Copper from critical minerals data (focus on export-controlled minerals); slice top producers to 3; use full FRED series names for shipping indices; add `daily` cache tier (86400s) and move minerals route to it; align client-side circuit breaker TTLs with server TTLs; fix upstream-unavailable banner to only show when no data is present; register supply-chain routes in Vite dev server plugin |
| - **Cache migration**: old `supply_chain:chokepoints:v1` and `supply_chain:minerals:v1` Redis keys are no longer read by any consumer — they will expire via TTL with no action required |
|
|
| ## [2.5.24] - 2026-03-03 |
|
|
| ### Highlights |
|
|
| - **UCDP conflict data** — integrated Uppsala Conflict Data Program for historical & ongoing armed conflict tracking (#760) |
| - **Country brief sharing** — maximize mode, shareable URLs, native browser share button, expanded sections (#743, #854) |
| - **Unified Vercel deployment** — consolidated 4 separate deployments into 1 via runtime variant detection (#756) |
| - **CDN performance overhaul** — POST→GET conversion, per-domain edge functions, tiered bootstrap for ~46% egress reduction (#753, #795, #838) |
| - **Security hardening** — CSP script hashes replace unsafe-inline, crypto.randomUUID() for IDs, XSS-safe i18n, Finnhub token header (#781, #844, #861, #744) |
| - **i18n expansion** — French support with Live TV channels, hardcoded English strings replaced with translation keys (#794, #851, #839) |
|
|
| ### Added |
|
|
| - UCDP (Uppsala Conflict Data Program) integration for armed conflict tracking (#760) |
| - Iran & Strait of Hormuz conflict zones, upgraded Ukraine polygon (#731) |
| - 100 Iran war events seeded with expanded geocoder (#792) |
| - Country brief maximize mode, shareable URLs, expanded sections & i18n (#743) |
| - Native browser share button for country briefs (#854) |
| - French i18n support with French Live TV channels (#851) |
| - Geo-restricted live channel support, restored WELT (#765) |
| - Manage Channels UX — toggle from grid + show all channels (#745) |
| - Command palette: disambiguate Map vs Panel commands, split country into map/brief (#736) |
| - Command palette: rotating contextual tips replace static empty state (#737) |
| - Download App button for web users with dropdown (#734, #735) |
| - Reset layout button to restore default panel sizes and order (#801) |
| - System status moved into settings (#735) |
| - Vercel cron to pre-warm AviationStack cache (#776) |
| - Runtime variant detection — consolidate 4 Vercel deployments into 1 (#756) |
| - CJS syntax check in pre-push hook (#769) |
|
|
| ### Fixed |
|
|
| - **Security**: XSS — wrap `t()` calls in `escapeHtml()` (#861), use `crypto.randomUUID()` instead of `Math.random()` for ID generation (#844), move Finnhub API key from query string to `X-Finnhub-Token` header (#744) |
| - **i18n**: replace hardcoded English strings with translation keys (#839), i18n improvements (#794) |
| - **Market**: parse comma-separated query params and align Railway cache keys (#856), Railway market data cron + complete missing tech feed categories (#850), Yahoo relay fallback + RSS digest relay for blocked feeds (#835), tech UNAVAILABLE feeds + Yahoo batch early-exit + sector heatmap gate (#810) |
| - **Aviation**: move AviationStack fetching to Railway relay, reduce to 40 airports (#858) |
| - **UI**: cancel pending debounced calls on component destroy (#848), guard async operations against stale DOM references (#843) |
| - **Sentry**: guard stale DOM refs, audio.play() compat, add 16 noise filters (#855) |
| - **Relay**: exponential backoff for failing RSS feeds (#853), deduplicate UCDP constants crashing Railway container (#766) |
| - **API**: remove `[domain]` catch-all that intercepted all RPC routes (#753 regression) (#785), pageSize bounds validation on research handlers (#819), return 405 for wrong HTTP method (#757), pagination cursor for cyber threats (#754) |
| - **Conflict**: bump Iran events cache-bust to v7 (#724) |
| - **OREF**: prevent LLM translation cache from poisoning Hebrew→English pipeline (#733), strip translation labels from World Brief input (#768) |
| - **Military**: harden USNI fleet report ship name regex (#805) |
| - **Sidecar**: add required params to ACLED API key validation probe (#804) |
| - **Macro**: replace hardcoded BTC mining thresholds with Mayer Multiple (#750) |
| - **Cyber**: reduce GeoIP per-IP timeout from 3s to 1.5s (#748) |
| - **CSP**: restore unsafe-inline for Vercel bot-challenge pages (#788), add missing script hash and finance variant (#798) |
| - **Runtime**: route all /api/* calls through CDN edge instead of direct Vercel (#780) |
| - **Desktop**: detect Linux node target from host arch (#742), harden Windows installer update path + map resize (#739), close update toast after clicking download (#738), only open valid http(s) links externally (#723) |
| - **Webcams**: replace dead Tel Aviv live stream (#732), replace stale Jerusalem feed (#849) |
| - Story header uses full domain WORLDMONITOR.APP (#799) |
| - Open variant nav links in same window instead of new tab (#721) |
| - Suppress map renders during resize drag (#728) |
| - Append deduction panel to DOM after async import resolves (#764) |
| - Deduplicate stale-while-revalidate background fetches in CircuitBreaker (#793) |
| - CORS fallback, rate-limit bump, RSS proxy allowlist (#814) |
| - Unavailable stream error messages updated (#759) |
|
|
| ### Performance |
|
|
| - Tier slow/fast bootstrap data for ~46% CDN egress reduction (#838) |
| - Convert POST RPCs to GET for CDN caching (#795) |
| - Split monolithic edge function into per-domain functions (#753) |
| - Increase CDN cache TTLs + add stale-if-error across edge functions (#777) |
| - Bump CDN cache TTLs for oref-alerts and youtube/live (#791) |
| - Skip wasted direct fetch for Vercel-blocked domains in RSS proxy (#815) |
|
|
| ### Security |
|
|
| - Replace CSP unsafe-inline with script hashes and add trust signals (#781) |
| - Expand Permissions-Policy and tighten CSP connect-src (#779) |
|
|
| ### Changed |
|
|
| - Extend support for larger screens (#740) |
| - Green download button + retire sliding popup (#747) |
| - Extract shared relay helper into `_relay.js` (#782) |
| - Consolidate `SummarizeArticleResponse` status fields (#813) |
| - Consolidate `declare const process` into shared `env.d.ts` (#752) |
| - Deduplicate `clampInt` into `server/_shared/constants` |
| - Add error logging for network errors in error mapper (#746) |
| - Redis error logging + reduced timeouts for edge functions (#749) |
|
|
| --- |
|
|
| ## [2.5.21] - 2026-03-01 |
|
|
| ### Highlights |
|
|
| - **Iran Attacks map layer** — conflict events with severity badges, related event popups, and CII integration (#511, #527, #547, #549) |
| - **Telegram Intel panel** — 27 curated OSINT channels via MTProto relay (#550) |
| - **OREF Israel Sirens** — real-time alerts with Hebrew→English translation and 24h history bootstrap (#545, #556, #582) |
| - **GPS/GNSS jamming layer** — detection overlay with CII integration (#570) |
| - **Day/night terminator** — solar terminator overlay on map (#529) |
| - **Breaking news alert banner** — audio alerts for critical/high RSS items with cooldown bypass (#508, #516, #533) |
| - **AviationStack integration** — global airport delays for 128 airports with NOTAM closure detection (#552, #581, #583) |
| - **Strategic risk score** — theater posture + breaking news wired into scoring algorithm (#584) |
|
|
| ### Added |
|
|
| - Iran Attacks map layer with conflict event popups, severity badges, and priority rendering (#511, #527, #549) |
| - Telegram Intel panel with curated OSINT channel list (#550, #600) |
| - OREF Israel Sirens panel with Hebrew-to-English translation (#545, #556) |
| - OREF 24h history bootstrap on relay startup (#582) |
| - GPS/GNSS jamming detection map layer + CII integration (#570) |
| - Day/night solar terminator overlay (#529) |
| - Breaking news active alert banner with audio for critical/high items (#508) |
| - AviationStack integration for non-US airports + NOTAM closure detection (#552, #581, #583) |
| - RT (Russia Today) HLS livestream + RSS feeds (#585, #586) |
| - Iran webcams tab with 4 feeds (#569, #572, #601) |
| - CBC News optional live channel (#502) |
| - Strategic risk score wired to theater posture + breaking news (#584) |
| - CII scoring: security advisories, Iran strikes, OREF sirens, GPS jamming (#547, #559, #570, #579) |
| - Country brief + CII signal coverage expansion (#611) |
| - Server-side military bases with 125K+ entries + rate limiting (#496) |
| - AVIATIONSTACK_API key in desktop settings (#553) |
| - Iran events seed script and latest data (#575) |
| |
| ### Fixed |
| |
| - **Aviation**: stale IndexedDB cache invalidation + reduced CDN TTL (#607), broken lock replaced with direct cache + cancellation tiers (#591), query all airports instead of rotating batch (#557), NOTAM routing through Railway relay (#599), always show all monitored airports (#603) |
| - **Telegram**: AUTH_KEY_DUPLICATED fixes — latch to stop retry spam (#543), 60s startup delay (#587), graceful shutdown + poll guard (#562), ESM import path fixes (#537, #542), missing relay auth headers (#590) |
| - **Relay**: Polymarket OOM prevention — circuit breaker + concurrency limiter (#519), request deduplication (#513), queue backpressure + response slicing (#593), cache stampede fix (#592), kill switch (#523); smart quotes crash (#563); graceful shutdown (#562, #565); curl for OREF (#546, #567, #571); maxBuffer ENOBUFS (#609); rsshub.app blocked (#526); ERR_HTTP_HEADERS_SENT guard (#509); Telegram memory cleanup (#531) |
| - **Live news**: 7 stale YouTube fallback IDs replaced (#535, #538), broken Europe channel handles (#541), eNCA handle + VTC NOW removal + CTI News (#604), RT HLS recovery (#610), YouTube proxy auth alignment (#554, #555), residential proxy + gzip for detection (#551) |
| - **Breaking news**: critical alerts bypass cooldown (#516), keyword gaps filled (#517, #521), fake pubDate filter (#517), SESSION_START gate removed (#533) |
| - **Threat classifier**: military/conflict keyword gaps + news-to-conflict bridge (#514), Groq 429 stagger (#520) |
| - **Geo**: tokenization-based matching to prevent false positives (#503), 60+ missing locations in hub index (#528) |
| - **Iran**: CDN cache-bust pipeline v4 (#524, #532, #544), read-only handler (#518), Gulf misattribution via bbox disambiguation (#532) |
| - **CII**: Gulf country strike misattribution (#564), compound escalation for military action (#548) |
| - **Bootstrap**: 401/429 rate limiting fix (#512), hydration cache + polling hardening (#504) |
| - **Sentry**: guard YT player methods + GM/InvalidState noise (#602), Android OEM WebView bridge injection (#510), setView invalid preset (#580), beforeSend null-filename leak (#561) |
| - Rate limiting raised to 300 req/min sliding window (#515) |
| - Vercel preview origin regex generalized + bases cache key (#506) |
| - Cross-env for Windows-compatible npm scripts (#499) |
| - Download banner repositioned to bottom-right (#536) |
| - Stale/expired Polymarket markets filtered (#507) |
| - Cyber GeoIP centroid fallback jitter made deterministic (#498) |
| - Cache-control headers hardened for polymarket and rss-proxy (#613) |
| |
| ### Performance |
| |
| - Server-side military base fetches: debounce + static edge cache tier (#497) |
| - RSS: refresh interval raised to 10min, cache TTL to 20min (#612) |
| - Polymarket cache TTL raised to 10 minutes (#568) |
| |
| ### Changed |
| |
| - Stripped 61 debug console.log calls from 20 service files (#501) |
| - Bumped version to 2.5.21 (#605) |
| |
| --- |
| |
| ## [2.5.20] - 2026-02-27 |
| |
| ### Added |
| |
| - **Edge caching**: Complete Cloudflare edge cache tier coverage with degraded-response policy (#484) |
| - **Edge caching**: Cloudflare edge caching for proxy.worldmonitor.app (#478) and api.worldmonitor.app (#471) |
| - **Edge caching**: Tiered edge Cache-Control aligned to upstream TTLs (#474) |
| - **API migration**: Convert 52 API endpoints from POST to GET for edge caching (#468) |
| - **Gateway**: Configurable VITE_WS_API_URL + harden POST-to-GET shim (#480) |
| - **Cache**: Negative-result caching for cachedFetchJson (#466) |
| - **Security advisories**: New panel with government travel alerts (#460) |
| - **Settings**: Redesign settings window with VS Code-style sidebar layout (#461) |
|
|
| ### Fixed |
|
|
| - **Commodities panel**: Was showing stocks instead of commodities — circuit breaker SWR returned stale data from a different call when cacheTtlMs=0 (#483) |
| - **Analytics**: Use greedy regex in PostHog ingest rewrites (#481) |
| - **Sentry**: Add noise filters for 4 unresolved issues (#479) |
| - **Gateway**: Convert stale POST requests to GET for backwards compat (#477) |
| - **Desktop**: Enable click-to-play YouTube embeds + CISA feed fixes (#476) |
| - **Tech variant**: Use rss() for CISA feed, drop build from pre-push hook (#475) |
| - **Security advisories**: Route feeds through RSS proxy to avoid CORS blocks (#473) |
| - **API routing**: Move 5 path-param endpoints to query params for Vercel routing (#472) |
| - **Beta**: Eagerly load T5-small model when beta mode is enabled |
| - **Scripts**: Handle escaped apostrophes in feed name regex (#455) |
| - **Wingbits**: Add 5-minute backoff on /v1/flights failures (#459) |
| - **Ollama**: Strip thinking tokens, raise max_tokens, fix panel summary cache (#456) |
| - **RSS/HLS**: RSS feed repairs, HLS native playback, summarization cache fix (#452) |
| |
| ### Performance |
| |
| - **AIS proxy**: Increase AIS snapshot edge TTL from 2s to 10s (#482) |
| |
| --- |
| |
| ## [2.5.10] - 2026-02-26 |
| |
| ### Fixed |
| |
| - **Yahoo Finance rate-limit UX**: Show "rate limited — retrying shortly" instead of generic "Failed to load" on Markets, ETF, Commodities, and Sector panels when Yahoo returns 429 (#407) |
| - **Sequential Yahoo calls**: Replace `Promise.all` with staggered batching in commodity quotes, ETF flows, and macro signals to prevent 429 rate limiting (#406) |
| - **Sector heatmap Yahoo fallback**: Sector data now loads via Yahoo Finance when `FINNHUB_API_KEY` is missing (#406) |
| - **Finnhub-to-Yahoo fallback**: Market quotes route Finnhub symbols through Yahoo when API key is not configured (#407) |
| - **ETF early-exit on rate limit**: Skip retry loop and show rate-limit message immediately instead of waiting 60s (#407) |
| - **Sidecar auth resilience**: 401-retry with token refresh for stale sidecar tokens after restart; `diagFetch` auth helper for settings window diagnostics (#407) |
| - **Verbose toggle persistence**: Write verbose state to writable data directory instead of read-only app bundle on macOS (#407) |
| - **AI summary verbosity**: Tighten prompts to 2 sentences / 60 words max with `max_tokens` reduced from 150 to 100 (#404) |
| - **Settings modal title**: Rename from "PANELS" to "SETTINGS" across all 17 locales (#403) |
| - **Sentry noise filters**: CSS.escape() for news ID selectors, player.destroy guard, 11 new ignoreErrors patterns, blob: URL extension frame filter (#402) |
|
|
| --- |
|
|
| ## [2.5.6] - 2026-02-23 |
|
|
| ### Added |
|
|
| - **Greek (Ελληνικά) locale** — full translation of all 1,397 i18n keys (#256) |
| - **Nigeria RSS feeds** — 5 new sources: Premium Times, Vanguard, Channels TV, Daily Trust, ThisDay Live |
| - **Greek locale feeds** — Naftemporiki, in.gr, iefimerida.gr for Greek-language news coverage |
| - **Brasil Paralelo source** — Brazilian news with RSS feed and source tier (#260) |
|
|
| ### Performance |
|
|
| - **AIS relay optimization** — backpressure queue with configurable watermarks, spatial indexing for chokepoint detection (O(chokepoints) vs O(chokepoints × vessels)), pre-serialized + pre-gzipped snapshot cache eliminating per-request JSON.stringify + gzip CPU (#266) |
|
|
| ### Fixed |
|
|
| - **Vietnam flag country code** — corrected flag emoji in language selector (#245) |
| - **Sentry noise filters** — added patterns for SW FetchEvent, PostHog ingest; enabled SW POST method for PostHog analytics (#246) |
| - **Service Worker same-origin routing** — restricted SW route patterns to same-origin only, preventing cross-origin fetch interception (#247, #251) |
| - **Social preview bot allowlisting** — whitelisted Twitterbot, facebookexternalhit, and other crawlers on OG image assets (#251) |
| - **Windows CORS for Tauri** — allow `http://` origin from `tauri.localhost` for Windows desktop builds (#262) |
| - **Linux AppImage GLib crash** — fix GLib symbol mismatch on newer distros by bundling compatible libraries (#263) |
|
|
| --- |
|
|
| ## [2.5.2] - 2026-02-21 |
|
|
| ### Fixed |
|
|
| - **QuotaExceededError handling** — detect storage quota exhaustion and stop further writes to localStorage/IndexedDB instead of silently failing; shared `markStorageQuotaExceeded()` flag across persistent-cache and utility storage |
| - **deck.gl null.getProjection crash** — wrap `setProps()` calls in try/catch to survive map mid-teardown races in debounced/RAF callbacks |
| - **MapLibre "Style is not done loading"** — guard `setFilter()` in mousemove/mouseout handlers during theme switches |
| - **YouTube invalid video ID** — validate video ID format (`/^[\w-]{10,12}$/`) before passing to IFrame Player constructor |
| - **Vercel build skip on empty SHA** — guard `ignoreCommand` against unset `VERCEL_GIT_PREVIOUS_SHA` (first deploy, force deploy) which caused `git diff` to fail and cancel builds |
| - **Sentry noise filters** — added 7 patterns: iOS readonly property, SW FetchEvent, toLowerCase/trim/indexOf injections, QuotaExceededError |
|
|
| --- |
|
|
| ## [2.5.1] - 2026-02-20 |
|
|
| ### Performance |
|
|
| - **Batch FRED API requests** — frontend now sends a single request with comma-separated series IDs instead of 7 parallel edge function invocations, eliminating Vercel 25s timeouts |
| - **Parallel UCDP page fetches** — replaced sequential loop with Promise.all for up to 12 pages, cutting fetch time from ~96s worst-case to ~8s |
| - **Bot protection middleware** — blocks known social-media crawlers from hitting API routes, reducing unnecessary edge function invocations |
| - **Extended API cache TTLs** — country-intel 12h→24h, GDELT 2h→4h, nuclear 12h→24h; Vercel ignoreCommand skips non-code deploys |
|
|
| ### Fixed |
|
|
| - **Partial UCDP cache poisoning** — failed page fetches no longer silently produce incomplete results cached for 6h; partial results get 10-min TTL in both Redis and memory, with `partial: true` flag propagated to CDN cache headers |
| - **FRED upstream error masking** — single-series failures now return 502 instead of empty 200; batch mode surfaces per-series errors and returns 502 when all fail |
| - **Sentry `Load failed` filter** — widened regex from `^TypeError: Load failed$` to `^TypeError: Load failed( \(.*\))?$` to catch host-suffixed variants (e.g., gamma-api.polymarket.com) |
| - **Tooltip XSS hardening** — replaced `rawHtml()` with `safeHtml()` allowlist sanitizer for panel info tooltips |
| - **UCDP country endpoint** — added missing HTTP method guards (OPTIONS/GET) |
| - **Middleware exact path matching** — social preview bot allowlist uses `Set.has()` instead of `startsWith()` prefix matching |
|
|
| ### Changed |
|
|
| - FRED batch API supports up to 15 comma-separated series IDs with deduplication |
| - Missing FRED API key returns 200 with `X-Data-Status: skipped-no-api-key` header instead of silent empty response |
| - LAYER_TO_SOURCE config extracted from duplicate inline mappings into shared constant |
|
|
| --- |
|
|
| ## [2.5.0] - 2026-02-20 |
|
|
| ### Highlights |
|
|
| **Local LLM Support (Ollama / LM Studio)** — Run AI summarization entirely on your own hardware with zero cloud dependency. The desktop app auto-discovers models from any OpenAI-compatible local inference server (Ollama, LM Studio, llama.cpp, vLLM) and populates a selection dropdown. A 4-tier fallback chain ensures summaries always generate: Local LLM → Groq → OpenRouter → browser-side T5. Combined with the Tauri desktop app, this enables fully air-gapped intelligence analysis where no data leaves your machine. |
|
|
| ### Added |
|
|
| - **Ollama / LM Studio integration** — local AI summarization via OpenAI-compatible `/v1/chat/completions` endpoint with automatic model discovery, embedding model filtering, and fallback to manual text input |
| - **4-tier summarization fallback chain** — Ollama (local) → Groq (cloud) → OpenRouter (cloud) → Transformers.js T5 (browser), each with 5-second timeout before silently advancing to the next |
| - **Shared summarization handler factory** — all three API tiers use identical logic for headline deduplication (Jaccard >0.6), variant-aware prompting, language-aware output, and Redis caching (`summary:v3:{mode}:{variant}:{lang}:{hash}`) |
| - **Settings window with 3 tabs** — dedicated **LLMs** tab (Ollama endpoint/model, Groq, OpenRouter), **API Keys** tab (12+ data source credentials), and **Debug & Logs** tab (traffic log, verbose mode, log file access). Each tab runs an independent verification pipeline |
| - **Consolidated keychain vault** — all desktop secrets stored as a single JSON blob in one OS keychain entry (`secrets-vault`), reducing macOS Keychain authorization prompts from 20+ to exactly 1 on app startup. One-time auto-migration from individual entries with cleanup |
| - **Cross-window secret synchronization** — saving credentials in the Settings window immediately syncs to the main dashboard via `localStorage` broadcast, with no app restart needed |
| - **API key verification pipeline** — each credential is validated against its provider's actual API endpoint. Network errors (timeouts, DNS failures) soft-pass to prevent transient failures from blocking key storage; only explicit 401/403 marks a key invalid |
| - **Plaintext URL inputs** — endpoint URLs (Ollama API, relay URLs, model names) display as readable text instead of masked password dots in Settings |
| - **5 new defense/intel RSS feeds** — Military Times, Task & Purpose, USNI News, Oryx OSINT, UK Ministry of Defence |
| - **Koeberg nuclear power plant** — added to the nuclear facilities map layer (the only commercial reactor in Africa, Cape Town, South Africa) |
| - **Privacy & Offline Architecture** documentation — README now details the three privacy levels: full cloud, desktop with cloud APIs, and air-gapped local with Ollama |
| - **AI Summarization Chain** documentation — README includes provider fallback flow diagram and detailed explanation of headline deduplication, variant-aware prompting, and cross-user cache deduplication |
|
|
| ### Changed |
|
|
| - AI fallback chain now starts with Ollama (local) before cloud providers |
| - Feature toggles increased from 14 to 15 (added AI/Ollama) |
| - Desktop architecture uses consolidated vault instead of per-key keychain entries |
| - README expanded with ~85 lines of new content covering local LLM support, privacy architecture, summarization chain internals, and desktop readiness framework |
|
|
| ### Fixed |
|
|
| - URL and model fields in Settings display as plaintext instead of masked password dots |
| - OpenAI-compatible endpoint flow hardened for Ollama/LM Studio response format differences (thinking tokens, missing `choices` array edge cases) |
| - Sentry null guard for `getProjection()` crash with 6 additional noise filters |
| - PathLayer cache cleared on layer toggle-off to prevent stale WebGL buffer rendering |
|
|
| --- |
|
|
| ## [2.4.1] - 2026-02-19 |
|
|
| ### Fixed |
|
|
| - **Map PathLayer cache**: Clear PathLayer on toggle-off to prevent stale WebGL buffers |
| - **Sentry noise**: Null guard for `getProjection()` crash and 6 additional noise filters |
| - **Markdown docs**: Resolve lint errors in documentation files |
|
|
| --- |
|
|
| ## [2.4.0] - 2026-02-19 |
|
|
| ### Added |
|
|
| - **Live Webcams Panel**: 2x2 grid of live YouTube webcam feeds from global hotspots with region filters (Middle East, Europe, Asia-Pacific, Americas), grid/single view toggle, idle detection, and full i18n support (#111) |
| - **Linux download**: added `.AppImage` option to download banner |
|
|
| ### Changed |
|
|
| - **Mobile detection**: use viewport width only for mobile detection; touch-capable notebooks (e.g. ROG Flow X13) now get desktop layout (#113) |
| - **Webcam feeds**: curated Tel Aviv, Mecca, LA, Miami; replaced dead Tokyo feed; diverse ALL grid with Jerusalem, Tehran, Kyiv, Washington |
|
|
| ### Fixed |
|
|
| - **Le Monde RSS**: English feed URL updated (`/en/rss/full.xml` → `/en/rss/une.xml`) to fix 404 |
| - **Workbox precache**: added `html` to `globPatterns` so `navigateFallback` works for offline PWA |
| - **Panel ordering**: one-time migration ensures Live Webcams follows Live News for existing users |
| - **Mobile popups**: improved sheet/touch/controls layout (#109) |
| - **Intelligence alerts**: disabled on mobile to reduce noise (#110) |
| - **RSS proxy**: added 8 missing domains to allowlist |
| - **HTML tags**: repaired malformed tags in panel template literals |
| - **ML worker**: wrapped `unloadModel()` in try/catch to prevent unhandled timeout rejections |
| - **YouTube player**: optional chaining on `playVideo?.()` / `pauseVideo?.()` for initialization race |
| - **Panel drag**: guarded `.closest()` on non-Element event targets |
| - **Beta mode**: resolved race condition and timeout failures |
| - **Sentry noise**: added filters for Firefox `too much recursion`, maplibre `_layers`/`id`/`type` null crashes |
|
|
| ## [2.3.9] - 2026-02-18 |
|
|
| ### Added |
|
|
| - **Full internationalization (14 locales)**: English, French, German, Spanish, Italian, Polish, Portuguese, Dutch, Swedish, Russian, Arabic, Chinese Simplified, Japanese — each with 1100+ translated keys |
| - **RTL support**: Arabic locale with `dir="rtl"`, dedicated RTL CSS overrides, regional language code normalization (e.g. `ar-SA` correctly triggers RTL) |
| - **Language switcher**: in-app locale picker with flag icons, persists to localStorage |
| - **i18n infrastructure**: i18next with browser language detection and English fallback |
| - **Community discussion widget**: floating pill linking to GitHub Discussions with delayed appearance and permanent dismiss |
| - **Linux AppImage**: added `ubuntu-22.04` to CI build matrix with webkit2gtk/appindicator dependencies |
| - **NHK World and Nikkei Asia**: added RSS feeds for Japan news coverage |
| - **Intelligence Findings badge toggle**: option to disable the findings badge in the UI |
|
|
| ### Changed |
|
|
| - **Zero hardcoded English**: all UI text routed through `t()` — panels, modals, tooltips, popups, map legends, alert templates, signal descriptions |
| - **Trending proper-noun detection**: improved mid-sentence capitalization heuristic with all-caps fallback when ML classifier is unavailable |
| - **Stopword suppression**: added missing English stopwords to trending keyword filter |
|
|
| ### Fixed |
|
|
| - **Dead UTC clock**: removed `#timeDisplay` element that permanently displayed `--:--:-- UTC` |
| - **Community widget duplicates**: added DOM idempotency guard preventing duplicate widgets on repeated news refresh cycles |
| - **Settings help text**: suppressed raw i18n key paths rendering when translation is missing |
| - **Intelligence Findings badge**: fixed toggle state and listener lifecycle |
| - **Context menu styles**: restored intel-findings context menu styles |
| - **CSS theme variables**: defined missing `--panel-bg` and `--panel-border` variables |
|
|
| ## [2.3.8] - 2026-02-17 |
|
|
| ### Added |
|
|
| - **Finance variant**: Added a dedicated market-first variant (`finance.worldmonitor.app`) with finance/trading-focused feeds, panels, and map defaults |
| - **Finance desktop profile**: Added finance-specific desktop config and build profile for Tauri packaging |
|
|
| ### Changed |
|
|
| - **Variant feed loading**: `loadNews` now enumerates categories dynamically and stages category fetches with bounded concurrency across variants |
| - **Feed resilience**: Replaced direct MarketWatch RSS usage in finance/full/tech paths with Google News-backed fallback queries |
| - **Classification pressure controls**: Tightened AI classification budgets for tech/full and tuned per-feed caps to reduce startup burst pressure |
| - **Timeline behavior**: Wired timeline filtering consistently across map and news panels |
| - **AI summarization defaults**: Switched OpenRouter summarization to auto-routed free-tier model selection |
|
|
| ### Fixed |
|
|
| - **Finance panel parity**: Kept data-rich panels while adding news panels for finance instead of removing core data surfaces |
| - **Desktop finance map parity**: Finance variant now runs first-class Deck.GL map/layer behavior on desktop runtime |
| - **Polymarket fallback**: Added one-time direct connectivity probe and memoized fallback to prevent repeated `ERR_CONNECTION_RESET` storms |
| - **FRED fallback behavior**: Missing `FRED_API_KEY` now returns graceful empty payloads instead of repeated hard 500s |
| - **Preview CSP tooling**: Allowed `https://vercel.live` script in CSP so Vercel preview feedback injection is not blocked |
| - **Trending quality**: Suppressed noisy generic finance terms in keyword spike detection |
| - **Mobile UX**: Hidden desktop download prompt on mobile devices |
|
|
| ## [2.3.7] - 2026-02-16 |
|
|
| ### Added |
|
|
| - **Full light mode theme**: Complete light/dark theme system with CSS custom properties, ThemeManager module, FOUC prevention, and `getCSSColor()` utility for theme-aware inline styles |
| - **Theme-aware maps and charts**: Deck.GL basemap, overlay layers, and CountryTimeline charts respond to theme changes in real time |
| - **Dark/light mode header toggle**: Sun/moon icon in the header bar for quick theme switching, replacing the duplicate UTC clock |
| - **Desktop update checker**: Architecture-aware download links for macOS (ARM/Intel) and Windows |
| - **Node.js bundled in Tauri installer**: Sidecar no longer requires system Node.js |
| - **Markdown linting**: Added markdownlint config and CI workflow |
|
|
| ### Changed |
|
|
| - **Panels modal**: Reverted from "Settings" back to "Panels" — removed redundant Appearance section now that header has theme toggle |
| - **Default panels**: Enabled UCDP Conflict Events, UNHCR Displacement, Climate Anomalies, and Population Exposure panels by default |
|
|
| ### Fixed |
|
|
| - **CORS for Tauri desktop**: Fixed CORS issues for desktop app requests |
| - **Markets panel**: Keep Yahoo-backed data visible when Finnhub API key is skipped |
| - **Windows UNC paths**: Preserve extended-length path prefix when sanitizing sidecar script path |
| - **Light mode readability**: Darkened neon semantic colors and overlay backgrounds for light mode contrast |
|
|
| ## [2.3.6] - 2026-02-16 |
|
|
| ### Fixed |
|
|
| - **Windows console window**: Hide the `node.exe` console window that appeared alongside the desktop app on Windows |
|
|
| ## [2.3.5] - 2026-02-16 |
|
|
| ### Changed |
|
|
| - **Panel error messages**: Differentiated error messages per panel so users see context-specific guidance instead of generic failures |
| - **Desktop config auto-hide**: Desktop configuration panel automatically hides on web deployments where it is not relevant |
|
|
| ## [2.3.4] - 2026-02-16 |
|
|
| ### Fixed |
|
|
| - **Windows sidecar crash**: Strip `\\?\` UNC extended-length prefix from paths before passing to Node.js — Tauri `resource_dir()` on Windows returns UNC-prefixed paths that cause `EISDIR: lstat 'C:'` in Node.js module resolution |
| - **Windows sidecar CWD**: Set explicit `current_dir` on the Node.js Command to prevent bare drive-letter working directory issues from NSIS shortcut launcher |
| - **Sidecar package scope**: Add `package.json` with `"type": "module"` to sidecar directory, preventing Node.js from walking up the entire directory tree during ESM scope resolution |
|
|
| ## [2.3.3] - 2026-02-16 |
|
|
| ### Fixed |
|
|
| - **Keychain persistence**: Enable `apple-native` (macOS) and `windows-native` (Windows) features for the `keyring` crate — v3 ships with no default platform backends, so API keys were stored in-memory only and lost on restart |
| - **Settings key verification**: Soft-pass network errors during API key verification so transient sidecar failures don't block saving |
| - **Resilient keychain reads**: Use `Promise.allSettled` in `loadDesktopSecrets` so a single key failure doesn't discard all loaded secrets |
| - **Settings window capabilities**: Add `"settings"` to Tauri capabilities window list for core plugin permissions |
| - **Input preservation**: Capture unsaved input values before DOM re-render in settings panel |
|
|
| ## [2.3.0] - 2026-02-15 |
|
|
| ### Security |
|
|
| - **CORS hardening**: Tighten Vercel preview deployment regex to block origin spoofing (`worldmonitorEVIL.vercel.app`) |
| - **Sidecar auth bypass**: Move `/api/local-env-update` behind `LOCAL_API_TOKEN` auth check |
| - **Env key allowlist**: Restrict sidecar env mutations to 18 known secret keys (matching `SUPPORTED_SECRET_KEYS`) |
| - **postMessage validation**: Add `origin` and `source` checks on incoming messages in LiveNewsPanel |
| - **postMessage targetOrigin**: Replace wildcard `'*'` with specific embed origin |
| - **CORS enforcement**: Add `isDisallowedOrigin()` check to 25+ API endpoints that were missing it |
| - **Custom CORS migration**: Migrate `gdelt-geo` and `eia` from custom CORS to shared `_cors.js` module |
| - **New CORS coverage**: Add CORS headers + origin check to `firms-fires`, `stock-index`, `youtube/live` |
| - **YouTube embed origins**: Tighten `ALLOWED_ORIGINS` regex in `youtube/embed.js` |
| - **CSP hardening**: Remove `'unsafe-inline'` from `script-src` in both `index.html` and `tauri.conf.json` |
| - **iframe sandbox**: Add `sandbox="allow-scripts allow-same-origin allow-presentation"` to YouTube embed iframe |
| - **Meta tag validation**: Validate URL query params with regex allowlist in `parseStoryParams()` |
|
|
| ### Fixed |
|
|
| - **Service worker stale assets**: Add `skipWaiting`, `clientsClaim`, and `cleanupOutdatedCaches` to workbox config — fixes `NS_ERROR_CORRUPTED_CONTENT` / MIME type errors when users have a cached SW serving old HTML after redeployment |
|
|
| ## [2.2.6] - 2026-02-14 |
|
|
| ### Fixed |
|
|
| - Filter trending noise and fix sidecar auth |
| - Restore tech variant panels |
| - Remove Market Radar and Economic Data panels from tech variant |
|
|
| ### Docs |
|
|
| - Add developer X/Twitter link to Support section |
| - Add cyber threat API keys to `.env.example` |
|
|
| ## [2.2.5] - 2026-02-13 |
|
|
| ### Security |
|
|
| - Migrate all Vercel edge functions to CORS allowlist |
| - Restrict Railway relay CORS to allowed origins only |
|
|
| ### Fixed |
|
|
| - Hide desktop config panel on web |
| - Route World Bank & Polymarket via Railway relay |
|
|
| ## [2.2.3] - 2026-02-12 |
|
|
| ### Added |
|
|
| - Cyber threat intelligence map layer (Feodo Tracker, URLhaus, C2IntelFeeds, OTX, AbuseIPDB) |
| - Trending keyword spike detection with end-to-end flow |
| - Download desktop app slide-in banner for web visitors |
| - Country briefs in Cmd+K search |
|
|
| ### Changed |
|
|
| - Redesign 4 panels with table layouts and scoped styles |
| - Redesign population exposure panel and reorder UCDP columns |
| - Dramatically increase cyber threat map density |
|
|
| ### Fixed |
|
|
| - Resolve z-index conflict between pinned map and panels grid |
| - Cap geo enrichment at 12s timeout, prevent duplicate download banners |
| - Replace ipwho.is/ipapi.co with ipinfo.io/freeipapi.com for geo enrichment |
| - Harden trending spike processing and optimize hot paths |
| - Improve cyber threat tooltip/popup UX and dot visibility |
|
|
| ## [2.2.2] - 2026-02-10 |
|
|
| ### Added |
|
|
| - Full-page Country Brief Page replacing modal overlay |
| - Download redirect API for platform-specific installers |
|
|
| ### Fixed |
|
|
| - Normalize country name from GeoJSON to canonical TIER1 name |
| - Tighten headline relevance, add Top News section, compact markets |
| - Hide desktop config panel on web, fix irrelevant prediction markets |
| - Tone down climate anomalies heatmap to stop obscuring other layers |
| - macOS: hide window on close instead of quitting |
|
|
| ### Performance |
|
|
| - Reduce idle CPU from pulse animation loop |
| - Harden regression guardrails in CI, cache, and map clustering |
|
|
| ## [2.2.1] - 2026-02-08 |
|
|
| ### Fixed |
|
|
| - Consolidate variant naming and fix PWA tile caching |
| - Windows settings window: async command, no menu bar, no white flash |
| - Constrain layers menu height in DeckGLMap |
| - Allow Cloudflare Insights script in CSP |
| - macOS build failures when Apple signing secrets are missing |
|
|
| ## [2.2.0] - 2026-02-07 |
|
|
| Initial v2.2 release with multi-variant support (World + Tech), desktop app (Tauri), and comprehensive geopolitical intelligence features. |
|
|