# v0.1.10 审计导出与告警接收器生产部署证据

## 结论

`v0.1.10` 已于 2026-07-18 完成仓库门禁、签名多架构镜像发布、生产 Cosign 验签、不可变 digest 部署、部署前数据库备份、readiness、运行监控、公网 smoke、管理员 TOTP MFA 注册、真实审计导出，以及 Cloudflare Worker+D1 固定告警接收端的生产切换与四步 receipt 闭环。生产 app 当前运行：

```text
ghcr.io/youniszhang/younis-ai-lab@sha256:ccdab3f407be418d3aa9c92d2e0fdfc14146e1e3b7824eae69a25ba2c90c950a
```

本次发布没有修改 `src/content`、65 篇文章的 frontmatter 日期或文件名前缀。

## GitHub 发布链

- PR #13 以 merge commit `85d9aff35f050b12f1e43ca689300c94397b3b67` 合入 `main`。
- PR CI run `29633577995` 和 merge 后 main CI run `29633620601` 均成功。
- `v0.1.10` 解引用到同一 merge commit。
- Release image run `29633683324` 成功完成 `preflight:ci`、生产依赖审计、`linux/amd64` 与 `linux/arm64` 构建、SBOM、provenance、GitHub OIDC keyless Cosign 签名和 digest allowlist artifact。
- artifact `image-digest-allowlist-v0.1.10` 为 79 字节，内容固定为 `0.1.10 sha256:ccdab3f407be418d3aa9c92d2e0fdfc14146e1e3b7824eae69a25ba2c90c950a`。
- artifact SHA-256 为 `389221dfa520b6fe7fe3686eb2052012118be588f9df67b66685f8055d22ce09`，本地受忽略副本位于 `output/release/v0.1.10-run-29633683324/image-digest-allowlist.txt`。

## 生产部署

生产 allowlist 原子加入 `0.1.10` 后保持 `0600 root:root`，包含既有 `0.1.9` 与本次 `0.1.10` 两条记录，文件 SHA-256 为 `cc12b69bf9c6ad9c37318d24743506b5662ae2634aeeabf9a7099129f97d8893`。

部署脚本先确认 tag 解析出的 digest 与 allowlist 完全一致，再按精确 tag workflow identity 和 GitHub OIDC issuer 完成 Cosign 验签。随后创建数据库备份、运行 migration、仅重建 app 并通过配置的 `http://127.0.0.1:13000/ready` 检查。权威状态为：

```text
COSIGN_VERIFIED=true
BACKUP_RESULT=passed
READINESS_RESULT=passed
RESULT=success
UPDATED_AT=2026-07-18T06:25:32Z
```

部署前备份为 `backups/pre-0.1.10-20260718T062512Z.sql.gz`，SHA-256 为 `dbee917e63e163c33d1e10f927f2cf5e69fd05252f3f3ec15bc0522b8819519f`。

## 运行验证

- app、CMS、CMS PostgreSQL 和主 PostgreSQL 四个容器均为 healthy。
- readiness、app、PostgreSQL、restart、disk、5xx 和 backup 七项监控均为 `ok/notifiedStatus=ok`。
- `blog-runtime-monitor.timer` 为 `active`，最近一次 oneshot 为 `Result=success`、`ExecMainStatus=0`。
- 公网 `/`、`/blog/`、`/manage/` 和 `/ready` 均返回 `200`。
- 未认证 `POST /api/admin/audit-events/export` 返回 `403`，新路由已上线且权限门禁生效。
- 管理员完成 TOTP 注册后，管理页显示 MFA“已启用”，真实导出入口可用。

## 生产审计导出

生产管理员 MFA 会话于 2026-07-18 完成两次真实 JSONL 导出。两次响应均为 `200 application/x-ndjson`，带 `cache-control: no-store`、下载文件名和完整 `x-audit-content-sha256`；页面显示的摘要前缀与响应头一致。

| 导出 | UTC 时间                   | 行数 | 文件字节 | 内容 SHA-256                                                       | events SHA-256                                                     |
| ---- | -------------------------- | ---: | -------: | ------------------------------------------------------------------ | ------------------------------------------------------------------ |
| 首次 | `2026-07-18T08:39:08.578Z` |    2 |      949 | `cc3467ef064a0580c4d0ae1c71d97b001a1a768bb85e04d906b553d039998e0c` | `399a507cfa3879a809f9d0603a28b5f07ae55d0e251afcd25f5bfe3d2d228e8c` |
| 二次 | `2026-07-18T08:40:56.587Z` |    3 |     1459 | `118b568ec1df2e16426e55e8634c983a978f36e1bccc7055bc869f1d6555a2f7` | `75504c04766922f062450065a0416ae95ddad64cfa158134e84f231db62ae5db` |

逐字节计算的文件 SHA-256 与各自响应头完全一致，manifest 的 `rowCount` 与 JSONL 事件数一致，`eventsSha256` 与事件区重新计算结果一致，`truncated=false`。首次导出包含 `mfa.enabled`；二次导出额外包含唯一一条 `audit.exported`，其 metadata 记录首次导出的 `rowCount=2`、`truncated=false` 和完整内容 SHA-256，证明导出动作在返回文件前成功写入审计表。

两份原始文件位于受 `.gitignore` 排除的 `output/production-audit/v0.1.10/`，目录为 `0700`、文件为 `0600`。验证过程没有输出 actor 明细或 metadata 内容，也没有发现未脱敏的敏感 metadata 键。

## 固定告警接收端

