From 762f73a6c61d3bc7b9b74b6948dd910aae6ee2b9 Mon Sep 17 00:00:00 2001 From: Your Name Date: Sun, 14 Jun 2026 18:48:38 +0800 Subject: [PATCH] =?UTF-8?q?docs(security):=20=E6=96=B0=E5=A2=9E=20public?= =?UTF-8?q?=20gateway=20rendered=20diff=20gate=20=E8=8D=89=E7=A8=BF=20[ski?= =?UTF-8?q?p=20ci]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/LOGBOOK.md | 28 ++ .../PUBLIC-GATEWAY-PREFLIGHT-INVENTORY.md | 6 + ...ATEWAY-REDACTED-EXPORT-INTAKE-PREFLIGHT.md | 6 + ...PUBLIC-GATEWAY-RENDERED-DIFF-GATE-DRAFT.md | 111 ++++++ .../SECURITY-SUPPLY-CHAIN-PROGRESS.md | 3 +- ...way-rendered-diff-gate-draft.snapshot.json | 348 ++++++++++++++++++ ...-04-15-MASTER-ai-autonomous-flywheel-v2.md | 15 + ...026-06-04-iwooos-security-governance-p0.md | 1 + ...public-gateway-rendered-diff-gate-draft.py | 262 +++++++++++++ .../security-mirror-progress-guard.py | 148 ++++++++ 10 files changed, 927 insertions(+), 1 deletion(-) create mode 100644 docs/security/PUBLIC-GATEWAY-RENDERED-DIFF-GATE-DRAFT.md create mode 100644 docs/security/public-gateway-rendered-diff-gate-draft.snapshot.json create mode 100644 scripts/security/public-gateway-rendered-diff-gate-draft.py diff --git a/docs/LOGBOOK.md b/docs/LOGBOOK.md index 3cf469389..cdd9ede7e 100644 --- a/docs/LOGBOOK.md +++ b/docs/LOGBOOK.md @@ -1,3 +1,31 @@ +## 2026-06-14|Public Gateway rendered diff / nginx gate 草稿本地完成 + +**背景**:P0-16 已建立 redacted export 收件預檢,但即使未來收到並接受脫敏 ref,也不能直接進 `nginx -t`、reload 或 route smoke。下一步需先把 rendered diff、owner review、`nginx -t` approval package、reload、route smoke、DNS / TLS probe、certbot renew、maintenance window、rollback owner 與 post-check 拆成分階段 gate 草稿。 + +**完成項目**: +- 新增 `scripts/security/public-gateway-rendered-diff-gate-draft.py`,從 committed redacted export intake preflight snapshot 產生 metadata-only rendered diff / nginx gate 草稿。 +- 新增 `docs/security/public-gateway-rendered-diff-gate-draft.snapshot.json`,固定 `diff_gate_candidate_count=3`、`c0_diff_gate_candidate_count=2`、`c1_diff_gate_candidate_count=1`、`preflight_stage_count=7`、`blocked_action_count=14`。 +- 新增 `docs/security/PUBLIC-GATEWAY-RENDERED-DIFF-GATE-DRAFT.md`,說明 diff gate 欄位、preflight stages、blocked actions 與完成度。 +- `security-mirror-progress-guard.py` 已鎖住新 snapshot 的 schema、summary、execution false flags、diff gate ids、preflight stages、blocked actions 與每份候選 false flags。 +- P0 總帳、供應鏈進度、Public Gateway preflight / redacted export intake 文件與 MASTER 已同步 P0-17。 + +**本地驗證**: +- `python3 -m json.tool docs/security/public-gateway-rendered-diff-gate-draft.snapshot.json` 通過。 +- `python3 -m json.tool docs/security/public-gateway-redacted-export-intake-preflight.snapshot.json` 通過。 +- `python3 -m py_compile scripts/security/public-gateway-rendered-diff-gate-draft.py scripts/security/public-gateway-redacted-export-intake-preflight.py scripts/security/security-mirror-progress-guard.py` 通過。 +- 產生器 smoke:`PUBLIC_GATEWAY_RENDERED_DIFF_GATE_DRAFT_OK candidates=3 c0=2 stages=7 blocked=14 runtime_gate=0`。 +- `python3 scripts/security/security-mirror-progress-guard.py --root .` → `SECURITY_MIRROR_PROGRESS_GUARD_OK`。 +- `python3 scripts/security/source-control-owner-response-guard.py --root .` → `SOURCE_CONTROL_OWNER_RESPONSE_GUARD_OK`。 +- `python3 scripts/ops/doc-secrets-sanity-check.py docs .gitea` → `DOC_SECRET_SANITY_OK scanned_files=818`。 +- `git diff --check` 通過。 +- diff-only 工作溝通片語掃描通過;未加入工作溝通原文、委派 XML 或內部抱怨內容。 +- Snapshot assertion 通過:`PUBLIC_GATEWAY_RENDERED_DIFF_GATE_ASSERTIONS_OK candidates=3 c0=2 stages=7 blocked=14 runtime_gate=0`。 + +**完成度與邊界**: +- P0-17 Public Gateway rendered diff / nginx gate 草稿:`100%`。 +- Production browser verification:不適用;本輪只改 repo 內文件、snapshot、guard 與產生器,不變更前端 bundle 或 runtime。 +- redacted export accepted、rendered diff candidate / ready、live evidence、runtime gate、action buttons、Nginx live config、`nginx -t`、reload、DNS / TLS live probe、certbot renew、route smoke、maintenance window accepted、rollback owner accepted、ArgoCD sync、kubectl action、workflow / secret 修改、public route change、agent-bounty runtime、host write、active scan、production write 全部維持 `0 / false`。 + ## 2026-06-14|Public Gateway redacted export 收件預檢本地完成 **背景**:P0-15 已建立 Public Gateway live conf 脫敏匯出請求包,但尚未送件、尚未確認收件人、尚未收到 owner-provided redacted export ref。下一步需先定義收件預檢、拒收規則與 reviewer 分流,避免 raw Nginx live conf、secret payload、未脫敏 log 或未授權 `nginx -t` / reload 要求流入 repo、LOGBOOK、前端或 runtime。 diff --git a/docs/security/PUBLIC-GATEWAY-PREFLIGHT-INVENTORY.md b/docs/security/PUBLIC-GATEWAY-PREFLIGHT-INVENTORY.md index 96048ddf0..0da336b8b 100644 --- a/docs/security/PUBLIC-GATEWAY-PREFLIGHT-INVENTORY.md +++ b/docs/security/PUBLIC-GATEWAY-PREFLIGHT-INVENTORY.md @@ -27,6 +27,12 @@ Public gateway 是所有產品、後台、API、callback、Webhook、ACME 與 We 目前固定為 `intake_candidate_count=3`、`c0_intake_candidate_count=2`、`validation_check_count=10`、`rejection_guard_count=12`、`reviewer_intake_lane_count=5`、`redacted_export_received_count=0`、`accepted_redacted_export_count=0`。此 artifact 只表示未來收到 ref 時的補件、隔離、拒收與 rendered diff preflight 分流,不代表 request sent、recipient confirmed、live conf received / accepted、raw live conf stored、rendered diff ready、`nginx -t`、reload、route smoke、DNS / TLS probe、certbot renew、host write 或 production write。 +## 1.3 2026-06-14 Rendered Diff Gate Draft + +已新增 `docs/security/PUBLIC-GATEWAY-RENDERED-DIFF-GATE-DRAFT.md` 與 `docs/security/public-gateway-rendered-diff-gate-draft.snapshot.json`,將三份 redacted export intake candidate 轉成 rendered diff、`nginx -t`、reload、route smoke 與 rollback / post-check 的分階段 gate 草稿。 + +目前固定為 `diff_gate_candidate_count=3`、`c0_diff_gate_candidate_count=2`、`preflight_stage_count=7`、`blocked_action_count=14`、`redacted_export_accepted_count=0`、`rendered_diff_ready_count=0`、`nginx_test_executed_count=0`、`runtime_gate_count=0`。此 artifact 只表示下一層 gate 的 required shape,不代表 live conf accepted、rendered diff produced、`nginx -t`、reload、route smoke、DNS / TLS probe、certbot renew、host write 或 production write。 + ## 2. repo-only 摘要 | 指標 | 值 | diff --git a/docs/security/PUBLIC-GATEWAY-REDACTED-EXPORT-INTAKE-PREFLIGHT.md b/docs/security/PUBLIC-GATEWAY-REDACTED-EXPORT-INTAKE-PREFLIGHT.md index 58b00b1af..1dc28b4f4 100644 --- a/docs/security/PUBLIC-GATEWAY-REDACTED-EXPORT-INTAKE-PREFLIGHT.md +++ b/docs/security/PUBLIC-GATEWAY-REDACTED-EXPORT-INTAKE-PREFLIGHT.md @@ -91,6 +91,12 @@ P0-15 已建立 Public Gateway live conf 脫敏匯出請求包,但尚未送件 | `reject_execution_request` | 夾帶執行要求時拒收 | | `ready_for_rendered_diff_preflight` | 欄位完整且只有脫敏 ref 時,才可進 rendered diff preflight | +## 6.1 2026-06-14 Rendered Diff Gate Draft + +已新增 `docs/security/PUBLIC-GATEWAY-RENDERED-DIFF-GATE-DRAFT.md` 與 `docs/security/public-gateway-rendered-diff-gate-draft.snapshot.json`,將三份 redacted export intake candidate 轉成 rendered diff / `nginx -t` / reload / route smoke 分階段 gate 草稿。 + +目前固定為 `diff_gate_candidate_count=3`、`c0_diff_gate_candidate_count=2`、`preflight_stage_count=7`、`blocked_action_count=14`、`rendered_diff_ready_count=0`、`nginx_test_executed_count=0`、`runtime_gate_count=0`。此 artifact 只表示未來收件 accepted 後的下一層 gate 形狀,不代表 redacted export accepted、rendered diff ready、`nginx -t`、reload、route smoke、DNS / TLS probe、certbot renew、host write 或 production write。 + ## 7. 指令 產生 committed snapshot: diff --git a/docs/security/PUBLIC-GATEWAY-RENDERED-DIFF-GATE-DRAFT.md b/docs/security/PUBLIC-GATEWAY-RENDERED-DIFF-GATE-DRAFT.md new file mode 100644 index 000000000..b5c59c410 --- /dev/null +++ b/docs/security/PUBLIC-GATEWAY-RENDERED-DIFF-GATE-DRAFT.md @@ -0,0 +1,111 @@ +# IwoooS Public Gateway Rendered Diff Gate 草稿 + +| 項目 | 內容 | +|------|------| +| 日期 | 2026-06-14 | +| 狀態 | `rendered_diff_gate_draft_ready_no_runtime_action` | +| 工具 | `scripts/security/public-gateway-rendered-diff-gate-draft.py` | +| 輸入 | `docs/security/public-gateway-redacted-export-intake-preflight.snapshot.json` | +| Snapshot | `docs/security/public-gateway-rendered-diff-gate-draft.snapshot.json` | +| runtime gate | `0` | + +## 1. 目的 + +P0-16 已建立 redacted export 收件預檢,但即使未來收到並接受脫敏 ref,也不能直接進 `nginx -t`、reload 或 route smoke。P0-17 的目的,是先把 rendered diff、`nginx -t`、reload、route smoke、DNS / TLS probe、certbot renew、maintenance window、rollback owner 與 post-check 拆成分階段 gate 草稿。 + +本文件只定義 future gate draft。它不是 redacted export accepted、不是 rendered diff ready、不是 `nginx -t` 授權、不是 Nginx reload、不是 route smoke、不是 DNS / TLS probe、不是 certbot renew、不是 host write,也不是 production write 或 runtime gate。 + +## 2. 摘要 + +| 指標 | 值 | 說明 | +|------|----|------| +| diff gate candidate count | `3` | 對應三份 redacted export intake candidate | +| C0 diff gate candidate count | `2` | 188 all sites、188 internal tools HTTPS | +| diff gate field count | `12` | 每份 diff gate 草稿欄位 | +| preflight stage count | `7` | redacted export accepted 到 rollback / post-check 的分段 gate | +| blocked action count | `14` | 不可直接執行或不可誤讀的動作 | +| redacted export accepted | `0` | 尚未收到 / 接受 | +| rendered diff candidate / ready | `0 / 0` | 尚未產生 | +| nginx test authorized / executed | `0 / 0` | 尚未批准且未執行 | +| reload authorized / executed | `0 / 0` | 尚未批准且未執行 | +| route smoke authorized / executed | `0 / 0` | 尚未批准且未執行 | +| DNS / TLS probe、certbot renew | `0 / 0` | 尚未批准且未執行 | +| maintenance window / rollback owner | `0 / 0` | 尚未接受 | +| runtime gate / action button | `0 / 0` | 未開啟 | + +## 3. Diff Gate 欄位 + +| 欄位 | 內容規則 | +|------|----------| +| `diff_gate_id` | 固定對應 public gateway rendered diff gate,不建立 runtime action | +| `intake_id` | 對應 P0-16 redacted export intake candidate | +| `export_request_id` | 對應 P0-15 live conf export request | +| `config_id` | 對應 public gateway preflight row | +| `control_tier` | 保留 C0 / C1 風險分級 | +| `source_config_ref` | 指向 repo-only source config snapshot | +| `redacted_live_conf_ref` | 未接受前為空 | +| `rendered_diff_ref` | 未產生前為空 | +| `nginx_test_plan_ref` | 未批准前為空 | +| `route_smoke_plan_ref` | 未批准前為空 | +| `rollback_owner` | 未指定前為 `pending_rollback_owner` | +| `not_approval` | 必須為 `true` | + +## 4. Preflight Stages + +| Stage | 規則 | +|-------|------| +| `redacted_export_acceptance_required` | 必須先有合格 redacted export accepted metadata | +| `normalize_without_raw_conf_storage` | 只可在隔離工作區以脫敏 ref 產生 normalized diff | +| `rendered_diff_owner_review_required` | rendered diff 只可成為 owner review candidate | +| `nginx_test_approval_package_required` | `nginx -t` 必須另有人工批准包、rollback owner 與維護窗口 | +| `reload_approval_separate` | reload 與 public route change 必須獨立批准 | +| `route_smoke_matrix_required` | route smoke 需列 affected routes、預期 status、TLS / WebSocket / ACME checks | +| `postcheck_and_rollback_required` | 未來執行前需 rollback owner、post-check 與失敗撤回條件 | + +## 5. Blocked Actions + +| Action | 邊界 | +|--------|------| +| `read_live_conf_over_ssh` | 未授權不得執行 | +| `store_raw_live_conf` | 不得寫入 repo、LOGBOOK 或前端 | +| `render_diff_from_unredacted_payload` | 必須拒收或隔離 | +| `nginx_test_without_approval` | 不得執行 | +| `nginx_reload_without_approval` | 不得執行 | +| `route_smoke_without_plan` | 不得執行 | +| `dns_probe_without_approval` | 不得執行 | +| `tls_probe_without_approval` | 不得執行 | +| `certbot_renew_without_approval` | 不得執行 | +| `modify_nginx_conf` | 不得改 live conf | +| `modify_dns_tls_config` | 不得改 DNS / TLS / certbot | +| `change_public_route` | 不得變更公開路由 | +| `write_production_host` | 不得主機寫入 | +| `open_runtime_gate` | 不得開 runtime gate | + +## 6. 指令 + +產生 committed snapshot: + +```bash +python3 scripts/security/public-gateway-rendered-diff-gate-draft.py \ + --root . \ + --intake-preflight-report docs/security/public-gateway-redacted-export-intake-preflight.snapshot.json \ + --output docs/security/public-gateway-rendered-diff-gate-draft.snapshot.json \ + --generated-at 2026-06-14T20:05:00+08:00 +``` + +驗證 guard: + +```bash +python3 scripts/security/security-mirror-progress-guard.py --root . +``` + +## 7. 完成度 + +| 工作 | 完成度 | 說明 | +|------|--------|------| +| rendered diff gate draft artifact | `100%` | 產生器、snapshot 與文件已固定 | +| redacted export accepted | `0%` | 尚未收到 / 接受 | +| rendered diff candidate / ready | `0%` | 尚未產生 | +| nginx test / reload / route smoke | `0%` | 尚未批准且未執行 | +| DNS / TLS / certbot | `0%` | 尚未批准且未執行 | +| runtime reload / host write | `0%` | 未授權且未執行 | diff --git a/docs/security/SECURITY-SUPPLY-CHAIN-PROGRESS.md b/docs/security/SECURITY-SUPPLY-CHAIN-PROGRESS.md index 93a9ed5fb..0833d9aa6 100644 --- a/docs/security/SECURITY-SUPPLY-CHAIN-PROGRESS.md +++ b/docs/security/SECURITY-SUPPLY-CHAIN-PROGRESS.md @@ -7,7 +7,7 @@ | 本階段完成 | 資安供應鏈 contract manifest + Source Control Approval Board + Draft Reconcile Plan + Ref Detail Diff + Ref Truth Classification + Source Control Ref Truth Owner Response 收件包 + GitHub Primary Readiness Gate + GitHub Primary Rollback ADR + GitHub Target Owner Decision Response 收件包 + Gitea 認證清冊匯出請求 + Gitea 認證清冊匯入驗收契約 + Gitea 清冊覆蓋 Owner Attestation + Gitea Owner Attestation Approval Lane 對齊 + Gitea Owner Attestation Response 收件包 + Workflow / Secret Name Inventory + Workflow / Secret Name Local Evidence + Workflow / Secret Name Redacted Export Request + Workflow / Secret Name Owner Response 收件包 + Source Control Owner Response Validation Rollup + Kali 112 live integration status + Security Finding contract + Kali scan scope approval package + Security Approval Queue + S3 人工批准 Gate + S3 人工決策紀錄 + S3 人工審查封包 + S3 人工決策狀態轉移 + S3 後續 runtime gate 準備契約 + 鏡像 readiness index + 鏡像接收計畫 + 鏡像事件信封 + 鏡像路由矩陣 + 鏡像驗收契約 + 鏡像隔離契約 + 鏡像 dry-run 報告契約 + 鏡像狀態彙整契約 + IwoooS 前端態勢入口 + IwoooS posture projection contract + IwoooS 既有前端資安頁面整合 + IwoooS 覆蓋與邊界矩陣 + IwoooS 只讀資安處理旅程 + IwoooS owner evidence readiness board + IwoooS host coverage view + IwoooS host action gate matrix + IwoooS host evidence readiness board + IwoooS host evidence collection order + IwoooS host evidence intake preflight + IwoooS host evidence review outcome lanes + IwoooS host evidence review handoff packets + IwoooS host evidence reviewer checklist + IwoooS host evidence reviewer outcome lanes + IwoooS host owner decision candidate packets + IwoooS host owner decision review checklist + IwoooS host owner decision review outcome lanes + IwoooS host owner decision record draft packets + IwoooS host owner decision record draft review checklist + IwoooS host owner decision record draft review outcome lanes + IwoooS host owner decision record write-up packets + IwoooS host owner decision record write-up review checklist + IwoooS host owner decision record write-up review outcome lanes + IwoooS host owner decision record formal candidate packets + IwoooS host owner decision record formal candidate review checklist + IwoooS host owner decision record formal candidate review outcome lanes + IwoooS host owner decision record formal record queue packets + IwoooS host owner decision record formal record queue review checklist + IwoooS host owner decision record formal record queue review outcome lanes + IwoooS host owner decision record human handoff readiness packets + IwoooS host owner decision record human handoff readiness review checklist + IwoooS host owner decision record human handoff readiness review outcome lanes + IwoooS host owner decision record human record owner review candidate packets + IwoooS host owner decision record human record owner review candidate checklist + IwoooS host owner decision record human record owner review candidate outcome lanes + IwoooS host owner decision record human record owner review preparation packets + IwoooS host owner decision record human record owner review preparation checklist + IwoooS progress acceleration lanes + IwoooS owner response next-action focus + IwoooS S4.9 owner response preflight + IwoooS S4.9 owner response request templates + IwoooS progress hold movement gates + IwoooS AwoooP read-only landing readiness + IwoooS AwoooP cross-session handoff packets + AwoooP 首頁 IwoooS 資安鏡像候選 + AwoooP 工作鏈路 IwoooS 資安鏡像候選 + AwoooP 審批佇列 IwoooS owner response 只讀焦點 | | 本階段追加 | AwoooP 合約儀表板 IwoooS 資安契約只讀候選 + AwoooP 租戶管理 IwoooS 資安租戶範圍只讀候選 + AwoooP 執行監控 IwoooS 執行狀態只讀候選 + 既有安全 / 合規頁面 IwoooS 只讀反向橋接 + 告警 / 錯誤 / 授權 / 治理頁面 IwoooS 只讀反向橋接 + 稽核 / 工程審查頁面 IwoooS 深色只讀反向橋接 + IwoooS 前端資安頁面連接狀態板 + IwoooS GitHub 主要來源就緒度只讀狀態板 + AwoooP 工作鏈路 GitHub 主要來源就緒度只讀工作項 + AwoooP 合約儀表板 GitHub 主要來源就緒度合約只讀候選 + AwoooP 審批佇列 GitHub 主要來源就緒度審批邊界 + AwoooP 首頁 GitHub 主要來源就緒度只讀摘要 + AwoooP 租戶管理 GitHub 主要來源就緒度租戶範圍 + AwoooP 執行監控 GitHub 主要來源就緒度執行邊界 + IwoooS / AwoooP 資安可視區塊繁體中文呈現防護檢查 + AwoooP 執行詳情 / 審批詳情繁體中文呈現防護檢查 + AwoooP 首頁負責人回覆驗收總覽 + AwoooP 工作鏈路負責人回覆驗收只讀工作項 + AwoooP 合約儀表板負責人回覆驗收契約只讀候選 + AwoooP 審批佇列負責人回覆驗收只讀審查邊界 + AwoooP 租戶管理負責人回覆驗收租戶範圍 + AwoooP 執行監控負責人回覆驗收執行邊界 + AwoooP 執行詳情負責人回覆驗收詳情邊界 + AwoooP 審批決策負責人回覆驗收審批邊界 + IwoooS AwoooP 資安入口覆蓋狀態板 + IwoooS 階段式資安收斂節奏圖 + IwoooS 下一步人工收件作戰板 + IwoooS 人工回覆安全驗收閘道 + IwoooS 人工回覆審查結果分流 + IwoooS 人工決策準備佇列 + IwoooS 人工決策紀錄草稿防誤用 + IwoooS 人工決策正式紀錄負責人指派確認準備包 + IwoooS 人工決策正式紀錄負責人指派確認清單 + IwoooS 人工決策正式紀錄負責人指派確認結果分流 + IwoooS 人工決策正式紀錄負責人指派決策準備包 + IwoooS 人工決策正式紀錄負責人指派決策檢查清單 + IwoooS S4.9 負責人回覆封套欄位 + IwoooS S4.9 負責人回覆封套送件前檢查 + IwoooS S4.9 負責人回覆封套送件前結果分流 + IwoooS S4.9 負責人回覆送件請求草稿 + IwoooS S4.9 負責人回覆送件鏈路摘要 + IwoooS 低摩擦分階段收斂主控 + IwoooS 低摩擦下一步行動邊界 + IwoooS 64% 進度移動訊號驗收條 + IwoooS 第一個進度解鎖路徑 + IwoooS 第一解鎖證據包 + IwoooS 第一解鎖證據包預檢分流 + IwoooS 第一解鎖證據包補件路徑 + IwoooS 第一解鎖證據包補件送審前檢查 + IwoooS 第一解鎖證據包補件送審結果分流 + IwoooS 第一解鎖證據包 reviewer 指派準備包 + IwoooS 第一解鎖證據包 reviewer 指派前檢查 + IwoooS 第一解鎖證據包 reviewer 指派前檢查結果分流 + IwoooS 正式只讀 landing 與 Kali 112 只讀證據進度重估 | | 本階段追加補充 | IwoooS 目前具體工作地圖 + IwoooS 目前具體交付清單 + IwoooS 目前阻塞與解除條件 + IwoooS 三軸進度與全產品套用範圍 + IwoooS 全產品分階段套用台帳 + IwoooS 全產品 rollout 波次驗收門檻 + IwoooS 全產品 rollout 驗收結果分流 + IwoooS 全產品證據接線地圖 + IwoooS 全產品證據接線預檢 + IwoooS 全產品證據接線預檢結果分流 + IwoooS 全產品預檢補件回收台帳 + IwoooS 全產品補件重試門檻 + IwoooS 全產品重試結果分流 + IwoooS 全產品人工審查候選準備 + IwoooS 全產品人工審查候選預檢 + IwoooS 全產品人工審查候選預檢結果分流 + IwoooS 全產品人工審查候選預檢補件回收台帳 + IwoooS 全產品人工審查候選預檢補件重試門檻 + IwoooS 全產品只讀套用快照 + P2-145 owner response acceptance gate 正式驗證完成 | -| P0 追加 | IwoooS P0 配置控管優先序前台正式驗證完成;Nginx public gateway、DNS / TLS / certbot、K8s / ArgoCD / production manifests、Workflow / runner / secret metadata、Public / admin / API runtime config、agent-bounty runtime / treasury 六類先列為即時風險配置;高價值配置 Gate 已補上 `k8s/nginx/**`、`scripts/ops/**/*cert*`、`scripts/ops/**/*tls*`,sample 從 `matched=0 / C0=0` 收斂到 `matched=3 / C0=2`;Gate 預設工作樹 preflight 已可讀取 staged / unstaged / untracked,本地 smoke 對臨時 `k8s/nginx/*` 檔命中 C0;Owner Packet snapshot 已同步為 `packets=3 / c0=2`,Coverage snapshot 已同步最新 patterns;IwoooS / AwoooP 前台 Owner Packet 摘要已正式驗證 `packet=3 / c0=2`,feature commit `e999c16b`、deploy marker `16c6b983`、Gitea code-review `2973` / CD `2972` success;IwoooS posture projection snapshot / schema / guard 已同步 `packet=3 / c0=2`,不再保留舊 `1 / 0` 口徑;高價值配置 Owner Packet 收件預檢已新增 `checks=9 / lanes=5 / required_fields=27 / blocked_requests=16`;高價值配置 Owner Request 草稿包已新增 `drafts=3 / handoff_fields=11 / forbidden_payloads=12 / sent=0`;Public Gateway live conf 匯出請求包已新增 `requests=3 / c0=2 / redaction_rules=8 / received=0`;Public Gateway redacted export 收件預檢已新增 `candidates=3 / c0=2 / checks=10 / rejection_guards=12 / received=0 / accepted=0`;owner response / live evidence / runtime gate / action buttons 仍全部為 0 | +| P0 追加 | IwoooS P0 配置控管優先序前台正式驗證完成;Nginx public gateway、DNS / TLS / certbot、K8s / ArgoCD / production manifests、Workflow / runner / secret metadata、Public / admin / API runtime config、agent-bounty runtime / treasury 六類先列為即時風險配置;高價值配置 Gate 已補上 `k8s/nginx/**`、`scripts/ops/**/*cert*`、`scripts/ops/**/*tls*`,sample 從 `matched=0 / C0=0` 收斂到 `matched=3 / C0=2`;Gate 預設工作樹 preflight 已可讀取 staged / unstaged / untracked,本地 smoke 對臨時 `k8s/nginx/*` 檔命中 C0;Owner Packet snapshot 已同步為 `packets=3 / c0=2`,Coverage snapshot 已同步最新 patterns;IwoooS / AwoooP 前台 Owner Packet 摘要已正式驗證 `packet=3 / c0=2`,feature commit `e999c16b`、deploy marker `16c6b983`、Gitea code-review `2973` / CD `2972` success;IwoooS posture projection snapshot / schema / guard 已同步 `packet=3 / c0=2`,不再保留舊 `1 / 0` 口徑;高價值配置 Owner Packet 收件預檢已新增 `checks=9 / lanes=5 / required_fields=27 / blocked_requests=16`;高價值配置 Owner Request 草稿包已新增 `drafts=3 / handoff_fields=11 / forbidden_payloads=12 / sent=0`;Public Gateway live conf 匯出請求包已新增 `requests=3 / c0=2 / redaction_rules=8 / received=0`;Public Gateway redacted export 收件預檢已新增 `candidates=3 / c0=2 / checks=10 / rejection_guards=12 / received=0 / accepted=0`;Public Gateway rendered diff / nginx gate 草稿已新增 `candidates=3 / c0=2 / stages=7 / blocked=14 / rendered_diff=0 / runtime=0`;owner response / live evidence / runtime gate / action buttons 仍全部為 0 | | 原則 | 低摩擦分階段;文件、schema、read-only evidence 優先;不做 runtime enforcement、不切 primary | | P0 主控板 | `docs/workplans/2026-06-04-iwooos-security-governance-p0.md` | @@ -391,6 +391,7 @@ headline 要再往上,需要 S4.9 / S4.10 / S4.11 / S4.12 任一 owner respons | 高價值配置 Owner Request 草稿包 | 本地完成 | `high-value-config-owner-request-draft.snapshot.json` 已固定 `request_draft_count=3`、`c0_request_draft_count=2`、`handoff_envelope_field_count=11`、`required_owner_field_total=27`、`forbidden_payload_count=12`、`request_sent_count=0`,並由 `security-mirror-progress-guard.py` 鎖住 request ids、handoff fields 與 false flags | 這是人工送件前草稿與 handoff envelope,不是 request sent、recipient confirmed、audit event emitted、owner response received / accepted、reviewer queue write、Nginx reload、DNS / TLS change、workflow 修改、host write、active scan、production write 或 runtime gate;不需要 production browser smoke | | Public Gateway live conf 匯出請求包 | 本地完成 | `public-gateway-live-conf-export-request.snapshot.json` 已固定 `export_request_count=3`、`c0_export_request_count=2`、`redaction_rule_count=8`、`redacted_export_received_count=0`、`raw_live_conf_stored_count=0`,並由 `security-mirror-progress-guard.py` 鎖住 export ids、redaction rules 與 false flags | 這是 owner-provided redacted export request draft,不是 request sent、live conf received、rendered diff ready、`nginx -t`、Nginx reload、route smoke、DNS / TLS probe、certbot renew、host write、production write 或 runtime gate;不需要 production browser smoke | | Public Gateway redacted export 收件預檢 | 本地完成 | `public-gateway-redacted-export-intake-preflight.snapshot.json` 已固定 `intake_candidate_count=3`、`c0_intake_candidate_count=2`、`validation_check_count=10`、`rejection_guard_count=12`、`reviewer_intake_lane_count=5`、`redacted_export_received_count=0`、`accepted_redacted_export_count=0`,並由 `security-mirror-progress-guard.py` 鎖住 intake ids、validation checks、rejection guards、reviewer lanes 與 false flags | 這是 redacted export 收件前預檢,不是 request sent、recipient confirmed、redacted export received / accepted、raw live conf stored、rendered diff ready、`nginx -t`、Nginx reload、route smoke、DNS / TLS probe、certbot renew、host write、production write 或 runtime gate;不需要 production browser smoke | +| Public Gateway rendered diff / nginx gate 草稿 | 本地完成 | `public-gateway-rendered-diff-gate-draft.snapshot.json` 已固定 `diff_gate_candidate_count=3`、`c0_diff_gate_candidate_count=2`、`preflight_stage_count=7`、`blocked_action_count=14`、`redacted_export_accepted_count=0`、`rendered_diff_ready_count=0`、`nginx_test_executed_count=0`、`runtime_gate_count=0`,並由 `security-mirror-progress-guard.py` 鎖住 diff gate ids、preflight stages、blocked actions 與 false flags | 這是 rendered diff / nginx / route smoke 分階段 gate 草稿,不是 redacted export accepted、rendered diff ready、`nginx -t`、Nginx reload、route smoke、DNS / TLS probe、certbot renew、host write、production write 或 runtime gate;不需要 production browser smoke | | S3 approval gate | 進行中 | `security_approval_gate_v1` 已建立 8 個人工 gate items:7 pending、1 block candidate、0 approved | 不得繞過人工批准;批准後仍需 follow-up runtime gate | | S3.0 人工批准 Gate 契約 | 完成草案 | 定義批准範圍、決策選項、required reviewers、still forbidden 與 follow-up runtime gate | AwoooP 可記錄決策,不可執行 gate item | | S3.1 人工決策紀錄契約 | 完成草案 | `security_approval_decision_record_v1` 已建立;目前 0 筆 decision records、0 個 runtime action 授權 | AwoooP 可稽核決策,不可把決策當執行 | diff --git a/docs/security/public-gateway-rendered-diff-gate-draft.snapshot.json b/docs/security/public-gateway-rendered-diff-gate-draft.snapshot.json new file mode 100644 index 000000000..3731208f3 --- /dev/null +++ b/docs/security/public-gateway-rendered-diff-gate-draft.snapshot.json @@ -0,0 +1,348 @@ +{ + "blocked_actions": [ + "read_live_conf_over_ssh", + "store_raw_live_conf", + "render_diff_from_unredacted_payload", + "nginx_test_without_approval", + "nginx_reload_without_approval", + "route_smoke_without_plan", + "dns_probe_without_approval", + "tls_probe_without_approval", + "certbot_renew_without_approval", + "modify_nginx_conf", + "modify_dns_tls_config", + "change_public_route", + "write_production_host", + "open_runtime_gate" + ], + "diff_gate_candidates": [ + { + "action_buttons_allowed": false, + "blocked_actions": [ + "read_live_conf_over_ssh", + "store_raw_live_conf", + "render_diff_from_unredacted_payload", + "nginx_test_without_approval", + "nginx_reload_without_approval", + "route_smoke_without_plan", + "dns_probe_without_approval", + "tls_probe_without_approval", + "certbot_renew_without_approval", + "modify_nginx_conf", + "modify_dns_tls_config", + "change_public_route", + "write_production_host", + "open_runtime_gate" + ], + "certbot_renew_authorized": false, + "config_id": "host188_all_sites", + "control_tier": "C0", + "diff_gate_fields": [ + "diff_gate_id", + "intake_id", + "export_request_id", + "config_id", + "control_tier", + "source_config_ref", + "redacted_live_conf_ref", + "rendered_diff_ref", + "nginx_test_plan_ref", + "route_smoke_plan_ref", + "rollback_owner", + "not_approval" + ], + "diff_gate_id": "public_gateway_rendered_diff_gate:host188_all_sites", + "dns_tls_probe_authorized": false, + "export_request_id": "public_gateway_live_conf_export:host188_all_sites", + "host": "192.168.0.188", + "intake_id": "public_gateway_redacted_export_intake:host188_all_sites", + "live_path": "/etc/nginx/sites-enabled/all-sites.conf", + "maintenance_window_accepted": false, + "nginx_reload_authorized": false, + "nginx_reload_executed": false, + "nginx_test_authorized": false, + "nginx_test_executed": false, + "nginx_test_plan_ref": null, + "not_approval": true, + "owner_gate": "public_gateway_owner_response_required", + "preflight_stages": [ + "redacted_export_acceptance_required", + "normalize_without_raw_conf_storage", + "rendered_diff_owner_review_required", + "nginx_test_approval_package_required", + "reload_approval_separate", + "route_smoke_matrix_required", + "postcheck_and_rollback_required" + ], + "production_write_authorized": false, + "redacted_export_accepted": false, + "redacted_live_conf_ref": null, + "rendered_diff_candidate": false, + "rendered_diff_ready": false, + "rendered_diff_ref": null, + "rollback_owner": "pending_rollback_owner", + "rollback_owner_accepted": false, + "route_smoke_authorized": false, + "route_smoke_executed": false, + "route_smoke_plan_ref": null, + "runtime_gate": false, + "source_config_ref": "docs/security/public-gateway-preflight-inventory.snapshot.json", + "status": "draft_waiting_redacted_export_acceptance" + }, + { + "action_buttons_allowed": false, + "blocked_actions": [ + "read_live_conf_over_ssh", + "store_raw_live_conf", + "render_diff_from_unredacted_payload", + "nginx_test_without_approval", + "nginx_reload_without_approval", + "route_smoke_without_plan", + "dns_probe_without_approval", + "tls_probe_without_approval", + "certbot_renew_without_approval", + "modify_nginx_conf", + "modify_dns_tls_config", + "change_public_route", + "write_production_host", + "open_runtime_gate" + ], + "certbot_renew_authorized": false, + "config_id": "host188_internal_tools_https", + "control_tier": "C0", + "diff_gate_fields": [ + "diff_gate_id", + "intake_id", + "export_request_id", + "config_id", + "control_tier", + "source_config_ref", + "redacted_live_conf_ref", + "rendered_diff_ref", + "nginx_test_plan_ref", + "route_smoke_plan_ref", + "rollback_owner", + "not_approval" + ], + "diff_gate_id": "public_gateway_rendered_diff_gate:host188_internal_tools_https", + "dns_tls_probe_authorized": false, + "export_request_id": "public_gateway_live_conf_export:host188_internal_tools_https", + "host": "192.168.0.188", + "intake_id": "public_gateway_redacted_export_intake:host188_internal_tools_https", + "live_path": "owner_confirmation_required", + "maintenance_window_accepted": false, + "nginx_reload_authorized": false, + "nginx_reload_executed": false, + "nginx_test_authorized": false, + "nginx_test_executed": false, + "nginx_test_plan_ref": null, + "not_approval": true, + "owner_gate": "public_tools_owner_response_required", + "preflight_stages": [ + "redacted_export_acceptance_required", + "normalize_without_raw_conf_storage", + "rendered_diff_owner_review_required", + "nginx_test_approval_package_required", + "reload_approval_separate", + "route_smoke_matrix_required", + "postcheck_and_rollback_required" + ], + "production_write_authorized": false, + "redacted_export_accepted": false, + "redacted_live_conf_ref": null, + "rendered_diff_candidate": false, + "rendered_diff_ready": false, + "rendered_diff_ref": null, + "rollback_owner": "pending_rollback_owner", + "rollback_owner_accepted": false, + "route_smoke_authorized": false, + "route_smoke_executed": false, + "route_smoke_plan_ref": null, + "runtime_gate": false, + "source_config_ref": "docs/security/public-gateway-preflight-inventory.snapshot.json", + "status": "draft_waiting_redacted_export_acceptance" + }, + { + "action_buttons_allowed": false, + "blocked_actions": [ + "read_live_conf_over_ssh", + "store_raw_live_conf", + "render_diff_from_unredacted_payload", + "nginx_test_without_approval", + "nginx_reload_without_approval", + "route_smoke_without_plan", + "dns_probe_without_approval", + "tls_probe_without_approval", + "certbot_renew_without_approval", + "modify_nginx_conf", + "modify_dns_tls_config", + "change_public_route", + "write_production_host", + "open_runtime_gate" + ], + "certbot_renew_authorized": false, + "config_id": "host110_ollama_proxy", + "control_tier": "C1", + "diff_gate_fields": [ + "diff_gate_id", + "intake_id", + "export_request_id", + "config_id", + "control_tier", + "source_config_ref", + "redacted_live_conf_ref", + "rendered_diff_ref", + "nginx_test_plan_ref", + "route_smoke_plan_ref", + "rollback_owner", + "not_approval" + ], + "diff_gate_id": "public_gateway_rendered_diff_gate:host110_ollama_proxy", + "dns_tls_probe_authorized": false, + "export_request_id": "public_gateway_live_conf_export:host110_ollama_proxy", + "host": "192.168.0.110", + "intake_id": "public_gateway_redacted_export_intake:host110_ollama_proxy", + "live_path": "/etc/nginx/sites-enabled/110-ollama-proxy.conf", + "maintenance_window_accepted": false, + "nginx_reload_authorized": false, + "nginx_reload_executed": false, + "nginx_test_authorized": false, + "nginx_test_executed": false, + "nginx_test_plan_ref": null, + "not_approval": true, + "owner_gate": "ai_provider_proxy_owner_response_required", + "preflight_stages": [ + "redacted_export_acceptance_required", + "normalize_without_raw_conf_storage", + "rendered_diff_owner_review_required", + "nginx_test_approval_package_required", + "reload_approval_separate", + "route_smoke_matrix_required", + "postcheck_and_rollback_required" + ], + "production_write_authorized": false, + "redacted_export_accepted": false, + "redacted_live_conf_ref": null, + "rendered_diff_candidate": false, + "rendered_diff_ready": false, + "rendered_diff_ref": null, + "rollback_owner": "pending_rollback_owner", + "rollback_owner_accepted": false, + "route_smoke_authorized": false, + "route_smoke_executed": false, + "route_smoke_plan_ref": null, + "runtime_gate": false, + "source_config_ref": "docs/security/public-gateway-preflight-inventory.snapshot.json", + "status": "draft_waiting_redacted_export_acceptance" + } + ], + "diff_gate_fields": [ + "diff_gate_id", + "intake_id", + "export_request_id", + "config_id", + "control_tier", + "source_config_ref", + "redacted_live_conf_ref", + "rendered_diff_ref", + "nginx_test_plan_ref", + "route_smoke_plan_ref", + "rollback_owner", + "not_approval" + ], + "execution_boundaries": { + "action_buttons_allowed": false, + "certbot_renew_authorized": false, + "dns_tls_probe_authorized": false, + "nginx_reload_authorized": false, + "nginx_reload_executed": false, + "nginx_test_authorized": false, + "nginx_test_executed": false, + "not_authorization": true, + "production_write_authorized": false, + "read_live_conf_over_ssh": false, + "rendered_diff_authorized": false, + "route_smoke_authorized": false, + "route_smoke_executed": false, + "runtime_execution_authorized": false, + "store_raw_live_conf": false + }, + "generated_at": "2026-06-14T20:05:00+08:00", + "git_commit": "f856df1c", + "next_steps": [ + "等待 redacted export accepted metadata;沒有 accepted metadata 前不得產生 rendered diff。", + "rendered diff candidate 必須另走 reviewer / owner review,不得自動進 nginx -t。", + "`nginx -t`、reload、route smoke、DNS / TLS probe、certbot renew 與 production write 都必須另行人工批准。" + ], + "preflight_stages": [ + { + "gate_effect": "不增加 rendered_diff / nginx_test / reload / route_smoke / runtime gate。", + "instruction": "必須先有合格 redacted export accepted metadata,否則不得產生 rendered diff。", + "stage_id": "redacted_export_acceptance_required", + "status": "required_before_runtime_action" + }, + { + "gate_effect": "不增加 rendered_diff / nginx_test / reload / route_smoke / runtime gate。", + "instruction": "只可在隔離工作區以脫敏 ref 產生 normalized diff,不得把 raw live conf 寫入 repo。", + "stage_id": "normalize_without_raw_conf_storage", + "status": "required_before_runtime_action" + }, + { + "gate_effect": "不增加 rendered_diff / nginx_test / reload / route_smoke / runtime gate。", + "instruction": "rendered diff 只可成為 owner review candidate,不自動批准。", + "stage_id": "rendered_diff_owner_review_required", + "status": "required_before_runtime_action" + }, + { + "gate_effect": "不增加 rendered_diff / nginx_test / reload / route_smoke / runtime gate。", + "instruction": "`nginx -t` 必須另有人工批准包、rollback owner 與維護窗口。", + "stage_id": "nginx_test_approval_package_required", + "status": "required_before_runtime_action" + }, + { + "gate_effect": "不增加 rendered_diff / nginx_test / reload / route_smoke / runtime gate。", + "instruction": "reload 與 public route change 必須獨立於 rendered diff 與 nginx -t。", + "stage_id": "reload_approval_separate", + "status": "required_before_runtime_action" + }, + { + "gate_effect": "不增加 rendered_diff / nginx_test / reload / route_smoke / runtime gate。", + "instruction": "route smoke 需列出 affected routes、預期 status、TLS / WebSocket / ACME checks。", + "stage_id": "route_smoke_matrix_required", + "status": "required_before_runtime_action" + }, + { + "gate_effect": "不增加 rendered_diff / nginx_test / reload / route_smoke / runtime gate。", + "instruction": "任何未來執行前都需 rollback owner、post-check 與失敗撤回條件。", + "stage_id": "postcheck_and_rollback_required", + "status": "required_before_runtime_action" + } + ], + "schema_version": "public_gateway_rendered_diff_gate_draft_v1", + "source_intake_preflight_schema_version": "public_gateway_redacted_export_intake_preflight_v1", + "source_intake_preflight_status": "redacted_export_intake_preflight_ready_no_payload_received", + "status": "rendered_diff_gate_draft_ready_no_runtime_action", + "summary": { + "action_button_count": 0, + "blocked_action_count": 14, + "c0_diff_gate_candidate_count": 2, + "c1_diff_gate_candidate_count": 1, + "certbot_renew_authorized_count": 0, + "diff_gate_candidate_count": 3, + "diff_gate_field_count": 12, + "dns_tls_probe_authorized_count": 0, + "maintenance_window_accepted_count": 0, + "nginx_reload_authorized_count": 0, + "nginx_reload_executed_count": 0, + "nginx_test_authorized_count": 0, + "nginx_test_executed_count": 0, + "preflight_stage_count": 7, + "redacted_export_accepted_count": 0, + "rendered_diff_candidate_count": 0, + "rendered_diff_ready_count": 0, + "rollback_owner_accepted_count": 0, + "route_smoke_authorized_count": 0, + "route_smoke_executed_count": 0, + "runtime_gate_count": 0 + } +} diff --git a/docs/superpowers/specs/2026-04-15-MASTER-ai-autonomous-flywheel-v2.md b/docs/superpowers/specs/2026-04-15-MASTER-ai-autonomous-flywheel-v2.md index cff4b75f7..d7c4f4553 100644 --- a/docs/superpowers/specs/2026-04-15-MASTER-ai-autonomous-flywheel-v2.md +++ b/docs/superpowers/specs/2026-04-15-MASTER-ai-autonomous-flywheel-v2.md @@ -691,6 +691,7 @@ Alert / Sentry / SigNoz / Gitea / Market Watch / Operator | `docs/security/high-value-config-owner-request-draft.snapshot.json` + `scripts/security/high-value-config-owner-request-draft.py` | 高價值配置 Owner Request 草稿包已本地完成;承接收件預檢 snapshot,固定 3 份 request draft、2 份 C0 request draft、11 欄 handoff envelope、27 個 required owner fields、12 類 forbidden payload 與 16 類 blocked request;`security-mirror-progress-guard.py` 已鎖住 schema、summary、false flags、request ids、handoff fields 與每份草稿 false flags;request sent、recipient confirmed、audit event emitted、received、accepted、rejected、reviewer queue write、runtime gate、action buttons 仍全部為 `0` | 這是人工送件前 request draft 與 handoff envelope,不是 request sent、recipient confirmed、audit event emitted、owner response received / accepted、reviewer queue write、Nginx reload、DNS / TLS change、workflow 修改、host write、active scan、production write 或 runtime gate | | `docs/security/public-gateway-live-conf-export-request.snapshot.json` + `scripts/security/public-gateway-live-conf-export-request.py` | Public Gateway live conf 匯出請求包已本地完成;承接 public gateway preflight inventory,固定 3 份 owner-provided redacted export request、2 份 C0、1 份 C1、12 欄 export request field、8 條 redaction rule;redacted export received、raw live conf stored、rendered diff、`nginx -t`、reload、route smoke、runtime gate、action buttons 仍全部為 `0` | 這是 live conf 脫敏匯出請求草稿,不是 request sent、live conf received、raw conf 收存、rendered diff ready、`nginx -t`、Nginx reload、DNS / TLS probe、certbot renew、route smoke、host write、production write 或 runtime gate | | `docs/security/public-gateway-redacted-export-intake-preflight.snapshot.json` + `scripts/security/public-gateway-redacted-export-intake-preflight.py` | Public Gateway redacted export 收件預檢已本地完成;承接 live conf 匯出請求包,固定 3 份 intake candidate、2 份 C0、1 份 C1、11 欄 intake field、10 項 validation check、12 類 rejection guard 與 5 條 reviewer intake lane;request sent、recipient confirmed、redacted export received、accepted、quarantine、rejected、rendered diff、`nginx -t`、reload、route smoke、runtime gate、action buttons 仍全部為 `0` | 這是 redacted export 收件前預檢,不是 request sent、recipient confirmed、live conf received / accepted、raw conf 收存、rendered diff ready、`nginx -t`、Nginx reload、DNS / TLS probe、certbot renew、route smoke、host write、production write 或 runtime gate | +| `docs/security/public-gateway-rendered-diff-gate-draft.snapshot.json` + `scripts/security/public-gateway-rendered-diff-gate-draft.py` | Public Gateway rendered diff / nginx gate 草稿已本地完成;承接 redacted export intake preflight,固定 3 份 diff gate candidate、2 份 C0、1 份 C1、12 欄 diff gate field、7 個 preflight stage 與 14 類 blocked action;redacted export accepted、rendered diff ready、`nginx -t` executed、reload、route smoke、DNS / TLS probe、certbot renew、runtime gate、action buttons 仍全部為 `0` | 這是 rendered diff / nginx / route smoke 分階段 gate 草稿,不是 redacted export accepted、rendered diff ready、`nginx -t`、Nginx reload、DNS / TLS probe、certbot renew、route smoke、host write、production write 或 runtime gate | | `docs/evaluations/ai_agent_live_read_model_gate_2026-06-11.json` + `GET /api/v1/agents/agent-live-read-model-gate` | P2-403B AgentSession / Redis Streams live read model gate;定義 safe fields、Redis envelope、worker gate、rollback plan 與 no-write smoke,不連 DB、不讀寫 Redis、不啟動 worker | #### 3.2.1c 2026-06-11 AI Agent 主動營運委派與版本生命週期契約 @@ -833,6 +834,7 @@ Repo / registry / release notes / K8s / host / observability / backup evidence 72. 建立高價值配置 Owner Request 草稿包與 dispatch handoff。✅ 本地完成;新增 `high-value-config-owner-request-draft.py`、snapshot 與說明文件,將三包收件預檢轉成 `request_draft_count=3`、`c0_request_draft_count=2`、`handoff_envelope_field_count=11`、`required_owner_field_total=27`、`forbidden_payload_count=12` 的人工送件前草稿包;`security-mirror-progress-guard.py` 已固定 schema、summary、false flags、request ids、handoff fields 與每份草稿 false flags;request sent、recipient confirmed、audit event emitted、received、accepted、rejected、reviewer queue write、runtime gate、action buttons 仍全部為 `0`。這不是 request sent、owner response received / accepted、Nginx reload、DNS / TLS change、workflow 修改、host write、active scan、production write 或 runtime gate。 73. 建立 Public Gateway live conf 匯出請求包。✅ 本地完成;新增 `public-gateway-live-conf-export-request.py`、snapshot 與說明文件,將 public gateway preflight inventory 的三份 source config 轉成 owner-provided redacted export request draft;固定 `export_request_count=3`、`c0_export_request_count=2`、`c1_export_request_count=1`、`export_request_field_count=12`、`redaction_rule_count=8`,並保持 redacted export received、raw live conf stored、rendered diff、`nginx -t`、reload、route smoke、runtime gate、action buttons 全部為 `0`。這不是 request sent、live conf received、raw conf 收存、rendered diff ready、`nginx -t`、Nginx reload、DNS / TLS probe、certbot renew、host write、production write 或 runtime gate。 74. 建立 Public Gateway redacted export 收件預檢。✅ 本地完成;新增 `public-gateway-redacted-export-intake-preflight.py`、snapshot 與說明文件,將三份 live conf export request 轉成 `intake_candidate_count=3`、`c0_intake_candidate_count=2`、`validation_check_count=10`、`rejection_guard_count=12`、`reviewer_intake_lane_count=5` 的收件前預檢;request sent、recipient confirmed、redacted export received、accepted、quarantine、rejected、rendered diff、`nginx -t`、reload、route smoke、runtime gate、action buttons 仍全部為 `0`。這不是 request sent、live conf received / accepted、raw conf 收存、rendered diff ready、`nginx -t`、Nginx reload、DNS / TLS probe、certbot renew、host write、production write 或 runtime gate。 +75. 建立 Public Gateway rendered diff / nginx gate 草稿。✅ 本地完成;新增 `public-gateway-rendered-diff-gate-draft.py`、snapshot 與說明文件,將三份 redacted export intake candidate 轉成 `diff_gate_candidate_count=3`、`c0_diff_gate_candidate_count=2`、`preflight_stage_count=7`、`blocked_action_count=14` 的 rendered diff / nginx / route smoke 分階段 gate 草稿;redacted export accepted、rendered diff ready、`nginx -t` executed、reload、route smoke、DNS / TLS probe、certbot renew、runtime gate、action buttons 仍全部為 `0`。這不是 redacted export accepted、rendered diff ready、`nginx -t`、Nginx reload、DNS / TLS probe、certbot renew、host write、production write 或 runtime gate。 #### 3.2.1d 2026-06-11 Agent 互動、學習與成長證據面 @@ -4744,3 +4746,16 @@ Trigger commit `f5cd37b7` 與 deploy marker `0ba92357` 已把 governance UI 的 - P0 總帳、供應鏈進度、Public Gateway export request 文件與 MASTER artifact 表已同步 P0-16。 **裁決:** 這是 redacted export 收件前預檢,不是 request sent、recipient confirmed、redacted export received / accepted、raw live conf stored、rendered diff ready、`nginx -t`、Nginx reload、route smoke、DNS / TLS probe、certbot renew、host write、active scan、production write 或 runtime gate;IwoooS headline 仍維持 `64%`,active runtime gate 仍 `0`。 + +### 2026-06-14 20:05 (台北) — §3.2 / §5 — 新增 Public Gateway rendered diff / nginx gate 草稿 — 把收件候選轉成分階段 runtime 前置 gate + +**觸發**:P0-16 已建立 redacted export 收件預檢;即使未來收到並接受脫敏 ref,也不能直接進 `nginx -t`、reload 或 route smoke。需要先把 rendered diff、owner review、`nginx -t` approval package、reload、route smoke、DNS / TLS probe、certbot renew、maintenance window、rollback owner 與 post-check 拆成可驗證 gate 草稿。 + +**已推進:** +- 新增 `scripts/security/public-gateway-rendered-diff-gate-draft.py`,讀取 `docs/security/public-gateway-redacted-export-intake-preflight.snapshot.json`,產生三份 rendered diff gate 草稿。 +- 新增 `docs/security/public-gateway-rendered-diff-gate-draft.snapshot.json`,固定 `diff_gate_candidate_count=3`、`c0_diff_gate_candidate_count=2`、`c1_diff_gate_candidate_count=1`、`preflight_stage_count=7`、`blocked_action_count=14`。 +- 新增 `docs/security/PUBLIC-GATEWAY-RENDERED-DIFF-GATE-DRAFT.md`,說明 diff gate 欄位、preflight stages、blocked actions 與完成度。 +- `security-mirror-progress-guard.py` 已鎖住新 snapshot 的 schema、summary、execution false flags、diff gate ids、preflight stages、blocked actions 與每份候選 false flags。 +- P0 總帳、供應鏈進度、Public Gateway preflight / intake 文件與 MASTER artifact 表已同步 P0-17。 + +**裁決:** 這是 rendered diff / nginx / route smoke 分階段 gate 草稿,不是 redacted export accepted、rendered diff ready、`nginx -t`、Nginx reload、route smoke、DNS / TLS probe、certbot renew、host write、active scan、production write 或 runtime gate;IwoooS headline 仍維持 `64%`,active runtime gate 仍 `0`。 diff --git a/docs/workplans/2026-06-04-iwooos-security-governance-p0.md b/docs/workplans/2026-06-04-iwooos-security-governance-p0.md index 9184c1756..4fd3182ff 100644 --- a/docs/workplans/2026-06-04-iwooos-security-governance-p0.md +++ b/docs/workplans/2026-06-04-iwooos-security-governance-p0.md @@ -81,6 +81,7 @@ | P0-14 | 高價值配置 Owner Request 草稿包 / dispatch handoff | 100% | 已新增 `high-value-config-owner-request-draft.py`、snapshot 與文件,把三包預檢轉成 3 份 request draft、11 欄 handoff envelope、27 個 required owner fields、12 類 forbidden payload 與 16 類 blocked request;request sent、recipient confirmed、audit event、received、accepted、runtime gate 仍全部為 `0` | `HIGH_VALUE_CONFIG_OWNER_REQUEST_DRAFT_OK drafts=3 c0=2 fields=27 sent=0 runtime_gate=0`;progress guard 固定 snapshot schema、summary、false flags、request ids、handoff fields 與每份草稿 false flags;純 repo 內文件 / snapshot / guard,不需要 production browser smoke | | P0-15 | Public Gateway live conf 匯出請求包 | 100% | 已新增 `public-gateway-live-conf-export-request.py`、snapshot 與文件,把 3 份 public gateway source config 轉成 owner-provided live conf 脫敏匯出請求;redacted export received、raw live conf stored、rendered diff、`nginx -t`、reload、route smoke、runtime gate 仍全部為 `0` | `PUBLIC_GATEWAY_LIVE_CONF_EXPORT_REQUEST_OK requests=3 c0=2 redaction_rules=8 received=0 runtime_gate=0`;progress guard 固定 snapshot schema、summary、false flags、export request ids、redaction rules 與每份請求 false flags;純 repo 內文件 / snapshot / guard,不需要 production browser smoke | | P0-16 | Public Gateway redacted export 收件預檢 | 100% | 已新增 `public-gateway-redacted-export-intake-preflight.py`、snapshot 與文件,把 3 份 live conf export request 轉成 redacted export 收件候選;validation checks `10`、rejection guards `12`、reviewer lanes `5`;request sent、recipient confirmed、redacted export received、accepted、quarantine、rejected、rendered diff、`nginx -t`、reload、route smoke、runtime gate 仍全部為 `0` | `PUBLIC_GATEWAY_REDACTED_EXPORT_INTAKE_PREFLIGHT_OK candidates=3 c0=2 checks=10 rejected=0 runtime_gate=0`;progress guard 固定 snapshot schema、summary、false flags、intake ids、validation checks、rejection guards 與 reviewer lanes;純 repo 內文件 / snapshot / guard,不需要 production browser smoke | +| P0-17 | Public Gateway rendered diff / nginx gate 草稿 | 100% | 已新增 `public-gateway-rendered-diff-gate-draft.py`、snapshot 與文件,把 3 份 redacted export intake candidate 轉成 rendered diff、`nginx -t`、reload、route smoke、DNS / TLS、certbot、rollback / post-check 分階段 gate 草稿;redacted export accepted、rendered diff ready、`nginx -t` executed、reload、route smoke、runtime gate 仍全部為 `0` | `PUBLIC_GATEWAY_RENDERED_DIFF_GATE_DRAFT_OK candidates=3 c0=2 stages=7 blocked=14 runtime_gate=0`;progress guard 固定 snapshot schema、summary、false flags、diff gate ids、preflight stages、blocked actions 與每份草稿 false flags;純 repo 內文件 / snapshot / guard,不需要 production browser smoke | ## 3. S4.9 Owner Response Gate 規範 diff --git a/scripts/security/public-gateway-rendered-diff-gate-draft.py b/scripts/security/public-gateway-rendered-diff-gate-draft.py new file mode 100644 index 000000000..6049068cf --- /dev/null +++ b/scripts/security/public-gateway-rendered-diff-gate-draft.py @@ -0,0 +1,262 @@ +#!/usr/bin/env python3 +""" +IwoooS Public Gateway rendered diff gate 草稿產生器。 + +本工具讀取 redacted export intake preflight snapshot,產生未來 rendered +diff、nginx -t、reload 與 route smoke 的分階段 gate 草稿。它不讀 live +conf、不產生 diff、不執行 nginx -t、不 reload、不做 route smoke。 +""" + +from __future__ import annotations + +import argparse +import json +import subprocess +import sys +from datetime import datetime, timedelta, timezone +from pathlib import Path +from typing import Any + + +TAIPEI = timezone(timedelta(hours=8)) + +DIFF_GATE_FIELDS = [ + "diff_gate_id", + "intake_id", + "export_request_id", + "config_id", + "control_tier", + "source_config_ref", + "redacted_live_conf_ref", + "rendered_diff_ref", + "nginx_test_plan_ref", + "route_smoke_plan_ref", + "rollback_owner", + "not_approval", +] + +PREFLIGHT_STAGES = [ + ( + "redacted_export_acceptance_required", + "必須先有合格 redacted export accepted metadata,否則不得產生 rendered diff。", + ), + ( + "normalize_without_raw_conf_storage", + "只可在隔離工作區以脫敏 ref 產生 normalized diff,不得把 raw live conf 寫入 repo。", + ), + ( + "rendered_diff_owner_review_required", + "rendered diff 只可成為 owner review candidate,不自動批准。", + ), + ( + "nginx_test_approval_package_required", + "`nginx -t` 必須另有人工批准包、rollback owner 與維護窗口。", + ), + ( + "reload_approval_separate", + "reload 與 public route change 必須獨立於 rendered diff 與 nginx -t。", + ), + ( + "route_smoke_matrix_required", + "route smoke 需列出 affected routes、預期 status、TLS / WebSocket / ACME checks。", + ), + ( + "postcheck_and_rollback_required", + "任何未來執行前都需 rollback owner、post-check 與失敗撤回條件。", + ), +] + +BLOCKED_ACTIONS = [ + "read_live_conf_over_ssh", + "store_raw_live_conf", + "render_diff_from_unredacted_payload", + "nginx_test_without_approval", + "nginx_reload_without_approval", + "route_smoke_without_plan", + "dns_probe_without_approval", + "tls_probe_without_approval", + "certbot_renew_without_approval", + "modify_nginx_conf", + "modify_dns_tls_config", + "change_public_route", + "write_production_host", + "open_runtime_gate", +] + + +def git_short_sha(root: Path) -> str: + try: + result = subprocess.run( + ["git", "rev-parse", "--short", "HEAD"], + cwd=root, + check=True, + capture_output=True, + text=True, + ) + return result.stdout.strip() + except Exception: + return "unknown" + + +def load_json(path: Path) -> dict[str, Any]: + return json.loads(path.read_text(encoding="utf-8")) + + +def preflight_stages() -> list[dict[str, Any]]: + return [ + { + "stage_id": stage_id, + "status": "required_before_runtime_action", + "instruction": instruction, + "gate_effect": "不增加 rendered_diff / nginx_test / reload / route_smoke / runtime gate。", + } + for stage_id, instruction in PREFLIGHT_STAGES + ] + + +def diff_gate_candidate(intake: dict[str, Any]) -> dict[str, Any]: + config_id = intake["config_id"] + return { + "diff_gate_id": f"public_gateway_rendered_diff_gate:{config_id}", + "status": "draft_waiting_redacted_export_acceptance", + "intake_id": intake["intake_id"], + "export_request_id": intake["export_request_id"], + "config_id": config_id, + "host": intake["host"], + "live_path": intake["live_path"], + "control_tier": intake["control_tier"], + "owner_gate": intake["owner_gate"], + "source_config_ref": "docs/security/public-gateway-preflight-inventory.snapshot.json", + "redacted_live_conf_ref": None, + "rendered_diff_ref": None, + "nginx_test_plan_ref": None, + "route_smoke_plan_ref": None, + "rollback_owner": "pending_rollback_owner", + "diff_gate_fields": DIFF_GATE_FIELDS, + "preflight_stages": [stage_id for stage_id, _instruction in PREFLIGHT_STAGES], + "blocked_actions": BLOCKED_ACTIONS, + "not_approval": True, + "redacted_export_accepted": False, + "rendered_diff_candidate": False, + "rendered_diff_ready": False, + "nginx_test_authorized": False, + "nginx_test_executed": False, + "nginx_reload_authorized": False, + "nginx_reload_executed": False, + "route_smoke_authorized": False, + "route_smoke_executed": False, + "dns_tls_probe_authorized": False, + "certbot_renew_authorized": False, + "maintenance_window_accepted": False, + "rollback_owner_accepted": False, + "runtime_gate": False, + "action_buttons_allowed": False, + "production_write_authorized": False, + } + + +def build_report(root: Path, intake_report: dict[str, Any], generated_at: str | None) -> dict[str, Any]: + report_time = generated_at or datetime.now(TAIPEI).isoformat(timespec="seconds") + intake_candidates = intake_report.get("intake_candidates", []) + diff_gate_candidates = [diff_gate_candidate(item) for item in intake_candidates] + c0_candidates = [item for item in diff_gate_candidates if item.get("control_tier") == "C0"] + c1_candidates = [item for item in diff_gate_candidates if item.get("control_tier") == "C1"] + + return { + "schema_version": "public_gateway_rendered_diff_gate_draft_v1", + "generated_at": report_time, + "git_commit": git_short_sha(root), + "source_intake_preflight_schema_version": intake_report.get("schema_version"), + "source_intake_preflight_status": intake_report.get("status"), + "status": "rendered_diff_gate_draft_ready_no_runtime_action", + "summary": { + "diff_gate_candidate_count": len(diff_gate_candidates), + "c0_diff_gate_candidate_count": len(c0_candidates), + "c1_diff_gate_candidate_count": len(c1_candidates), + "diff_gate_field_count": len(DIFF_GATE_FIELDS), + "preflight_stage_count": len(PREFLIGHT_STAGES), + "blocked_action_count": len(BLOCKED_ACTIONS), + "redacted_export_accepted_count": 0, + "rendered_diff_candidate_count": 0, + "rendered_diff_ready_count": 0, + "nginx_test_authorized_count": 0, + "nginx_test_executed_count": 0, + "nginx_reload_authorized_count": 0, + "nginx_reload_executed_count": 0, + "route_smoke_authorized_count": 0, + "route_smoke_executed_count": 0, + "dns_tls_probe_authorized_count": 0, + "certbot_renew_authorized_count": 0, + "maintenance_window_accepted_count": 0, + "rollback_owner_accepted_count": 0, + "runtime_gate_count": 0, + "action_button_count": 0, + }, + "execution_boundaries": { + "read_live_conf_over_ssh": False, + "store_raw_live_conf": False, + "rendered_diff_authorized": False, + "nginx_test_authorized": False, + "nginx_test_executed": False, + "nginx_reload_authorized": False, + "nginx_reload_executed": False, + "route_smoke_authorized": False, + "route_smoke_executed": False, + "dns_tls_probe_authorized": False, + "certbot_renew_authorized": False, + "production_write_authorized": False, + "runtime_execution_authorized": False, + "action_buttons_allowed": False, + "not_authorization": True, + }, + "diff_gate_fields": DIFF_GATE_FIELDS, + "preflight_stages": preflight_stages(), + "blocked_actions": BLOCKED_ACTIONS, + "diff_gate_candidates": diff_gate_candidates, + "next_steps": [ + "等待 redacted export accepted metadata;沒有 accepted metadata 前不得產生 rendered diff。", + "rendered diff candidate 必須另走 reviewer / owner review,不得自動進 nginx -t。", + "`nginx -t`、reload、route smoke、DNS / TLS probe、certbot renew 與 production write 都必須另行人工批准。", + ], + } + + +def main() -> int: + parser = argparse.ArgumentParser(description="IwoooS Public Gateway rendered diff gate 草稿產生器") + parser.add_argument("--root", default=".", help="repo root") + parser.add_argument( + "--intake-preflight-report", + default="docs/security/public-gateway-redacted-export-intake-preflight.snapshot.json", + help="public-gateway-redacted-export-intake-preflight.py 輸出的 JSON", + ) + parser.add_argument("--output", help="寫出 JSON 報告") + parser.add_argument("--generated-at", help="固定報告時間,供 committed snapshot 使用") + args = parser.parse_args() + + root = Path(args.root).resolve() + intake_report = load_json(root / args.intake_preflight_report) + report = build_report(root, intake_report, args.generated_at) + payload = json.dumps(report, ensure_ascii=False, indent=2, sort_keys=True) + + if args.output: + output = Path(args.output) + output.parent.mkdir(parents=True, exist_ok=True) + output.write_text(payload + "\n", encoding="utf-8") + else: + print(payload) + + summary = report["summary"] + print( + "PUBLIC_GATEWAY_RENDERED_DIFF_GATE_DRAFT_OK " + f"candidates={summary['diff_gate_candidate_count']} " + f"c0={summary['c0_diff_gate_candidate_count']} " + f"stages={summary['preflight_stage_count']} " + f"blocked={summary['blocked_action_count']} " + f"runtime_gate={summary['runtime_gate_count']}", + file=sys.stderr, + ) + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/scripts/security/security-mirror-progress-guard.py b/scripts/security/security-mirror-progress-guard.py index 1cf0ec708..c1df12bc1 100755 --- a/scripts/security/security-mirror-progress-guard.py +++ b/scripts/security/security-mirror-progress-guard.py @@ -110,6 +110,9 @@ def validate(root: Path) -> None: public_gateway_redacted_export_intake_preflight = load_json( security_dir / "public-gateway-redacted-export-intake-preflight.snapshot.json" ) + public_gateway_rendered_diff_gate_draft = load_json( + security_dir / "public-gateway-rendered-diff-gate-draft.snapshot.json" + ) s49_request_draft = load_json(security_dir / "gitea-inventory-owner-attestation-request-draft.snapshot.json") kali_status = load_json(security_dir / "kali-integration-status.snapshot.json") iwooos_projection_page = ( @@ -3459,6 +3462,151 @@ def validate(root: Path) -> None: f"public_gateway_redacted_export_intake_preflight.{item['intake_id']}.{false_key}", item[false_key], ) + assert_equal( + "public_gateway_rendered_diff_gate_draft.schema", + public_gateway_rendered_diff_gate_draft["schema_version"], + "public_gateway_rendered_diff_gate_draft_v1", + ) + assert_equal( + "public_gateway_rendered_diff_gate_draft.source_intake_preflight_schema_version", + public_gateway_rendered_diff_gate_draft["source_intake_preflight_schema_version"], + "public_gateway_redacted_export_intake_preflight_v1", + ) + assert_equal( + "public_gateway_rendered_diff_gate_draft.status", + public_gateway_rendered_diff_gate_draft["status"], + "rendered_diff_gate_draft_ready_no_runtime_action", + ) + expected_public_gateway_rendered_diff_summary = { + "diff_gate_candidate_count": 3, + "c0_diff_gate_candidate_count": 2, + "c1_diff_gate_candidate_count": 1, + "diff_gate_field_count": 12, + "preflight_stage_count": 7, + "blocked_action_count": 14, + "redacted_export_accepted_count": 0, + "rendered_diff_candidate_count": 0, + "rendered_diff_ready_count": 0, + "nginx_test_authorized_count": 0, + "nginx_test_executed_count": 0, + "nginx_reload_authorized_count": 0, + "nginx_reload_executed_count": 0, + "route_smoke_authorized_count": 0, + "route_smoke_executed_count": 0, + "dns_tls_probe_authorized_count": 0, + "certbot_renew_authorized_count": 0, + "maintenance_window_accepted_count": 0, + "rollback_owner_accepted_count": 0, + "runtime_gate_count": 0, + "action_button_count": 0, + } + for key, expected in expected_public_gateway_rendered_diff_summary.items(): + assert_equal( + f"public_gateway_rendered_diff_gate_draft.summary.{key}", + public_gateway_rendered_diff_gate_draft["summary"][key], + expected, + ) + for false_key in [ + "read_live_conf_over_ssh", + "store_raw_live_conf", + "rendered_diff_authorized", + "nginx_test_authorized", + "nginx_test_executed", + "nginx_reload_authorized", + "nginx_reload_executed", + "route_smoke_authorized", + "route_smoke_executed", + "dns_tls_probe_authorized", + "certbot_renew_authorized", + "production_write_authorized", + "runtime_execution_authorized", + "action_buttons_allowed", + ]: + assert_false( + f"public_gateway_rendered_diff_gate_draft.execution_boundaries.{false_key}", + public_gateway_rendered_diff_gate_draft["execution_boundaries"][false_key], + ) + assert_true( + "public_gateway_rendered_diff_gate_draft.execution_boundaries.not_authorization", + public_gateway_rendered_diff_gate_draft["execution_boundaries"]["not_authorization"], + ) + expected_public_gateway_rendered_diff_ids = [ + "public_gateway_rendered_diff_gate:host188_all_sites", + "public_gateway_rendered_diff_gate:host188_internal_tools_https", + "public_gateway_rendered_diff_gate:host110_ollama_proxy", + ] + assert_equal( + "public_gateway_rendered_diff_gate_draft.diff_gate_ids", + [item["diff_gate_id"] for item in public_gateway_rendered_diff_gate_draft["diff_gate_candidates"]], + expected_public_gateway_rendered_diff_ids, + ) + expected_public_gateway_rendered_diff_stage_ids = [ + "redacted_export_acceptance_required", + "normalize_without_raw_conf_storage", + "rendered_diff_owner_review_required", + "nginx_test_approval_package_required", + "reload_approval_separate", + "route_smoke_matrix_required", + "postcheck_and_rollback_required", + ] + assert_equal( + "public_gateway_rendered_diff_gate_draft.preflight_stage_ids", + [item["stage_id"] for item in public_gateway_rendered_diff_gate_draft["preflight_stages"]], + expected_public_gateway_rendered_diff_stage_ids, + ) + expected_public_gateway_rendered_diff_blocked_actions = [ + "read_live_conf_over_ssh", + "store_raw_live_conf", + "render_diff_from_unredacted_payload", + "nginx_test_without_approval", + "nginx_reload_without_approval", + "route_smoke_without_plan", + "dns_probe_without_approval", + "tls_probe_without_approval", + "certbot_renew_without_approval", + "modify_nginx_conf", + "modify_dns_tls_config", + "change_public_route", + "write_production_host", + "open_runtime_gate", + ] + assert_equal( + "public_gateway_rendered_diff_gate_draft.blocked_actions", + public_gateway_rendered_diff_gate_draft["blocked_actions"], + expected_public_gateway_rendered_diff_blocked_actions, + ) + for item in public_gateway_rendered_diff_gate_draft["diff_gate_candidates"]: + assert_equal( + f"public_gateway_rendered_diff_gate_draft.{item['diff_gate_id']}.field_count", + len(item["diff_gate_fields"]), + 12, + ) + assert_true( + f"public_gateway_rendered_diff_gate_draft.{item['diff_gate_id']}.not_approval", + item["not_approval"], + ) + for false_key in [ + "redacted_export_accepted", + "rendered_diff_candidate", + "rendered_diff_ready", + "nginx_test_authorized", + "nginx_test_executed", + "nginx_reload_authorized", + "nginx_reload_executed", + "route_smoke_authorized", + "route_smoke_executed", + "dns_tls_probe_authorized", + "certbot_renew_authorized", + "maintenance_window_accepted", + "rollback_owner_accepted", + "runtime_gate", + "action_buttons_allowed", + "production_write_authorized", + ]: + assert_false( + f"public_gateway_rendered_diff_gate_draft.{item['diff_gate_id']}.{false_key}", + item[false_key], + ) for source_path in [ "docs/security/public-gateway-preflight-inventory.snapshot.json", "docs/security/PUBLIC-GATEWAY-PREFLIGHT-INVENTORY.md",