@@ -369,6 +369,13 @@ dev 根盘空间在安装后曾接近满盘;2026-06-17 进入 canary 前已清
- `node /tmp/check-pingora-canary-access-log-parity.mjs --realpath --nginx-log-file /var/log/nginx/genarrative-pingora-realpath-canary.access.log --pingora-log-file /var/log/genarrative/pingora-gateway.access.log ... --json` 对账 `6/6 matched` , `missingCount=0` , `mismatchCount=0` ;除 healthz 探针映射到 `__genarrative_pingora/healthz` 外,API config、SpacetimeDB identity、WSS subscribe、静态代表路径和 generated 拒绝路径的 Nginx path 与 Pingora path 完全一致。
- 恢复后 `grep genarrative-pingora-realpath-canary /etc/nginx/conf.d/genarrative.conf` 无匹配,`ss -ltnp | grep :18083` 无监听,证明临时 realpath canary server 已移除;`genarrative-pingora-gateway.service` 仍为 `active` 且 `NRestarts=0` 。
同日下一阶段在 current release `/opt/genarrative/releases/dev-pingora-api-20260617230221` 上重新启用 loopback realpath canary,并用正式 Host `dev.genarrative.world` 验收。第一次把独立 server 写成 `/etc/nginx/conf.d/genarrative-pingora-realpath-canary.conf` 时,因它早于 `genarrative.conf` 加载,`nginx -t` 报 `unknown log format "genarrative_upstream"` ;改为 `/etc/nginx/conf.d/zz-genarrative-pingora-realpath-canary.conf` 后 `nginx -t` 和 reload 均通过。随后:
- `node -- /opt/genarrative/current/scripts/check-pingora-canary-live.mjs --realpath --base-url http://127.0.0.1:18083 --host dev.genarrative.world --json` 通过:healthz `200` 、API config `200` 、SpacetimeDB identity `405` 、静态代表路径 `404` 、generated 拒绝路径 `404` ,全部带 `X-Genarrative-Nginx-Handoff: pingora-realpath-canary` 。
- `node -- /opt/genarrative/current/scripts/check-pingora-canary-access-log-parity.mjs --realpath ... --json` 对账 `12/12 matched` , `missingCount=0` , `mismatchCount=0` 。
- `node -- /opt/genarrative/current/scripts/check-pingora-release-readiness.mjs --release-runtime-only --require-realpath-live ...` 通过 current release 自包含自审、realpath live smoke 和 realpath access log 对账,最终对账 `18/18 matched` 。
- 本阶段没有启用前缀 canary,因此不能同时传 `--require-live` ;否则正式 HTTP 入口会按 Nginx 策略返回 `301` ,并缺少 `X-Genarrative-Nginx-Handoff: pingora-canary` 。当前 public `80/443` 仍由 Nginx 承接,realpath canary 只保留在 `127.0.0.1:18083` 用于后续 loopback 复核。
## dev API release 正式路径验收记录
2026-06-17 已在 dev 机用正式 API release 路径部署包含 Pingora 的发布包 `dev-pingora-api-20260617140915` 。发布包由分支 `codex/pingoranginx` 的 `68bf3b698a885ee2cd248dcba81e4b57460000f8` 构建,包含 `api-server` 、`api-server.sha256` 、`pingora-gateway` 、`pingora-gateway.sha256` 、`release-manifest.json` 和随包部署 / 自审脚本。部署命令从上传到 `/tmp/dev-pingora-api-20260617140915` 的发布包内执行 `scripts/deploy/production-api-deploy.sh` ,目标 release root 为 `/opt/genarrative/releases` , current link 为 `/opt/genarrative/current` 。
@@ -470,7 +477,7 @@ dev 根盘空间在安装后曾接近满盘;2026-06-17 进入 canary 前已清
2. 涉及 Nginx 模板、Pingora 路由、限流分组或路由文档时,同步更新 `deploy/pingora/nginx-route-parity.matrix.json` ,并运行 `npm run check:pingora-route-parity` 与 `cargo test -p pingora-gateway --manifest-path server-rs/Cargo.toml matches_nginx_route_parity_matrix` 。
3. 容器内使用同一份 Web 产物、同一组真实上游地址跑 Pingora smoke,并继续对照 `deploy/nginx/genarrative.conf` 扩展真实上游路由 parity 自动测试。
4. 使用 `deploy/nginx/snippets/genarrative-pingora-canary.conf` 做 Nginx 前缀 canary;启用前先跑 `npm run check:nginx-pingora-canary` 和 `npm run check:pingora-canary-docker` ,有 Nginx 或 Docker 的目标环境分别强制跑 `node scripts/check-nginx-pingora-canary.mjs --require-nginx` 与 `node scripts/check-pingora-canary-docker.mjs --require-docker --pull` ,其中 Docker handoff 会自动对账临时 Nginx 与 Pingora access log。启用后跑 `npm run check:pingora-canary-live` ,再用 current release 随包 access log parity 脚本对账目标机 Nginx 与 Pingora access log。canary live 的 base URL、prefix、Host、额外 path 和 timeout 不能包含换行或 NUL; canary live timeout 和 access log `since-lines` 必须是正整数,非法值直接失败。
5. 前缀 canary 通过后,再使用 `deploy/nginx/snippets/genarrative-pingora-realpath-canary.conf` 做真实路径 canary;它必须作为独立本机 `server` include 到 Nginx `http` 上下文,不能 include 到生产 `443` server 内。启用后跑 `node scripts/check-pingora-canary-live.mjs --realpath --base-url http://127.0.0.1:18083 --host <域名>` ,再用 current release 随包 access log parity 脚本传 `--realpath --nginx-log-file /var/log/nginx/genarrative-pingora-realpath-canary.access.log` 对账 `/api/creation-entry/config` 、`/v1/identity` 和 `/assets/app.js` 等真实路径。
5. 前缀 canary 通过后,再使用 `deploy/nginx/snippets/genarrative-pingora-realpath-canary.conf` 做真实路径 canary;它必须作为独立本机 `server` include 到 Nginx `http` 上下文,不能 include 到生产 `443` server 内。该片段使用 `access_log ... genarrative_upstream` ,如果放入 `/etc/nginx/conf.d/` ,文件名必须保证晚于定义 `log_format genarrative_upstream` 的主站配置加载,例如 `/etc/nginx/conf.d/zz-genarrative-pingora-realpath-canary.conf` ;否则 `nginx -t` 会报 `unknown log format "genarrative_upstream"` 。 启用后跑 `node scripts/check-pingora-canary-live.mjs --realpath --base-url http://127.0.0.1:18083 --host <域名>` ,再用 current release 随包 access log parity 脚本传 `--realpath --nginx-log-file /var/log/nginx/genarrative-pingora-realpath-canary.access.log` 对账 `/api/creation-entry/config` 、`/v1/identity` 和 `/assets/app.js` 等真实路径。
6. 目标机 canary include 后必须跑正式切换聚合门禁:源码 checkout / CI / 构建环境执行默认全量命令 `node scripts/check-pingora-release-readiness.mjs --require-docker --pull-docker --require-nginx --require-live --live-base-url http://127.0.0.1 --live-host <域名> --live-nginx-access-log /var/log/nginx/genarrative.access.log --live-pingora-access-log /var/log/genarrative/pingora-gateway.access.log` ;目标机 current release 执行 `/opt/genarrative/current/scripts/check-pingora-release-readiness.mjs --release-runtime-only --require-live --live-base-url http://127.0.0.1 --live-host <域名> --live-nginx-access-log /var/log/nginx/genarrative.access.log --live-pingora-access-log /var/log/genarrative/pingora-gateway.access.log` 。启用真实路径 canary 时,两种命令都追加 `--require-realpath-live --realpath-live-base-url http://127.0.0.1:18083 --realpath-live-host <域名> --realpath-live-nginx-access-log /var/log/nginx/genarrative-pingora-realpath-canary.access.log --realpath-live-pingora-access-log /var/log/genarrative/pingora-gateway.access.log` 。缺少 Host 会直接失败,避免 live canary 误测默认 vhost; live smoke 后还会按 `request_id` 对账 Nginx 与 Pingora access log,缺少同一请求的 Pingora 日志、状态码、方法或 path 漂移都会失败。
7. 如需评估 Pingora 直连公网入口,必须显式配置 `TLS_LISTEN` 、证书、私钥和 `HTTP_REDIRECT_LISTEN` ; Certbot 证书先用随包 `scripts/deploy/pingora-tls-cert-sync.mjs` 同步到 `/etc/genarrative/pingora-tls/<域名>/` ,不要直接 chmod Let’ s Encrypt live/archive 原路径;绑定 `80/443` 时还必须人工启用 `genarrative-pingora-gateway-direct-entry.conf` drop-in 授予 `CAP_NET_BIND_SERVICE` 。随后用 `npm run check:pingora-gateway-smoke` 覆盖 TLS / HTTP/2 ALPN / redirect / WSS subscribe;目标机必须先跑 `npm run check:pingora-direct-preflight -- --env-file /etc/genarrative/pingora-gateway.env --require-live-env --systemd-cat --check-cert-readable --check-service-env-file --check-service-user-cert-readable --check-service-binary-executable --check-ports-free` ,再跑 `npm run check:pingora-direct-live` 或 release readiness 的 `--require-direct` ,且 `--require-direct` 必须带 direct HTTPS base URL、direct HTTP base URL、正式域名 Host/SNI、redirect Location host、Pingora access log 文件、health patrol env 文件、direct preflight env 文件、systemd drop-in 生效检查、service EnvironmentFile 一致性检查、当前用户和 systemd 服务用户证书可读检查、service 二进制可执行检查、端口释放检查和显式 SpacetimeDB 数据库名,并会拒绝 `--skip-wss` 。高端口 rehearsal 使用 `https://127.0.0.1:<高端口>` 打入但期望 HTTP redirect Location 指向正式域名默认 HTTPS 入口时,额外传 `--direct-redirect-base-url https://<域名>` ; `--direct-redirect-host` 仍必须保留,用于 runbook Host 一致性约束。direct live 会用生成的 `request_id` 反查 Pingora access log;缺少对应日志、method 漂移、path 漂移或 status 漂移都算直连门禁失败。direct preflight 会拒绝开启网关保护但未确认共享保护层的 `GENARRATIVE_PINGORA_GATEWAY_INSTANCE_COUNT>1` 配置;`--env-file` 、`--systemd-service` 、服务用户和 env 中的 listen / cert / key 值都不能包含换行或 NUL,执行 `systemctl cat` 或 `sudo -u <serviceUser> test -r <file>` 前还会复核子命令参数,避免污染参数进入目标机预检命令;direct live 的 URL、Host、redirect base URL、probe token、额外 path、数据库名、access log 路径、timeout 和布尔 env 也不能包含换行或 NUL,且会在发起请求前失败;direct live timeout 必须是正整数,直连相关布尔 env 只接受 `true/false` 、`1/0` 、`yes/no` 、`on/off` 或空值,非法值直接失败。证书申请与续期仍由 Certbot / 外部自动化承担,网关只读取现有文件。
8. 正式直连 runbook 的启用前基础门禁和启用后 `--require-direct` 复核必须调用 `/opt/genarrative/current/scripts/check-pingora-release-readiness.mjs --release-runtime-only` 。该脚本、`scripts/check-pingora-canary-live.mjs` 和 `deploy/nginx/` 必须进入生产 API release、Jenkins API Build 归档、Jenkins API Deploy 复制清单和目标机 current release;缺失时部署应 fail-fast,切换窗口不能依赖源码 checkout 或 Jenkins workspace。