From 76e5bbcf47e56e543e16b881bb8ebdc6ca8ae3b3 Mon Sep 17 00:00:00 2001 From: Your Name Date: Mon, 29 Jun 2026 10:48:28 +0800 Subject: [PATCH] feat(delivery): expose credential escrow scorecard --- .../services/delivery_closure_workbench.py | 104 ++++++++++++++++++ .../test_delivery_closure_workbench_api.py | 65 +++++++++++ apps/web/src/lib/api-client.ts | 44 +++++++- docs/LOGBOOK.md | 15 +++ ...backup_dr_readiness_matrix_2026-06-04.json | 24 +++- 5 files changed, 246 insertions(+), 6 deletions(-) diff --git a/apps/api/src/services/delivery_closure_workbench.py b/apps/api/src/services/delivery_closure_workbench.py index 9e52ea43..a9e6c073 100644 --- a/apps/api/src/services/delivery_closure_workbench.py +++ b/apps/api/src/services/delivery_closure_workbench.py @@ -411,6 +411,59 @@ def build_delivery_closure_workbench( "metric": { "kind": "readiness_row_count", "rows": _int(backup_rollups.get("total_rows")), + "credential_escrow_intake_scorecard_schema_version": str( + backup_rollups.get( + "credential_escrow_intake_scorecard_schema_version" + ) + or "" + ), + "credential_escrow_intake_scorecard_verifier": str( + backup_rollups.get("credential_escrow_intake_scorecard_verifier") + or "" + ), + "credential_escrow_intake_status": str( + backup_rollups.get("credential_escrow_intake_status") or "" + ), + "credential_escrow_active_gate_present": backup_rollups.get( + "credential_escrow_active_gate_present" + ) + is True, + "credential_escrow_preflight_status": str( + backup_rollups.get("credential_escrow_preflight_status") or "" + ), + "credential_escrow_required_item_count": _int( + backup_rollups.get("credential_escrow_required_item_count") + ), + "credential_escrow_effective_missing_count": _int( + backup_rollups.get("credential_escrow_effective_missing_count") + ), + "credential_escrow_owner_response_received_count": _int( + backup_rollups.get( + "credential_escrow_owner_response_received_count" + ) + ), + "credential_escrow_owner_response_accepted_count": _int( + backup_rollups.get( + "credential_escrow_owner_response_accepted_count" + ) + ), + "credential_escrow_runtime_gate_count": _int( + backup_rollups.get("credential_escrow_runtime_gate_count") + ), + "credential_escrow_secret_value_collection_allowed": ( + backup_rollups.get( + "credential_escrow_secret_value_collection_allowed" + ) + is True + ), + "credential_marker_write_authorized_count": _int( + backup_rollups.get("credential_marker_write_authorized_count") + ), + "credential_escrow_forbidden_true_field_count": _int( + backup_rollups.get( + "credential_escrow_forbidden_true_field_count" + ) + ), }, "href": "/operations", "next_action": _first_backup_action(backup.get("readiness_rows")), @@ -646,6 +699,57 @@ def build_delivery_closure_workbench( ) ) ), + "backup_credential_escrow_intake_scorecard_schema_version": str( + backup_rollups.get( + "credential_escrow_intake_scorecard_schema_version" + ) + or "" + ), + "backup_credential_escrow_intake_scorecard_verifier": str( + backup_rollups.get("credential_escrow_intake_scorecard_verifier") + or "" + ), + "backup_credential_escrow_intake_status": str( + backup_rollups.get("credential_escrow_intake_status") or "" + ), + "backup_credential_escrow_active_gate_present": backup_rollups.get( + "credential_escrow_active_gate_present" + ) + is True, + "backup_credential_escrow_preflight_status": str( + backup_rollups.get("credential_escrow_preflight_status") or "" + ), + "backup_credential_escrow_required_item_count": _int( + backup_rollups.get("credential_escrow_required_item_count") + ), + "backup_credential_escrow_effective_missing_count": _int( + backup_rollups.get("credential_escrow_effective_missing_count") + ), + "backup_credential_escrow_owner_response_received_count": _int( + backup_rollups.get( + "credential_escrow_owner_response_received_count" + ) + ), + "backup_credential_escrow_owner_response_accepted_count": _int( + backup_rollups.get( + "credential_escrow_owner_response_accepted_count" + ) + ), + "backup_credential_escrow_runtime_gate_count": _int( + backup_rollups.get("credential_escrow_runtime_gate_count") + ), + "backup_credential_escrow_secret_value_collection_allowed": ( + backup_rollups.get( + "credential_escrow_secret_value_collection_allowed" + ) + is True + ), + "backup_credential_marker_write_authorized_count": _int( + backup_rollups.get("credential_marker_write_authorized_count") + ), + "backup_credential_escrow_forbidden_true_field_count": _int( + backup_rollups.get("credential_escrow_forbidden_true_field_count") + ), "github_write_channel_ready": github_preflight.get( "github_write_channel_ready" ) diff --git a/apps/api/tests/test_delivery_closure_workbench_api.py b/apps/api/tests/test_delivery_closure_workbench_api.py index 29be7ecb..b601c9ef 100644 --- a/apps/api/tests/test_delivery_closure_workbench_api.py +++ b/apps/api/tests/test_delivery_closure_workbench_api.py @@ -169,6 +169,40 @@ def test_delivery_closure_workbench_endpoint_returns_product_summary(): data["summary"]["production_deploy_non110_runner_remaining_blocker_count"] == 3 ) + assert ( + data["summary"][ + "backup_credential_escrow_intake_scorecard_schema_version" + ] + == "awoooi_post_reboot_credential_escrow_intake_scorecard_v1" + ) + assert ( + "scripts/reboot-recovery/post-reboot-credential-escrow-intake-scorecard.py" + in data["summary"]["backup_credential_escrow_intake_scorecard_verifier"] + ) + assert data["summary"]["backup_credential_escrow_intake_status"] == ( + "blocked_waiting_non_secret_credential_escrow_evidence" + ) + assert data["summary"]["backup_credential_escrow_active_gate_present"] is True + assert data["summary"]["backup_credential_escrow_preflight_status"] == ( + "blocked_waiting_owner_response_content" + ) + assert data["summary"]["backup_credential_escrow_required_item_count"] == 5 + assert data["summary"]["backup_credential_escrow_effective_missing_count"] == 5 + assert ( + data["summary"]["backup_credential_escrow_owner_response_received_count"] + == 0 + ) + assert ( + data["summary"]["backup_credential_escrow_owner_response_accepted_count"] + == 0 + ) + assert data["summary"]["backup_credential_escrow_runtime_gate_count"] == 0 + assert ( + data["summary"]["backup_credential_escrow_secret_value_collection_allowed"] + is False + ) + assert data["summary"]["backup_credential_marker_write_authorized_count"] == 0 + assert data["summary"]["backup_credential_escrow_forbidden_true_field_count"] == 0 assert data["summary"]["github_write_channel_ready"] is False assert data["summary"]["github_account_status"] == "suspended" assert data["summary"]["github_account_suspended"] is True @@ -380,6 +414,37 @@ def test_delivery_closure_workbench_endpoint_returns_product_summary(): assert lanes["gitea"]["metric"]["kind"] == "workflow_count" assert lanes["runtime"]["metric"]["kind"] == "surface_count" assert lanes["backup"]["metric"]["kind"] == "readiness_row_count" + assert ( + lanes["backup"]["metric"][ + "credential_escrow_intake_scorecard_schema_version" + ] + == "awoooi_post_reboot_credential_escrow_intake_scorecard_v1" + ) + assert lanes["backup"]["metric"]["credential_escrow_intake_status"] == ( + "blocked_waiting_non_secret_credential_escrow_evidence" + ) + assert lanes["backup"]["metric"]["credential_escrow_active_gate_present"] is True + assert ( + lanes["backup"]["metric"]["credential_escrow_effective_missing_count"] + == 5 + ) + assert ( + lanes["backup"]["metric"]["credential_escrow_owner_response_received_count"] + == 0 + ) + assert ( + lanes["backup"]["metric"]["credential_escrow_owner_response_accepted_count"] + == 0 + ) + assert lanes["backup"]["metric"]["credential_escrow_runtime_gate_count"] == 0 + assert ( + lanes["backup"]["metric"][ + "credential_escrow_secret_value_collection_allowed" + ] + is False + ) + assert lanes["backup"]["metric"]["credential_marker_write_authorized_count"] == 0 + assert lanes["backup"]["metric"]["credential_escrow_forbidden_true_field_count"] == 0 assert sources["github_private_backup"]["loaded"] is True assert sources["production_deploy_readback"]["loaded"] is True assert ( diff --git a/apps/web/src/lib/api-client.ts b/apps/web/src/lib/api-client.ts index 6fec69d0..1e4a6891 100644 --- a/apps/web/src/lib/api-client.ts +++ b/apps/web/src/lib/api-client.ts @@ -2584,6 +2584,19 @@ export interface DeliveryClosureWorkbenchSnapshot { production_deploy_non110_runner_ready_registration_count: number production_deploy_non110_runner_safe_next_step: string production_deploy_non110_runner_remaining_blocker_count: number + backup_credential_escrow_intake_scorecard_schema_version: string + backup_credential_escrow_intake_scorecard_verifier: string + backup_credential_escrow_intake_status: string + backup_credential_escrow_active_gate_present: boolean + backup_credential_escrow_preflight_status: string + backup_credential_escrow_required_item_count: number + backup_credential_escrow_effective_missing_count: number + backup_credential_escrow_owner_response_received_count: number + backup_credential_escrow_owner_response_accepted_count: number + backup_credential_escrow_runtime_gate_count: number + backup_credential_escrow_secret_value_collection_allowed: boolean + backup_credential_marker_write_authorized_count: number + backup_credential_escrow_forbidden_true_field_count: number github_write_channel_ready: boolean github_account_status: string github_account_suspended: boolean @@ -2664,7 +2677,23 @@ export interface DeliveryClosureWorkbenchSnapshot { } | { kind: 'workflow_count'; count: number } | { kind: 'surface_count'; total: number } - | { kind: 'readiness_row_count'; rows: number } + | { + kind: 'readiness_row_count' + rows: number + credential_escrow_intake_scorecard_schema_version: string + credential_escrow_intake_scorecard_verifier: string + credential_escrow_intake_status: string + credential_escrow_active_gate_present: boolean + credential_escrow_preflight_status: string + credential_escrow_required_item_count: number + credential_escrow_effective_missing_count: number + credential_escrow_owner_response_received_count: number + credential_escrow_owner_response_accepted_count: number + credential_escrow_runtime_gate_count: number + credential_escrow_secret_value_collection_allowed: boolean + credential_marker_write_authorized_count: number + credential_escrow_forbidden_true_field_count: number + } href: string operator_unblock?: DeliveryOperatorUnblock next_action: string @@ -14655,6 +14684,19 @@ export interface BackupDrReadinessMatrixSnapshot { by_offsite_status: Record blocked_row_ids: string[] action_required_row_ids: string[] + credential_escrow_intake_scorecard_schema_version: string + credential_escrow_intake_scorecard_verifier: string + credential_escrow_intake_status: string + credential_escrow_active_gate_present: boolean + credential_escrow_preflight_status: string + credential_escrow_required_item_count: number + credential_escrow_effective_missing_count: number + credential_escrow_owner_response_received_count: number + credential_escrow_owner_response_accepted_count: number + credential_escrow_runtime_gate_count: number + credential_escrow_secret_value_collection_allowed: boolean + credential_marker_write_authorized_count: number + credential_escrow_forbidden_true_field_count: number } readiness_rows: Array<{ target_id: string diff --git a/docs/LOGBOOK.md b/docs/LOGBOOK.md index 76c6c198..14013bb6 100644 --- a/docs/LOGBOOK.md +++ b/docs/LOGBOOK.md @@ -7,6 +7,21 @@ **邊界**:只改 committed source / snapshot / API type / tests / docs;未使用 GitHub;未讀 token / `.runner` 內容 / cookie / session / secret;未操作 host / Docker / K8s / runner service;未 workflow_dispatch。 +## 2026-06-29 — 10:42 Delivery Workbench credential escrow scorecard 投影 + +**完成內容**: +- 將 `post-reboot-credential-escrow-intake-scorecard.py` 的 no-secret scorecard contract 投影到 Backup / DR readiness rollups、Delivery Workbench summary 與 backup lane metric。 +- Workbench 現在可讀回 `blocked_waiting_non_secret_credential_escrow_evidence`、active gate present、5 個 escrow item missing、owner response / runtime gate / marker write / forbidden true fields 皆為 `0`。 +- 同步 web API type,避免前端將 credential escrow gate 退回只靠文件或人工語意。 + +**驗證結果**: +- `jq empty docs/evaluations/backup_dr_readiness_matrix_2026-06-04.json`:通過。 +- `PYTHONPATH=apps/api python3.11 -m pytest apps/api/tests/test_delivery_closure_workbench_api.py apps/api/tests/test_backup_dr_readiness_matrix.py apps/api/tests/test_backup_dr_readiness_matrix_api.py -q`:`8 passed`。 +- `pnpm --dir apps/web typecheck`:通過。 +- `git diff --check`:通過。 + +**邊界**:只改 committed source / snapshot / API type / tests;未讀 password / token / `.runner` / raw session / SQLite / auth / `.env`,未寫 credential marker,未操作 host / Docker / K8s / runner service,未使用 GitHub。 + ## 2026-06-29 — 09:36 credential escrow intake scorecard no-secret readback **完成內容**: diff --git a/docs/evaluations/backup_dr_readiness_matrix_2026-06-04.json b/docs/evaluations/backup_dr_readiness_matrix_2026-06-04.json index dd5bd2d3..45d156c6 100644 --- a/docs/evaluations/backup_dr_readiness_matrix_2026-06-04.json +++ b/docs/evaluations/backup_dr_readiness_matrix_2026-06-04.json @@ -6,7 +6,8 @@ "docs/runbooks/BACKUP-STATUS.md", "docs/evaluations/backup_dr_target_inventory_2026-06-04.json", "scripts/backup/backup-status.sh", - "scripts/backup/verify-offsite-full-sync.sh" + "scripts/backup/verify-offsite-full-sync.sh", + "scripts/reboot-recovery/post-reboot-credential-escrow-intake-scorecard.py" ], "program_status": { "overall_completion_percent": 91, @@ -43,7 +44,20 @@ "action_required_row_ids": [ "signoz", "velero_k8s_resources" - ] + ], + "credential_escrow_intake_scorecard_schema_version": "awoooi_post_reboot_credential_escrow_intake_scorecard_v1", + "credential_escrow_intake_scorecard_verifier": "scripts/reboot-recovery/post-reboot-credential-escrow-intake-scorecard.py --summary-file --owner-packet-file --response-file --offsite-report-file --escrow-status-file --json", + "credential_escrow_intake_status": "blocked_waiting_non_secret_credential_escrow_evidence", + "credential_escrow_active_gate_present": true, + "credential_escrow_preflight_status": "blocked_waiting_owner_response_content", + "credential_escrow_required_item_count": 5, + "credential_escrow_effective_missing_count": 5, + "credential_escrow_owner_response_received_count": 0, + "credential_escrow_owner_response_accepted_count": 0, + "credential_escrow_runtime_gate_count": 0, + "credential_escrow_secret_value_collection_allowed": false, + "credential_marker_write_authorized_count": 0, + "credential_escrow_forbidden_true_field_count": 0 }, "readiness_rows": [ { @@ -282,9 +296,9 @@ "notification_policy": "missing markers must stay action-required;不得成功洗版。", "gate_status": "credential_approval_required", "evidence_level": "blocked_live_evidence", - "evidence_refs": ["docs/runbooks/BACKUP-STATUS.md", "scripts/backup/mark-credential-escrow-verified.sh", "scripts/backup/offsite-escrow-evidence-report.sh"], - "blocker_summary": "Five evidence markers missing;不得自動寫 marker 或暴露 credential。", - "next_action": "P1-105 起草人工 escrow review 批准包。" + "evidence_refs": ["docs/runbooks/BACKUP-STATUS.md", "scripts/backup/mark-credential-escrow-verified.sh", "scripts/backup/offsite-escrow-evidence-report.sh", "scripts/reboot-recovery/post-reboot-credential-escrow-intake-scorecard.py"], + "blocker_summary": "Five evidence markers missing;不得自動寫 marker、不得讀或暴露 credential value;可收斂 redacted non-secret evidence refs。", + "next_action": "用 credential escrow intake scorecard 收斂 no-secret evidence refs;preflight 通過前維持 marker write/runtime gate 為 0。" }, { "target_id": "velero_k8s_resources",