#!/usr/bin/env python3 """Build the single P0-005 DR escrow evidence checklist.""" from __future__ import annotations import argparse import json from datetime import datetime from pathlib import Path from typing import Any SCHEMA_VERSION = "awoooi_dr_escrow_evidence_checklist_v1" OWNER_PACKET_SCHEMA = "awoooi_post_reboot_next_gate_owner_packets_v1" OWNER_RESPONSE_SCHEMA = "awoooi_post_reboot_next_gate_owner_response_v1" GATE_ID = "credential_escrow_evidence" ITEMS = [ "restic_repository_password", "offsite_provider_credentials", "break_glass_admin_credentials", "dns_registrar_recovery", "oauth_ai_provider_recovery", ] def parse_args() -> argparse.Namespace: parser = argparse.ArgumentParser( description="Emit one no-secret checklist for the P0-005 DR escrow lane.", ) parser.add_argument("--output", type=Path, help="Write JSON to this path.") return parser.parse_args() def evidence_item(item_id: str) -> dict[str, Any]: return { "item_id": item_id, "required_fields": [ "non_secret_evidence_ref", "recovery_owner", "reviewer", "last_reviewed_at", "contains_secret_value=false", ], "accepted_ref_classes": [ "password_manager_item_id", "sealed_envelope_id", "recovery_checklist_id", "review_ticket_id", ], "rejected_values": [ "passwords", "tokens", "private keys", "recovery codes", "secret URLs", "session cookies", ], "marker_dry_run_command": ( "/backup/scripts/mark-credential-escrow-verified.sh " f"--item {item_id} --evidence-id --dry-run" ), } def owner_packet() -> dict[str, Any]: return { "schema_version": OWNER_PACKET_SCHEMA, "source": {"next_required_gates": [GATE_ID]}, "owner_packets": [ { "packet_id": GATE_ID, "title": "P0-005 DR credential escrow evidence", "priority": "P0", "required_items": ITEMS, } ], } def owner_response_skeleton() -> dict[str, Any]: return { "schema_version": OWNER_RESPONSE_SCHEMA, "generated_at": datetime.now().astimezone().isoformat(timespec="seconds"), "responses": [ { "gate_id": GATE_ID, "owner_role": "owner_role_here", "owner_team": "owner_team_here", "decision": "pending", "decision_reason": "decision_reason_here", "affected_scope": "P0-005 DR credential escrow evidence", "redacted_evidence_refs": ["redacted_evidence_ref_here"], "followup_owner": "followup_owner_here", "runtime_action_requested": False, "runtime_action_authorized": False, "host_write_requested": False, "host_write_authorized": False, "secret_value_included": False, "secret_value_collection_allowed": False, "credential_marker_write_requested": False, "credential_marker_write_authorized": False, "escrow_items": [ { "item_id": item_id, "non_secret_evidence_ref": "non_secret_evidence_ref_here", "recovery_owner": "owner_role_here", "reviewer": "reviewer_here", "last_reviewed_at": "pending", "contains_secret_value": False, } for item_id in ITEMS ], } ], } def build_payload() -> dict[str, Any]: return { "schema_version": SCHEMA_VERSION, "generated_at": datetime.now().astimezone().isoformat(timespec="seconds"), "workplan_id": "P0-005", "status": "waiting_for_five_redacted_non_secret_evidence_refs", "active_gate": GATE_ID, "required_item_count": len(ITEMS), "required_items": [evidence_item(item_id) for item_id in ITEMS], "owner_packet": owner_packet(), "owner_response_skeleton": owner_response_skeleton(), "single_preflight_command": ( "python3 scripts/reboot-recovery/post-reboot-owner-response-preflight.py " "--owner-packet-file " "--response-file --json --no-color" ), "scorecard_command": ( "python3 scripts/reboot-recovery/post-reboot-credential-escrow-intake-scorecard.py " "--summary-file --owner-packet-file " "--response-file " "--offsite-report-file " "--escrow-status-file --json --no-color" ), "exit_criteria": [ "preflight_status=ready_for_independent_reviewer_acceptance", "owner_response_received_count=1", "owner_response_accepted_count=1", "forbidden_true_field_count=0", "effective_escrow_missing_count=0 after marker dry-runs and accepted marker writes", ], "execution_rules": [ "Use this as the only P0-005 intake packet.", "Do not create per-item owner packets.", "Do not include secret values in evidence refs.", "Do not reopen cold-start or CI/CD while this checklist is pending.", ], } def main() -> int: args = parse_args() payload = build_payload() text = json.dumps(payload, indent=2, ensure_ascii=False) + "\n" if args.output: args.output.parent.mkdir(parents=True, exist_ok=True) args.output.write_text(text, encoding="utf-8") else: print(text, end="") return 0 if __name__ == "__main__": raise SystemExit(main())