* Add premium finance stock analysis suite
* docs: link premium finance from README
Add Premium Stock Analysis entry to the Finance & Markets section
with a link to docs/PREMIUM_FINANCE.md.
* fix: address review feedback on premium finance suite
- Chunk Redis pipelines into batches of 200 (Upstash limit)
- Add try-catch around cachedFetchJson in backtest handler
- Log warnings on Redis pipeline HTTP failures
- Include name in analyze-stock cache key to avoid collisions
- Change analyze-stock and backtest-stock gateway cache to 'slow'
- Add dedup guard for concurrent ledger generation
- Add SerpAPI date pre-filter (tbs=qdr:d/w)
- Extract sanitizeSymbol to shared module
- Extract buildEmptyAnalysisResponse helper
- Fix RSI to use Wilder's smoothing (matches TradingView)
- Add console.warn for daily brief summarization errors
- Fall back to stale data in loadStockBacktest on error
- Make daily-market-brief premium on all platforms
- Use word boundaries for short token headline matching
- Add stock-analysis 15-min refresh interval
- Stagger stock-analysis and backtest requests (200ms)
- Rename signalTone to stockSignalTone
- register-interest.js: coerce source/appVersion to string with a 100-char cap
before forwarding to Convex. Non-string values (objects, arrays) are truthy so
the previous || 'unknown' guard passed them through, causing Convex to throw
a type-validation error and surface a 500 to the caller. Also fixes unbounded
metadata strings filling the registrations table cheaply.
- rss-proxy.js: apply the same www-normalization used by the initial domain check
to the 301-redirect hostname check. The old bare ALLOWED_DOMAINS.includes(hostname)
call rejected canonical redirects (e.g. bbc.co.uk -> www.bbc.co.uk) even when
one form is allowlisted, breaking several feeds silently.
- _api-key.js: align BROWSER_ORIGIN_PATTERNS Vercel-preview regex with the
narrower pattern already enforced by _cors.js (worldmonitor-*-elie-*.vercel.app).
The broader worldmonitor-*.vercel.app pattern was dead code because _cors.js
rejects those origins before _api-key.js is reached.
Same-origin browser requests don't send Origin header (per CORS spec),
causing validateApiKey to reject them. Extract origin from Referer as
fallback. Increase rate limit from 60 to 200 req/min to accommodate
the ~50 requests fired during page initialization.
- api/_api-key.js: preview URL pattern was user-specific (-elie-),
rejecting other collaborators' Vercel preview deployments.
Generalized to match any worldmonitor-*.vercel.app origin.
- military-bases.ts: client cache key only checked bbox/zoom, ignoring
type/kind/country filters. Switching filters without panning returned
stale results. Unified into single cacheKey string.
* feat(military): server-side military bases with 125K entries + rate limiting (#485)
Migrate military bases from 224 static client-side entries to 125,380
server-side entries stored in Redis GEO sorted sets, served via
bbox-filtered GEOSEARCH endpoint with server-side clustering.
Data pipeline:
- Pizzint/Polyglobe: 79,156 entries (Supabase extraction)
- OpenStreetMap: 45,185 entries
- MIRTA: 821 entries
- Curated strategic: 218 entries
- 277 proximity duplicates removed
Server:
- ListMilitaryBases RPC with GEOSEARCH + HMGET + tier/filter/clustering
- Antimeridian handling (split bbox queries)
- Blue-green Redis deployment with atomic version pointer switch
- geoSearchByBox() + getHashFieldsBatch() helpers in redis.ts
Security:
- @upstash/ratelimit: 60 req/min sliding window per IP
- IP spoofing fix: prioritize x-real-ip (Vercel-injected) over x-forwarded-for
- Require API key for non-browser requests (blocks unauthenticated curl/scripts)
- Input validation: allowlisted types/kinds, regex country, clamped bbox/zoom
Frontend:
- Viewport-driven loading with bbox quantization + debounce
- Server-side grid clustering at low zoom levels
- Enriched popup with kind, category badges (airforce/naval/nuclear/space)
- Static 224 bases kept as search fallback + initial render
* fix(military): fallback to production Redis keys in preview deployments
Preview deployments prefix Redis keys with `preview:{sha}:` but military
bases data is seeded to unprefixed (production) keys. When the prefixed
`military:bases:active` key is missing, fall back to the unprefixed key
and use raw (unprefixed) keys for geo/meta lookups.
* fix: remove unused 'remaining' destructure in rate-limit (TS6133)
* ci: add typecheck:api to pre-push hook to catch server-side TS errors
* debug(military): add X-Bases-Debug response header for preview diagnostics
* fix(bases): trigger initial server fetch on map load
fetchServerBases() was only called on moveend — if the user
never panned/zoomed, the API was never called and only the 224
static fallback bases showed.
* 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.