新增 docs/retired.md(114 行)作为唯一废弃/虚构条目登记处:
- 12 条未采用的路线(teloxide / feature 集 / teloxide-core 回退 /
grammers / BotError 六个未落地变体 / 速率桶+429 分支 / 多步 FSM /
模块子树拆分 / telegram_dispatch 测试 / docker healthcheck 示例)
- 原 teloxide 候选对比评估记录 + FSM 五态状态表(保留决策依据,非当前事实)
- 24 条虚构条目(文档曾写、src/ 从未有):幽灵模块树、delivery:pending 键、
check_config_reload、PushVerification、CancellationToken、Preview、
LlmErr、四个不存在的 LLM 环境变量、llm.call span、熔断/半开设计等
- 2 条已删除文档(docs/todo.md、HANDOFF.md,后者从未进入 git 历史)
- 3 步重启流程
docs/design.md 850 → 764 行:17 处目标形态改写为可核实的当前事实。
重点:§4 压缩并指向 retired.md;§5.2 补 Channel/Notifier/MessageAdapter
三 trait 零实现;§6 树改为真实 12 文件结构;§8.1 换成真实 src/error.rs
(原目标形态 6 变体整体移至 retired.md);§8.3 明标无 tracing/metrics/
优雅关闭;§10.0 GATE-P0 已过;§10.5 明标 notify.rs 平铺单文件;
§11.4-11.7 的 17 条 Q 收敛为一节(均已由已实现代码回答);
§12 全节重写(LlmClient 非 AiClient、AiError 3 变体、无熔断、
无附件、无长邮件保护、read_email 实取全文送 LLM);§12.8/§12.9 合并。
docs/reference.md:删 delivery:pending:{stream} 换真实 stalwart:{jmap,telegram}
流名;删 cloudflare-worker/src/commands.rs 论断;键锚点校正;补网关
fail-closed 无开关说明。
AGENTS.md:§6 注册 retired.md;新增 §6.3 幽灵名规则(文档出现的模块/函数/
键必须是 src/ 真实存在,要留不存在的名字必须同时登记 retired.md);
teloxide 专属措辞通用化为「渠道层不引入第三方 Bot 框架类型」;
保留「不得声称使用未引入依赖」约束。
docs/roadmap.md:item 2/26 按真实数字重写(18 条路由 12 条经 Worker 转发,
不转发的 6 条中仅 2 条需业务直连);决策待定补处置建议。
README / README.zh-CN 阅读表加 retired.md,保持 1:1 对等。
待决策项收敛结论:真剩 2 项(Q-DEP-A Worker 白名单、Q-DEP-B 平台选型),
均为上线时运维选择,不影响代码结构与门禁,可挂起不产生技术债。
src/、web/、cloudflare-worker/ 零改动。基线仍 45 passed / 0 failed / 1 ignored。
|
||
|---|---|---|
| .github/workflows | ||
| cloudflare-worker | ||
| docs | ||
| src | ||
| web | ||
| .dockerignore | ||
| .env.example | ||
| .gitignore | ||
| AGENTS.md | ||
| Cargo.lock | ||
| Cargo.toml | ||
| Dockerfile | ||
| README.md | ||
| README.zh-CN.md | ||
| hoststack.yaml | ||
README.md
MessageWeave
中文文档 / Chinese documentation → README.zh-CN.md
One Telegram bot per mailbox. New mail arrives as metadata; the original is always fetched read-only from your own JMAP server.
1. What it is
MessageWeave sits between a JMAP mail server (for example Stalwart) and a Telegram bot.
- Inbound. A JMAP push subscription tells the service that a message changed. Nothing is polled at rest.
- Outbound. Each event becomes a short, metadata-only Telegram notification: sender, subject, timestamp. Mail bodies are never sent to Telegram.
- On demand. When you ask, the service reads the original through a fresh JMAP read call. Nothing is cached in between.
- Optionally summarised. If you explicitly opt in, an OpenAI-compatible LLM returns a short summary. Requests, responses and attachment bytes are never persisted.
State is an architectural boundary here, not a preference.
- Redis is the only state store. No database, no embedded store, no second store.
- The process writes nothing to disk. No log files, no data files, no local volume.
- No long-lived connections. No SSE, no WebSocket, no long polling.
- Logs go to stdout only. The platform's collector owns log storage and retention.
- What it is not. It is not a mail client, not a proxy, and not a relay. It forwards metadata to one bot; it does not read arbitrary URLs, open tunnels, or broker traffic.
2. 5-minute setup
Requires a reachable Redis 7 instance and a JMAP session plus a Telegram bot token.
cp .env.example .env # fill REDIS_URL and CONFIG_ENCRYPTION_KEY
docker build -t messageweave:latest .
docker run --env-file .env -p 8080:8080 messageweave:latest
Then open http://localhost:8080 in a browser. The first-boot SPA calls
POST /api/bootstrap, stores the business secrets in Redis, and creates the admin session.
From that point the browser configures the service through Redis — no environment variable
is read or written again for business configuration.
For local builds without Docker: cargo build --locked produces the message-weave binary.
If the service answers requests but does nothing useful, check the logs for a startup warning: a missing required variable does not crash the process. It serves a read-only setup router over in-memory state instead. See §3.
3. Configuration entry points
Three layers. They are not interchangeable.
Boot-time environment — 2 required, 2 optional.
| Variable | Required | Default |
|---|---|---|
REDIS_URL |
yes | — |
CONFIG_ENCRYPTION_KEY |
yes | — |
PORT |
no | 8080 |
RUN_MODE |
no | webhook |
RUN_MODE accepts webhook or reconcile. .env.example ships only the two required
variables; the rest are documented defaults.
Business configuration — Redis, written by the browser.
POST /api/bootstrap is the one-shot trust bootstrap; PUT /api/business-config hot-
reloads individual settings within about one second. The process never reads business
secrets from the environment in normal operation.
Health.
GET /healthz returns 200 unconditionally — it is liveness, not readiness. GET /ready
returns 503 until configuration and Redis are available. Probe /ready, not /healthz,
when deciding whether to route traffic.
4. Security boundary, in one sentence
All state lives in Redis; the process reads only two environment variables and writes nothing to disk.
One corollary matters for setup: the bootstrap one-shot token is the password component of
REDIS_URL. The Redis ACL credential is therefore both the application connection
credential and the trust root for bootstrap — keep it private and rotate it as one value.
5. Where to read more
| Document | What it answers |
|---|---|
docs/design.md |
Why the system is shaped this way: data flow, JMAP semantics, Redis streams, AI consent rules |
docs/deployment.md |
How to deploy: Dockerfile, secrets, Redis hosting, webhook/push/reconcile routing, multi-instance load balancing |
docs/reference.md |
Single source of truth for verifiable facts — Redis keys and TTLs, error codes, routes, environment layers, budgets |
docs/roadmap.md |
Gaps, blockers and the next phase |
docs/retired.md |
What was tried and dropped — abandoned routes, unreleased designs, and names that never existed |
AGENTS.md |
Contribution rules, hard boundaries, the stable-ID index for cross-document references |
Facts that matter are traceable. If two documents disagree, docs/reference.md wins.