File size: 6,295 Bytes
d705bb5 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 | ---
title: "API Key Gating & Registration β Deployment Guide"
description: "Deploying WORLDMONITOR_API_KEY gating for the desktop app β cloud fallback, local-only fallback, and Convex-backed registration email capture."
---
## 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:
```bash
# 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
```bash
# 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
```bash
# 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`.
### Local Vercel env dumps
Do not keep Vercel env exports in the repository root. `.env.vercel-backup`
and `.env.vercel-export` are ignored by Git, but they are still plaintext
production secret dumps that local tools, editor agents, backup software, or
dependency install scripts can read.
The pre-push hook fails when either file exists. Pull environment values only
when needed, work from a short-lived local env file, and delete the file after
use. Secret rotation and deletion from developer machines are operational
tasks; rotate exposed keys through the owning vendor dashboards, prioritizing
LLM, payment, auth, Redis, and Convex credentials.
## 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 |
|