Documentation Index
Fetch the complete documentation index at: https://worldmonitor.app/docs/llms.txt
Use this file to discover all available pages before exploring further.
Overview
Desktop cloud fallback is gated on a WORLDMONITOR_API_KEY. Without a valid key, the desktop app operates local-only (sidecar). A registration form collects emails via Convex DB for future key distribution.
Architecture
Desktop App Cloud (Vercel)
┌──────────────────┐ ┌──────────────────────┐
│ fetch('/api/...')│ │ api/[domain]/v1/[rpc]│
│ │ │ │ │ │
│ ┌──────▼───────┐ │ │ ┌──────▼───────┐ │
│ │ sidecar try │ │ │ │ validateApiKey│ │
│ │ (local-first)│ │ │ │ (origin-aware)│ │
│ └──────┬───────┘ │ │ └──────┬───────┘ │
│ fail │ │ │ 401 if invalid │
│ ┌──────▼───────┐ │ fallback │ │
│ │ WM key check │─┼──────────────►│ ┌──────────────┐ │
│ │ (gate) │ │ +header │ │ route handler │ │
│ └──────────────┘ │ │ └──────────────┘ │
└──────────────────┘ └──────────────────────┘
Required Environment Variables
Vercel
| Variable | Description | Example |
|---|
WORLDMONITOR_VALID_KEYS | Comma-separated list of valid API keys | wm_abc123def456,wm_xyz789 |
CONVEX_URL | Convex deployment URL (from npx convex deploy) | https://xyz-123.convex.cloud |
Generating API keys
Keys must be at least 16 characters (validated client-side). Recommended format:
# Generate a key
openssl rand -hex 24 | sed 's/^/wm_/'
# Example output: wm_a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6
Add to WORLDMONITOR_VALID_KEYS in Vercel dashboard (comma-separated, no spaces).
Convex Setup
First-time deployment
# 1. Install (already in package.json)
npm install
# 2. Login to Convex
npx convex login
# 3. Initialize project (creates .env.local with CONVEX_URL)
npx convex init
# 4. Deploy schema and functions
npx convex deploy
# 5. Copy the deployment URL to Vercel env vars
# The URL is printed by `npx convex deploy` and saved in .env.local
Verify Convex deployment
# Typecheck Convex functions
npx convex dev --typecheck
# Open Convex dashboard to see registrations
npx convex dashboard
Schema
The registrations table stores:
| Field | Type | Description |
|---|
email | string | Original email (for display) |
normalizedEmail | string | Lowercased email (for dedup) |
registeredAt | number | Unix timestamp |
source | string? | Where the registration came from |
appVersion | string? | Desktop app version |
Indexed by normalizedEmail for duplicate detection.
Security Model
Client-side (desktop app)
installRuntimeFetchPatch() checks WORLDMONITOR_API_KEY before allowing cloud fallback
- Key must be present AND valid (min 16 chars)
secretsReady promise ensures secrets are loaded before first fetch (2s timeout)
- Fail-closed: any error in key check blocks cloud fallback
Server-side (Vercel edge)
api/_api-key.js validates X-WorldMonitor-Key header on sebuf routes
- Origin-aware: desktop origins (
tauri.localhost, tauri://, asset://) require a key
- Web origins (
worldmonitor.app) pass through without a key
- Non-desktop origin with key header: key is still validated
- Invalid key returns
401 { error: "Invalid API key" }
CORS
X-WorldMonitor-Key is allowed in both server/cors.ts and api/_cors.js.
Verification Checklist
After deployment:
Files Reference
| File | Role |
|---|
src/services/runtime.ts | Client-side key gate + header attachment |
src/services/runtime-config.ts | WORLDMONITOR_API_KEY type, validation, secretsReady |
api/_api-key.js | Server-side key validation (origin-aware) |
api/[domain]/v1/[rpc].ts | Sebuf gateway — calls validateApiKey |
api/register-interest.js | Registration endpoint → Convex |
server/cors.ts / api/_cors.js | CORS headers with X-WorldMonitor-Key |
src/components/WorldMonitorTab.ts | Settings UI for key + registration |
convex/schema.ts | Convex DB schema |
convex/registerInterest.ts | Convex mutation |