Go to file
anonsaber 800d6e0d00 notify: SPA 管理凭据改用 CONFIG_ENCRYPTION_KEY,解除与 Redis ACL 密码耦合
原实现把 SPA 管理凭据派生自 REDIS_URL 的 ACL 密码,并在 handler 侧用
`.is_empty()` 直接拒绝空凭据。托管 Redis 常见配置是 TLS-only 无密码,
那种情况下 `.is_empty()` 恒为真,管理界面永久 401 —— `POST /api/bootstrap`
与 `POST /api/admin/session` 双双不可用,无法完成初始化。

改为直接取 CONFIG_ENCRYPTION_KEY:它本就是启动必填 Secret(64 位 hex /
32 字节,见 config::encryption_key_from_env),启动时已校验,因此生产环境
不可能出现空凭据,handler 侧的空串守卫随之失效,一并移除。Redis ACL 密码
回归本职:只用于数据库连接。

- main.rs:env 只读一次(encryption_key_raw),admin_token 与已校验值不可能
  分叉;删除 Url::parse(redis_url).password() 分支。
- notify.rs:AppState 字段 bootstrap_token → admin_token(4 个构造点同步),
  两处 handler 删除空串守卫,bootstrap() 文档不再称 Redis ACL bootstrap。
- 测试更名 bootstrap_rejects_missing_admin_credential:断言语义不变(测试态
  空凭据收到 Bearer 仍 401),名称改为反映新语义。

门禁全绿:cargo fmt --check / check --locked / clippy --all-targets -D warnings
/ test --locked = 45 passed, 0 failed, 1 ignored。
2026-09-28 12:43:33 +08:00
.github/workflows feat: initial MessageWeave release 2026-09-25 09:14:04 +08:00
cloudflare-worker worker: /reconcile 走 per-route 长超时且绝不故障转移(01a0e595) 2026-09-28 12:18:04 +08:00
docs worker: /reconcile 走 per-route 长超时且绝不故障转移(01a0e595) 2026-09-28 12:18:04 +08:00
src notify: SPA 管理凭据改用 CONFIG_ENCRYPTION_KEY,解除与 Redis ACL 密码耦合 2026-09-28 12:43:33 +08:00
web feat(web): add SPA i18n with browser-language detection (zh/en, default en) 2026-09-27 12:53:36 +08:00
.dockerignore feat: initial MessageWeave release 2026-09-25 09:14:04 +08:00
.env.example refactor: rename project to MessageWeave 2026-09-25 10:29:18 +08:00
.gitignore chore(worker): rename proxy to lb and ignore build cache 2026-09-27 12:52:55 +08:00
AGENTS.md docs: /ready 端到端就绪语义同步 + 删除 Redis 错误映射虚构断言 2026-09-28 12:08:33 +08:00
Cargo.lock fix(redis): install explicit rustls crypto provider 2026-09-25 18:02:21 +08:00
Cargo.toml fix(redis): install explicit rustls crypto provider 2026-09-25 18:02:21 +08:00
Dockerfile chore(worker): rename proxy to lb and ignore build cache 2026-09-27 12:52:55 +08:00
README.md docs: /ready 端到端就绪语义同步 + 删除 Redis 错误映射虚构断言 2026-09-28 12:08:33 +08:00
README.zh-CN.md docs: /ready 端到端就绪语义同步 + 删除 Redis 错误映射虚构断言 2026-09-28 12:08:33 +08:00
hoststack.yaml docs(hoststack): add native Rust deployment config 2026-09-25 18:51:36 +08:00

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, Redis, and both upstreams (a JMAP session GET and Telegram getMe, 3s each) are reachable. Probe /ready, not /healthz, when deciding whether to route traffic — but /ready is the heavier probe (~3s worst case), so a monitor that must stay cheap should watch the gateway's aggregated /healthz.

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.