feat: WmailHub v1.0.0 —— Cloudflare Email Workers 邮件接收与转发平台(本地可用版本)
- Console:React 19 + antd 6.6.5 前端(登录 admin/admin)+ Hono 管理 API (Nodes/Domains/Mailboxes/Emails/Webhooks/Keys/OAuth/Admin) - Node:Bun 转发节点(HMAC 签名、有限重试、心跳、结构化日志、优雅退出) - Email Worker:Cloudflare email handler + 本地联调入口 - shared:HMAC 协议(docs/04)、wmh_ Key(32 位)、AES-GCM 加密、SSRF 防护 - 邮件管线:postal-mime 解析、附件、catch-all、限额、malformed 降级、TTL 1 天 - Webhook:签名投递、指数退避重试、SSRF 校验、投递日志 - API v1 + MCP(JSON-RPC,六工具,凭据 kind 隔离) - Cloudflare OAuth:真实 + 本地 sim 模式,自动安装管线(幂等,无需手工命令) - 测试:bun test 41 例(协议安全/管线/TTL/Webhook/SSRF/MCP/OAuth/鉴权)+ Playwright UI 冒烟 - LOGO/favicon 由 codex CLI 生成
This commit is contained in:
@@ -0,0 +1,100 @@
|
||||
# WmailHub 开发设计总览
|
||||
|
||||
## 1. 项目定位
|
||||
|
||||
WmailHub 是一个基于 Cloudflare Email Workers 的轻量邮件接收与转发平台。
|
||||
|
||||
核心原则:
|
||||
|
||||
- Cloudflare 负责邮件基础设施。
|
||||
- Console 是控制平面,包含前端与管理 Node。
|
||||
- Node 是邮件数据转发平面,无前端、无业务判断。
|
||||
- Console 通过 Cloudflare OAuth 自动完成 Node、Domain、Mailbox、Email Worker 的配置。
|
||||
- Node 只能处理被授权 Cloudflare Account 下的域名邮箱。
|
||||
- Node 与 Console 的通信必须使用 HTTPS + 应用层签名。
|
||||
- 邮件默认短期保存,例如 1 天,过期自动删除。
|
||||
- 邮件进入 Console 后才能进入 Webhook、API、MCP 等业务能力。
|
||||
|
||||
## 2. 总体架构
|
||||
|
||||
```text
|
||||
Internet
|
||||
|
|
||||
v
|
||||
Cloudflare Email Routing
|
||||
|
|
||||
v
|
||||
Cloudflare Email Worker
|
||||
|
|
||||
v
|
||||
Node
|
||||
| HTTPS + Request Signature
|
||||
v
|
||||
Console Management Node
|
||||
|
|
||||
+--> Database
|
||||
+--> Webhook
|
||||
+--> API
|
||||
+--> MCP
|
||||
+--> Frontend
|
||||
```
|
||||
|
||||
## 3. Console
|
||||
|
||||
Console = Frontend + Management Node。
|
||||
|
||||
负责:Cloudflare OAuth、Cloudflare Account、Node、Domain、Mailbox、Email、Webhook、API、MCP、Node 安装与配置、数据存储、TTL 清理调度。
|
||||
|
||||
用户不执行 Node 安装命令。点击 Add Node 后进入 Cloudflare OAuth,授权后由 Cloudflare API 自动完成安装和配置。
|
||||
|
||||
## 4. Node
|
||||
|
||||
Node 是纯后端组件,只做:接收 Cloudflare Email Worker 的邮件、将邮件安全转发到 Console。
|
||||
|
||||
Node 不做 Domain 判断、Mailbox 判断、邮件业务路由、Webhook、MCP、API、用户权限、邮件长期存储。
|
||||
|
||||
Node 收到什么就转发什么。
|
||||
|
||||
## 5. 权限边界
|
||||
|
||||
一个 Node 与一个 Cloudflare Account 建立绑定。Node A 只能处理 Account A 下配置的 Email Worker,不允许处理其他 Account。
|
||||
|
||||
Domain 与 Mailbox 的具体业务信息由 Console 管理,Node 不需要理解这些业务字段。
|
||||
|
||||
## 6. 核心邮件流程
|
||||
|
||||
```text
|
||||
Email
|
||||
|
|
||||
v
|
||||
Cloudflare Email Routing
|
||||
|
|
||||
v
|
||||
Email Worker
|
||||
|
|
||||
v
|
||||
Node
|
||||
|
|
||||
v
|
||||
Console
|
||||
|
|
||||
+--> Store
|
||||
+--> Webhook
|
||||
+--> API
|
||||
+--> MCP
|
||||
```
|
||||
|
||||
## 7. 生命周期
|
||||
|
||||
默认邮件 TTL 为 1 天。数据库记录 expires_at,Cron 定期删除过期记录及对应 R2 对象。
|
||||
|
||||
## 8. 第一版明确不做
|
||||
|
||||
- 自建 SMTP Server
|
||||
- IMAP / POP3
|
||||
- 发件服务器
|
||||
- 完整邮箱客户端
|
||||
- 复杂邮件规则引擎
|
||||
- AI 邮件分析
|
||||
- 多租户复杂组织系统
|
||||
- Node 端业务路由
|
||||
@@ -0,0 +1,47 @@
|
||||
# WmailHub 系统架构
|
||||
|
||||
## Console
|
||||
|
||||
Console = Frontend + Management Node,是系统控制中心。
|
||||
|
||||
负责 Cloudflare OAuth、Node、Domain、Mailbox、Email、Webhook、API、MCP 以及数据存储。
|
||||
|
||||
## Node
|
||||
|
||||
Node 是纯后端、无前端的邮件转发节点。
|
||||
|
||||
唯一核心流程:Cloudflare Email Worker -> Node -> Console。
|
||||
|
||||
Node 不负责 Domain、Mailbox、Webhook、MCP、API、用户权限或业务路由。
|
||||
|
||||
## Cloudflare Worker
|
||||
|
||||
Email Worker 是邮件入口,只负责接收 Cloudflare Email Event 并安全转发给 Node。
|
||||
|
||||
## 控制平面
|
||||
|
||||
```text
|
||||
Cloudflare Account
|
||||
+-- Node
|
||||
+-- Domain
|
||||
+-- Mailbox
|
||||
+-- Email Worker
|
||||
```
|
||||
|
||||
这些资源关系由 Console 管理。
|
||||
|
||||
## 数据平面
|
||||
|
||||
```text
|
||||
Email Worker -> Node -> Console
|
||||
```
|
||||
|
||||
Node 不参与业务决策。
|
||||
|
||||
## Node Scope
|
||||
|
||||
第一版一个 Node 绑定一个 Cloudflare Account。Node A 不允许处理 Account B 的资源。
|
||||
|
||||
## 扩展
|
||||
|
||||
未来可以有多个 Node,例如 Node JP、Node US、Node EU。每个 Node 默认仍绑定一个 Cloudflare Account。
|
||||
@@ -0,0 +1,11 @@
|
||||
# Cloudflare OAuth 与自动安装
|
||||
|
||||
Console 的 Add Node 入口必须打开 Cloudflare OAuth 授权页面。用户授权后,Console 通过 Cloudflare API 自动完成 Node、Email Worker、Domain、Mailbox 的配置。
|
||||
|
||||
用户不执行 shell 安装命令,也不手工创建 API Token。
|
||||
|
||||
流程:Console -> Add Node -> Cloudflare OAuth -> 授权回调 -> 获取 Account/Zone -> 创建 Node Identity -> 创建 Node Credential -> 创建或更新 Email Worker -> 配置 Email Routing -> 创建 Domain/Mailbox -> Node Ready。
|
||||
|
||||
Cloudflare credential 与 Node credential 必须分离。Cloudflare credential 加密存储,不进入前端和日志。
|
||||
|
||||
安装过程必须幂等,重复执行不能创建重复资源。OAuth state、授权回调和 Account ownership 必须验证。
|
||||
@@ -0,0 +1,44 @@
|
||||
# Node 设计
|
||||
|
||||
Node 是无前端、纯后端的邮件转发节点。
|
||||
|
||||
核心流程:
|
||||
|
||||
```text
|
||||
Email Worker -> Node -> Console
|
||||
```
|
||||
|
||||
## Node 职责
|
||||
|
||||
- 接收 Email Worker 请求
|
||||
- 验证 Worker 请求
|
||||
- 获取邮件
|
||||
- 安全转发到 Console
|
||||
- 有限重试临时网络错误
|
||||
- 心跳
|
||||
- 上报版本和状态
|
||||
|
||||
## Node 不负责
|
||||
|
||||
- Domain 路由
|
||||
- Mailbox 路由
|
||||
- Webhook
|
||||
- MCP
|
||||
- API
|
||||
- 用户权限
|
||||
- 邮件业务存储
|
||||
- 邮件规则
|
||||
|
||||
Node 收到什么就转发什么。
|
||||
|
||||
## Node Scope
|
||||
|
||||
Node 只能服务创建它的 Cloudflare Account。不得通过普通业务请求把 Node 任意绑定到其他 Account。
|
||||
|
||||
## 配置
|
||||
|
||||
Node 至少需要 node_id、cloudflare_account_id、console_endpoint、credential。
|
||||
|
||||
credential secret 必须安全保存。
|
||||
|
||||
Node 可以有限重试 Console 网络错误,但不承担长期邮件队列职责。
|
||||
@@ -0,0 +1,49 @@
|
||||
# Node 与 Console 通信协议
|
||||
|
||||
Node -> Console 必须使用 HTTPS + HMAC 请求签名 + timestamp + nonce + body hash。
|
||||
|
||||
## Credential
|
||||
|
||||
```text
|
||||
node_id
|
||||
credential_id
|
||||
credential_secret
|
||||
cloudflare_account_id
|
||||
```
|
||||
|
||||
Console 保存 secret 的安全摘要;Node 保存实际 secret。
|
||||
|
||||
## 签名
|
||||
|
||||
```text
|
||||
body_hash = SHA256(raw_body)
|
||||
canonical = METHOD + "\n" + PATH + "\n" + TIMESTAMP + "\n" + NONCE + "\n" + body_hash
|
||||
signature = HMAC-SHA256(secret, canonical)
|
||||
```
|
||||
|
||||
请求头:
|
||||
|
||||
```text
|
||||
Authorization: WmailHub-Node <node_id>
|
||||
X-WmailHub-Timestamp: <unix_seconds>
|
||||
X-WmailHub-Nonce: <random>
|
||||
X-WmailHub-Body-SHA256: <hex>
|
||||
X-WmailHub-Signature: <hex>
|
||||
X-WmailHub-Protocol: 1
|
||||
```
|
||||
|
||||
Console 拒绝 timestamp 超窗、重复 nonce、错误 signature、错误 body hash、已撤销 credential。默认 timestamp window 建议 5 分钟。
|
||||
|
||||
## API
|
||||
|
||||
```text
|
||||
POST /api/internal/nodes/{node_id}/emails
|
||||
POST /api/internal/nodes/{node_id}/heartbeat
|
||||
GET /api/internal/nodes/{node_id}/config
|
||||
```
|
||||
|
||||
邮件请求推荐 `Content-Type: message/rfc822`,body 直接使用 raw MIME。
|
||||
|
||||
Console 可以立即撤销 credential。Node 收到 401/403 后不得无限重试。
|
||||
|
||||
协议必须版本化。
|
||||
@@ -0,0 +1,57 @@
|
||||
# 数据模型
|
||||
|
||||
核心实体:CloudflareAccount、Node、Domain、Mailbox、Email、EmailAttachment、Webhook、WebhookDelivery、OAuthConnection。
|
||||
|
||||
## nodes
|
||||
|
||||
```text
|
||||
id, name, cloudflare_account_id, credential_id, status, version, last_seen_at, created_at, updated_at
|
||||
```
|
||||
|
||||
## domains
|
||||
|
||||
```text
|
||||
id, cloudflare_account_id, node_id, name, status, worker_id, created_at, updated_at
|
||||
```
|
||||
|
||||
## mailboxes
|
||||
|
||||
```text
|
||||
id, domain_id, address, status, retention_seconds, created_at, updated_at
|
||||
```
|
||||
|
||||
## emails
|
||||
|
||||
```text
|
||||
id, node_id, mailbox_id nullable, message_id, from_address, to_addresses,
|
||||
cc_addresses, subject, text_body, html_body, raw_storage_key, size,
|
||||
received_at, expires_at, created_at
|
||||
```
|
||||
|
||||
Node 可以不知道 mailbox_id。Console 收到邮件后,根据邮件内容和自身配置关联。
|
||||
|
||||
## email_attachments
|
||||
|
||||
```text
|
||||
id, email_id, filename, content_type, size, storage_key, content_id
|
||||
```
|
||||
|
||||
## webhooks
|
||||
|
||||
```text
|
||||
id, name, endpoint, secret, enabled, created_at, updated_at
|
||||
```
|
||||
|
||||
## webhook_deliveries
|
||||
|
||||
```text
|
||||
id, webhook_id, email_id, status, attempt_count, next_retry_at, last_error, delivered_at, created_at
|
||||
```
|
||||
|
||||
## oauth_connections
|
||||
|
||||
```text
|
||||
id, provider, account_id, encrypted_access_token, refresh_token, expires_at, created_at, updated_at
|
||||
```
|
||||
|
||||
OAuth token 必须加密存储。所有 Email 必须有 expires_at,默认 retention 为 86400 秒。
|
||||
@@ -0,0 +1,42 @@
|
||||
# Email 生命周期与 TTL
|
||||
|
||||
## 接收
|
||||
|
||||
```text
|
||||
Cloudflare -> Email Worker -> Node -> Console
|
||||
```
|
||||
|
||||
Console 验证 Node 身份和请求完整性,然后解析 MIME、提取 header、text/html 和附件,保存元数据,raw MIME/大附件写入 R2,计算 expires_at,并创建 Webhook Delivery。
|
||||
|
||||
## TTL
|
||||
|
||||
默认保留 1 天:
|
||||
|
||||
```text
|
||||
received_at = 2026-09-30T10:00:00Z
|
||||
expires_at = 2026-10-01T10:00:00Z
|
||||
```
|
||||
|
||||
Retention 可按 Mailbox 配置。
|
||||
|
||||
## Cron
|
||||
|
||||
使用 Cloudflare Cron Trigger,建议每 15 分钟执行一次清理。实际频率根据当前 Cloudflare 限制调整。
|
||||
|
||||
```text
|
||||
Cron -> 查询 expires_at <= now -> 删除 R2 -> 删除附件 -> 删除 Email
|
||||
```
|
||||
|
||||
删除必须幂等。
|
||||
|
||||
## Webhook
|
||||
|
||||
邮件保存成功后再执行 Webhook。Webhook 失败不能导致邮件重新进入 Node。
|
||||
|
||||
## 异常
|
||||
|
||||
R2 删除失败时保留数据库记录并记录 cleanup error,下一次 Cron 重试。数据库已删除但 R2 删除失败时,需要 orphan cleanup。
|
||||
|
||||
## 数据最小化
|
||||
|
||||
邮件、附件、raw MIME、Webhook Delivery 和临时日志都应有生命周期,不做无限期保存。
|
||||
@@ -0,0 +1,45 @@
|
||||
# Webhook、API 与 MCP
|
||||
|
||||
Webhook、API、MCP 全部属于 Console,Node 不实现这些能力。
|
||||
|
||||
## Webhook
|
||||
|
||||
邮件保存成功后创建 delivery。Payload:
|
||||
|
||||
```json
|
||||
{
|
||||
"event": "email.received",
|
||||
"id": "msg_xxx",
|
||||
"mailbox": "hello@example.com",
|
||||
"from": "user@example.org",
|
||||
"to": ["hello@example.com"],
|
||||
"subject": "Hello",
|
||||
"text": "Hello World",
|
||||
"received_at": "2026-09-30T10:00:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
Webhook 必须签名,建议提供 X-WmailHub-Event、X-WmailHub-Delivery、X-WmailHub-Timestamp、X-WmailHub-Signature。
|
||||
|
||||
2xx 为成功;5xx 和 timeout 重试;4xx 按策略处理。使用指数退避和最大尝试次数。
|
||||
|
||||
## API
|
||||
|
||||
```text
|
||||
/api/v1/nodes
|
||||
/api/v1/domains
|
||||
/api/v1/mailboxes
|
||||
/api/v1/emails
|
||||
/api/v1/webhooks
|
||||
/api/v1/mcp
|
||||
```
|
||||
|
||||
API 属于 Console。
|
||||
|
||||
## MCP
|
||||
|
||||
MCP 允许 Agent 查询邮箱、获取邮件、查询邮件状态、管理邮箱和 Webhook。Node 不暴露 MCP。
|
||||
|
||||
MCP/API 获取邮件时必须检查用户权限、邮件是否过期、mailbox scope 和 API credential scope。
|
||||
|
||||
Webhook endpoint 必须防 SSRF,至少阻止 localhost、private IPv4/IPv6、link-local、metadata endpoint 和 DNS rebinding。
|
||||
@@ -0,0 +1,47 @@
|
||||
# 安全设计
|
||||
|
||||
## 安全边界
|
||||
|
||||
```text
|
||||
Cloudflare Email Worker
|
||||
|
|
||||
| authenticated request
|
||||
v
|
||||
Node
|
||||
|
|
||||
| TLS + HMAC
|
||||
v
|
||||
Console
|
||||
```
|
||||
|
||||
## Cloudflare OAuth
|
||||
|
||||
OAuth Token 加密存储,不进入前端和日志,也不作为 Node credential。
|
||||
|
||||
## Node Credential
|
||||
|
||||
由 Console 自动生成。用户不手工配置。支持撤销和重新 enrollment。
|
||||
|
||||
## 请求签名
|
||||
|
||||
所有 Node -> Console 请求必须签名,签名覆盖 method、path、timestamp、nonce、body hash,防止篡改和 replay。
|
||||
|
||||
## Worker -> Node
|
||||
|
||||
Worker 使用 Node 专用 ingress secret。Worker 不应拥有 Console credential。
|
||||
|
||||
## 最小权限
|
||||
|
||||
Cloudflare OAuth 只申请业务所需权限。
|
||||
|
||||
## 日志
|
||||
|
||||
禁止记录 Cloudflare OAuth token、Node secret、Webhook secret、Authorization header 和完整邮件正文。
|
||||
|
||||
## 附件限制
|
||||
|
||||
限制单文件大小、单邮件总大小、文件数量和 MIME header 大小,避免资源耗尽。
|
||||
|
||||
## 数据保留
|
||||
|
||||
默认 Email TTL 为 1 天,过期自动删除。
|
||||
@@ -0,0 +1,42 @@
|
||||
# Cloudflare 部署设计
|
||||
|
||||
## 服务
|
||||
|
||||
第一版目标使用 Cloudflare Workers、Email Workers / Email Routing、D1、R2、Cron Triggers、OAuth;Queues 在需要可靠异步投递时再加入,并以当前 Cloudflare 限制为准。
|
||||
|
||||
## Console
|
||||
|
||||
Console 本身部署在 Cloudflare Workers:
|
||||
|
||||
```text
|
||||
Worker
|
||||
+-- Frontend Assets
|
||||
+-- Management API
|
||||
+-- OAuth Callback
|
||||
+-- Cron Handler
|
||||
```
|
||||
|
||||
## Node
|
||||
|
||||
Node 是独立纯后端程序,无前端。可以部署在用户服务器、云服务器或其他可访问 Console 的环境,但 Cloudflare Email Worker 必须能访问 Node ingress。
|
||||
|
||||
## Email Worker
|
||||
|
||||
Email Worker 只接收 Email Event 并安全转发到 Node,不保存业务数据库。
|
||||
|
||||
## R2
|
||||
|
||||
用于 raw MIME 和 attachments:
|
||||
|
||||
```text
|
||||
emails/{yyyy}/{mm}/{dd}/{email_id}/raw.eml
|
||||
emails/{yyyy}/{mm}/{dd}/{email_id}/attachments/{attachment_id}
|
||||
```
|
||||
|
||||
## D1
|
||||
|
||||
保存 nodes、domains、mailboxes、emails、webhooks、webhook deliveries、oauth connections。
|
||||
|
||||
## Cron
|
||||
|
||||
至少实现 cleanup job,建议每 15 分钟一次,具体以当前 Cloudflare 限制调整。
|
||||
@@ -0,0 +1,61 @@
|
||||
# ZCODE 实施计划
|
||||
|
||||
## Phase 1 项目骨架
|
||||
|
||||
```text
|
||||
WmailHub/
|
||||
console/
|
||||
node/
|
||||
worker/
|
||||
docs/
|
||||
```
|
||||
|
||||
Console、Node、Worker 必须边界清晰,共享协议独立。
|
||||
|
||||
## Phase 2 Console
|
||||
|
||||
实现 Console Web、Management API、数据库、认证、Node CRUD、Domain CRUD、Mailbox CRUD、Email CRUD。
|
||||
|
||||
## Phase 3 Cloudflare OAuth
|
||||
|
||||
实现 OAuth start、callback、state、token exchange、Account/Zone discovery、加密 token storage。
|
||||
|
||||
## Phase 4 自动 Node 安装
|
||||
|
||||
Add Node 后完成:OAuth、Node identity、Node credential、Email Worker、Worker -> Node、Email Routing、Domain、Mailbox、Node heartbeat。正常流程禁止要求用户复制安装命令。
|
||||
|
||||
## Phase 5 Node
|
||||
|
||||
实现 bootstrap、secure config、Email Worker ingress、Console egress、HMAC signing、heartbeat、有限 retry、graceful shutdown、structured logging。
|
||||
|
||||
Node 不实现业务路由。
|
||||
|
||||
## Phase 6 Email Pipeline
|
||||
|
||||
```text
|
||||
Cloudflare -> Worker -> Node -> Console -> D1/R2
|
||||
```
|
||||
|
||||
测试 plain text、HTML、MIME、attachment、多收件人、大正文和 malformed mail。
|
||||
|
||||
## Phase 7 Webhook
|
||||
|
||||
实现 CRUD、secret、signing、delivery、retry、timeout、失败状态和日志。
|
||||
|
||||
## Phase 8 TTL
|
||||
|
||||
实现 retention configuration、expires_at、Cron cleanup、R2 cleanup 和 orphan cleanup。默认 1 day。
|
||||
|
||||
## Phase 9 API / MCP
|
||||
|
||||
实现 Console API,并提供 list_mailboxes、list_emails、get_email、create_mailbox、delete_mailbox、list_webhooks 等 MCP 工具。权限必须基于 Console credential。
|
||||
|
||||
## Phase 10 安全测试
|
||||
|
||||
测试 OAuth CSRF、credential revoke、replay attack、伪造 Node 请求、body 篡改、过期 timestamp、Webhook SSRF、未授权 mailbox、未授权 Cloudflare Account、超大邮件和超大附件。
|
||||
|
||||
## Phase 11 验收
|
||||
|
||||
必须完成:Cloudflare OAuth -> 自动 Node 安装 -> Domain 配置 -> Mailbox 创建 -> 真实 Email -> Email Worker -> Node -> Console -> Database/Webhook/API/MCP -> TTL 自动删除。
|
||||
|
||||
如果正常流程要求用户手工登录服务器执行安装命令,则不符合设计目标。
|
||||
Reference in New Issue
Block a user