Files
worldmonitor/docs/roadmap-pro.md
Elie Habib 09a582e089 fix(docs): correct numerical inaccuracies, add missing features and variants (#1472)
* fix(docs): correct variant count from 4 to 5

Updated overview.mdx and architecture.mdx to reflect all 5 platform
variants (World Monitor, Tech Monitor, Happy Monitor, Finance Monitor,
Commodity Monitor). Previously only 2 were listed in the table and
the text said "four".

* fix(docs): correct all numerical inaccuracies across documentation

Updated counts verified against actual codebase:
- AI datacenters: 111 → 313
- Undersea cables: 55 → 86
- Map layers: 45 → 49
- News sources: 80+ → 344
- Service domains: 22 → 24
- CII countries: 20 → 24
- Military bases: 210/220+ → 226
- Strategic ports: 61/83 → 62
- Airports: 30/107 → 111
- Chokepoints: 6/8 → 9
- Signal entities: 100+/600+ → 66
- Variant count: four → five (added Commodity Monitor)

* docs(overview): add Happy, Finance, and Commodity Monitor sections

Added detailed documentation sections for the three previously
undocumented platform variants, including layers, panels, and
news categories for each.

* docs(features): document 13 previously undocumented features

Added: trade routes, FIRMS fire detection, webcam surveillance,
country brief, aviation intelligence panel, climate anomalies,
displacement tracking, Gulf economies, WTO trade policy, central
banks & BIS, market watchlist, NOTAM closure detection, and
offline ML capabilities.

* docs: standardize terminology, add cross-references, fix stale refs

Phase 4: Renamed "News Importance Scoring" to "Headline Scoring",
"Signal Correlation" to "Cross-Stream Correlation". Added cross-refs
between CII/convergence, CORS/API-key, maritime/finance chokepoints.
Deduplicated Population Exposure content. Clarified hotspot vs focal
point distinction.

Phase 5: Rewrote daily_stock_analysis as historical context. Added
legacy note for api/*.js files. Added OREF 24h rolling history boost
and GPS jamming classification thresholds to algorithms.mdx. Fixed
orbital-surveillance tier table contradictions.

Phase 6: Fixed orphaned markdown separator in orbital-surveillance.

* fix(docs): catch remaining stale numbers in secondary doc files

Fixed stale counts in data-sources.mdx, strategic-risk.mdx,
documentation.mdx, ai-intelligence.mdx, PRESS_KIT.md, and
roadmap-pro.md that were missed in the initial pass.

* fix(docs): address review findings from fresh-eyes pass

- desktop-app.mdx: "four variants" → "five", "22 services" → "24"
- infrastructure-cascade.mdx: chokepoints 8 → 9, node total 279 → 350
- data-sources.mdx: chokepoints 8 → 9
- overview.mdx: remove duplicated intro sentence
- getting-started.mdx: fix stale file tree comments (AI clusters, airports, entities)
2026-03-12 07:48:47 +04:00

47 KiB
Raw Blame History

World Monitor Pro — Implementation Roadmap

Context

The /pro landing page promises features across 4 tiers (Free, Pro, API, Enterprise) but almost nothing beyond the marketing page exists. Current state:

  • Convex: bare — registrations + counters tables only
  • Auth: none — no Clerk, no sessions. Desktop uses manual WORLDMONITOR_API_KEY in keychain
  • Payments: none — no Stripe
  • Gating: UI-only on desktop (6 panels, 3 map layers). No server-side enforcement. api/_api-key.js validates against static WORLDMONITOR_VALID_KEYS env var
  • User dashboard: none
  • API tier: none (marketed as separate product)
  • Delivery channels: none (Slack/Telegram/Discord/WhatsApp/Email)
  • AI briefings: none (LLM infra exists via Groq but no scheduled briefs)
  • Equity research: basic quotes only — no financials, analyst targets, valuation metrics

Key architectural constraint: main app is vanilla TS + Vite (NOT React). Only pro-test/ landing page is React. Clerk must use @clerk/clerk-js headless SDK.

Recommended MVP scope: Phases 04 + tasks 5.1, 5.2 = monetization MVP. Defer Phase 6 XL features until revenue validates demand.


Dependency Graph

Phase 0 (Decisions)
  ├──→ Phase 1 (Auth) ────┐
  └──→ Phase 2 (Schema) ──┤
                           ├──→ Phase 3 (Payments) ──→ Phase 4 (Gating)
                           │                                  │
                           │                           ┌──────┼──────┐
                           │                           ▼      ▼      ▼
                           └──────────────────→ Phase 5   Phase 6  Phase 7
                                              (Dashboard) (Pro)    (API)
                                                                     │
                                                                     ▼
                                                              Phase 8 (Enterprise)

Critical path: Decisions → Auth + Schema (parallel) → Payments → Gating → everything else


Summary

Phase P0 P1 P2 P3 Total
0: Decisions 3 3
1: Auth 2 2 4
2: Schema 2 1 1 4
3: Payments 3 2 1 6
4: Gating 2 2 4
5: Dashboard 2 3 5
6: Pro Features 5 3 8
7: API Tier 2 2 4
8: Enterprise 10 10
Total 12 16 10 10 48

GitHub Issues

Phase 0: Foundational Decisions


Issue #0.1: Select authentication provider

Title: decision: auth provider — Clerk (@clerk/clerk-js headless) vs Convex Auth

Labels: decision, auth, P0 Priority: P0 | Size: S | Dependencies: None

Description: Evaluate and select an authentication provider for World Monitor Pro.

Options:

  1. Clerk (recommended) — first-class Convex integration, handles email/social login, webhook sync to Convex. @clerk/clerk-js headless SDK for vanilla TS app, @clerk/clerk-react for /pro React page.
  2. Convex Auth — built-in, fewer moving parts, but newer and less battle-tested.
  3. Supabase Auth — battle-tested but adds another infra layer on top of Convex.

Key constraint: Main app is vanilla TS + Vite (NOT React). Auth SDK must support headless DOM mounting (mountSignIn() / mountSignUp()).

Acceptance criteria:

  • Decision documented with rationale
  • Prototype: sign-in flow working in vanilla TS with chosen provider
  • Verify Convex webhook sync works (user created in Clerk → user appears in Convex)

Issue #0.2: Select payment provider

Title: decision: payment provider — Stripe Checkout (hosted) vs embedded

Labels: decision, payments, P0 Priority: P0 | Size: S | Dependencies: None

Description: Select payment processing approach.

Recommendation: Stripe Checkout (hosted). Simpler than embedded, handles SCA/3DS automatically, less frontend code. Stripe Customer Portal for billing management.

Acceptance criteria:

  • Stripe account configured with test mode
  • Decision documented: hosted vs embedded checkout

Issue #0.3: API tier architecture decision

Title: decision: API tier architecture — separate Stripe products, independent of Pro plan

Labels: decision, api-tier, P0 Priority: P0 | Size: S | Dependencies: None

Description: The marketing page states API is "separate from Pro — use both or either." Define the entitlement model.

Decision points:

  • Separate Stripe products: Pro Monthly/Annual + API Starter + API Business
  • A user can have Pro (dashboard features) without API access, or API access without Pro
  • Single entitlements projection table derives access from all active subscriptions
  • Rate limits per rateLimitTier, not per product

Acceptance criteria:

  • Entitlement matrix documented (which endpoints are free/pro/api-only)
  • Schema for entitlements projection table designed

Phase 1: Authentication (Weeks 12)


Issue #1.1: Clerk + Convex integration

Title: feat(auth): Clerk + Convex integration — users table, webhook sync

Labels: auth, backend, infra, P0 Priority: P0 | Size: M | Dependencies: #0.1

Description: Set up Clerk as the authentication provider and wire it into Convex via webhook.

Implementation:

  1. Install @clerk/clerk-js (vanilla TS main app) + @clerk/clerk-react (pro-test React page)
  2. Add users table to convex/schema.ts (see schema in Phase 2)
  3. Create Clerk webhook handler as Convex HTTP action:
    • user.created → create user in Convex with clerkId, email, name, plan: "free"
    • user.updated → sync email/name changes
    • user.deleted → anonymize/tombstone user records (NOT hard delete audit/billing)
  4. Configure environment variables: VITE_CLERK_PUBLISHABLE_KEY, CLERK_SECRET_KEY, CLERK_WEBHOOK_SECRET

Key files:

  • convex/schema.ts — add users table
  • convex/clerk-webhook.ts — new HTTP action
  • .env.example — add Clerk env vars

Acceptance criteria:

  • User signs up via Clerk → user document created in Convex users table
  • User updates profile in Clerk → Convex user updated
  • Webhook signature verified (reject unsigned/invalid requests)
  • Automated: Clerk webhook integration test

Issue #1.2: Sign-in/sign-up UI in vanilla TS dashboard

Title: feat(auth): sign-in/sign-up UI in vanilla TS dashboard (clerk-js headless)

Labels: auth, frontend, P0 Priority: P0 | Size: M | Dependencies: #1.1

Description: Add authentication UI to the main vanilla TS dashboard using Clerk's headless @clerk/clerk-js SDK.

Implementation:

  1. Initialize Clerk instance in app entry point
  2. Use clerk.mountSignIn(element) / clerk.mountSignUp(element) for auth modals
  3. Add user avatar + dropdown to existing navbar (sign out, account, billing links)
  4. Expose currentUser and user entitlements via a service module (src/services/auth.ts)
  5. Update locked panel CTA from "Join Waitlist" to "Sign Up / Sign In"

Key files:

  • src/main.ts — Clerk initialization
  • src/services/auth.ts — new auth service
  • src/components/Panel.ts — update locked panel CTA (line ~300)
  • src/locales/en.json — update premium.joinWaitlist to "Sign In to Unlock"

Risk: @clerk/clerk-js headless is less documented than React SDK. Prototype mountSignIn() early to validate the approach.

Acceptance criteria:

  • Sign in / sign up modal works in vanilla TS app
  • User avatar + dropdown in navbar
  • Locked panel CTA says "Sign In to Unlock" (or "Upgrade to Pro" if already signed in as free)
  • Auth state persists across page refreshes

Issue #1.3: Tauri desktop auth flow

Title: feat(auth): Tauri desktop auth flow — PKCE + deep link callback

Labels: auth, desktop, P1 Priority: P1 | Size: L | Dependencies: #1.1

Description: Implement Clerk auth flow for the Tauri desktop app with proper session persistence.

Implementation:

  1. Register worldmonitor://auth/callback deep link URI scheme in Tauri config
  2. Use PKCE OAuth flow (Clerk supports this)
  3. On successful callback, store Clerk session token in macOS Keychain via existing setSecret() pattern
  4. Token lifecycle: refresh on app foreground, auto-refresh if <5min remaining
  5. Logout: clear keychain entry + clerk.signOut() + invalidate cached entitlements
  6. Fallback: if deep link fails, show one-time code flow (email-based)

Key files:

  • src-tauri/tauri.conf.json — register deep link
  • src-tauri/capabilities/default.json — add deep-link capability
  • src/services/runtime-config.ts — existing getSecretState/setSecret

Risk: Tauri WKWebView has known limitations. Use system browser for OAuth callback, pass token back via deep link.

Acceptance criteria:

  • Sign in works on macOS desktop app
  • Session persists across app restarts (keychain)
  • Token auto-refreshes
  • Sign out clears all cached state

Issue #1.4: Migrate waitlist registrations to users table

Title: feat(auth): migrate waitlist registrations → users table

Labels: auth, migration, P1 Priority: P1 | Size: M | Dependencies: #1.1

Description: Migrate existing Convex registrations table entries to the new users table.

Migration playbook:

  1. Dry-run: migrate to staging Convex first, validate counts match
  2. Dedupe: normalize emails, merge duplicate registrations by normalizedEmail
  3. Consent: existing Turnstile-verified registrations have implicit consent; send "your account is ready" email with opt-out link via Resend
  4. Create Clerk accounts: use Clerk Admin API to create user accounts for each registration
  5. Preserve data: copy referralCode, referralCount, source, appVersion
  6. Rollback: keep registrations table intact, only deprecate after 30-day validation period
  7. Validation: post-migration script compares registrations count vs users count, flags mismatches

Acceptance criteria:

  • All waitlist emails have corresponding users entries
  • Referral codes and counts preserved
  • "Account ready" emails sent via Resend
  • registrations table untouched (rollback safety)
  • Dry-run report shows 0 mismatches

Phase 2: Convex Schema Expansion (Weeks 12, parallel with Phase 1)


Issue #2.1: Core schema — users, subscriptions, entitlements, apiKeys, usage, savedViews, alertRules

Title: feat(backend): users/subscriptions/entitlements/apiKeys/usage/savedViews/alertRules schema

Labels: backend, convex, P0 Priority: P0 | Size: M | Dependencies: #0.1, #0.3

Description: Design and implement the full Convex schema for Pro features.

Schema:

// New tables alongside existing registrations + counters

users: {
  clerkId: string (indexed),
  email: string (indexed),
  name: string,
  stripeCustomerId?: string (indexed),
  referralCode: string (indexed),
  referralCount: number,
  createdAt: number,
  updatedAt: number,
}

subscriptions: {
  userId: Id<"users"> (indexed),
  stripeSubscriptionId: string (indexed),
  product: "pro" | "api_starter" | "api_business",
  status: "active" | "past_due" | "canceled" | "trialing",
  currentPeriodStart: number,
  currentPeriodEnd: number,
  cancelAtPeriodEnd: boolean,
  createdAt: number,
}

entitlements: {
  userId: Id<"users"> (indexed, unique),
  dashboardTier: "free" | "pro",
  apiTier: "none" | "starter" | "business",
  rateLimitTier: "free_anon" | "free_authed" | "pro" | "api_starter" | "api_business",
  features: string[],           // ["equity_research", "ai_briefs", "saved_views", ...]
  derivedFrom: Id<"subscriptions">[],
  computedAt: number,
}
// Derived projection — recomputed on every subscription change
// Single source of truth for ALL gating decisions

stripeEvents: {
  eventId: string (indexed, unique),
  processedAt: number,
  eventType: string,
}
// Idempotency table — prevents duplicate webhook processing

apiKeys: {
  userId: Id<"users"> (indexed),
  keyHash: string (indexed),     // SHA-256 hash — NEVER store plaintext
  prefix: string,                // first 8 chars for UI identification
  name: string,
  scopes: string[],              // ["read:market", "read:conflict", "*"]
  tier: "starter" | "business",
  expiresAt?: number,
  lastUsedAt?: number,
  createdAt: number,
  revokedAt?: number,
}
// 256-bit random keys (crypto.getRandomValues), prefixed wm_live_ / wm_test_
// Constant-time comparison via crypto.timingSafeEqual on hash

usage: {
  apiKeyId: Id<"apiKeys"> (indexed),
  date: string,                  // YYYY-MM-DD
  endpoint: string,
  count: number,
}

savedViews: {
  userId: Id<"users"> (indexed),
  name: string,
  panels: string[],
  mapLayers: object,
  watchlistSymbols: string[],
  createdAt: number,
}

alertRules: {
  userId: Id<"users"> (indexed),
  name: string,
  type: "threshold" | "event" | "keyword",
  config: object,
  channels: object[],
  enabled: boolean,
  createdAt: number,
}

auditLog: {
  userId: Id<"users"> (indexed),
  action: string,
  resource: string,
  metadata: object,
  ip?: string,
  createdAt: number,
}
// Structured audit for: auth events, key lifecycle, billing changes, entitlement decisions

Key file: convex/schema.ts

Acceptance criteria:

  • All tables created with proper indexes
  • Schema passes Convex validation (npx convex dev)
  • entitlements table has unique constraint on userId

Issue #2.2: User CRUD mutations & queries

Title: feat(backend): user CRUD mutations & queries

Labels: backend, convex, P0 Priority: P0 | Size: M | Dependencies: #2.1

Description: Implement Convex mutations and queries for user management.

Functions:

  • users.getByClerkId(clerkId) — query
  • users.getByApiKey(keyHash) — query (joins apiKeys → users → entitlements)
  • users.create({ clerkId, email, name }) — mutation (from Clerk webhook)
  • users.update({ userId, ...fields }) — mutation
  • users.anonymize(userId) — mutation (for account deletion — tombstone PII, preserve audit/billing)
  • entitlements.recompute(userId) — mutation (rebuild from active subscriptions)
  • entitlements.getByUserId(userId) — query
  • auditLog.write({ userId, action, resource, metadata, ip }) — mutation

Acceptance criteria:

  • CRUD operations work via Convex dashboard
  • recompute correctly derives entitlements from multiple subscriptions
  • Anonymize replaces PII with deleted-{hash} but preserves audit records
  • Automated: unit tests for entitlement recomputation (free, pro, api_starter, pro+api_business)

Issue #2.3: API key generation, hashing, and validation

Title: feat(backend): API key generation (wm_live_xxx), hashing, validation

Labels: backend, convex, P1 Priority: P1 | Size: M | Dependencies: #2.1

Description: Implement secure API key lifecycle management.

Implementation:

  1. Generation: 256-bit random via crypto.getRandomValues(), prefixed wm_live_ or wm_test_
  2. Storage: SHA-256 hash only in Convex. Plaintext returned once on creation — never again.
  3. Validation: constant-time comparison via crypto.timingSafeEqual on hashed input
  4. Scopes: per-key permission list (e.g., ["read:market", "read:conflict", "*"])
  5. Expiry: optional expiresAt field
  6. Rotation: create new key → user confirms → revoke old key
  7. Audit: all create/revoke/rotate events logged to auditLog

Functions:

  • apiKeys.create({ userId, name, scopes, tier }) — returns plaintext key ONCE
  • apiKeys.validate(keyHash) — query, returns entitlements if valid
  • apiKeys.revoke(keyId) — mutation, sets revokedAt
  • apiKeys.listByUser(userId) — query (returns prefix + metadata, never hash)

Acceptance criteria:

  • Key format: wm_live_<32 hex chars>
  • Plaintext never stored or logged
  • Revoked keys return 401
  • Expired keys return 401
  • Automated: hash/verify round-trip test, constant-time comparison test

Issue #2.4: Usage tracking — daily counters

Title: feat(backend): usage tracking — daily counters per API key per endpoint

Labels: backend, convex, P2 Priority: P2 | Size: S | Dependencies: #2.1

Description: Track API usage per key per day for billing and dashboard display.

Functions:

  • usage.record(apiKeyId, endpoint) — mutation (increment or create daily counter)
  • usage.getDaily(apiKeyId, date) — query
  • usage.getMonthly(apiKeyId, month) — query (aggregate)

Acceptance criteria:

  • Daily counters increment correctly
  • Monthly aggregation sums daily values

Phase 3: Payments — Stripe (Weeks 34)


Issue #3.1: Stripe products and prices

Title: feat(payments): Stripe products — Pro Monthly/Annual + API Starter/Business

Labels: payments, infra, P0 Priority: P0 | Size: S | Dependencies: #0.2

Description: Create Stripe products and price objects for all tiers.

Products:

  1. World Monitor Pro Monthly — $X/mo
  2. World Monitor Pro Annual — $X/yr (discount)
  3. World Monitor API Starter — $Y/mo (1,000 req/day, 5 webhook rules)
  4. World Monitor API Business — $Z/mo (50,000 req/day, unlimited webhooks + SLA)

Environment variables:

  • STRIPE_SECRET_KEY, STRIPE_PUBLISHABLE_KEY, STRIPE_WEBHOOK_SECRET
  • STRIPE_PRO_MONTHLY_PRICE_ID, STRIPE_PRO_ANNUAL_PRICE_ID
  • STRIPE_API_STARTER_PRICE_ID, STRIPE_API_BUSINESS_PRICE_ID

Acceptance criteria:

  • Products created in Stripe test mode
  • Price IDs stored as env vars
  • .env.example updated

Issue #3.2: Checkout flow via Convex HTTP action

Title: feat(payments): checkout flow — Convex HTTP action → Stripe Checkout redirect

Labels: payments, backend, P0 Priority: P0 | Size: M | Dependencies: #2.1, #3.1

Description: Create a Convex HTTP action that generates a Stripe Checkout Session and returns the URL.

Implementation:

  1. Convex HTTP action receives authenticated user ID + product choice
  2. Look up or create Stripe customer (store stripeCustomerId on user)
  3. Create Stripe Checkout Session with success_url and cancel_url
  4. Return checkout URL for client-side redirect
  5. Handle upgrade (free → pro), API tier purchase, and plan switching

Acceptance criteria:

  • Authenticated user can initiate checkout
  • Redirects to Stripe Checkout
  • Success URL leads back to dashboard with success message
  • Stripe customer ID stored on user record

Issue #3.3: Stripe webhook handler

Title: feat(payments): Stripe webhook handler — subscription lifecycle in Convex

Labels: payments, backend, P0 Priority: P0 | Size: L | Dependencies: #3.2

Description: Handle Stripe webhook events to manage subscription lifecycle in Convex.

Safety requirements:

  • Signature verification: stripe.webhooks.constructEvent() with STRIPE_WEBHOOK_SECRET
  • Idempotency: check stripeEvents table by event.id before processing; skip duplicates
  • Event age monitoring: log alerts for events older than 5 minutes (indicates outage/retry), but do NOT reject them — legitimate Stripe retries can arrive late
  • Subscription reconciliation: do NOT use a forward-only state machine. Fetch current subscription object via stripe.subscriptions.retrieve() and reconcile status, current_period_end, and items. This correctly handles past_due → active, resumed subscriptions, and plan switches.
  • Dead-letter: failed processing logged to auditLog with full event payload for manual retry
  • Entitlement recomputation: every subscription change triggers recomputeEntitlements(userId) + Redis cache invalidation

Webhook events:

  • checkout.session.completed → create subscription, link to user, recompute entitlements
  • invoice.paid → renew subscription period
  • invoice.payment_failed → update status to past_due, send warning email via Resend
  • customer.subscription.updated → reconcile from Stripe object, recompute entitlements
  • customer.subscription.deleted → mark canceled, recompute entitlements (downgrade)

Acceptance criteria:

  • All 5 webhook events handled correctly
  • Duplicate events are idempotent (no double processing)
  • Entitlements update within seconds of payment
  • Failed webhooks logged for manual retry
  • Automated: webhook contract tests via Stripe CLI trigger

Issue #3.4: Pricing page

Title: feat(payments): pricing page — replace waitlist form with real plans + checkout

Labels: payments, frontend, P1 Priority: P1 | Size: M | Dependencies: #3.2

Description: Replace the current waitlist form on /pro with a real pricing page that initiates checkout.

Implementation:

  • Side-by-side comparison: Free vs Pro vs API vs Enterprise
  • Monthly/annual toggle for Pro
  • "Get Started" buttons → Clerk sign-in (if not authed) → Stripe Checkout
  • "Coming Soon" section for Enterprise with "Contact Sales" CTA
  • Integrate with existing i18n (23 languages)

Acceptance criteria:

  • Pricing page shows all tiers with features
  • Checkout flow works end-to-end
  • Works in all 23 supported languages

Issue #3.5: Billing management via Stripe Customer Portal

Title: feat(payments): billing management via Stripe Customer Portal

Labels: payments, frontend, P1 Priority: P1 | Size: S | Dependencies: #3.3

Description: Add a link/button that redirects to Stripe Customer Portal for self-service billing management (update payment method, view invoices, cancel subscription, switch plans).

Acceptance criteria:

  • Portal link accessible from /account/billing
  • User can update payment method, view invoices, cancel

Issue #3.6: 14-day free trial for Pro

Title: feat(payments): 14-day free trial for Pro

Labels: payments, backend, P2 Priority: P2 | Size: S | Dependencies: #3.3

Description: Configure Stripe to offer a 14-day trial for Pro tier (no credit card required). Trial expiry → email reminder via Resend. Auto-downgrade on trial end via webhook.

Acceptance criteria:

  • Trial activates without credit card
  • Reminder email sent 3 days before trial ends
  • Auto-downgrade on expiry triggers entitlement recomputation

Phase 4: Feature Gating (Week 5)


Issue #4.1: Server-side entitlement verification in gateway.ts

Title: feat(gating): server-side entitlement verification in gateway.ts

Labels: gating, backend, P0 Priority: P0 | Size: L | Dependencies: #2.2, #2.3

Description: Add entitlement-aware middleware to the server gateway so pro-only endpoints are enforced server-side.

Implementation:

  1. After validateApiKey() (gateway.ts line 161), inject entitlement check
  2. Look up user entitlements from Redis cache (ent:{userId} or ent:key:{keyHash})
  3. If cache miss, query Convex, populate cache with 60s TTL
  4. Active invalidation: recomputeEntitlements() deletes Redis cache entry immediately via Upstash REST API
  5. Fail-closed: if Redis AND Convex both unavailable, return 503 (never grant unauthorized access)
  6. Check endpoint against entitlement matrix
  7. Return 403 { error: "Upgrade required", requiredPlan: "pro", upgradeUrl: "/pro" } for gated endpoints

Entitlement matrix:

Endpoint Category free_anon free_authed pro api_starter api_business
Public data (seismology, news, weather)
Market quotes, crypto, commodities
Equity research (financials, targets)
AI briefs, flash alerts
Economy analytics (correlations)
Risk scoring, scenario analysis

API key migration (dual-read rollout):

  1. Phase A: validate against BOTH static WORLDMONITOR_VALID_KEYS AND new entitlements. Log comparison metrics.
  2. Phase B: after 1 week with 0 mismatches, flip flag to new system only. Keep env var as emergency rollback.
  3. Phase C: remove static key validation code after 30 days.

Key files:

  • server/gateway.ts — main middleware injection point
  • api/_api-key.js — extend validation logic
  • server/_shared/rate-limit.ts — rate limit by entitlement tier

Acceptance criteria:

  • Free user gets 403 on equity research endpoint
  • Pro user gets 200 on equity research endpoint
  • API starter gets 200 on data endpoints, 403 on dashboard-only features
  • Fail-closed: 503 when Redis + Convex both down
  • Dual-read metrics dashboard shows match/mismatch counts
  • Automated: E2E entitlement gating tests per tier

Issue #4.2: Client-side plan context service

Title: feat(gating): client-side plan context service (src/services/plan-context.ts)

Labels: gating, frontend, P0 Priority: P0 | Size: M | Dependencies: #1.2, #2.2

Description: Create a client-side service that exposes user plan/entitlements for UI gating.

Implementation:

  1. New service: src/services/plan-context.ts
  2. On auth, query user entitlements from Convex
  3. Expose helpers: isPro(), hasApiAccess(), getPlan(), hasFeature(name)
  4. Replace ALL getSecretState('WORLDMONITOR_API_KEY').present checks with plan context
  5. Works for both web (Clerk session) and desktop (Clerk + Tauri keychain)
  6. Include computedAt timestamp for staleness detection

Key files to update:

  • src/components/Panel.ts — replace getSecretState check
  • src/components/DeckGLMap.ts — replace layer premium check
  • src/components/GlobeMap.ts — replace layer premium check
  • src/app/panel-layout.ts — replace _wmKeyPresent logic

Acceptance criteria:

  • isPro() returns true for pro users, false for free
  • All getSecretState('WORLDMONITOR_API_KEY') references replaced
  • Plan context updates within 60s of subscription change

Issue #4.3: Refactor panel/layer premium flags

Title: feat(gating): refactor panel/layer premium flags → plan context

Labels: gating, frontend, P1 Priority: P1 | Size: M | Dependencies: #4.2

Description: Update panel and map layer configurations to use the new plan context instead of desktop-only isDesktopRuntime() checks.

Changes:

  • src/config/panels.ts — premium flags read from plan context, apply to web AND desktop
  • src/config/map-layer-definitions.ts — same
  • Locked panel CTA: "Upgrade to Pro" → links to pricing page (not waitlist)
  • Expand locked panels beyond current 2+4 to cover all pro-tier features

Acceptance criteria:

  • Premium gating works on web (not just desktop)
  • Locked panels link to /pro pricing page
  • Enhanced panels show "PRO" badge for free users

Issue #4.4: Per-plan rate limiting

Title: feat(gating): per-plan rate limiting in gateway

Labels: gating, backend, P1 Priority: P1 | Size: M | Dependencies: #4.1

Description: Implement tiered rate limiting based on user plan.

Rate limits:

Tier Requests/day Requests/min
Free (no auth) 100 5
Free (authenticated) 500 10
Pro 10,000 60
API Starter 1,000 30
API Business 50,000 300

Unauthenticated identity:

  • Key: cf-connecting-ip + endpoint path bucket
  • Trusted-proxy rule: only honor cf-connecting-ip from Cloudflare IP ranges. Non-CF sources fall back to actual remote address. Log spoofing attempts.
  • Turnstile challenge at 50% daily quota
  • Abuse flag at 3x daily limit

Acceptance criteria:

  • Free user rate-limited at 100 req/day
  • Pro user rate-limited at 10,000 req/day
  • Rate limit headers returned: X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset
  • Automated: rate limit integration tests per tier

Phase 5: User Dashboard (Weeks 67)


Issue #5.1: Account page

Title: feat(dashboard): /account page — profile, plan badge, API keys

Labels: dashboard, frontend, P1 Priority: P1 | Size: M | Dependencies: #1.2, #2.2, #2.3

Description: Create an account page at /account showing profile info, current plan, and API key management.

Sections:

  1. Profile: name, email (from Clerk), avatar
  2. Plan: current plan badge, expiry date, upgrade button
  3. API Keys: list keys (prefix only), create, copy (once), revoke, regenerate
  4. Referral: referral code, count, share link (preserved from waitlist)

Acceptance criteria:

  • Profile info displayed from Clerk
  • Plan badge shows current tier
  • API key CRUD works (create shows plaintext once, subsequent views show prefix only)
  • Referral stats visible

Issue #5.2: Billing page

Title: feat(dashboard): /account/billing — invoices, plan management

Labels: dashboard, frontend, P1 Priority: P1 | Size: M | Dependencies: #3.3

Description: Create a billing page showing current subscription, next payment date, and link to Stripe Customer Portal.

Sections:

  1. Current plan + next billing date + amount
  2. Payment method (last 4 digits)
  3. Link to Stripe Customer Portal for self-service management
  4. Upgrade/downgrade buttons

Acceptance criteria:

  • Shows current plan and billing cycle
  • Stripe portal link works
  • Upgrade/downgrade triggers new checkout

Issue #5.3: API usage stats

Title: feat(dashboard): API usage stats with daily/monthly charts

Labels: dashboard, frontend, P2 Priority: P2 | Size: M | Dependencies: #2.4, #5.1

Description: Display API usage charts on the account page (daily and monthly breakdowns).

Acceptance criteria:

  • Daily usage bar chart
  • Monthly aggregation
  • Per-endpoint breakdown

Issue #5.4: Notification preferences & delivery channels

Title: feat(dashboard): notification preferences & delivery channels config

Labels: dashboard, frontend, P2 Priority: P2 | Size: M | Dependencies: #5.1

Description: Settings page for configuring notification delivery channels (Slack webhook URL, Telegram bot, Discord webhook, email preferences).

Acceptance criteria:

  • Add/remove delivery channels
  • Test notification button per channel
  • Channel credentials encrypted at rest

Issue #5.5: API documentation page

Title: feat(docs): interactive API docs page with OpenAPI 3.1 from proto defs

Labels: dashboard, docs, P2 Priority: P2 | Size: L | Dependencies: #4.1

Description: Generate OpenAPI 3.1 spec from existing sebuf proto definitions (50+ RPCs across 15+ domains). Host interactive API explorer.

Implementation:

  • Parse proto files with (sebuf.http.config) annotations
  • Generate OpenAPI 3.1 spec
  • Host via Swagger UI or Redoc at /developers or /api/docs
  • Per-endpoint plan requirements documented
  • Code examples in curl, Python, JS/TS

Acceptance criteria:

  • OpenAPI spec covers all public endpoints
  • Interactive explorer works
  • Plan requirements shown per endpoint

Phase 6: Pro Features (Weeks 812)


Issue #6.1: Equity research data pipeline

Title: feat(pro): equity research — financials, analyst targets, valuation metrics

Labels: pro-feature, backend, P1 Priority: P1 | Size: XL | Dependencies: #4.1

Description: Build a new data pipeline for equity research features (pro-only).

Data to add:

  • Financial statements (income, balance sheet, cash flow)
  • Analyst consensus price targets
  • Valuation metrics (PE, PB, EV/EBITDA)
  • Earnings calendar

Sources (evaluate): Finnhub premium, Financial Modeling Prep (FMP), Alpha Vantage

Implementation:

  • New sebuf proto service: worldmonitor/equity/v1/
  • RPCs: get-company-financials, get-analyst-consensus, get-valuation-metrics, list-earnings-calendar
  • Redis caching via cachedFetchJson pattern
  • New panel: Equity Research dashboard (pro-only, gated via entitlements)

Acceptance criteria:

  • Financial data available for major US stocks
  • Analyst targets displayed with consensus rating
  • Equity panel shows for pro users, locked for free
  • Data refreshes at least daily

Issue #6.2: AI daily briefs engine

Title: feat(pro): AI daily briefs engine — scheduled LLM summaries

Labels: pro-feature, backend, P1 Priority: P1 | Size: XL | Dependencies: #4.1, #6.5

Description: Build a scheduled AI briefing system that synthesizes overnight developments and delivers via configured channels.

Implementation:

  1. Cron job (Railway or Convex cron) runs at configurable time per user timezone
  2. Aggregates latest data from Redis bootstrap keys (40+ keys exist)
  3. Ranks events by user's configured focus areas (markets, geopolitics, energy, etc.)
  4. Generates structured brief via Groq LLM (infrastructure exists in deduct-situation.ts)
  5. Stores brief in Convex briefs table
  6. Delivers via configured channels (email, Slack, Telegram, Discord)

Flash alerts: real-time event detection → LLM classification (existing classify-event RPC) → push notification

Acceptance criteria:

  • Daily brief generated and delivered
  • Focus areas configurable per user
  • Brief stored and viewable in dashboard
  • Flash alerts delivered within 5 minutes of event

Issue #6.3: Sub-60s data refresh

Title: feat(pro): sub-60s data refresh for pro users

Labels: pro-feature, backend, P1 Priority: P1 | Size: L | Dependencies: #4.1

Description: Reduce data refresh interval for pro users from 5-15 minutes to <60 seconds.

Phased approach:

  1. Phase 1: reduce client-side polling interval based on plan (simplest — just change DataLoaderManager interval for pro users)
  2. Phase 2: Server-Sent Events (SSE) for high-frequency data (markets, alerts) — push new data as it arrives

Acceptance criteria:

  • Pro users see data refresh <60s
  • Free users unchanged (5-15 min)
  • Server load monitored (10x more requests from pro)

Issue #6.4: Server-side watchlists & custom views

Title: feat(pro): persistent server-side watchlists & custom views

Labels: pro-feature, fullstack, P1 Priority: P1 | Size: M | Dependencies: #2.1, #4.2

Description: Migrate watchlists from localStorage to Convex savedViews table for cross-device sync.

Currently localStorage-only:

  • src/services/market-watchlist.ts
  • src/services/aviation/watchlist.ts

Implementation:

  • On first sign-in, import existing localStorage watchlists to Convex
  • Sync changes bidirectionally (Convex → client on load, client → Convex on change)
  • Cross-device sync (web ↔ desktop)

Acceptance criteria:

  • Watchlists persist across devices
  • localStorage data migrated on first sign-in
  • Offline-first: works without connection, syncs on reconnect

Issue #6.5: Delivery channels — Slack, Telegram, Discord, Email

Title: feat(pro): delivery channels — Slack, Telegram, Discord, Email

Labels: pro-feature, backend, P1 Priority: P1 | Size: XL | Dependencies: #2.1

Description: Build multi-channel delivery infrastructure for AI briefs and alerts.

Channels (in priority order):

  1. Email — Resend (already integrated). Extend for formatted briefs/alerts.
  2. Slack — incoming webhook URL (user provides). Format messages with blocks.
  3. Telegram — Bot API. Create @WorldMonitorBot. User starts conversation, store chat_id.
  4. Discord — webhook URL (user provides). Format with embeds.
  5. WhatsApp — P3 (requires Twilio/Meta business verification, highest cost)

Security:

  • Webhook URL allowlisting: only hooks.slack.com, discord.com/api/webhooks, Telegram API
  • Secrets encrypted via server-managed envelope encryption (APP_ENCRYPTION_KEY env var)
  • PII redacted from outbound payloads
  • Per-channel signing/verification where supported

Acceptance criteria:

  • Email delivery works (formatted brief)
  • Slack webhook delivery works
  • Telegram bot delivery works
  • Discord webhook delivery works
  • Secrets encrypted at rest
  • Test notification button per channel

Issue #6.6: Economy analytics — correlation views

Title: feat(pro): economy analytics — GDP/inflation/rates correlation views

Labels: pro-feature, frontend, P2 Priority: P2 | Size: L | Dependencies: #4.1

Description: Build correlation views on top of existing economic data (FRED, BIS, World Bank RPCs already exist).

New visualizations:

  • GDP growth vs market performance
  • Inflation trends vs central bank rates
  • Growth cycle detection and labeling
  • Cross-country comparison charts

Acceptance criteria:

  • Correlation charts display correctly
  • Data from existing FRED/BIS/World Bank endpoints
  • Pro-only (gated via entitlements)

Issue #6.7: Risk monitoring — convergence alerting & scenario analysis

Title: feat(pro): risk monitoring — convergence alerting & scenario analysis

Labels: pro-feature, fullstack, P2 Priority: P2 | Size: L | Dependencies: #4.1

Description: Enhance existing risk analytics with scenario analysis and convergence alerting.

Existing engines (enhance, don't rebuild):

  • src/services/geo-convergence.ts — convergence detection
  • src/services/focal-point-detector.ts — focal point detection
  • src/services/country-instability.ts — CII scoring
  • src/services/signal-aggregator.ts — signal aggregation

New:

  • Scenario analysis UI (what-if modeling)
  • Convergence alerting (push when signals converge in a region)
  • Risk trend visualization over time

Acceptance criteria:

  • Scenario analysis UI works
  • Convergence alerts delivered via configured channels
  • Pro-only (gated via entitlements)

Issue #6.8: 22-services-1-key

Title: feat(pro): 22-services-1-key — replace BYOK with Pro key for all services

Labels: pro-feature, fullstack, P1 Priority: P1 | Size: M | Dependencies: #4.1

Description: Pro users should NOT need to configure individual API keys for Finnhub, FRED, ACLED, etc. A single World Monitor Pro subscription gives access to all 24 services.

Implementation:

  • Server-side: pro requests use World Monitor's own upstream API keys (already configured as env vars)
  • Free tier: continues using BYOK via desktop settings panel
  • Gateway identifies pro user → skips BYOK requirement → uses server-side keys for upstream calls

Key files:

  • src/services/settings-constants.ts — 20+ key definitions
  • server/gateway.ts — skip BYOK check for pro users

Acceptance criteria:

  • Pro user sees data without configuring any individual API keys
  • Free user still uses BYOK
  • No upstream API key leakage to client

Phase 7: API Tier (Weeks 1014, separate product)


Issue #7.1: API key issuance & management portal

Title: feat(api-tier): API key issuance & management portal

Labels: api-tier, fullstack, P1 Priority: P1 | Size: M | Dependencies: #2.3, #5.1

Description: Extend the account page with API key management specifically for API tier subscribers.

Features:

  • Create multiple keys with different names/scopes
  • View usage per key
  • Rotate keys (create new → confirm → revoke old)
  • Set per-key rate limits within tier allowance

Acceptance criteria:

  • Multiple keys can be created
  • Per-key usage visible
  • Key rotation flow works

Issue #7.2: Per-key usage tracking with daily limits

Title: feat(api-tier): per-key usage tracking with daily limits

Labels: api-tier, backend, P1 Priority: P1 | Size: M | Dependencies: #2.4, #4.4

Description: Track and enforce daily usage limits per API key based on tier (Starter: 1,000/day, Business: 50,000/day).

Implementation:

  • Increment usage counter on each request
  • Check daily total before processing
  • Return 429 with Retry-After header when limit exceeded
  • Dashboard shows usage vs limit

Acceptance criteria:

  • Daily limit enforced per key
  • 429 returned with retry info when exceeded
  • Usage dashboard shows consumption

Issue #7.3: OpenAPI 3.1 spec from sebuf protos

Title: feat(api-tier): OpenAPI 3.1 spec auto-generation from sebuf protos

Labels: api-tier, docs, P2 Priority: P2 | Size: L | Dependencies: None

Description: Auto-generate OpenAPI 3.1 specification from existing sebuf proto definitions.

Acceptance criteria:

  • Spec covers all public RPC endpoints
  • Plan requirements documented per endpoint
  • Code examples in curl, Python, JS/TS

Issue #7.4: Webhook delivery system

Title: feat(api-tier): webhook delivery system with retry & HMAC signatures

Labels: api-tier, backend, P2 Priority: P2 | Size: L | Dependencies: #7.1

Description: Allow API tier subscribers to configure webhook endpoints for event delivery.

Implementation:

  • Convex table: webhookEndpoints (userId, url, events, secret)
  • HMAC-SHA256 signature on each delivery
  • Exponential backoff retry (3 attempts)
  • Starter: 5 webhook rules; Business: unlimited
  • Delivery log with status codes

Acceptance criteria:

  • Webhook delivery works with signature
  • Failed deliveries retried with backoff
  • Tier limits enforced

Phase 8: Enterprise (Months 412+, all P3)


Issue #8.1: Organization accounts & team management

Title: feat(enterprise): organization accounts & team management

Labels: enterprise, fullstack, P3 Priority: P3 | Size: XL | Dependencies: #1.1

Description: Multi-user organizations with shared dashboards, seat management, and invite flow.


Issue #8.2: RBAC

Title: feat(enterprise): RBAC (role-based access control)

Labels: enterprise, backend, P3 Priority: P3 | Size: L | Dependencies: #8.1

Description: Role-based access: admin, analyst, viewer. Per-role permissions for panels, data access, and configuration.


Issue #8.3: SSO (SAML/OIDC)

Title: feat(enterprise): SSO (SAML/OIDC via Clerk Enterprise)

Labels: enterprise, auth, P3 Priority: P3 | Size: L | Dependencies: #8.1

Description: Enterprise SSO via Clerk's Enterprise plan. Requires Clerk Enterprise subscription.


Issue #8.4: TV/SOC display mode

Title: feat(enterprise): TV/SOC display mode

Labels: enterprise, frontend, P3 Priority: P3 | Size: M | Dependencies: None

Description: Full-screen dashboard for wall displays with auto-rotating panels and custom layouts. Some exists in src/services/tv-mode.ts.


Issue #8.5: White-label & embeddable panels

Title: feat(enterprise): white-label & embeddable panels

Labels: enterprise, frontend, P3 Priority: P3 | Size: XL

Description: Your brand, your domain, your desktop app. Embeddable iframe panels (50+ available).


Issue #8.6: Satellite imagery & SAR integration

Title: feat(enterprise): satellite imagery & SAR integration

Labels: enterprise, backend, P3 Priority: P3 | Size: XL

Description: Live-edge satellite imagery and SAR (Synthetic Aperture Radar) with change detection. Requires partnerships with Maxar/Planet.


Issue #8.7: AI agents with investor personas & MCP

Title: feat(enterprise): AI agents with investor personas & MCP

Labels: enterprise, backend, P3 Priority: P3 | Size: XL

Description: Autonomous intelligence agents using Model Context Protocol. Connect as tool to Claude, GPT, or custom LLMs.


Issue #8.8: 100+ data connectors

Title: feat(enterprise): 100+ data connectors

Labels: enterprise, backend, P3 Priority: P3 | Size: XL

Description: PostgreSQL, Snowflake, Splunk, Sentinel, Jira, Slack, Teams. Export to PDF, PowerPoint, CSV, GeoJSON. Multi-quarter effort.


Issue #8.9: On-premises / air-gapped deployment

Title: feat(enterprise): on-premises / air-gapped deployment

Labels: enterprise, infra, P3 Priority: P3 | Size: XL

Description: Docker-based on-premises deployment with air-gapped option. Full architecture rethink required — currently all cloud-native (Vercel + Convex + Railway).


Issue #8.10: Android TV app

Title: feat(enterprise): Android TV app

Labels: enterprise, frontend, P3 Priority: P3 | Size: XL

Description: Dedicated Android TV app for SOC walls and trading floors. Separate codebase.


Key Risks

  1. Vanilla TS + Clerk: @clerk/clerk-js headless is less documented than React SDK. Prototype early.
  2. Edge + Convex plan lookups: Vercel Edge can't import Convex. Must cache in Upstash Redis with active invalidation on webhook events.
  3. Sub-60s refresh at scale: 10x more requests from pro users. SSE/WebSocket needed long-term.
  4. API as separate product: Multiple Stripe subscriptions per user adds billing complexity. entitlements projection table mitigates scattered logic.
  5. Desktop auth + Tauri WKWebView: Known limitations. PKCE flow with worldmonitor:// deep link callback.
  6. API key migration outage: Dual-read rollout (old + new in parallel) with comparison metrics before cutover.

Observability & Operations

  • Audit logging: all auth events, key lifecycle, billing changes, entitlement decisions → auditLog table
  • Structured metrics: entitlement cache hit/miss ratio, webhook processing latency, API key validation latency
  • Alerting: Slack/PagerDuty for webhook failures, entitlement errors, rate limit abuse spikes
  • Incident rollback plan:
    • Auth cutover: feature flag to disable Clerk and revert to static API key validation
    • Payment cutover: Stripe test mode for staging; webhook replay via Stripe Dashboard
    • Migration rollback: registrations table preserved 30 days alongside users

Data Retention & Privacy

  • API usage data: retained 90 days, then aggregated to monthly summaries
  • Audit logs: retained 1 year, then archived to cold storage
  • AI-generated briefs: retained 30 days per user, older briefs auto-deleted
  • PII handling: email + name stored in Convex (encrypted at rest). No PII in Redis cache. No PII in outbound delivery payloads.
  • Account deletion: Clerk user.deleted webhook → delete user data. Audit logs and billing records are NOT deleted — user identifiers anonymized/tombstoned (deleted-{hash}). Stripe customer marked deleted; invoice history retained by Stripe.