docs: drop the SPA-footer deploy proof and reanchor notify.rs refs

Four spots told the operator to read the SPA footer to confirm a
deploy landed. The footer no longer renders a version, so restating
that would send an operator looking at a control that no longer
exists. Point them at /api/status instead; the version field itself is
unchanged.

The previous commit inserted 500+ lines into notify.rs, moving
register_push and register_telegram_webhook. Reanchor the two
deployment.md line refs; the other seven refs in that file are before
the insertion point and are unchanged.

Document the new retire-on-change contract in reference.md.
This commit is contained in:
anonsaber 2026-10-01 11:18:07 +08:00
parent 0b27ca03a6
commit 2089a95b79
5 changed files with 15 additions and 9 deletions

View File

@ -71,12 +71,14 @@ section in the configuration page. Enter the public HTTPS origin that receives c
the Worker URL when the gateway is enabled, otherwise the backend origin. The page registers the Worker URL when the gateway is enabled, otherwise the backend origin. The page registers
`/webhook/tg` with Telegram and `/push/jmap` with Stalwart. Credentials remain in the backend; `/webhook/tg` with Telegram and `/push/jmap` with Stalwart. Credentials remain in the backend;
the browser sends only the callback URLs. The protected registration handlers are the browser sends only the callback URLs. The protected registration handlers are
`src/notify.rs:1439` for Telegram and `src/notify.rs:1315` for Stalwart. `src/notify.rs:1619` for Telegram and `src/notify.rs:1464` for Stalwart.
Telegram's secret token and `allowed_updates: ["message"]` are taken from the saved business Telegram's secret token and `allowed_updates: ["message"]` are taken from the saved business
configuration. Stalwart verification and verification-code writeback happen automatically configuration. Stalwart verification and verification-code writeback happen automatically
after registration. Registration can be repeated safely; changing the callback origin updates after registration. Registration can be repeated safely; registering the same origin is
Telegram and creates a Stalwart subscription for the new URL. idempotent. Changing the callback origin updates Telegram (atomic replacement) and, for
Stalwart, destroys the previous subscription before creating the new one under the registration
lock, so the old origin stops receiving pushes.
To remove a subscription, call `POST /api/push/disable` with the same `callback_url`. To remove a subscription, call `POST /api/push/disable` with the same `callback_url`.

View File

@ -420,7 +420,7 @@ Testing: **No integrated test directory**, and no stand-alone util tool module.
### 7.3.1 Configuration Management API ### 7.3.1 Configuration Management API
- `GET /` provides a SPA embedded in the Rust binary; `/assets/config.js` and `/assets/styles.css` provide page resources. The service does not read or write local files while running (`C-NO-LOCAL-WRITE`). - `GET /` provides a SPA embedded in the Rust binary; `/assets/config.js` and `/assets/styles.css` provide page resources. The service does not read or write local files while running (`C-NO-LOCAL-WRITE`).
- `GET /api/status` publicly returns `{ "ready": boolean, "mode": "configured" | "configuration-setup", "missing": string[], "version": string }`, which only lists the missing environment variable names. When `REDIS_URL` or `CONFIG_ENCRYPTION_KEY` is missing, SPA only displays the configuration boot status and missing variables; the management session authorization area is displayed only after the service status is confirmed to be ready=true. `version` is the `BUILD_VERSION` (`<git-sha or nogit>+<UTC build time>`) baked by `build.rs` during compilation. The SPA footer uses this to confirm whether the deployment is actually implemented. - `GET /api/status` publicly returns `{ "ready": boolean, "mode": "configured" | "configuration-setup", "missing": string[], "version": string }`, which only lists the missing environment variable names. When `REDIS_URL` or `CONFIG_ENCRYPTION_KEY` is missing, SPA only displays the configuration boot status and missing variables; the management session authorization area is displayed only after the service status is confirmed to be ready=true. `version` is the `BUILD_VERSION` (`<git-sha or nogit>+<UTC build time>`) baked by `build.rs` during compilation. The SPA no longer renders it, so the API is the only way to confirm a deployment landed.
- `POST /api/admin/session` accepts `Authorization: Bearer <CONFIG_ENCRYPTION_KEY>` and returns `{ "session": "<opaque>", "expires_in": 1800 }`; the admin session only saves the digest in Redis and expires after 1,800 seconds. `POST /api/admin/session/revoke` revokes the current session and returns `204` successfully. - `POST /api/admin/session` accepts `Authorization: Bearer <CONFIG_ENCRYPTION_KEY>` and returns `{ "session": "<opaque>", "expires_in": 1800 }`; the admin session only saves the digest in Redis and expires after 1,800 seconds. `POST /api/admin/session/revoke` revokes the current session and returns `204` successfully.
- The admin page only saves the session in JavaScript memory. Request settings `credentials: omit`, `cache: no-store`, without using cookies, localStorage or sessionStorage. The admin API accepts a valid admin session; the compatibility path also accepts `WORKER_TOKEN`. Worker transparently transmits the authentication header (`SAF-LB-PASSTHRU`) as it is. - The admin page only saves the session in JavaScript memory. Request settings `credentials: omit`, `cache: no-store`, without using cookies, localStorage or sessionStorage. The admin API accepts a valid admin session; the compatibility path also accepts `WORKER_TOKEN`. Worker transparently transmits the authentication header (`SAF-LB-PASSTHRU`) as it is.
- `GET /api/config` and `PUT /api/config` only read and write non-sensitive running parameters: - `GET /api/config` and `PUT /api/config` only read and write non-sensitive running parameters:

