15 KiB
Raw Blame History

内部狗粮运行保障

本目录把 Wave0/Wave1 的开波和收口条件落实为默认拒绝的本地证据门。工具只读取本地 JSON、TSV 和附件并重算结论,不连接 staging不发告警不执行停服、删档或回滚也不包含 Playwright/Puppeteer。浏览器采样、真玩和截图只能由 mini-desktop 上的项目统一入口 /gstack 产生。

1. 固定波次

波次 创作者 玩家 连续值班覆盖
wave0 3 5 从工具固化的 startedAt 起真实经过至少 24 小时
wave1 10 30 Wave0 收口后开始,真实经过至少 72 小时

初始化只接受这两个名称。startedAt 由工具读取 UTC 当前时间写入CLI 不提供回填参数。重复初始化会校验既有 manifest.json 后返回不覆盖任何已填写证据。Wave1 初始化前会重跑 Wave0 收口门,并把 Wave0 的 close-result.json 哈希、最后一份冻结清单哈希和 closedAt 固化进 Wave1 manifest任一绑定变化、P0、确认发布假阳或超过 4 小时待审积压都会拒绝 Wave1。

deploy/dogfood-ops/init-wave.sh \
  --root /受控证据根 \
  --wave wave0 \
  --rc-commit <commit-pinned-RC的40位哈希>

生成目录中的 JSON 带 _template: true,表示它还不是证据。填写完成后删除该字段。附件统一放在波次目录内的 attachments/JSON/TSV 只写相对路径。绝对路径、.. 路径、符号链接和不存在的附件都会被拒绝。

2. 执行顺序

开波前按以下顺序执行,任一步非零都保持发布冻结:

# 1. 初始化并填写同一波次的全部结构化证据。
deploy/dogfood-ops/init-wave.sh \
  --root /受控证据根 \
  --wave wave0 \
  --rc-commit <commit-pinned-RC的40位哈希>

# 2. 先跑开波门;模板、缺字段、阈值不达标都返回非零。
deploy/dogfood-ops/check-readiness.sh --wave-dir /受控证据根/wave0

# 3. 每日证据采集结束后冻结。当日清单已存在且内容变化时拒绝覆盖。
deploy/dogfood-ops/freeze-evidence.sh \
  --wave-dir /受控证据根/wave0 \
  --date 2026-07-22

# 4. 收口门除全部开波条件外,还会校验真实运行时长和全部实际 UTC 运行日;
#    通过后由工具生成 close-result.json。
deploy/dogfood-ops/check-close.sh --wave-dir /受控证据根/wave0

# 5. 只有 Wave0 收口回执仍可复核时,才允许初始化 Wave1。
deploy/dogfood-ops/init-wave.sh \
  --root /受控证据根 \
  --wave wave1 \
  --rc-commit <Wave1使用的commit-pinned-RC的40位哈希>

GATE_PASS 只表示本地证据在当前规则下可对账,不替代人工判断,不代表 staging 已部署、告警已送达或波次已完成。真实结论必须回到原始截图、网络记录、日志和数据库 ID 核验。

3. 证据清单

