Files
worldmonitor/docs/API_KEY_DEPLOYMENT.md
Elie Habib 01a7a791ab feat(docs): add Mintlify documentation site at /docs (#1444)
Set up Mintlify to serve docs at worldmonitor.app/docs via Vercel rewrites
proxying to worldmonitor.mintlify.dev.

- Add mint.json with navigation (5 doc groups + 22 OpenAPI API references)
- Add Vercel rewrites for /docs, exclude from SPA catch-all and no-cache rules
- Exclude /docs from CSP headers (Mintlify manages its own scripts/styles)
- Add frontmatter to all 18 navigation docs for proper Mintlify rendering
- Fix internal links from ./FILE.md to /FILE format for Mintlify routing
- Convert ../path links to GitHub URLs (source code references)
- Add .mintlifyignore for internal docs (Docs_To_Review, roadmap, etc.)
- Copy app icon as logo.png and favicon.png
2026-03-11 23:12:54 +04:00

5.6 KiB

title, description
title description
API Key Gating & Registration — Deployment Guide 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.

API Key Gating & Registration — Deployment Guide

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:

  • Set WORLDMONITOR_VALID_KEYS in Vercel
  • Set CONVEX_URL in Vercel
  • Run npx convex deploy to push schema
  • Desktop without key: cloud fallback blocked (console shows cloud fallback blocked)
  • Desktop with invalid key: sebuf requests get 401
  • Desktop with valid key: cloud fallback works as before
  • Web access: no key required, works normally
  • Registration form: submit email, check Convex dashboard
  • Duplicate email: shows "already registered"
  • Existing settings tabs (LLMs, API Keys, Debug) unchanged

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