View File

@ -420,7 +420,7 @@ message-weave/
### 7.3.1 配置管理 API ### 7.3.1 配置管理 API
- `GET /` 提供嵌入 Rust 二进制的 SPA;`/assets/config.js` 与 `/assets/styles.css` 提供页面资源。服务不在运行时读取或写入本地文件(`C-NO-LOCAL-WRITE`)。 - `GET /` 提供嵌入 Rust 二进制的 SPA;`/assets/config.js` 与 `/assets/styles.css` 提供页面资源。服务不在运行时读取或写入本地文件(`C-NO-LOCAL-WRITE`)。
- `GET /api/status` 公开返回 `{ "ready": boolean, "mode": "configured" | "configuration-setup", "missing": string[], "version": string }`,只列缺少的环境变量名称。缺少 `REDIS_URL` 或 `CONFIG_ENCRYPTION_KEY` 时,SPA 只显示配置引导状态与缺失变量;服务状态确认 ready=true 后才显示管理会话授权区。`version` 是 `build.rs` 在编译期烘焙的 `BUILD_VERSION`(`<git-sha 或 nogit>+<UTC 构建时间>`),SPA 页脚据此确认部署是否真正落地。 - `GET /api/status` 公开返回 `{ "ready": boolean, "mode": "configured" | "configuration-setup", "missing": string[], "version": string }`,只列缺少的环境变量名称。缺少 `REDIS_URL` 或 `CONFIG_ENCRYPTION_KEY` 时,SPA 只显示配置引导状态与缺失变量;服务状态确认 ready=true 后才显示管理会话授权区。`version` 是 `build.rs` 在编译期烘焙的 `BUILD_VERSION`(`<git-sha 或 nogit>+<UTC 构建时间>`)。SPA 已不再渲染它,确认部署落地只能查这个接口。
- `POST /api/admin/session` 接受 `Authorization: Bearer <CONFIG_ENCRYPTION_KEY>`,返回 `{ "session": "<opaque>", "expires_in": 1800 }`;Redis 中保存的是会话令牌的摘要,30 分钟后过期。`POST /api/admin/session/revoke` 可立即撤销会话,成功返回 `204`。 - `POST /api/admin/session` 接受 `Authorization: Bearer <CONFIG_ENCRYPTION_KEY>`,返回 `{ "session": "<opaque>", "expires_in": 1800 }`;Redis 中保存的是会话令牌的摘要,30 分钟后过期。`POST /api/admin/session/revoke` 可立即撤销会话,成功返回 `204`。
- 管理页面仅在 JavaScript 内存中保存 session。请求设置 `credentials: omit`、`cache: no-store`,不使用 Cookie、localStorage 或 sessionStorage。管理 API 接受有效 admin session;兼容路径也接受 `WORKER_TOKEN`。Worker 原样透传鉴权头(`SAF-LB-PASSTHRU`)。 - 管理页面仅在 JavaScript 内存中保存 session。请求设置 `credentials: omit`、`cache: no-store`,不使用 Cookie、localStorage 或 sessionStorage。管理 API 接受有效 admin session;兼容路径也接受 `WORKER_TOKEN`。Worker 原样透传鉴权头(`SAF-LB-PASSTHRU`)。
- `GET /api/config` 与 `PUT /api/config` 只读取和写入非敏感运行参数: - `GET /api/config` 与 `PUT /api/config` 只读取和写入非敏感运行参数:

View File

