* feat(map): migrate basemap from CARTO to self-hosted PMTiles on Cloudflare R2
Replace CARTO tile provider (frequent 403 errors) with self-hosted PMTiles
served from Cloudflare R2. Uses @protomaps/basemaps for style generation
with OpenFreeMap as automatic fallback when VITE_PMTILES_URL is unset.
- Add pmtiles and @protomaps/basemaps dependencies
- Create src/config/basemap.ts for PMTiles protocol registration and style building
- Update DeckGLMap.ts to use PMTiles styles (non-happy variants)
- Fix fallback detection using data event instead of style.load
- Update SW cache rules: replace CARTO/MapTiler with PMTiles NetworkFirst
- Add Protomaps preconnect hints in index.html
- Bundle pmtiles + @protomaps/basemaps in maplibre chunk
- Upload 3.4GB world tiles (zoom 0-10) to R2 bucket worldmonitor-maps
* fix(map): use CDN custom domain maps.worldmonitor.app for PMTiles
Replace r2.dev URL with custom domain backed by Cloudflare CDN edge.
Update preconnect hint and .env.example with production URL.
* fix(map): harden PMTiles fallback detection to prevent false triggers
- Require 2+ network errors before triggering OpenFreeMap fallback
- Use persistent data listener instead of once (clears timeout on first tile load)
- Increase fallback timeout to 10s for PMTiles header + initial tile fetch
- Add console.warn for map errors to aid debugging
- Remove redundant style.load listener (fires immediately for inline styles)
* feat(settings): add Map Tile Provider selector in settings
Add dropdown in Settings → Map section to switch between:
- Auto (PMTiles → OpenFreeMap fallback)
- PMTiles (self-hosted)
- OpenFreeMap
- CARTO
Choice persists in localStorage and reloads basemap instantly.
* fix(map): make OSS-friendly — default to free OpenFreeMap, hide PMTiles when unconfigured
- Default to OpenFreeMap when VITE_PMTILES_URL is unset (zero config for OSS users)
- Hide PMTiles/Auto options from settings dropdown when no PMTiles URL configured
- If user previously selected PMTiles but env var is removed, gracefully fall back
- Remove production URL from .env.example to avoid exposing hosted tiles
- Add docs link for self-hosting PMTiles in .env.example
* docs: add map tile provider documentation to README and MAP_ENGINE.md
Document the tile provider system (OpenFreeMap, CARTO, PMTiles) in
MAP_ENGINE.md with self-hosting instructions, fallback behavior, and
OSS-friendly defaults. Update README to reference tile providers in
the feature list, tech stack, and environment variables table.
* fix: resolve rebase conflicts and fix markdown lint errors
- Restore OSS-friendly basemap defaults (MAP_PROVIDER_OPTIONS as IIFE,
getMapProvider with hasTilesUrl check)
- Fix markdown lint: add blank lines after ### headings in README
- Reconcile UnifiedSettings import with MAP_PROVIDER_OPTIONS constant
* enhance supply chain panel
* fix(supply-chain): resolve P1 threat zeroing and P2 geo-first misclassification
P1: threat baseline is now always applied regardless of config
staleness — stale config only adds a review-recommended note,
never zeros the score.
P2: resolveChokepointId now checks text evidence first and only
falls back to proximity when text has no confident match.
Adds regression test: text "Bab el-Mandeb" with location near
Suez correctly resolves to bab_el_mandeb.
---------
Co-authored-by: fayez bast <fayezbast15@gmail.com>
- Remove PostHog analytics runtime and configuration
- Add API rate limiting (api/_rate-limit.js)
- Harden traffic controls across edge functions
- Add runtime fallback controls and data-loader improvements
- Add military base data scripts (fetch-mirta-bases, fetch-osm-bases)
- Gitignore large raw data files
- Settings playground prototypes
* fix: sort tariff datapoints newest-first in trade policy panel
* fix: update tests broken by cachedFetchJson migration
- Restore "Strip unterminated" comment in summarize-article.ts that
tests use to locate the unterminated tag stripping section
- Update ACLED tests to check for cachedFetchJson instead of removed
getCachedJson/setCachedJson patterns
* chore: bump version to 2.5.9 and make pre-push hook executable
* docs: update README with supply chain intel, universal CII, Happy Monitor, security hardening, and recent features
Update data layer count to 36+, add Happy Monitor variant to Live Demos,
expand Cmd+K command palette description, and add trade routes to
Infrastructure section.
* feat: make intelligence alert popup opt-in via dropdown toggle
Auto-popup was interrupting users every 10s refresh cycle. Badge still
counts and pulses silently. New toggle in dropdown (default OFF) lets
users explicitly opt in to auto-popup behavior.
* chore: bump version to 2.5.5
## Changelog
### Features
- Intelligence alert popup is now opt-in (default OFF) — badge counts silently, toggle in dropdown to enable auto-popup
### Bug Fixes
- Linux: disable DMA-BUF renderer on WebKitGTK to prevent blank white screen (NVIDIA/immutable distros)
- Linux: add DejaVu Sans Mono + Liberation Mono font fallbacks for monospace rendering
- Consolidate monospace font stacks into --font-mono CSS variable (fixes undefined var bug)
- Reduce dedup coordinate rounding from 0.5° to 0.1° (~10km precision)
- Vercel build: handle missing previous deploy SHA
- Panel base class: add missing showRetrying method
- Vercel ignoreCommand shortened to fit 256-char limit
### Infrastructure
- Upstash Redis shared caching for all RPC handlers + cache key contamination fix
- Format Rust code and fix Windows focus handling
### Docs
- Community guidelines: contributing, code of conduct, security policy
- Updated .env.example
* chore: track Cargo.lock for reproducible Rust builds
* fix: update layer help popup with all current map layers
Added missing layers to the ? help popup across all 3 variants:
- Full: UCDP Events, Displacement, Spaceports, Cyber Threats, Fires,
Climate Anomalies, Critical Minerals; renamed Shipping→Ship Traffic
- Tech: Tech Events, Cyber Threats, Fires
- Finance: GCC Investments
* docs: update README with crypto prices, analytics, typography, and dedup grid fix
* fix: add /ingest to service worker NetworkOnly routes
The SW was intercepting PostHog /ingest/* requests and returning
no-response (404) because no cache match existed. Adding NetworkOnly
ensures analytics requests pass through to Vercel's rewrite proxy.
* chore: update Cargo.lock for v2.5.5
* fix: use explicit colors for findings toggle switch visibility
* fix(sentry): add noise filters for 5 non-actionable error patterns
Filter dynamic import alt phrasing, script parse errors, maplibre
style/WebGL crashes, and CustomEvent promise rejections. Also fix
beforeSend to catch short Firefox null messages like "E is null".
* fix: cache write race, settings stale key status, yahoo gate concurrency
P1: Replace async background thread cache write with synchronous fs::write
to prevent out-of-order writes and dirty flag cleared before persistence.
P2: Add WorldMonitorTab.refresh() called after loadDesktopSecrets() so
the API key badge reflects actual keychain state.
P3: Replace timestamp-based Yahoo gate with promise queue to ensure
sequential execution under concurrent callers.
* feat: add Upstash Redis shared caching to all RPC handlers + fix cache key contamination
- Add Redis L2 cache (getCachedJson/setCachedJson) to 28 RPC handlers
across all service domains (market, conflict, cyber, economic, etc.)
- Fix 10 P1 cache key contamination bugs where under-specified keys
caused cross-request data pollution (e.g. filtered requests returning
unfiltered cached data)
- Restructure list-internet-outages to cache-then-filter pattern so
country/timeRange filters always apply after cache read
- Add write_lock mutex to PersistentCache in main.rs to prevent
desktop cache write-race conditions
- Document FMP (Financial Modeling Prep) as Yahoo Finance fallback TODO
in market/v1/_shared.ts
* fix: cache-key contamination and PizzINT/GDELT partial-failure regression
- tech-events: fetch with limit=0 and cache full result, apply limit
slice after cache read to prevent low-limit requests poisoning cache
- pizzint: restore try-catch around PizzINT fetch so GDELT tension
pairs are still returned when PizzINT API is down
* fix: remove extra closing brace in pizzint try-catch
* fix: recompute conferenceCount/mappableCount after limit slice
* fix: bypass WM API key gate for registration endpoint
/api/register-interest must reach cloud without a WorldMonitor API key,
otherwise desktop users can never register (circular dependency).
### Motivation
- Reduce sidecar API egress by preferring Brotli (br) for clients that
support it while keeping gzip as a fallback for broad compatibility.
- Ensure the sidecar negotiates compression consistently (set
`Content-Encoding`, append `Vary: Accept-Encoding`) and avoids sending
stale `Content-Length` when responses are compressed.
- Provide an Nginx proxy baseline that preserves client
`Accept-Encoding` and prefers Brotli at the proxy layer.
### Description
- Updated `src-tauri/sidecar/local-api-server.mjs` to negotiate response
compression via a new `maybeCompressResponseBody` routine that prefers
Brotli (`br`) and falls back to gzip for payloads > 1KB, preserves
existing `Content-Encoding` from handlers, and appends `Vary:
Accept-Encoding` when compressing.
- Added a small helper `canCompress` and promisified `brotliCompress` so
Brotli compression runs asynchronously while gzip remains synchronous
for fallback.
- Ensure `Content-Length` is removed when `Content-Encoding` is applied
so downstream callers/proxies don't see stale lengths.
- Added tests to `src-tauri/sidecar/local-api-server.test.mjs` that
verify Brotli is preferred when `Accept-Encoding` includes `br` and gzip
is used when `br` is not present, including decompression checks with
`brotliDecompressSync`/`gunzipSync`.
- Added `deploy/nginx/brotli-api-proxy.conf` as an example Nginx
configuration enabling Brotli with gzip fallback and forwarding
`Accept-Encoding` while disabling upstream decompression (`gunzip off`).
- Updated `README.md` to reflect the sidecar now advertising `br/gzip`
behavior and adjusted the expected payload reduction notes.
### Testing
- Ran the sidecar unit tests with `node --test
src-tauri/sidecar/local-api-server.test.mjs`, which executed 19 tests
and all passed (`# pass 19`, `# fail 0`).
- No other automated test suites were modified; the new tests are
self-contained under the sidecar test file and validated the compression
behavior.
------
[Codex
Task](https://chatgpt.com/codex/tasks/task_e_6997871302188333bb1ecb20714d2ad3)
- Ollama/LM Studio integration with auto model discovery and 4-tier fallback chain
- Settings window split into LLMs, API Keys, and Debug tabs
- Consolidated keychain vault (1 OS prompt instead of 20+)
- README expanded with privacy architecture, summarization chain docs
- CHANGELOG updated with full v2.5.0 release notes
- 5 new defense/intel RSS feeds, Koeberg nuclear plant added
Add Turkish (tr) as the 14th supported language with full translation
of all 1,134 locale keys, locale loader, tr-TR formatting, and flag
mapping. Update documentation to reflect 14 languages.
- Replace MIT with AGPL-3.0-only to enforce attribution on derivatives
- Move hardcoded Sentry DSN to VITE_SENTRY_DSN env var
- Add null-coalesce guards for i18n legend keys and SVG viewBox
- Extend Sentry ignoreErrors with 7 additional noise patterns
MD012 / no-multiple-blanks Multiple consecutive blank lines
MD022 / blanks-around-headings Headings should be surrounded by blank lines
MD032 / blanks-around-lists Lists should be surrounded by blank lines
- Expand SUPPRESSED_TRENDING_TERMS from 13 to ~170 entries to filter
common English words (department, state, news, etc.) from intelligence
findings
- Move sidecar admin endpoints (debug-toggle, traffic-log, env-update,
local-status) before LOCAL_API_TOKEN auth gate — settings window sends
bare fetch without token, causing silent 401 failures
- Restore Market Radar and Economic Indicators panels to tech variant
- Remove stale Documentation section from README
- Clean up .env.example cyber threat keys (handled internally)
- Bump v2.2.6
Update README with 5 new How It Works sections and 10 inline updates
reflecting implemented features. Data layer count 29→30+, data sources
14→22, new Tech Stack and Roadmap entries.
Plot live botnet C2 servers, malware distribution nodes, and malicious IPs
on the globe using free abuse.ch APIs (Feodo Tracker + URLhaus).
- Vercel edge API with triple-layer caching (Redis → memory → stale fallback)
- IP geolocation via ipwho.is + ipapi.co (HTTPS-compatible with Edge runtime)
- Severity-based color coding (critical=red, high=orange, medium=amber, low=yellow)
- Feature-gated behind VITE_ENABLE_CYBER_LAYER=true env var
- Frontend circuit breaker, data sanitization, 10min auto-refresh
- Tauri desktop support: 3 new secret keys (URLHAUS, OTX, AbuseIPDB)
- Full test suite (6 unit tests), e2e harness updates, popup + tooltip rendering
Shield.io badges for Windows, macOS ARM, and macOS Intel desktop downloads
pointing to the new /api/download redirect. Web app links restyled as badges.
- Rename variant default from 'world' to 'full' across config files
- Replace all startups.worldmonitor.app references with tech.worldmonitor.app
- Add CARTO basemap tile caching to Workbox runtime config (basemaps.cartocdn.com)
Explains that `vercel dev` is needed instead of `npm run dev` for
the API edge functions to work. Covers three deployment options
(Vercel, local with CLI, static frontend only) and platform notes
for Raspberry Pi/ARM users. Addresses #44.