- Defer YouTube player init via IntersectionObserver + requestIdleCallback
gate with clickable placeholder (no eager iframe_api load)
- Stagger loadAllData() into 3 priority tiers: critical (immediate),
important (after rAF yield), deferred (requestIdleCallback fire-and-forget)
- Move DeckGL supercluster rebuilds into map 'load' callback
- Cancel deferred tier-3 callbacks on App destroy (prevents post-teardown work)
- Add bot-check detection with YouTube sign-in window for desktop (Tauri)
- Safe DOM construction for all new UI paths (no innerHTML with user data)
* security: block SSRF and enforce global auth on sidecar endpoints
Addresses trust boundary vulnerabilities in the desktop sidecar's
locally-exposed API server (127.0.0.1:46123) reported in
"Breaking the Trust Boundary in a 14k Star OSINT Dashboard":
- SSRF protection on /api/rss-proxy: block private/reserved IPs
(127.x, 10.x, 172.16-31.x, 192.168.x, 169.254.x, multicast),
validate DNS resolution to prevent rebinding, reject non-http(s)
protocols and URLs with embedded credentials
- Global auth gate: move LOCAL_API_TOKEN check above ALL endpoints
so /api/rss-proxy, /api/local-status, /api/local-traffic-log,
/api/local-debug-toggle, and /api/register-interest now require
authentication (only /api/service-status health check is exempt)
- Cryptographic token generation: replace RandomState-based token
in main.rs with getrandom crate (OS-backed CSPRNG, 32 bytes)
- Traffic log privacy: strip query strings from logged paths to
prevent leaking feed URLs and user research patterns
- CORS hardening: tighten worldmonitor.app origin regex from
(.*\.)? to ([a-z0-9-]+\.)? to block multi-level subdomain spoofing
- 10 new security tests covering auth enforcement on every endpoint,
SSRF blocking for private IPs/localhost/non-http/credentials,
health check exemption, and traffic log sanitization
https://claude.ai/code/session_018vNVfwPh25tbZmtiX66KxP
* security: pin resolved IP in rss-proxy to close TOCTOU DNS rebinding window
isSafeUrl() now returns the resolved addresses, and fetchWithTimeout()
accepts a resolvedAddress option that bypasses runtime DNS via a custom
lookup callback (HTTPS) or URL rewrite with Host header (HTTP).
The rss-proxy handler threads the first validated IPv4 through, so the
TCP connection is guaranteed to reach the same IP that passed the
private-range check.
https://claude.ai/code/session_018vNVfwPh25tbZmtiX66KxP
---------
Co-authored-by: Claude <noreply@anthropic.com>
* feat(live): custom channel management — add/remove/reorder, standalone window, i18n
- Standalone channel management window (?live-channels=1) with list, add form, restore defaults
- LIVE panel: gear icon opens channel management; channel tabs reorderable via DnD
- Row click to edit; custom modal for delete confirmation (no window.confirm)
- i18n for all locales (manage, addChannel, youtubeHandle, displayName, etc.)
- UI: margin between channel list and add form in management window
- settings-window: panel display settings comment in English
Co-authored-by: Cursor <cursoragent@cursor.com>
* feat(tauri): channel management in desktop app, dev base_url fix
- Add live-channels.html and live-channels-main.ts for standalone window
- Tauri: open_live_channels_window_command, close_live_channels_window, open live-channels window (WebviewUrl::App or External from base_url)
- LiveNewsPanel: in desktop runtime invoke Tauri command with base_url (window.location.origin) so dev works when Vite runs on a different port than devUrl
- Vite: add liveChannels entry to build input
- capabilities: add live-channels window
- tauri.conf: devUrl 3000 to match vite server.port
- docs: PR_LIVE_CHANNEL_MANAGEMENT.md for PR #276
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix: address review issues in live channel management PR
- Revert settings button to open modal (not window.open popup)
- Revert devUrl from localhost:3000 to localhost:5173
- Guard activeChannel against empty channels (fall back to defaults)
- Escape i18n strings in innerHTML with escapeHtml() to prevent XSS
- Only store displayNameOverrides for actually renamed channels
- Use URL constructor for live-channels window URL
- Add CSP meta tag to live-channels.html
- Remove unused i18n keys (edit, editMode, done) from all locales
- Remove unused CSS classes (live-news-manage-btn/panel/wrap)
- Delete PR instruction doc (PR_LIVE_CHANNEL_MANAGEMENT.md)
---------
Co-authored-by: Masaki <yukkurihakutaku@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix: persist circuit breaker cache to IndexedDB across page reloads
On page reload, all 28+ circuit breaker in-memory caches are lost,
triggering 20-30 simultaneous POST requests to Vercel edge functions.
Wire the existing persistent-cache.ts (IndexedDB + localStorage +
Tauri fallback) into CircuitBreaker so every breaker automatically:
- Hydrates from IndexedDB on first execute() call (~1-5ms read)
- Writes to IndexedDB fire-and-forget on every recordSuccess()
- Falls back to stale persistent data on network failure
- Auto-disables for breakers with cacheTtlMs=0 (live pricing)
Zero consumer code changes -- all 28+ breaker call sites untouched.
Reloads within the cache TTL (default 10min) serve instantly from
IndexedDB with zero network calls.
Also adds deletePersistentCache() to persistent-cache.ts for clean
cache invalidation via clearCache().
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* test: add Playwright e2e tests for circuit breaker persistent cache
7 tests covering: IndexedDB persistence on success, hydration on new
instance, TTL expiry forcing fresh fetch, 24h stale ceiling rejection,
clearCache cleanup, cacheTtlMs=0 auto-disable, and network failure
fallback to stale persistent data.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: desktop cache deletion + clearCache race condition
P1: deletePersistentCache sent empty string to write_cache_entry,
which fails Rust's serde_json::from_str (not valid JSON). Add
dedicated delete_cache_entry Tauri command that removes the key
from the in-memory HashMap and flushes to disk.
P2: clearCache() set persistentLoaded=false, allowing a concurrent
execute() to re-hydrate stale data from IndexedDB before the async
delete completed. Remove the reset — after explicit clear there is
no reason to re-hydrate from persistent storage.
* fix: default persistCache to false, fix falsy data guard
P1b: 6 breakers store Date objects (weather, aviation, ACLED,
military-flights, military-vessels, GDACS) which become strings
after JSON round-trip. Callers like MapPopup.getTimeUntil() call
date.getTime() on hydrated strings → TypeError. Change default
to false (opt-in) so persistence requires explicit confirmation
that the payload is JSON-safe.
P2: `if (!entry?.data) return` drops valid falsy payloads (0,
false, empty string). Use explicit null/undefined check instead.
* fix: address blocking review issues on circuit breaker persistence
- clearCache() nulls persistentLoadPromise to orphan in-flight hydration
- delete_cache_entry defers disk flush to exit handler (avoids 14MB sync write)
- hydratePersistentCache checks TTL before setting lastDataState to 'cached'
- deletePersistentCache resets cacheDbPromise on IDB error + logs warning
- hydration catch logs warning instead of silently swallowing
- deletePersistentCache respects isStorageQuotaExceeded() for localStorage
---------
Co-authored-by: Elias El Khoury <efk@anghami.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* fix: resolve AppImage crash on Ubuntu 25.10+ (GLib symbol mismatch)
The AppImage bundles GLib from the build system, but host GIO modules
(e.g. GVFS libgvfsdbus.so) compiled against a newer GLib reference
symbols like g_task_set_static_name that don't exist in the older
bundled copy, causing "undefined symbol" errors and WebKit crashes.
Set GIO_MODULE_DIR="" when running as AppImage to prevent host GIO
modules from loading against the incompatible bundled GLib. GVFS
features (network mounts, trash, MTP) are unused by this app.
Note: the CI should also be upgraded from ubuntu-22.04 to ubuntu-24.04
in .github/workflows/build-desktop.yml to ship GLib 2.80+ and extend
forward-compatibility. This requires workflows permission to push.
https://claude.ai/code/session_01J8HBrfb26GJm22MFCeGoAA
* fix(appimage): keep bundled GIO modules for Ubuntu 25.10
---------
Co-authored-by: Claude <noreply@anthropic.com>
* fix: resolve AppImage blank white screen and font crash on Linux (#238)
Disable WebKitGTK DMA-BUF renderer by default on Linux to prevent blank
white screens caused by GPU buffer allocation failures (common with
NVIDIA drivers and immutable distros like Bazzite). Add Linux-native
monospace font fallbacks (DejaVu Sans Mono, Liberation Mono) to all font
stacks so WebKitGTK font resolution doesn't hit out-of-bounds vector
access when macOS-only fonts (SF Mono, Monaco) are unavailable.
https://claude.ai/code/session_01TF2NPgSSjgenmLT2XuR5b9
* fix: consolidate monospace font stacks into --font-mono variable
- Define --font-mono in :root (main.css) and .settings-shell (settings-window.css)
- Align font stack: SF Mono, Monaco, Cascadia Code, Fira Code, DejaVu Sans Mono, Liberation Mono
- Replace 3 hardcoded JetBrains Mono stacks with var(--font-mono)
- Replace 4 hardcoded settings-window stacks with var(--font-mono)
- Fix pre-existing bug: var(--font-mono) used in 4 places but never defined
- Match index.html skeleton font stack to --font-mono
---------
Co-authored-by: Claude <noreply@anthropic.com>
* 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).
* chore: apply cargo fmt formatting to main.rs
Pure formatting normalization with no logic changes. Separated from
the behavioral fix to keep git blame clean.
https://claude.ai/code/session_01RPQ1PEqxTSEG6rB5XadzEz
* fix: restrict settings-window re-focus to macOS to avoid Windows focus churn
On Windows, the Focused(true) handler on the main window calls
show()+set_focus() on the settings window, which steals focus back,
retriggering the event in a tight loop and presenting as a UI hang.
Gate the match arm with #[cfg(target_os = "macos")] (compile-time
attribute) instead of cfg!() (runtime macro) to match the convention
used by the adjacent macOS-only handlers and eliminate dead code on
non-macOS builds entirely.
https://claude.ai/code/session_01RPQ1PEqxTSEG6rB5XadzEz
---------
Co-authored-by: Claude <noreply@anthropic.com>
- Sidecar calls Convex HTTP API directly (Vercel Attack Challenge Mode
blocks server-side proxy). CONVEX_URL read from env, not hardcoded.
- Rust injects CONVEX_URL into sidecar via option_env! (CI) / env var (dev)
- GitHub Actions passes CONVEX_URL secret to all 4 build steps
- Tighten WM tab CSS spacing so all content fits in one viewport
* feat: API key gating for desktop cloud fallback + registration system
Gate desktop cloud fallback behind WORLDMONITOR_API_KEY — desktop users
need a valid key for cloud access, otherwise operate local-only (sidecar).
Add email registration system via Convex DB for future key distribution.
Client-side: installRuntimeFetchPatch() checks key presence before
allowing cloud fallback, with secretsReady promise + 2s timeout.
Server-side: origin-aware validation in sebuf gateway — desktop origins
require key, web origins pass through.
- Add WORLDMONITOR_API_KEY to 3-place secret system (Rust, TS, sidecar)
- New "World Monitor" settings tab with key input + registration form
- New api/_api-key.js server-side validation (origin-aware)
- New api/register-interest.js edge function with rate limiting
- Convex DB schema + mutation for email registration storage
- CORS headers updated for X-WorldMonitor-Key + Authorization
- E2E tests for key gate (blocked without key, allowed with key)
- Deployment docs (API_KEY_DEPLOYMENT.md) + updated desktop config docs
* fix: harden worldmonitor key + registration input handling
* fix: show invalid WorldMonitor API key status
* fix: simplify key validation, trim registration checks, add env example vars
- Inline getValidKeys() in _api-key.js
- Remove redundant type checks in register-interest.js
- Simplify WorldMonitorTab status to present/missing
- Add WORLDMONITOR_VALID_KEYS and CONVEX_URL to .env.example
* feat(sidecar): integrate proto gateway bundle into desktop build
The sidecar's buildRouteTable() only discovers .js files, so the proto
gateway at api/[domain]/v1/[rpc].ts was invisible — all 45 sebuf RPCs
returned 404 in the desktop app. Wire the existing build script into
Tauri's build commands and add esbuild as an explicit devDependency.
- Split settings window into 3 tabs: LLMs (Ollama/Groq/OpenRouter),
API Keys (data feeds), and Debug & Logs
- Add featureFilter option to RuntimeConfigPanel for rendering subsets
- Consolidate keychain to single JSON vault entry (1 macOS prompt vs 20)
- Add Ollama model discovery with /api/tags + /v1/models fallback
- Strip <think> reasoning tokens from Ollama responses
- Suppress thinking with think:false in Ollama request body
- Parallel secret verification with 15s global timeout
- Fix manual model input overlapping dropdown (CSS grid-area + hidden-input class)
- Add loading spinners to settings tab panels
- Suppress notification popups when settings window is open
- Filter embed models from Ollama dropdown
- Fix settings window black screen flash with inline dark background
- Use for...of entries() instead of index-based loops in summarization.ts
to satisfy strict noUncheckedIndexedAccess (7 TS18048/TS2345 errors)
- Replace fragile API_PROVIDERS[1] with .find(p => p.name === groq)
- Add OLLAMA_API_URL and OLLAMA_MODEL to SUPPORTED_SECRET_KEYS in main.rs
so keychain secrets are injected into sidecar on desktop startup
Add CREATE_NO_WINDOW (0x08000000) creation flag to the sidecar
Command::new() spawn on Windows. Without this, node.exe inherits
a visible console window that overlays the Tauri GUI.
Tauri resource_dir() on Windows returns \\?\ extended-length paths that
Node.js module resolution cannot handle, causing EISDIR: lstat 'C:'.
Strip the prefix before passing to Node.js, set current_dir to the
sidecar directory, and add package.json with "type": "module" to prevent
ESM scope walk-up to drive root.
- Show "Staged" status/pill for buffered secrets instead of "Missing"
- Add macOS Edit menu (Cmd+C/V/X/Z) for WKWebView clipboard support
- Raise settings window when main gains focus (prevent hide-behind)
- Fix Cloudflare verification to probe Radar API (not token/verify)
- Fix EIA verification URL to valid v2 endpoint
- Force IPv4 globally: monkey-patch fetch() to avoid IPv6 ETIMEDOUT
on government APIs (EIA, NASA FIRMS) with broken AAAA records
- Soft-pass on network errors during secret verification (don't block save)
- Add desktopRequiredSecrets to skip relay URLs on desktop
- Cross-window sync for secrets and feature toggles via localStorage events
- Add @tauri-apps/cli devDependency
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
- Make open_settings_window_command async to prevent WebView2 deadlock on Windows
- Create settings window with visible(false) to avoid white flash before content loads
- Remove menu bar from settings window on Windows/Linux (macOS uses screen-level menu)
- Frontend calls plugin:window|show + set_focus after init completes
- Add worldmonitor.app to frame-src CSP in index.html (was only in
tauri.conf.json, causing iframe block)
- Add devtools feature and Help > Toggle Developer Tools menu item
- Try native YouTube JS API first, fall back to cloud bridge on Error 153
- Add pause-then-play workaround for WKWebView channel switching
Prevents unauthorized local processes from accessing the sidecar on
localhost:46123. Token is generated at Tauri startup using RandomState
hasher, injected into sidecar env, and lazy-loaded by the frontend
fetch patch via get_local_api_token command.
Service-status endpoint remains public for health checks.
Co-authored-by: RinZ27 <RinZ27@users.noreply.github.com>
- Fix YouTube Error 153 by serving embed bridge from cloud URL (origin match)
- Fix channel switching when playerContainer detached from DOM
- Fix Fires panel infinite spinner when API returns 0 or fails
- Make TECH variant button open web URL instead of being disabled
- Fix circuit breaker caching empty results as success in 6 services
(polymarket, wingbits, military-flights, outages, conflicts, protests)
- Improve sidecar: cloud-preferred routing, failed import caching, log dedup
- Add FINNHUB_API_KEY and NASA_FIRMS_API_KEY to Tauri secret keys
- Add early 503 for missing ACLED token in risk-scores