@ -272,7 +272,8 @@ Response conventions:
`ready` is `false` and `missing` lists the absent required keys (`REDIS_URL`, `ready` is `false` and `missing` lists the absent required keys (`REDIS_URL`,
`CONFIG_ENCRYPTION_KEY`) when a variable is absent — the route itself never errors, so it is `CONFIG_ENCRYPTION_KEY`) when a variable is absent — the route itself never errors, so it is
safe to poll. `version` is the `BUILD_VERSION` string baked in by `build.rs` safe to poll. `version` is the `BUILD_VERSION` string baked in by `build.rs`
(`<git-sha-or-nogit>+<UTC build time>`) and is how the SPA footer proves a deploy landed. (`<git-sha-or-nogit>+<UTC build time>`). The SPA no longer renders it — the API is now the
only way to confirm a deploy landed.
- `/debug/*` returns **503** in exactly one place: `POST /debug/notify` when the business - `/debug/*` returns **503** in exactly one place: `POST /debug/notify` when the business
config is not loaded or there is no outbound client — `service_unavailable` with config is not loaded or there is no outbound client — `service_unavailable` with
`Retry-After: 30`. The three probe endpoints instead report failure `Retry-After: 30`. The three probe endpoints instead report failure
@ -703,7 +704,9 @@ the secret in that response. Re-run `setWebhook` after rotating the secret.
Register Stalwart push with `POST /api/push/register` and an HTTPS `callback_url`. The backend Register Stalwart push with `POST /api/push/register` and an HTTPS `callback_url`. The backend
creates the subscription and completes Stalwart's verification callback. Repeating the same creates the subscription and completes Stalwart's verification callback. Repeating the same
callback URL is idempotent. Use `POST /api/push/disable` with that URL to remove it. callback URL is idempotent. Registering with a different callback URL destroys the previous
subscription first, then creates the new one and repoints the current-pointer mapping, so the old
origin stops receiving pushes. Use `POST /api/push/disable` with that URL to remove it.
The backend has no internal scheduler. [`scripts/cron-drain.sh`](../scripts/cron-drain.sh) The backend has no internal scheduler. [`scripts/cron-drain.sh`](../scripts/cron-drain.sh)
calls `/reconcile` (when `MW_RECONCILE_TOKEN` is set) and then `/worker`. Configure calls `/reconcile` (when `MW_RECONCILE_TOKEN` is set) and then `/worker`. Configure

View File

@ -271,7 +271,7 @@ env var — 并且“DEBUG_TOKEN”非空**(“SAF-DEBUG-GATE”;门被读
“ready”为“false”,“missing”列出了缺少的必需密钥(“REDIS_URL”、 “ready”为“false”,“missing”列出了缺少的必需密钥(“REDIS_URL”、
`CONFIG_ENCRYPTION_KEY`)当变量不存在时 - 路由本身永远不会出错,所以它是 `CONFIG_ENCRYPTION_KEY`)当变量不存在时 - 路由本身永远不会出错,所以它是
可以安全进行民意调查。 `version` 是由 `build.rs` 烘焙的 `BUILD_VERSION` 字符串 可以安全进行民意调查。 `version` 是由 `build.rs` 烘焙的 `BUILD_VERSION` 字符串
(`<git-sha-or-nogit>+<UTC 构建时间>`),这就是 SPA 页脚证明部署已落地的方式。 (`<git-sha-or-nogit>+<UTC 构建时间>`)。SPA 已不再渲染它,确认部署落地只能查这个接口。
- `/debug/*` 在一个地方返回 **503**:业务执行时`POST /debug/notify` - `/debug/*` 在一个地方返回 **503**:业务执行时`POST /debug/notify`
配置未加载或没有出站客户端 — `service_unavailable` 配置未加载或没有出站客户端 — `service_unavailable`
`重试时间:30`。三个探测端点反而报告失败 `重试时间:30`。三个探测端点反而报告失败
@ -694,7 +694,8 @@ Telegram、JMAP、allowlist、worker、reconcile 和 LLM 业务设置驻留在 R
使用 `getWebhookInfo` 检查已注册的 URL;Telegram 不会在响应中回显 Webhook 密钥。更换密钥后,需重新调用 `setWebhook`。 使用 `getWebhookInfo` 检查已注册的 URL;Telegram 不会在响应中回显 Webhook 密钥。更换密钥后,需重新调用 `setWebhook`。
通过 `POST /api/push/register` 和 HTTPS `callback_url` 注册 Stalwart Push。后端会创建订阅并完成 Stalwart 的验证回调; 通过 `POST /api/push/register` 和 HTTPS `callback_url` 注册 Stalwart Push。后端会创建订阅并完成 Stalwart 的验证回调;
重复注册相同 URL 是幂等的。调用 `POST /api/push/disable` 并传入该 URL 可删除订阅。 重复注册相同 URL 是幂等的。用一个不同的回调 URL 注册时,后端会先销毁上一个订阅,再创建新订阅并重指向当前指针映射,
因此旧 origin 不再收到推送。调用 `POST /api/push/disable` 并传入该 URL 可删除订阅。
后端没有内置调度器。外部调度器运行 [`scripts/cron-drain.sh`](../scripts/cron-drain.sh):设置了 `MW_RECONCILE_TOKEN` 时, 后端没有内置调度器。外部调度器运行 [`scripts/cron-drain.sh`](../scripts/cron-drain.sh):设置了 `MW_RECONCILE_TOKEN` 时,
脚本先调用 `/reconcile`,再调用 `/worker`。在调度器的密钥管理器中配置 `MW_APP_URL`、`MW_WORKER_TOKEN`, 脚本先调用 `/reconcile`,再调用 `/worker`。在调度器的密钥管理器中配置 `MW_APP_URL`、`MW_WORKER_TOKEN`,