Technical Documentation: rememberber/WePush
ℹ️ Provenance: Multi-Tier Ingestion · CodeWiki Reference
Overview (`readme.md`)
WePush
专注批量推送的小而美的工具
产品线与版本选择
WePush 采用 Classic 与 Next 双轨发展。两条产品线彼此独立,允许按各自需要演进和拥有重复代码;Next 不会影响 Classic 的安装与使用。
| 产品线 | 当前定位 | 适合场景 | 入口 |
|---|---|---|---|
| WePush Classic | 稳定桌面客户端 | 微信、短信、邮件、HTTP 等成熟批量推送场景 | Classic 下载 |
| WePush Next | 1.2.0 Stable |
三平台离线自部署、1.x 兼容承诺、Service/Agent、资源治理、AI 助手接入、可恢复运维、WebUI/Desktop 与可扩展 Provider | Next 1.2.0 下载 |
当前源码的 Classic 与 Next 均要求 JDK 25+ 构建;不含运行时的发行包及 Java SDK 要求 Java 25+。完整安装包内置 Java 25 运行时。
AI 助手接入:Codex、MCP 与 Skill
Classic 和 Next 均支持让 Codex 等 AI 助手使用 WePush。可一键安装 Codex MCP + Skill、仅安装 Skill,或生成通用 stdio MCP 配置给其他兼容客户端。两版独立实现,安装名称不同,可以同时使用。
此功能随 Classic 当前发行版和 Next 1.2.0 提供。更早的 Next 1.1.0 及不含该入口的旧 Classic 安装包需要升级后再使用。
| 版本 | 接入入口 | MCP / Skill 名称 | 主要能力 |
|---|---|---|---|
| Classic | 应用 → AI 助手接入 | wepush-classic |
查询已有任务、查看发送预览、空跑、授权发送、查询进度与历史 |
| Next | 设置 → AI 助手接入 | wepush |
发现渠道与 Schema、查询资源、创建消息/受众/任务、空跑、授权发送、查询结果与控制运行 |
快速开始:
- 在 WePush 中配置渠道账号。Classic 还需保存消息、人群和任务;Next 需启动 Service 并核对 Service URL 与 Workspace。
- 在 Classic 或 Next Desktop 的接入页面点击 一键接入 Codex,再重新连接 Codex MCP,必要时重启 Codex。两版桌面安装均使用各自自带的运行时,无需额外安装 Node.js 或 Java。
- 让助手先查询并空跑任务,例如:“用 WePush Classic 列出任务,空跑任务 1,并告诉我结果。”正式发送前核对预览中的消息与人群,按用户授权执行,再查询实际运行结果。
使用 Next WebUI 时,在接入页面下载安装器并执行页面生成的命令,需要 Node.js 24+。仅使用 Skill 可点击 仅安装 Skill;其他支持 stdio MCP 的客户端可使用 Classic 的 复制通用 MCP 配置 或 Next Desktop 的 导出通用 MCP 配置。
- **Classic 执行范围:**已保存的手动固定线程任务,线程数 1~100,关闭任务的结果邮件提醒;使用期间保持 Classic 运行。账号、消息、人群及任务的编辑仍在 Classic 界面完成。
- **Next 运行要求:**保持 Service 可达;启用认证后,在 AI 客户端环境中设置
WEPUSH_API_TOKEN。安装器不复制登录 Token,调用遵循已有 Workspace 权限。 - **发送防重:**正式发送使用预览确认令牌和请求编号;超时后查询原运行,重试时复用原请求编号与参数,结果不明时不自动重发。
安装目录、客户端配置、完整工具列表及排错说明见 Classic 接入指南 和 Next 接入指南。
WePush Next Stable
Next 是位于 next/ 的完整新架构产品线,包含 Core Engine、Provider SPI、可安装 Service、远程 Agent、Remote/Embedded Java SDK、React WebUI 和 Electron Desktop。1.2.0 是兼容 1.1.0 的稳定 Minor 发行版,在既有 1.x 兼容承诺之上提供 Codex MCP/Skill 接入,并把 Java 运行基线升级到 25。
Next 的长期定位是由用户自行下载、安装、部署和运维的开源产品。项目不建设承载用户业务数据的官方公共 SaaS,不提供注册、计费、订阅或公共租户平台,也不规划云 KMS/Secret Manager 集成。用户可以在自己的环境中运行 Standalone,或自行搭建 Server/HA 和远程 Agent。
`1.2.0` 重点更新
- Desktop 一键安装 Codex MCP 与 Skill;WebUI 和发行包提供
wepush-ai.mjs。正式发送仍使用预览确认、幂等键和 Workspace 权限。 - Java 运行基线升级到 25。完整包携带 Java 25 运行时;精简包和 Java SDK 需要系统 Java 25+。
- 不新增数据库迁移。运营商插件、Workspace 资源治理、脱敏诊断、跨 Run 熔断、PostgreSQL 通知、Multipart Artifact 和 WebUI 主题继续可用。
Next 组件
| 组件 | 主要职责 | 形态与边界 |
|---|---|---|
| Core / Engine | 执行批量任务,管理并发、重试、暂停、恢复、取消、结果与运行事件 | 纯 Java 无界面执行内核,不直接提供网络 API,也不依赖 Service、UI 或具体 Provider |
| Provider SPI / Provider | 定义消息渠道扩展契约,负责账号校验、消息渲染和实际发送 | Core 只面向 SPI;内置 HTTP 和 8 个标准渠道,另有 CMPP/SMGP/SGIP/SMPP 独立签名插件 |
| Service | 提供配置、调度、运行控制、Secret、Artifact、审计、REST/SSE、OpenAPI 和 Agent 控制面 | 可前台运行或安装为 Linux systemd、macOS launchd、Windows Service;本机模式可内嵌 Core Engine |
| Agent | 在独立主机接收 Lease,使用 Core Engine 执行任务并回传事件、结果和 Artifact | 可独立安装,通过 gRPC 主动连接 Service;本机内嵌执行时不需要 Agent |
| Remote Java SDK | 让 Java 应用通过强类型客户端调用远程 Service API | 已实现;只依赖公开的 service-api 契约,不依赖 Core、Engine 或具体 Provider |
| Embedded Java SDK | 让 Java 应用在自己的进程内直接装配 Core Engine 和选定 Provider,无需启动 Service | 1.2.0 Java SDK 附件包含 HTTP 与标准渠道 Provider;业务应用仍须显式选择 |
| WebUI | 提供可视化配置、任务与调度、运行中心、Agent 观察和动态调试 API 文档 | TypeScript + Vite + React,可由 Service 直接托管,也可在开发环境独立运行 |
| Desktop UI | 提供与 WebUI 一致的桌面管理体验和安全 Electron 外壳 | 连接并管理已安装的本机 Service,使用系统安全存储保存 Token;不把 Service 内嵌进 UI |
当前典型调用关系为:WebUI / Desktop UI / Remote Java SDK → Service API → Service → 内嵌 Core Engine;远程执行时则由 Service → Agent → Core Engine → Provider 完成发送。进程内集成使用 业务 Java 应用 → Embedded Java SDK → Core Engine → Provider,不经过 Service。
第一次使用请从《WePush Next 对外使用指南》开始。macOS/Windows 发行物按项目约定不使用商业代码签名,请只从 next-v1.2.0 GitHub Release 下载,并使用同一 Release 中的 SHA256SUMS 校验完整性。
- Next 项目说明
- 产品目标、边界与路线图
- Next 内置 Provider 指南
- 架构与概要设计
- 详细设计
- 部署与运维
1.2.0Release Notes1.1.0Release Notes- 1.x 兼容性策略
- 升级与回滚指南
1.0.0Release Notes0.1.0-beta.1Release Notes
WePush Classic
Classic 是现有稳定客户端,继续在仓库原有目录中独立维护和演进。
支持的平台
Windows • Linux • macOS
目前已经支持的消息类型
- 模板消息-公众号
- 模板消息-小程序
- 订阅消息-小程序
- 微信客服消息(文本、图文、图片、语音、视频、音乐、小程序卡片)
- 微信企业号/企业微信消息
- 企业微信小程序通知消息
- 小程序统一服务消息
- 钉钉
- 飞书群自定义机器人(文本、富文本、消息卡片、原始 JSON;支持签名校验)
- 阿里云短信
- 阿里大于模板短信
- 腾讯云短信
- 华为云短信
- 百度云短信
- 又拍云短信
- 七牛云短信
- 云片网短信
- 网易云信短信
- 榛子云短信
- Luosimao短信
- 极光短信
- 极光推送
- HTTP请求(单次、批量、压测)
功能&亮点
- 支持自定义消息内容并批量推送
- 支持变量消息(可实现根据发送目标用户不同每条消息内容不一样)
- 支持消息编辑、预览、消息管理
- 支持通过文件导入用户(txt、csv、excel)
- 支持通过MySQL导入用户
- 支持微信公众号全员推送
- 支持微信全家桶消息(公众号、小程序、企业号)
- 支持各种粒度的定时推送
- 支持推送历史管理和失败重新推送
- 支持多账号管理和切换(微信)
- 支持各种搜索、导入、导出
- 小而美的可视化界面,支持亮暗多种外观风格
- 支持全局字体字号设置
- 支持推送结果邮件通知
- 支持一键接入 Codex MCP / Skill,供 AI 助手查询任务、空跑校验和按授权发送(接入说明)
……
截图速览
安装文件下载
使用到的一些小技术点
- Java
- Java Swing
- 线程池
- 连接池(HTTP:按通道/账号共享 OkHttp 连接池)
- HTTP/2(ALPN 自动协商,并自动回退 HTTP/1.1)
- 定时任务
- SQLite
- MyBatis
Classic HTTP 性能对照
Classic 默认使用 HTTP/2 + HTTP/1.1 自动协商,任务结束时会在日志中输出协议命中、新建/复用连接、429 以及 p50/p95/p99 耗时。可用同一批数据强制 HTTP/1.1 作为对照组:
java -Dwepush.http.protocol=http1 -jar WePush.jar并发上限默认沿用全局最大线程数,也可按消息类型覆盖(<msgType> 为消息类型数值):
java -Dwepush.http.maxConcurrency.<msgType>=20 -jar WePush.jar遇到的麻烦和挑战
- Swing界面不好控制,导致需要投入较多精力和耐心
- 工作过于饱和,经常到半夜很晚才挤出一点时间
- 要做的事情有很多,比如WePush中间件及其附属的集消息中心、通知报警、任务、批量、重试、统计等于一身的方便部署的Web管理应用
- 陪家人时间变少或无
- 锻炼身体时间变少或无
- 越来越发现需要不断学习源码和底层的重要性
特别感谢
开发&构建
https://gitee.com/zhoubochina/WePush/wikis/build
使用帮助
https://gitee.com/zhoubochina/WePush/wikis/help
QQ交流群:
鼓励&赞赏
如果WePush对您有所帮助或便利,
欢迎对我每天下班和周末时光的努力进行肯定,
您的赞赏将会给我带来更多动力
Overview (`next/README.md`)
WePush Next
WePush Next 是与 Classic 完全独立的新产品线。Classic 与 Next 可以按需拥有重复代码,彼此不建立共享源码依赖。
产品定位:WePush Next 是开源、可下载、可安装、由用户自行部署和运维的消息推送产品。项目不建设官方公共 SaaS、注册计费订阅、公共租户平台或云 KMS/Secret Manager 集成。正式范围和分版本计划见《产品目标、边界与路线图》。
当前版本:
1.2.0Stable。下载next-v1.2.0。Desktop 与安装包按项目约定不使用商业代码签名;下载后必须验证同一 Release 中的SHA256SUMS。发行边界和升级承诺见UNSIGNED-NOTICE.md、《兼容性策略》、《升级与回滚指南》与docs/releases/1.2.0.md。
第一次下载和使用请从《WePush Next 对外使用指南》开始。渠道账号、模板、Recipient、SecretRef、限流和错误语义见《内置 Provider 指南》。
1.2.0 提供 Codex MCP / Skill 接入,支持查询和创建资源、空跑、授权发送及运行控制,快速配置见下方「AI 助手接入」。
下载与安装
正式发行物统一位于 next-v1.2.0 GitHub Release:
| 使用方式 | 选择的发行物 |
|---|---|
| 推荐的 Standalone 安装 | 与操作系统、架构匹配的 wepush-next-1.2.0-<os>-<arch> 完整包,已包含 Java Runtime |
| 已有 Java 25+ 或分组件部署 | wepush-next-1.2.0.tar.gz 或 wepush-next-1.2.0.zip 精简包 |
| 只使用管理界面 | 与操作系统、架构匹配的 wepush-next-desktop-1.2.0-<os>-<arch> |
| Java 集成 | wepush-next-java-sdk-1.2.0.zip |
| 运营商短信 Agent 插件 | wepush-provider-{cmpp,smgp,sgip,smpp}-1.2.0.zip 及 wepush-provider-trusted-key-1.2.0.env |
下载后先验证 SHA256SUMS。安装运营商插件时,还必须校验信任公钥文件及各插件的旁路 .sha256;不要从插件 ZIP 或第三方页面获取替代公钥。完整资产说明和升级步骤见 1.2.0 Release Notes。
完整包解压后执行统一 Standalone 安装入口:
# Linux / macOS
sudo ./install/install.sh# Windows 管理员 PowerShell
Set-ExecutionPolicy -Scope Process Bypass
& .\install\install.ps1安装完成前会检查 Service Readiness、Flyway 数据库版本和本地 Provider Dry Run。需要便携运行或已有 Java 25+ 时,可选择精简包;分组件部署仍可直接使用 install/<os>/install.* service|agent|all。
AI 助手接入(MCP / Skill)
启动 Next Service 并配置渠道账号后,打开 设置 → AI 助手接入,核对 Service URL 与当前 Workspace:
- **Desktop:**点击 一键接入 Codex 安装 MCP + Skill,或选择 仅安装 Skill;使用 Desktop 自带运行时,无需另装 Node.js。其他支持 stdio MCP 的客户端可使用 导出通用 MCP 配置。
- **WebUI / 新构建发行包:**下载页面提供的独立安装器,或使用发行包中的
ai/wepush-ai.mjs,通过 Node.js 24+ 执行安装命令:
node ./wepush-ai.mjs install --target codex --url http://127.0.0.1:18990 --workspace ws_default安装后重新连接 Codex MCP,必要时重启 Codex。MCP 名称为 wepush,Skill 位于 ~/.agents/skills/wepush,可与 Classic 的 wepush-classic 同时安装。启用 Service 认证时,在 AI 客户端环境中设置 WEPUSH_API_TOKEN;安装器不复制登录 Token。
助手可以发现 Provider Schema、查询现有资源、创建消息/受众/任务、执行 Dry Run、准备与提交正式发送,以及查询结果、暂停、恢复或取消运行。正式发送需核对预览并取得覆盖内容与受众的用户授权;网络超时后保留原请求编号,先查询运行结果。
完整工具列表、权限要求、其他客户端配置及排错见《AI 助手接入指南》。Classic 的入口与执行范围见《Classic 接入指南》。
验证 Java 工程
cd next
./mvnw verify完整构建、三平台安装、Server/HA、Agent Enrollment、插件升级和恢复步骤见 docs/deployment-and-operations.md。安全问题请按 SECURITY.md 私下报告。
启动 Service
cd next
./mvnw -pl service/service-app -am package -DskipTests
java -jar service/service-app/target/wepush-next-service.jarService 默认只监听 127.0.0.1:18990。启动后可访问:
http://127.0.0.1:18990/actuator/healthhttp://127.0.0.1:18990/api/v1/system/infohttp://127.0.0.1:18990/api/v1/providershttp://127.0.0.1:18990/api/v1/agentshttp://127.0.0.1:18990/openapi.yaml
本地无认证模式只允许监听回环地址;将 WEPUSH_BIND_ADDRESS 改为非回环地址时必须同时开启 WEPUSH_SECURITY_ENABLED=true 并配置足够强度的 WEPUSH_BOOTSTRAP_TOKEN,否则 Service 拒绝启动。
Standalone 数据默认保存到 .local/data/wepush-next.db,可通过 WEPUSH_DATABASE_PATH 指定其他位置。Service 首次启动会运行 Flyway 迁移并创建 ws_default 工作区。
Secret 默认使用本地信封加密:密文进入 SQLite,主密钥单独保存到 .local/secrets/master-key.json。可通过 WEPUSH_MASTER_KEY_PATH 修改路径,或使用 WEPUSH_MASTER_KEY_BASE64 注入 32-byte Base64 主密钥。已有密文但主密钥缺失、权限不安全或认证失败时,Service 会失败关闭,不会生成新密钥覆盖。
Artifact 默认保存到 .local/artifacts,SQLite 只保存元数据、SHA-256、大小和生命周期状态。可通过 WEPUSH_ARTIFACT_ROOT 修改根目录;临时结果导出默认保留 24 小时,可通过 WEPUSH_EXPORT_RETENTION 使用 ISO-8601 Duration 调整。Service 会按 WEPUSH_RETENTION_INTERVAL 周期回收过期且未 Pin/Legal Hold 的文件。
启动 WebUI 开发环境
cd next/ui
pnpm install
pnpm check
pnpm devWebUI 默认运行在 http://127.0.0.1:5173,开发代理连接本地 Service。界面、API Client、Schema Renderer、设计 Token 与 Electron Desktop 外壳均在同一个 pnpm Workspace 内。
启动 Agent
cd next
./mvnw -pl agent/agent-app -am package -DskipTests
java -jar agent/agent-app/target/wepush-next-agent.jarAgent 默认主动连接 127.0.0.1:19090 的 gRPC 双向控制流,发送 Hello 和周期心跳,断线后使用带抖动的指数退避重连;Sequence 与 Lease Fence Journal 默认保存在 .local/agent/agent-state.properties。
常用环境变量:
WEPUSH_AGENT_ID:稳定 Agent 身份,默认local-agent。WEPUSH_SERVICE_HOST/WEPUSH_AGENT_GRPC_PORT:Service gRPC 地址,默认127.0.0.1:19090。WEPUSH_AGENT_GRPC_TOKEN:仅用于回环开发/Bootstrap 的共享 Token;正式 Agent 使用 Enrollment Credential。WEPUSH_AGENT_GRPC_PLAINTEXT:本地开发默认true;远端部署应关闭并使用 TLS。WEPUSH_AGENT_STATE_PATH:Agent Journal 文件位置。
Service 的 gRPC 端口默认只绑定回环地址。暴露到非回环地址时强制 TLS,HTTP Lease/Artifact 与 gRPC 都拒绝匿名 Agent;正式生产通过一次性 Enrollment 获取长期 Credential 和客户端证书,并使用 mTLS。共享 Token 只保留为回环开发兼容入口。
要把 Run 交给独立 Agent 执行,Service 使用以下配置启动:
WEPUSH_EXECUTION_MODE=remote \
WEPUSH_AGENT_PUBLIC_BASE_URL=http://127.0.0.1:18990 \
java -jar service/service-app/target/wepush-next-service.jar远端模式会按 Provider ID/版本和可用容量选择在线 Agent,持久化带 Epoch/Fencing Token 的 Lease。Agent 校验冻结 Execution Spec 与 Audience 的 SHA-256 后 ACK,随后使用同一 Core Engine 执行,并经 gRPC 回传事件、Item Result、命令确认和 Run Summary。
需要 Secret 的远端 Run 会使用 Agent 在 Hello 中发布的会话级 X25519 公钥。Service 只解析冻结配置中实际引用的最小 Secret 集,使用一次性 X25519、HKDF-SHA-256 和 AES-256-GCM 加密,并绑定 Agent、Run、Lease、Epoch、Fencing Token 与过期时间。Agent 仅在内存中解密,运行结束立即清零,不写入 Journal。正式跨主机部署仍应关闭明文 gRPC 并配置 TLS;Secret Envelope 不替代 Agent 身份认证和传输层安全。
Java SDKs
Next 提供两种独立 SDK。Remote SDK 通过 HTTP 调用 Service,只依赖公开 service-api,不依赖 Core、Engine 或具体 Provider:
稳定发行包在 sdk/ 中附带可安装到本地 Maven Repository 的 POM/JAR,步骤见 sdk/README.md。
try (var client = WePushClient.builder()
.endpoint(URI.create("http://127.0.0.1:18990"))
.build()) {
var system = client.system().info();
var providers = client.providers().list();
var agents = client.agents().list();
var workspace = client.workspace("ws_default");
var runs = workspace.runs();
var artifacts = workspace.runArtifacts(runs.getFirst().id());
}Embedded SDK 则在调用方 JVM 内直接运行 Engine,显式装配所需 Provider,不启动 Service、Agent 或数据库:
var store = new InMemoryExecutionStore();
try (var engine = WePushEngine.builder()
.provider(new HttpProviderFactory())
.resultSink(store)
.eventSink(store)
.build()) {
var summary = engine.start(spec, recipients)
.completion().toCompletableFuture().join();
}从源码安装、依赖声明和完整示例见 sdk/README.md 与 sdk/embedded-java/README.md。1.2.0 发行包和独立 Java SDK 附件同时提供 Remote SDK、Embedded SDK、HTTP Provider 与标准渠道 Provider;CMPP、SMGP、SGIP、SMPP 则作为独立签名 Agent 插件发布,不进入 Embedded SDK 的默认依赖。
`1.2.0` 已交付基线
- Core API、Provider SPI、虚拟线程 Engine,以及 HTTP、SMTP Email、飞书/钉钉/企微机器人、阿里云短信、微信公众号、小程序和企业微信应用消息 Provider;CMPP、SMGP、SGIP、SMPP 以四个独立签名插件交付。
- Agent Protocol、Protobuf/gRPC 双向控制流、Sequence/Fencing Runtime、持久 Journal、加密 Secret Envelope、远端 Core 执行适配与常驻 Agent 包。
- Service 分层、SQLite/PostgreSQL/Flyway、Workspace 资源治理、跨 Run 账号认证熔断、资源编辑/不可变修订/分页、流式 Audience Import、正式发送确认、关联重发、真实总览、Agent 注册/心跳/持久 Lease、信封加密 Secret Store、Result/Command/Artifact 持久化、SSE,以及可切换的内嵌/远端执行器。
- PostgreSQL
LISTEN/NOTIFY低延迟唤醒、结构化脱敏诊断包、手动版本检查、Nginx/Traefik/Kubernetes 自建模板,以及 Agent Presigned Multipart Artifact(最大 5 TiB)。 - 相互独立的 Remote Java SDK、Embedded Java SDK 和 TypeScript API Client。
- React WebUI、亮色/暗色/跟随系统主题、低分辨率和键盘可访问性、动态 Provider/SecretRef Schema、渠道消息示例、资源生命周期、CSV/TXT 导入、Dry Run/正式确认/结果/失败重发闭环、真实 Workspace 和运行总览、动态 API 文档、Electron 安全外壳和共享前端 packages。
- Codex MCP / Skill 接入:Desktop 一键安装,WebUI 与发行包提供
wepush-ai.mjs;正式发送仍走既有确认令牌和 Workspace 权限。 - Java 25 构建与运行基线;完整包内置 Java 25 运行时,精简包和 Java SDK 需要系统 Java 25+。
- 一体化 Standalone/离线 Windows 安装、含 Java Runtime 完整包、正式备份恢复、升级健康门与自动回退;Desktop 提供本机 Service 运维、系统安全 Token 存储和签名插件 Stage/Activate/Rollback。
- 架构、单元、契约、浏览器 E2E、Desktop 冒烟、三平台安装/恢复/失败升级以及定时长稳矩阵。
产品范围和迭代优先级以 docs/product-scope-and-roadmap.md 为准;模块边界和实现细节以 docs/architecture-and-high-level-design.md、docs/detailed-design.md、docs/implementation-status.md、docs/deployment-and-operations.md 及 docs/adr/ 为准。
Overview (`next/plugins/README.md`)
WePush Next Carrier SMS Provider Plugins
provider-cmpp、provider-smgp、provider-sgip 和 provider-smpp 是四个独立 PF4J Provider 插件。它们只通过公开 Provider SPI 和 Agent Plugin API 接入,不是 Service 或 Agent 的内置依赖。共同的协议适配代码位于 provider-carrier-common,与 Classic 不存在源码或构建依赖。
每个插件支持协议登录、单号码 MT 文本提交、长短信分片、SUBMIT_RESP 判定、连接测试和无网络 Dry Run。网关受理不代表终端送达;本版本不处理 DLR、MO 或 SGIP 反向监听。
构建签名包
打包脚本不会内置或生成发布私钥。提供 Ed25519 PKCS#8 DER 的 Base64 和可信 Key ID 后分别构建:
首次配置时,可用 JDK 25+ 生成 Agent 所需的 PKCS#8 私钥和 X.509 公钥(Base64):
java scripts/GeneratePluginSigningKey.java private-key.b64 public-key.b64私钥文件只提供给离线构建/发布环境;Agent 仅配置 public-key.b64 的内容。
export WEPUSH_PLUGIN_SIGNING_KEY_ID=release-2026
export WEPUSH_PLUGIN_SIGNING_KEY_PKCS8_BASE64='...'
./scripts/package-carrier-provider.sh cmpp ./target/provider-plugins
./scripts/package-carrier-provider.sh smgp ./target/provider-plugins
./scripts/package-carrier-provider.sh sgip ./target/provider-plugins
./scripts/package-carrier-provider.sh smpp ./target/provider-pluginsAgent/Service 使用对应 Ed25519 X.509 公钥配置 WEPUSH_PLUGIN_TRUSTED_KEYS。插件包先经过 SHA-256 清单、Ed25519 签名、共享 API、Zip Slip 和压缩大小校验,再由 PF4J 加载。
Overview (`next/sdk/README.md`)
WePush Next Java SDKs
WePush Next 提供两种边界不同、彼此独立的 Java SDK:
| SDK | Maven Artifact | 执行位置 | 依赖边界 | 适用场景 |
|---|---|---|---|---|
| Remote Java SDK | sdk-java |
WePush Service / Agent | 只依赖公开 service-api,不依赖 Core、Engine 或 Provider |
应用通过 HTTP 调用已部署的 WePush |
| Embedded Java SDK | embedded-java |
调用方 JVM 进程内 | 依赖 Core API、Engine、Provider SPI;Provider 由应用显式选择 | 不启动 Service,直接把 Engine 嵌入 Java 应用 |
两者不是上下级关系,也不会合并成一个 Artifact。Remote SDK 保持稳定的网络契约边界;Embedded SDK 提供进程内执行能力。
1.2.0发行包和独立 Java SDK 附件同时提供 Remote Java SDK 与 Embedded Java SDK。
从源码安装
需要 Java 25 或更高版本。在仓库根目录执行:
cd next
./mvnw -pl sdk/sdk-java,sdk/embedded-java,providers/provider-http,providers/provider-standard -am install从稳定发行附件安装
进入包含 POM/JAR 的 sdk/ 目录(独立 Java SDK 压缩包则进入其根目录),先安装 Parent,再按需要安装 Remote 或 Embedded 依赖闭包:
VERSION=1.2.0
mvn install:install-file \
-Dfile="wepush-next-parent-$VERSION.pom" \
-DpomFile="wepush-next-parent-$VERSION.pom"
install_jar() {
artifact="$1"
mvn install:install-file \
-Dfile="$artifact-$VERSION.jar" \
-DpomFile="$artifact-$VERSION.pom"
}
# Remote Java SDK
install_jar service-api
install_jar sdk-java
# Embedded Java SDK 与内置 Provider
install_jar core-api
install_jar provider-spi
install_jar engine
install_jar embedded-java
install_jar provider-http
install_jar provider-standardWindows PowerShell 可以对相同文件逐条执行 mvn install:install-file。
Remote Java SDK
<dependency>
<groupId>com.fangxuele.wepush.next</groupId>
<artifactId>sdk-java</artifactId>
<version>1.2.0</version>
</dependency>Remote SDK 使用 WePushClient 连接 Service;建议 Service 和客户端使用相同 1.x Minor,兼容范围见《兼容性策略》:
try (var client = WePushClient.builder()
.endpoint(URI.create("http://127.0.0.1:18990"))
.build()) {
var system = client.system().info();
var workspace = client.workspace("ws_default");
var runs = workspace.runs();
}Remote SDK 支持资源分页/编辑、CSV/TXT 文件上传、正式发送确认、筛选重发和真实总览。例如上传文件不会先把完整内容读入 SDK 内存:
var workspace = client.workspace("ws_default");
var preview = workspace.uploadAudience(
Path.of("recipients.csv"), "Imported", null, "CSV", "itemId",
Map.of("mobile", "mobile"), null);
var audience = workspace.commitAudienceImport(preview.id());
var confirmation = workspace.confirmRun("job-id");
var run = workspace.createRun("job-id", UUID.randomUUID().toString(),
new ControlPlaneApi.CreateRunRequest(false, Map.of(), "manual",
confirmation.confirmationToken()));每个 Cursor Page 最多 100 条;正式发送和失败重发的确认令牌只短期有效,并绑定当前资源或来源 Run 状态。
Embedded Java SDK
<dependency>
<groupId>com.fangxuele.wepush.next</groupId>
<artifactId>embedded-java</artifactId>
<version>1.2.0</version>
</dependency>
<dependency>
<groupId>com.fangxuele.wepush.next</groupId>
<artifactId>provider-http</artifactId>
<version>1.2.0</version>
</dependency>
<dependency>
<groupId>com.fangxuele.wepush.next</groupId>
<artifactId>provider-standard</artifactId>
<version>1.2.0</version>
</dependency>provider-standard 提供 SMTP Email、飞书/钉钉/企微机器人、阿里云短信、微信公众号、小程序和企业微信应用消息。Embedded SDK 不创建数据库、不启动 Web Server、不读取 Service 配置,也不自动扫描第三方 Provider。应用必须通过 .provider(new SmtpProviderFactory()) 等方式显式注册允许的实现,并负责持久化结果、事件、Artifact 和 Secret 的适配器。渠道配置见内置 Provider 指南。
完整示例、资源所有权和生产接入说明见 Embedded Java SDK README。
公开 API 遵循 1.x 兼容承诺;新增能力优先通过新方法、Builder 或新类型提供,详见《兼容性策略》。
Overview (`next/ui/README.md`)
WePush Next UI
Web 与 Desktop 共用 React Features、API Client、Schema Renderer、UI primitives 和 Design Tokens。
cd next/ui
corepack pnpm install
corepack pnpm check
corepack pnpm dev开发服务器把 /api、/actuator 和 /openapi.yaml 代理到 http://127.0.0.1:18990。
Overview (`next/deployment/templates/README.md`)
WePush Next self-host templates
These templates extend the production-oriented deployment/container/compose.server.yaml baseline.
They intentionally contain no usable credentials. Copy the relevant example, replace everyREPLACE_ME/hostname, and keep PostgreSQL, S3 and Agent gRPC on private networks.
nginx/wepush-next.conf: TLS reverse proxy for the HTTP API/Web UI and long-lived SSE streams.traefik/compose.override.yaml: labels for an existing Traefik v3 deployment.kubernetes/: Service Deployment, internal Services, ingress and Pod disruption budget.
Server mode still requires PostgreSQL, S3, API security and Agent gRPC mTLS. Following the controlled
upgrade guide, let one Service instance finish Flyway migration before rolling the remaining pods; verify /actuator/health/readiness and/actuator/health/installation before sending traffic.
Overview (`next/docs/adr/README.md`)
WePush Next 架构决策索引
本目录保存 WePush Next 已接受的跨组件架构决策。架构与详细设计描述当前整体状态;ADR 记录选择原因、边界和后果。发生冲突时,应先确认 ADR 是否已被后续决策替代,再同步更新设计文档。
产品定位、长期非目标和迭代优先级以《产品目标、边界与路线图》为准;ADR 不得以技术扩展名义改变该产品边界。
| ADR | 状态 | 决策摘要 |
|---|---|---|
| ADR-0001 | 已接受 | Classic 与 Next 双轨独立发展,允许按需重复代码 |
| ADR-0002 | 已接受 | Java 25、Spring Boot 4.1.x、TypeScript/Vite/React、Electron 技术基线 |
| ADR-0003 | 已接受 | 默认并正式使用本地 AES-256-GCM 信封加密 Secret Store,不规划云密钥适配 |
| ADR-0004 | 已接受 | Agent 使用 gRPC 双向流,UI/SDK 事件使用 SSE |
| ADR-0005 | 已接受 | PF4J、签名插件、ClassLoader 隔离和滚动重启更新 |
| ADR-0006 | 已接受 | PostgreSQL 18、无状态 Service、Advisory Lock 和 Fencing |
| ADR-0007 | 已接受 | Standalone 本地制品、用户自建 Server S3-compatible 制品及保留策略 |
| ADR-0008 | 已接受 | Workspace 只作为同一自建实例信任域内的逻辑隔离 |
新增或替代决策时使用下一个连续编号,并在状态中明确“提议、已接受、已替代或已拒绝”。替代已有 ADR 时,旧 ADR 保留历史并链接到新 ADR。
Overview (`next/sdk/embedded-java/README.md`)
WePush Next Embedded Java SDK
Embedded Java SDK 让 Java 应用在自己的 JVM 进程内直接运行 WePush Core Engine,无需安装或启动 Service、Agent、数据库和 WebUI。
当前实现提供:
WePushEngine.builder()显式注册一个或多个ProviderFactory。- 从
List<RecipientRecord>或流式RecipientSource启动 Run。 - 返回 Core
RunHandle,支持查询状态、提交暂停/恢复/取消/并发调整命令以及等待RunSummary。 - 共享的 Engine 级 Sink,或由工厂创建的 Run 级 Result/Event/Artifact Sink。
- 可替换
SecretResolver与ExecutionClock。 - 面向测试和小任务的线程安全
InMemoryExecutionStore。
它刻意不包含 Service API、数据库、Spring、Agent、调度器、多租户控制面和 Provider 插件发现。应用只获得执行内核,其他生命周期由应用自己管理。
1.2.0发行包和独立 Java SDK 附件包含 Embedded SDK 及所需 POM/JAR。
要求与依赖
需要 Java 25 或更高版本。从源码安装当前模块、HTTP Provider 和标准渠道 Provider:
cd next
./mvnw -pl sdk/embedded-java,providers/provider-http,providers/provider-standard -am install调用方声明 SDK,并只加入自己允许使用的 Provider:
<dependency>
<groupId>com.fangxuele.wepush.next</groupId>
<artifactId>embedded-java</artifactId>
<version>1.2.0</version>
</dependency>
<dependency>
<groupId>com.fangxuele.wepush.next</groupId>
<artifactId>provider-http</artifactId>
<version>1.2.0</version>
</dependency>
<dependency>
<groupId>com.fangxuele.wepush.next</groupId>
<artifactId>provider-standard</artifactId>
<version>1.2.0</version>
</dependency>provider-standard 中包含 SMTP、飞书/钉钉/企微机器人、阿里云短信、微信公众号、小程序和企业微信应用消息。它只加入 Classpath,不会被 Embedded SDK 自动启用;调用方仍须显式 .provider(new SmtpProviderFactory()) 或注册其他允许的 Factory。Account、Message、Recipient 和 SecretRef 示例见内置 Provider 指南。
最小可运行示例
下面使用 HTTP Provider 的 Dry Run,因此不会访问 example.com:
/* Detailed source-code truncated for AI context efficiency. */正式发送只应在完成小范围测试、目标服务限流和幂等校验后把 dryRun 改为 false。HTTP Provider 默认阻止私网目标;只有明确受信的内部地址才应开启 allowPrivateAddresses。
Secret
默认 SecretResolver 只在 Provider 实际请求 Secret 时失败。需要认证的 Provider 必须显式配置:
var engine = WePushEngine.builder()
.provider(new HttpProviderFactory())
.secretResolver(ref -> loadSecretFromYourStore(ref))
.resultSink(resultSink)
.build();SecretValue 是可关闭资源。自定义 Resolver 不应记录明文;Secret Store、轮换和访问控制由宿主应用负责。
Sink 与资源所有权
共享 Sink 的生命周期与 Engine 一致,必须支持多个 Run 并发调用:
WePushEngine.builder()
.resultSink(sharedResultSink)
.eventSink(sharedEventSink)
.artifactSink(sharedArtifactSink);按 Run 隔离时使用工厂;工厂返回的资源在 Run 完成或启动被拒绝后由 Engine 关闭:
WePushEngine.builder()
.resultSinkFactory(spec -> openResultSink(spec.runId()))
.eventSinkFactory(spec -> openEventSink(spec.runId()))
.artifactSinkFactory(spec -> openArtifactSink(spec.runId()));start(spec, RecipientSource) 会接管 RecipientSource,完成或拒绝时关闭它。start(spec, List) 会先复制列表。WePushEngine.close() 会取消仍在运行的 Run、等待执行器退出并关闭共享 Sink;建议始终使用 try-with-resources。
InMemoryExecutionStore 不限容量,只适合测试和可控的小任务。长期进程和大批量发送应实现有界或持久化的 ResultSink / RunEventSink,并根据需要实现 ArtifactSink。
Provider 边界
SDK 不通过 Classpath 或 PF4J 自动发现 Provider。每个允许使用的实现都必须通过 .provider(...) 注册,RunExecutionSpec.provider 的 ID 和实现版本必须精确匹配。自定义渠道只需实现 provider-spi 的 ProviderFactory,无需依赖 Embedded SDK。
Classic Ai Integration (`docs/classic-ai-integration.md`)
Classic 的 MCP 与 Skill 接入
此功能随包含本次改动的 Classic 安装包提供;已发布的旧安装包不会自动获得该入口。Classic 的实现独立于 Next,不需要启动 Next Service,也不依赖 Next 的源码、Node.js 或构建产物。
一键接入
- 启动 Classic,先在界面中配置账号、消息、人群和任务。
- 打开 应用 → AI 助手接入。
- 点击 一键接入 Codex,安装器会写入 Codex MCP 配置和
wepush-classicSkill,同时开启本机桥接。重新启动 Codex 或新建会话加载接入。 - 对助手说:“用 WePush Classic 查看任务,空跑任务 1,告诉我校验结果。”
使用期间保持 Classic 运行。桥接默认关闭,首次开启后会在下次启动 Classic 时自动恢复。关闭本机接入会停止接受新调用,已启动的任务继续执行;退出 Classic 会中断本机任务。
安装名称均为 wepush-classic,与 Next 的 wepush 可以共存:
| 内容 | 位置 |
|---|---|
| Codex MCP | $CODEX_HOME/config.toml,未设置时为 ~/.codex/config.toml,表名 [mcp_servers.wepush-classic] |
| Skill | ~/.agents/skills/wepush-classic/SKILL.md |
| Skill 启动脚本 | Skill 目录中的 scripts/wepush-classic.sh / scripts/wepush-classic.ps1 |
| 通用 MCP JSON | 安装后的 Skill 目录中的 mcp.json |
| 本机连接与防重记录 | ~/.WePush5/ai/ |
安装器保留其他 MCP、模型和 Profile 配置,并在修改已有 Codex 配置前生成 config.toml.wepush-classic-.bak。遇到同名的手动 MCP 或非本安装器创建的 Skill 时停止,避免覆盖。重复安装会更新自己管理的文件;自定义 Skill 请使用另一个目录名称。
启动配置引用当前 Classic 的 Java 运行时和应用目录,发行版无需另装 Java。移动应用、切换开发目录或升级导致路径变化后,请在新位置再执行一次安装。
其他助手与 Skill 单独使用
支持 stdio MCP 的客户端:点击 复制通用 MCP 配置,把 JSON 中 mcpServers.wepush-classic 合并到客户端配置中,再重新连接。客户端可能有自己的配置格式,保留生成的 command 和 args。
仅使用 Skill:点击 仅安装 Skill。Codex 会从用户 Skill 目录发现它;其他支持 Skill 的助手可加载或复制整个目录。脚本使用与 MCP 相同的工具和本机桥接。使用 tools 查询参数,call <工具名> 从标准输入读取一个 UTF-8 JSON 对象:
sh ~/.agents/skills/wepush-classic/scripts/wepush-classic.sh tools
printf '%s' '{"taskId":1}' | sh ~/.agents/skills/wepush-classic/scripts/wepush-classic.sh call wepush_classic_get_taskWindows 示例:
'{"taskId":1}' | powershell -NoProfile -ExecutionPolicy Bypass -File "$HOME/.agents/skills/wepush-classic/scripts/wepush-classic.ps1" call wepush_classic_get_task工具与执行范围
| 工具 | 能力 |
|---|---|
wepush_classic_system_info |
检查连接与能力 |
wepush_classic_list_tasks |
分页查询已保存的任务 |
wepush_classic_get_task |
查看消息模板、账号名称、人群名称及数量 |
wepush_classic_prepare_run |
返回发送预览与有效期 10 分钟的确认令牌,不投递消息 |
wepush_classic_dry_run |
使用 Classic 原有渠道逻辑空跑校验 |
wepush_classic_send_run |
执行已授权的正式发送 |
wepush_classic_get_run |
按 runId 查询进度与结果 |
wepush_classic_list_history |
分页查询指定任务的 Classic 历史 |
分页参数为 offset(默认 0)和 limit(默认 30,最大 100)。工具只操作已有资源,账号、消息、人群和任务的创建及编辑仍在 Classic 界面中完成。
当前执行范围为已保存的手动固定线程任务:线程数 1~100,已关闭任务的结果邮件提醒,每个 Classic 实例最多同时执行 4 个由 AI 启动的任务。计划、触发、变速任务和结果邮件提醒仍由 Classic 原有界面执行。各渠道的成功语义、限流和空跑行为沿用现有发送器;空跑不证明真实渠道认证或投递一定成功。
正式发送前,助手必须核对 prepare_run 的预览,确保已有用户授权覆盖内容、人群及发送时机,再传入 taskId、confirmationToken、新的标准小写 UUID idempotencyKey 和 userConfirmed: true。任务、消息、账号或人群在预览后变化时,旧预览失效。接入在同一个数据库锁内核对资源并准备发送器,接受运行后保留该次任务和人群快照。
本次 AI 接入的“发送预览”是只读操作。Classic 原有消息编辑界面中的“预览发送”仍按原行为向预览目标实际发送。
防重与结果
runId 与调用者提供的 idempotencyKey 相同。请求在启动发送前持久化,重复使用相同请求编号和相同参数会返回原运行记录。确认令牌只允许启动一次正式发送,但已接受请求的原样重试不受令牌过期影响。
超时或响应丢失时先查询原 runId,必要时原样重试;不要换 UUID 重发。Classic 进程退出或执行异常后可能返回 UNKNOWN,必须先核对 Classic 历史和渠道结果,不会自动恢复投递。防重记录长期保留在 ai/requests;删除这些记录会失去对应请求的防重保护。
FINISHED 只表示执行结束,应继续核对 totalCount、successCount 和 failCount。它不保证所有目标发送成功,也不等同于终端已送达。
本机边界
桥接只监听 127.0.0.1 随机端口,每次开启生成新的令牌;拒绝带 Origin 的浏览器请求。连接文件在支持 POSIX 权限的系统上仅当前用户可读写。MCP 和 Skill 配置只保存连接文件路径,不包含令牌或渠道凭据。不要共享 ~/.WePush5/ai/connection.json;同一系统用户下的可信本机客户端可以通过此文件调用桥接。
工具不返回账号配置、原始日志、完整人群记录或结果文件。消息模板里的常见凭据字段(如 Header、Cookie、Token)会隐藏;自定义正文中的敏感文本仍属于消息内容,应按实际使用范围处理。
开发验证
使用 Java 25+ 和 Maven 执行 mvn test。新增测试覆盖 TOML 保留与冲突、重复安装、脚本路径转义、MCP 协议、鉴权与令牌轮换、配置变化、确认过期、持久防重及并发重试。真实链路测试在隔离子进程中使用临时数据库与本机 HTTP 接收器,断言空跑零投递、正式发送使用已接受的消息快照、重复请求不增加投递。
配置依据:Codex MCP、Codex Skills、MCP 生命周期及工具协议。
Classic Carrier Sms Protocol Design (`docs/classic-carrier-sms-protocol-design.md`)
WePush Classic 运营商短信协议支持设计
1. 目标与结论
Classic 版新增统一的“运营商协议短信”消息类型,支持 CMPP 2.0/3.0、SMGP 3.0、SGIP 1.2 和 SMPP 3.4 的 MT 短信提交。这四种协议均为长连接二进制协议,不应继续套用现有 HTTP 短信供应商的“每条消息创建客户端”模型。
结论:在 Classic 现有账号、消息和任务模型上实现,不需要修改数据库表结构。账号参数和消息内容继续分别保存于 TAccount.accountConfig 和 TMsg.content JSON 中。
2. 产品范围
交付范围:
- 四协议的单号码 MT 文本短信发送,长短信由协议层自动分片。
- 一个统一消息类型,账号中选择具体协议。
- 每个账号共享一个协议客户端,复用 TCP 长连接,支持断线重连、窗口控制和应答超时。
- 将
SUBMIT_RESP是否成功映射为 WePush 的发送成功/失败,返回协议消息 ID 或错误码。 - 模板变量替换、测试登录、测试发送、批量任务、无限推送和模拟发送与现有 Classic 能力保持一致。
- 账号删除、重新配置或应用退出时关闭对应连接。
明确不支持:
- 状态报告(DLR)的请求、解析、落库和发送历史回填。所有协议的状态报告请求位固定为 0;
SUBMIT_RESP成功只表示网关已受理,不代表终端已收到。 - MO 上行短信的解析、存储或业务处理。
- SGIP 反向 TCP 监听服务。
- 群发指令中一次携带多个目标号码;Classic 现有任务粒度保持一人一次提交。
部分网关可能无视请求位,在现有双向连接上推送 DELIVER。客户端只发送协议要求的确认包并丢弃消息,不将其交给 WePush 业务层;这是维持连接稳定所需的传输层行为,不属于状态报告或上行业务支持。
3. 技术选型
使用 Maven Central 中的 com.chinamobile.cmos:sms-client:0.1 作为协议客户端。它在同一抽象下支持 CMPP、SMGP、SGIP 和 SMPP,并提供连接池、心跳/重连、窗口和长短信分片能力。该组件及其核心声明为 Apache-2.0。
引入前后必须通过下列门槛:
- Java 25 下编译、建连、发送和关闭均无需额外 JVM
--add-opens。无用的 FST/持久化路径从运行时依赖中排除。 - 检查运行时依赖和安装包体积;对文本短信无用的 WAP/MMS/持久化依赖仅在确认不会触发类加载后才能排除。
- 连接不可达和应答丢失时,WePush 设置的超时必须真正结束当次等待。
- 最终分发前对全部运行时依赖做许可证和安全审计。文本编码必需的
smsj声明为 MPL-1.1;WAP/MMS 才使用的 GPL-2.0wbxml-stream已从 Classic 运行时依赖排除。Netty 保持 4.1 二进制兼容线并由根 POM 统一锁定安全修复版本,不使用协议库原始锁定的 4.1.86.Final。协议库带入的 Commons Pool、BeanUtils 和 Codec 也由根 POM 锁定到仍兼容其调用方式的正式维护版本。
如上述门槛无法满足,保留 CarrierSmsGatewayClient 内部接口,可以替换协议库而不影响 UI、配置和任务层。
4. 整体设计
Classic 任务工作线程
|
CarrierSmsMsgSender
|
CarrierSmsSessionRegistry -- accountId --> 共享 CarrierSmsGatewayClient
| |
+--------------------------------------+
|
sms-client / TCP 长连接池
|
CMPP | SMGP | SGIP | SMPP 网关CarrierSmsSessionRegistry 是进程级单例,以账号 ID 为键。任务线程只获取共享客户端,不直接创建或关闭 TCP 连接。同一账号配置发生变化时,通过配置指纹识别并原子替换旧客户端。
模拟发送不创建网络连接。真实发送在首次使用账号时惰性建连。
5. 配置模型
5.1 账号配置
| 字段 | 作用 | 适用协议 |
|---|---|---|
protocol |
CMPP / SMGP / SGIP / SMPP |
全部 |
host, port |
网关地址 | 全部 |
username, password |
鉴权信息 | 全部 |
version |
协议版本 | CMPP/SMGP/SMPP |
maxChannels |
账号最大 TCP 连接数,默认 1 | 全部 |
windowSize |
单连接未应答窗口,默认 16 | 全部 |
requestTimeoutMillis |
SUBMIT_RESP 超时,默认 10000 ms |
全部 |
sourceAddress |
企业号码/SP 号/接入号 | 全部,协议字段名不同 |
serviceId |
业务代码 | CMPP/SMGP/SGIP/SMPP |
msgSrc |
企业代码 | CMPP/SMGP |
nodeId |
SGIP 企业编号 | SGIP |
corpId |
SGIP 企业代码 | SGIP |
heartbeatIntervalSeconds |
空闲连接心跳间隔,默认 30 秒 | 全部 |
端口默认值仅用于 UI 快速填写,不作为协议规则强制:CMPP 7890、SMGP 8900、SGIP 8801、SMPP 2775。实际以运营商/网关商提供的参数为准。
密码存储延续 Classic 现有账号机制;本功能不在日志、异常信息或 toString() 中输出密码。
5.2 消息配置
消息 JSON 只保存 content 文本。内容支持 Classic 现有 Velocity 变量。号码仍来自人员表第一列,发送前去除首尾空格,不擅自改写国家码或号段。
6. 协议映射和成功判定
| 协议 | 提交请求 | 接收人 | 内容 | 提交成功 |
|---|---|---|---|---|
| CMPP | CMPP_SUBMIT |
Dest_terminal_Id |
Msg_Content |
CMPP_SUBMIT_RESP.Result == 0 |
| SMGP | SMGP_SUBMIT |
DestTermID |
MsgContent |
SMGP_SUBMIT_RESP.Status == 0 |
| SGIP | SGIP_SUBMIT |
UserNumber |
MessageContent |
SGIP_SUBMIT_RESP.Result == 0 |
| SMPP | submit_sm |
destination_addr |
short_message |
PDU command_status == 0 |
应答类型不符、超时、连接失败、编码/分片失败或非零结果码均返回失败。失败描述包含协议和数值错误码,但不包含凭据。
对长短信,所有分片的提交应答均为成功才认为该接收人提交成功。任一分片失败时不自动重发整条长短信,避免收件人获得重复内容。
7. 并发、生命周期与可观测性
- Classic 任务线程数不等于 TCP 连接数。实际协议并发上限为
maxChannels * windowSize,两层都需有界。 - 断线后下一次发送重新建连;协议层和
MsgSender上层均不对结果不明的提交自动重试。 - 账号删除时调用
close(accountId),应用关闭时调用shutdown()。 - 日志记录协议、账号 ID、网关地址、连接状态、应答耗时和错误码。号码按现有 Classic 行为展示,密码绝不记录。
8. UI 行为
- 消息类型页新增“运营商协议短信”。
- 账号页选择协议后只显示通用字段和当前协议专属字段,切换协议不丢失已输入的其他字段。
- 保存前校验必填项、端口范围、连接数、窗口和超时。保存账号不主动连接运营商网关。
- 账号页提供“测试登录”,只验证 TCP 和协议鉴权,不提交短信。
- 消息编辑页提供多行文本内容、变量提示和字符数显示。
9. 测试与验收
自动化测试:
- 四协议账号配置的 JSON 序列化/反序列化、默认值、边界和协议专属校验。
- 四协议提交请求字段映射。
- 四类成功应答、非零错误、错误应答类型、异常和超时映射。
- 同账号并发只创建一个共享客户端;配置变更、账号删除和应用退出正确关闭客户端。
- 模拟发送不创建客户端或 TCP 连接。
- 本地模拟网关覆盖四协议登录、中文提交、长短信分片和提交应答。
- 现有 Classic 单元测试和打包构建不回归。
联调验收:每种协议至少需要一个测试网关账号,覆盖登录、心跳、中文短信、长短信、网关拒绝、断线重连和应答超时。无真实网关时只能完成编译、单测和本地仿真验收,不能宣称已通过运营商现网联调。
10. 完成边界
实现以获取并校验 SUBMIT_RESP 为终点。任务统计中的“成功”统一表示网关受理;产品不推断、不展示终端送达状态,也不规划 DLR、MO 或 SGIP 反向监听能力。
Next Docs Ai Integration (`next/docs/ai-integration.md`)
用 Codex 等 AI 助手操作 WePush
本功能随 1.2.0 的 WebUI、Desktop 和完整发行包交付,使用同一个独立安装器 wepush-ai.mjs。
MCP 桥接器由 AI 客户端通过 stdio 启动,再调用用户自己的 WePush Next REST API,不需要新开服务端口或部署公共服务。它遵循已有 Workspace RBAC、Provider 校验、发送确认和 Idempotency-Key 语义。
桌面端一键接入
- 启动 WePush Next Service,先在 WePush 中配置渠道账号及 SecretRef。
- 打开 设置 → AI 助手接入,核对 Service URL 和当前 Workspace。
- 点击 一键接入 Codex,安装 MCP 和 Skill;或点击 仅安装 Skill。
- 重新连接 Codex 的 MCP,必要时重启 Codex。可以说:“用 WePush 列出已有任务,并对我选择的任务执行 Dry Run。”
桌面安装使用随 Desktop 提供的 Electron Node Runtime,无需另外安装 Node.js 或 Codex CLI。安装的 MCP 指向该 Desktop 可执行文件,移动或卸载 Desktop 后需要重新安装接入。
安装器会在 ~/.agents/skills/wepush 写入 Skill、独立工具脚本和连接信息。Codex MCP 配置写入 $CODEX_HOME/config.toml,未设置时使用 ~/.codex/config.toml;修改前保留同目录 .wepush-.bak 备份。重复安装仅更新自己的配置块,保留其他 MCP、模型设置及注释。如果已有同名手动配置或自定义 Skill,安装会报告冲突并保留原文件。
“仅安装 Skill”不修改已有 MCP 配置,也不移除此前安装的 MCP;Skill 使用其自己的连接文件。需要同步修改已有 MCP 的连接时,选择“一键接入 Codex”。
浏览器或发行包安装
浏览器不能直接写入客户端配置。在 设置 → AI 助手接入 下载安装器,然后在下载目录执行界面生成的命令。也可使用新构建发行包的 ai/wepush-ai.mjs。这两种方式需要 Node.js 24+:
node ./wepush-ai.mjs install --target codex --url http://127.0.0.1:18990 --workspace ws_default以上命令也适用于 Windows PowerShell。--target skill 只安装 Codex Skill;--target generic 把工具和通用 mcpServers JSON 写入 ~/.wepush/ai,不更改 Codex 配置。安装后不需要保留下载的原文件。
Service URL 是 AI 客户端运行环境 能访问的地址。容器、WSL、远端主机中的 127.0.0.1 不指向桌面所在机器;需要配置实际可达的 HTTPS 地址。桥接器仅允许回环地址使用 HTTP,拒绝 URL 中的密码或查询参数,并拒绝跟随 API 重定向。
Token 与权限
本地 Service 未启用认证时可直接使用。启用认证后,在启动 AI 客户端的环境中设置 WEPUSH_API_TOKEN,使用当前 Workspace 的 VIEWER(查询)或 OPERATOR(创建和发送)Token。已有系统管理员 Token 不应作为日常发送身份。
安装器不读取或复制 WePush 登录 Token,不将 Token 写入 Skill、MCP 配置或导出文件。Codex 配置通过 env_vars = ["WEPUSH_API_TOKEN"] 将客户端的环境变量传给 MCP 子进程。GUI 客户端的环境与终端可能不同,设置后应重启客户端;其他客户端按其环境变量设置方式传入该变量。
独立脚本同时支持 WEPUSH_SERVICE_URL、WEPUSH_WORKSPACE_ID 覆盖连接文件;MCP 安装配置中指定的 URL/Workspace 优先于客户端外部的同名环境变量。修改连接最直接的方式是重新安装。
其他 MCP / Skill 客户端
Desktop 的 导出通用 MCP 配置 会准备本机运行文件并打开 JSON 保存对话框;取消保存时,配置仍在 ~/.wepush/ai/mcp.json。将其中 mcpServers.wepush 合并到支持 stdio 的客户端(例如 Claude Desktop 或 Cursor)的配置中,保留其他服务器条目。导出包含当前机器的绝对路径;迁移到另一台机器时应在那台机器运行安装器。
其他支持 Agent Skills 的客户端可以使用生成的 SKILL.md 与整个 scripts/ 目录。脚本封装了已检测到的运行时路径,跨机器移动需要重新生成安装文件。此版本不提供远程 HTTP MCP 端点或 OAuth,云端仅支持远程 HTTP MCP 的客户端不能直接使用该 stdio 配置。
工具与发送流程
| 工具 | 能力 |
|---|---|
wepush_system_info |
测试连接、版本及当前 Workspace |
wepush_list_providers / wepush_provider_schema |
发现渠道与账号、消息、收件人 Schema |
wepush_list_resources / wepush_get_resource |
分页查询或读取账号、消息、受众、任务及 Run |
wepush_create_message / wepush_create_audience / wepush_create_job |
创建草稿及任务,不发送 |
wepush_dry_run |
执行测试运行 |
wepush_prepare_run / wepush_send_run |
预览正式发送并使用确认令牌创建 Run |
wepush_run_results / wepush_run_command |
查询逐条结果、暂停、恢复或取消 |
先读取现有资源及渠道 Schema,准备消息、受众、任务,再执行 Dry Run。大于 1000 条的受众使用 WePush CSV/TXT 导入。账号、密钥、调度和失败重发在本版本仍由 WePush 界面管理。
正式发送需要先取得预览、核对消息内容和受众,然后在用户授权范围内提交 confirmationToken、同一个 idempotencyKey 和 userConfirmed: true。userConfirmed 是供助手表达已有授权的参数,不是独立的人工身份验证;实际权限和令牌有效性由 Service 校验。
返回 Run ID 表示已创建运行,不表示收件人已收到消息。应继续查询 Run 状态和结果。网络超时不自动重试;需要重试时复用原幂等键与请求内容。UNKNOWN 不等于失败,不自动重发。
Skill 在 MCP 不可用时可以直接调用同一套工具:
# 在安装后的 Skill 目录中执行;Windows 使用 scripts/wepush-ai.ps1
./scripts/wepush-ai.sh tools
printf '%s' '{}' | ./scripts/wepush-ai.sh call wepush_system_info
./scripts/wepush-ai.sh call wepush_create_message < message-arguments.jsontools 返回工具描述和完整 JSON Schema。调用参数走 stdin,避免把消息内容拼接成 shell 命令。
移除与排错
- 移除 Codex MCP 可运行
codex mcp remove wepush,或在客户端中移除该服务器;这不会删除 WePush 的业务数据。 - 不再使用 Skill 时删除安装器管理的
~/.agents/skills/wepush。通用安装文件位于~/.wepush/ai。 401:检查 AI 客户端实际继承的WEPUSH_API_TOKEN;403:检查 Token 的 Workspace 和角色。- 安装提示同名冲突:保留原配置,重命名自定义条目或先移除不再使用的条目,再安装。
- 安装中断后若残留
.install-lock目录,确认没有安装进程运行后移除该空目录再重试。
实现接入格式参考 Codex MCP 文档、Codex Skills 文档和 MCP stdio 传输规范。
开发验证:在 next/ui 运行 pnpm check 和 pnpm --filter @wepush-next/web e2e。构建 Service JAR 后,可运行 pnpm --filter @wepush-next/ai-integration test:service;该测试创建临时数据库和仅本机可达的 Service,通过 MCP 验证认证、Schema、草稿、发送预览、幂等 Dry Run 与结果,结束后清理,不实际投递消息。
Next Docs Architecture And High Level Design (`next/docs/architecture-and-high-level-design.md`)
WePush Next 架构与概要设计
- 文档状态:已接受基线
- 文档版本:0.3
- 日期:2026-08-27
- 适用范围:
next/ - 关联决策:见 ADR 索引
- 产品范围:产品目标、边界与路线图
1. 文档目的
本文档描述 WePush Next 的目标架构和概要设计,作为后续模块创建、接口设计、技术选型、任务拆分和验收的共同基线。
WePush Next 是独立于当前 Classic 客户端的新产品线。本文档不要求 Classic 按照本架构改造,也不限制 Classic 继续独立增加消息类型和功能。
2. 建设目标
WePush Next 的目标是从单机桌面推送工具发展为同时支持本地使用、用户自建服务化部署和分布式执行的消息推送产品。产品始终由用户自行下载、安装和运维,不建设官方公共 SaaS 或承载用户业务数据的集中平台。
主要目标如下:
- 提供与 UI、数据库和 Web 框架无关的 Core Engine。
- 提供可独立运行的 Agent,支持本地或远程执行推送任务。
- 提供可安装到 Linux、Windows 和 macOS 的 Service。
- Service 对外提供稳定、可版本化的 HTTP API。
- 提供轻量的远程 Java SDK 和依赖 Core 的嵌入式 Java SDK。
- 提供 WebUI 和 Desktop UI,两者使用相同的 Service API。
- 通过 Schema 驱动可视化配置,减少新增 Provider 时的 UI 开发量。
- 提供任务运行监控、实时日志、动态并发调整和交互式 API 文档。
- 支持从单机 SQLite 平滑发展到 PostgreSQL 和多 Agent 部署。
- 保证 Classic 与 Next 可以在同一台机器上并行安装和运行。
3. 非目标
以下项目不属于当前架构目标:
- 不追求与 Classic 的内部代码、数据模型或 UI 实现兼容。
- 不要求首个版本迁移 Classic 的全部消息类型。
- 不承诺外部消息渠道的严格 Exactly Once 语义。
- Standalone 首个版本不要求多节点;正式 Server 模式按 PostgreSQL 控制面高可用基线建设。
- 不在首期建设通用工作流编排平台。
- 不在 Core 中实现用户、权限、租户、HTTP API 或数据库管理。
- 不要求 WebUI 与 Desktop UI 使用完全相同的外壳技术,但应尽量共享前端能力。
- 不建设官方公共 SaaS、用户注册、计费、套餐、订阅、账单或公共市场。
- 不提供面向互不信任或恶意公共租户的物理隔离和跨区域 Active-Active 控制面。
- 不建设云 KMS、云 Secret Manager、Vault 或操作系统凭据库形式的 Service Secret Store 官方适配器。
- 不引入强制自动更新、远程配置下发或使用遥测回传。
长期产品边界及其变更规则以《产品目标、边界与路线图》为准。
4. 架构原则
4.1 明确执行位置
所有推送动作最终由 Agent 中的 Core Engine 执行。Service 负责任务定义、调度和控制,不直接包含消息渠道的发送业务逻辑。
单机模式可以将 Agent 嵌入 Service 进程,但逻辑边界保持不变。
4.2 依赖指向稳定抽象
Core Engine 依赖 Core API 和 Provider SPI,不依赖 Service、Agent、UI、MyBatis、Spring 或具体 Provider。
4.3 配置和运行快照分离
账号、消息、受众和任务定义可以被修改;一次 Run 启动后必须形成不可变执行快照,避免运行过程中读取到被修改的配置。
4.4 Schema 驱动扩展
Provider 使用 JSON Schema 描述账号、消息和受众配置,UI、API 校验和 SDK 辅助能力围绕同一份 Schema 工作。
4.5 事件驱动运行状态
Core 不直接更新 UI。进度、日志、告警和状态变化都以结构化 Run Event 输出,由 Agent 和 Service 负责传输与持久化。
4.6 默认安全
本地模式默认只监听回环地址;远程模式必须启用认证。Secret 不出现在日志、普通查询响应和运行事件中。
4.7 渐进式分布
先完成 Service 内嵌 Agent 的单机闭环,再引入远程 Agent。分布式能力建立在稳定的运行状态机和租约模型之上。
5. 系统上下文
flowchart LR
Operator[操作人员] --> WebUI[WebUI]
Operator --> Desktop[Desktop UI]
JavaApp[Java 应用] --> SDK[Remote Java SDK]
WebUI --> API[Service API]
Desktop --> API
SDK --> API
API --> Service[WePush Service<br/>控制面]
Service --> Database[(业务数据库)]
Service --> ArtifactStore[(制品存储)]
Service --> Agent[WePush Agent<br/>执行面]
Agent --> Engine[Core Engine]
Engine --> Providers[Provider 插件]
Providers --> Channels[微信 / 短信 / 邮件 / HTTP 等]6. 总体组件
| 组件 | 核心职责 | 明确不负责 |
|---|---|---|
| Core API | 执行命令、状态、事件、结果和策略的稳定模型 | 数据库、HTTP、UI、Provider 实现 |
| Core Engine | 执行流水线、并发、限流、重试、取消、结果汇总 | 调度、用户权限、REST API |
| Provider SPI | Provider 生命周期、配置 Schema、发送和能力接口 | 具体渠道实现 |
| Provider | 渠道协议适配、认证、请求构建和响应解释 | Service CRUD、UI 控件 |
| Agent | 注册、心跳、任务租约、Core 执行、事件上报 | 面向用户的业务管理 API |
| Service | 配置管理、调度、Run 编排、Agent 管理、持久化、认证 | 直接调用渠道发送消息 |
| Remote Java SDK | Service API 的类型安全客户端 | 本地执行引擎 |
| Embedded Java SDK | 在 Java 进程内使用 Core 和 Provider | 远程 Service 管理能力 |
| WebUI | 浏览器管理、配置、监控和 API 调试 | 直接访问数据库或 Core |
| Desktop UI | 桌面入口、本地 Service 管理、远程 Service 连接 | 直接执行 Provider |
| Distribution | 三平台安装、服务注册、升级和卸载 | 业务逻辑 |
7. 建议目录与模块结构
next/
├── pom.xml
├── docs/
│ ├── architecture-and-high-level-design.md
│ └── adr/
├── platform/
│ └── wepush-bom/
├── core/
│ ├── core-api/
│ ├── provider-spi/
│ └── engine/
├── providers/
│ ├── provider-http/
│ ├── provider-standard/
│ └── ...
├── agent/
│ ├── agent-protocol/
│ ├── agent-runtime/
│ └── agent-app/
├── service/
│ ├── service-api/
│ ├── service-domain/
│ ├── service-application/
│ ├── service-infrastructure/
│ └── service-app/
├── sdk/
│ ├── sdk-java/
│ └── embedded-java/
├── ui/
│ ├── web/
│ └── desktop/
├── distributions/
│ ├── linux/
│ ├── windows/
│ └── macos/
└── tests/
├── architecture-tests/
├── contract-tests/
├── integration-tests/
└── end-to-end-tests/模块可以随实现进展合并或拆细,但组件依赖方向不得被破坏。
8. 模块依赖规则
flowchart BT
CoreAPI[core-api]
SPI[provider-spi] --> CoreAPI
Engine[engine] --> CoreAPI
Engine --> SPI
Provider[providers/*] --> SPI
AgentRuntime[agent-runtime] --> Engine
AgentApp[agent-app] --> AgentRuntime
AgentApp --> Provider
ServiceDomain[service-domain] --> CoreAPI
ServiceApp[service-application] --> ServiceDomain
ServiceInfra[service-infrastructure] --> ServiceApp
ServiceBoot[service-app] --> ServiceInfra
ServiceBoot --> AgentRuntime
ServiceBoot --> Provider
SDK[sdk-java] --> ServiceAPI[service-api]
Embedded[embedded-java] --> Engine强制规则:
core/*不得依赖agent/*、service/*、sdk/*或ui/*。- Provider 不得直接访问 Service 数据库。
sdk-java不得依赖 Engine 或具体 Provider。embedded-java可以依赖 Engine 和选定 Provider。- UI 只能通过公开 API 或 SDK 访问 Service。
- Service 的领域层不得依赖 Controller、数据库实现或操作系统服务实现。
- 可使用架构测试阻止禁止的包依赖进入主分支。
9. 部署形态
9.1 Standalone 单机模式
flowchart LR
UI[WebUI / Desktop UI] --> Service[Service]
subgraph Process[单个 Service 进程]
Service --> EmbeddedAgent[Embedded Agent]
EmbeddedAgent --> Engine[Core Engine]
end
Service --> SQLite[(SQLite)]
Service --> LocalFiles[(本地制品目录)]适用个人电脑、开发环境和轻量服务器。Service、调度器和内嵌 Agent 在同一进程内运行,使用 SQLite 和本地文件系统。
9.2 Server + Remote Agent 模式
flowchart TB
UI[WebUI / SDK] --> LB[HTTP/2 负载均衡器]
Agent1[Agent A] --> LB
Agent2[Agent B] --> LB
Agent3[Agent C] --> LB
LB --> Service1[Service A]
LB --> Service2[Service B]
Service1 --> PostgreSQL[(PostgreSQL 18 HA)]
Service2 --> PostgreSQL
Service1 --> ObjectStore[(S3-compatible Artifact Store)]
Service2 --> ObjectStore
Agent1 --> Channels[外部渠道]
Agent2 --> Channels
Agent3 --> Channels远程 Agent 主动通过负载均衡器建立出站 gRPC 双向流,便于跨防火墙部署。正式 Server HA 至少包含两个无状态 Service 实例;PostgreSQL 是业务和协调状态的事实源,对象制品不得依赖某个 Service 的本地磁盘。具体约束见 ADR-0006。
9.3 Embedded 模式
业务 Java 应用通过 embedded-java 直接创建 Engine,并显式注册所需 Provider。该模式不依赖 Service,但也不自动获得 Service 的用户管理、调度、持久化和 WebUI 能力。
10. Core Engine 概要设计
10.1 核心输入
Engine 接收不可变的 RunExecutionSpec,主要包含:
runId- Provider 标识和版本
- 账号配置或 Secret 引用
- 消息模板快照
- 受众快照或可流式读取的 Recipient Source
- 并发、限流和重试策略
- Dry Run、结果保存和超时策略
- 执行环境及扩展属性
Engine 不接受数据库主键后自行查询数据库,所有执行所需信息由调用方准备并显式传入。
10.2 执行流水线
flowchart LR
Validate[校验执行快照] --> Prepare[初始化 Provider]
Prepare --> Read[流式读取 Recipient]
Read --> Render[渲染消息]
Render --> Limit[并发与限流]
Limit --> Send[Provider 发送]
Send --> Classify[结果分类]
Classify --> Retry{是否可重试}
Retry -- 是 --> Backoff[退避等待]
Backoff --> Send
Retry -- 否 --> Persist[写入结果流]
Persist --> Event[发布进度事件]10.3 执行策略
ConcurrencyPolicy:固定并发或可动态调整并发。RateLimitPolicy:全局、Provider、账号或任务级速率限制。RetryPolicy:最大次数、指数退避、抖动和可重试错误分类。TimeoutPolicy:单次请求和整个 Run 的超时。ResultPolicy:结果明细、响应体、失败样本和制品保存策略。CancellationPolicy:协作式取消和取消后的未发送数据处理。
Java 25 虚拟线程可以作为 I/O 型发送的默认执行单元,但必须通过并发闸门和限流器限制实际外部请求数量,不能把虚拟线程数量直接等同于渠道并发能力。
10.4 事件接口
Core 通过 RunEventSink 输出结构化事件,例如:
RUN_STARTEDPROGRESS_UPDATEDITEM_FAILEDCONCURRENCY_CHANGEDRETRY_SCHEDULEDRUN_CANCELLINGRUN_COMPLETEDRUN_FAILED
高频进度在 Agent 内聚合后批量上报,避免每条消息产生一次数据库写入。
10.5 结果与内存模型
- Recipient、发送结果和日志均采用流式或批量处理。
- 不在内存中长期保存完整的成功、失败和未发送列表。
- 大结果集写入 Artifact Store,数据库只保存摘要、索引和制品引用。
- Run 结束时生成成功、失败、未发送和可选响应体制品。
11. Provider SPI 概要设计
11.1 Provider 能力
每个 Provider 至少暴露:
- 唯一
providerId和实现版本。 - 展示名称、分类和能力列表。
- 账号配置 JSON Schema。
- 消息配置 JSON Schema。
- 受众字段定义及可选受众配置 Schema。
- 配置校验和连接测试能力。
- 同步发送接口;异步渠道可通过统一结果句柄扩展。
- 错误分类、是否可重试和建议退避时间。
- 可选 Dry Run、预览和动态限流能力。
11.2 Schema 示例
{
"providerId": "wepush.http",
"version": "1.0.0",
"capabilities": ["preview", "dry-run", "response-body"],
"accountSchema": {"$ref": "schemas/account.json"},
"messageSchema": {"$ref": "schemas/message.json"},
"uiSchema": {"$ref": "schemas/ui.json"}
}敏感字段通过 writeOnly 及 x-wepush-secret 标识。UI 只显示“已配置”状态,Service 查询接口不得返回原值。
11.3 Provider 生命周期
Provider 实例应按账号和执行上下文创建,禁止使用可变全局单例保存 Token、连接和运行进度。连接池可以共享,但必须具有明确的关闭和隔离策略。
外部 Provider 使用 PF4J 3.15.x 发现,每个插件使用独立 ClassLoader 隔离私有厂商 SDK。正式环境仅装载清单与 Ed25519 签名均通过验证的版本。更新采用版本化目录、Agent Drain、重启、验证和激活流程,多 Agent 场景滚动执行;不在 JVM 内热替换正在使用的 Provider。ClassLoader 隔离不是恶意代码沙箱;如果用户自建安全场景需要运行非受信插件,必须单独评审独立进程 Runner,当前不将其列为既定路线图。详见 ADR-0005。
12. Agent 概要设计
12.1 职责
- 向 Service 注册 Agent 身份、版本和能力。
- 周期性上报心跳、负载、Provider 清单和运行状态。
- 领取有时限的 Run Lease。
- 下载执行快照和所需制品。
- 调用 Core Engine 执行。
- 批量上报事件、指标和结果制品。
- 接收取消、暂停、恢复和动态调整并发等命令。
- 在进程退出时尽力完成优雅停止。
12.2 租约与恢复
sequenceDiagram
participant A as Agent
participant S as Service
A->>S: Connect + Hello(gRPC 双向流)
S-->>A: Welcome + 协议窗口
loop 心跳/调度
A->>S: Heartbeat(capacity, providers)
S-->>A: LeaseOffer + executionSpecRef
A->>S: LeaseAck(epoch, fencingToken)
end
A->>A: Core 执行
loop 运行期间
A->>S: Heartbeat + EventBatch
S-->>A: EventAck + RunCommand
end
A->>S: complete(summary, artifacts)- Lease 有明确到期时间并通过心跳续约。
- Agent 失联后,Run 先进入
LOST或RECOVERING,不能立即无条件重发。 - 如果外部渠道已接收请求、但结果尚未持久化,系统可能无法确定发送结果,应标记为
UNKNOWN。 - 支持渠道幂等键时应传递
runId + itemId;不支持时明确接受极端故障下的重复风险。
12.3 Agent 与 Service 协议
远程 Agent 的长期控制协议固定为 HTTP/2 上的 gRPC 双向流,契约使用 Protobuf。连接承载 Hello、心跳、能力、Lease、命令、事件确认和终态;每个方向具有单调 Sequence,Lease 仍使用 Epoch 和 Fencing Token。
Enrollment 和凭据轮换使用 HTTPS REST;WebUI、Desktop UI 和远程 SDK 的 Run 实时事件使用 SSE;大体积 Artifact 通过短期签名 HTTPS URL 传输。正式远程 Agent 不发布长轮询过渡协议,也不默认使用 WebSocket。详见 ADR-0004。
13. Service 概要设计
13.1 分层
service-api:公开 DTO、API 契约和错误模型。service-domain:领域实体、状态机和仓储接口。service-application:用例编排、事务边界和权限检查。service-infrastructure:数据库、Secret、Artifact、Agent 通信等适配器。service-app:Web 框架、配置、启动和部署入口。
Service 基线固定为 Java 25 和 Spring Boot 4.1.x,初始实现版本为 4.1.1;数据库迁移使用显式版本化迁移工具,依赖版本由 BOM 统一管理。Spring 只存在于 Service App、Web 和 Infrastructure 层,不进入 Core、Provider SPI、Agent Runtime、Remote Java SDK 或 Embedded SDK。详见 ADR-0002。
13.2 主要领域对象
| 对象 | 说明 |
|---|---|
| ProviderDescriptor | 已安装 Provider、版本、能力和 Schema |
| Account | 渠道账号元数据及 Secret 引用 |
| MessageTemplate | 消息模板及版本 |
| Audience | 受众定义 |
| AudienceSnapshot | 一次物化后的不可变受众版本 |
| JobDefinition | 账号、消息、受众和执行策略的组合 |
| Schedule | Cron、时区、启停和错过执行策略 |
| Run | 一次执行实例和状态摘要 |
| RunSnapshot | Run 启动时冻结的完整执行配置 |
| Agent | 执行节点身份、能力和状态 |
| AgentLease | Agent 对 Run 的限时所有权 |
| RunEvent | 状态、进度、日志和控制事件 |
| Artifact | 输入快照、结果、日志等大对象引用 |
13.3 Run 状态机
stateDiagram-v2
[*] --> PENDING
PENDING --> LEASED
LEASED --> RUNNING
LEASED --> PENDING: 租约未确认
RUNNING --> PAUSED
PAUSED --> RUNNING
RUNNING --> CANCELLING
PAUSED --> CANCELLING
CANCELLING --> CANCELLED
RUNNING --> SUCCEEDED
RUNNING --> PARTIAL
RUNNING --> FAILED
RUNNING --> LOST: Agent 失联
LOST --> RECOVERING
RECOVERING --> PENDING: 允许重新执行
LOST --> FAILED: 无法安全恢复所有状态变更由应用服务执行并校验前置状态,Controller、UI 和 Agent 不能直接修改数据库状态字段。
13.4 调度
- Schedule 归 Service 管理,不属于 UI。
- 每次调度触发创建独立 Run 和 Run Snapshot。
- 明确时区和夏令时行为。
- 支持错过触发策略:跳过、立即补一次或按限制补偿。
- Standalone 由单 Service 调度;Server HA 由持有 PostgreSQL Session Advisory Lock 的实例运行 Schedule Scanner,连接失效即释放领导权。
14. 对外 API 设计
14.1 基本约定
- API 前缀:
/api/v1;Workspace 业务资源前缀为/api/v1/workspaces/{workspaceId}。 - OpenAPI 作为公开契约和 SDK 生成来源。
- 资源标识使用不透明 ID,建议 UUID 或 UUIDv7。
- 创建 Run 等非幂等操作支持
Idempotency-Key。 - 列表接口统一分页、排序和过滤。
- 错误响应使用统一 Problem Detail 模型,并包含稳定错误码和 Trace ID。
- Secret 字段只写不读,更新时支持“保持原值”和“替换”。
14.2 主要资源
/* Detailed source-code truncated for AI context efficiency. */14.3 实时事件
运行监控首期采用 Server-Sent Events。客户端可以携带事件游标或 Last-Event-ID 断线续传;历史事件仍可通过普通分页 API 查询。
命令通过 REST 提交,事件通过 SSE 下发。浏览器端不默认引入 WebSocket。
14.4 交互式 API 文档
Service 提供:
/openapi.json或等价 OpenAPI 描述地址。/docs交互式文档页面。- 当前登录身份的认证信息注入。
- 请求示例、响应示例和错误码说明。
- Try It Out 动态调用能力。
生产环境中,API 调试页面必须经过权限控制。危险操作需要二次确认,Secret 不得写入浏览器持久化存储或出现在生成的示例中。
15. Remote Java SDK 设计
15.1 远程 SDK
sdk-java 面向 Service API:
- 从 OpenAPI 生成底层模型和客户端。
- 在生成代码之上提供稳定的手写 Facade。
- 支持认证、超时、重试、分页和 SSE 订阅。
- 不依赖 Core Engine、Provider 或 Service 内部实体。
- SDK 的兼容性以公开 API 为准,而不是以 Service 内部版本为准。
15.2 Embedded SDK
embedded-java 面向本地进程内执行:
- 依赖 Core Engine 和用户选择的 Provider。
- 使用显式 Builder 注册 Provider、Secret Resolver、事件和结果适配器。
- 不自动启动 Service,不包含 WebUI 和 Service 数据库。
- 可以为测试、第三方 Java 应用嵌入和自定义执行环境提供能力。
16. WebUI 概要设计
主要功能区域:
- 总览:Service、Agent、Provider 和近期 Run 状态。
- 账号管理:Schema 动态表单、连接测试和 Secret 状态。
- 消息管理:可视化配置、模板变量、预览和 Dry Run。
- 受众管理:文件、数据库、第三方来源导入及快照管理。
- 任务与调度:任务组合、执行策略、Cron 和时区。
- 运行中心:进度、吞吐、成功率、失败分类、并发动态调整。
- 日志与制品:实时日志、结果下载、失败重试入口。
- Agent 管理:在线状态、版本、能力、负载和禁用。
- API 文档:动态接口说明和在线调试。
动态表单由 Provider Schema 驱动。JSON Schema 负责数据类型和校验,UI Schema 负责分组、顺序、控件提示和 WePush 扩展控件。
前端固定采用 TypeScript 6.0.x、Vite 8.1.x 和 React 19.2.x,以 Node.js 24 LTS、pnpm 和单一 Lockfile 管理工程。样式采用 Tailwind CSS;shadcn/ui 只在能降低复杂组件成本时按需引入,复制进仓库的组件源码由项目自行维护。WebUI 是纯 SPA,正式运行不依赖 Node.js 服务端。详见 ADR-0002。
17. Desktop UI 概要设计
Desktop UI 是 Service API 的薄客户端,不直接依赖 Core 或数据库。
目标能力:
- 连接本机 Standalone Service 或远程 Service。
- 首次运行引导和本地 Service 状态管理。
- 提供系统托盘、通知、自动启动和桌面更新体验。
- 尽量复用 WebUI 页面、Schema 渲染器和 API 客户端。
- 本地连接使用回环地址和短期引导凭据,避免无认证端口。
Desktop 外壳固定为 Electron 43.x,并复用 WebUI 的 React 页面、Schema Renderer、API Client 和设计 Token。Main、Preload、Renderer 严格分层;Renderer 禁用 Node.js 集成,启用 Context Isolation 和 Sandbox,只通过固定白名单 IPC 使用本机 Service 状态/启停/日志/诊断、签名插件生命周期和 Token safeStorage,不提供任意命令入口。浏览器 Token 只保存在标签页会话,Desktop 在系统安全后端不可用时拒绝弱持久化。该选择不改变 Desktop 只能通过 Service API 访问业务能力的边界。详见 ADR-0002。
18. 数据与存储设计
18.1 数据库
- Standalone 默认 SQLite。
- Server 模式固定使用 PostgreSQL 18.x;数据库自身复制、故障转移和备份由部署者选择并负责运维。
- 通过 Repository Port 隔离数据库实现,但不以支持任意数据库为目标。
- 所有 Schema 变化必须使用版本化迁移,不允许运行时拼接临时升级逻辑。
- 数据库事务以应用用例为边界,不共享全局 Session。
18.2 Artifact Store
Artifact Store 保存:
- Audience Snapshot 数据文件。
- 成功、失败、未发送结果。
- 可选 Provider 响应体。
- 大体积日志和导出文件。
Standalone 默认使用 LocalFileArtifactStore;Server 默认使用受控 S3-compatible API 的 S3ArtifactStore。Agent 使用 Service 签发的短期 Presigned URL 直传,数据库保存元数据、SHA-256、大小、状态和对象键。对象键按 Workspace 分区;保留期由 Service 清理任务执行,对象存储 Lifecycle 只作兜底。详见 ADR-0007。
18.3 Secret Store
- 默认实现为
LocalEnvelopeSecretStore,每个 Secret 使用独立随机 DEK 和 AES-256-GCM 加密,AAD 绑定 Workspace、Secret ID、类型和版本。 - 数据库保存密文、Nonce、加密后的 DEK 和主密钥版本;主密钥只来自独立受保护文件或显式外部注入。
- Standalone 首启可创建仅当前用户可读的主密钥文件;Server 不得静默生成新主密钥,缺失或权限不安全时 Fail Closed。
SecretStore保持 Port 以维持模块边界和可测试性;官方产品实现固定为本地信封加密,不规划 Vault、云 KMS、云 Secret Manager 或操作系统凭据存储适配器。ElectronsafeStorage不作为 Service Secret 实现。- Agent 只在执行期间获取最小范围 Secret,并在内存中短期持有。
密钥轮换默认只重包裹 DEK,不重写业务密文。详见 ADR-0003。
19. 安全设计
- Standalone 默认绑定
127.0.0.1或::1。 - 远程访问必须使用 TLS。
- 用户 API 支持管理员、操作员和只读角色。
- Agent 使用独立身份,不复用用户 Token。
- 关键操作记录审计事件,包括操作者、时间、对象和结果。
- 所有日志、事件、异常和 API 响应经过 Secret Redactor。
- 文件上传限制大小、类型和解析资源,防止压缩炸弹和超大内存占用。
- HTTP Provider 对目标地址提供可配置的 SSRF 防护策略。
- 机器人 Webhook 必须匹配厂商官方 HTTPS 域名与路径;短信和微信系生产 API 固定官方端点,测试回环端点不进入公开构造接口。
- API 调试功能遵循当前用户权限,不提供绕过权限的内部调用通道。
Workspace 逻辑隔离进入正式 Server 产品范围;Standalone 自动使用隐藏的 Default Workspace。Account、Secret、Message、Audience、Job、Schedule、Run、Artifact、Agent Pool 和 API Token 均必须归属 Workspace,所有 Repository、唯一索引、缓存键和授权检查显式携带 workspaceId。Workspace 只服务于同一用户自建实例内的团队和权限分组,不演进为公共 SaaS 租户;计费、自助注册和恶意租户物理隔离属于长期非目标。详见 ADR-0008。
20. 可观测性
Service 和 Agent 应提供:
- Health、Readiness 和版本信息。
- 结构化日志和统一 Trace ID、Run ID、Agent ID。
- Run 级吞吐、成功、失败、重试、限流和延迟指标。
- Agent 心跳延迟、可用容量和任务数。
- Provider 请求耗时和错误分类,但不记录敏感请求内容。
- 审计日志与普通运行日志分离。
监控接口默认不公开到非受信网络。
21. 交付与跨平台安装
21.1 Service 制品
- 通用可执行 JAR 或应用镜像。
- Linux:tar/deb/rpm,并提供 systemd 服务单元。
- Windows:zip/msi/exe,并通过 Windows Service Wrapper 或等价机制注册服务。
- macOS:pkg/dmg,并通过 launchd 注册服务或用户级 Agent。
- 可选 Docker/OCI 镜像作为 Linux Server 部署补充。
21.2 独立标识
Next 必须使用独立于 Classic 的:
- 应用名称和 Service 名称,例如
WePush Next、wepush-next。 - 默认配置目录和数据目录。
- 默认端口。
- 日志目录。
- 用户可控的版本检查标识和发布资产名称;不使用与 Classic 混合的更新元数据。
安装、升级和卸载不得读取、覆盖或删除 Classic 的文件。
22. 一致性与投递语义
WePush Next 对外声明 At Least Once 为基础投递语义。
当外部渠道已经接受请求,但 Agent 在持久化结果前崩溃时,系统无法可靠判断是否已发送。此类消息应进入 UNKNOWN,由用户或策略决定是否重试。只有 Provider 原生支持幂等键时,才可以提供更强的去重保证。
Run 的状态摘要、事件和结果制品允许短暂最终一致,但终态必须经过校验,确保计数与已持久化结果可追溯。
23. 测试策略
| 测试类型 | 重点 |
|---|---|
| 单元测试 | 状态机、模板、重试、限流、结果分类 |
| 架构测试 | 禁止依赖、包边界、Core 纯净性 |
| Provider 契约测试 | Schema、配置校验、错误分类、Dry Run |
| API 契约测试 | OpenAPI、错误模型、兼容性和 SDK 生成 |
| 集成测试 | SQLite、PostgreSQL 18、S3-compatible Artifact、Secret、SSE、Agent gRPC |
| 故障测试 | Agent 失联、Service 重启、租约到期、重复上报 |
| 性能测试 | 大受众流式处理、并发、内存上限和事件聚合 |
| E2E 测试 | WebUI/SDK 创建任务到结果下载完整链路 |
| 安装测试 | Linux、Windows、macOS 安装、启动、升级和卸载 |
发布流水线必须先完成测试和契约校验,再生成平台安装包。安装包构建不得通过跳过测试来代替独立的测试阶段。
24. Classic 数据迁移
迁移是可选辅助能力,不是两个产品线的运行时连接。
- 迁移工具只读 Classic 数据库和配置。
- 迁移前创建可恢复备份。
- 将 Classic 账号、消息、受众和任务转换为 Next 模型。
- Secret 在导入过程中重新加密。
- 不保证所有 Classic 配置都能无损转换,无法转换的内容生成报告。
- 不提供持续双向同步。
- Next 运行时不得直接访问 Classic 数据库。
25. 版本与兼容策略
- Next 在架构建设期使用独立的
0.x版本。 - 公开 API 使用路径主版本,例如
/api/v1。 - Remote Java SDK 与 API 兼容矩阵独立于 Service 内部模块版本。
- Agent 协议单独版本化,Service 应允许一个受控范围内的新旧 Agent 共存。
- Provider SPI 的不兼容变化必须提升 SPI 主版本。
- Run Snapshot 保存必要的 Provider 和 Schema 版本,便于结果追溯。
26. 首个纵向里程碑
第一阶段不追求创建所有 Provider 和所有 UI 页面,而是完成 HTTP Provider 的完整闭环:
- HTTP Provider 提供账号和消息 Schema。
- Core Engine 能流式执行、限流、取消并输出事件。
- Embedded Agent 能领取并执行 Run。
- Service 能管理账号、消息、受众、任务和 Run。
- Remote Java SDK 能创建任务、启动 Run 和订阅事件。
- WebUI 能动态配置 HTTP Provider、启动 Run 并查看实时结果。
- Standalone 安装包能在至少一个平台完成安装和重启恢复。
该纵向链路已完成;0.1.0-alpha.4 进一步交付 SMTP、三类群机器人、阿里云短信、微信公众号、小程序和企业微信应用消息,详见内置 Provider 指南。
27. 阶段规划
阶段 A:工程和契约基线
- 创建 Maven 聚合工程和模块边界。
- 定义 Core API、Provider SPI 和架构测试。
- 定义 OpenAPI、错误模型和 Provider Schema 约定。
阶段 B:单机运行闭环
- 实现 Core Engine、HTTP Provider 和 Embedded Agent。
- 实现 Service、SQLite、Artifact 和基础 Secret 存储。
- 完成 Remote Java SDK 和最小 WebUI。
阶段 C:产品化单机版本
- 完善可视化配置、任务调度、运行监控和 API 文档。
- 增加 Desktop UI 和三平台 Service 安装。
- 完成升级、备份和故障恢复。
阶段 D:服务器与分布式执行
- 增加 PostgreSQL、远程 Agent 和租约恢复。
- 增加 Agent 管理、RBAC、审计和远程 TLS。
- 落实 S3-compatible Artifact Store 和 PostgreSQL 控制面高可用。
阶段 E:Provider 扩展
- 按业务优先级迁移或重新实现消息类型。
- 为每个 Provider 建立契约测试和模拟服务。
- 完善 Schema 组件库和 Provider 开发文档。
截至 2026-08-23,阶段 A–D 的 0.1.0 架构基线已经实现并进入持续验证;阶段 E 是按业务优先级持续增加消息类型的长期产品迭代,不阻塞当前 Next 目标架构成立。实现证据见 实现状态,安装、HA、升级和恢复见 部署与运维。
28. 已接受的架构决策
- ADR-0001:Classic 与 Next 双轨独立发展
- ADR-0002:Service、WebUI 与 Desktop 技术基线
- ADR-0003:默认 Secret Store
- ADR-0004:Agent 长期通信协议
- ADR-0005:Provider 插件发现、隔离和更新
- ADR-0006:PostgreSQL Server 模式控制面高可用
- ADR-0007:Artifact Store 协议和保留策略
- ADR-0008:Workspace 多租户范围
这些决策是 Next 初始实现的正式基线。公共 SaaS、计费订阅、外部 Vault/云 KMS/Secret Manager、恶意租户物理隔离和跨地域控制面属于长期非目标,不得通过临时代码或普通功能 ADR 隐式引入。非受信 Provider 的进程级隔离只有在明确服务于用户自建安全场景时才可以单独评审,且不得改变产品定位。
29. 架构验收条件
以下条件用于判断首个架构闭环是否成立:
- Core 模块不存在 Swing、AWT、Spring、MyBatis 和具体数据库依赖。
- Core 可以在纯单元测试中使用内存适配器执行完整 Run。
- Provider 不通过数据库 ID 自行加载账号或消息配置。
- 同一个 HTTP Run 可以由 Embedded Agent 和 Remote Agent 使用相同 Core 行为执行。
- WebUI、Desktop UI 和 Remote Java SDK 通过同一公开 API 管理任务。
- Service 重启后能够恢复非终态 Run,并明确区分可重试和结果未知状态。
- Agent 失联不会导致 Service 无限等待,也不会未经判断立即重复发送。
- 大受众执行不要求将全部 Recipient 和结果加载到内存。
- Secret 不出现在查询响应、日志、事件和结果制品中。
- OpenAPI 能生成 Remote Java SDK,并通过 API 契约测试。
- Classic 与 Next 能在同一台机器上同时安装和运行,配置和数据互不影响。
30. 文档维护
本文档描述当前目标架构,不替代详细设计、API 契约和 ADR。
- 会改变跨组件边界的决策必须新增或更新 ADR。
- 单个模块内部的详细设计应放在对应模块的
docs/或 README 中。 - OpenAPI、JSON Schema 和数据库迁移文件是可执行契约,应与实现一同评审。
- 当实现与本文档不一致时,应先确认是实现偏离还是架构演进,再更新代码或文档,不能长期保持隐式差异。
Next Docs Compatibility Policy (`next/docs/compatibility-policy.md`)
WePush Next 兼容性策略
- 适用版本:
1.0.0及后续1.x(当前稳定版1.2.0) - 首个稳定兼容基线:
1.0.0 - 最低直接升级版本:
0.1.0-beta.1 1.0.0最低可回滚版本:0.1.0-beta.11.1.0推荐回滚版本:1.0.0(必须使用升级前完整备份)1.2.0推荐回滚版本:1.1.0(必须使用升级前完整备份)
1. 版本规则
WePush Next 从 1.0.0 起使用语义化版本:
- Patch(
1.0.x)只包含兼容的修复、安全更新和文档调整。 - Minor(
1.x.0)可以增加可选 API、配置和 Provider 能力,但不能破坏已有用法。 - Major(
2.0.0)才允许移除或改变已经公开的兼容契约,并必须提供迁移说明。 - 带连字符的版本仍是预览版;不带连字符的版本是稳定版。
2. REST、SSE 与 OpenAPI
/api/v1 和发行包中的 OpenAPI 3.1 文档是 1.x 公共 HTTP 契约。1.x 内承诺:
- 不删除已有路径、HTTP 方法、响应状态或必需响应字段。
- 不新增已有请求必须提供的字段,不收窄字段类型、长度或合法取值。
- 可以增加可选请求字段、响应字段、端点和错误码;客户端应忽略未知响应字段。
- 已发布字段或端点至少经过一个 Minor 版本的弃用期后,才可在下一个 Major 移除。
- SSE 事件保持事件 ID 可恢复、事件类型可识别;可以增加新的事件类型和 Payload 字段。
1.0.0 发行门禁对 0.1.0-beta.1 OpenAPI 做忽略版本号后的精确摘要校验,因此 Beta 用户既有 HTTP 调用在稳定版中没有契约漂移。1.1.0 只增加 Workspace Policy、系统运维、认证熔断和 Agent Multipart 等端点与可选字段;已有路径继续由兼容性摘要门禁保护。1.2.0 不新增 REST/SSE 路径或必填字段,AI 助手通过既有 API 接入。
3. Java SDK 与 Provider SPI
- Remote Java SDK 与 Embedded Java SDK 的公开类型在
1.x内保持源码兼容;新增能力优先使用新方法、Builder 或新类型。 - SDK 与 Service 必须使用相同 Major;Service 接受同 Major 的旧 Minor SDK 请求。
- Provider SPI 以
spiMajorVersion=1为稳定边界。1.xAgent 可加载 SPI Major 1 且通过清单、摘要和 Ed25519 签名校验的插件。 - Provider ID 与实现版本属于 Run Snapshot。升级不会悄悄把既有 Run 改到另一个 Provider 实现版本。
- Provider 行为发生不可兼容变化时必须发布新的实现版本,不能覆盖旧 Snapshot 语义。
4. 配置兼容
0.1.0-beta.1已公开的WEPUSH_*配置键在整个1.x保持可解析;发行门禁逐项验证这些键仍存在。- Workspace Artifact 保留策略在首次显式更新前继续采用既有
WEPUSH_EXPORT_RETENTION与WEPUSH_AGENT_ARTIFACT_RETENTION的分类默认值;管理员保存 Workspace Policy 后,由该 Workspace 的统一保留期接管新 Artifact。 - 新配置必须有安全默认值。升级不能默认启用外部网络访问、遥测、公开监听或弱认证。
- 配置弃用必须记录替代键,并在至少一个 Minor 周期内继续接受旧键;非法或冲突配置启动时失败关闭。
- Service/Agent 配置文件由用户控制,安装和升级脚本不能覆盖已存在文件。
5. 数据库迁移与回滚
- Flyway 迁移只向前执行,已经发布的迁移文件不可修改、重排或复用版本号。
- SQLite 与 PostgreSQL 使用相同的有序迁移历史,并在 CI 中分别验证。
0.1.0-beta.1的数据库版本为 V13;1.0.0增加只新增元数据表的 V14,不删除或改写用户数据。1.0.0可以直接从0.1.0-beta.1升级。自动化测试会先创建 V13 数据,再迁移到 V14 并核对原数据。- V14 是附加迁移,因此
1.0.0最低可回滚到0.1.0-beta.1。仍建议使用升级前自动备份回滚,以同时恢复数据库、Master Key、Artifact 和 Agent 状态。 1.1.0继续增加附加迁移:V15workspace_policy、V16account_auth_circuit、V17artifact_multipart_upload。这些迁移不删除、重命名或改写 V14 及以前的业务数据;SQLite 与 PostgreSQL 都从 V13 带用户数据迁移到 V17 做自动验证。1.0.0不会读取 V15–V17 新表,但1.1.0人工回滚仍必须恢复升级前完整备份,避免新策略、熔断、上传会话及对象存储状态遗留。不能把“旧程序暂时可启动”等同于受支持的数据回滚。1.2.0不新增 Flyway 迁移,数据库版本保持 V17。它仍应通过升级前完整备份回滚到1.1.0,以便同时恢复程序、配置和数据。- 未来版本一旦包含旧版本无法读取的迁移,Release Notes 必须提高最低可回滚版本,并明确只能通过备份恢复。
6. Agent 协议
- Agent Hello 声明协议 Major/Minor;
1.xService 与 Agent 使用协议 Major 1。 - 同 Major 内允许新增可忽略字段和帧类型,不改变 Lease/Fence、Sequence、ACK 和结果未知语义。
- 建议 Service 与 Agent 使用相同版本;滚动升级期间支持当前稳定版与上一稳定 Minor 的短期混合运行。
- Agent 失联不会直接触发无条件重发。Lease 到期后进入恢复流程;外部发送结果无法确认时记录
UNKNOWN。 1.1.0在 Major 1 内增加 Multipart Plan/Part/Complete/Abort 交互;单次 Presigned Put 继续兼容。超过 1 GiB 的 Agent Artifact 只有在 Service 和 Agent 都升级到1.1.0后才可用。1.2.0不改变 Agent 协议。Service 与 Agent 可在1.1.0和1.2.0之间短期混合运行;运营商插件的实现版本随发行包更新为1.2.0,新 Run 应使用同一 Release 的签名插件。- 精简包和 Java SDK 从
1.2.0起要求 Java 25+。完整安装包携带 Java 25 运行时,不要求宿主机另行安装 JDK。这是运行基线调整,不改变已发布的 HTTP 契约。
7. 不属于兼容承诺的内容
- 内部 Java 包、数据库表的直接访问、未记录环境变量和测试端点不是公共接口。
- 第三方消息平台自身的接口变更不受 WePush 控制;Provider 会按错误分类和版本规则处理。
0.x早期 Alpha 版本不提供直升1.0.0的保证,必须先升级到0.1.0-beta.1或导出后重建。- WePush 不提供公共 SaaS 或官方托管控制面,因此不存在云端租户、计费或订阅数据的兼容承诺。
Next Docs Deployment And Operations (`next/docs/deployment-and-operations.md`)
WePush Next 部署与运维
本文是 1.2.0 稳定自部署版的可执行部署说明。Standalone 使用 SQLite 和本地 Artifact;用户自建 Server 使用 PostgreSQL、S3-compatible Artifact、两个以上 Service 实例以及外部负载均衡器。Classic 不参与 Next 的构建、安装或运行。WePush 不提供官方托管控制面,所有部署、数据、密钥和备份均由用户掌控;产品边界见《产品目标、边界与路线图》。
Service 的无认证开发模式仅允许绑定回环地址。任何非回环 HTTP 监听都必须启用 API Security;server 模式还会强制 PostgreSQL、S3-compatible Artifact Store 和 Agent gRPC TLS,缺少任一项均启动失败。
1. 构建与发行物
要求 JDK 25+、Node.js 24 和 pnpm 11.22.0:
cd next/ui
pnpm install --frozen-lockfile
pnpm check
cd ..
./scripts/prepare-winsw.sh
./mvnw verify
./mvnw -DskipTests package测试必须先独立通过,-DskipTests 只用于之后的发行打包阶段。输出位于:
distribution/target/wepush-next-1.2.0.tar.gzdistribution/target/wepush-next-1.2.0.zip- 对应
.sha256文件
上面两个归档是使用系统 Java 25+ 的精简包。Release 流水线还在 Linux、macOS、Windows Runner 上用 JDK 25 jlink 生成对应架构的完整包,目录内多出 runtime/;两个变体都携带固定摘要的 WinSW 2.12.0,因此 Windows 用户安装时不联网。prepare-winsw.sh 只在源码发行构建阶段从上游下载并验证固定长度与 SHA-256。
归档内包含 Service/Agent Fat JAR、生产 WebUI、统一/分组件安装脚本、正式 Backup/Restore/Upgrade、配置模板和 Provider 插件生命周期工具。Desktop 原生目录包在当前操作系统执行:
cd next/ui
pnpm --filter @wepush-next/desktop packageDesktop 打包器使用锁定版本的 Electron,不引入带 Git 构建依赖的额外打包链;缺少 Electron Runtime 时会运行该锁定版本随附的安装脚本。macOS、Windows、Linux 必须分别在目标操作系统打包。
pnpm 已显式信任固定版本 Electron 的原生运行时下载脚本;无法访问 GitHub Release Asset 的网络可在安装依赖时设置受信 ELECTRON_MIRROR。按项目明确边界,1.2.0 不使用商业签名:macOS 只做 ad-hoc 签名且不提交 Apple Notarization,Windows 不做 Authenticode 签名。系统可能显示未知开发者警告,请只从项目 GitHub Releases 下载并验证 SHA256SUMS;详见 UNSIGNED-NOTICE.md。
2. Linux Standalone
解压、校验并安装:
sha256sum wepush-next-1.2.0-linux-x64.tar.gz
tar -xzf wepush-next-1.2.0-linux-x64.tar.gz
sudo ./wepush-next-1.2.0/install/install.sh安装布局:
- 版本目录:
/opt/wepush-next/releases/<version> - 原子当前链接:
/opt/wepush-next/current - 配置:
/etc/wepush-next/service.env、agent.env - 数据:
/var/lib/wepush-next/service、agent - systemd:默认安装
wepush-next-service;高级all模式另安装wepush-next-agent
环境文件由 root 持有,只向相应服务组开放读取。修改配置后执行:
sudo systemctl restart wepush-next-service
curl --fail http://127.0.0.1:18990/actuator/health/installation备份会停止两个进程以获得 SQLite、Journal、Outbox 和配置的一致快照,并恢复原先处于运行状态的服务:
sudo /opt/wepush-next/current/install/linux/backup.sh
sudo /opt/wepush-next/current/install/linux/restore.sh --validate-only "$BACKUP_FILE"
sudo /opt/wepush-next/current/install/linux/restore.sh "$BACKUP_FILE" "$BACKUP_SHA256"
sudo /opt/wepush-next/current/install/linux/upgrade.sh release.tar.gz <sha256>
sudo /opt/wepush-next/current/install/linux/uninstall.sh
sudo /opt/wepush-next/current/install/linux/uninstall.sh --purge # 明确删除配置与数据3. macOS Standalone
sudo ./wepush-next-1.2.0/install/install.sh
launchctl print system/com.fangxuele.wepush-next.service
curl --fail http://127.0.0.1:18990/actuator/health/installation版本、数据、配置和日志分别位于 /Library/WePushNext、/Library/Preferences/wepush-next 与 /Library/Logs/WePushNext。安装器用 LaunchDaemon 托管 Service/Agent,并拒绝覆盖非符号链接的 current 路径。
LaunchDaemon 默认以执行 sudo 的非 root 用户运行;无人值守安装必须显式设置已存在的 WEPUSH_SERVICE_USER,安装器拒绝回退到 root。
sudo /Library/WePushNext/current/install/macos/backup.sh
sudo /Library/WePushNext/current/install/macos/restore.sh --validate-only "$BACKUP_FILE"
sudo /Library/WePushNext/current/install/macos/restore.sh "$BACKUP_FILE" "$BACKUP_SHA256"
sudo /Library/WePushNext/current/install/macos/upgrade.sh release.zip <sha256>
sudo /Library/WePushNext/current/install/macos/uninstall.sh4. Windows Standalone
以管理员 PowerShell 执行:
Expand-Archive .\wepush-next-1.2.0-windows-x64.zip .\release
Set-ExecutionPolicy -Scope Process Bypass
& .\release\wepush-next-1.2.0\install\install.ps1
Get-Service WePushNextService
Invoke-WebRequest http://127.0.0.1:18990/actuator/health/installation安装器只使用发行包内的 WinSW 2.12.0,并在安装前再次校验固定长度和 SHA-256;缺失或被修改时失败关闭,不进行在线下载。Service 以低权限 LocalService 运行。版本目录位于 %ProgramFiles%\WePush Next,配置和数据位于 %ProgramData%\WePush Next;该目录移除继承 ACL,只允许 LocalService、SYSTEM 和 Administrators。备份默认写到独立的 %ProgramData%\WePush Next Backups。
& "$env:ProgramFiles\WePush Next\current\install\windows\backup.ps1"
& "$env:ProgramFiles\WePush Next\current\install\windows\restore.ps1" -ValidateOnly -Archive '<backup>.zip'
& "$env:ProgramFiles\WePush Next\current\install\windows\restore.ps1" -Archive '<backup>.zip' -ExpectedSha256 '<sha256>'
& "$env:ProgramFiles\WePush Next\current\install\windows\upgrade.ps1" -Archive release.zip -ExpectedSha256 <sha256>
& "$env:ProgramFiles\WePush Next\current\install\windows\uninstall.ps1"只有显式 -Purge 才删除持久数据。
5. Agent Enrollment 与远程运行
Server 管理员在 WebUI 设置页或 API 创建一次性 Enrollment Token,并绑定 Workspace。Agent 第一次启动设置:
WEPUSH_AGENT_ID=agent-east-1
WEPUSH_AGENT_ENROLLMENT_TOKEN=<one-time-token>
WEPUSH_SERVICE_BASE_URL=https://wepush.example.com
WEPUSH_SERVICE_HOST=wepush.example.com
WEPUSH_AGENT_GRPC_PORT=19090
WEPUSH_AGENT_GRPC_PLAINTEXT=falseAgent 生成 P-256 私钥,在 HTTPS Enrollment 后保存长期 Credential、客户端证书与 CA;证书或 Credential 距到期不足 14 天时自动轮换。身份文件、私钥、Event Outbox、Completion Outbox 和 Lease Journal 均必须位于持久卷。非回环部署不允许匿名 Agent;gRPC 必须启用 TLS,生产基线要求 mTLS。
6. Provider 插件
HTTP、SMTP Email、飞书/钉钉/企微机器人、阿里云短信、微信公众号、小程序和企业微信应用消息是发行包内置 Provider,不需要放入插件目录。内置渠道的账号、SecretRef、最小消息和验证步骤见《内置 Provider 指南》。
CMPP、SMGP、SGIP 和 SMPP 从 1.1.0 起作为独立 Release Asset 交付,文件名为 wepush-provider--1.2.0.zip 及对应 .sha256。官方签名 Key ID/公钥在同一 Release 的 wepush-provider-trusted-key-1.2.0.env 中,并带独立 .sha256;它们也全部列入统一 SHA256SUMS。插件不会随核心发行包自动启用。先从同一 GitHub Release 下载并同时校验这些文件,再执行 Stage/Activate;不要信任插件 ZIP 内或第三方页面提供的替代公钥。能力边界、源码构建与发布签名说明见 plugins/README.md。
正式模式必须设置 WEPUSH_PLUGIN_TRUSTED_KEYS,格式是 keyId:Base64Ed25519PublicKey,多个发布者用逗号分隔。插件 ZIP 包内 plugin.json 的 SHA-256 清单与 signature.ed25519 必须通过验证;ZIP Slip、共享 API 重复打包、未知签名者和 SPI 不兼容都会导致 Agent 失败关闭。
Linux 应以 Agent 服务账号执行 Stage/Activate,确保文件所有权正确:
sudo -u wepush-agent /opt/wepush-next/current/plugins/stage.sh provider.zip
sudo -u wepush-agent /opt/wepush-next/current/plugins/activate.sh <pluginId>.zip
sudo -u wepush-agent /opt/wepush-next/current/plugins/rollback.sh <pluginId>.zipStage 先调用 Agent 的生产校验器读取 plugin.json,验证摘要、SPI 和 Ed25519 签名,再以 .zip 作为稳定文件名落盘。激活采用版本备份、Supervisor 重启和健康观察;新插件导致 Agent 退出时自动恢复旧包并再次启动。Desktop 的 Providers 页提供相同的本地选择、校验、Stage、Activate 和 Rollback 操作,并通过系统授权写入插件目录。该机制是受控滚动更新,不在 JVM 内热卸载 ClassLoader。
7. Server/HA 容器拓扑
deployment/container/compose.server.yaml 是本地/验收拓扑:PostgreSQL 18、MinIO、两个无状态 Service 和 HAProxy。HTTP API 通过 127.0.0.1:18990 暴露;Agent gRPC 通过 TCP 透传的 19090 暴露,因此 mTLS 端到端保持在 Agent 与 Service 之间。
首次运行:
cd next/deployment/container
./generate-dev-secrets.sh
docker compose --env-file .env.server.local -f compose.server.yaml up --build -d
curl --fail http://127.0.0.1:18990/actuator/health/readiness
curl --fail http://127.0.0.1:18990/actuator/prometheus开发脚本生成的 CA/密钥和 .env.server.local 已被 .gitignore 排除,且拒绝覆盖已有文件。两个 Service 共享:
- PostgreSQL 业务状态、Lease、Service→Agent Outbox、Schedule 与审计事实源;
- Agent CA、API Bootstrap Token、Secret 主密钥和 Artifact 签名密钥;
- MinIO Artifact 对象。
Schedule Scanner 使用 PostgreSQL Advisory Lock 单 Leader;Agent 命令和 Lease Offer 使用持久 outbox,只有持有当前 gRPC 流的实例发送;SSE 以数据库事件日志和周期轮询跨实例补偿。
1.1.0 另对 wepush_run_pending、wepush_agent_outbox、wepush_run_event 三个 PostgreSQL Channel 执行 LISTEN/NOTIFY,只用于缩短调度、Agent 命令和 SSE 的唤醒延迟。连接断开、通知丢失或重复不得影响正确性,周期扫描、持久 Outbox 与事件游标必须始终保持启用。
除 Compose 验收拓扑外,deployment/templates/ 提供可复制的 Nginx、Traefik 和 Kubernetes 用户自建模板。模板只描述网络入口、Service 与配置挂载,不创建受项目托管的数据库、对象存储或密钥服务;部署者必须根据自己的域名、证书、StorageClass、Secret 管理和备份方案完成替换。
该 Compose 为本地协议验收拓扑,明确使用 WEPUSH_S3_SERVER_SIDE_ENCRYPTION=NONE。正式自建环境应配置对象存储原生 AES256,或由部署者在其存储层保证等价的静态加密;WePush 不对接或管理云 KMS。正式环境还必须:
- 使用由部署者负责的 PostgreSQL HA 和备份恢复;
- 在 HTTP API 前终止受信 TLS,保留 gRPC TLS 透传或等价的受控 mTLS 方案;
- 将 Master Key、Agent CA 和其他密钥放入权限受控的只读挂载文件,不使用提交到源码或共享目录的 Compose env 文件;
- 配置 S3 Lifecycle 作为未完成 Multipart 与误删保护的兜底;
- 至少两个 Service 跨故障域部署并配置 Readiness/Prometheus 告警。
停止本地环境:
docker compose --env-file .env.server.local -f compose.server.yaml down只有确认不再需要测试数据时才执行带 --volumes 的删除。
8. `1.1.0` 资源、诊断与 Artifact 运维
- 系统管理员在 WebUI 设置页或
/api/v1/workspaces/{workspaceId}/policy管理 Agent 数、活动 Run、总发送并发、Artifact 容量和默认保留期。首次显式保存策略前,新 Artifact 继续采用已有导出/Agent 分类保留配置;保存后统一使用 Workspace 保留期。调整上限不会删除既有资源;当前使用量超过新上限时拒绝新增操作,直到使用量回落。 /api/v1/system/diagnostics只允许系统管理员手动生成 ZIP;包内配置、日志摘要和运行状态会移除 Token、Secret、完整 Recipient 与 Provider 敏感响应。诊断包仍应按敏感运维数据保存和传输。/api/v1/system/version-check只在用户点击或显式调用时访问 GitHub Releases,筛选稳定的next-v*版本。它没有后台调度、遥测或自动下载/安装行为;完全离线环境无需配置例外。- 账号认证熔断可在 Account 对应管理页检查或复位。先确认渠道凭据已经修正;直接复位但不修正根因会再次熔断。
- Agent Presigned Multipart 会话记录在 V17。数据库清理会 Abort 失败/过期会话;S3-compatible Bucket 还必须配置 24 小时终止未完成 Multipart 的 Lifecycle 兜底。不要只删除数据库行而保留对象存储上传会话。
9. 升级、恢复与验收
Standalone 升级顺序:一致性备份 → 校验发行 SHA-256 → 展开新版本目录 → 原子切换 current → 重启 → Installation Health(Readiness、Flyway 当前版本、内置 Provider 本地 Dry Run)→ 成功保留备份。任一步失败都会切回旧 current,用刚生成的备份恢复配置和数据,再验证旧版本;命令以失败状态退出并保留恢复目录供人工审计。Server 滚动升级先执行单实例 Migration Job,再逐个替换 Service,最后滚动 Drain/Restart Agent。
Backup Archive 包含 BACKUP-MANIFEST、逐文件 SHA256SUMS 和配置/数据 Payload。Restore 在展开前拒绝路径穿越,并要求实际 Payload 文件集合与摘要清单完全一致;替换前保存 pre-restore-*,恢复后执行相同 Installation Health,失败时恢复原目录。不要分别恢复数据库和 Master Key。
最低验收项:
- Readiness 和 Prometheus 可访问。
- WebUI 能保存 Account/Message/Audience/Job 并启动 Dry Run。
- SSE 能重连并从
Last-Event-ID回放。 - Agent Enrollment、Hello/Welcome、Lease、Event Ack、Artifact Commit 和 Run Completion 成功。
- 停掉一个 Service 后 API、Schedule、SSE 和 Agent 重连仍可恢复。
- 备份恢复后 SQLite/PostgreSQL、Artifact、Agent Journal/Outbox 与 Secret 主密钥一致。
- Workspace 配额拒绝与审计一致,认证熔断可观察/复位,诊断包抽查不存在 Token、Secret、完整 Recipient 或未脱敏 Provider 响应。
- S3 Presigned Multipart 可完成和中止;模拟丢失 PostgreSQL 通知后,轮询仍可推进 Run、Agent Outbox 与 SSE。
Next Docs Detailed Design (`next/docs/detailed-design.md`)
WePush Next 详细设计
- 文档状态:已接受基线,随实现演进
- 文档版本:0.3
- 日期:2026-08-27
- 适用范围:
next/ - 上位文档:WePush Next 架构与概要设计
- 关联决策:见 ADR 索引
- 产品范围:产品目标、边界与路线图
1. 文档目的
本文档在架构与概要设计基础上,进一步定义 WePush Next 各组件的模块、接口、状态、数据、协议、异常、并发和部署细节,使开发人员可以据此拆分任务并开始实现。
文中的 Java 接口和 JSON 结构用于表达设计契约,正式实现时可以调整语法和类型名称,但不得未经评审改变组件职责、依赖方向和对外协议语义。
2. 已确定的设计基线
| 项目 | 首期设计基线 |
|---|---|
| Java | Java 25 |
| 构建 | next/pom.xml 独立 Maven 聚合工程 |
| Service | Java 25、Spring Boot 4.1.x,初始版本 4.1.1 |
| API | HTTPS REST;Workspace 资源前缀 /api/v1/workspaces/{workspaceId} |
| 实时事件 | Server-Sent Events |
| API 契约 | Contract First OpenAPI 3.1 |
| Provider 配置 | JSON Schema 2020-12 + UI Schema |
| WebUI | TypeScript 6.0.x、Vite 8.1.x、React 19.2.x、Tailwind CSS、按需 shadcn/ui |
| Desktop | Electron 43.x,复用 WebUI Renderer |
| Agent 协议 | HTTP/2 + Protobuf + gRPC 双向流;Enrollment 使用 HTTPS REST |
| Provider 插件 | PF4J 3.15.x、签名包、ClassLoader 隔离、滚动重启更新 |
| Standalone 数据库 | SQLite |
| Server 数据库 | PostgreSQL 18.x;至少两个无状态 Service 实例构成正式 HA |
| Secret | LocalEnvelopeSecretStore,AES-256-GCM 信封加密 |
| 大对象 | Standalone 本地文件;Server 使用 S3-compatible Artifact Store |
| 租户边界 | Workspace 逻辑多租户进入正式 Server 范围 |
| 投递语义 | At Least Once;支持时使用 Provider 幂等键 |
| 单机执行 | Service 内嵌 Agent |
| 分布式执行 | 远程 Agent 主动连接 Service |
上述基线分别由 ADR-0002 至 ADR-0008 固化。WePush Next 只面向用户自建和自运维场景;公共 SaaS、计费订阅、外部 Vault/云 KMS/Secret Manager、恶意公共租户物理隔离和跨区域控制面属于长期非目标。
3. 命名与代码约定
3.1 Maven 坐标
建议使用以下坐标:
groupId: com.fangxuele.wepush.next
artifactId: wepush-next-<module>Next 不复用 Classic 的包名,防止类路径混淆和误依赖。
3.2 Java 包名
com.fangxuele.wepush.next.core.api
com.fangxuele.wepush.next.core.engine
com.fangxuele.wepush.next.provider.spi
com.fangxuele.wepush.next.provider.http
com.fangxuele.wepush.next.agent.protocol
com.fangxuele.wepush.next.agent.runtime
com.fangxuele.wepush.next.service.api
com.fangxuele.wepush.next.service.domain
com.fangxuele.wepush.next.service.application
com.fangxuele.wepush.next.service.infrastructure
com.fangxuele.wepush.next.sdk
com.fangxuele.wepush.next.embedded3.3 领域术语
| 术语 | 含义 |
|---|---|
| Provider | 一个具体消息渠道能力实现 |
| Account | Provider 账号配置及 Secret 引用 |
| Message | 可版本化的消息模板 |
| Audience | 受众定义 |
| Audience Snapshot | 不可变、可执行的受众快照 |
| Job | Account、Message、Audience 和执行策略的组合 |
| Schedule | Job 的定时触发规则 |
| Run | Job 的一次执行实例 |
| Run Snapshot | Run 启动时冻结的全部执行配置 |
| Recipient | 一条目标数据及模板变量 |
| Item | Run 中一条 Recipient 的执行单元 |
| Artifact | 输入、日志、结果等大体积制品 |
| Lease | Agent 在限定时间内对 Run 的执行权 |
4. 代码模块和职责
4.1 Core 模块
core/core-api
ID、值对象、RunExecutionSpec、RunEvent、RunSummary、执行策略
core/provider-spi
ProviderFactory、ProviderSession、配置 Schema、错误分类
core/engine
DefaultExecutionEngine、调度循环、并发、限流、重试、事件聚合4.2 Agent 模块
agent/agent-protocol
Agent 与 Service 的版本化 DTO,不直接复用 Service 数据库实体
agent/agent-runtime
Agent 状态机、Lease 管理、Core 调用、事件缓冲、命令执行
agent/agent-app
远程 Agent 启动入口、Provider 组合、配置和操作系统集成4.3 Service 模块
service/service-api
OpenAPI 文件、生成的 Server 接口、公开 DTO、错误码
service/service-domain
领域对象、状态机、Repository Port、领域规则
service/service-application
用例、权限、事务编排、DTO 映射
service/service-infrastructure
JDBC、Artifact、Secret、Agent 协议、系统时间等适配器
service/service-app
Spring Boot、Controller、Security、配置和内嵌 Agent 组合4.4 组合原则
agent-runtime只依赖 Provider SPI,不编译依赖某个具体 Provider。agent-app和service-app是具体 Provider 的组合入口。embedded-java只提供 Engine 装配能力,业务应用自行添加 Provider 依赖。- Service 的公开 DTO、Agent 协议 DTO、Core 对象和数据库记录是四类不同模型,通过 Mapper 显式转换。
- 禁止为了减少转换而让数据库记录穿透到 Controller、Agent 或 Core。
5. 标识、时间和版本
5.1 标识
- 对外资源 ID 使用不透明字符串,首期建议 UUIDv7。
- 数据库主键与 API ID 使用同一逻辑 ID,避免暴露自增序号。
- Recipient Item ID 由
runId + audienceSnapshotId + sequence稳定生成。 - Agent、Lease、Event 和 Artifact 都使用独立 ID。
5.2 时间
- Java 内部使用
Instant表示绝对时间。 - API 使用 UTC ISO-8601,例如
2026-08-22T08:30:00Z。 - Schedule 单独保存 IANA 时区,例如
Asia/Shanghai。 - 数据库统一保存 UTC;仅在 API 和 UI 边界转换时区。
- 所有超时使用
Duration,禁止使用无单位的裸整数。
5.3 版本
- Provider 具有实现版本和 SPI 主版本。
- Schema 具有独立
schemaVersion。 - Agent 协议通过 URL 和消息字段双重版本化。
- Run Snapshot 保存 Provider、Schema、模板和 Audience Snapshot 版本。
6. Core API 数据模型
6.1 配置文档
Core 不依赖 Jackson JsonNode。跨 Core 边界的 Provider 配置使用不可变文档:
public record ConfigDocument(
String schemaId,
String schemaVersion,
String mediaType,
byte[] canonicalContent
) {
public ConfigDocument {
canonicalContent = canonicalContent.clone();
}
@Override
public byte[] canonicalContent() {
return canonicalContent.clone();
}
}默认 mediaType 为 application/json。Provider 在自己的模块中解析为强类型配置。
6.2 Secret 引用
public record SecretRef(String namespace, String name, String version) {}
public interface SecretResolver {
SecretValue resolve(SecretRef ref);
}
public interface SecretValue extends AutoCloseable {
char[] copyChars();
byte[] copyBytes();
@Override void close();
}SecretValue 的 toString() 必须返回固定掩码,关闭时尽力清理内部缓冲区。
6.3 Recipient
public record RecipientRecord(
String itemId,
long sequence,
Map<String, RecipientValue> fields
) {}
public sealed interface RecipientValue
permits TextValue, NumberValue, BooleanValue, NullValue, BinaryRefValue {}字段名区分大小写;Provider Schema 必须声明必填字段和字段用途。
6.4 RunExecutionSpec
public record RunExecutionSpec(
String runId,
ProviderRef provider,
ConfigDocument accountConfig,
ConfigDocument messageConfig,
ExecutionPolicies policies,
Map<String, String> attributes,
boolean dryRun,
Instant createdAt
) {}Recipient Source、Secret、Artifact 和 Event Sink 是运行端口,不直接序列化进 RunExecutionSpec。
6.5 执行策略
public record ExecutionPolicies(
ConcurrencyPolicy concurrency,
RateLimitPolicy rateLimit,
RetryPolicy retry,
TimeoutPolicy timeout,
ResultPolicy result
) {}- 并发范围必须有最小值和最大值。
- 动态调整只能落在允许范围内。
- Retry 的总耗时不得超过 Run 或 Item Timeout。
- Result Policy 决定是否保存响应摘要或完整响应 Artifact。
6.6 Run 状态
public enum RunState {
PENDING,
LEASED,
RUNNING,
PAUSED,
CANCELLING,
CANCELLED,
SUCCEEDED,
PARTIAL,
FAILED,
LOST,
RECOVERING
}| 状态 | 含义 |
|---|---|
PENDING |
已创建,等待可用 Agent |
LEASED |
Service 已授予 Lease,等待 Agent 确认或启动 |
RUNNING |
Agent 和 Core 正在执行 |
PAUSED |
暂停派发新 Item,保留执行上下文 |
CANCELLING |
已接受取消,正在结束在途请求和 Flush 结果 |
CANCELLED |
取消处理完成 |
SUCCEEDED |
全部 Item 成功或按策略跳过 |
PARTIAL |
存在明确失败、未知或未发送 Item,但 Run 正常完成收尾 |
FAILED |
Run 级错误导致无法继续或无法正确完成收尾 |
LOST |
Agent 失联,当前执行结果无法立即确认 |
RECOVERING |
Service 正在判断是否恢复、重新领取或终止 |
Service 管理完整状态机;Core 主要处理 RUNNING、PAUSED、CANCELLING 和执行终态。Core 不自行把 LOST 转为 RECOVERING。
7. Core Engine 接口
7.1 外部接口
public interface ExecutionEngine {
RunHandle start(RunExecutionSpec spec, ExecutionPorts ports);
}
public record ExecutionPorts(
RecipientSource recipientSource,
SecretResolver secretResolver,
ResultSink resultSink,
ArtifactSink artifactSink,
RunEventSink eventSink,
ExecutionClock clock
) {}
public interface RunHandle {
String runId();
RunState state();
CommandResult submit(RunCommand command);
CompletionStage<RunSummary> completion();
}start() 在完成参数校验和资源预留后返回。完整执行结果通过 completion() 获取。
7.2 命令模型
public sealed interface RunCommand
permits PauseRun, ResumeRun, CancelRun, ChangeConcurrency {}
public record PauseRun(String commandId) implements RunCommand {}
public record ResumeRun(String commandId) implements RunCommand {}
public record CancelRun(String commandId, String reason) implements RunCommand {}
public record ChangeConcurrency(String commandId, int target) implements RunCommand {}命令按 commandId 幂等。同一个命令重复提交返回原处理结果。
7.3 Engine 内部组件
DefaultExecutionEngine
├── RunCoordinator
├── RecipientDispatcher
├── ConcurrencyGate
├── RateLimiter
├── RetryExecutor
├── ProviderSessionManager
├── ProgressAggregator
├── ResultBatcher
├── EventDispatcher
└── ResourceScope每个 Run 创建独立 RunCoordinator,Engine 自身不保存跨 Run 的可变业务状态。
8. Core 执行生命周期
8.1 主流程
sequenceDiagram
participant Caller as Agent/Embedded SDK
participant Engine as ExecutionEngine
participant Source as RecipientSource
participant Provider as ProviderSession
participant Sink as Result/Event Sink
Caller->>Engine: start(spec, ports)
Engine->>Engine: 校验策略和配置
Engine->>Provider: open(context)
Engine->>Sink: RUN_STARTED
loop 流式读取
Engine->>Source: nextBatch()
Source-->>Engine: Recipient batch
Engine->>Provider: send(item)
Provider-->>Engine: ProviderResult
Engine->>Sink: result batch + progress
end
Engine->>Provider: close()
Engine->>Sink: flush + RUN_COMPLETED
Engine-->>Caller: RunSummary8.2 启动校验顺序
- Run ID、Provider 和端口非空。
- 执行策略内部一致。
- Provider 存在且 SPI 兼容。
- Account 和 Message Schema 版本受支持。
- Provider 执行配置校验通过。
- Recipient Source 可打开。
- Result 和 Artifact Sink 可写。
- Provider Session 创建成功。
校验失败不进入 RUNNING,返回稳定错误分类。
8.3 资源关闭
资源按与创建相反的顺序关闭。无论正常结束、取消还是异常,都必须尝试:
- 停止读取新 Recipient。
- 等待或终止在途发送。
- Flush Result Sink。
- Flush Event Sink。
- 关闭 Provider Session。
- 关闭 Recipient Source。
- 完成 RunSummary。
单个资源关闭失败不能阻止其他资源关闭,但会进入 Run 的 suppressedErrors。
9. 并发模型
9.1 调度结构
- 每个 Run 有一个协调线程或协调任务。
- Recipient 按批次读取,不预加载完整 Audience。
- 每个 Item 使用虚拟线程执行 I/O 发送。
ConcurrencyGate使用许可数限制真实在途请求。RateLimiter在获取并发许可之后、调用 Provider 之前生效。- Provider 可以声明比用户配置更小的并发上限。
最终有效并发:
min(
用户目标并发,
Job 最大并发,
Provider 最大并发,
Agent 可用容量,
Service 下发容量
)9.2 背压
- Recipient Source 只有在执行队列低于高水位时才读取下一批。
- Result Sink 阻塞或积压时,Engine 减缓新的 Item 派发。
- Event Sink 使用有界缓冲区;进度事件可以合并,终态和错误事件不得丢弃。
- 队列容量全部可配置且有安全默认值。
9.3 动态并发
ChangeConcurrency 修改目标许可数:
- 增大时立即释放新许可。
- 减小时不强制中断在途请求,待请求结束后自然收缩。
- 目标值越界时拒绝命令并返回允许范围。
- 每次变化产生
CONCURRENCY_CHANGED事件。
9.4 Provider 线程安全
Provider Descriptor 和只读 Schema 可以跨 Run 共享。Provider Session 默认是 Run 级实例,必须明确声明:
THREAD_SAFE:同一 Session 可被并发调用。SERIALIZED:Engine 对同一 Session 串行调用。SESSION_PER_WORKER:Provider 为工作单元创建子 Session。
未声明时按 SERIALIZED 处理。
10. 暂停、取消和停止
10.1 暂停
- 暂停停止派发新 Item。
- 已经调用 Provider 的请求继续完成。
- Recipient Source 保持打开,但不得无限占用不可续期资源。
- 超过
maxPauseDuration后由策略决定自动恢复或取消。
10.2 取消
- 状态先进入
CANCELLING。 - 不再读取或派发新 Item。
- 可取消的在途请求收到 Cancellation Token。
- 不支持取消的 Provider 请求等待 Item Timeout。
- 尚未派发的 Recipient 记录为
UNSENT,不是FAILED。 - 所有结果 Flush 后进入
CANCELLED。
10.3 Agent 关闭
Agent 收到操作系统停止信号后:
- 停止领取新 Lease。
- 向 Service 上报
DRAINING。 - 在 Grace Period 内等待 Run 完成。
- 超时后对 Run 发起取消并 Flush 本地事件。
- 上报最终心跳后退出。
11. 重试、超时和错误分类
11.1 Provider 错误分类
public enum ErrorCategory {
AUTHENTICATION,
AUTHORIZATION,
INVALID_REQUEST,
RECIPIENT_INVALID,
RATE_LIMITED,
TEMPORARY_REMOTE,
PERMANENT_REMOTE,
NETWORK,
TIMEOUT,
CANCELLED,
INTERNAL,
UNKNOWN
}ProviderResult 必须同时给出:
- 是否成功。
- 稳定 Provider 错误码。
- ErrorCategory。
- 是否建议重试。
- 可选
retryAfter。 - 已脱敏的诊断摘要。
- 外部请求 ID。
11.2 默认重试规则
RATE_LIMITED:按 ProviderretryAfter,否则指数退避。TEMPORARY_REMOTE、NETWORK:允许重试。TIMEOUT:仅当 Provider 调用语义允许时重试,否则转UNKNOWN。AUTHENTICATION、AUTHORIZATION:不逐条重试,可触发 Run 级快速失败。INVALID_REQUEST、RECIPIENT_INVALID、PERMANENT_REMOTE:不重试。INTERNAL、UNKNOWN:由显式策略决定,默认不自动重试。
11.3 退避
delay = min(maxDelay, initialDelay * multiplier^(attempt - 1)) + jitter重试必须同时受最大次数、Item Deadline 和 Run Deadline 限制。
11.4 熔断和快速失败
同一 Account 在短时间内连续发生认证失败时,Engine 停止继续发送并将 Run 标记为 FAILED 或 PARTIAL。熔断统计属于 Run 级,跨 Run 熔断由 Agent 或 Service 后续扩展。
12. 结果和进度设计
12.1 Item 状态
PENDING 尚未派发
IN_FLIGHT 已调用 Provider,结果未确定
SUCCEEDED Provider 明确成功
FAILED Provider 明确失败且不再重试
UNKNOWN 可能已发送,但无法确认结果
UNSENT Run 结束前未调用 Provider
SKIPPED 因校验或策略主动跳过12.2 RunSummary
public record RunSummary(
String runId,
RunState finalState,
long total,
long succeeded,
long failed,
long unknown,
long unsent,
long skipped,
long retried,
Instant startedAt,
Instant endedAt,
List<ArtifactRef> artifacts,
List<ExecutionError> suppressedErrors
) {}计数必须满足:
total = succeeded + failed + unknown + unsent + skippedretried 是尝试次数统计,不参与总数恒等式。
12.3 Result Sink
public interface ResultSink extends AutoCloseable {
void append(List<ItemResult> batch);
void flush();
}- 默认批量大小建议 100~1000,可配置。
- 同一
itemId + attempt重复写入必须幂等。 - CSV 适合用户下载,内部恢复建议使用 JSON Lines 或结构化二进制格式。
- 响应体默认只保存摘要,完整内容由 Result Policy 显式开启。
12.4 ProgressAggregator
- 内部计数实时更新。
- 对 Agent 的进度事件按时间间隔或数量阈值合并。
- 默认每 500ms 至 1s 产生一次进度事件。
- 终态前强制发送最后一条完整计数。
13. Provider SPI
13.1 ProviderFactory
public interface ProviderFactory {
ProviderDescriptor descriptor();
ValidationResult validateAccount(ConfigDocument account);
ValidationResult validateMessage(ConfigDocument message);
ConnectionTestResult testConnection(
ConfigDocument account,
SecretResolver secrets,
Duration timeout);
ProviderSession open(ProviderOpenContext context);
}13.2 ProviderSession
public interface ProviderSession extends AutoCloseable {
ProviderResult send(ProviderSendRequest request, CancellationToken token);
default PreviewResult preview(ProviderPreviewRequest request) {
throw new UnsupportedOperationException("preview");
}
@Override void close();
}13.3 ProviderSendRequest
public record ProviderSendRequest(
String runId,
String itemId,
int attempt,
RecipientRecord recipient,
ConfigDocument messageConfig,
String idempotencyKey,
Instant deadline
) {}Provider 不得通过 runId、accountId 或 messageId 查询 Service 数据库。
13.4 ProviderDescriptor
Descriptor 至少包含:
providerId- 展示名称、类别、说明和图标引用
- 实现版本、SPI 版本
- Account、Message、Recipient 和 UI Schema
- 能力:Preview、Dry Run、Idempotency、Response Body 等
- 并发上限和默认超时建议
- Thread Safety Mode
- 支持的平台和架构
14. Provider Schema 规范
14.1 文件布局
META-INF/wepush/provider.json
META-INF/wepush/schemas/account.schema.json
META-INF/wepush/schemas/message.schema.json
META-INF/wepush/schemas/recipient.schema.json
META-INF/wepush/schemas/ui.schema.json一个内置模块包含多个 Provider 时,可以使用 META-INF/wepush/providers/<slug>/ 分目录保存各自 Descriptor 和三类 Schema;Factory 仍通过标准 ServiceLoader 文件发现。provider-standard 使用该布局。
14.2 扩展字段
x-wepush-secret Secret 字段,不可回读
x-wepush-widget 自定义 UI 控件
x-wepush-variable 允许使用 Recipient 变量
x-wepush-multiline 多行编辑器
x-wepush-code-language JSON、HTML、SQL 等编辑模式
x-wepush-display-order 展示顺序
x-wepush-advanced 放入高级设置
x-wepush-capability 依赖的 Provider 能力14.3 校验层次
- UI 本地 Schema 校验,用于即时提示。
- Service Schema 校验,作为保存前权威校验。
- Provider 语义校验,例如签名算法、字段组合和渠道限制。
- 可选连接测试,不作为普通保存的隐式步骤。
UI 校验结果不能替代 Service 校验。
15. Provider 发现、隔离和更新
15.1 发现
- 外部 Provider 插件使用 PF4J 3.15.x;Provider SPI 作为 Extension Point,Core Engine 不依赖 PF4J。
- Agent App 的
ProviderCatalogAdapter把 PF4J Extension 转换为 Core 的ProviderFactory。 - 内置 Provider 作为系统 Extension 进入相同 Catalog,避免执行路径出现两套语义。
- Agent 启动时扫描
plugins//<version>/,校验plugin.json、SPI 兼容范围、文件 SHA-256 和 Ed25519 签名,再装载被激活的版本。 - 重复 Provider ID、缺失 Schema、签名失败或 SPI 不兼容时,插件进入
REJECTED,原因写入健康状态和审计日志。
插件包固定包含实现 JAR、Descriptor、JSON Schema、UI Schema、图标、许可证和签名清单。正式发行默认只接受受信发布者签名的包;未签名包仅能在显式 Developer Mode 中装载。
15.2 隔离
- 每个插件使用独立 PF4J ClassLoader;JDK、Provider SPI、Core API 和统一日志 API 由 Parent ClassLoader 提供。
- 插件私有的厂商 SDK 和三方依赖由插件 ClassLoader 加载,插件不得打包 SPI/Core API 的重复副本。
- Provider 不能直接访问 Service Repository、Spring Bean 或其他 Workspace 数据。
- ClassLoader 只解决依赖冲突,不构成恶意代码沙箱。非受信插件必须通过未来的独立进程 Provider Runner 执行。
15.3 更新和回滚
更新按 STAGED → VERIFIED → DRAINING → RESTARTING → ACTIVE 推进:先下载到版本目录并验证,再停止向目标 Agent 分配新 Run,等待或按策略处理现有 Run,重启 Agent 后验证能力清单,最后激活新版本。多 Agent 环境逐台滚动;旧版本保留到回滚窗口结束。
不在 JVM 内卸载、替换正在使用的 Provider ClassLoader。Run Snapshot 固定 Provider ID、版本和 Schema 版本;只有存在兼容 Agent 时才调度新 Run。详见 ADR-0005。
16. 首个 HTTP Provider 详细设计
HTTP Provider 用于验证完整纵向链路。
16.1 Account 配置
{
"baseUrl": "https://api.example.com",
"defaultHeaders": {
"User-Agent": "WePush-Next"
},
"auth": {
"type": "BEARER",
"token": {"$secret": "http-account-token"}
},
"connectTimeout": "PT5S"
}16.2 Message 配置
{
"method": "POST",
"path": "/notify",
"headers": {
"Content-Type": "application/json"
},
"query": {},
"bodyTemplate": "{\"mobile\":\"{{mobile}}\",\"content\":\"{{content}}\"}",
"successCondition": {
"status": [200, 201],
"jsonPath": "$.success",
"equals": true
},
"saveResponseBody": false
}16.3 安全限制
- 默认只允许 HTTP/HTTPS,生产建议仅 HTTPS。
- URL 由 Base URL 和受控 Path 组合,默认不允许 Recipient 控制 Host。
- DNS 解析后执行私网、环回、链路本地和云元数据地址策略检查。
- 禁止自动将 Secret 写入日志或响应 Artifact。
- 响应体设最大字节数,超出部分截断并标记。
- 重定向默认关闭;开启时每次跳转重新执行地址安全检查。
16.4 错误映射
| HTTP 情况 | ErrorCategory | 默认重试 |
|---|---|---|
| 2xx 且成功条件成立 | 成功 | 否 |
| 400、422 | INVALID_REQUEST | 否 |
| 401 | AUTHENTICATION | 否,Run 快速失败 |
| 403 | AUTHORIZATION | 否 |
| 404 | PERMANENT_REMOTE | 否 |
| 408、504 | TIMEOUT | 依幂等策略 |
| 429 | RATE_LIMITED | 是 |
| 500、502、503 | TEMPORARY_REMOTE | 是 |
| DNS/连接失败 | NETWORK | 是 |
17. Agent Runtime 组件
AgentRuntime
├── AgentIdentityStore
├── GrpcAgentClient
├── RegistrationManager
├── HeartbeatLoop
├── AgentControlStream
├── LeaseSupervisor
├── RunExecutor
├── CommandInbox
├── EventOutbox
├── ArtifactTransfer
├── LocalJournal
└── ProviderCatalog17.1 Agent 状态
STARTING
UNREGISTERED
CONNECTING
ONLINE
DRAINING
DEGRADED
OFFLINE
STOPPED- Provider 装载失败但核心仍可运行时进入
DEGRADED。 - 无有效身份时进入
UNREGISTERED,只允许 Enrollment。 - Service 不可达时进入
OFFLINE,不得领取新任务。
17.2 本地 Agent 标识
本地保存:
agentId- Agent credential 或证书引用
- Service endpoint
- 最后确认的 Event Sequence
- 未完成 Lease Journal
- Provider 清单摘要
文件权限必须限制为 Agent 运行用户可读。
18. Agent 注册和身份
18.1 Enrollment
管理员在 Service 创建一次性 Enrollment Token。Agent 首次启动调用:
POST /internal/agent/v1/enroll
Authorization: Enrollment <one-time-token>请求:
{
"displayName": "worker-shanghai-01",
"agentVersion": "0.1.0",
"protocolVersions": ["1"],
"platform": {
"os": "linux",
"arch": "x86_64",
"java": "21"
},
"providers": [
{"id": "wepush.http", "version": "1.0.0", "spi": "1"}
]
}响应包含 agentId 和长期 Agent Credential。Enrollment Token 使用一次后立即失效。
18.2 后续认证
- 首期可使用独立 Agent Token,通过 TLS 传输。
- 支持证书后优先使用 mTLS。
- Agent Token 与用户 API Token 使用不同签发域、权限和过期策略。
- Agent Credential 支持轮换和吊销。
19. Agent gRPC 控制流和租约协议
19.1 服务契约
正式契约位于 agent/agent-protocol/src/main/proto/。核心形态如下,具体字段以版本化 Proto 为准:
service AgentControlService {
rpc Connect(stream AgentToService) returns (stream ServiceToAgent);
}
message AgentToService {
string agent_id = 1;
uint64 sequence = 2;
oneof payload {
Hello hello = 10;
Heartbeat heartbeat = 11;
LeaseAck lease_ack = 12;
EventBatch event_batch = 13;
CommandAck command_ack = 14;
RunCompleted run_completed = 15;
Draining draining = 16;
}
}
message ServiceToAgent {
uint64 sequence = 1;
oneof payload {
Welcome welcome = 10;
LeaseOffer lease_offer = 11;
RunCommand command = 12;
EventAck event_ack = 13;
DrainRequest drain = 14;
}
}同一方向的 sequence 单调递增。gRPC 保证单条活跃流内有序,但 Agent 和 Service 仍持久化最后确认位置,以处理断线、重连和重复帧。应用消息设置大小上限;Audience、结果、日志和插件包不进入控制流。
19.2 建连和心跳
Agent 使用 Enrollment 获得的身份建立 Connect,第一帧必须是 Hello,包含 Agent 版本、协议范围、平台、Provider 清单摘要、容量和恢复游标。Service 返回 Welcome,确认选定协议版本、Server 时间、心跳间隔、消息上限和双方已确认 Sequence。
Heartbeat 包含 Agent 状态、最大/活跃 Run、可用并发和当前 Lease 摘要。心跳间隔默认 10 秒;超过三个周期未收到有效帧时 Service 将连接标记为失联并启动 Lease 恢复,但具体 Lease 到期以数据库时间为准。
19.3 Lease Offer 和确认
Service 根据 Agent 能力和容量主动发送 LeaseOffer,其中包含:
leaseId、runId、epoch、fencingToken、expiresAt。- Execution Snapshot 元数据和短期下载 URL。
- Audience Artifact 元数据和短期下载 URL。
- 仅限本次 Run 的 Secret Envelope。
Agent 下载并校验 Snapshot 后发送 LeaseAck。未在 Ack Deadline 内确认的 Offer 失效;Service 只有在校验 Agent、Lease、Epoch 和 Fencing Token 后才允许 Run 进入 RUNNING。
当前实现可通过 wepush.execution.mode=remote 启用该链路:agent_lease 保存会话归属、Epoch、Fencing Token、状态和最后连续 Event Sequence;Execution Spec 与 Audience 由受 Agent Credential/Bootstrap Token 保护的 /internal/agent/v1/leases/{leaseId}/... JSON API 提供,并在 Offer 中携带 SHA-256。Secret Envelope 已采用一次性 X25519 + HKDF-SHA-256 + AES-256-GCM 实现,Agent 会话公钥由 Hello 发布并持久化,密文同时绑定 Agent/Run/Lease/Epoch/Fence/过期时间,明文只在 Agent 运行内存短期存在。Agent Artifact 使用 Lease 绑定的短期上传计划,可经 Service 流式上传或 S3 Presigned Put,Commit 前执行 Head、大小和 SHA-256 完整性校验;Service 直接写入达到 100 MiB 时使用 Multipart。
19.4 Fencing 和重连
每次重新分配 Lease 都递增 epoch 并生成新的不透明 Fencing Token。所有 LeaseAck、Heartbeat Lease、EventBatch、CommandAck 和 RunCompleted 都携带 leaseId + epoch + fencingToken,旧 Epoch 写入一律拒绝。
Agent 断线后以指数退避和抖动重连负载均衡器,并在 Hello 中报告未完成 Lease、双方 Sequence 和 Outbox 范围。任意 Service 实例从 PostgreSQL 恢复状态并返回继续、停止或重新同步指令;连接到原实例不是恢复前提。
20. Agent 命令和事件上报
20.1 命令结构
RunCommand 包含 Agent 方向 Sequence、Command ID、Run ID、Lease Fencing 信息、命令类型、Payload 和创建时间。
- Sequence 在单 Agent 范围内单调递增。
- Agent 按顺序处理并持久化最后确认 Sequence。
- 命令按
commandId幂等。 - 命令结果通过
CommandAck和 Run Event 同时体现。
20.2 事件批次
Agent 使用 EventBatch 帧发送 firstSequence、lastSequence 和事件集合。Service 持久化后发送 EventAck,给出最后连续确认 Sequence。Agent 可以安全重传整个批次,Service 通过 runId + eventSequence 去重;发现缺口时要求从已确认位置重发。
Event Outbox 有内存和本地磁盘上限。达到高水位时优先聚合进度类事件并对 Core 施加背压,状态转换、错误和终态事件不得静默丢弃。
20.3 完成和 Artifact
RunCompleted 包含 RunSummary、Artifact ID/校验值/大小和最后 Event Sequence。大文件由 Agent 使用 Service 签发的短期 HTTPS URL 直接上传;Service 通过 Head 和 Checksum 验证所有必需 Artifact 均为 READY 后才提交 Run 终态。缺失内容时发送可重试拒绝,Agent 保留 Journal 和 Outbox。
21. Agent 本地恢复
Local Journal 不保存明文 Secret,至少记录:
- Lease ID、Run ID、Epoch。
- Execution Snapshot 校验值。
- 已确认事件 Sequence。
- 本地待上传 Artifact。
- Agent 退出原因和时间。
Agent 重启后:
- 读取 Journal。
- 向 Service 查询 Lease 当前所有权。
- 仍有效且允许恢复时继续上报或恢复执行。
- Lease 已失效时停止旧执行,只允许上传诊断信息。
- 无法确认 Provider 调用结果的 Item 标记为
UNKNOWN。
首期可以不支持从中间 Recipient 位置继续执行,但必须正确终止和报告,不允许静默丢失 Run。
22. Service 领域和应用服务
22.1 主要应用服务
ProviderQueryService
AccountApplicationService
MessageApplicationService
AudienceApplicationService
JobApplicationService
ScheduleApplicationService
RunApplicationService
RunCommandService
AgentApplicationService
AgentLeaseService
ArtifactApplicationService
SecretApplicationService
AuditApplicationService22.2 典型用例:创建 Run
事务内完成:
- 校验 Idempotency Key。
- 加载 Job 并检查权限。
- 加载 Account、Message Revision 和 Audience Snapshot。
- 校验 Provider 存在且配置版本兼容。
- 创建不可变 Run Snapshot。
- 创建
PENDINGRun。 - 写入
RUN_CREATED事件和审计记录。 - 保存 Idempotency 结果。
事务提交后发出可用性提示,由已连接的 Service 实例尝试生成 LeaseOffer。提示丢失不影响正确性,数据库扫描仍会发现待调度 Run。
22.3 典型用例:下发命令
- 校验用户和 Run 权限。
- 根据状态机判断命令是否合法。
- 生成 Command ID 和 Agent Sequence。
- 持久化命令。
- 返回
ACCEPTED,不等待 Agent 完成命令。 - Agent Ack 后更新命令状态并产生 Run Event。
23. Service 事务边界
23.1 原则
- 一个应用服务方法对应一个明确事务边界。
- 不在数据库事务内调用外部 Provider、上传大 Artifact 或等待 Agent。
- Repository 方法不自行提交事务。
- 事务提交后事件通过本地通知或 Outbox 触发后续处理。
- 数据库连接和 Session 按事务获取,不使用全局共享 Session。
23.2 乐观锁
可修改聚合包含 version 字段:
UPDATE job_definition
SET ..., version = version + 1
WHERE id = ? AND version = ?受影响行数为 0 时返回并发修改错误。
23.3 Run 状态并发
Run 状态变化使用状态条件和版本双重保护:
UPDATE run
SET state = ?, version = version + 1
WHERE id = ? AND state IN (...) AND version = ?禁止先读后无条件写。
24. Service 公开 API
24.1 OpenAPI 文件拆分
service/service-api/src/main/openapi/
├── openapi.yaml
├── paths/
│ ├── accounts.yaml
│ ├── messages.yaml
│ ├── audiences.yaml
│ ├── jobs.yaml
│ ├── schedules.yaml
│ ├── runs.yaml
│ └── agents.yaml
└── schemas/
├── common.yaml
├── problem.yaml
├── account.yaml
├── run.yaml
└── event.yaml构建时先校验和打包 OpenAPI,再生成 Server Stub 与 Remote Java SDK。生成代码不得手工修改。
24.2 创建 Run
POST /api/v1/workspaces/{workspaceId}/jobs/{jobId}/runs
Idempotency-Key: 20260822-manual-001
Content-Type: application/json{
"dryRun": false,
"policyOverrides": {
"concurrency": {"target": 50}
},
"reason": "manual",
"confirmationToken": "token-from-run-confirmation"
}手工正式发送必须先调用 POST /jobs/{jobId}/run-confirmation。Service 返回 Provider、Account、Audience 数量、策略、并发、限速、预计规模和五分钟令牌;令牌绑定 Workspace、Job 及 Account/Message/Audience/Policy 版本,资源发生变化或令牌过期后必须重新确认。Scheduler 使用内部可信入口,不依赖交互令牌。
成功返回 202 Accepted:
{
"id": "run-id",
"state": "PENDING",
"createdAt": "2026-08-22T08:30:00Z",
"links": {
"self": "/api/v1/workspaces/workspace-id/runs/run-id",
"events": "/api/v1/workspaces/workspace-id/runs/run-id/events"
}
}24.3 运行详情
Run Detail 包含:
- 状态和状态原因。
- Job、Provider 和 Snapshot 版本。
- Agent 与 Lease 摘要。
- 总量及各结果计数。
- 当前并发、速率和重试数。
- 开始、结束和持续时间。
- Artifact 链接。
- 允许的下一步命令列表。
24.4 SSE
GET /api/v1/workspaces/{workspaceId}/runs/{runId}/events
Accept: text/event-stream
Last-Event-ID: 120id: 121
event: progress
data: {"runId":"...","succeeded":300,"failed":2,"inFlight":50}- SSE 心跳注释间隔默认 15 秒。
- 连接断开后客户端指数退避重连。
- Event ID 只在单 Run 内有序。
- 请求过旧且事件已被清理时返回明确的游标过期错误,客户端改用 Run Detail 恢复快照。
25. API 通用约定
25.1 分页
首期使用 Cursor Pagination:
{
"items": [],
"page": {
"nextCursor": "opaque",
"hasMore": true
}
}Cursor 不暴露 SQL 结构,使用 HMAC 保护,并绑定名称、状态和时间筛选。公开资源页 limit 为 1..100;服务端只读取 limit + 1 条判断 hasMore,篡改、跨资源或跨筛选复用 Cursor 会被拒绝。
25.1.1 资源版本、文件导入与关联重发
- Account、Message、Audience、Job 和 Schedule 使用乐观版本更新;Message 内容变更创建新 Revision,Audience Recipient 变更创建新 Snapshot。
- CSV/TXT 使用流式 Reader 分批写入 staging 表,数据库保留首个
itemId并标记重复行;预览最多返回 20 条接受/错误记录,完整错误通过流式 CSV 下载。 - Import Commit 使用
INSERT ... SELECT创建 Snapshot 和 Recipient,不把完整文件载入 JVM;更新已有 Audience 时再次校验资源版本。 - FAILED、UNKNOWN、UNSENT 重发先返回匹配 Item 数量和短期确认令牌,再创建带
sourceRunId的新 Run;run_retry_item限定执行集合,新 Run 复制来源 Run Snapshot,不读取当前可变资源。
25.2 错误响应
{
"type": "https://wepush.example/errors/run-state-conflict",
"title": "Run state conflict",
"status": 409,
"code": "RUN_STATE_CONFLICT",
"detail": "Run cannot be paused from SUCCEEDED",
"traceId": "...",
"errors": []
}稳定错误码至少包含:
VALIDATION_FAILED
AUTHENTICATION_REQUIRED
ACCESS_DENIED
RESOURCE_NOT_FOUND
VERSION_CONFLICT
IDEMPOTENCY_CONFLICT
PROVIDER_NOT_AVAILABLE
PROVIDER_CONFIG_INVALID
RUN_STATE_CONFLICT
AGENT_NOT_AVAILABLE
LEASE_EXPIRED
ARTIFACT_NOT_READY
RATE_LIMITED
INTERNAL_ERROR25.3 HTTP 状态
200查询或同步操作成功。201资源创建完成。202异步命令已接受。204删除或无响应体操作成功。400请求格式或字段错误。401未认证。403无权限。404资源不存在或不可见。409状态、版本或幂等冲突。422Provider 语义校验失败。429调用频率超限。503暂无可用能力或服务未就绪。
26. 数据库逻辑模型
所有业务表包含:
id 不透明 ID
workspace_id 工作空间边界
created_at UTC 创建时间
updated_at UTC 更新时间
version 乐观锁版本即使首期只有一个 Workspace,也保留 workspace_id。
26.1 身份和权限
| 表 | 关键字段 |
|---|---|
workspace |
name、status、settings_json |
app_user |
username、display_name、status、password_hash 或 external_subject |
role_binding |
user_id、workspace_id、role |
api_token |
owner_id、token_hash、scopes、expires_at、last_used_at |
agent_credential |
agent_id、credential_hash/cert_fingerprint、expires_at、revoked_at |
Token 只保存不可逆 Hash,明文只在创建时返回一次。
26.2 Provider 和账号
| 表 | 关键字段 |
|---|---|
provider_catalog |
provider_id、implementation_version、spi_version、descriptor_json、status |
account |
provider_id、name、config_json、secret_set_id、status、last_test_at |
secret_record |
secret_set_id、secret_name、ciphertext、key_version、updated_at |
account.config_json 不包含明文 Secret,只保存普通配置和 Secret 引用。
26.3 消息和受众
| 表 | 关键字段 |
|---|---|
message_template |
provider_id、name、current_revision、status |
message_revision |
message_id、revision、schema_version、content_json、content_hash |
audience |
name、source_type、source_config_json、status |
audience_snapshot |
audience_id、revision、artifact_id、record_count、content_hash、state |
Message Revision 和 Audience Snapshot 创建后不可修改。
26.4 Job、Schedule 和 Run
| 表 | 关键字段 |
|---|---|
job_definition |
name、account_id、message_id、audience_id、policies_json、enabled |
schedule |
job_id、cron、timezone、misfire_policy、enabled、next_fire_at |
run |
job_id、snapshot_id、state、state_reason、agent_id、计数、时间、version |
run_snapshot |
run_id、provider_ref、account_config、message_content、policies、hash |
run_command |
run_id、agent_id、sequence、type、payload_json、state、ack_at |
run_event |
run_id、sequence、type、occurred_at、payload_json、severity |
26.5 Agent 和 Lease
| 表 | 关键字段 |
|---|---|
agent |
name、status、version、protocol_version、platform_json、last_seen_at |
agent_provider |
agent_id、provider_id、version、spi_version、status |
agent_lease |
run_id、agent_id、epoch、token_hash、state、expires_at、acked_at |
约束:同一 Run 同时最多一个 ACTIVE Lease。
26.6 Artifact、幂等和审计
| 表 | 关键字段 |
|---|---|
artifact |
type、backend、location、size、sha256、content_type、state、expires_at |
idempotency_record |
scope、key_hash、request_hash、response_status、response_body、expires_at |
audit_event |
actor_type、actor_id、action、resource_type、resource_id、result、details_json |
27. 索引和保留策略
建议索引:
run(workspace_id, state, created_at)
run(job_id, created_at)
run_event(run_id, sequence) UNIQUE
schedule(enabled, next_fire_at)
agent(status, last_seen_at)
agent_lease(run_id, state) UNIQUE WHERE state = ACTIVE
agent_lease(agent_id, expires_at)
message_revision(message_id, revision) UNIQUE
audience_snapshot(audience_id, revision) UNIQUE
idempotency_record(scope, key_hash) UNIQUESQLite 不支持的条件索引能力通过事务校验和等价唯一字段实现。
默认保留基线:
- Run 摘要作为业务记录保存,具体归档期由 Workspace 策略配置。
- 高频 Run Event 和运行日志保存 30 天后压缩或删除。
- Audit Event 保存时间长于普通日志。
- Audience Snapshot 在被引用期间保留,解除引用后 30 天;运行结果 90 天;完整 Provider 响应 7 天;临时导出 24 小时。
- 删除前验证没有有效引用;
PINNED或LEGAL_HOLD跳过普通 TTL。 - Secret 不进入备份外的普通导出。
28. SQLite 与 PostgreSQL 18 适配
28.1 统一语义
- Repository 接口和领域行为保持一致。
- JSON 在 PostgreSQL 使用 JSONB,在 SQLite 使用规范化 JSON TEXT。
- 枚举使用可读字符串,不使用数据库特有 Enum。
- 时间以 UTC 文本或数据库支持的时间类型保存,由适配器统一转换。
28.2 调度和 Lease Claim 差异
PostgreSQL 使用行锁和 SKIP LOCKED 领取候选 Run。SQLite 使用短事务和单写者模型完成领取,Standalone 限制单 Service 实例。
Server 中只有持有 PostgreSQL Session-level Advisory Lock 的 Service 实例运行 Schedule Scanner;锁使用专用连接持有,连接失效自动释放。Run Claim 在短事务中执行并同时写入递增 Epoch 和 Fencing Token,不能只依赖进程内锁。
LISTEN/NOTIFY 仅作为新 Run、新命令和新事件的低延迟唤醒提示;所有消费者都必须有数据库轮询和游标恢复路径,正确性不依赖通知必达。
28.3 连接管理
- 使用连接池或 DataSource,每个事务独立获取连接。
- SQLite 池规模保持较小并启用 WAL、busy timeout。
- PostgreSQL 根据 Service 实例和负载配置池大小。
- 禁止全局共享 Connection、Session 或 Mapper 实例承载事务状态。
28.4 控制面高可用
- 正式 Server HA 至少运行两个无状态 Service 实例,并置于支持 HTTP/2 与 gRPC 的负载均衡器后。
- 业务状态、Agent Sequence、Lease、Command、Event 和幂等记录都持久化在 PostgreSQL;Service 本地缓存不是事实源。
- PostgreSQL 自身复制、备份和故障转移由部署者选择并负责运维,WePush 安装包不自行组建数据库集群。
- Schema Migration 以集群级单例执行,采用 Expand → Migrate → Contract,滚动升级期间保持相邻版本兼容。
- 一个 Service 退出时 REST/SSE 客户端重连,Agent gRPC 自动重连;系统不要求连接粘滞。
详见 ADR-0006。
29. Artifact Store
29.1 接口
public interface ArtifactStore {
ArtifactUploadPlan beginUpload(ArtifactCreateCommand command);
ArtifactMetadata completeUpload(ArtifactCompleteCommand command);
ArtifactDownloadPlan authorizeDownload(ArtifactRef ref, Optional<ByteRange> range);
ArtifactMetadata stat(ArtifactRef ref);
void delete(ArtifactRef ref);
}ArtifactUploadPlan 可以是本地受控 Stream,也可以是带到期时间的 S3 Presigned URL/Multipart Plan。Upload 先进入 UPLOADING,写入完成并校验 SHA-256 后进入 READY。失败或超时的上传由清理任务回收。
29.2 本地目录
data/artifacts/
├── audiences/<snapshot-id>/recipients.jsonl
├── runs/<run-id>/success.jsonl
├── runs/<run-id>/failed.jsonl
├── runs/<run-id>/unknown.jsonl
├── runs/<run-id>/unsent.jsonl
└── runs/<run-id>/logs.jsonl路径由 Artifact Store 生成,不能直接使用用户输入的任务名称,避免路径穿越和非法字符问题。
29.3 Server 对象存储
- Server 默认实现
S3ArtifactStore,只依赖 Put/Get/Head/Delete、Range、Presigned URL 和 Multipart Upload 等受控 S3-compatible 子集。 - 对象键格式为
workspaces/{workspaceId}/{artifactType}/{yyyy}/{mm}/{artifactId},不含用户文件名和 Secret。 - Agent 不持有长期对象存储 Credential;Service 签发最小权限、短期有效的上传或下载 URL。
- Service 托管流式写入达到 100 MiB 时默认使用 Multipart。Agent 对不超过 1 GiB 的对象继续使用绑定长度和 SHA-256 的单次 Presigned Put,超过该阈值时使用 Presigned Multipart Plan,最大单 Artifact 为 5 TiB。Service 每批最多签发 100 个 Part URL,并显式约束 S3 的 10,000 Part 上限;Agent 可独立重试 Part,提交 ETag 列表后由 Service Complete 并用 Head 元数据核对总大小与 SHA-256。未完成会话由 V17 事实表驱动主动 Abort,对象存储 Lifecycle 在 24 小时后终止孤儿 Multipart 作为兜底。
- Server 模式启用对象存储原生 AES256 或由部署方自行保证等价的存储端加密;WePush 不对接或管理云 KMS Key。
- 数据库元数据是生命周期事实源,对象存储 List 只能用于对账和孤儿清理。
29.4 下载
- Service 校验权限后返回流或短期签名 URL。
- 支持 Range 请求。
- 下载文件名通过响应头提供,存储路径不暴露给客户端。
- 敏感 Artifact 可要求再次认证或更高权限。
29.5 保留和删除
Service 清理任务根据 Workspace 策略把到期对象从 READY 标记为 DELETING,执行幂等删除后置为 DELETED。失败使用退避重试;对象不存在视为删除成功。默认保留期采用第 27 节基线,对象存储 Lifecycle 仅作为兜底。详见 ADR-0007。
30. Secret 详细设计
30.1 默认实现和加密信封
默认 SecretStore 实现为 LocalEnvelopeSecretStore。每条 Secret 生成独立 256-bit 随机 DEK,使用 AES-256-GCM 和随机 Nonce 加密;AAD 至少包含 workspaceId + secretId + secretType + recordVersion,从而阻止跨记录替换密文。
plaintext secret
↓ 使用随机 DEK 加密
ciphertext + nonce + algorithm
↓ 使用 Master Key 加密 DEK
encrypted DEK + key version数据库保存 Ciphertext、Nonce、Encrypted DEK、算法和 Key Version。Master Key 不存入业务数据库。
- Standalone 首启可创建主密钥文件,并限制为当前用户可读:Unix 权限
0600,Windows 使用专用 Service Account ACL。 - Server 从只读挂载文件读取 Master Key;允许环境变量显式注入但不作为推荐生产方式。
- Server HA 的所有 Service 实例挂载相同的 Active/Retained Key Ring;轮换由单一管理操作生成新版本,再通过受控部署传播到所有实例。
- Server 发现密钥缺失、权限过宽或 Key Version 不可解析时 Fail Closed,不静默生成新密钥。
- Master Key 轮换默认只重新包裹各 Secret 的 DEK,并保留旧 Key Version 直到验证和回滚窗口结束。
- Electron
safeStorage只可用于 Desktop 自身的本地 UI Credential,不用于 Service Secret。Service 不规划 Vault、云 KMS、云 Secret Manager 或操作系统凭据存储 Adapter。
30.2 API 行为
创建或更新账号时:
{
"token": {"operation": "REPLACE", "value": "secret"},
"password": {"operation": "KEEP"}
}查询时只返回:
{
"token": {"configured": true, "updatedAt": "..."}
}不提供读取 Secret 明文的普通 API。
30.3 Agent Secret Envelope
- Service 只下发 Run 所需的最小 Secret 集。
- Envelope 绑定 Agent ID、Run ID、Lease ID、Epoch 和过期时间。
- 使用 Agent 公钥或受 TLS 保护的短期会话密钥加密。
- Agent 解密后只在内存持有,不写 Local Journal。
- Lease 失效后 Envelope 不再接受重新获取。
当前实现使用 Agent 进程启动时生成的 X25519 密钥对,Hello 只发布 X.509 编码公钥;Service 为每个 Envelope 生成一次性 X25519 密钥,使用 HKDF-SHA-256 派生 AES-256-GCM 密钥。冻结 Account/Message 中形如 {namespace,name,version} 的标准 SecretRef 被递归去重扫描,未引用的 Secret 不会进入 Envelope。Agent 在 ACK 前完成认证解密,运行完成/失败后清零 Secret Material。Envelope 上限为 512 KiB,单 Secret 上限为 64 KiB,并且不替代 TLS、Enrollment 和 Agent 身份认证。
31. Scheduler 详细设计
31.1 Schedule 字段
cron
timezone
enabled
misfirePolicy: SKIP | FIRE_ONCE | CATCH_UP_LIMITED
catchUpLimit
startAt
endAt
lastFireAt
nextFireAt31.2 触发幂等
每个计划触发使用以下幂等键:
schedule:<scheduleId>:<scheduledInstant>即使调度线程重启或重复扫描,也只创建一个 Run。
31.3 扫描
- 周期性查询
enabled = true AND next_fire_at <= now。 - 在短事务内锁定 Schedule、创建 Run、更新下次时间。
- 不在调度事务中物化大型 Audience;Run 创建后异步准备 Snapshot。
- Cron 解析和 Next Fire 计算使用同一库和版本,保存测试样例防止升级行为变化。
32. Remote 与 Embedded Java SDK 详细设计
32.1 包结构
sdk-java
├── generated/ OpenAPI 生成代码
├── WePushClient.java 统一入口
├── WorkspaceClient.java Workspace 范围入口
├── AccountsClient.java
├── MessagesClient.java
├── AudiencesClient.java
├── JobsClient.java
├── RunsClient.java
├── AgentsClient.java
├── EventSubscription.java
└── WePushException.java32.2 使用方式
try (WePushClient client = WePushClient.builder()
.endpoint(URI.create("https://wepush.example.com"))
.token(() -> tokenProvider.currentToken())
.connectTimeout(Duration.ofSeconds(5))
.requestTimeout(Duration.ofSeconds(30))
.build()) {
WorkspaceClient workspace = client.workspace(workspaceId);
Run run = workspace.runs().start(jobId,
StartRunRequest.builder().dryRun(false).build(),
"manual-20260822-001");
try (EventSubscription events = workspace.runs().events(run.id())) {
events.forEach(System.out::println);
}
}32.3 SDK 重试
- GET 和明确幂等请求可对网络错误自动重试。
- POST 创建操作只有携带 Idempotency Key 时才能自动重试。
- SDK 不对 Service 返回的业务失败盲目重试。
429和503遵循Retry-After,并有最大等待限制。
32.4 Embedded SDK
try (WePushEngine engine = WePushEngine.builder()
.provider(new HttpProviderFactory())
.secretResolver(secretResolver)
.resultSink(resultSink)
.eventSink(eventSink)
.build()) {
RunHandle handle = engine.start(spec, recipients);
handle.submit(new RunCommand.ChangeConcurrency(commandId, 50));
RunSummary summary = handle.completion().toCompletableFuture().join();
}Embedded SDK 不创建数据库、不读取 Next Service 配置目录,也不自动发现未显式允许的第三方 Provider。共享 Sink 与 Engine 同生命周期且必须支持并发 Run;使用 resultSinkFactory、eventSinkFactory 或 artifactSinkFactory 创建的 Sink 归单个 Run 所有,并在完成或启动拒绝后关闭。
33. WebUI 详细设计
33.1 技术与工程基线
- TypeScript 6.0.x,开启
strict、noUncheckedIndexedAccess和未使用代码检查。 - Vite 8.1.x、React 19.2.x、Node.js 24 LTS;pnpm 通过 Corepack 固定精确版本并使用单一 Lockfile。
- Tailwind CSS 提供设计 Token 和布局原语;shadcn/ui 仅按需引入复杂可访问组件,复制后的源码视为项目代码并纳入测试。
- WebUI 是纯 SPA,由 Service 提供静态资源或独立 CDN 部署;生产环境不需要 Node.js Server,也不使用 React Server Components。
33.1.1 视觉语言
WebUI 与 Desktop Renderer 采用接近 Codex 客户端的克制型工作台风格,但不复制其品牌标识或专有资产:
- 使用暖灰应用背景、浅灰紧凑侧栏、白色内容面和低对比细边框组织层级。
- 左侧导航保持稳定,顶部区域只保留页面标题、搜索/命令入口和连接状态。
- 字号、圆角和阴影偏小,依靠留白、对齐和字重形成视觉层级,避免装饰性渐变和大面积高饱和色。
- 默认以单色交互为主;绿色、黄色、红色和蓝色只表达成功、警告、失败和信息状态。
- 总览优先显示真实 Service、Provider、Agent 和 Run 数据;没有数据时使用明确空状态,不填充伪造趋势或示例成功率。
- WebUI 与 Desktop 必须复用
design-tokens和共享 UI primitives,平台外壳不得另建一套视觉变量。 - 保持键盘操作、可见焦点、语义标签、颜色对比和窄屏退化能力;后续暗色主题仍由同一语义 Token 映射实现。
前端 Workspace 结构固定为:
ui/
├── apps/
│ ├── web/
│ └── desktop/
└── packages/
├── api-client/
├── schema-renderer/
├── ui/
├── features/
└── design-tokens/33.2 路由
/login
/overview
/providers
/accounts
/accounts/:id
/messages
/messages/:id
/audiences
/audiences/:id
/jobs
/jobs/:id
/schedules
/runs
/runs/:id
/agents
/agents/:id
/docs
/settings33.3 前端分层
app/ 路由、认证、全局错误处理
api/ 生成客户端和 Facade
features/ 按业务功能组织页面和用例
schema-renderer/ JSON Schema 与 UI Schema 表单
components/ 通用展示组件
stores/ 登录身份、连接状态和少量全局状态
events/ SSE 连接、游标和重连Server State 以 API 缓存为主,不复制成长期全局可变 Store。
33.4 Schema 表单
| Schema | 默认控件 |
|---|---|
string |
文本框 |
string + format=password 或 Secret |
密钥输入框 |
string + enum |
下拉框 |
boolean |
开关 |
integer/number |
数字输入 |
array |
可增删列表或表格 |
object |
分组表单 |
x-wepush-code-language |
代码编辑器 |
| Recipient Variable | 变量选择和插入器 |
表单保存时发送原始 Schema 数据,不把 UI 布局字段混入 Provider 配置。
33.5 Run 页面
Run Detail 页面展示:
- 状态、进度条和结果计数。
- 实时吞吐和延迟图。
- 当前 Agent、Lease 和 Provider。
- 当前并发、允许范围、动态调整控件。
- 结构化实时日志和错误分类。
- Pause、Resume、Cancel 等状态相关操作。
- 成功、失败、未知、未发送 Artifact 下载。
SSE 断开时显示连接状态并自动重连;不能将“监控连接断开”误显示为“Run 失败”。
33.6 API 文档页
- 展示当前 Service 的实时 OpenAPI。
- 使用当前登录身份调用,但不在 Local Storage 保存长期 Token。
- Secret 示例只使用占位符。
- 对删除、取消、真实发送等操作增加风险提示。
- 只读角色禁用 Try It Out 的写操作。
34. Desktop UI 详细设计边界
Desktop 固定采用 Electron 43.x。apps/desktop 只实现 Main/Preload、窗口、托盘、通知、自动启动和更新能力,Renderer 复用 packages/features 中的 WebUI 页面。
34.1 启动流程
flowchart TD
Start[Desktop 启动] --> Config[读取连接配置]
Config --> Mode{本地还是远程}
Mode -- 远程 --> Login[连接远程 Service 并登录]
Mode -- 本地 --> Health[探测本机 Service Health]
Health -->|在线| Bootstrap[获取本地短期会话]
Health -->|离线| StartService[启动或提示安装 Service]
StartService --> Health
Bootstrap --> UI[加载共享 UI]
Login --> UI34.2 本地认证
- Service 不因来自 localhost 就跳过认证。
- Desktop 使用受操作系统权限保护的 Bootstrap Channel 获取一次性 Token。
- 一次性 Token 换取短期会话后立即失效。
- 不把管理员长期 Token 写入普通配置文件。
34.3 进程边界
- Main、Preload 和 Renderer 严格分层;Renderer 设置
nodeIntegration: false、contextIsolation: true、sandbox: true。 - Renderer 只加载随安装包发布的本地内容,不执行任意远程代码;CSP 禁止
unsafe-eval。 - Preload 只暴露版本化、最小白名单 IPC,参数按 Schema 校验;不得暴露通用文件系统、Shell 或进程执行能力。
- Desktop 退出不等于停止 Service 或正在运行的 Run。
- Desktop 更新和 Service 更新是两个独立过程。
- Desktop 可以显示 Service 版本不兼容并引导升级。
- Desktop 不加载 Provider JAR、不读取数据库文件。
35. 认证、授权和审计
35.1 角色
| 角色 | 权限摘要 |
|---|---|
ADMIN |
用户、Agent、Secret、系统配置及全部业务操作 |
OPERATOR |
账号、消息、受众、Job、Run 和调度操作 |
VIEWER |
查询配置摘要、运行状态、日志和文档 |
读取账号不等于读取 Secret。即使 ADMIN 也不能通过普通 API 取回明文 Secret。
ADMIN、OPERATOR、VIEWER 角色绑定在 Workspace 范围内。另设显式 SYSTEM_ADMIN,只用于 Workspace 生命周期、全局 Agent 视图和跨 Workspace 应急治理;它可以越过 Workspace 角色,但每次操作仍携带目标 workspaceId 并进入审计。普通 Workspace ADMIN 不能签发、查看或吊销其他 Workspace 的 API Token/Enrollment Token。Standalone 的 Default Workspace 在普通 UI 中隐藏,但 API 和 Repository 仍使用其真实 ID。
35.2 权限检查
- Controller 只完成认证和基础 Scope 检查。
- Application Service 根据 Workspace、资源所有权和操作执行最终授权。
- Agent Internal API 仅接受 Agent 身份。
- Artifact 下载再次校验 Run 或 Audience 权限。
35.3 审计事件
必须审计:
- 登录、Token 创建和吊销。
- Account、Secret、Job、Schedule 的创建和修改。
- Run 启动、取消、暂停、恢复和并发调整。
- Agent Enrollment、禁用和 Credential 轮换。
- Artifact 下载和敏感配置导出。
- 权限失败和关键系统配置改变。
审计 Details 只保存字段名和变更摘要,不保存 Secret 原值。
36. 输入与网络安全
- 所有 API 请求设置 Body 大小上限。
- 文件导入流式解析并限制解压后大小、行数、列数和单字段长度。
- CSV/Excel 导出防止公式注入。
- 模板引擎禁止任意类访问、文件访问和反射。
- HTTP Provider 实施 SSRF 保护和响应大小限制。
- 标准渠道生产实现固定或严格校验厂商官方端点;响应体有界,诊断不包含 Secret、Token URL 或完整远端响应。
- 上传文件名仅作为展示信息,不作为真实路径。
- 日志字段使用结构化参数,不拼接未清理的控制字符。
- OpenAPI 文档和 UI Schema 中的富文本需要清理危险 HTML。
- Service 默认安全响应头,并明确 CORS Allowlist。
37. 可观测性详细设计
37.1 日志字段
timestamp
level
service
instanceId
traceId
workspaceId
runId
agentId
leaseId
providerId
event
message日志必须通过统一 Redactor 处理 Header、URL Query、JSON 字段和异常信息中的 Secret。
37.2 指标
wepush_runs_total{state,provider}
wepush_run_items_total{result,provider}
wepush_run_inflight{run,provider}
wepush_provider_requests_total{provider,result,error_category}
wepush_provider_request_duration_seconds{provider}
wepush_provider_retries_total{provider,category}
wepush_agent_heartbeat_age_seconds{agent}
wepush_agent_active_runs{agent}
wepush_lease_expired_total
wepush_event_outbox_size{agent}
wepush_artifact_upload_bytes_total{type}Run ID、Agent ID 等高基数字段不作为默认 Metrics Label,应通过日志和 Trace 查询。
37.3 Health
- Liveness:进程和核心线程是否存活。
- Readiness:数据库、迁移、Secret、Artifact 是否可用。
- Agent Readiness:身份、Service 连接和 Provider Catalog 是否正常。
- Provider Health 不对每次 Health 请求调用外部渠道,连接测试由用户显式触发。
38. 配置设计
示例:
wepush:
mode: standalone
server:
bind-address: 127.0.0.1
port: 18990
public-base-url: http://127.0.0.1:18990
database:
type: sqlite
url: jdbc:sqlite:${WEPUSH_DATA_DIR}/wepush-next.db
artifacts:
type: local
directory: ${WEPUSH_DATA_DIR}/artifacts
secrets:
type: local-envelope
master-key-file: ${WEPUSH_CONFIG_DIR}/master.key
agent:
embedded:
enabled: true
max-runs: 2
max-concurrency: 200
security:
remote-access: false
events:
progress-interval: PT1S
batch-size: 100规则:
- 配置支持文件、环境变量和命令行覆盖,优先级明确。
- Secret 不直接写入普通 YAML,使用环境变量引用或 Secret Store。
- 启动时打印生效配置摘要,但敏感字段必须掩码。
- 未识别配置项默认报错或告警,不能静默忽略拼写错误。
- 配置变更是否支持热加载由字段明确声明,默认需要重启。
39. 默认端口和目录
以下为初始发行基线,安装包落地时允许用发行 ADR 调整未公开的具体值:
| 平台 | 配置 | 数据 | 日志 |
|---|---|---|---|
| Linux | /etc/wepush-next/ |
/var/lib/wepush-next/ |
/var/log/wepush-next/ |
| Windows | %ProgramData%\WePush Next\service.env、agent.env |
%ProgramData%\WePush Next\service、agent |
%ProgramData%\WePush Next\logs |
| macOS Service | /Library/Preferences/wepush-next/ |
/Library/WePushNext/data/ |
/Library/Logs/WePushNext/ |
| 开发模式 | next/.local/config |
next/.local/data |
next/.local/logs |
默认 HTTP 端口建议 18990,不得与 Classic 使用的资源发生冲突。
开发模式目录必须加入 .gitignore。
40. Service 安装和生命周期
40.1 Linux
- Service 使用专用系统用户
wepush,Agent 使用独立的wepush-agent,配置组权限和数据目录彼此分离。 - systemd Unit 以非 root 用户运行。
ExecStart指向版本化发行目录;完整发行变体携带 JDK 25jlinkRuntime Image,精简变体校验并使用系统 Java 25+,启动脚本按此顺序自动选择。- 配置
Restart=on-failure和合理的 Stop Timeout。 - 安装后不自动开放公网防火墙端口。
40.2 Windows
- 使用独立 Windows Service 名称
WePushNextService。 - 通过发行包离线携带且固定长度/SHA-256 的 WinSW 启动包内 Runtime 或系统 Java 25+;WinSW 使用低权限
LocalService,ProgramDataACL 只授予 LocalService、SYSTEM 和 Administrators。 - 配置滚动日志、失败重启和优雅停止。
- 安装、卸载和 Service 控制需要管理员权限。
40.3 macOS
- 系统级 Service 使用 LaunchDaemon;用户级 Standalone 可使用 LaunchAgent。
- 安装包明确选择一种运行级别,不能混用数据目录。
- LaunchDaemon 必须使用明确存在的非 root 账号;安装器默认使用
sudo的发起用户,也允许显式设置WEPUSH_SERVICE_USER。 - 未签名告知、Release SHA-256、SBOM 和升级流程单独验证。
40.4 升级
- 拒绝或等待新 Run。
- Service 进入 Draining。
- 生成带 Manifest 和逐文件 SHA-256 的数据库、Master Key、Artifact、Agent Identity、Journal、Outbox、插件与配置一致性备份,并调用 Restore 校验。
- 停止旧进程。
- 替换应用并执行迁移。
- 启动并完成 Readiness、Flyway 当前版本和本地无网络 Provider Dry Run 检查。
- 失败时原子切回旧版本、恢复升级前备份、验证旧版本并保留可诊断的恢复目录。
数据库迁移一旦包含不可逆变化,必须明确最低可回滚版本。
41. 测试详细设计
41.1 Core 单元测试
使用 Fake Clock、Fake Provider、In-Memory Recipient Source 和 Result Sink,覆盖:
- 空 Audience、单条和大批量。
- 并发增减和背压。
- 暂停、恢复和取消竞态。
- Retry、Retry-After 和 Deadline。
- Provider Session 打开或关闭失败。
- Result/Event Sink 暂时失败。
- 最终计数恒等式。
- Secret 清理和日志脱敏。
测试不能依赖真实时间 Sleep,应通过可控 Clock 和调度器推进。
41.2 Provider 契约测试
所有 ProviderFactory 必须通过同一套测试:
- Descriptor 和 Schema 可加载。
- Provider ID 唯一且合法。
- Secret 字段正确标记。
- 非法配置返回结构化 Validation Error。
- Session 线程安全声明与行为一致。
- ErrorCategory 映射完整。
- Close 幂等。
41.3 Agent 故障测试
- LeaseOffer 后未 Ack。
- Heartbeat 超时。
- Event Batch 重传和乱序。
- 旧 Epoch Agent 上报。
- Service 不可达时 Event Outbox 满。
- Agent 在 Provider 调用前、调用中和调用后崩溃。
- 重启后 Journal 与 Service 状态冲突。
41.4 Service 集成测试
- SQLite 和 PostgreSQL 使用同一 Repository 契约测试。
- 创建 Run 事务和幂等键。
- 并发 Lease Claim 只能产生一个 Active Lease,并产生唯一有效 Fencing Token。
- 两个 Service 实例竞争调度锁时只能有一个 Schedule Scanner。
LISTEN/NOTIFY丢失后数据库扫描仍能推进状态。- Run 状态机非法转换。
- Schedule 重复扫描不重复创建 Run。
- Secret 查询不返回明文。
- Artifact 未完成、损坏和越权下载。
- SSE 游标续传和过期。
41.5 E2E
首条流水线:
启动 Standalone Service
→ Web/API 创建 HTTP Account
→ 创建 Message 和 Audience Snapshot
→ 创建 Job
→ 启动 Run
→ Embedded Agent 领取
→ Mock HTTP Server 接收请求
→ SSE 观察进度
→ 下载结果 Artifact
→ 重启 Service 验证历史42. 性能和容量验证
首期不承诺所有 Provider 的统一吞吐,但必须建立可重复基线:
- 100,000 Recipient 不全部加载进内存。
- 结果 Artifact 流式写入,内存随 Recipient 总数近似恒定。
- 进度事件数量由时间窗口控制,不与 Item 数量线性增长。
- 并发从低到高调整时不丢失 Item。
- Service 同时维持多个 SSE 连接时不会阻塞 Run 更新。
- Agent Event Outbox 在网络抖动下有明确磁盘和内存上限。
性能报告记录硬件、JVM、Provider Mock 延迟、并发、吞吐、P50/P95/P99 延迟、GC 和最大内存。
43. CI/CD 详细流程
validate
├── 格式和静态检查
├── OpenAPI 校验
├── JSON Schema 校验
└── 依赖与许可证检查
test
├── 单元测试
├── 架构测试
├── Provider 契约测试
└── Service 集成测试
package
├── Java 制品
├── WebUI
├── Service/Agent 应用镜像
└── Java SDK
platform-package
├── Linux
├── Windows
└── macOS
smoke
├── 安装
├── 启动和 Health
├── HTTP Provider 最小 Run
└── 卸载/升级验证Next CI 使用 next/** 路径过滤,Classic 和 Next 互不依赖对方构建产物。
44. 首个纵向实现拆分
44.1 Iteration 1:纯 Core
- 创建模块和架构测试。
- 实现 Core 类型、ExecutionEngine 和 Fake Provider。
- 完成并发、取消、结果和事件单元测试。
44.2 Iteration 2:HTTP Provider
- 完成 Descriptor、Schema、Session 和 Mock Server 契约测试。
- 实现 SSRF、超时、响应限制和错误分类。
- 通过 Embedded SDK 运行。
44.3 Iteration 3:Service 单机闭环
- SQLite、迁移、Account/Message/Audience/Job/Run API。
- Embedded Agent 复用同一 Lease/状态语义,但通过 Java 接口直连,不经过 gRPC。
- OpenAPI、Java SDK 和 SSE。
44.4 Iteration 4:最小 WebUI
- Account Schema 表单。
- Message、Audience 和 Job 创建。
- Run Detail、SSE 和结果下载。
- API 文档页面。
44.5 Iteration 5:远程 Agent
- Enrollment、身份、gRPC Connect、Heartbeat、LeaseOffer/LeaseAck 和 EventBatch/EventAck。
- Fencing Token 与 Agent Journal。
- Agent 失联和恢复测试。
当前进度:Iteration 5 基线已完成。Hello/Heartbeat、持久 Lease/Fence、Snapshot/Audience 下载校验、加密 Secret Envelope、远端 Core 执行、磁盘 Event/Completion Outbox、EventBatch/EventAck、RunCommand/CommandAck、Agent Artifact 上传/完整性提交、RunCompleted、重复 Offer/Event 去重、进程重启恢复、一次性 Enrollment、Credential/证书轮换以及 TLS/mTLS 已形成纵向闭环;Service→Agent 消息使用数据库 outbox 支持 Agent 在任意 Service 实例重连。
44.6 Iteration 6:产品化
- Workspace 权限、审计、
LocalEnvelopeSecretStore。 - Scheduler。
- Desktop 和三平台 Service 安装。
- PostgreSQL 18 HA、S3-compatible Artifact Store 和部署文档。
当前进度:Iteration 10(1.2.0)已完成。1.1.0 的运营商插件、资源治理、诊断、熔断、PostgreSQL 通知、Multipart Artifact 和 WebUI 可访问性继续有效;1.2.0 增加 Codex MCP/Skill 接入,并将 Java 基线升级到 25,且不新增数据库迁移。具体版本边界见产品路线图和 1.2.0 Release Notes。
44.7 Iteration 7:日常使用闭环
- Account/Message/Audience/Job/Schedule 编辑、修订、复制、状态管理。
- CSV/TXT 流式导入、字段映射、预览、去重、错误行和不可变 Snapshot。
- Dry Run、正式发送影响确认、结果查看和失败项关联重发。
- 资源/Run/审计签名分页筛选、真实总览和 Standalone/Server Workspace UX。
当前进度:Iteration 7 已在 0.1.0-alpha.3 完成。OpenAPI、Remote Java SDK、TypeScript Client、WebUI、SQLite V12/V13 和纵向集成测试同步交付;启动恢复也使用有界分页扫描。
44.8 Iteration 8:真实消息渠道
- 内置 SMTP、飞书/钉钉/企微机器人、阿里云短信、微信公众号、小程序和企业微信应用消息。
- 每个 Provider 同步交付 Account/Message/Recipient Schema、SecretRef、Dry Run、真实发送、错误分类和本地协议测试。
- Service/Agent 通过 ServiceLoader 发现,Embedded SDK 由调用方显式选择;WebUI 继续按实时 Schema 渲染。
当前进度:Iteration 8 已在 0.1.0-alpha.4 完成。标准渠道集中在独立 provider-standard 模块,生产端点受限于厂商官方域名;微信系 Token 只在 Session 内缓存并在明确失效后刷新一次。协议、重试、限流、幂等和最小配置见内置 Provider 指南。
45. 完成定义
一个模块或功能只有同时满足以下条件才视为完成:
- 实现符合依赖规则和领域状态机。
- 公开契约、Schema 或配置已记录。
- 单元、契约和必要的集成测试通过。
- 错误、日志和指标可诊断且不泄露 Secret。
- API 和 UI 具备明确的权限行为。
- 资源关闭、取消和进程退出路径经过测试。
- 文档、示例和升级说明同步更新。
- 不需要 Classic 源码或构建产物即可编译和运行。
46. 已决 ADR 与后续事项
本轮技术基线已由 ADR-0002 至 ADR-0008 确认,包括 Web/Desktop/Service、Secret、Agent 协议、Provider 插件、PostgreSQL HA、Artifact 和 Workspace 多租户。
以下事项可以在对应能力进入迭代前继续形成独立 ADR,但不阻塞当前架构:
- OpenAPI Generator、数据库迁移工具、路由和 Server State 库的具体版本。
- Desktop 本地 Bootstrap Channel、平台安装和未签名发行完整性验证。
- 非受信 Provider 的独立进程 Runner;只有明确服务于用户自建安全场景时才进入评审。
公共 SaaS、计费订阅、外部 Vault/云 KMS/Secret Manager、恶意公共租户物理隔离和跨地域控制面不是“后续非基线事项”,而是长期产品非目标。任何后续 ADR 不得把它们重新引入。允许评审的扩展不得反向污染 Core API,也不得削弱 Workspace、Lease Fencing、Secret 和 Artifact 的既定安全边界。
47. 文档演进规则
- 本文档负责跨模块的实现级设计。
- Java API、OpenAPI、JSON Schema 和数据库迁移文件落地后,以可执行契约为准,并保持与本文一致。
- 仅影响模块内部的实现细节记录在模块 README 或模块设计文档。
- 改变组件职责、依赖方向、运行语义或安全边界时,先提交 ADR,再修改本文档。
- 文档中的示例不得包含真实 Token、Secret、生产地址或个人数据。
Next Docs Implementation Status (`next/docs/implementation-status.md`)
WePush Next 实现状态
更新时间:2026-09-26
产品范围和后续优先级以《产品目标、边界与路线图》为准。Next 的长期定位是用户自行下载、安装、部署和运维;不建设官方公共 SaaS、注册计费订阅、云 KMS/Secret Manager 或恶意公共租户物理隔离。
1. 里程碑结论
next/ 的当前稳定基线为 1.2.0:在 1.1.0 的运营商签名插件、Workspace 资源治理、脱敏诊断、跨 Run 认证熔断、PostgreSQL 低延迟唤醒、Agent Presigned Multipart 与 WebUI 主题/可访问性之上,增加 Codex MCP/Skill 接入,并把 Java 运行基线升级到 25。数据库仍为 V17,REST/SSE 与 Agent 协议保持兼容。Classic 源码和构建保持不动;两条产品线不共享源码依赖,允许各自存在相似实现。
React WebUI / Electron Desktop / Remote Java SDK
│ REST + Bearer RBAC + SSE
▼
Spring Boot Service API(1..N 实例)
│ │ │
SQLite/PostgreSQL Secret Store Local/S3 Artifact
│ │ ▲ Presigned/Commit
└──── durable control ────────┘
│ gRPC 双向流
▼
Enrolled Remote Agent + PF4J
│
Core Engine
│
Provider SPI
业务 Java 应用 → Embedded Java SDK → Core Engine → Provider SPIStandalone 默认是单 Service + SQLite + Local Artifact + Embedded Engine;Server 默认模型是 PostgreSQL 18 + S3-compatible Store + 多 Service + Remote Agent。PostgreSQL 和 S3 不是使用 Next 的前置条件,只是 Server/HA 形态的共享事实源。
2. 组件完成状态
| 组件 | 当前基线 |
|---|---|
| Core | Framework-free API、虚拟线程 Engine、并发/限速/重试/暂停/取消、流式 Result/Event/Artifact 端口 |
| Provider | 独立 SPI;内置 HTTP、SMTP、飞书/钉钉/企微机器人、阿里云短信、微信系渠道;CMPP/SMGP/SGIP/SMPP 独立 Ed25519 签名插件;Schema、SecretRef、Dry Run、错误分类与端点约束 |
| Agent | gRPC 双向流、Sequence/Fence Journal、磁盘 Event/Completion Outbox、重连恢复、Secret Envelope、Presigned Put/Multipart Artifact、Enrollment/轮换、TLS/mTLS |
| Service | Spring Boot 4.1.1、SQLite/PostgreSQL、资源修订/分页、Workspace Policy、认证熔断、脱敏诊断、手动版本检查、Local/S3 Artifact、RBAC/审计/Scheduler、Agent HA Outbox、PostgreSQL 通知与跨实例 SSE 补偿 |
| Remote Java SDK | 只依赖公开 service-api;覆盖 System、Provider、Agent、Workspace Policy、资源生命周期/分页、Audience 上传、Run、Artifact、Schedule、Security、诊断、版本检查与认证熔断 |
| Embedded Java SDK | Framework-free 进程内 Engine 门面;显式 Provider、SecretResolver、Result/Event/Artifact Sink,支持列表或流式 Recipient 与完整 RunHandle 控制 |
| WebUI | TypeScript/Vite/React;资源编辑/修订、导入/确认/重发、总览/分页/调度、Security、Workspace Policy、诊断/版本/熔断管理、动态 Schema、API 调试、亮暗主题、低分辨率与可访问性、AI 助手安装器下载 |
| Desktop | Electron 安全外壳,共用 WebUI;本机 Service 检测/启停/日志/诊断、系统原生 API Token 安全存储、签名插件生命周期、一键安装 Codex MCP/Skill;目标系统原生目录打包,不依赖 Core 或 Service 内部实现 |
| Distribution | 系统 Java 精简包 + 三平台 jlink Runtime 完整包;统一 Standalone/高级分组件安装、离线 WinSW、正式备份/恢复、Beta 升级、升级健康门、自动回退与安全卸载;容器 Server/HA 拓扑 |
3. Core、Provider 与 Agent
- Core API 不依赖 Spring、数据库、HTTP Server、PF4J 或 UI;Service 和 Agent 通过端口适配同一个 Engine。
- 内置标准渠道位于独立
provider-standard模块,通过 Provider SPI 与 ServiceLoader 接入 Service/Agent;Embedded 应用按需要显式声明并注册它,不把渠道依赖强加给 Engine。 - SMTP 使用 Angus Mail;机器人、短信和微信系使用 JDK HttpClient。生产实现固定官方端点或严格校验用户提供的官方 Webhook,本地 mock 入口只对同包测试可见。
- 微信系 Access Token 按 Session 缓存并提前刷新;只有远端明确拒绝 Token 时安全重放一次。提交后超时、I/O 或
5xx均按UNKNOWN处理,不假定外部渠道 Exactly Once。 - Run Snapshot 固定 Provider ID/实现版本,调度只选择上报精确兼容版本且有容量的 Agent。
- PF4J 只位于 Agent App 边界。每个插件独立 ClassLoader,Provider SPI/Core API/日志 API 由 Parent 提供;这解决依赖冲突,不宣称是恶意代码沙箱。
- 正式插件包必须包含 SHA-256 清单和 Ed25519 签名。未知发布者、清单篡改、Zip Slip、压缩炸弹、捆绑共享 API 或重复 Provider 版本都会失败关闭。
- CMPP、SMGP、SGIP、SMPP 分别以独立插件发布,共享代码只存在 Next 内部的
provider-carrier-common构建模块;插件没有 Classic 源码或构建依赖,Release 流水线在上传前用 Agent 生产校验器重新验签。 - 插件更新采用 Stage → 原子替换 → Supervisor Restart → Health Verify;失败自动恢复旧包。运行中不热卸载 ClassLoader。
- Agent 持久化 Lease Fence、双向 Sequence、Event Batch 与 Run Completion。Service ACK 前数据不会从 Outbox 删除;重连后按 Fence/Sequence 重放。
- 同一权威 Lease Offer 重放只推进 Service Sequence,不重复启动 Run。进程重启发现曾处于 RUNNING 的 Lease 时按 Unknown/Unsent 安全语义生成可恢复 Completion,而不假定外部渠道 Exactly Once。
- Secret 只扫描冻结 Account/Message 中实际引用的
SecretRef;Service 使用一次性 X25519 + HKDF-SHA-256 + AES-256-GCM 生成绑定 Agent/Run/Lease/Epoch/Fence/过期时间的 Envelope。Agent 明文只存在运行内存并主动清零。 - Agent 的 Core
ArtifactSink先写 owner-only 临时文件并计算大小/SHA-256,再获取 Lease 绑定的上传计划;不超过 1 GiB 使用单次 Put,更大文件使用可重试 Presigned Multipart,Complete/Abort 后再 Commit;READY Artifact 引用进入RunCompleted。 - Enrollment Token 一次性消费并绑定 Workspace。Agent 生成 P-256 私钥,保存长期 Credential、客户端证书和 CA,在到期窗口内自动轮换。匿名 Agent 仅允许回环开发;非回环 HTTP/gRPC 必须认证,gRPC 强制 TLS,生产要求 mTLS。
4. Service、HA、安全与数据
- SQLite 开启 Foreign Key、WAL、Busy Timeout 和原子 Flyway 迁移;V10/V11 收口 Enrollment Workspace 和系统管理员,V12/V13 增加资源生命周期、关联重发和流式 Audience Import,并由迁移测试验证实际约束。
- PostgreSQL 使用 Hikari;相同有序迁移由 PostgreSQL 18 CI 实库验证。Server 模式启动校验 PostgreSQL、S3、API Security 和 Agent TLS 均已启用。
- Schedule Scanner 使用 PostgreSQL Session Advisory Lock 单 Leader;触发 Run 仍使用持久幂等键。
- Lease Offer 和 Run Command 先写
agent_message_outbox。任意 Service 扫描待发送消息,只有持有 Agent 当前 gRPC 流的实例实际投递,Agent ACK 后关闭消息。 - SSE 历史从
run_event回放;本实例实时发布,同时每个实例轮询有本地订阅者的游标,补偿其他实例提交的事件。 - PostgreSQL Server/HA 对新 Run、Agent Outbox 和 Run Event 使用三个固定
LISTEN/NOTIFYChannel 加速唤醒;通知丢失、断线或重复时仍由周期扫描、持久 Outbox 和事件游标保证推进。 - Workspace Policy 限制 Agent 数量、活动 Run、总发送并发、Artifact 总量和默认保留期;账号认证失败在 V16 事实表中跨 Run 累积并熔断,冷却到期或管理员复位后恢复。
- API Token 只保存 SHA-256,明文仅签发时返回一次。VIEWER/OPERATOR/ADMIN 按 Workspace 授权;Bootstrap
SYSTEM_ADMIN只承担全局治理。启用安全时按系统管理员是否存在而非“任意 Token 是否存在”初始化 Bootstrap,避免先创建普通 Token 后锁死全局治理。Token 与 Enrollment 管理 API 进入 Workspace 路径,普通 ADMIN 无法跨 Workspace 签发、枚举或吊销;写操作和拒绝结果进入审计日志。 - 无认证开发模式只能绑定回环地址;HTTP 对外监听强制 API Security,Server 模式进一步强制 PostgreSQL、S3 与 Agent gRPC TLS,误配置时失败关闭。
- Workspace 有正式列表/创建/详情 API。Account、Secret、Message、Audience、Job、Schedule、Run、Artifact、API Token 与 Agent Enrollment Binding 均显式带 Workspace 边界;旧的未绑定开发 Agent 只兼容
ws_default。 - 默认且正式的 Secret Store 为 Local Envelope:随机 DEK + AES-256-GCM + 主密钥封装。主密钥来自 owner-only 文件或显式注入;密文存在而主密钥丢失/权限不安全时失败关闭。
SecretStorePort 用于模块隔离和测试,官方产品不规划 OS Keychain、Vault、云 KMS 或 Secret Manager 形式的 Service Adapter。 - Local Artifact 采用受控路径、临时写、fsync、SHA-256 与原子移动。S3 Store 支持 Put/Get/Range/Head/Delete、Presigned Put、服务端与 Agent Multipart、Complete/Abort、原生 AES256 和对象元数据校验;不包含云 KMS 集成。
- Artifact 状态是
UPLOADING → READY → DELETING → DELETED,失败进入FAILED;TTL 清理尊重 Pin/Legal Hold。对象存储 Lifecycle 是兜底,不替代数据库事实源。 - Actuator 暴露 Health/Readiness/Prometheus,指标带稳定 application 标签;Server HAProxy 对 Readiness 做后端摘除。
5. API、SDK 与 UI
- OpenAPI 3.1 覆盖资源编辑/分页、Audience Import、发送确认/重发/总览、Schedule/Security/Workspace 与 Agent Internal Enrollment/Lease/Artifact API;全局 Bearer Security 和内部自定义认证显式声明。
- Maven 契约测试拒绝 YAML 重复键、重复/缺失
operationId、无 Response 和不可解析的本地$ref。 - Remote Java SDK 只有
service-api依赖,绝不依赖 Core/Engine/Provider;支持签名分页、资源编辑、Schedule CRUD、发送确认/重发、总览和使用文件 BodyPublisher 的流式 multipart Audience 上传。 - Embedded Java SDK 依赖 Core API、Provider SPI 和 Engine,但不依赖 Service、Agent、Spring 或具体 Provider;应用显式注册允许的 Provider,并选择共享或按 Run 创建的 Sink。
- TypeScript Client 使用可更新 Bearer Token;SSE 使用自定义 Fetch Parser,因此 Server 安全模式不受原生
EventSource无法设置 Authorization Header 的限制。 - WebUI 接入 Account/Message/Audience/Job/Run/Artifact 全链路、资源编辑/复制/状态、修订历史/Diff、CSV/TXT 导入、Schedule 完整编辑、发送确认/重发、Workspace 选择、API Token、Agent Enrollment、审计与动态 API 调试;Schema Renderer 支持本地
$ref和嵌套 SecretRef 默认示例。 - Desktop 主进程保持
contextIsolation=true、nodeIntegration=false,只暴露固定 IPC:本机 Service 状态/启停/日志/诊断、签名插件 Stage/Activate/Rollback 和 API TokensafeStorage。渲染页不能执行任意命令;浏览器 Token 仅使用sessionStorage。
6. 当前数据库事实源
在原有 Workspace、Account、Message Revision、Audience Snapshot、Job、Run Snapshot/Event/Result/Command、Secret 和 Artifact 表之外,当前迁移还包含:
| 表 | 用途 |
|---|---|
agent_registration |
Agent 当前会话、平台、Provider、容量、Sequence、X25519 公钥 |
agent_lease |
Run/Agent 归属、Epoch、Fencing Token、Event Cursor 和恢复状态 |
agent_message_outbox |
跨 Service 实例的 Lease Offer/Run Command 持久消息 |
agent_enrollment_token |
一次性 Enrollment、Workspace、过期和消费状态 |
agent_credential |
哈希长期 Credential、证书指纹、轮换/撤销/使用时间 |
agent_workspace_binding |
Agent 与可执行 Workspace 的正式绑定 |
api_principal / api_token / role_binding |
API 身份、Token 和 Workspace RBAC |
audit_event |
控制面安全与变更审计 |
schedule_definition |
Cron、时区、Misfire、启停和下次触发时间 |
audience_import_session / audience_import_row |
流式文件导入会话、预览计数、数据库去重和错误行 |
run_retry_item |
关联重发 Run 的精确 Item 集合;来源与状态同时保存在 run_instance |
workspace_policy |
Workspace 的 Agent、Run、发送并发、Artifact 容量和默认保留期策略 |
account_auth_circuit |
账号跨 Run 的认证失败计数、熔断状态、冷却时间和管理员复位状态 |
artifact_multipart_upload |
Agent Presigned Multipart 会话、大小、摘要、Part 规划、到期和状态 |
7. 验证与发布门禁
本地/常规门禁:
./mvnw verify:Core、Provider、Agent、Service、SDK、架构和纵向集成测试。pnpm check:全部 Workspace TypeScript、Vitest、Web Vite 构建与 Desktop TypeScript 构建。- 标准渠道测试使用本地 SMTP/HTTP mock 验证真实协议、签名、错误映射、Token 缓存/刷新和 Dry Run;Engine 纵向测试逐一执行全部 8 个标准渠道。
- SQLite 空库迁移断言真实列和 Foreign Key,而不只相信 Flyway 版本号。
- 真实 gRPC 纵向链路覆盖 Hello/Welcome、Lease Ack、受保护文档、Secret、Command Ack、Event 去重、Agent Artifact 上传/Commit、Run Completion。
- 插件测试覆盖有效签名、未知签名者、Zip Slip 和空目录。
- 安装脚本通过 POSIX shell 语法、PowerShell AST、launchd plist、WinSW XML 与 Compose 配置校验;三平台自测实际覆盖备份内容摘要、完整恢复和强制升级失败回退。
- 浏览器 Playwright E2E 覆盖 Standalone/Provider/会话 Token;三平台打包后启动 Desktop 冒烟;定时矩阵在 Java 25、SQLite/PostgreSQL 上执行长稳健康检查。
- 发行归档生成系统 Java 精简包和含 Runtime 的平台完整包,并检查离线 WinSW、Service/Agent/WebUI/安装/恢复脚本及 Remote/Embedded Java SDK 均存在。
.github/workflows/next-ci.yml 另外使用真实 PostgreSQL 18 和固定 MinIO 版本验证:
- PostgreSQL 从 V13 用户数据迁移到 V17,并验证 Workspace Policy、认证熔断、Multipart 表、控制面通知和已有数据;
- S3 Presigned Put、Presigned Multipart Complete/Abort、Range、Head/Checksum、Delete 和 100 MiB Service Multipart;
- Java/UI 门禁通过后才生成发行归档。
next-v*Tag 由独立发行流水线生成三平台未签名 Desktop 包、CycloneDX SBOM 和统一SHA256SUMS;无后缀版本发布为稳定 GitHub Release,带后缀版本才标记为 Pre-release。
8. 稳定版完成边界与后续演进
1.2.0 在 1.1.0 上完成兼容增量:不新增 Flyway 迁移,不改变 REST/SSE 与 Agent 协议;Codex MCP/Skill 复用既有 API、确认令牌和 Workspace 权限。Java 基线为 25,完整包携带对应运行时。1.1.0 已完成的 Provider 插件、资源治理、诊断、可靠性、大 Artifact 与 UI 门禁继续有效。
以下属于 1.x 内可兼容增加的产品增量,不是稳定版缺口:
- 以独立签名插件继续增加其他短信、推送和运营商协议,不扩大内置核心依赖。
- 在兼容策略范围内增加可选 API、运维诊断和用户自建部署模板。
- 更新始终由用户主动触发;macOS/Windows 应用保持未使用商业代码签名,并继续以 Release、SHA-256、SBOM 和明确告知提供完整性证据。
公共 SaaS、自助注册、计费订阅、公共市场、云 KMS/Secret Manager、恶意公共租户物理隔离、跨区域 Active-Active、强制自动更新和遥测回传属于长期非目标,不进入后续产品增量。
路线图内的增量继续遵守 Classic/Next 双轨独立、Remote Java SDK 不依赖 Core、Embedded Java SDK 不依赖 Service、Workspace 显式隔离、Lease Fencing、Secret 最小暴露和 Artifact 完整性边界。
Next Docs Product Scope And Roadmap (`next/docs/product-scope-and-roadmap.md`)
WePush Next 产品目标、边界与路线图
- 文档状态:已确认产品方向
- 更新日期:2026-08-29
- 适用范围:
next/
1. 产品定位
WePush Next 是一套开源、可下载、可安装、可由用户自行部署和运维的消息推送产品。项目的目标是让个人、团队或组织在自己控制的计算、网络、数据库和存储环境中运行 WePush,并自行掌控账号、消息、受众、运行结果、Secret 和 Artifact。
WePush 项目不运营承载用户业务数据的官方集中服务,也不把公共云平台化作为商业或技术演进方向。Standalone、用户自建 Server/HA、远程 Agent、Remote Java SDK 和 Embedded Java SDK 都服务于同一个目标:让用户在自己的环境中完成安装、集成、发送、升级、备份和恢复。
本文档是 WePush Next 产品范围和迭代优先级的主文档。架构、详细设计、ADR、实现状态和部署文档中的范围描述必须与本文一致。
2. 产品原则
2.1 用户掌控
- 用户自行选择部署主机、网络入口、数据库、对象存储和备份位置。
- 用户数据和密钥默认保存在用户控制的环境中。
- 默认安装不依赖 WePush 官方在线控制面、账号系统或托管后端。
- 除用户配置的消息 Provider、远程 Agent、对象存储和数据库连接外,运行时不产生非必要外部网络请求。
- 不采集或上传业务数据、受众数据、Secret、运行结果和使用遥测。
2.2 可安装、可升级、可恢复
- Standalone 必须保持低依赖、低门槛和开箱可用。
- Server/HA 必须提供清晰的用户自建参考拓扑,而不是依赖官方托管服务。
- 每次升级必须以可验证备份、版本化迁移、健康检查和明确回滚边界为前提。
- 安装、升级、备份、恢复和卸载都属于正式产品能力,不是文档外的运维责任。
2.3 单一管理信任域
- Workspace 用于同一个自建实例内的团队、资源和权限分组。
- Workspace RBAC、审计和数据 Scope 是正式安全边界。
- WePush 不把 Workspace 定义为公共 SaaS 租户,不承诺对互不信任或恶意租户提供进程级、数据库级或基础设施级物理隔离。
2.4 可控发布
- 用户决定何时下载和安装新版本,不进行强制自动更新。
- 可以提供手动触发或显式启用的版本检查,但不得成为运行前提。
- Provider 通过发行包或用户本地安装的签名插件交付,不建设官方集中插件市场。
3. 正式产品范围
以下能力属于 WePush Next 的长期正式范围:
- Windows、Linux 和 macOS Standalone 安装与便携运行。
- SQLite、本地 Artifact Store 和本地信封加密 Secret Store。
- 用户自建 PostgreSQL、S3-compatible Artifact Store、多 Service 和远程 Agent。
- WebUI、Desktop UI、REST/SSE API、OpenAPI、Remote Java SDK 和 Embedded Java SDK。
- Account、Message、Audience、Job、Schedule、Run、Result、Artifact 和审计管理。
- 邮件、短信、微信、企业 IM、HTTP 等 Provider 及签名插件扩展机制。
- 并发、限流、重试、暂停、恢复、取消、幂等、结果未知和失败重发语义。
- Workspace 逻辑隔离、VIEWER/OPERATOR/ADMIN 权限和 Agent 授权。
- TLS/mTLS、Secret 最小暴露、Artifact 完整性、安装包来源/校验和/SBOM 和明确签名状态;应用商业代码签名不是发布前提。
- 用户可执行的升级、备份、恢复、回滚、日志、指标和故障诊断。
PostgreSQL、S3-compatible Store 和多节点 HA 是可选的用户自建形态,不是使用 WePush 的前置条件。S3-compatible 只表示协议兼容,可以由用户在本地或私有环境中运行。
4. 明确且长期不做
以下能力不进入 WePush Next 路线图,也不能以“后续扩展”名义隐式引入:
- WePush 官方公共 SaaS、集中控制面或承载用户业务数据的托管平台。
- SaaS 用户自助注册、组织开户、计费、套餐、订阅、账单和支付。
- 公共租户市场、公共 Provider 市场或依赖官方账号的插件分发。
- 面向互不信任或恶意租户的进程级、数据库级、主机级物理隔离。
- 为每个 Workspace 自动创建独立数据库、集群或云资源。
- 跨区域 Active-Active 公共控制面和全球租户调度。
- AWS KMS、其他云 KMS、云 Secret Manager、HashiCorp Vault 或操作系统凭据库形式的 Service Secret Store 官方适配器。
- 强制自动更新、未经用户同意的远程配置下发和使用遥测回传。
SecretStore、数据库和 Artifact Store 保持端口抽象,是为了模块边界、测试和本地实现可替换性,不表示项目计划建设云厂商适配层。Service 的正式 Secret 方案是本地信封加密,Master Key 使用用户控制的受保护文件或显式注入。对象存储建议使用存储端原生 AES256 或由部署方自行管理的存储安全能力;WePush 不负责对接或管理云 KMS。
如果未来有人提出上述能力,应视为改变产品定位,而不是普通功能迭代;在当前产品方向下直接拒绝进入路线图。
5. 当前基线
1.0.0 已形成独立构建的稳定自部署产品:
- Core API、Provider SPI、虚拟线程 Engine,以及 HTTP、SMTP Email、三类群机器人、阿里云短信、微信公众号、小程序和企业微信应用消息 Provider。
- Standalone Service、SQLite、Local Artifact、Local Envelope Secret Store。
- PostgreSQL/S3-compatible Server/HA 参考拓扑和远程 Agent。
- REST/SSE、OpenAPI、Remote Java SDK 和 Embedded Java SDK。
- React WebUI、Electron Desktop 和三平台安装脚本。
- Agent Enrollment、Lease/Fencing、Secret Envelope、Artifact 上传和断线恢复。
- Account/Message/Audience/Job/Schedule 的编辑、修订、复制、启停与归档。
- CSV/TXT 流式受众导入、正式发送二次确认、失败项关联重发、分页筛选与真实总览。
- 三平台含 Java Runtime 完整包、系统 Java 精简包、统一 Standalone/高级分组件安装和离线 Windows Service Wrapper。
- 正式 Backup/Restore、Payload 文件集合与逐文件内容校验、升级 Installation Health 与失败自动回退。
- Desktop 本机 Service 运维、原生 Token 安全存储,以及签名插件 Stage/Activate/Rollback。
- 1.x REST/SSE、Java SDK、配置、数据库迁移、Provider SPI 和 Agent 协议兼容承诺。
- V13→V14 无损迁移、Beta 成功升级/失败回退、100,000 Recipient 流式执行和跨平台故障门禁。
- 浏览器 E2E、Desktop 冒烟、三平台恢复/失败升级和 Java 25 长稳矩阵。
当前稳定基线不依赖商业代码签名,不需要也不会扩大公共平台能力。后续 1.x 只在兼容边界内改进自部署体验、可靠性和 Provider 生态。
6. 迭代路线图
版本名称用于表达建议发布边界;如果实际版本号调整,里程碑内容和验收条件保持不变。
6.1 `0.1.0-alpha.2`:方向收口与当前成果发布
目标:在不扩大功能范围的前提下,修正产品边界、安全问题和发布元数据,发布当前已经完成的 Embedded Java SDK。
计划内容:
- 同步本文档所定义的产品边界到 README、架构、详细设计、ADR、实现状态和部署文档。
- 清理现有 AWS KMS 配置和实现路径,S3-compatible Store 保留原生 AES256 配置。
- 修复安全模式下 Artifact 下载未携带 Bearer Token 的问题,使用认证请求下载文件。
- 将 Embedded Java SDK、Core、Provider SPI、Engine 和 HTTP Provider POM/JAR 纳入正式发行附件。
- 更新版本号、OpenAPI、CHANGELOG、Release Notes 和 Preview Notice。
- 通过 Java、UI、SQLite、PostgreSQL、MinIO、三平台 Desktop 和发行归档门禁。
验收条件:
- 文档不再把云 KMS、外部 Secret Manager 或公共 SaaS 描述为未来产品增量。
- 安全模式下可以完成 Run、生成结果导出并成功下载 Artifact。
alpha.2发行附件包含 Remote 和 Embedded 两种 Java SDK。- Standalone 默认不依赖任何官方在线服务。
6.2 `0.1.0-alpha.3`:日常使用闭环
目标:让普通用户不编写 JSON、不直接调用 API,也能完成完整发送和结果处理。
状态:已完成(2026-08-27)。
已交付内容:
- Account 编辑、连接测试、启停和归档。
- Message 编辑并生成新 Revision、历史查看、Diff 和复制。
- Audience CSV/TXT 流式导入、字段映射、预览、去重、错误行下载和新 Snapshot。
- Job 编辑、复制、启停和归档;Schedule 支持完整编辑。
- 正式发送前展示 Provider、账号、受众数量、策略、并发、限速和预计执行规模,并要求二次确认。
- 从 FAILED、UNKNOWN 和 UNSENT Item 创建有来源关联的新 Run。
- Run、资源和审计日志增加游标分页、名称、状态和时间筛选。
- 总览、活动 Run、最近运行和趋势全部接入真实数据。
- Standalone 隐藏不必要的 Workspace 复杂度;Server UI 支持真实 Workspace 选择。
验收结果:
- 集成测试已覆盖 UI 背后的 CSV 导入、去重、错误行下载、Dry Run、正式确认发送、结果查看和失败项重发 API 链路。
- 集成测试直接断言 Message/Audience 更新后,既有 Run Snapshot 仍保存原内容和原 Audience Snapshot ID。
- Run、资源、Schedule 和审计使用最多 100 条的签名 Cursor Page;启动恢复也按 100 条批次扫描,不再无界载入历史 Run。
6.3 `0.1.0-alpha.4`:真实消息渠道
目标:在 HTTP Provider 之外建立可实际使用的消息渠道组合。
状态:已完成(2026-08-28)。
已交付内容:
wepush.email.smtp:无认证或账号认证 SMTP,支持 NONE/STARTTLS/TLS、文本/HTML Multipart、Reply-To、CC/BCC。wepush.bot.feishu、wepush.bot.dingtalk、wepush.bot.wecom:固定官方 Webhook、厂商签名、常用消息类型、Raw JSON 和本地安全限流。wepush.sms.aliyun:用户自有 AccessKey、POP HMAC-SHA1 签名、模板变量、OutId追踪和业务错误映射。wepush.wechat.official、wepush.wechat.mini、wepush.wecom.app:用户自有 App/Corp 凭据、Session Token 缓存、失效后单次安全刷新,以及任意受支持业务 Payload。- 全部 Provider 随 Service、Agent 和发行包内置;Embedded SDK 仍要求调用方显式选择 Provider。
- WebUI 通过实时 Schema 渲染 SecretRef 和消息默认示例;配置、Recipient 和最小验证步骤见《内置 Provider 指南》。
验收结果:
- 每个 Provider 都提供 Account、Message、Recipient JSON Schema、SecretRef、结构化字段校验、Dry Run 和真实发送。
- Provider 模块使用本地 SMTP/HTTP mock 覆盖协议、签名、Token 刷新、错误分类、响应上限和 Secret 不解析 Dry Run;不依赖真实第三方账号。
- Engine 纵向测试通过 ServiceLoader 发现并逐一执行 8 个标准渠道;Service 和 Agent 测试断言完整 9 Provider Catalog。
- 重试、幂等、限流、未知结果和 Provider Code 映射已按渠道记录;提交后不确定失败不会自动重发。
- 生产实现固定厂商官方端点,只有包内测试构造器允许回环 mock;远端响应和带 Token URL 不进入诊断。
Classic 与 Next 可以复用业务需求、测试数据和验收经验,但不建立共享源码依赖。
6.4 `0.1.0-beta.1`:自部署运维成熟
目标:让用户能够长期、可预测地安装和维护 WePush,而不依赖项目维护者远程介入。
状态:已完成(2026-08-28)。
已交付内容:
- 提供一体化 Standalone 安装体验,并保留便携包和高级分组件安装。
- 提供包含 Java Runtime 的完整发行包,同时保留使用系统 Java 的精简包。
- Windows 发行包离线携带经过校验的 Service Wrapper,不在安装时临时下载。
- Desktop 检测本地 Service,并提供启动、停止、日志和连接诊断入口。
- 增加正式 Restore 工具和备份内容校验。
- 升级后自动执行 Readiness、数据库版本和最小 Dry Run 验证;失败时切回旧版本并给出恢复指引。
- UI 支持本地上传、验证、Stage、Activate 和 Rollback 签名 Provider 插件。
- Desktop 使用本机安全存储保存 UI Token;浏览器默认只在会话范围保存 Token。
- 增加浏览器 E2E、Desktop 冒烟、安装/升级/恢复和长时间运行测试矩阵。
- 清理 JDK 未来兼容警告、Shade 重复资源和发行依赖技术债。
验收条件:
- Windows、Linux 和 macOS 均能在文档支持的离线条件下完成 Standalone 安装。
- 从上一预览版本升级失败时,用户数据不丢失并可恢复旧版本运行。
- 备份恢复后数据库、Master Key、Artifact、Agent Identity、Journal 和 Outbox 一致。
验收结果:
- Release 流水线在 Linux、macOS、Windows 分别用 JDK 25
jlink生成完整包;精简包保留系统 Java 路径,Windows 两种包都离线携带固定摘要的 WinSW 2.12.0。 - 统一安装入口在安装后检查 Installation Health;该健康组同时断言 Flyway 当前版本和内置 HTTP Provider 的本地无网络 Dry Run。
- 三平台运维自测创建代表数据库、Master Key、Artifact、Agent Identity、Journal、Event/Completion Outbox 和插件的快照,验证文件集合/逐文件摘要、完整恢复,以及强制新版本健康失败后旧版本/数据回退。
- Desktop IPC 只允许固定的服务、插件和安全存储操作;浏览器 E2E 断言 Token 不进入
localStorage,Desktop 三平台包执行启动冒烟。 - Java 25 × SQLite/PostgreSQL 的定时长稳矩阵,以及 JDK Native Access 和 Shade 重复资源清理进入发行门禁。
6.5 `1.0.0`:稳定发行
目标:对自部署用户提供明确的兼容性、安全和运维承诺。
状态:已完成(2026-08-29)。
已交付内容:
- 明确 API、配置和数据库迁移兼容策略。
- 至少支持从最近一个 Beta 无损升级,并记录最低可回滚版本。
- 三平台安装、升级、备份、恢复和卸载自动化验收通过。
- 大受众、长时间任务、断网重连、磁盘不足、进程崩溃和 Agent 失联测试通过。
- 核心 Provider 具备稳定的限流、幂等、错误分类和重试语义。
- macOS 和 Windows 发行物明确保持未使用商业代码签名;通过项目 GitHub Release、统一 SHA-256、SBOM 和未签名告知建立可验证交付,更新仍由用户主动触发。
- 默认运行不存在未声明的外部网络依赖或数据回传。
- 安全、部署、升级、恢复、Provider 开发和用户指南完整且相互一致。
验收结果:
0.1.0-beta.1OpenAPI 忽略版本号后保持精确一致,Beta 配置键逐项进入自动兼容门禁。- SQLite 和 PostgreSQL 都从 V13 创建用户数据后迁移到 V14,并验证原数据、版本和兼容元数据。
- Linux、macOS、Windows 运维自测覆盖成功升级、失败自动回退、完整恢复、默认卸载保留数据和显式 Purge。
- Core 自动执行 100,000 Recipient 流式任务;Agent 测试覆盖断网重放、进程崩溃后的 UNKNOWN、Outbox 上限和模拟磁盘写失败。
- Java 25 × SQLite/PostgreSQL 长稳、浏览器 E2E、三平台 Desktop 启动和发行归档验证进入 CI/Release 门禁。
6.6 `1.1.0`:自部署治理与 Provider 生态
目标:在保持 1.x 兼容承诺、默认离线和用户自建边界的前提下,增强运营商短信接入、Server 资源治理、故障诊断、HA 唤醒、大 Artifact 和 WebUI 使用体验。
状态:已完成(2026-08-30)。
已交付内容:
- 将 CMPP、SMGP、SGIP 和 SMPP 实现为独立签名 Provider 插件;可以参考 Classic 的业务需求和测试经验,但不建立源码或构建依赖。
- 增加 Workspace 级 Agent 数量、并发 Run、总发送并发、Artifact 容量和默认保留期策略,并在 API、SDK、WebUI、调度与运行入口执行一致门禁。
- 提供可下载的脱敏诊断包、更多用户自建部署模板,以及默认关闭、仅由用户手动触发的版本检查。
- 增加跨 Run 的账号认证失败熔断;在 PostgreSQL Server/HA 中以
LISTEN/NOTIFY优化新 Run、Agent 命令和 SSE 事件唤醒,同时保留数据库轮询和 Outbox 作为正确性事实源。 - 将 Agent Artifact 上传从单次 Presigned Put 扩展为可恢复的 Presigned Multipart Plan;原有单次上传契约继续兼容。
- 增加暗色主题,改善低分辨率布局、键盘操作、可见焦点、语义标签和颜色对比。
验收条件:
- 四种运营商协议均有独立插件包、协议编解码、连接测试、Dry Run、错误分类、边界校验和本地模拟网关测试;未安装插件时不增加 Service/Agent 核心依赖。
- Workspace 配额在单 Service 和多 Service 竞争下都不会超发;拒绝结果可诊断且进入审计,策略更新保持向后兼容。
- 诊断包不包含 Token、Secret、完整 Recipient 或未脱敏 Provider 响应;版本检查不会后台运行或回传遥测。
- 跨 Run 熔断可自动恢复和由管理员安全复位;丢失 PostgreSQL 通知时轮询仍能推进所有状态。
- Agent 可上传超过 1 GiB 的 Artifact,支持分片重试、Complete/Abort、SHA-256/大小校验和进程失败清理。
- WebUI 在亮色/暗色、1366×768 和键盘操作下通过自动化与人工验收,关键文本和控件满足既定对比度要求。
- OpenAPI、Remote Java SDK、配置、数据库迁移和 Agent 协议变更满足 1.x 兼容策略,并通过 Java、UI、SQLite、PostgreSQL、MinIO、Desktop 与发行归档门禁。
验收结果:
- 四种运营商协议均以独立 Maven/PF4J 模块构建,签名包由 Agent 生产校验器验签;本地网关测试覆盖登录、短短信和中文长短信,核心发行物不增加这些协议依赖。
- V15 Workspace Policy 在 Agent 注册、Run 创建/领取、总发送并发、Artifact 创建和保留期清理入口执行;API、SDK 和 WebUI 提供一致的查看与更新能力。
- 脱敏诊断、手动版本检查、Nginx/Traefik/Kubernetes 模板、V16 跨 Run 认证熔断及管理员复位已经交付。
- PostgreSQL 对待调度 Run、Agent Outbox 和 Run Event 发布通知,所有订阅者继续保留周期扫描、持久 Outbox 和事件游标恢复。
- V17 记录 Multipart 会话;Agent 支持分批获取 Part URL、重试、Complete/Abort、完整性检查与孤儿清理,S3 单对象上限为 5 TiB。
- WebUI 的主题、低分辨率、键盘焦点、语义和减少动画进入类型检查、组件测试、构建与浏览器 E2E 门禁。
6.7 `1.2.0`:AI 助手接入与 Java 25
目标:在不改变 1.x HTTP、数据库和 Agent 协议的前提下,让用户用自己的 AI 客户端操作已部署的 WePush,并把运行基线统一到 Java 25。
状态:已完成(2026-09-26)。
已交付内容:
- Desktop 一键安装 Codex MCP 与 Skill,WebUI 和发行包提供同一个
wepush-ai.mjs。桥接器由 AI 客户端以 stdio 启动,只调用用户自己的 REST API。 - 查询、创建草稿、Dry Run、预览确认后的正式发送,以及结果查询和运行控制。正式发送继续要求确认令牌、幂等键和 Workspace 权限;安装器不复制登录 Token。
- Java 构建与运行基线升级到 25。完整包携带 Java 25 运行时;精简包和 Java SDK 要求系统 Java 25+。
验收结果:
- 不新增 Flyway 迁移,OpenAPI 在忽略版本号后保持既有兼容摘要。
- AI 安装、连接和工具调用进入安装器测试;发行归档包含
ai/wepush-ai.mjs。 - 浏览器 E2E 覆盖设置页的 AI 助手接入入口。
7. 优先级规则
后续任务使用以下顺序决策:
- 数据安全、发送正确性和可恢复性问题优先于新增功能。
- 安装、升级、备份和恢复优先于公共平台能力。
- 完整用户闭环优先于一次增加大量 Provider。
- 每个 Provider 的可验证质量优先于渠道数量。
- Standalone 简单体验优先,同时保持用户自建 Server/HA 的清晰路径。
- 默认离线、自包含和无遥测优先于依赖外部控制服务的便利性。
8. 完成定义
路线图中的功能只有同时满足以下条件才算完成:
- 有明确的用户场景、权限行为和失败语义。
- API、Schema、SDK 和 UI 同步更新。
- 单元、契约、集成或 E2E 测试覆盖与风险匹配。
- 不泄露 Secret、受众数据和敏感 Provider 响应。
- Standalone 不增加未声明的在线依赖。
- Server/HA 仍可由用户在自己的环境中部署和恢复。
- 安装、升级和数据迁移影响已经记录。
- 用户指南、部署文档、实现状态和 CHANGELOG 同步更新。
9. 规划维护
- 每次版本发布后更新本文件的完成状态和下一里程碑。
- 新功能进入实现前必须确认它属于第 3 节正式范围,且不违反第 4 节长期非目标。
- 产品范围与其他文档冲突时,先按本文纠正文档,再开始实现。
- 任何会把 WePush 变成官方托管公共平台的提案,均视为产品定位变更,不进入普通需求评审。
Next Docs Provider Guide (`next/docs/provider-guide.md`)
WePush Next 内置 Provider 指南
本文适用于 1.2.0,说明内置 Provider 的最小配置、Recipient 字段、发送语义和故障处理。所有渠道都连接用户自己的服务或渠道账号;WePush 不代注册账号、不转售消息额度、不代收款,也不把凭据上传到官方公共平台。CMPP、SMGP、SGIP、SMPP 不属于内置 Provider,而是独立签名 Agent 插件;协议能力、构建和安装见 plugins/README.md及《部署与运维》。
1. 共同行为
Service 和 Agent 随发行包内置以下 Provider:
| Provider ID | 渠道 | 实现版本 |
|---|---|---|
wepush.http |
通用 HTTP | 0.1.0 |
wepush.email.smtp |
SMTP Email | 0.1.0 |
wepush.bot.feishu |
飞书 / Lark 群机器人 | 0.1.0 |
wepush.bot.dingtalk |
钉钉群机器人 | 0.1.0 |
wepush.bot.wecom |
企业微信群机器人 | 0.1.0 |
wepush.sms.aliyun |
阿里云短信 | 0.1.0 |
wepush.wechat.official |
微信公众号 | 0.1.0 |
wepush.wechat.mini |
微信小程序 | 0.1.0 |
wepush.wecom.app |
企业微信应用消息 | 0.1.0 |
Provider 实现版本与 WePush 产品版本独立。创建 Account、Message 或 Run 时,应使用 Provider Catalog 返回的精确版本。
1.1 SecretRef
密码、Webhook URL、签名密钥、AccessKey Secret、AppSecret 和 CorpSecret 必须保存到当前自部署实例的 Secret Store,并在 Account 中只写引用:
{"namespace":"smtp","name":"password","version":"v1"}本地 Secret API 路径为 /api/v1/workspaces/{workspaceId}/secrets/{namespace}/{name}/versions/{version}。响应、日志、Provider metadata 和错误诊断都不返回 Secret 明文或带令牌的完整远端响应。
1.2 模板、Dry Run 与重试
- 文本字段使用
{{fieldName}}引用 Recipient 字段;JSON 模板中的变量应放在 JSON 字符串内。 - 所有本页渠道支持 Dry Run。Dry Run 会完成配置、Recipient、模板和 Payload 校验,但不会解析 Secret、获取 Access Token 或访问网络。
- 除明确声明外,外部渠道不提供 Exactly Once。Engine 只重试 Provider 明确标记为 retryable 的确定失败;提交后发生超时、连接中断或服务端
5xx时结果记为UNKNOWN,不会自动重发,以避免重复消息。 idempotencyKey只是运行追踪信息。阿里云短信会把最多 64 个字符写入OutId;它不等同于服务端去重。企业微信应用消息默认启用渠道自身的短窗口重复检查。- Provider Descriptor 的最大并发是安全上限,Job 的并发和速率策略仍应根据用户自己的渠道配额进一步收紧。
2. SMTP Email
最小 Account:
{
"host":"smtp.example.com",
"port":587,
"security":"STARTTLS",
"username":"[email protected]",
"password":{"namespace":"smtp","name":"password","version":"v1"},
"fromAddress":"[email protected]",
"fromName":"WePush"
}不需要认证的内网 SMTP 必须同时省略 username 和 password。security 可取 NONE、STARTTLS、TLS;不要在不受信网络使用 NONE。
Message 与 Recipient:
{"subjectTemplate":"Hello {{name}}","textBodyTemplate":"Welcome {{name}}","htmlBodyTemplate":"<strong>Welcome {{name}}</strong>"}{"email":"[email protected]","name":"Alice"}同时提供文本和 HTML 时发送 multipart/alternative。连接测试会执行 SMTP 握手和认证,但不发送邮件。Provider 使用单一串行 Transport,避免同一 SMTP 会话被并发破坏;服务端拒绝 Recipient 归类为 RECIPIENT_INVALID,认证失败不重试,连接建立前失败可重试,提交后 I/O 不确定记为 UNKNOWN。SMTP 不提供幂等保证。
3. 群机器人
三个机器人都把完整 Webhook URL 存为 SecretRef,生产实现只接受各厂商官方 HTTPS 域名和路径,从而避免把 Secret 或消息转发到任意主机。
3.1 飞书 / Lark
{
"webhook":{"namespace":"feishu","name":"webhook","version":"v1"},
"signingSecret":{"namespace":"feishu","name":"signing-secret","version":"v1"},
"keyword":"optional-keyword"
}{"type":"TEXT","contentTemplate":"Build {{build}} completed","mentionField":"openId"}支持 TEXT、POST、INTERACTIVE 和 RAW;INTERACTIVE / RAW 使用 rawJsonTemplate。Recipient 可提供 openId。本地安全限流为每秒 5 次且每分钟 100 次,Payload 上限 20 KiB。签名按飞书自定义机器人协议生成。参见飞书自定义机器人文档。
3.2 钉钉
{
"webhook":{"namespace":"dingtalk","name":"webhook","version":"v1"},
"signingSecret":{"namespace":"dingtalk","name":"signing-secret","version":"v1"}
}{"type":"MARKDOWN","titleTemplate":"Build {{build}}","contentTemplate":"Deployment completed","mentionField":"mobile"}支持 TEXT、MARKDOWN、LINK、ACTION_CARD 和 RAW。Recipient 可提供 mobile。本地安全限流为每分钟 20 次,Payload 上限 20 KiB。启用加签时,时间戳和 HMAC-SHA256 签名只在请求时生成。
3.3 企业微信群机器人
{"webhook":{"namespace":"wecom-bot","name":"webhook","version":"v1"}}{"type":"TEXT","contentTemplate":"Hello {{name}}","mentionField":"mobile"}支持 TEXT、MARKDOWN 和 RAW,Recipient 可提供 mobile。本地安全限流为每分钟 20 次,Payload 上限 20 KiB。企业微信群机器人不使用独立 signingSecret。
机器人 HTTP 429 或业务响应中的频率限制归类为 RATE_LIMITED;签名、Token 或 Key 被拒绝归类为认证错误;提交后超时、I/O 或 5xx 记为 UNKNOWN。机器人渠道不提供幂等保证。
4. 阿里云短信
Account:
{
"accessKeyId":"LTAI...",
"accessKeySecret":{"namespace":"aliyun","name":"access-key-secret","version":"v1"},
"regionId":"cn-hangzhou"
}建议为自建 WePush 实例创建最小权限 RAM 用户,不要使用主账号 AccessKey。生产端点固定为 https://dysmsapi.aliyuncs.com/。
Message 与 Recipient:
{
"signName":"你的短信签名",
"templateCode":"SMS_123456789",
"templateParamJsonTemplate":"{\"name\":\"{{name}}\",\"code\":\"{{code}}\"}"
}{"phoneNumber":"13800138000","name":"Alice","code":"7312"}Provider 使用 SendSms 2017-05-25 RPC API 和 HMAC-SHA1 POP 签名。连接测试只验证 Secret 存在和官方端点可达,不消耗短信,也不代表签名、模板或余额一定可用;首次正式发送前必须对测试号码做小范围验证。
MOBILE_* 等号码错误归类为 RECIPIENT_INVALID,模板/签名/参数错误归类为 INVALID_REQUEST,AccessKey/签名错误归类为认证错误,权限错误归类为授权错误,业务频控归类为 RATE_LIMITED。OutId 只用于关联追踪,不承诺去重。参见阿里云发送短信 API和阿里云 RPC 签名机制。
5. 微信公众号与小程序
两者 Account 结构相同,appSecret 必须使用 SecretRef:
{
"appId":"wx...",
"appSecret":{"namespace":"wechat","name":"app-secret","version":"v1"}
}公众号支持:
TEMPLATE:模板消息;SUBSCRIBE:订阅通知;CUSTOM:客服消息。
小程序支持:
SUBSCRIBE:订阅消息;UNIFORM:统一服务消息。
Message 的 payloadJsonTemplate 是各微信接口的业务 JSON,Provider 会覆盖 touser,因此不要把 Access Token 或目标 OpenID 写进模板:
{
"type":"TEMPLATE",
"payloadJsonTemplate":"{\"template_id\":\"TEMPLATE_ID\",\"data\":{\"name\":{\"value\":\"{{name}}\"}}}"
}Recipient:
{"openId":"openid...","name":"Alice"}Provider 在 Session 内缓存 Access Token,并提前 5 分钟刷新。只有远端明确返回 Token 无效或过期代码时,才刷新 Token 并安全重放一次;普通超时或 5xx 不重放消息。无效 OpenID、未关注或未订阅归类为 RECIPIENT_INVALID,API 权限/IP 白名单问题归类为授权错误,模板数据问题归类为 INVALID_REQUEST,配额限制归类为 RATE_LIMITED。连接测试会真实获取一次 Access Token,但不会发送消息。参见微信公众号 Access Token、公众号模板消息和小程序订阅消息。
6. 企业微信应用消息
Account:
{
"corpId":"ww...",
"corpSecret":{"namespace":"wecom","name":"corp-secret","version":"v1"},
"agentId":100001
}payloadJsonTemplate 可使用企业微信应用消息接口支持的任意 msgtype 和对应内容;Provider 负责覆盖目标、agentid 和重复检查字段:
{
"type":"APP",
"payloadJsonTemplate":"{\"msgtype\":\"text\",\"text\":{\"content\":\"Hello {{name}}\"}}",
"enableDuplicateCheck":true,
"duplicateCheckInterval":1800
}每条 Recipient 至少包含 userId、partyId 或 tagId 之一,分别映射为 touser、toparty、totag:
{"userId":"alice","name":"Alice"}Token 缓存、单次安全刷新和错误策略与微信 Provider 相同。默认开启企业微信 30 分钟重复检查;该机制由企业微信定义,不等同于跨时间、跨模板的 Exactly Once。参见企业微信发送应用消息。
7. 验证顺序
对每个新账号按以下顺序操作:
- 在 Secret Store 写入凭据,只在 Account 中保存 SecretRef。
- 在 Provider 页面保存 Account,执行连接测试并阅读返回代码;阿里云短信连接测试不验证模板和额度。
- 在 Message 页面从 Schema 默认示例开始,替换真实模板 ID、签名和内容。
- 导入 1 至 3 个自有测试 Recipient,先运行 Dry Run。
- 将并发和速率设置为渠道配额以内,核对正式发送确认页后小范围发送。
- 在 Run Item 中按
providerCode、category、retryable和UNKNOWN语义处理失败;不要盲目重发UNKNOWN。
本地 mock、协议签名、错误映射、Secret 不解析 Dry Run、Service/Agent 发现和 Engine 纵向测试均随源码测试套件执行。Classic 只作为需求与验收经验来源,Next Provider 不依赖 Classic 源码。
Next Docs Upgrade Guide (`next/docs/upgrade-guide.md`)
WePush Next 升级与回滚指南
本文以 1.1.0 升级到 1.2.0 为当前路径,也适用于 0.1.0-beta.1 或 1.0.0 经稳定版升级和后续 1.x Minor/Patch 升级。完整兼容承诺见《兼容性策略》。
1.2.0 将 Java 基线升级至 25,且不新增数据库迁移。精简包部署与 Java SDK 使用方须先将系统 Java 升级至 25+;完整包使用随包提供的 Java 25 运行时。旧版发布说明中的 Java 21 要求仅适用于 1.1.0 及更早版本。
1. 升级前检查
- 确认当前版本至少为
0.1.0-beta.1,推荐先处于1.1.0,且 Installation Health 为UP。使用精简包或 Java SDK 时,先安装 Java 25+。 - 确认磁盘有足够空间同时容纳当前数据、备份和新发行包;建议可用空间不低于数据目录大小的两倍加发行包大小。
- 从 GitHub Release 下载正确平台/架构的
1.2.0包和SHA256SUMS,完成 SHA-256 校验。 - 阅读
UNSIGNED-NOTICE.md。macOS/Windows 发行物没有商业代码签名。 - 暂停新 Schedule 或选择业务低峰。正在运行的外部发送必须先完成或由操作员确认结果处理方式。
2. 自动备份与升级
升级脚本会在切换版本前创建完整备份,安装新版本后检查 Readiness、Flyway 当前版本和内置 HTTP Provider 的无网络 Dry Run。任一步失败都会恢复旧版本链接和升级前数据。
Linux:
sudo /opt/wepush-next/current/install/linux/upgrade.sh \
wepush-next-1.2.0-linux-x64.tar.gz <sha256>macOS:
sudo /Library/WePushNext/current/install/macos/upgrade.sh \
wepush-next-1.2.0-macos-arm64.zip <sha256>Windows(管理员 PowerShell):
& "$env:ProgramFiles\WePush Next\current\install\windows\upgrade.ps1" `
-Archive .\wepush-next-1.2.0-windows-x64.zip `
-ExpectedSha256 <sha256>3. 升级后验证
GET /actuator/health/readiness返回UP。GET /actuator/health/installation显示数据库迁移和内置 Provider Dry Run 通过。GET /api/v1/system/info返回1.2.0。- Installation Health 显示 Flyway 当前版本为 V17;
workspace_policy、account_auth_circuit和artifact_multipart_upload已创建。1.2.0不新增迁移。 - 原 Workspace、Account、Message、Audience、Job、Schedule、历史 Run 和 Artifact 可读取。
- Agent 重新连接,Provider Catalog 完整;如使用超过 1 GiB Artifact 或运营商插件,确认 Agent 也为
1.2.0并已配置本版本发行签名公钥。先执行 Dry Run,再用自有测试目标执行小规模真实发送。 - 需要 AI 助手时,在 Desktop 设置 → AI 助手接入 重新安装 MCP/Skill,或使用发行包中的
ai/wepush-ai.mjs。旧的1.1.0安装包没有该安装器。 - Server/HA 确认周期扫描持续工作,并在 PostgreSQL 日志/指标中检查通知链路;
LISTEN/NOTIFY失效不得阻断 Run、Agent 命令或 SSE 推进。 - 备份文件仍保留在默认备份目录,且可用 Restore 的
--validate-only/-ValidateOnly验证。
4. 回滚
升级健康检查失败时脚本会自动回滚。升级成功后如需人工回滚,先停止 Service/Agent,再使用升级前备份恢复;不要只替换 JAR 而保留未经确认的新数据库。
1.2.0 不新增表。从 1.1.0 升级时,受支持的回滚目标是 1.1.0;若数据库曾经过 1.1.0 的 V15–V17,不能把只替换 JAR 当成回滚到 1.0.0。无论目标版本如何,都必须通过升级前完整备份恢复。备份同时覆盖数据库、Master Key、Artifact、Agent Identity、Journal、Event/Completion Outbox 和插件。
Restore 会:
- 拒绝路径穿越、非普通文件、未列入摘要的 Payload 和内容摘要不一致;
- 在替换前保留
pre-restore-*原目录; - 恢复后运行 Installation Health;
- 健康检查失败时恢复操作前数据。
5. 卸载
默认卸载只删除服务注册和程序文件,保留配置与数据。只有显式使用 --purge(Linux/macOS)或 -Purge(Windows)才删除用户数据。执行 Purge 前必须自行保存需要保留的备份。
三平台默认卸载、数据保留和显式 Purge 都进入发行自动化门禁。
Next Docs User Guide (`next/docs/user-guide.md`)
WePush Next 对外使用指南
WePush Next 是由用户自行下载、安装、部署和运维的开源产品,不依赖官方公共 SaaS、账号注册、计费订阅或云 KMS/Secret Manager。数据、密钥和运行环境均由用户掌控;完整定位和后续计划见《产品目标、边界与路线图》。
本文面向第一次下载和使用 WePush Next 的用户,适用于 1.2.0 稳定自部署版。
1.2.0提供明确的 1.x API、配置、数据库和 Agent 协议兼容承诺。Desktop 与发行包未使用商业代码签名;请只从项目 GitHub Release 下载,并在运行前校验SHA256SUMS。详见《兼容性策略》和UNSIGNED-NOTICE.md。
1. 先选择需要的组件
WePush Next 由多个可以独立使用的组件组成:
| 组件 | 用途 | 第一次体验是否需要 |
|---|---|---|
| Service | 保存配置和运行数据,提供 WebUI、OpenAPI 与 Service API,并可在本机执行任务 | 必需 |
| WebUI | 浏览器中的可视化配置、运行中心、动态 API 文档 | 已包含在 Service 发行包中 |
| Desktop UI | Electron 桌面管理界面,连接并管理已安装的本机 127.0.0.1:18990 Service |
可选 |
| Agent | 从 Service 接收任务并在独立节点执行,适合远程或分布式部署 | 本地体验不需要 |
| Remote Java SDK | 供其他 Java 应用通过 Service API 集成 WePush | 按需,1.2.0 附件已包含 |
| Embedded Java SDK | 在 Java 应用进程内直接装配 Engine 和 Provider,不需要 Service | 按需,1.2.0 附件已包含 |
| Core / Engine | Service 与 Agent 内部的执行引擎 | 不需要单独安装 |
发行包的统一脚本负责安装 Standalone Service;Desktop UI 不重复内嵌 Service,但可以检测、启动、停止已安装的本机 Service,读取日志和生成脱敏诊断。
Classic 与 Next 是彼此独立的两条产品线。Classic 继续提供成熟的桌面批量推送能力;Next 当前内置 HTTP、SMTP Email、飞书/钉钉/企微机器人、阿里云短信、微信公众号、小程序和企业微信应用消息。两者没有自动迁移或共享数据要求,可以并行安装和使用。
2. 下载与系统要求
官方下载页:
发行附件:
| 文件 | 内容或平台 |
|---|---|
wepush-next-1.2.0-linux-<arch>.tar.gz |
Linux 完整包,内含 Java Runtime,推荐首次安装 |
wepush-next-1.2.0-macos-<arch>.zip |
macOS 完整包,内含 Java Runtime,推荐首次安装 |
wepush-next-1.2.0-windows-<arch>.zip |
Windows 完整包,内含 Java Runtime 和离线 WinSW |
wepush-next-1.2.0.tar.gz / .zip |
使用系统 Java 25+ 的精简便携包;内容不含 Java Runtime |
wepush-next-desktop-1.2.0-<os>-<arch>.* |
对应平台的 Desktop 管理界面 |
wepush-next-java-sdk-1.2.0.zip |
Remote Java SDK、Embedded Java SDK 及其 POM/JAR 依赖闭包 |
wepush-provider-{cmpp,smgp,sgip,smpp}-1.2.0.zip |
可选运营商短信 Agent 签名插件;各自附带 .sha256 |
wepush-provider-trusted-key-1.2.0.env |
官方插件签名 Key ID/公钥的 Agent 环境配置;附带 .sha256 并列入统一 SHA256SUMS |
SHA256SUMS |
全部附件的 SHA-256 |
wepush-next-1.2.0-sbom.cdx.json |
CycloneDX SBOM |
完整包运行 Service/Agent 不要求预装 Java。精简包、从源码构建和任一种 Java SDK 需要 Java 25 或更高版本:
java -version发行工作流按目标 Runner 架构生成附件;文件名中的 <arch> 以 Release 实际列出的 x64 或 arm64 为准。
校验下载文件
下载 SHA256SUMS 后,计算目标文件的 SHA-256,并与其中同名行比较:
Linux:
sha256sum wepush-next-1.2.0.tar.gz
grep 'wepush-next-1.2.0.tar.gz$' SHA256SUMSmacOS:
shasum -a 256 wepush-next-1.2.0.tar.gz
grep 'wepush-next-1.2.0.tar.gz$' SHA256SUMSWindows PowerShell:
Get-FileHash .\wepush-next-1.2.0.zip -Algorithm SHA256
Select-String -Path .\SHA256SUMS -Pattern 'wepush-next-1.2.0.zip$'两个值必须完全一致。Desktop 包也使用相同方法校验。
3. 五分钟安装:Standalone
Standalone 默认只监听 127.0.0.1:18990,使用 SQLite、本地 Artifact Store 和内嵌 Engine,不需要 PostgreSQL、对象存储或 Agent。
下载当前操作系统的完整包并解压。统一入口默认只安装 Standalone Service;安装末尾会验证 Readiness、数据库迁移版本和不会访问网络的 Provider Dry Run。
Linux
tar -xzf wepush-next-1.2.0-linux-x64.tar.gz
cd wepush-next-1.2.0
sudo ./install/install.shmacOS
ditto -x -k wepush-next-1.2.0-macos-arm64.zip .
cd wepush-next-1.2.0
sudo ./install/install.shWindows PowerShell
Expand-Archive .\wepush-next-1.2.0-windows-x64.zip .\wepush-next
Set-Location .\wepush-next\wepush-next-1.2.0
Set-ExecutionPolicy -Scope Process Bypass
& .\install\install.ps1安装完成后打开:
- WebUI:
http://127.0.0.1:18990/ - 健康检查:
http://127.0.0.1:18990/actuator/health/readiness - OpenAPI:
http://127.0.0.1:18990/openapi.yaml
也可以在另一个终端验证:
curl --fail http://127.0.0.1:18990/actuator/health/readiness
curl --fail http://127.0.0.1:18990/api/v1/system/info需要便携运行而不安装系统服务时,Linux/macOS 在解压目录执行 ./bin/wepush-service;Windows 执行 install\windows\run-service.ps1。便携模式应从解压目录启动,使默认 .local 数据与该目录保持在一起。完整包优先使用内含 Runtime,精简包自动使用系统 Java。
4. 可选:使用 Desktop UI
先按上一节安装 Service,再解压对应平台的 Desktop 附件:
- Linux:运行
wepush-next-desktop-1.2.0-linux-x64/WePush Next/wepush-next。 - macOS:打开
wepush-next-desktop-1.2.0-macos-arm64/WePush Next.app。 - Windows:运行
wepush-next-desktop-1.2.0-windows-x64\WePush Next\WePush Next.exe。
Desktop 会连接本机 http://127.0.0.1:18990。设置页可以检测、启动、停止 Service,读取最近日志并生成不包含 Token/Secret 的诊断;涉及系统服务或插件目录的写操作会请求操作系统管理员授权。
Desktop 的 API Token 通过 Electron safeStorage 写入操作系统原生安全存储;如果 Linux 桌面没有可用 Keyring,Desktop 会拒绝以弱后端持久化。浏览器 WebUI 默认只写 sessionStorage,关闭标签页后即清除,不再使用长期 localStorage。
由于发行包没有商业签名,macOS Gatekeeper 或 Windows SmartScreen 可能显示未知开发者/发布者。只有在文件来自项目 Release 且 SHA-256 校验一致时,才使用系统提供的“打开”流程;不要全局关闭系统安全能力。
不想安装 Desktop 时,直接使用浏览器 WebUI 即可,两种 UI 连接的是同一套 Service API 和数据。
4.1 `1.2.0` 设置与主题
- 顶部主题选择支持亮色、暗色和跟随系统;选择保存在本地浏览器偏好中,不会上传。
- 系统管理员可在设置页为当前 Workspace 设置 Agent、活动 Run、总发送并发、Artifact 容量和默认保留期。降低上限不会自动删除资源;超过新上限时,新增操作会给出可诊断拒绝。
- “生成诊断包”会由 Service 返回结构化脱敏 ZIP;仍应把它视为内部运维资料,不要公开上传。
- “检查版本”只有点击时才查询 GitHub 的稳定
next-v*Release,不会后台检查、自动下载或发送遥测。 - Account 的认证熔断面板显示跨 Run 认证失败和冷却状态。先修正 Secret/凭据,再由管理员复位;网络失败、限流或 Recipient 错误不会计入认证熔断。
- 运营商短信使用 Release 中独立的 CMPP/SMGP/SGIP/SMPP 签名插件。安装前校验
SHA256SUMS并配置发行公钥;这些插件当前处理主动提交和长短信,不处理状态报告或上行短信。 - 设置 → AI 助手接入 可安装 Codex MCP 与 Skill,或下载
wepush-ai.mjs。正式发送仍需预览确认。说明见《AI 助手接入指南》。
5. 完成第一次安全 Dry Run
下面的流程只验证 Account → Message → Audience → Job → Engine → Provider 全链路。HTTP Provider 的 Dry Run 不会向配置的远端地址发送 HTTP 请求。
5.1 创建 HTTP 账号
- 打开
Providers。 - 选择内置的
HTTPProvider。 - 账号名称填写
Preview HTTP。 baseUrl填写https://example.com。connectTimeout保持PT5S。allowPrivateAddresses保持关闭,auth保持NONE或不配置。- 点击“保存账号”。
只有明确受信的内部 HTTP 服务才应启用 allowPrivateAddresses。默认关闭可避免任务访问本机、局域网和云元数据地址。
5.2 创建消息
进入“消息”,选择 HTTP Provider,并填写:
- 名称:
Preview message - 方法:
POST - 路径:
/messages - Headers:
{"Content-Type":"application/json"} - Body Template:
{
"name": "{{name}}",
"mobile": "{{mobile}}"
}模板变量使用 {{fieldName}},运行时从每个 Recipient 的 fields 中取值。保存消息后会形成不可变修订。
5.3 创建受众
进入“受众”,新建 UTF-8 CSV 文件:
itemId,mobile,name
alice,13000000000,Alice
bob,13100000000,Bob- 填写受众名称并选择 CSV;TXT 也受支持,每个非空行作为一条 Recipient。
- 选择
itemId列,把mobile、name映射到同名 Recipient 字段。 - 点击“上传并预览”,核对总行数、接受数、错误数和重复数。
- 有错误时先下载错误行 CSV;重复
itemId只保留首次出现的记录。 - 点击“确认并生成 Snapshot”。
itemId 应在同一受众中保持稳定且唯一;字段必须覆盖消息模板引用的变量。更新已有受众时请选择目标受众,提交后会生成新的不可变 Snapshot,不会改变既有 Run。
5.4 创建任务并运行
- 进入“任务与调度”。
- 选择刚创建的账号、消息和受众。
- 保持较小的目标并发,例如
2。 - 保存任务。
- 在右侧任务卡片点击
Dry Run。 - 进入“运行中心”,查看状态、计数、事件和每条 Recipient 的结果。
正常情况下,两条结果会以 Provider Code DRY_RUN 成功完成。该过程不会请求 example.com/messages。
6. 正式 HTTP 发送
完成 Dry Run 后,在任务卡片点击“正式发送”。界面会先展示 Provider/版本、账号、受众及数量、完整策略、目标并发、限速和预计执行规模。只有再次点击“我已核对,开始发送”才会创建真实 Run;确认令牌五分钟有效,并绑定当前资源版本,资源变化后必须重新核对。
将
dryRun设为false会真实访问 HTTP 目标。请先使用专用测试端点、小范围受众和服务端幂等机制验证,不要把上面的example.com示例用于正式运行。
运行结束后进入“运行中心”。PARTIAL 或 FAILED Run 可以选择 FAILED、UNKNOWN、UNSENT 状态并点击失败重发;Service 会显示匹配 Item 数量,再创建带来源 Run ID 的新 Run。重发使用来源 Run 的冻结 Snapshot,不会混入之后编辑的 Message 或 Audience。
直接调用 API 时同样必须先获取确认上下文:
curl --request POST \
http://127.0.0.1:18990/api/v1/workspaces/ws_default/jobs/<JOB_ID>/run-confirmation
curl --request POST \
--header 'Content-Type: application/json' \
--header 'Idempotency-Key: replace-with-a-unique-value' \
--data '{"dryRun":false,"policyOverrides":{},"reason":"manual-live-run","confirmationToken":"<TOKEN_FROM_PREVIOUS_RESPONSE>"}' \
http://127.0.0.1:18990/api/v1/workspaces/ws_default/jobs/<JOB_ID>/runs开启 API Security 后还需添加:
Authorization: Bearer <TOKEN>HTTP Provider 不跟随重定向,默认阻止私有地址,限制响应体大小,并支持配置成功状态码与幂等 Header。正式接入前应按目标系统的认证、限流、超时和幂等约定完成配置。
SMTP、机器人、短信和微信系渠道使用相同的 Account → Message → Audience → Job → Dry Run → 正式确认流程。每个渠道的最小 Account、Message、Recipient、SecretRef、连接测试边界、限流和 Provider Code 处理见《内置 Provider 指南》。所有正式发送都使用用户自己的渠道账号与额度,WePush 不提供集中账号、代充值或平台代理发送。
7. API 文档与 Java SDKs
当前源码还提供 设置 → AI 助手接入,可一键安装 Codex MCP + Skill 或导出其他客户端的 MCP 配置。完整步骤见《AI 助手接入指南》,需要使用包含此功能的新构建。
WebUI 的“API 文档”会读取 Service 提供的 OpenAPI,支持查看请求 Schema、生成请求和动态调试。写操作发送前会二次确认;权限、审计和同源限制仍由 Service 强制执行。
Remote Java SDK 是 Service API 客户端,不依赖 Core、Engine 或具体 Provider。beta.1 发行包和独立 SDK 压缩包都包含它,安装说明见 Java SDK README。
最小示例:
try (var client = WePushClient.builder()
.endpoint(URI.create("http://127.0.0.1:18990"))
.build()) {
var system = client.system().info();
var providers = client.providers().list();
var workspace = client.workspace("ws_default");
var runs = workspace.runs();
}Embedded Java SDK 依赖 Core Engine,但不依赖 Service、Agent、数据库或 Spring;应用显式注册允许的 Provider,并直接传入执行快照和 Recipient:
var store = new InMemoryExecutionStore();
try (var engine = WePushEngine.builder()
.provider(new HttpProviderFactory())
.resultSink(store)
.eventSink(store)
.build()) {
var summary = engine.start(spec, recipients)
.completion().toCompletableFuture().join();
}beta.1 发行包和独立 Java SDK 附件同时包含 Embedded SDK 与所需依赖。完整依赖、配置、资源所有权和示例见 Embedded Java SDK README。
8. 何时需要 Agent
默认内嵌执行模式下,Service 自己调用 Core Engine,本地体验不需要 Agent。以下情况再部署 Agent:
- 任务需要在另一台主机或受控网络中执行。
- 希望 Service 只承担控制面,执行资源独立扩缩容。
- 不同执行节点需要安装不同 Provider 插件。
- 需要借助 Lease、Fencing、心跳和远端 Artifact 回传管理分布式运行。
Agent 使用 Service 的 19090 gRPC 控制流。跨主机部署必须使用 Enrollment、长期 Credential 和 TLS/mTLS,不应沿用回环开发的明文配置。完整步骤见部署与运维文档。
9. 安装为系统服务
统一 install/install.sh 或 install/install.ps1 默认安装 Standalone Service。高级部署可直接调用平台目录下的安装器并传入 service、agent 或 all。
Linux / systemd
tar -xzf wepush-next-1.2.0-linux-x64.tar.gz
sudo ./wepush-next-1.2.0/install/install.sh
systemctl status wepush-next-servicemacOS / launchd
从普通登录用户的终端使用 sudo,安装器会将该用户作为非 root 服务用户:
ditto -x -k wepush-next-1.2.0-macos-arm64.zip .
sudo ./wepush-next-1.2.0/install/install.sh
launchctl print system/com.fangxuele.wepush-next.service无人值守安装需要显式设置已经存在的 WEPUSH_SERVICE_USER。
Windows Service
以管理员 PowerShell 执行:
Expand-Archive .\wepush-next-1.2.0-windows-x64.zip .\release
Set-ExecutionPolicy -Scope Process Bypass
& .\release\wepush-next-1.2.0\install\install.ps1
Get-Service WePushNextServiceWindows 发行包已经携带并再次校验固定版本 WinSW;安装阶段不访问网络。精简包也携带 WinSW,只有 Java 由系统提供。
配置路径、备份、升级、卸载、Server/HA、PostgreSQL、S3 与插件步骤见部署与运维文档。
10. 数据、备份与安全
便携 Standalone 默认保存:
- SQLite:
.local/data/wepush-next.db - Secret 主密钥:
.local/secrets/master-key.json - Artifact:
.local/artifacts
系统服务安装后的路径因操作系统而异,具体以部署文档和生成的 service.env 为准。正式 backup 会停止相关服务,写入格式/版本/内容清单和每个文件的 SHA-256,再调用 Restore 校验;restore 会先保留恢复前副本,替换后执行安装健康门,失败则自动恢复原数据。数据库、Secret 主密钥、Artifact、Agent Identity、Journal、Event/Completion Outbox 和插件必须作为一个一致备份恢复。
默认无认证模式仅允许 Service 监听回环地址。不要直接把 127.0.0.1 改成 0.0.0.0。非回环监听必须同时启用 API Security、设置强 Bootstrap Token,并在网络入口配置 TLS;Server 模式还会强制 PostgreSQL、S3-compatible Artifact Store 和 Agent gRPC TLS。
11. 常见问题
UI 显示 Service 未连接
- 确认
http://127.0.0.1:18990/actuator/health/readiness可访问。 - 确认 Java 版本是 21 或更高。
- 检查
18990是否被其他程序占用。 - Desktop 当前只连接本机
127.0.0.1:18990。
Service 启动后看不到 WebUI
- 使用发行包中的
bin/wepush-service,或按 Windows 便携命令设置WEPUSH_WEB_ROOT。 - 确认解压目录下存在
web/index.html。 - 直接从源码 JAR 启动时需要先构建 WebUI,或单独设置静态资源目录。
HTTP 运行报告 `SSRF_BLOCKED`
目标解析到了私有、回环或受限地址。只有目标确实是受信内部服务时,才在 Account 中开启 allowPrivateAddresses;不要为了绕过未知错误而开启。
API 返回 `401` 或 `403`
Service 已开启安全模式。把 Bootstrap Token 或已签发 Token 填入 WebUI“设置 → 当前 API Token”;命令行请求使用 Authorization: Bearer <TOKEN>。
操作系统阻止 Desktop 首次启动
先核对来源和 SHA-256。校验不一致时立即删除文件;校验一致时使用操作系统针对单个应用提供的确认流程,不要关闭 Gatekeeper、SmartScreen 或终端安全软件。
12. 反馈与进一步文档
普通问题和功能建议可提交 GitHub Issue。安全漏洞不要公开披露,请按 SECURITY.md 中的方式私下报告。
Next Docs Adr 0001 Dual Track Development (`next/docs/adr/0001-dual-track-development.md`)
ADR-0001:Classic 与 Next 双轨独立发展
- 状态:已接受
- 日期:2026-08-22
- 决策者:WePush 项目维护者
背景
当前 WePush 是一个已经稳定运行和发布的 Java Swing 客户端,具备完整的消息配置、用户导入、任务调度和批量推送能力。项目计划在 next/ 目录中建设新的目标架构,包括 Core Engine、Agent、Java SDK、Service、Desktop UI 和 WebUI。
如果直接在现有客户端中持续拆分,会同时影响现有用户、发布节奏和新架构设计。另一方面,为了强制消除重复代码而建立跨项目公共模块,也会重新产生构建、版本和架构耦合,限制两条产品线的独立演进。
因此,需要明确 Classic 与 Next 之间的长期关系,以及对代码重复的处理原则。
决策
WePush 采用 Classic 与 Next 双轨独立发展的模式。
- 当前仓库根目录下的客户端视为 Classic,继续按照现有产品定位、技术架构和发布流程独立发展。
next/是自包含的新项目,用于实现新的目标架构,并拥有独立的构建入口、模块结构、依赖管理、测试和发布流程。- Classic 与 Next 之间不建立运行时或编译期依赖。Next 不依赖 Classic 的 Java 包、数据库实体、静态工具类或内部实现,Classic 也不依赖 Next 的模块。
- 不为了复用代码而建立跨两条产品线的
shared、common等公共模块,也不使用软链接、共享源码目录或其他隐式源码复用方式。 - 两边允许出现相同或相似代码。复制代码是被接受的工程选择;代码一旦复制到另一条产品线,即成为该产品线独立维护的代码,不要求后续自动同步。
- 新功能、缺陷修复和安全修复根据实际需要决定进入 Classic、Next 或同时进入两边,不设置强制双向移植规则。
- Classic 与 Next 可以采用不同的数据模型、API、依赖版本和具体实现,也允许产品行为逐渐产生差异,不以内部实现兼容为目标。
- 两边可以共享需求理解、协议资料、供应商行为样例、设计经验和测试思路,但这些内容按需分别落入各自项目,不形成共享运行时制品。
- Classic 主要承担稳定桌面客户端和新消息类型快速实验场景;Next 按新的组件化、服务化目标发展,不为兼容 Classic 的内部架构而妥协。
- 两套应用使用独立的版本号、配置目录、数据存储、网络端口和安装标识,确保可以在同一环境中并行安装和运行。
结果
正面影响
- Classic 的稳定发布不会被 Next 的架构建设阻塞。
- Next 可以自由选择适合目标架构的模型、协议和技术实现。
- 两条产品线的依赖、版本和发布边界清晰,局部改动不会隐式影响另一边。
- 消息类型可以先在 Classic 快速验证,再按 Next 的 Provider SPI 和配置 Schema 重新实现。
- 避免公共模块逐渐演变为新的耦合中心。
接受的代价
- 某些 Provider、工具函数、协议适配和缺陷修复可能需要分别实现。
- 两边的相似代码可能随时间产生差异。
- 同时维护两条产品线会增加测试、发布和问题跟踪成本。
- 不能假设在一边完成的修复会自动覆盖另一边。
这些代价是本决策明确接受的,不应仅以“消除重复代码”为理由重新引入跨项目依赖。
实施约束
- Classic 保持当前根目录构建方式;Next 使用
next/下的独立构建入口。 - CI 按目录和产品线分别执行,任何一边都不能依赖另一边的构建产物才能完成测试或发布。
- Issue、提交和发布说明应标明适用范围:
classic、next或both。 - 从一边复制实现到另一边时,应同时复制或重写必要测试,并按照目标产品线的架构规范完成适配。
- 数据迁移通过显式迁移工具完成,Next 不直接操作或依赖 Classic 的运行时数据库。
- 如果未来需要改变本决策、引入共享模块或建立编译期依赖,必须提交新的 ADR 说明收益、边界和迁移方案。
非目标
- 不要求 Classic 与 Next 在任意时间点保持完整功能对等。
- 不要求相同消息类型在两边使用相同实现。
- 不建设自动代码同步机制。
- 不将 Classic 逐步改造成 Next;两者是独立产品线,而不是临时分支关系。
Next Docs Adr 0002 Technology Baseline (`next/docs/adr/0002-technology-baseline.md`)
ADR-0002:Service、WebUI 与 Desktop 技术基线
- 状态:已接受
- 日期:2026-08-22
- 决策者:WePush 项目维护者
背景
WePush Next 需要同时提供 Service、WebUI 和 Desktop UI。技术基线必须兼顾 Java 团队的开发效率、交互式管理界面的复杂度、Web 与 Desktop 复用、跨平台发布和长期安全更新。
决策
Service
- Java 基线固定为 Java 25。
- Service Web 框架固定为 Spring Boot 4.1.x,初始实现版本固定为 4.1.1。
- 使用 Spring MVC、Spring Security、Actuator、Validation 和 JDBC/DataSource 集成。
- Service 以可执行 JAR 或应用镜像运行,不部署传统 WAR。
- Spring 依赖只允许存在于 Service App、Web 和 Infrastructure 层,不进入 Core API、Core Engine、Provider SPI、Agent Runtime、Java SDK 或 Embedded SDK。
- Spring Boot 小版本和补丁版本可以在兼容测试通过后更新;升级 4.2 或更高特性线必须单独评审。
WebUI
- 语言固定为 TypeScript,初始基线为 TypeScript 6.0.x,并启用严格模式。
- 构建工具固定为 Vite 8.1.x。
- UI 框架固定为 React 19.2.x。
- 构建运行时固定为 Node.js 24 LTS,不使用非 LTS Node 作为 CI 和正式构建基线。
- 包管理器固定为 pnpm,通过 Corepack 固定项目使用的精确版本,并提交唯一 Lockfile。
- 样式基线使用 Tailwind CSS。
- shadcn/ui 按需引入,不把完整组件集合一次性加入项目。通过 shadcn CLI 引入的组件源码归 WePush Next 自己维护。
- WebUI 是纯 SPA,通过 Service API 工作,不使用 React Server Components,也不要求 Node.js 作为正式运行时。
- OpenAPI 生成 TypeScript API Client,业务代码通过手写 Facade 使用生成客户端。
Desktop UI
- Desktop 外壳固定为 Electron。
- 初始 Electron 基线固定为 43.x,在创建工程时锁定当时最新的 43.x 安全补丁版本。
- Desktop 复用 WebUI 的 React 页面、Schema Renderer、API Client 和设计 Token。
- Electron Main、Preload 和 Renderer 分层;Renderer 不直接获得 Node.js 权限。
- 强制启用
contextIsolation和 Renderer Sandbox,关闭nodeIntegration。 - Preload 只暴露经过白名单定义、参数校验和来源校验的最小 IPC API。
- Desktop 只加载发行包内的本地 UI 资源,并通过 GitHub Release 与
SHA256SUMS验证包完整性;不得加载带 Node 能力的远程页面。应用不以商业代码签名作为发行前提。 - Electron 每个受支持主版本生命周期较短,项目持续跟随受支持稳定版本,至少每季度评估一次主版本升级。
前端目录
next/ui/
├── pnpm-workspace.yaml
├── package.json
├── pnpm-lock.yaml
├── apps/
│ ├── web/
│ └── desktop/
└── packages/
├── api-client/
├── schema-renderer/
├── ui/
├── features/
└── design-tokens/apps/web 和 apps/desktop 可以共享 packages/*,但共享仅发生在 Next 内部,不影响 Classic 的独立性。
结果
正面影响
- WebUI 和 Desktop 可以共享大部分交互与业务前端代码。
- Vite 和 React 适合动态 Schema 表单、运行监控和交互式 API 文档。
- Electron 提供成熟的跨平台桌面能力和系统集成能力。
- Spring Boot 统一 Service 的 API、安全、事务、健康检查和部署生命周期。
- Core、Agent Runtime 和 Provider 保持框架无关,未来替换 Service 框架不会推翻执行架构。
接受的代价
- Electron 安装包和内存占用高于轻量 WebView 外壳。
- Electron 需要跟随快速发布节奏持续升级。
- 项目同时维护 Maven 和 pnpm 两套构建体系。
- shadcn/ui 组件是项目源码,升级和无障碍质量由项目自行负责。
参考
- Spring Boot
- React Versions
- Vite 8
- Node.js Releases
- Electron Releases
- Electron Security
- shadcn/ui for Vite
Next Docs Adr 0003 Default Secret Store (`next/docs/adr/0003-default-secret-store.md`)
ADR-0003:默认 Secret Store
- 状态:已接受
- 日期:2026-08-22
- 修订:2026-08-27,明确外部 Secret Store 属于长期非目标
- 决策者:WePush 项目维护者
背景
WePush 需要保存短信、微信、邮件、HTTP 等 Provider 的 Token、密码和私钥。默认实现必须同时适用于 Windows、Linux、macOS、Headless Service 和容器环境,且不能依赖 Desktop 进程或某个特定云厂商。
决策
默认实现采用本地信封加密 Secret Store,名称为 LocalEnvelopeSecretStore。
数据加密
- 每次 Secret 写入生成随机 Data Encryption Key(DEK)。
- Secret 使用 AES-256-GCM、随机 Nonce 和认证附加数据加密。
- DEK 使用版本化 Master Key(KEK)加密。
- 数据库只保存 Ciphertext、Nonce、Encrypted DEK、算法、Key Version 和更新时间。
- 认证附加数据绑定 Workspace ID、Secret Set ID、Secret Name 和记录版本,防止密文被移动到其他记录后解密。
Master Key
- Master Key 与业务数据库分开存放。
- Standalone 初始化时生成 256 位随机 Master Key,保存在独立 Key File。
- Linux 和 macOS Key File 权限必须为 Service 用户独占,目标权限为
0600。 - Windows 使用显式 ACL,只允许 Service 身份和受控管理员访问。
- Server 或容器模式优先从只读挂载文件读取 Master Key;允许显式环境变量注入,但不作为推荐方式。
- Server HA 的所有 Service 实例必须挂载同一份 Active/Retained Key Ring;Key Ring 版本变化通过受控部署传播,不能由各实例独立生成。
- Server 模式在未执行显式初始化时不得静默生成新 Master Key,防止重建容器后产生无法解密的数据。
密钥轮换
- 每个 Master Key 具有
keyVersion。 - 新写入使用当前 Active Key。
- 读取支持仍在保留期内的旧 Key。
- 轮换任务逐批重新封装 DEK,不需要重新加密完整 Secret 内容。
- 删除旧 Key 前必须证明不存在引用,并生成审计记录。
API 和运行时
- Secret 普通查询只返回
configured、updatedAt和版本摘要,不返回明文。 - Secret 不进入日志、Run Event、Artifact、OpenAPI 示例或浏览器持久化存储。
- Agent 只获取当前 Run 所需的最小 Secret Envelope,并绑定 Agent、Run、Lease、Epoch 和过期时间。
- Agent 不将解密 Secret 写入 Local Journal。
- Electron 的
safeStorage不作为 Service Secret Store,因为 Service 必须能在无 Desktop 会话的 Headless 环境运行。
产品边界
SecretStore 保持 Port 接口,用于维持模块边界、测试替身和本地实现可替换性。WePush Next 的官方产品实现固定为 LocalEnvelopeSecretStore,不规划 HashiCorp Vault、云 KMS、云 Secret Manager 或操作系统凭据存储形式的 Service Adapter。Master Key 由用户控制的受保护文件或显式注入提供。
失败策略
- Master Key 缺失、权限不安全、Key Version 未知或认证标签校验失败时,Service Fail Closed。
- 不允许自动创建新 Key 后继续启动已有数据库。
- 解密失败不得把 Ciphertext 或密钥材料写入异常消息。
- Readiness 显示 Secret Store 不可用,但不暴露具体密钥路径和内容。
结果
该方案跨平台、可离线、适合安装包和 Headless Service,并确保默认安装不依赖官方或第三方托管密钥服务。代价是项目需要提供完整的密钥初始化、备份、权限检查和轮换流程,部署者也必须把 Master Key 纳入自己的备份与恢复制度。
Next Docs Adr 0004 Agent Communication Protocol (`next/docs/adr/0004-agent-communication-protocol.md`)
ADR-0004:Agent 长期通信协议
- 状态:已接受
- 日期:2026-08-22
- 决策者:WePush 项目维护者
背景
Agent 与 Service 之间需要长期传输注册信息、心跳、能力、Lease、运行命令、事件批次和完成结果。该连接是双向、长生命周期、需要顺序和背压的机器间协议,与浏览器运行监控和大文件传输的需求不同。
决策
不同类型流量使用不同协议,不建立一个包办所有流量的通道。
Agent 控制流
- 远程 Agent 的正式长期协议使用 gRPC 双向流,承载于 HTTP/2。
- 使用 Protocol Buffers 定义
AgentControlService.Connect双向流。 - Agent 主动建立出站连接,Service 不主动连接 Agent。
- 同一逻辑流承载 Hello、Capability、Heartbeat、Lease Offer/Ack、Command/Ack、Run Event Batch、Run Summary 和 Drain 状态。
- 每个方向具有独立单调 Sequence;应用层仍进行幂等和断线恢复,不能仅依赖单次 gRPC 流的顺序保证。
- Lease 继续使用 Epoch 和 Fencing Token,gRPC 连接本身不等于 Lease 所有权。
- Agent 断线后指数退避并带抖动重连,可连接任一健康 Service 实例。
- 设置明确 Deadline、Keepalive、最大消息大小和流控上限。
注册和管理
- Agent Enrollment、Credential 轮换、插件目录查询等低频管理操作使用 HTTPS REST 或 gRPC Unary;首期优先复用 Service HTTPS REST 管理入口。
- Agent 正式连接使用独立 Agent 身份,目标安全基线为 mTLS;Bootstrap 阶段可以使用一次性 Enrollment Token。
浏览器和 SDK
- WebUI、Desktop UI 和远程 SDK 的 Run 实时事件继续使用 SSE。
- 用户命令继续使用 REST API。
- 不使用 WebSocket 作为 Agent 或浏览器的默认协议。
Artifact
- Audience Snapshot、结果、日志和插件包不通过 gRPC 控制流传输。
- Agent 使用 Service 签发的短期 HTTPS URL 直接上传或下载 Artifact。
- gRPC 消息只携带 Artifact ID、校验值、大小、状态和短期传输描述。
版本兼容
- Proto Package 使用主版本,例如
wepush.agent.v1。 - 已发布字段编号永不复用;删除字段必须
reserved。 - 新字段优先使用
optional或新消息类型,旧 Agent 忽略未知字段。 - Service 维护明确的 Agent 协议兼容窗口。
为什么不选择其他方案
- 长轮询适合快速 MVP,但命令、事件、心跳和背压最终会形成多个相互协调的 HTTP 循环。
- SSE 是服务端到客户端单向流,不能独立承担 Agent 双向控制。
- WebSocket 可以双向通信,但缺少 Protobuf/gRPC 的服务契约、生成代码、Deadline 和标准流控能力。
- gRPC 不适合直接服务浏览器交互文档和大 Artifact,因此不替代 REST、SSE 和对象存储。
实施策略
Embedded Agent 直接调用 Java 接口,不经过网络。远程 Agent 功能开始实现时直接建设 gRPC,不先发布需要长期兼容的长轮询协议。
参考
Next Docs Adr 0005 Provider Plugin Lifecycle (`next/docs/adr/0005-provider-plugin-lifecycle.md`)
ADR-0005:Provider 插件发现、隔离和更新
- 状态:已接受
- 日期:2026-08-22
- 决策者:WePush 项目维护者
背景
WePush Provider 会引入不同厂商 SDK。它们可能依赖不同版本的 HTTP、JSON、加密和日志库。Provider 还需要携带 Descriptor、JSON Schema、UI Schema 和图标。插件机制需要减少依赖冲突,同时保证运行中任务不会被不安全的代码替换破坏。
决策
插件框架
- 外部 Provider 插件框架采用 PF4J 3.15.x。
- Provider SPI 是 PF4J Extension Point,但 Core Engine 不依赖 PF4J。
- Agent App 的 Provider Catalog Adapter 负责把 PF4J Extension 转换为 Core 的
ProviderFactory。 - 内置 Provider 可以作为系统 Extension 参与同一个 Provider Catalog。
插件包
wepush-provider-http-1.0.0.zip
├── plugin.jar
├── lib/
├── plugin.json
├── schemas/
├── assets/
├── LICENSES/
└── signature.ed25519plugin.json 包含 Plugin ID、Provider ID、版本、SPI 版本、主类、依赖、平台限制、SHA-256 清单和签名 Key ID。
依赖隔离
- 每个插件使用独立 PF4J ClassLoader。
- JDK、Provider SPI、Core API 和统一日志 API 由 Parent ClassLoader 提供。
- 插件私有 SDK 和三方依赖由插件 ClassLoader 加载。
- 插件不得捆绑 Provider SPI、Core API 或日志 API 的重复副本。
- 构建阶段检测 Split Package、重复共享类和禁止依赖。
ClassLoader 隔离只解决依赖冲突,不是恶意代码安全沙箱。JVM 内 Provider 被视为受信代码。需要运行不受信第三方插件时,必须增加独立进程 Provider Runner,不能宣称 PF4J 已提供安全隔离。
签名和信任
- 正式发行环境默认只加载签名有效且发布者受信的插件。
- 插件包使用 SHA-256 清单和 Ed25519 签名。
- Agent 发行包内置官方公钥,并允许管理员添加受信发布者。
- 未签名插件只允许在显式 Developer Mode 中加载,Service 和 UI 必须显示安全警告。
- ZIP 解包采用路径白名单、大小上限和 Zip Slip 防护。
安装和激活
首期发行工具使用显式 Staging/Active/Rollback 目录;插件 ZIP 文件名必须包含稳定 Plugin ID 和版本:
plugins/
├── staging/
│ └── wepush-provider-http-1.2.0.zip
├── active/
│ └── wepush-provider-http-1.2.0.zip
└── rollback/
└── wepush-provider-http-1.0.0.zip.20260823T010203Z- 下载到 Staging 目录并记录包级 SHA-256。
- 激活时先把旧包移动到 Rollback,再把新包原子移动到 Active;Agent 启动在 PF4J 解包前验证 ZIP 边界、清单、签名、Descriptor、共享包和 SPI 兼容性。
- Agent 启动/健康验证失败时,Supervisor 工具恢复 Rollback 旧包并再次启动。
- Service 保存期望插件版本,Agent 上报实际插件清单和状态。
更新策略
- 不进行 JVM 内热替换,不在运行中卸载或替换 Provider ClassLoader。
- 单 Agent 工具执行 Stage、Activate、Restart、Verify、Rollback;多 Agent 编排在激活前先把目标 Agent 从可调度池摘除,等待当前 Run 完成或达到 Drain Timeout。
- 首期命令行工具不伪造进程内热更新;Drain 编排由 Service/运维层控制,Supervisor 只负责重启、健康验证和回滚。
- 重启后加载新版本并通过 Health 和自检;失败时恢复旧 ZIP 并再次重启。
- 多 Agent 环境逐台滚动,确保始终保留可执行旧任务的 Agent。
- Provider 版本由 Run Snapshot 固定;调度新 Run 前必须存在兼容 Agent。
Provider 删除
- 被 Run Snapshot、Job 或历史诊断引用的版本不能立即物理删除。
- 删除先进入
DEPRECATED,禁止新配置使用。 - 没有运行和保留引用后才允许从 Agent 清理文件。
- 历史 Run 仍保存 Provider ID、版本和 Schema 快照,即使代码已经删除。
结果
该方案提供清晰的发现和依赖隔离,同时避免热替换引发线程、连接池、静态缓存和 ClassLoader 泄漏。代价是插件升级需要 Agent 重启,但滚动升级可以避免整体停机。
参考
Next Docs Adr 0006 Postgresql Control Plane Ha (`next/docs/adr/0006-postgresql-control-plane-ha.md`)
ADR-0006:PostgreSQL Server 模式控制面高可用
- 状态:已接受
- 日期:2026-08-22
- 修订:2026-08-27,明确 HA 只服务于用户自建信任域
- 决策者:WePush 项目维护者
背景
Standalone 模式使用单 Service 和 SQLite,不需要控制面集群。Server 模式需要支持 Service 实例故障、Agent 重连、调度互斥、并发领取 Run 和任意实例提供 API/SSE。方案应尽量减少必须部署的中间件。
决策
基线拓扑
- Server 模式数据库基线为 PostgreSQL 18.x。
- 正式 HA 至少部署两个无状态 Service 实例,位于支持 HTTP/2 和 gRPC 的负载均衡器后。
- PostgreSQL 是业务状态和协调状态的唯一持久化事实源。
- PostgreSQL 自身的复制、故障转移和备份由部署者自行选择并运维,不由 WePush 安装包组建数据库集群,也不依赖 WePush 官方托管服务。
- Artifact 使用 S3-compatible Store,不使用 Service 本地磁盘共享状态。
- 首期 HA 不强制引入 Redis、Kafka、ZooKeeper 或独立任务队列。
API 和 Agent 连接
- REST 请求可以落到任意 Service 实例,不要求 Sticky Session。
- SSE 可以连接任意实例,历史游标从 PostgreSQL 读取,新事件通过本地发布加数据库轮询补偿获得。
- Agent gRPC 流连接一个 Service 实例;实例失败时 Agent 重连到负载均衡器并恢复 Sequence、Lease 和事件上报。
- 当前 Agent 连接的 Owner Instance 只作为可丢失的路由提示,不成为持久化真相。
调度互斥
- 只有持有 PostgreSQL Session-level Advisory Lock 的 Service 实例运行 Schedule Scanner。
- 使用专用数据库连接持有锁,连接断开时 PostgreSQL 自动释放锁。
- 其他实例持续尝试获取锁,成为新的调度 Leader。
- 每次 Schedule 触发仍使用数据库幂等键,Leader Lock 不是防重复的唯一手段。
Run 领取和 Lease
- 候选 Run 通过短事务、Run 状态条件更新和“每个 Run 仅一个 Active Lease”的数据库唯一索引领取;并发冲突失败后由下一次扫描重试。批量吞吐需要提高时可以在 PostgreSQL Repository 内加入
FOR UPDATE SKIP LOCKED,不改变应用层语义。 - 创建 Lease 时递增 Epoch 并生成随机 Lease Token。
- 同一 Run 同时最多一个 Active Lease。
- 所有 Agent 写入校验 Lease ID、Epoch 和 Token,拒绝旧 Agent 的过期写入。
- Service 实例故障不会自动宣布 Run 失败;由 Lease Timeout 和恢复状态机判断。
实例间通知与持久消息
- Service→Agent 的 Lease Offer 和 Run Command 先写
agent_message_outbox,所有 Service 周期扫描;只有持有 Agent 当前 gRPC 流的实例发送,ACK 后关闭消息。 - SSE 以
run_event为事实源,每个实例只轮询本机存在订阅者的 Run Cursor,从而观察其他实例提交的事件。 1.1.0使用 PostgreSQLLISTEN/NOTIFY作为待调度 Run、Agent Outbox 和 Run Event 的低延迟唤醒优化,但不得替代 outbox、事件表和周期扫描。- 禁止把 LISTEN/NOTIFY 当成持久消息队列。
数据库迁移
- 发布前或滚动启动前只运行一个 Migration Job。
- Service Readiness 在数据库版本不兼容时失败。
- 迁移采用 Expand、Migrate、Contract,保证滚动期间相邻 Service 版本可短期共存。
- 破坏性 Contract 迁移只能在旧版本实例全部退出后执行。
故障行为
| 故障 | 预期行为 |
|---|---|
| 一个 Service 实例退出 | REST/SSE 重连;Agent gRPC 重连;其他实例继续服务 |
| 调度 Leader 退出 | Advisory Lock 自动释放,其他实例接任 |
| 一个轮询周期未观察到新事件/消息 | 下一周期继续从持久游标扫描补偿 |
| PostgreSQL 暂时不可用 | Service Readiness 失败,不创建新 Run;Agent 保留有界 Outbox |
| PostgreSQL 主从切换 | Service 重建连接,重新竞选调度 Leader |
| Agent 连接到新实例 | 使用 Sequence 和 Lease Epoch 恢复,不重复接受旧写入 |
非目标
- 不在 WePush 内实现 PostgreSQL 选主和数据复制。
- 跨区域 Active-Active 控制面不属于产品范围。
- 不通过分布式锁实现外部渠道 Exactly Once。
- SQLite Standalone 不支持多 Service 实例并发访问同一个数据库文件。
参考
Next Docs Adr 0007 Artifact Store And Retention (`next/docs/adr/0007-artifact-store-and-retention.md`)
ADR-0007:Artifact Store 协议和保留策略
- 状态:已接受
- 日期:2026-08-22
- 修订:2026-08-27,移除云 KMS 产品方向
- 决策者:WePush 项目维护者
背景
Audience Snapshot、运行结果、完整响应、日志和插件包可能远大于数据库适合保存的范围。Standalone 需要零依赖本地存储,Server 和 HA 模式需要所有 Service 与 Agent 可访问的共享存储。
决策
存储实现
- Standalone 默认使用
LocalFileArtifactStore。 - Server 默认使用
S3ArtifactStore,协议限定为 S3-compatible API 的受控子集。 - 数据库保存 Artifact 元数据和生命周期状态,不把对象存储 List 结果作为事实源。
S3-compatible 最小能力:
- Put Object
- Get Object 和 Range
- Head Object
- Delete Object
- Multipart Upload、Complete 和 Abort
- Presigned Upload/Download URL
- 对象校验和与原生服务端加密配置
对象命名
<environment>/<workspaceId>/<artifactType>/<yyyy>/<mm>/<artifactId>- Object Key 不包含用户名、任务名、手机号、邮箱或原始上传文件名。
- Artifact ID 全局唯一且不可变。
- 原始文件名只作为经过清理的下载展示元数据。
上传和下载
- Agent 不持有长期对象存储 Credential。
- Service 在权限、Lease 和配额校验后签发短期 Presigned URL。
- URL 绑定方法、Key、Content Length 范围、Checksum 和短期过期时间。
- 上传完成后 Agent 通知 Service,Service通过 Head/Checksum 验证后把 Artifact 置为
READY。 - Service 托管流式写入达到 100 MiB 时默认使用 Multipart Upload。Agent 对不超过 1 GiB 的 Artifact 使用带长度与校验和约束的单次 Presigned Put;
1.1.0对更大对象使用可恢复 Presigned Multipart Plan,单对象最大 5 TiB,每批最多签发 100 个 Part URL 并约束 10,000 Part 上限。Complete 后必须核对总大小和 SHA-256,失败或过期会话主动 Abort,对象存储 24 小时 Lifecycle 负责孤儿兜底。 - 下载支持 Range,用户下载仍先经过 Service 权限检查。
完整性和加密
- Artifact 元数据保存 SHA-256、大小和 Content Type。
- 传输使用 TLS。
- Server 模式启用对象存储原生 AES256,或由部署者在其存储层保证等价加密。WePush 不对接或管理云 KMS Key。
- 极敏感 Artifact 可以在客户端加密后再上传,具体类型另行配置。
生命周期状态
UPLOADING → READY → DELETING → DELETED
↘ FAILED- 删除采用两阶段流程,先标记
DELETING,对象删除成功后标记DELETED。 - Object Store 操作幂等,清理任务可以安全重试。
- 数据库引用和 Pin/Legal Hold 阻止进入删除流程。
默认保留策略
| Artifact 类型 | 默认保留 |
|---|---|
| Audience Snapshot | 被 Job/Run 引用期间保留,最后引用释放后再保留 30 天 |
| 成功、失败、未知、未发送结果 | Run 结束后 90 天 |
| 结构化运行日志 | Run 结束后 30 天 |
| 完整 Provider 响应体 | Run 结束后 7 天 |
| 导出文件和临时下载 | 创建后 24 小时 |
| 未完成 Multipart Upload | 24 小时后 Abort(对象存储 Lifecycle 兜底) |
| Provider 插件包 | 存在 Agent、Job 或 Run Snapshot 引用期间保留 |
- Workspace 管理员可以在系统允许范围内缩短或延长策略。
PINNED和LEGAL_HOLDArtifact 不受普通 TTL 删除。- 对象存储 Lifecycle Rule 可作为兜底,但 Service 数据库清理任务是业务生命周期的主控制器。
Local Store 等价语义
Local Store 使用相同 Artifact ID、状态、校验和和保留规则。写入先进入临时文件,Flush、fsync 和校验后原子移动到最终路径。路径由 Store 生成,不使用用户输入拼接。
结果
该方案使 Standalone 保持自包含,同时让 Server 能使用兼容对象存储。Agent 直传避免 Service 成为大文件带宽瓶颈。代价是需要管理 Presigned URL、Multipart 清理和数据库/对象存储的最终一致性。
参考
Next Docs Adr 0008 Workspace Multitenancy Scope (`next/docs/adr/0008-workspace-multitenancy-scope.md`)
ADR-0008:Workspace 多租户范围
- 状态:已接受
- 日期:2026-08-22
- 修订:2026-08-27,明确 Workspace 不是公共 SaaS 租户
- 决策者:WePush 项目维护者
背景
Standalone 通常只有一个用户,但用户自建的 Server 模式可能出现多个团队、账号、Agent 和权限边界。如果完全按单 Workspace 实现,后续增加团队 Scope 需要修改所有表、唯一索引、Repository、API、Artifact 和 Secret。Workspace 因此是同一部署信任域内的逻辑边界,不是公共 SaaS 租户模型。
决策
Workspace 逻辑隔离进入 WePush Next 正式产品范围,但公共多租户 SaaS 长期不进入产品范围。
Standalone
- 初始化时自动创建一个 Default Workspace。
- UI 默认隐藏 Workspace 切换和管理复杂度。
- 数据、API 和 Repository 内部仍显式携带 Workspace ID。
Server
- 支持多个 Workspace。
- 用户通过 Role Binding 获得某个 Workspace 的 ADMIN、OPERATOR 或 VIEWER 权限。
- Account、Secret、Message、Audience、Job、Schedule、Run、Artifact、Agent Pool 和 API Token 都归属 Workspace。
- Agent 可以加入一个或多个明确授权的 Agent Pool,Lease 只能分配给 Run 所属 Workspace 可用的 Pool。
API 范围
Workspace 业务资源使用显式路径:
/api/v1/workspaces/{workspaceId}/accounts
/api/v1/workspaces/{workspaceId}/messages
/api/v1/workspaces/{workspaceId}/audiences
/api/v1/workspaces/{workspaceId}/jobs
/api/v1/workspaces/{workspaceId}/schedules
/api/v1/workspaces/{workspaceId}/runs
/api/v1/workspaces/{workspaceId}/agents身份、公开 Provider Catalog、系统 Health 和 Agent Internal API 不使用该业务路径,但仍按自身规则校验 Scope。
Java SDK 创建 WorkspaceClient 后执行资源操作,避免每个方法重复传 Workspace ID:
WorkspaceClient workspace = client.workspace(workspaceId);
workspace.jobs().startRun(jobId, request);数据隔离
- 所有 Workspace 资源表包含非空
workspace_id。 - 唯一索引包含
workspace_id,例如(workspace_id, provider_id, name)。 - Repository 方法必须接收
WorkspaceId,禁止提供无 Scope 的通用findById(id)给业务用例。 - API ID 即使全局唯一,也不能替代 Workspace 权限检查。
- 跨 Workspace 管理操作集中在独立 System Administration 用例中。
- PostgreSQL Row Level Security 可以作为未来防御层,但不作为首期正确性的唯一保障。
Secret、Artifact 和指标
- Secret 认证附加数据绑定 Workspace ID。
- Artifact Object Key 以 Workspace ID 分区。
- Presigned URL 在签发前校验 Workspace 权限。
- 审计事件记录 Workspace ID。
- 指标默认不把 Workspace ID 作为高基数 Label;按 Workspace 的统计通过查询模型提供。
配额
Server 支持 Workspace 级策略入口,包括最大 Agent、并发 Run、总并发、Artifact 容量和保留期。早期版本可以只实现默认策略,但模型和校验入口必须存在。这些限制用于自建实例的资源治理,不用于计费套餐。
明确不做
- 不提供计费、订阅套餐、租户自助注册和公共市场。
- 不提供恶意租户之间的进程级、数据库级、主机级物理隔离。
- 不为每个 Workspace 自动创建独立数据库。
- 不允许 Provider 插件执行任意跨 Workspace 查询。
结果
Workspace 从第一张表、第一条 API 和第一个 Artifact 开始成为正式边界,可以避免团队资源相互混淆。Standalone 通过 Default Workspace 保持简单体验。代价是每个用例、Repository 和测试都必须验证 Workspace Scope;这一成本只服务于用户自建实例内的权限和资源管理,不为公共 SaaS 铺路。
Next Docs Releases 0.1.0 Alpha.1 (`next/docs/releases/0.1.0-alpha.1.md`)
WePush Next 0.1.0-alpha.1 — Public Preview
这是 WePush Next 新架构产品线的首个公开预览版。Classic 客户端不受影响,仍在原有目录中独立演进。
下载内容
wepush-next-0.1.0-alpha.1.tar.gz/.zip:Service、Agent、WebUI 和 Linux/macOS/Windows 服务安装脚本。wepush-next-java-sdk-0.1.0-alpha.1.zip:Java SDK、公开 API 契约和本地 Maven 安装说明。wepush-next-desktop-0.1.0-alpha.1-<os>-<arch>.*:对应平台的 Electron Desktop 应用。wepush-next-0.1.0-alpha.1-sbom.cdx.json:CycloneDX 软件物料清单。SHA256SUMS:全部发布附件的 SHA-256 校验值。
运行 Service、Agent 与 Java SDK 需要 Java 21。具体安装、配置、备份、升级和卸载步骤见分发包中的 docs/deployment-and-operations.md。
第一次使用请阅读《WePush Next 对外使用指南》,其中包含发行物选择、三平台便携启动与服务安装、Desktop 连接、首个安全 Dry Run、正式 API 调用和常见问题。
重要提示
这是未签名的预览发行:macOS 没有 Developer ID/Notarization,Windows 没有 Authenticode。操作系统可能显示未知开发者警告。请只从本 Release 下载并校验 SHA256SUMS;不要将本版本直接用于关键生产业务。
API、数据结构和升级路径仍可能变化。升级前请备份数据库、Secret 主密钥、Agent 身份、插件与 Artifact。完整边界见 PREVIEW-NOTICE.md,安全问题请按 SECURITY.md 私下报告。
主要能力
- Core Engine 与 Provider SPI;内置 HTTP Provider。
- 可独立安装的 Service 与 Agent,支持本机执行和远端 gRPC 执行。
- SQLite Standalone、PostgreSQL Server/HA 基线,以及 Local/S3 Artifact Store。
- Java SDK、OpenAPI、React WebUI、可视化配置和运行中心。
- Electron Desktop 外壳和 Linux、macOS、Windows 服务脚本。
详细变化见 CHANGELOG.md。
Next Docs Releases 0.1.0 Alpha.2 (`next/docs/releases/0.1.0-alpha.2.md`)
WePush Next 0.1.0-alpha.2 — Public Preview
0.1.0-alpha.2 是一次产品方向和安全收口发布。WePush Next 的长期目标已明确为开源、可下载、可安装、由用户自行部署和运维;项目不建设官方公共 SaaS、注册计费订阅、公共租户平台或云 KMS/Secret Manager 集成。Classic 客户端不受影响,继续在原有目录中独立演进。
主要变化
- 新增产品目标、永久边界和
alpha.2至1.0.0的分版本路线图,并同步 README、架构、详细设计、ADR、部署和实现状态文档。 - 新增 Embedded Java SDK,Java 应用可在自身 JVM 中显式装配 Core Engine 和允许的 Provider,无需启动 Service、Agent 或数据库。
- 发行包和独立 Java SDK 附件同时携带 Remote SDK、Embedded SDK、Core API、Provider SPI、Engine 和 HTTP Provider 的 POM/JAR。
- 修复开启 API Security 时 WebUI Artifact 下载没有携带 Bearer Token 的问题。
- S3-compatible Artifact Store 只保留
AUTO/AES256/NONE服务端加密配置,移除 AWS KMS 配置与实现路径。 - 修正 macOS Desktop 主可执文件和应用版本元数据,三平台发布工作流会校验包内产品版本。
完整产品边界与后续计划见《产品目标、边界与路线图》。
下载内容
wepush-next-0.1.0-alpha.2.tar.gz/.zip:Service、Agent、WebUI、OpenAPI、Java SDK 和 Linux/macOS/Windows 服务安装脚本。wepush-next-java-sdk-0.1.0-alpha.2.zip:Remote/Embedded Java SDK 与它们的本地 Maven 安装依赖。wepush-next-desktop-0.1.0-alpha.2-<os>-<arch>.*:Linux x64、macOS Apple Silicon 和 Windows x64 Desktop 应用。wepush-next-0.1.0-alpha.2-sbom.cdx.json:CycloneDX 软件物料清单。SHA256SUMS:全部发布附件的 SHA-256 校验值。
运行 Service、Agent 或 Java SDK 需要 Java 21 或更高版本。安装、便携启动、备份、升级和卸载步骤见《对外使用指南》和《部署与运维》。
预览版提示
本版本仍不是稳定版,API、配置、数据结构和升级路径可能变化,不建议直接用于关键生产业务。Desktop 和安装包未使用商业代码签名:macOS 没有 Developer ID/Notarization,Windows 没有 Authenticode。请只从本 Release 下载并校验 SHA256SUMS。
升级前请备份数据库、Secret 主密钥、Agent 身份、插件与 Artifact。安全问题请按 SECURITY.md 私下报告。
Next Docs Releases 0.1.0 Alpha.3 (`next/docs/releases/0.1.0-alpha.3.md`)
WePush Next 0.1.0-alpha.3 — Public Preview
0.1.0-alpha.3 完成日常使用闭环。用户现在可以在 WebUI 或 Desktop UI 中导入受众、配置资源、先做 Dry Run、核对正式发送影响、查看结果并只重发失败项;不需要手写 Recipient JSON 或直接调用 API。
WePush Next 仍只面向用户自行下载、安装、部署和运维。项目不提供官方公共 SaaS、注册、计费、订阅、公共租户平台、云 KMS/Secret Manager 集成或面向恶意公共租户的物理隔离。
主要变化
- Account 支持编辑、Provider 连接测试、启停和归档。
- Message 内容更新生成不可变 Revision,并支持历史、Diff 和复制;Audience 内容更新生成不可变 Snapshot。
- Audience 支持 CSV/TXT 流式导入、字段映射、最多 20 行预览、数据库去重、错误行 CSV 下载,以及新建或更新快照。
- Job 支持编辑、复制、启停和归档;Schedule 可编辑名称、Job、Cron、时区、Misfire 和启停状态。
- 正式发送前展示 Provider/版本、账号、受众、策略、并发、限速和预计规模,并要求使用短期确认令牌。
- 可筛选 FAILED、UNKNOWN、UNSENT Item 创建关联的新 Run;新 Run 复用来源 Run 的冻结 Snapshot,只执行选中 Item。
- Run、Account、Message、Audience、Job、Schedule 和审计统一使用签名 Cursor Page,并支持名称、状态和时间筛选。
- 总览接入真实 Run 总数、活动数、成功数、问题数、活动/最近 Run 和 14 天趋势。
- Standalone 隐藏 Workspace 复杂度;用户自建 Server 使用真实 Workspace 选择。
- OpenAPI、TypeScript Client 和 Remote Java SDK 已同步;Java SDK 的受众上传使用流式 multipart 文件 BodyPublisher。
验证
- 完整 Maven 测试矩阵通过,包括 OpenAPI、SQLite 13 个迁移、Service/API、Agent/gRPC、架构、SDK 和 HTTP Provider 纵向测试。
- 集成测试覆盖 CSV 导入/去重/错误下载、不可变 Snapshot、正式发送确认、真实 HTTP 部分失败和筛选重发。
- pnpm TypeScript、Vitest、Web Vite 和 Desktop 构建通过。
- PostgreSQL 与 S3-compatible 集成仍由带对应服务的 CI 门禁验证。
下载内容
wepush-next-0.1.0-alpha.3.tar.gz/.zip:Service、Agent、WebUI、OpenAPI、Java SDK 和三平台服务安装脚本。wepush-next-java-sdk-0.1.0-alpha.3.zip:Remote/Embedded Java SDK 与本地 Maven 安装依赖。wepush-next-desktop-0.1.0-alpha.3-<os>-<arch>.*:Linux x64、macOS Apple Silicon 和 Windows x64 Desktop。wepush-next-0.1.0-alpha.3-sbom.cdx.json与SHA256SUMS。
本版本仍不是稳定版,API、配置、数据结构和升级路径可能变化。Desktop 与安装包未使用商业代码签名;请只从本 Release 下载并校验 SHA256SUMS。升级前必须同时备份数据库、Secret 主密钥、Agent 身份、插件和 Artifact。
Next Docs Releases 0.1.0 Alpha.4 (`next/docs/releases/0.1.0-alpha.4.md`)
WePush Next 0.1.0-alpha.4 — Public Preview
0.1.0-alpha.4 把 Next 从“只有通用 HTTP”推进到可使用真实渠道的自部署消息系统。用户在自己的 Service、Agent 或 Embedded Java 应用中,使用自己的 SMTP、企业 IM、阿里云和微信账号完成配置与发送;WePush 不提供公共 SaaS、账号注册、计费订阅、平台代理或云 KMS。
本版新增渠道
| Provider ID | 能力 |
|---|---|
wepush.email.smtp |
NONE/STARTTLS/TLS、可选认证、文本/HTML Multipart、Reply-To、CC/BCC |
wepush.bot.feishu |
Text/Post/Interactive/Raw、自定义机器人签名、关键词与双窗口安全限流 |
wepush.bot.dingtalk |
Text/Markdown/Link/Action Card/Raw、时间戳 HMAC 签名与安全限流 |
wepush.bot.wecom |
Text/Markdown/Raw、官方 Webhook 校验与安全限流 |
wepush.sms.aliyun |
SendSms、POP HMAC-SHA1 签名、模板变量、OutId 追踪与业务错误映射 |
wepush.wechat.official |
模板、订阅、客服消息;Access Token 缓存和安全刷新 |
wepush.wechat.mini |
订阅、统一服务消息;Access Token 缓存和安全刷新 |
wepush.wecom.app |
企业微信任意应用消息 Payload、用户/部门/标签目标和重复检查 |
原有 wepush.http 继续内置,因此 Service 和 Agent 共发现 9 个内置 Provider。每个新 Provider 的实现版本为 0.1.0,与产品版本独立。
统一交付边界
- 每个渠道都有 Account、Message、Recipient Schema,WebUI 从实时 Schema 渲染表单和默认消息示例。
- 密码、Webhook、签名 Secret、AccessKey Secret、AppSecret 和 CorpSecret 只以 SecretRef 出现在配置中。
- Dry Run 完成配置、Recipient、模板和 Payload 校验,但不解析 Secret、不获取 Token、不访问网络。
- 本地 mock 测试覆盖实际 SMTP/HTTP 协议、厂商签名、Token 刷新、响应上限和错误分类;Engine 纵向测试逐一执行 8 个标准渠道。
- Service 与 Agent 通过 ServiceLoader 自动装载;Embedded Java SDK 仍要求调用方显式选择 Factory。
- 机器人、短信和微信系生产实现固定或严格校验厂商官方端点,防止把 Secret 与消息发送到任意主机。
完整最小配置、Recipient 字段和 Provider Code 处理见《内置 Provider 指南》。
发送语义
外部消息渠道通常不提供 Exactly Once。本版本明确区分:
- 连接建立前的确定失败可以按策略重试;
- 厂商显式频控返回
RATE_LIMITED,可携带建议等待时间; - 提交后超时、I/O 或
5xx返回UNKNOWN,默认不自动重发; - 阿里云
OutId只用于追踪,不代表服务端去重; - 企业微信应用消息默认使用厂商提供的短窗口重复检查。
WebUI 与 SDK
- Schema Renderer 新增本地
$ref解析和嵌套 SecretRef 默认对象,渠道配置不再需要手写完整引用骨架。 - 标准渠道消息 Schema 带可编辑示例;正式值仍需替换为用户自己的模板、签名和账号参数。
- 主发行包和独立 Java SDK 附件加入
provider-standardPOM/JAR;SMTP 的 Angus Mail 依赖由 Maven 根据 POM 解析。 - OpenAPI 与 Remote SDK 的 Provider Catalog/Schema 通用契约保持不变,客户端无需针对每个渠道增加硬编码 API。
发行物
wepush-next-0.1.0-alpha.4.tar.gz/.zip:Service、Agent、WebUI、OpenAPI、Java SDK、内置 Provider 和三平台服务安装脚本。wepush-next-java-sdk-0.1.0-alpha.4.zip:Remote/Embedded Java SDK、HTTP/标准渠道 Provider 与本地 Maven 安装依赖。wepush-next-desktop-0.1.0-alpha.4-<os>-<arch>.*:Linux x64、macOS Apple Silicon 和 Windows x64 Desktop。wepush-next-0.1.0-alpha.4-sbom.cdx.json与SHA256SUMS。
预览限制
- 本版本不会替用户验证第三方账号额度、模板审核状态、手机号/关注/订阅授权或厂商侧配额;首次正式发送必须使用自有测试目标小范围验证。
- API、Schema、Provider 行为和升级路径在预览期仍可能不兼容变化。
- Desktop 和安装包未使用商业代码签名或 Apple Notarization,请只从项目 Release 下载并校验 SHA-256。
- 本版本不建设公共 SaaS、自助注册、计费订阅、公共租户平台、云 KMS/Secret Manager 或托管更新服务。
Next Docs Releases 0.1.0 Beta.1 (`next/docs/releases/0.1.0-beta.1.md`)
WePush Next 0.1.0-beta.1 — Public Preview
0.1.0-beta.1 把 Next 的重点从功能扩展转向自部署运维成熟:用户可以在 Windows、Linux 或 macOS 离线安装 Standalone,验证升级、备份和恢复,并通过 Desktop 管理本机 Service 与签名 Provider 插件。产品定位不变:WePush 只提供用户自行部署的开源产品,不提供公共 SaaS、注册计费订阅、云 KMS 或公共租户平台。
安装与发行
- 新增统一
install/install.sh/install/install.ps1,默认安装 Standalone Service;平台目录仍保留service、agent、all高级分组件入口。 - Release 同时提供使用系统 Java 21+ 的精简包,以及 Linux、macOS、Windows 对应架构的
jlinkJava Runtime 完整包。 - Windows 两类发行包都离线携带 WinSW 2.12.0;发行构建锁定来源、文件长度和 SHA-256,安装时再次校验,不临时联网下载。
- 启动脚本优先使用包内 Runtime,并显式允许 SQLite 所需的 JDK Native Access。
升级、备份与恢复
- 三平台 Backup 生成格式化 Manifest、逐文件
SHA256SUMS和一致的配置/数据 Payload,归档完成后立即调用 Restore 校验。 - Restore 在替换前拒绝路径穿越、未列入摘要的额外 Payload 和内容不一致,保留
pre-restore-*原数据,恢复后执行 Installation Health;失败自动恢复原目录。 - Upgrade 在切换新版本后验证 Readiness、Flyway 当前数据库版本和内置 Provider 无网络 Dry Run;失败时切回旧版本并恢复升级前备份。
- 跨平台自测实际比对数据库、Master Key、Artifact、Agent Identity、Journal、Event/Completion Outbox 和插件,并模拟新版本验证失败。
Desktop 与插件
- Desktop 设置页可检测、启动、停止本机 Service,读取最近日志并生成不包含 Token/Secret 的诊断。
- Desktop API Token 使用 Electron
safeStorage;Linux 没有安全 Keyring 时拒绝弱持久化。浏览器只使用当前标签页的sessionStorage,并清理旧localStorageToken。 - Providers 页支持选择本机插件 ZIP、调用 Agent 生产校验器验证清单/摘要/SPI/Ed25519 签名、Stage、Activate 和 Rollback。
- 插件按清单
pluginId使用稳定文件名;激活或回滚后 Agent 不健康会恢复先前版本。
验证与构建质量
- 新增 Playwright 浏览器 E2E、三平台 Desktop 打包后启动冒烟、POSIX/PowerShell 安装运维自测。
- 新增长稳工作流,覆盖 Java 21/25 × SQLite/PostgreSQL,并持续请求 Installation Health 和 System API。
- 清理 SQLite 在未来 JDK 的 Native Access 警告、Agent Shade 的模块/签名/许可证重复资源警告,并为直接基础设施测试补齐日志实现。
发行附件
wepush-next-0.1.0-beta.1-linux-<arch>.tar.gz、macos-<arch>.zip、windows-<arch>.zip:包含 Java Runtime 的完整自部署包。wepush-next-0.1.0-beta.1.tar.gz/.zip:使用系统 Java 21+ 的精简包。wepush-next-desktop-0.1.0-beta.1-<os>-<arch>.*:三平台未签名 Desktop 包。wepush-next-java-sdk-0.1.0-beta.1.zip:Remote/Embedded Java SDK、HTTP/标准渠道 Provider 与本地 Maven 依赖。wepush-next-0.1.0-beta.1-sbom.cdx.json与统一SHA256SUMS。
预览期边界
本版本仍未使用 Apple Developer ID/Notarization 或 Windows Authenticode,不是稳定生产版本。Server/HA 的 PostgreSQL、S3-compatible Store、TLS 和备份由部署者自建运维;WePush 不运行集中控制面,不上传数据或遥测。完整风险见 PREVIEW-NOTICE.md,操作步骤见《对外使用指南》和《部署与运维》。
Next Docs Releases 1.0.0 (`next/docs/releases/1.0.0.md`)
WePush Next 1.0.0 — Stable Self-hosted Release
1.0.0 是 WePush Next 首个稳定自部署版本。它把 Beta 的安装运维闭环提升为明确的 1.x API、配置、数据库和 Agent 协议兼容承诺,并补齐大受众、长时间运行、断线恢复、磁盘写失败、进程崩溃、Agent 失联、三平台升级与卸载门禁。
产品定位保持不变:WePush 提供用户自行下载、安装、部署和运维的开源产品,不运营官方公共 SaaS、集中控制面、注册计费订阅、云 KMS/Secret Manager 或公共租户平台。
稳定性与兼容性
/api/v1OpenAPI 与0.1.0-beta.1保持契约一致,发行门禁对忽略版本号后的公开文档做精确摘要校验。- Beta 已公开的
WEPUSH_*配置键进入 1.x 兼容基线。 - SQLite/PostgreSQL 从 Beta V13 迁移到 V14;迁移只新增稳定兼容元数据,不改写用户数据。
- 最低直接升级版本和最低可回滚版本均为
0.1.0-beta.1。 - Provider SPI Major 1、Remote/Embedded Java SDK 和 Agent 协议 Major 1 进入稳定承诺。
可靠性与故障验证
- Core 自动执行 100,000 Recipient 流式用例,限制读取批大小且不在测试 Sink 中保存全量结果。
- Agent Event/Completion Outbox 在模拟磁盘不可写时保持内存与已持久化状态一致,不跳过事件序号或误删未确认结果。
- Agent 断网后的业务事件重放、进程崩溃后的
UNKNOWNCompletion、Lease/Fence 恢复和失联超时继续由自动化测试覆盖。 - Java 21/25 × SQLite/PostgreSQL 长稳矩阵持续检查 Installation Health 和 System API。
- Linux、macOS、Windows 运维自测覆盖 Beta 成功升级、升级失败自动回退、可连续创建且不会同名碰撞的备份恢复、默认卸载保留数据以及显式 Purge。
安装与发行物
- 三平台完整包内置 Java Runtime;精简包使用用户提供的 Java 21+。
- Windows 包离线携带固定摘要的 WinSW,不在安装时下载服务包装器。
- 提供三平台 Desktop、Remote/Embedded Java SDK、CycloneDX SBOM、独立归档摘要和统一
SHA256SUMS。 - GitHub Release 是稳定 Release,不标记为 prerelease。
未签名边界
按项目既定选择,macOS/Windows Desktop 和安装包仍不使用商业代码签名或 Apple Notarization。用户必须从项目 GitHub Releases 下载并核对 SHA256SUMS;详见 UNSIGNED-NOTICE.md。Provider 插件仍强制 SHA-256 清单和 Ed25519 签名,二者不能混为一谈。
升级
从 0.1.0-beta.1 升级前先阅读《升级与回滚指南》。升级脚本会自动备份,并在数据库迁移和 Installation Health 失败时恢复旧版本和数据。
Next Docs Releases 1.1.0 (`next/docs/releases/1.1.0.md`)
WePush Next 1.1.0 — Self-hosted Governance and Provider Ecosystem
1.1.0 是兼容 1.0.0 的稳定 Minor 发行版,重点补齐运营商短信插件、Workspace 资源治理、自建运维、跨 Run 可靠性、大 Artifact 与 WebUI 可用性。WePush 仍然只提供用户自部署软件,不提供托管控制面、遥测或强制自动更新。
主要更新
- CMPP、SMGP、SGIP、SMPP 以四个独立、Ed25519 签名的 Agent Provider 插件交付。插件不依赖 Classic 源码,也不会把 Core API、Provider SPI、PF4J 或日志 API 打入私有依赖目录。
- Workspace 可限制 Agent 数、活动 Run 数、总发送并发、Artifact 总容量和默认保留期。策略在运行入口、Agent 绑定、Artifact 创建和清理路径统一执行,拒绝结果进入审计。
- 系统管理员可下载结构化脱敏诊断包、使用 Nginx/Traefik/Kubernetes 自建模板,并主动检查新版本。版本检查默认不运行,不上传使用数据。
- 相同账号连续出现认证失败时会跨 Run 熔断;冷却到期自动恢复,管理员也可检查并安全复位。
- PostgreSQL Server/HA 使用
LISTEN/NOTIFY加速待调度 Run、Agent Outbox 和 SSE Event 唤醒。通知只是提示;持久 Outbox、事件游标和周期扫描仍是事实源。 - Agent Artifact 在保留不超过 1 GiB 单次 Presigned Put 的同时,支持 Presigned Multipart、Part 重试、Complete/Abort 和失败清理,最大单文件扩展到 5 TiB。
- WebUI 增加亮色、暗色和跟随系统主题,并改善 1366×768、键盘焦点、语义标签、颜色对比和减少动画体验。
数据与兼容性
- REST/SSE 保持
/api/v1;新增端点和字段均为向后兼容扩展。 - Remote Java SDK 新增 Workspace Policy、诊断包、版本检查和账号熔断接口;已有
1.0.0SDK 调用继续可用。 - Provider SPI 与 Agent 协议 Major 仍为
1,支持1.0.0Service/Agent 在滚动升级窗口内短期混合运行;新功能需双方升级到1.1.0。 - Flyway 从 V14 升至 V17:V15 为 Workspace Policy,V16 为 Account Auth Circuit,V17 为 Artifact Multipart Session。三次迁移只新增表和索引,不改写已有业务数据。
升级步骤
- 下载
1.1.0对应平台发行包和SHA256SUMS,先校验 SHA-256。 - 使用发行包内升级脚本;脚本会在切换前创建完整备份,并在切换后检查 Readiness、Flyway V17 和本地 Provider Dry Run。
- 确认原 Workspace、资源、历史 Run 和 Artifact 可读取,Agent 已重连,再执行自有测试目标的 Dry Run 与小规模真实发送。
- Server/HA 部署应为 S3 Bucket 配置“24 小时后终止未完成 Multipart Upload”的 Lifecycle 规则;WePush 的会话清理仍会主动 Abort,此规则只做孤儿兜底。
- 安装运营商插件前,校验
wepush-provider-trusted-key-1.1.0.env、它的旁路.sha256和统一SHA256SUMS,再把文件中的WEPUSH_PLUGIN_TRUSTED_KEYS=keyId:Base64Ed25519PublicKey原样加入 Agent 环境并重启。不要从插件 ZIP 内或第三方页面获取替代公钥。
完整流程见《升级与回滚指南》;配置和兼容承诺见《兼容性策略》。
回滚
V15–V17 为附加迁移,旧 1.0.0 不会使用新增表;仍应通过升级前完整备份回滚,不要只替换 JAR。恢复会同时还原数据库、Master Key、Artifact、Agent Identity/Journal/Outbox 和插件,避免新旧程序状态错配。
发行边界
- macOS、Windows Desktop 和安装包继续不使用商业代码签名;只从项目 GitHub Release 获取文件,并验证
SHA256SUMS与 CycloneDX SBOM。 - 运营商 Provider 插件与应用发行物不同,必须经过 Ed25519 签名和 Agent 内置生产校验器验签。
- 四个运营商协议当前覆盖主动短信提交、长短信分片和同步提交响应;状态报告与上行短信仍由后续兼容版本扩展。
- Workspace 配额是自建环境的资源治理功能,不是公共多租户的恶意隔离或计费系统。
Next Docs Releases 1.2.0 (`next/docs/releases/1.2.0.md`)
WePush Next 1.2.0 — AI Assistant Access and Java 25
1.2.0 是兼容 1.1.0 的稳定 Minor 发行版。它让 Codex 等 AI 助手通过用户自己的 WePush 完成查询、准备和授权发送,并把 Java 运行基线升级到 25。WePush 仍然只提供用户自部署软件,不提供托管控制面、遥测或强制自动更新。
主要更新
- Desktop 可在 设置 → AI 助手接入 一键安装 Codex MCP 与 Skill,也可只安装 Skill,或导出通用 stdio MCP 配置。WebUI 和完整发行包提供同一个
ai/wepush-ai.mjs,用 Node.js 24+ 安装。 - MCP 由 AI 客户端通过 stdio 启动,再调用本机或用户指定的 WePush REST API。它不新开服务端口,也不把请求转到官方公共平台。
- 助手可以发现 Provider Schema、查询资源、创建消息/受众/任务、执行 Dry Run、在用户授权后提交正式发送,并查询结果或暂停、恢复、取消运行。正式发送继续使用预览确认令牌、Idempotency-Key 和 Workspace RBAC。
- 安装器不读取或复制登录 Token。启用认证时,在 AI 客户端环境中设置
WEPUSH_API_TOKEN。 - Java 构建与运行基线由 21 升级到 25。完整安装包携带 Java 25 运行时;精简包、分组件部署和 Java SDK 需要系统 Java 25+。
数据与兼容性
- REST/SSE 保持
/api/v1,本版本不新增路径或必填字段。 - 不新增 Flyway 迁移,数据库版本保持 V17。
- Provider SPI 与 Agent 协议 Major 仍为
1。1.1.0与1.2.0的 Service/Agent 可在滚动升级窗口内短期混合运行。 - 运营商插件实现版本更新为
1.2.0。新 Run 应安装同一 Release 的签名插件,并使用该 Release 的信任公钥文件。
升级步骤
- 使用精简包或 Java SDK 时,先安装 Java 25+。完整包用户使用包内运行时。
- 下载
1.2.0对应平台发行包和SHA256SUMS,先校验 SHA-256。 - 使用发行包内升级脚本。脚本会在切换前创建完整备份,并在切换后检查 Readiness、Flyway V17 和本地 Provider Dry Run。
- 确认
GET /api/v1/system/info返回1.2.0,原资源、历史 Run 和 Artifact 可读取,Agent 已重连。 - 需要 AI 助手时重新安装 MCP/Skill。
1.1.0安装包没有wepush-ai.mjs。 - 安装运营商插件前,校验
wepush-provider-trusted-key-1.2.0.env、它的旁路.sha256和统一SHA256SUMS,再把其中的WEPUSH_PLUGIN_TRUSTED_KEYS原样加入 Agent 环境并重启。
完整流程见《升级与回滚指南》;接入步骤见《AI 助手接入指南》。
回滚
1.2.0 不新增数据表。受支持的回滚目标是 1.1.0,且必须恢复升级前完整备份,不要只替换 JAR。若环境曾经升级过 1.1.0,数据库中已有 V15–V17,不能把当前数据直接交给 1.0.0。
发行边界
- macOS、Windows Desktop 和安装包继续不使用商业代码签名;只从项目 GitHub Release 获取文件,并验证
SHA256SUMS与 CycloneDX SBOM。 - 运营商 Provider 插件仍必须经过 Ed25519 签名和 Agent 内置生产校验器验签。
- AI 桥接器只允许回环地址使用 HTTP,拒绝 URL 中的密码或查询参数,并拒绝跟随 API 重定向。它不是远程 HTTP MCP,也不提供 OAuth。
--- METRICS ---
- Files Extracted: 36
- Estimated Token Budget: ~50896 tokens
- Canonical Reference: https://codewiki.google/github.com/rememberber/WePush