MessageWeave/cloudflare-worker
anonsaber e0882c7c68 docs: de-projectize AGENTS.md into language-agnostic norms; add docs/charter.md
AGENTS.md (276 -> 239 lines) is now a language-agnostic engineering-norms
document: code style, config and secrets handling, build environment,
testing gates, document governance. It states no project fact and names no
language, framework, dependency, or file. Verified: zero hits for JMAP,
Telegram, Redis, axum, Cloudflare, LLM, *.rs, and every project env var.

All project-specific content moved to the new docs/charter.md (~276 lines):
project goals, locked technology choices, 23 numbered security invariants,
implementation stages, prohibitions, test/acceptance gates with the full
GATE-P0 command and baseline, document boundary rules, and the stable ID
registry.

docs/charter.md §8 is the single authority index for stable IDs: 83 rows,
no duplicates, ID | definition file | one-line statement | category. Four
IDs that were referenced across the docs but never registered are now
registered: ARCH-STATE-REDIS, C-REDIS-EXTERNAL, GATE-UPTIME-KUMA, GATE-DOCS.
All previously registered IDs are preserved.

Defect fixed while rebuilding the registry: SAF-ADMIN-SESSION cited
src/config.rs `admin_session_digest`, a name that has 0 hits in src/ -- a
phantom identifier that survived in the authoritative registry since before
this restructure. Corrected to `session_digest` (src/config.rs:7). All 8
distinct backticked symbols in the registry's definition-file column now
verify against src/.

Two false project facts removed from the docs (both claimed the SPA writes
through POST /api/bootstrap, which web/config.js never calls -- 0 hits; the
actual path is PUT /api/business-config): README.md, README.zh-CN.md,
docs/reference.md. The first successful write creates the configuration and
later writes hot-reload it; POST /api/bootstrap remains the one-shot
automation endpoint.

Cross-reference redirections in 9 files: README.md, README.zh-CN.md,
docs/deployment.md, docs/design.md, docs/reference.md,
cloudflare-worker/README.md, cloudflare-worker/src/index.js,
cloudflare-worker/wrangler.toml now point project facts at charter.md and
language-agnostic quality rules at AGENTS.md.

Four validators extended to cover docs/charter.md: DOCS lists in
audit_anchors.py, check_tables.py, validate_docs.py, check_sec_refs.py, plus
charter.md added to check_sec_refs.py's TARGET_BEFORE regex and to
validate_docs.py's index-file cross-doc exemption.

Gates: GATE-DOCS PASS 4/4 (VALIDATION 0 errors, TABLE STRUCTURE 0
mismatches, ANCHOR AUDIT 227 line-refs / 0 errors, SECTION-REF AUDIT 191
refs / 0 errors). GATE-P0 PASS, baseline unchanged 62 passed / 0 failed /
1 ignored. Anchor audit proves line reachability only, not that the line
contains what the prose claims.
2026-09-28 23:39:26 +08:00
..
src docs: de-projectize AGENTS.md into language-agnostic norms; add docs/charter.md 2026-09-28 23:39:26 +08:00
test worker: /reconcile 走 per-route 长超时且绝不故障转移(01a0e595) 2026-09-28 12:18:04 +08:00
.gitignore feat: initial MessageWeave release 2026-09-25 09:14:04 +08:00
README.md docs: de-projectize AGENTS.md into language-agnostic norms; add docs/charter.md 2026-09-28 23:39:26 +08:00
package.json refactor: rename project to MessageWeave 2026-09-25 10:29:18 +08:00
tsconfig.json feat: initial MessageWeave release 2026-09-25 09:14:04 +08:00
wrangler.toml docs: de-projectize AGENTS.md into language-agnostic norms; add docs/charter.md 2026-09-28 23:39:26 +08:00

README.md

MessageWeave Cloudflare Worker(ARCH-LB-WORKER)

统一 HTTPS 入口 + 多后端 origin 故障转移(HA/LB 子项目)。 透传模型:Worker 不感知业务,原样转发请求到多个 https 后端 origin;仅「超时 / 5xx」做有界故障转移。

本组件是 safelist 受限的边缘负载均衡器(edge load balancer):路由固定 14 条白名单、 后端 origin 在部署期固定且仅允许 https、未知路径一律 404。它只对固定后端做请求转发与 故障转移,不接受任意目标主机,也不提供任何形式的流量中转或访问隐藏能力。

设计依据:docs/design.md §10 / NFR-HA-MULTI-INSTANCE、docs/deployment.md §10。 安全基线与禁止事项:docs/charter.md §3/§5(稳定 ID 注册表 §8)。