文件 必填内容
manifest.json 固定波次、人数、持续时间、commit-pinned rcCommit 和工具生成的 startedAtWave1 另绑定 Wave0 收口回执、最终冻结哈希和 closedAt
rc.json 完整 40 位 commit、tracked 干净标志、rcArtifactRef 与实际 rcArtifactSha256,只证明部署候选
config-identity.json 同一 RC commit、配置包路径、实际 SHA-256、采集时间
snapshot.json MySQL 快照路径/哈希,以及非 staging/production 目标的隔离恢复 PASS、时间和报告
storage-audit.json 对象存储审计报告、实际哈希、采集时间
accounts.json /2:绑定激活 run-id、签名数据库报告、报告哈希/采集时间,以及本波明确账号 ID+用户名
role-permissions.json 创作者本账号允许、跨账号拒绝、审核员只审、审核员改配置拒绝、只读写拒绝;每项带截图
main-chain.json 同一 project/version/game 的 gameArtifactSha256,分别绑定 APPROVED 审核记录、实际 runtime package 和主链阶段真实使用的数据库证据;完整覆盖 create→generate→preview→submit-review→approve→feed→play→telemetry
release-negatives.json DRAFT、REVIEWING、REJECTED、直调绕过、批准后产物漂移、下架后 feed/详情/深链八类拒绝证据
manual-play.json 每个发布游戏在进入 feed 前由 /gstack@mini-desktop 真玩,带测试人、时间和截图
performance.json 固定环境下至少 30 次独立 cold 导航;每条绑定 /gstack 原始 CDP trace/HAR 路径、实际 SHA-256、采集时间与采集器身份
p0.tsv 单一 P0 账本;任一 severity=P0 行都会拒绝开波和收口
telemetry.json 期望完整事件数、完整收到数、原始证据;完整率至少 95%
alerts.json 至少一条接收时间、接收人、外部消息 id 和原始证据,不接受仅写 PASS
oncall.tsv 无空档覆盖本波次完整时长的主备值班与联系方式
feedback.tsv 唯一反馈账本;每行 id、时间、报告人、角色、分类、级别、摘要、状态、owner、证据都完整
manual-acceptance.tsv 角色权限、主链、发布负例、入 feed 前真玩四项人工签认
drills.json stop/delete/rollback 三类演练的精确 ACK、操作人、时间和目录内证据
safety-status.json 固定停止条件逐项评估,任一缺失或触发即拒绝
freezes/*.sha256.json 工具生成的 createdAt、当日全部证据和附件 SHA-256覆盖 startedAt 到收口时的每个实际 UTC 日期,并以前序清单哈希串联
close-result.json 仅在收口门通过后生成;绑定 manifest、最终冻结清单、真实 startedAt/closedAt 和三类 Wave1 阻断计数

所有时间使用带时区 ISO 8601例如 2026-07-22T08:00:00Z。哈希必须是小写 64 位 SHA-256并与附件内容一致。

3.1 账号报告 /2

账号报告不能手写。采集器必须在 mini-infra 以 root signer 身份运行。它先固定通过 root@100.64.0.7 查询 game-staging-mysql/ruoyi-vue-pro,在 MySQL READ ONLY 事务内取得 40 条 CLAIMED 和 12 条 FREE 池记录;再固定查询 mini-infra 本机 infra-postgres/new-api,从容器已有的 POSTGRES_PASSWORD 设置 PGPASSWORD,不把密码放入参数、日志或报告。每条池记录必须按 newapiUserId + newapiTokenId 唯一命中 canonical neice_NNN 用户/token且 token 同时满足 status=1unlimited_quota=falseexpired_time=-1remain_quota>0。任一禁用、耗尽、ID 漂移、重复行或多 token 匹配都会在签名前失败。

# 在可信工作站从指定 RC commit 导出脚本,不使用当前未提交工作树。
set -euo pipefail
umask 077
: "${RC_COMMIT:?请先设置 commit-pinned RC 的 40 位哈希}"
: "${ACTIVATION_RUN_ID:?请先设置激活 run-id}"
[[ "$RC_COMMIT" =~ ^[0-9a-f]{40}$ ]]
[[ "$ACTIVATION_RUN_ID" =~ ^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$ ]]
PINNED_SCRIPT="$(mktemp)"
git show "${RC_COMMIT}:deploy/collect-dogfood-account-report.sh" >"$PINNED_SCRIPT"
chmod 700 "$PINNED_SCRIPT"
LOCAL_SHA="$(shasum -a 256 "$PINNED_SCRIPT" | awk '{print $1}')"

# mini-infra 不要求存在项目仓库;先传入临时名,哈希一致后再原子安装到固定 signer 目录。
REMOTE_INCOMING="/root/dogfood-r1-signer/collect-dogfood-account-report.sh.incoming-$RC_COMMIT"
scp -o BatchMode=yes -o StrictHostKeyChecking=yes \
  "$PINNED_SCRIPT" "root@100.64.0.8:$REMOTE_INCOMING"
REMOTE_SHA="$(ssh -o BatchMode=yes -o StrictHostKeyChecking=yes root@100.64.0.8 \
  "sha256sum '$REMOTE_INCOMING' | cut -d ' ' -f 1")"
[ "$LOCAL_SHA" = "$REMOTE_SHA" ]
ssh -o BatchMode=yes -o StrictHostKeyChecking=yes root@100.64.0.8 \
  "install -o root -g root -m 700 '$REMOTE_INCOMING' /root/dogfood-r1-signer/collect-dogfood-account-report.sh.new && \
  mv -f /root/dogfood-r1-signer/collect-dogfood-account-report.sh.new /root/dogfood-r1-signer/collect-dogfood-account-report.sh && \
  rm -f '$REMOTE_INCOMING'"
rm -f "$PINNED_SCRIPT"

# 在 mini-infra 运行固定安装件;报告和签名均留在 signer 主机的 0700 目录。
ssh -o BatchMode=yes -o StrictHostKeyChecking=yes root@100.64.0.8 \
  "install -d -o root -g root -m 700 /root/dogfood-account-reports && \
  /root/dogfood-r1-signer/collect-dogfood-account-report.sh \
  --activation-run-id '$ACTIVATION_RUN_ID' \
  --rc-commit '$RC_COMMIT' \
  --output /root/dogfood-account-reports/wave0-account-report.json"

采集器使用固定私钥 /root/dogfood-r1-signer/r1-ed25519-private.pem,生成报告及同名 .sig,两者权限均为 0600。报告包含账号身份、池绑定、newapiUsernametokenStatustokenRemainQuotatokenUnlimitedQuotatokenExpiredTime,不输出 token key、手机号、密码或 OAuth token。通过严格主机密钥校验把两个文件取回可信工作站再以 0600 安装进波次附件:

TRANSFER_DIR="$(mktemp -d)"
chmod 700 "$TRANSFER_DIR"
scp -o BatchMode=yes -o StrictHostKeyChecking=yes \
  root@100.64.0.8:/root/dogfood-account-reports/wave0-account-report.json \
  root@100.64.0.8:/root/dogfood-account-reports/wave0-account-report.json.sig \
  "$TRANSFER_DIR/"
install -m 600 "$TRANSFER_DIR/wave0-account-report.json" /受控证据根/wave0/attachments/
install -m 600 "$TRANSFER_DIR/wave0-account-report.json.sig" /受控证据根/wave0/attachments/
rm -rf "$TRANSFER_DIR"

随后填写:

{
  "schema": "dogfood-accounts/2",
  "activationRunId": "activation-20260723T010203Z",
  "databaseReportRef": "attachments/wave0-account-report.json",
  "databaseReportSignatureRef": "attachments/wave0-account-report.json.sig",
  "databaseReportSha256": "<报告文件SHA-256>",
  "databaseReportCapturedAt": "<报告内capturedAt原值>",
  "selectedAccounts": [
    {"id": 1001, "username": "dogfoodc01"},
    {"id": 1101, "username": "dogfoodp01"}
  ]
}

selectedAccounts 必须列出本波全部成员Wave0 精确 3 名创作者和 5 名玩家Wave1 精确 10 名创作者和 30 名玩家。证据门只使用仓内 deploy/trust/r1-attestation-ed25519.pub 验签,不读取 accounts.json、环境变量或命令行中的公钥路径。缺签、签名篡改、错误 issuer、固定查询身份漂移、RC 与 manifest 不一致、new-api user/token 身份重复、token 禁用/耗尽/unlimited/非永久有效都会失败关闭。

4. 首屏 P75 口径

采样环境固定为 mini-desktop、Chrome 146、390x844@1、Tailscale staging。每次禁用缓存后独立导航从 navigation commit 计到首个可交互游戏帧;每条记录使用唯一 navigationId 和唯一原始文件,并标记 cache=coldsource=/gstack@mini-desktopcollector 必须包含 tool=/gstackhost=mini-desktop 和非空 sessionId,且与原始文件内身份一致;collectedAt 必须带时区、不早于波次开始且不在未来。热缓存另行报告,禁止写入 performance.json

门不接受样本自填 durationMs。CDP trace 必须各有一个同 navigationIddogfood.navigationCommitdogfood.firstInteractiveGameFrame 微秒事件HAR 必须在对应 page 的 _gstack 中记录这两个原始时间点。门从受 SHA-256 保护的原始文件重算每条时长,再按 nearest-rank 排序,排名为 ceil(0.75 × n)。30 个样本取第 23 个值;门槛严格为 P75 < 3000ms2999ms 通过3000ms 失败,少于 30 个样本直接失败。

5. 停止条件

出现以下任一情况,立即停止扩容;前六项同时要求全局停止:

  1. 安全事件、跨账号访问、数据丢失、真实扣款、真实广告曝光、未审内容进入 feed。
  2. 生成成功率低于 80%。
  3. 出现任一确认假阳。
  4. 同一阻断根因影响两人。
  5. 埋点缺失超过 5%。
  6. 待审超过 4 小时。
  7. 验收成本突破生成 v3 上限。

safety-status.json 必须逐项使用固定 condition 名称评估。不要删除已触发项来让闸门变绿。

6. 回滚顺序

真实操作只由批准的部署窗口人工执行。本目录不提供环境命令,固定顺序不可调整:

  1. 后端关闭新账号与提交审核。
  2. 后端 feed 总闸切为 fail-closed。
  3. 生成验收从 v3 退回 v3_shadow
  4. 下架受影响游戏。
  5. 回退到上一 commit-pinned RC。
  6. 保留全部证据,创建或关联根因工单。

首个 RC 没有上一版 commit-pinned RC 时,第 5 步不可伪造为已演练;保持 rollback 红灯,等第二个 RC 窗口补证。

7. 停服、删档和回滚演练记录

先由获批人员在隔离目标完成真实演练并保存证据,再用本工具签收。精确 ACK 只证明操作者明确选择了对应波次和动作,不会触发环境操作。

deploy/dogfood-ops/record-drill.sh \
  --wave-dir /受控证据根/wave0 \
  --action stop \
  --ack ACK-DOGFOOD-wave0-stop \
  --operator 值班人ID \
  --performed-at 2026-07-22T08:00:00Z \
  --evidence-ref attachments/stop-drill-report.json

deleterollback 分别使用 ACK-DOGFOOD-wave0-deleteACK-DOGFOOD-wave0-rollback。同一动作、同一记录重复执行幂等返回;已有不同记录时拒绝覆盖。

删档范围只能是当前 batchId 关联的账号、项目、版本、feed 和遥测业务数据。审核记录、安全日志、发布证据、根因工单至少保留 180 天,不得随业务删档删除。删档报告必须列出实际删除对象和保留对象,不能只写总数。

8. 每日冻结

每天完成采集后运行 freeze-evidence.sh--date 必须等于工具当前 UTC 日期,不能填未来日期,也不能回填历史日期;清单中的 createdAt 由工具同时生成。清单覆盖该波次目录下除 freezes/ 和工具生成的 close-result.json 外的全部普通文件,并记录上一日清单哈希。工具拒绝符号链接、日期空档和同日覆盖;收口时按 startedAt 到当前时间的全部 UTC 日期逐日对账,因此跨日运行的 Wave0 通常需要两份,运行 72 小时的 Wave1 通常需要四份,不能在同一分钟伪造三天。若当天冻结后又补证,当日清单会拒绝覆盖,波次保持红灯。

9. 真实采集待办

本地测试 fixture 不可用于开波。真实执行仍需从批准窗口采集:

  • commit-pinned RC、制品和配置快照的原始文件及哈希
  • MySQL 本轮表快照、无网络隔离恢复报告、对象存储前缀审计;
  • wave0/wave1 精确账号与 FREE 额度盘点;
  • /gstack 在 mini-desktop 产生的角色权限、完整主链、八类发布负例、逐游戏入 feed 前真玩截图与网络证据;
  • 30 次独立冷导航的 /gstack 原始 CDP trace/HAR、逐文件哈希、采集时间和采集器身份以及单列的热缓存报告
  • P0、埋点完整率、告警接收回执、值班覆盖和反馈账本
  • 停服、限定 batchId 删档、第二个 RC 回滚的人工演练证据;
  • 每个采集日的 SHA-256 冻结清单。

10. 本地验证

python3 -m unittest discover -s deploy/dogfood-ops/tests -v
find deploy/dogfood-ops -name '*.sh' -exec bash -n {} +
python3 -m py_compile deploy/dogfood-ops/dogfood_ops.py deploy/dogfood-ops/tests/test_dogfood_ops.py
git diff --check -- deploy/dogfood-ops