/** * [ref](C17,[ref]/[ref] 认领,黑板 [ref] 原文即本口的契约):`POST /v1/admin/drain {reason}` —— * 编排壳在发停机信号(SIGTERM)**之前**先调本口显式声明停机因由;server 记入 `drainState.reason`, * draining 期间由 `/health`(`drainReason`)与 503 draining 体(`reason`)透出。未声明 = 两面键缺席 * (additive,不编数)——升级/缩容/驱逐在消费端因此可判别,而不是一律读成「实例没了」。 * * 族属 = adoption / retention-ops 的 operator lane(同一条 `explicitOperatorOk` 门、billable=false、 * **刻意不吃 drain 503**——它正是 drain 开始前要调的口,吃了就自锁)。门序照 retention-ops 逐字: * 身份 → 授权 → 验型。刻意**不进** `isCredentialGatedRewrite` 名单:那族的判据是「授权唯一输入是 * service token ∧ 爆炸半径跨租户持久改写」,本口只写一个进程内观测字段(重启即逝、零持久面), * operator 门(空表恒拒,fail-closed)已是正确强度。 * * reason 校验:非空 trim 后 string ≤ {@link MAX_DRAIN_REASON_CHARS} —— 它会进 /health 与 503 体 * (消费面是监控/壳),超长或怪型响亮拒([ref] 坏值立律),拒绝不留痕(不写半个值)。 * * 🔴 **写侧特权、读侧公开 —— 这条不对称是刻意的,但必须被写的人知道**([ref],2026-08-19 合并重扫 * partial,存活断言):本口是 operator-only + fail-closed(空 `OPERATOR_PRINCIPALS` 恒拒),而透出面里 * `/health` 在 `server.ts` 的鉴权门**之前**、无鉴权、缺省全网卡监听,USAGE 三处白纸黑字把它定为「无需 * 鉴权」—— 任何能连到这个端口的人都读得到这段**运维手打的自由文本**。 * **裁定:行为不改。** 判据三条:① `/health` 免鉴权是既成契约,而它正是这条信息的设计消费者(k8s * probe / LB / 编排器带不了 token,`drainReason` 放这儿的全部理由就是给它们看);② 值的内容完全由 * operator 自选,且窗口只在 draining 期;③ 同一免鉴权体上早已有 `dataRoot` 绝对路径 / `pid` / `port` / * `instanceId` / `configHash` / `storeProbe`,单独收紧本键不改变那面的暴露量级。 * (那条另立的兄弟件 —— `storeProbe.error` 把**原始异常 message 未截断**塞出去、DSN 片段可随之外泄 —— * 已在 [ref] 修掉:免鉴权面上它改成闭集词,原文只走日志轴。两件的判据不同故不合并:本键的值由 operator * 自选且窗口只在 draining 期,`storeProbe.error` 的值则来自不受控的驱动异常。) * ⇒ 真正的缺口是**告诫缺席**:此前路由头注、400 文案、CHANGELOG、wire 契约四处都只说「透出」,没有一处 * 说「其中一面免鉴权」,操作员要拼出「我写的字是公开的」得自己合并两处文档去推理。本批把它写进**写口 * 现场**(400 文案 + 本注 + USAGE 端点条目),因为那是操作员唯一会读到的地方。 * 词表化(把自由文本换成受限枚举)= 行为变更,**不在本批**:它会让「rolling upgrade to 7.36.0」这类真正 * 有用的因由说不出来,取舍要属主拍。 */ import type { IncomingMessage, ServerResponse } from "node:http"; import type { RouteCtx, RouteMatch, RouteIdsOf } from "../route-ctx.js"; export declare const ADMIN_DRAIN_PATH = "/v1/admin/drain"; /** 上限与 approvals 的 reason 族同量级(消费面同为人读短句;非同一常量——两面语义无关,独立演化)。 */ export declare const MAX_DRAIN_REASON_CHARS = 256; export declare function handleAdminDrain(req: IncomingMessage, res: ServerResponse, match: RouteMatch, ctx: RouteCtx): Promise; export declare const ADMIN_DRAIN_ROUTES: readonly [{ readonly id: "admin-drain"; readonly path: "/v1/admin/drain"; readonly methods: readonly ["POST"]; }]; type AdminDrainRouteId = RouteIdsOf; export {}; //# sourceMappingURL=admin-drain.d.ts.map