Files
worldmonitor/docs/API_KEY_DEPLOYMENT.md
Elie Habib a388afe400 feat: API key gating for desktop cloud fallback + registration (#215)
* 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.
2026-02-21 10:36:23 +00:00

5.3 KiB

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