目录

  • wrangler.toml — Wrangler 示例配置(name/main/compat/vars;后端 secret 走 wrangler secret)
  • src/index.js — Worker 入口(路由分发、/healthz 聚合、safelist 校验)
  • src/backends.js — origin 解析 + https/凭据/query 校验(C-HTTPS-INBOUND)
  • src/lb.js — 透传转发 + 有界故障转移(SAF-LB-PASSTHRU)
  • src/health.js — 健康聚合探针(MOD-HEALTH-AGG / C-NO-DB)
  • test/*.test.js — 单元/集成测试(零依赖,node --test)
  • package.json — npm 元数据(dev/deploy 调 wrangler;test 纯 node)

零依赖设计

运行时仅依赖 ES2022 标准 + 平台/Node 的 fetch/Headers/Request/Response/URL。 npm run test 直接 node --test test/,无需安装任何生产依赖; wrangler 仅在 check/deploy/dev 中作 devDependency(CI 可选装)。

语义

透传(A,SAF-LB-PASSTHRU)

  • 原样转发 method / headers(含鉴权头)/ body;不鉴权改写。
  • 所有后端实例共享同一组 secret(C-LB-SHARED-SECRETS):同一 TELEGRAM_WEBHOOK_SECRET、 JMAP_PUSH_VERIFICATION_CODE、API_TOKEN(API_TOKEN 在 §10 仅用于多实例互斥,非鉴权)
    • 相同 App Password(各后端实例的 JMAP 可不同,§10.2)。

路由 safelist(ARCH-LB-WORKER / C-LB-SINGLE-REG-URL)

  • 透传公开 GET /api/status 启动状态,以及管理 SPA API:POST /api/admin/session[/revoke]、GET|PUT /api/config、PUT /api/business-config。启动状态只包含缺少的环境变量名称;管理 API 的鉴权仍由后端执行。
  • 只透传 POST /webhook/tg、POST /push/jmap、POST /api/push/register、POST /api/push/disable、POST /reconcile、GET /ready。
  • 其它路径 → 404(不透传,避免 Worker 沦为后端任意路径的跳板)。
  • method 不符 → 405。

有界故障转移(§10.4,deployment.md)

  • 每次请求只试 min(LB_MAX_ATTEMPTS, origins.length) 次;默认 2(首次 + 1 次换实例)。
  • 仅 超时(AbortError)或 5xx 触发换下一个实例;4xx / 2xx / 3xx 直接返回。
  • 起点按 rng 随机化(默认 Math.random),实现双活分摊;单 origin 配置时退化为确定性。
  • 全失败 → 503 All Backends Unavailable(交由 Telegram / Stalwart 自动重投兜底,不丢消息)。
  • 健康缓存(aggregateHealth / LB_HEALTH_TTL_MS)仅服务于 /healthz 聚合视图;转发目前不做健康路由过滤(§10.4「只向健康实例转发」为后续增强,当前靠故障转移兜底)。
  • 例外:POST /reconcile 走 per-route 覆盖。 它是后端同步长任务——后端持集群级锁 lock:reconcile(初租 300s,期间由心跳续期),全局 10s 超时会把它误判为失败并故障转移到第二实例,而第二实例必然立刻返回 409。所以该路由改为:超时取 LB_RECONCILE_TIMEOUT_MS(默认 320000,留足锁初租 + 心跳余量),且 maxAttempts=1 绝不故障转移。其余快路径保持全局默认。

健康聚合(MOD-HEALTH-AGG;C-NO-DB / C-REDIS-ONLY-STATE)

  • GET /healthz(由 Worker 自身承载):按 TTL 缓存(默认 30s)探测各后端 /healthz, 返回 {status: ok|down, available, total, backends:[{origin, up, status}]}; 有 ≥1 后端 up → 200;全部 down → 503。
  • 不代理 Redis/JMAP:Worker 不做数据库侧检查;后端端到端就绪(配置完整性 + Redis 可达 + JMAP session + Telegram getMe,对应 ARCH-READY-BASELINE)由 /ready(透传)承担。

无长连接 / 无密钥日志(§4 红线)

  • 不使用 WebSocket/SSE/长轮询(C-NO-LONG-CONN):纯请求-响应转发,body 一次性 arrayBuffer 回灌。
  • 日志只打「方法 / 路径 / origin / 失败类别」;绝不打印 header / body / 鉴权 secret / App Password(SAF-LOG-PURITY)。

环境变量

变量 必填 说明
BACKEND_ORIGINS_JSON 是(secret) JSON 数组,形如 ["https://a.platform1.example","https://b.platform2.example"];必须全部 https、无内嵌凭据/query/fragment/路径。
LB_REQUEST_TIMEOUT_MS 否 单 origin 请求超时;默认 10000。建议 > 最坏 cold start + 最长 JMAP 拉取。
LB_MAX_ATTEMPTS 否 每请求最多 origin 尝试次数;默认 2(= 1 次故障转移)。POST /reconcile 固定为 1。
LB_RECONCILE_TIMEOUT_MS 否 仅 POST /reconcile 的单 origin 超时覆盖;默认 320000(须大于后端锁初租 300s + 心跳余量)。
LB_HEALTH_TTL_MS 否 健康探测缓存 TTL;默认 30000。

secret 通过 wrangler secret put BACKEND_ORIGINS_JSON 注入;wrangler.toml 里 vars 保持为空,不写明文。

本地 / CI 验证

cd cloudflare-worker

# 单元/集成测试(零依赖):
npm run test
# 等价:node --test test/

# 语法检查 + wrangler dry-run(需 npx wrangler,会拉 devDependency):
npm run check
# 等价:node --check src/*.js && node --check test/*.test.js && wrangler deploy --dry-run

部署示例

cd cloudflare-worker
# 安装 dev dep(仅 wrangler):
npm install
# 注入 secret:
npx wrangler secret put BACKEND_ORIGINS_JSON   # 粘贴 ["https://a.example","https://b.example"]
# 可选调参:
npx wrangler secret put LB_REQUEST_TIMEOUT_MS   # 建议 10000
npx wrangler secret put LB_MAX_ATTEMPTS         # 默认 2
# 部署:
npx wrangler deploy

健康探针:

curl https://lb.messageweave.example/healthz
# => {"status":"ok","available":2,"total":2,"backends":[{"origin":"https://a.example","up":true,"status":200},...]}