Cloudflare Worker+D1 实现由 PR #17 合入，merge commit 为 `809ff371f2a2edf7292d89094827fc7f41fcb96d`；PR CI run `29638755140` 与合并后 main CI run `29638796839` 均成功。生产资源为：

- 固定公开 hostname：`https://younis-alert-receiver.younis-ai-lab.workers.dev`；
- Worker version：`3c6acb17-cfd7-4abc-a38c-cbb38da8fc9d`；
- D1：`younis-alert-receipts`，database ID `548f3914-00c2-4cbe-a45b-8e1282a32d4f`，区域 `WNAM`；
- receipt 留存 90 天，每日清理 cron 为 `17 3 * * *`；
- `preview_urls=false`，没有启用随机版本 URL；
- 高熵路径 token 只存在 Wrangler secret、本地受忽略 `0600` 文件和生产 `monitor.env`，不在 Git、镜像或证据文档中。

公开 `/healthz` 在 DNS/证书传播完成后返回 `200 {"status":"ready"}`，错误告警路径返回 `404`。首次真实 smoke POST 返回 `204`，其 `eventObservedAt=2026-07-18T09:59:27.670Z`、body SHA-256 `cbbc71f1075055815d60306a08137d758d3d3a528ba3ee84ad1971b7f5048560` 和 request-id hash `9cb288e422ae621b` 与 D1 第 1 条 receipt 完全一致。

## 生产切换与四步演练

生产 `/root/younis-ai-lab-runtime/monitor.env` 从 Quick Tunnel 原子切换到固定 Worker，切换前备份为 `monitor.env.before-cloudflare-20260718T100358Z`。新配置继续保持 `MONITOR_REQUIRE_WEBHOOK=true`、`MONITOR_REQUIRE_WEBHOOK_REQUEST_ID=true` 和 `0600 root:root`；完整 webhook URL 的 SHA-256 为 `cf1c4ec526108f5d7a827da189b0dd0f511bc0cb9ea7679dfa2d38d6ff5631b2`，不记录 URL 原值。

关闭 timer 竞争窗口后连续执行四步演练，`trap` 在成功或失败时都恢复 timer。四个生产 transition 与 D1 receipt 的唯一关联为：

| transition           | observedAt                 | request-id hash    | body SHA-256                                                       |
| -------------------- | -------------------------- | ------------------ | ------------------------------------------------------------------ |
| `readiness:firing`   | `2026-07-18T10:05:19.533Z` | `122d016ce9e624b0` | `ce6fadf5b48e06f05329cbec3bd7fa392865fb70a6adfd89764542fdc869f947` |
| `readiness:resolved` | `2026-07-18T10:05:21.180Z` | `4c14c460c65b4424` | `2ce8e081171dc761d5532c5edbfddbce6a921cfe36ee0fd179e6f280c557d186` |
| `backup:firing`      | `2026-07-18T10:05:22.550Z` | `050266ce6d830417` | `62cae4a05d5c9f9cc17501fe96ba6b6f0c45440e24f1abf3562b739be8850ca2` |
| `backup:resolved`    | `2026-07-18T10:05:23.665Z` | `63b789fc2a1a84d9` | `cc122b7a91902c1cd21a085883447f7c6a22861ce4dc927309cf99d8616817a6` |

`verify-monitor-drill.mjs --receipt-file` 于 `2026-07-18T10:10:31.528Z` 逐条验证事件时间、check、status、source、request-id hash 和 body SHA-256，最终 `readiness=ok`、`backup=ok`。权威证据为：

| 工件            | 路径                                                                 | SHA-256                                                            | 权限             |
| --------------- | -------------------------------------------------------------------- | ------------------------------------------------------------------ | ---------------- |
| 生产事件账本    | `state/monitor/events-2026-07-18.jsonl`                              | `bab38744ce808289737a43a64bd9be209f393993b28cc07b09a092b5113506b9` | `0600 root:root` |
| D1 receipt 副本 | `state/monitor/drills/receiver-receipts-2026-07-18-cloudflare.jsonl` | `dd505bfb6562195e0ec42e294d57a117dec1cf1dcf2e47b52cce027f37c5ea80` | `0600 root:root` |
| 四步验收报告    | `state/monitor/drills/cloudflare-fixed-endpoint-2026-07-18.json`     | `f4785438c4ddab8b4cb768c8d7adb78a86000047012533eb743200a744caac59` | `0600 root:root` |
| 最终状态        | `state/monitor/current.json`                                         | `c62fa767a975e4fec1555a0baf1bf6cd20cdf47e21ba891cb5e5b8217ea2b42e` | `0600 root:root` |

D1 原始 JSON 与 JSONL 受忽略副本位于 `output/production-audit/v0.1.10/cloudflare-worker/`，目录 `0700`、文件 `0600`；SHA-256 分别为 `c77f876db0f53a6a729d8bfd10e5265211f9cf23fbeab29a3fbd506a21f21217` 和 `dd505bfb6562195e0ec42e294d57a117dec1cf1dcf2e47b52cce027f37c5ea80`。

演练后 `blog-runtime-monitor.timer` 为 `active/enabled`，七项检查全部为 `ok/notifiedStatus=ok`，服务器访问固定 `/healthz` 返回 `ready`。旧本机 Quick Tunnel receiver 与 cloudflared 进程已停止，生产不再依赖 `trycloudflare.com`。

## 门禁结论

固定 hostname 接收端、生产切换、四步真实投递、D1 receipt 唯一关联、最终健康状态、受限权限、证据哈希和旧 Quick Tunnel 退出均已验证；监控、告警、日志留存和审计导出退出门禁据此关闭。
