Files
awoooi/scripts/security/backup-restore-escrow-inventory.py
Your Name 93a1993d11
Some checks failed
CD Pipeline / tests (push) Successful in 1m30s
Code Review / ai-code-review (push) Successful in 16s
CD Pipeline / build-and-deploy (push) Successful in 4m30s
CD Pipeline / post-deploy-checks (push) Has been cancelled
feat(security): 新增 backup restore escrow 只讀清冊
2026-06-11 22:51:31 +08:00

774 lines
36 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
"""
IwoooS backup / restore / escrow / retention repo-only 清冊。
本工具只讀取已提交的 repo 檔案,整理 backup orchestration、service
backup scripts、restic retention、offsite sync、credential escrow、Velero
restore drill、alert rules 與 DR runbook evidence。它不執行 backup、不
restore、不呼叫 rclone、不寫 escrow marker、不跑 restic prune、不連
K8s、不 SSH、不讀 secret value。
"""
from __future__ import annotations
import argparse
import hashlib
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))
SURFACES: list[dict[str, Any]] = [
{
"surface_id": "backup_all_orchestrator",
"label": "全服務備份總控",
"source_path": "scripts/backup/backup-all.sh",
"expected_scope": "110_backup_host_all_services",
"config_kind": "backup_orchestrator",
"control_tier": "C0",
"current_state": "write_capable_orchestrator_visible_not_executed",
"backup_scope": ["gitea", "momo", "harbor", "awoooi", "langfuse", "monitoring", "signoz", "open-webui", "clawbot"],
"requires_live_evidence": True,
"requires_owner_response": True,
"next_owner_action": "補 backup owner、cron owner、失敗通知 owner、restore drill owner、rollback owner 與 post-check 指標。",
},
{
"surface_id": "backup_common_restic_retention",
"label": "Restic 共用設定與 GFS retention",
"source_path": "scripts/backup/common.sh",
"expected_scope": "restic_password_b2_retention_common",
"config_kind": "backup_common_policy",
"control_tier": "C0",
"current_state": "retention_and_credential_metadata_visible_secret_values_absent",
"backup_scope": ["RESTIC_PASSWORD_FILE", "B2 metadata", "KEEP_DAILY=30", "KEEP_WEEKLY=12", "KEEP_MONTHLY=24"],
"requires_live_evidence": True,
"requires_owner_response": True,
"next_owner_action": "補 restic password owner、B2 / rclone owner、retention owner、prune window 與 no-secret-value evidence。",
},
{
"surface_id": "backup_gitea_service_script",
"label": "Gitea 備份腳本",
"source_path": "scripts/backup/backup-gitea.sh",
"expected_scope": "gitea_database_and_repositories",
"config_kind": "service_backup_script",
"control_tier": "C0",
"current_state": "service_backup_script_visible_gate_closed",
"backup_scope": ["Gitea DB", "repositories", "app.ini redaction boundary"],
"requires_live_evidence": True,
"requires_owner_response": True,
"next_owner_action": "補 Gitea backup owner、freshness evidence、restore target isolation 與 secret redaction proof。",
},
{
"surface_id": "backup_momo_service_script",
"label": "MOMO PostgreSQL 備份腳本",
"source_path": "scripts/backup/backup-momo.sh",
"expected_scope": "momo_postgresql",
"config_kind": "service_backup_script",
"control_tier": "C0",
"current_state": "service_backup_script_visible_gate_closed",
"backup_scope": ["MOMO PostgreSQL", "188 database path"],
"requires_live_evidence": True,
"requires_owner_response": True,
"next_owner_action": "補 MOMO backup owner、188 DB access boundary、restore drill target 與 rollback owner。",
},
{
"surface_id": "backup_harbor_service_script",
"label": "Harbor 備份腳本",
"source_path": "scripts/backup/backup-harbor.sh",
"expected_scope": "harbor_registry_and_database",
"config_kind": "service_backup_script",
"control_tier": "C0",
"current_state": "service_backup_script_visible_gate_closed",
"backup_scope": ["Harbor registry", "Harbor DB", "image registry recovery"],
"requires_live_evidence": True,
"requires_owner_response": True,
"next_owner_action": "補 Harbor backup owner、registry restore smoke、robot account secret boundary 與 image rollback owner。",
},
{
"surface_id": "backup_awoooi_service_script",
"label": "AWOOOI PostgreSQL 完整備份腳本",
"source_path": "scripts/backup/backup-awoooi.sh",
"expected_scope": "awoooi_postgresql_and_k3s_datastore",
"config_kind": "service_backup_script",
"control_tier": "C0",
"current_state": "service_backup_script_visible_gate_closed",
"backup_scope": ["awoooi_prod", "awoooi_dev", "k3s datastore"],
"requires_live_evidence": True,
"requires_owner_response": True,
"next_owner_action": "補 AWOOOI DB backup owner、RPO owner、restore drill isolation 與 data masking policy。",
},
{
"surface_id": "backup_awoooi_frequent_script",
"label": "AWOOOI PostgreSQL 高頻備份腳本",
"source_path": "scripts/backup/backup-awoooi-frequent.sh",
"expected_scope": "awoooi_postgresql_high_frequency",
"config_kind": "service_backup_script",
"control_tier": "C0",
"current_state": "high_frequency_backup_script_visible_gate_closed",
"backup_scope": ["awoooi_prod", "6h RPO", "latest-only interaction"],
"requires_live_evidence": True,
"requires_owner_response": True,
"next_owner_action": "補高頻備份 owner、cron owner、latest-only retention owner 與 freshness evidence。",
},
{
"surface_id": "backup_langfuse_service_script",
"label": "Langfuse 備份腳本",
"source_path": "scripts/backup/backup-langfuse.sh",
"expected_scope": "langfuse_ai_trace_database",
"config_kind": "service_backup_script",
"control_tier": "C0",
"current_state": "service_backup_script_visible_gate_closed",
"backup_scope": ["Langfuse DB", "AI trace evidence"],
"requires_live_evidence": True,
"requires_owner_response": True,
"next_owner_action": "補 Langfuse backup owner、trace privacy boundary、restore smoke 與 secret redaction proof。",
},
{
"surface_id": "backup_monitoring_service_script",
"label": "Monitoring 備份腳本",
"source_path": "scripts/backup/backup-monitoring.sh",
"expected_scope": "prometheus_grafana_alertmanager",
"config_kind": "service_backup_script",
"control_tier": "C0",
"current_state": "service_backup_script_visible_gate_closed",
"backup_scope": ["Prometheus", "Grafana", "Alertmanager"],
"requires_live_evidence": True,
"requires_owner_response": True,
"next_owner_action": "補 observability backup owner、Grafana secret boundary、alert route restore smoke 與 rollback owner。",
},
{
"surface_id": "backup_signoz_service_script",
"label": "SigNoz 備份腳本",
"source_path": "scripts/backup/backup-signoz.sh",
"expected_scope": "signoz_clickhouse_and_sqlite",
"config_kind": "service_backup_script",
"control_tier": "C0",
"current_state": "service_backup_script_visible_gate_closed",
"backup_scope": ["SigNoz ClickHouse", "SigNoz SQLite"],
"requires_live_evidence": True,
"requires_owner_response": True,
"next_owner_action": "補 SigNoz disruptive guard owner、ClickHouse restore owner、告警靜音邊界與 post-check 指標。",
},
{
"surface_id": "backup_open_webui_service_script",
"label": "Open-WebUI 備份腳本",
"source_path": "scripts/backup/backup-open-webui.sh",
"expected_scope": "open_webui_volume",
"config_kind": "service_backup_script",
"control_tier": "C0",
"current_state": "service_backup_script_visible_gate_closed",
"backup_scope": ["Open-WebUI volume", "LLM conversation data"],
"requires_live_evidence": True,
"requires_owner_response": True,
"next_owner_action": "補 Open-WebUI data privacy owner、188 read boundary、restore target isolation 與 retention owner。",
},
{
"surface_id": "backup_clawbot_service_script",
"label": "ClawBot Redis 備份腳本",
"source_path": "scripts/backup/backup-clawbot.sh",
"expected_scope": "clawbot_redis_state",
"config_kind": "service_backup_script",
"control_tier": "C0",
"current_state": "service_backup_script_visible_gate_closed",
"backup_scope": ["ClawBot Redis", "agent state cache"],
"requires_live_evidence": True,
"requires_owner_response": True,
"next_owner_action": "補 ClawBot state owner、Redis restore owner、agent state masking 與 rollback owner。",
},
{
"surface_id": "backup_sentry_service_script",
"label": "Sentry 備份腳本",
"source_path": "scripts/backup/backup-sentry.sh",
"expected_scope": "sentry_self_hosted",
"config_kind": "service_backup_script",
"control_tier": "C0",
"current_state": "service_backup_script_visible_gate_closed",
"backup_scope": ["Sentry", "ClickHouse / Postgres / Redis dependency boundary"],
"requires_live_evidence": True,
"requires_owner_response": True,
"next_owner_action": "補 Sentry backup owner、multi-store restore owner、admin secret boundary 與 route smoke。",
},
{
"surface_id": "backup_ai_artifacts_script",
"label": "AI artifacts 備份腳本",
"source_path": "scripts/backup/backup-ai-artifacts.sh",
"expected_scope": "ai_artifacts",
"config_kind": "service_backup_script",
"control_tier": "C0",
"current_state": "artifact_backup_script_visible_gate_closed",
"backup_scope": ["AI artifacts", "model / evaluation outputs"],
"requires_live_evidence": True,
"requires_owner_response": True,
"next_owner_action": "補 artifact owner、retention owner、模型資料外送邊界與 restore validation。",
},
{
"surface_id": "backup_public_routes_script",
"label": "Public routes 備份腳本",
"source_path": "scripts/backup/backup-public-routes.sh",
"expected_scope": "public_route_reconstruction",
"config_kind": "service_backup_script",
"control_tier": "C0",
"current_state": "route_backup_script_visible_gate_closed",
"backup_scope": ["public routes", "Nginx route reconstruction", "frontend/API smoke evidence"],
"requires_live_evidence": True,
"requires_owner_response": True,
"next_owner_action": "補 route reconstruction owner、public/admin/API smoke、rollback ref 與 no-internal-transcript proof。",
},
{
"surface_id": "config_backup_capture",
"label": "Host / service / K8s 設定備份",
"source_path": "scripts/backup/backup-configs.sh",
"expected_scope": "110_188_120_121_cluster_configs",
"config_kind": "config_backup_script",
"control_tier": "C0",
"current_state": "config_capture_visible_blocked_until_owner_evidence",
"backup_scope": ["systemd", "docker", "nginx", "cron", "k8s", "host configs"],
"requires_live_evidence": True,
"requires_owner_response": True,
"next_owner_action": "補 config capture owner、secret redaction proof、120 blocked disposition、restore validation 與 retention owner。",
},
{
"surface_id": "backup_status_reporter",
"label": "備份狀態彙整腳本",
"source_path": "scripts/backup/backup-status.sh",
"expected_scope": "110_188_backup_status_summary",
"config_kind": "backup_status_reporter",
"control_tier": "C0",
"current_state": "status_reporter_visible_not_executed",
"backup_scope": ["freshness", "failure", "integrity", "restore drill", "offsite", "escrow"],
"requires_live_evidence": True,
"requires_owner_response": True,
"next_owner_action": "補 backup status owner、read-only execution window、SSH read boundary、notification owner 與 false-green 防線。",
},
{
"surface_id": "backup_integrity_check",
"label": "Restic integrity check",
"source_path": "scripts/backup/check-backup-integrity.sh",
"expected_scope": "restic_integrity_check",
"config_kind": "integrity_check_script",
"control_tier": "C0",
"current_state": "integrity_check_visible_not_executed",
"backup_scope": ["restic check", "read-data subset", "integrity evidence"],
"requires_live_evidence": True,
"requires_owner_response": True,
"next_owner_action": "補 integrity check owner、執行窗口、資源上限、結果證據與 restore drill 前置條件。",
},
{
"surface_id": "latest_only_retention_enforcer",
"label": "Latest-only retention enforcer",
"source_path": "scripts/backup/enforce-latest-only-retention.sh",
"expected_scope": "latest_only_retention",
"config_kind": "retention_enforcer",
"control_tier": "C0",
"current_state": "delete_capable_retention_script_visible_gate_closed",
"backup_scope": ["keep latest", "local delete", "retention marker"],
"requires_live_evidence": True,
"requires_owner_response": True,
"next_owner_action": "補 retention owner、刪除窗口、restore runway、offsite mirror interaction 與 rollback / stop condition。",
},
{
"surface_id": "offsite_sync_controller",
"label": "Offsite rclone sync controller",
"source_path": "scripts/backup/sync-offsite-backups.sh",
"expected_scope": "google_drive_rclone_offsite_mirror",
"config_kind": "offsite_sync_controller",
"control_tier": "C0",
"current_state": "remote_write_and_delete_capable_sync_visible_gate_closed",
"backup_scope": ["13 repos", "rclone sync", "remote delete", "success markers"],
"requires_live_evidence": True,
"requires_owner_response": True,
"next_owner_action": "補 offsite owner、remote delete owner、runway check、full sync window、rclone credential escrow 與 verifier evidence。",
},
{
"surface_id": "offsite_full_sync_verifier",
"label": "Offsite full sync verifier",
"source_path": "scripts/backup/verify-offsite-full-sync.sh",
"expected_scope": "offsite_full_sync_verification",
"config_kind": "offsite_verifier",
"control_tier": "C0",
"current_state": "remote_read_and_textfile_write_capable_verifier_visible_gate_closed",
"backup_scope": ["remote repo count", "latest-only evidence", "textfile metrics"],
"requires_live_evidence": True,
"requires_owner_response": True,
"next_owner_action": "補 verifier owner、remote read window、metric write owner、failure notification owner 與 evidence retention。",
},
{
"surface_id": "offsite_readiness_gate",
"label": "Offsite readiness gate",
"source_path": "scripts/backup/backup-offsite-readiness-gate.sh",
"expected_scope": "offsite_preflight_and_escrow_gate",
"config_kind": "offsite_readiness_gate",
"control_tier": "C0",
"current_state": "readiness_gate_visible_not_executed",
"backup_scope": ["status", "dry-run-small", "pre-full-sync", "escrow markers"],
"requires_live_evidence": True,
"requires_owner_response": True,
"next_owner_action": "補 readiness owner、dry-run scope、escrow owner、load/runway policy 與 accepted evidence refs。",
},
{
"surface_id": "offsite_escrow_evidence_report",
"label": "Offsite / escrow evidence report",
"source_path": "scripts/backup/offsite-escrow-evidence-report.sh",
"expected_scope": "offsite_escrow_redacted_report",
"config_kind": "offsite_escrow_report",
"control_tier": "C0",
"current_state": "redacted_report_visible_default_no_remote_status",
"backup_scope": ["script presence", "offsite marker", "escrow marker", "redacted output"],
"requires_live_evidence": True,
"requires_owner_response": True,
"next_owner_action": "補 evidence report owner、remote status opt-in owner、redaction proof 與 blocked marker disposition。",
},
{
"surface_id": "credential_escrow_marker",
"label": "Credential escrow marker writer",
"source_path": "scripts/backup/mark-credential-escrow-verified.sh",
"expected_scope": "credential_escrow_markers",
"config_kind": "credential_escrow_marker",
"control_tier": "C0",
"current_state": "marker_write_capable_script_visible_gate_closed",
"backup_scope": ["restic password", "offsite provider", "break-glass admin", "DNS recovery", "OAuth / AI provider recovery"],
"requires_live_evidence": True,
"requires_owner_response": True,
"next_owner_action": "補 escrow owner、non-secret evidence id、reviewer acceptance、marker write approval 與 retention owner。",
},
{
"surface_id": "offsite_rclone_config",
"label": "rclone offsite config helper",
"source_path": "scripts/backup/configure-offsite-rclone.sh",
"expected_scope": "rclone_config_metadata",
"config_kind": "offsite_rclone_config",
"control_tier": "C0",
"current_state": "credential_config_helper_visible_secret_values_not_collected",
"backup_scope": ["rclone remote", "Google Drive", "offsite.env metadata"],
"requires_live_evidence": True,
"requires_owner_response": True,
"next_owner_action": "補 rclone config owner、secret store owner、file mode evidence、no-value collection proof 與 recovery owner。",
},
{
"surface_id": "offsite_b2_config",
"label": "B2 offsite config helper",
"source_path": "scripts/backup/configure-offsite-b2.sh",
"expected_scope": "b2_config_metadata",
"config_kind": "offsite_b2_config",
"control_tier": "C0",
"current_state": "credential_config_helper_visible_secret_values_not_collected",
"backup_scope": ["Backblaze B2 metadata", "offsite env", "fallback provider"],
"requires_live_evidence": True,
"requires_owner_response": True,
"next_owner_action": "補 B2 provider owner、credential escrow owner、provider cost boundary 與 no-value collection proof。",
},
{
"surface_id": "backup_health_textfile_exporter",
"label": "Backup health textfile exporter",
"source_path": "scripts/ops/backup-health-textfile-exporter.py",
"expected_scope": "backup_health_prometheus_textfile",
"config_kind": "backup_health_exporter",
"control_tier": "C0",
"current_state": "textfile_write_capable_exporter_visible_gate_closed",
"backup_scope": ["freshness metrics", "restore drill metrics", "offsite metrics", "escrow metrics"],
"requires_live_evidence": True,
"requires_owner_response": True,
"next_owner_action": "補 exporter owner、textfile path owner、metric freshness SLO、false-green guard 與 alert owner。",
},
{
"surface_id": "velero_restore_test_cronjob",
"label": "Velero restore dry-run CronJob",
"source_path": "k8s/awoooi-prod/16-cronjob-backup-restore-test.yaml",
"expected_scope": "velero_weekly_restore_dry_run",
"config_kind": "velero_restore_cronjob",
"control_tier": "C0",
"current_state": "k8s_cronjob_manifest_visible_not_applied_by_this_inventory",
"backup_scope": ["Velero restore dry-run", "weekly schedule", "textfile metrics"],
"requires_live_evidence": True,
"requires_owner_response": True,
"next_owner_action": "補 Velero owner、dry-run namespace isolation、CronJob live evidence、restore approval 與 post-check 指標。",
},
{
"surface_id": "velero_restore_test_script_configmap",
"label": "Velero restore script ConfigMap",
"source_path": "k8s/awoooi-prod/17-configmap-backup-restore-scripts.yaml",
"expected_scope": "velero_restore_script_configmap",
"config_kind": "velero_restore_script_configmap",
"control_tier": "C0",
"current_state": "configmap_script_visible_timestamp_format_needs_owner_disposition",
"backup_scope": ["restore dry-run script", "13-digit textfile timestamp risk", "Prometheus textfile"],
"requires_live_evidence": True,
"requires_owner_response": True,
"next_owner_action": "補 ConfigMap owner、timestamp format disposition、CronJob rollout owner、metric scrape proof 與 rollback owner。",
},
{
"surface_id": "velero_standalone_restore_test_script",
"label": "Velero restore dry-run standalone script",
"source_path": "scripts/cron_backup_restore_test.sh",
"expected_scope": "velero_standalone_restore_script",
"config_kind": "velero_restore_standalone_script",
"control_tier": "C0",
"current_state": "standalone_script_visible_uses_seconds_textfile_timestamp_not_executed",
"backup_scope": ["restore dry-run", "Prometheus textfile seconds timestamp", "failure metric"],
"requires_live_evidence": True,
"requires_owner_response": True,
"next_owner_action": "補 standalone / ConfigMap drift disposition、restore drill owner、textfile owner 與 proof of isolation。",
},
{
"surface_id": "velero_credentials_manifest",
"label": "Velero MinIO credential manifest",
"source_path": "k8s/velero/01-credentials.yaml",
"expected_scope": "velero_minio_credentials_metadata",
"config_kind": "velero_credentials_manifest",
"control_tier": "C0",
"current_state": "placeholder_secret_manifest_visible_values_not_collected",
"backup_scope": ["MinIO credential names", "placeholder values", "External Secrets / Sealed Secrets recommendation"],
"requires_live_evidence": True,
"requires_owner_response": True,
"next_owner_action": "補 Velero credential owner、secret manager source、rotation owner、no-value collection proof 與 restore boundary。",
},
{
"surface_id": "velero_install_manifest",
"label": "Velero install manifest",
"source_path": "k8s/velero/02-velero-install.yaml",
"expected_scope": "velero_install_and_minio_storage",
"config_kind": "velero_install_manifest",
"control_tier": "C0",
"current_state": "cluster_admin_velero_manifest_visible_gate_closed",
"backup_scope": ["Velero Deployment", "cluster-admin binding", "MinIO s3Url", "backup storage location"],
"requires_live_evidence": True,
"requires_owner_response": True,
"next_owner_action": "補 Velero RBAC owner、MinIO endpoint owner、least privilege review、install window 與 rollback owner。",
},
{
"surface_id": "backup_restore_alert_rules",
"label": "Backup / restore alert rules",
"source_path": "ops/monitoring/alerts.yml",
"expected_scope": "backup_restore_prometheus_alerts",
"config_kind": "backup_restore_alert_rules",
"control_tier": "C0",
"current_state": "alert_rule_source_visible_reload_not_authorized",
"backup_scope": ["BackupRestoreTestFailed", "Velero freshness", "offsite freshness", "restore stale"],
"requires_live_evidence": True,
"requires_owner_response": True,
"next_owner_action": "補 alert rule owner、receiver owner、reload owner、silence boundary 與 failure-only notification policy。",
},
{
"surface_id": "backup_dr_readiness_contract",
"label": "Backup / DR readiness matrix",
"source_path": "docs/evaluations/backup_dr_readiness_matrix_2026-06-04.json",
"expected_scope": "backup_dr_readiness_contract",
"config_kind": "dr_readiness_contract",
"control_tier": "C0",
"current_state": "readiness_contract_visible_action_required_items_not_accepted",
"backup_scope": ["readiness matrix", "blocked targets", "restore drill status"],
"requires_live_evidence": True,
"requires_owner_response": True,
"next_owner_action": "補 readiness owner、blocked target disposition、freshness evidence、restore drill owner 與 accepted refs。",
},
{
"surface_id": "backup_restore_drill_approval_template",
"label": "Restore drill approval package template",
"source_path": "docs/evaluations/backup_restore_drill_approval_package_template_2026-06-05.json",
"expected_scope": "restore_drill_approval_template",
"config_kind": "restore_drill_approval_template",
"control_tier": "C0",
"current_state": "approval_template_visible_no_restore_execution",
"backup_scope": ["database restore", "configuration restore", "credential escrow", "K8s restore", "observability restore"],
"requires_live_evidence": True,
"requires_owner_response": True,
"next_owner_action": "補 owner response 實際封包、隔離環境、observer、rollback owner 與 restore stop condition。",
},
{
"surface_id": "offsite_escrow_readiness_contract",
"label": "Offsite / escrow readiness status",
"source_path": "docs/evaluations/offsite_escrow_readiness_status_2026-06-05.json",
"expected_scope": "offsite_escrow_readiness_contract",
"config_kind": "offsite_escrow_readiness_contract",
"control_tier": "C0",
"current_state": "offsite_verified_but_escrow_and_velero_blocked",
"backup_scope": ["offsite_rclone_full_sync", "credential_escrow_markers", "velero_k8s_resources"],
"requires_live_evidence": True,
"requires_owner_response": True,
"next_owner_action": "補 escrow marker owner、Velero metric binding、remote evidence expiry owner 與 offsite sync approval boundary。",
},
{
"surface_id": "backup_status_runbook",
"label": "Backup status runbook",
"source_path": "docs/runbooks/BACKUP-STATUS.md",
"expected_scope": "backup_status_runbook",
"config_kind": "backup_status_runbook",
"control_tier": "C0",
"current_state": "runbook_visible_contains_live_refresh_notes_needs_revalidation",
"backup_scope": ["110 backup center", "latest-only", "Google Drive / rclone", "credential escrow", "120 blocker"],
"requires_live_evidence": True,
"requires_owner_response": True,
"next_owner_action": "補截至本次階段的 owner-provided live refresh、stale evidence disposition、escrow blocker owner 與 validation refs。",
},
{
"surface_id": "cold_start_sop",
"label": "Full-stack cold-start SOP",
"source_path": "docs/runbooks/FULL-STACK-COLD-START-SOP.md",
"expected_scope": "cold_start_backup_restore_recovery",
"config_kind": "cold_start_sop",
"control_tier": "C0",
"current_state": "sop_visible_contains_backup_commands_not_authorized",
"backup_scope": ["cold start", "backup-all", "sync-offsite", "restore guard", "schedules"],
"requires_live_evidence": True,
"requires_owner_response": True,
"next_owner_action": "補 cold-start commander owner、backup command approval boundary、restore stop condition、rollback owner 與 post-start validation。",
},
]
FALSE_BOUNDARIES = {
"runtime_execution_authorized": False,
"host_write_authorized": False,
"backup_run_authorized": False,
"restore_run_authorized": False,
"restore_drill_authorized": False,
"offsite_sync_authorized": False,
"offsite_remote_delete_authorized": False,
"credential_escrow_marker_write_authorized": False,
"retention_change_authorized": False,
"restic_prune_authorized": False,
"rclone_config_authorized": False,
"velero_restore_authorized": False,
"velero_backup_authorized": False,
"kubectl_action_authorized": False,
"ssh_read_authorized": False,
"ssh_write_authorized": False,
"secret_value_collection_allowed": False,
"active_scan_authorized": False,
"action_buttons_allowed": False,
}
WRITE_CAPABLE_KINDS = {
"backup_orchestrator",
"service_backup_script",
"config_backup_script",
"retention_enforcer",
"offsite_sync_controller",
"offsite_verifier",
"credential_escrow_marker",
"offsite_rclone_config",
"offsite_b2_config",
"backup_health_exporter",
"velero_restore_cronjob",
"velero_restore_script_configmap",
"velero_restore_standalone_script",
"velero_credentials_manifest",
"velero_install_manifest",
}
BACKUP_SCRIPT_KINDS = {
"backup_orchestrator",
"service_backup_script",
"config_backup_script",
}
RESTORE_DRILL_KINDS = {
"velero_restore_cronjob",
"velero_restore_script_configmap",
"velero_restore_standalone_script",
"restore_drill_approval_template",
}
OFFSITE_ESCROW_KINDS = {
"offsite_sync_controller",
"offsite_verifier",
"offsite_readiness_gate",
"offsite_escrow_report",
"credential_escrow_marker",
"offsite_rclone_config",
"offsite_b2_config",
"offsite_escrow_readiness_contract",
}
VELERO_KINDS = {
"velero_restore_cronjob",
"velero_restore_script_configmap",
"velero_restore_standalone_script",
"velero_credentials_manifest",
"velero_install_manifest",
}
RETENTION_SURFACE_IDS = {
"backup_common_restic_retention",
"latest_only_retention_enforcer",
"offsite_sync_controller",
}
CREDENTIAL_SURFACE_IDS = {
"backup_common_restic_retention",
"credential_escrow_marker",
"offsite_rclone_config",
"offsite_b2_config",
"velero_credentials_manifest",
}
DR_CONTRACT_KINDS = {
"dr_readiness_contract",
"restore_drill_approval_template",
"offsite_escrow_readiness_contract",
}
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 file_metadata(root: Path, source_path: str) -> dict[str, Any]:
path = root / source_path
exists = path.exists()
if not exists:
return {"source_exists": False, "line_count": 0, "sha256": None}
content = path.read_bytes()
return {
"source_exists": True,
"line_count": len(content.decode("utf-8", errors="replace").splitlines()),
"sha256": hashlib.sha256(content).hexdigest(),
}
def build_surface(root: Path, surface: dict[str, Any]) -> dict[str, Any]:
metadata = file_metadata(root, surface["source_path"])
return {
**surface,
**metadata,
"owner_response_received": False,
"owner_response_accepted": False,
"live_evidence_received": False,
"restore_drill_accepted": False,
"offsite_sync_accepted": False,
"credential_escrow_accepted": False,
"retention_change_accepted": False,
"maintenance_window_accepted": False,
"rollback_owner_accepted": False,
"runtime_gate_open": False,
"action_buttons_allowed": False,
}
def count_kind(surfaces: list[dict[str, Any]], kinds: set[str]) -> int:
return sum(1 for surface in surfaces if surface["config_kind"] in kinds)
def build_report(root: Path, generated_at: str | None) -> dict[str, Any]:
report_time = generated_at or datetime.now(TAIPEI).isoformat(timespec="seconds")
surfaces = [build_surface(root, surface) for surface in SURFACES]
expected_scopes = sorted({surface["expected_scope"] for surface in surfaces})
write_capable = [surface for surface in surfaces if surface["config_kind"] in WRITE_CAPABLE_KINDS]
return {
"schema_version": "backup_restore_escrow_inventory_v1",
"generated_at": report_time,
"git_commit": git_short_sha(root),
"status": "repo_only_inventory_ready",
"source_scope": "committed_repo_files_only",
"summary": {
"surface_count": len(surfaces),
"source_exists_count": sum(1 for surface in surfaces if surface["source_exists"]),
"expected_scope_count": len(expected_scopes),
"backup_script_surface_count": count_kind(surfaces, BACKUP_SCRIPT_KINDS),
"restore_drill_surface_count": count_kind(surfaces, RESTORE_DRILL_KINDS),
"offsite_escrow_surface_count": count_kind(surfaces, OFFSITE_ESCROW_KINDS),
"velero_surface_count": count_kind(surfaces, VELERO_KINDS),
"retention_surface_count": sum(1 for surface in surfaces if surface["surface_id"] in RETENTION_SURFACE_IDS),
"credential_surface_count": sum(1 for surface in surfaces if surface["surface_id"] in CREDENTIAL_SURFACE_IDS),
"alert_surface_count": sum(1 for surface in surfaces if surface["config_kind"] == "backup_restore_alert_rules"),
"dr_readiness_contract_surface_count": count_kind(surfaces, DR_CONTRACT_KINDS),
"write_capable_surface_count": len(write_capable),
"surfaces_requiring_owner_response_count": sum(1 for surface in surfaces if surface["requires_owner_response"]),
"surfaces_requiring_live_evidence_count": sum(1 for surface in surfaces if surface["requires_live_evidence"]),
"owner_response_received_count": 0,
"owner_response_accepted_count": 0,
"live_evidence_received_count": 0,
"restore_drill_accepted_count": 0,
"offsite_sync_accepted_count": 0,
"credential_escrow_accepted_count": 0,
"retention_change_accepted_count": 0,
"maintenance_window_accepted_count": 0,
"rollback_owner_accepted_count": 0,
"runtime_gate_count": 0,
"action_button_count": 0,
"coverage_percent_before_inventory": 52,
"coverage_percent_after_inventory": 58,
},
"execution_boundaries": FALSE_BOUNDARIES,
"expected_scopes": expected_scopes,
"backup_surfaces": surfaces,
"write_capable_surfaces": [
{
"surface_id": surface["surface_id"],
"label": surface["label"],
"config_kind": surface["config_kind"],
"expected_scope": surface["expected_scope"],
"required_gate": "owner_response_plus_maintenance_window_plus_rollback_owner",
}
for surface in write_capable
],
"next_collection_order": [
"backup_common_restic_retention",
"offsite_sync_controller",
"credential_escrow_marker",
"velero_restore_test_script_configmap",
"velero_credentials_manifest",
"backup_health_textfile_exporter",
"backup_restore_alert_rules",
"backup_restore_drill_approval_template",
"backup_status_runbook",
"cold_start_sop",
],
"operator_interpretation": [
"這是 repo-only backup / restore / escrow / retention 清冊,不是 live backup、remote provider 或 cluster truth。",
"source_exists=true 只代表 repo 檔案存在不代表備份已成功、restore drill 已執行、offsite sync 已授權或 escrow marker 已可寫入。",
"write-capable surface 可見代表需要資安控管,不代表 backup、restore、rclone sync、remote delete、restic prune、Velero restore 或 kubectl 已授權。",
"所有 owner response、live evidence、restore drill acceptance、offsite sync acceptance、credential escrow acceptance、retention change acceptance 與 runtime gate 仍為 0。",
],
}
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(description="產生 IwoooS backup / restore / escrow / retention repo-only 清冊")
parser.add_argument("--root", default=".", help="repo root")
parser.add_argument("--output", help="輸出 JSON 檔")
parser.add_argument("--generated-at", help="固定 generated_at")
return parser.parse_args()
def main() -> int:
args = parse_args()
root = Path(args.root).resolve()
report = build_report(root, args.generated_at)
text = json.dumps(report, ensure_ascii=False, indent=2, sort_keys=True)
if args.output:
Path(args.output).write_text(text + "\n", encoding="utf-8")
else:
print(text)
print(
"BACKUP_RESTORE_ESCROW_INVENTORY_OK "
f"surfaces={report['summary']['surface_count']} "
f"backup_scripts={report['summary']['backup_script_surface_count']} "
f"offsite_escrow={report['summary']['offsite_escrow_surface_count']} "
f"runtime_gate={report['summary']['runtime_gate_count']}",
file=sys.stderr,
)
return 0
if __name__ == "__main__":
raise SystemExit(main())