| --- |
| 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." |
| --- |
| |
|
|
| 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. |
|
|
| |
|
|
| ``` |
| 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 │ │ |
| │ └──────────────┘ │ │ └──────────────┘ │ |
| └──────────────────┘ └──────────────────────┘ |
| ``` |
|
|
| |
|
|
| |
|
|
| | 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` | |
|
|
| |
|
|
| Keys must be at least 16 characters (validated client-side). Recommended format: |
|
|
| ```bash |
| |
| openssl rand -hex 24 | sed 's/^/wm_/' |
| |
| ``` |
|
|
| Add to `WORLDMONITOR_VALID_KEYS` in Vercel dashboard (comma-separated, no spaces). |
|
|
| |
|
|
| |
|
|
| ```bash |
| |
| npm install |
|
|
| |
| npx convex login |
|
|
| |
| npx convex init |
|
|
| |
| npx convex deploy |
|
|
| |
| |
| ``` |
|
|
| |
|
|
| ```bash |
| |
| npx convex dev --typecheck |
|
|
| |
| npx convex dashboard |
| ``` |
|
|
| |
|
|
| 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. |
|
|
| |
|
|
| |
|
|
| - `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 |
|
|
| |
|
|
| - `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" }` |
|
|
| |
|
|
| `X-WorldMonitor-Key` is allowed in both `server/cors.ts` and `api/_cors.js`. |
|
|
| |
|
|
| 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. |
|
|
| |
|
|
| 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 |
|
|
| |
|
|
| | 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 | |
|
|