File size: 6,324 Bytes
d9ffd67
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 密钥门控与注册 — 部署指南"
description: "桌面端云回退由 WORLDMONITOR_API_KEY 门控的部署与配置指南:无密钥时应用自动切换至本地 sidecar 模式仅使用离线数据,注册表单则通过 Convex 数据库收集邮箱以便密钥分发、灰度发布、使用度量、后续 Pro 订阅转化跟踪与用户沟通。"
---
## 概述

桌面端云回退受 `WORLDMONITOR_API_KEY` 门控。没有有效密钥时,桌面应用以仅本地模式(sidecar)运行。注册表单通过 Convex DB 收集邮箱,用于未来的密钥分发。

## 架构

```
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       
 └──────────────┘                 └──────────────┘      
└──────────────────┘               └──────────────────────┘
```

## 必需的环境变量

### Vercel

| 变量 | 描述 | 示例 |
|----------|-------------|---------|
| `WORLDMONITOR_VALID_KEYS` | 逗号分隔的有效 API 密钥列表 | `wm_abc123def456,wm_xyz789` |
| `CONVEX_URL` | Convex 部署 URL(来自 `npx convex deploy`) | `https://xyz-123.convex.cloud` |

### 生成 API 密钥

密钥必须至少 16 个字符(客户端验证)。推荐格式:

```bash
# 生成密钥
openssl rand -hex 24 | sed 's/^/wm_/'
# 示例输出: wm_a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6
```

 Vercel 仪表盘中添加到 `WORLDMONITOR_VALID_KEYS`(逗号分隔,无空格)。

## Convex 设置

### 首次部署

```bash
# 1. 安装(已在 package.json 中)
npm install

# 2. 登录 Convex
npx convex login

# 3. 初始化项目(创建带 CONVEX_URL 的 .env.local)
npx convex init

# 4. 部署 schema 和函数
npx convex deploy

# 5. 将部署 URL 复制到 Vercel 环境变量
# URL 由 `npx convex deploy` 打印并保存在 .env.local 中
```

### 验证 Convex 部署

```bash
# 类型检查 Convex 函数
npx convex dev --typecheck

# 打开 Convex 仪表盘查看注册情况
npx convex dashboard
```

### Schema

`registrations` 表存储:

| 字段 | 类型 | 描述 |
|-------|------|-------------|
| `email` | string | 原始邮箱(用于显示) |
| `normalizedEmail` | string | 小写邮箱(用于去重) |
| `registeredAt` | number | Unix 时间戳 |
| `source` | string? | 注册来源 |
| `appVersion` | string? | 桌面应用版本 |

通过 `normalizedEmail` 建立索引以检测重复。

## 安全模型

### 客户端(桌面应用)

- `installRuntimeFetchPatch()` 在允许云回退之前检查 `WORLDMONITOR_API_KEY`
- 密钥必须存在且有效(最少 16 个字符)
- `secretsReady` promise 确保在首次 fetch 之前加载密钥(2 秒超时)
- 故障关闭:密钥检查中的任何错误都会阻止云回退

### 服务端(Vercel edge)

- `api/_api-key.js`  sebuf 路由上验证 `X-WorldMonitor-Key` 
- **源感知**:桌面端来源(`tauri.localhost`、`tauri://`、`asset://`)需要密钥
- Web 来源(`worldmonitor.app`)无需密钥即可通过
- 非桌面端来源带密钥头:密钥仍会验证
- 无效密钥返回 `401 { error: "Invalid API key" }`

### CORS

`X-WorldMonitor-Key`  `server/cors.ts`  `api/_cors.js` 中均被允许。

### 本地 Vercel 环境导出

请勿将 Vercel 环境导出保留在仓库根目录。`.env.vercel-backup`
 `.env.vercel-export`  Git 忽略,但它们仍然是明文
生产密钥导出,本地工具、编辑器代理、备份软件或
依赖安装脚本都可能读取它们。

当任一文件存在时 pre-push 钩子会失败。仅在需要时拉取环境值,
从短期本地环境文件中操作,并在使用后删除该文件。密钥轮换
和从开发机器中删除属于运维任务;通过所属供应商仪表盘轮换
暴露的密钥,优先处理 LLM、支付、认证、Redis  Convex
凭据。

## 验证清单

部署后:

- [ ]  Vercel 中设置 `WORLDMONITOR_VALID_KEYS`
- [ ]  Vercel 中设置 `CONVEX_URL`
- [ ] 运行 `npx convex deploy` 推送 schema
- [ ] 无密钥的桌面端:云回退被阻止(控制台显示 `cloud fallback blocked`)
- [ ] 带无效密钥的桌面端:sebuf 请求返回 `401`
- [ ] 带有效密钥的桌面端:云回退正常工作
- [ ] Web 访问:无需密钥,正常工作
- [ ] 注册表单:提交邮箱,检查 Convex 仪表盘
- [ ] 重复邮箱:显示 "already registered"
- [ ] 现有设置标签页(LLM、API Keys、Debug)保持不变

## 文件参考

| 文件 | 角色 |
|------|------|
| `src/services/runtime.ts` | 客户端密钥门控 + 头附加 |
| `src/services/runtime-config.ts` | `WORLDMONITOR_API_KEY` 类型、验证、`secretsReady` |
| `api/_api-key.js` | 服务端密钥验证(源感知) |
| `api/[domain]/v1/[rpc].ts` | Sebuf 网关  调用 `validateApiKey` |
| `api/register-interest.js` | 注册端点  Convex |
| `server/cors.ts` / `api/_cors.js` |  `X-WorldMonitor-Key`  CORS  |
| `src/components/WorldMonitorTab.ts` | 密钥 + 注册的设置 UI |
| `convex/schema.ts` | Convex DB schema |
| `convex/registerInterest.ts` | Convex mutation |