#!/usr/bin/env python3 """Export exact private replay material for one guarded applied proposal. The exporter is intentionally read-only. It validates an existing worker replay receipt, reads the approved and applied proposal rows through one gate-owned function, and atomically publishes deterministic genesis-ledger input. """ from __future__ import annotations import argparse import json import os import stat import sys import tempfile import uuid from datetime import datetime from pathlib import Path from typing import Any HERE = Path(__file__).resolve().parent sys.path.insert(0, str(HERE)) import apply_proposal as apply_engine # noqa: E402 import kb_apply_replay_receipt as replay_receipt # noqa: E402 MATERIAL_ARTIFACT = "teleo_strict_proposal_replay_material" MATERIAL_CONTRACT_VERSION = 1 MATERIAL_FIELDS = frozenset( { "artifact", "contract_version", "sequence", "approved_proposal", "approval_snapshot", "applied_proposal", "replay_receipt", } ) PROPOSAL_FIELDS = frozenset( { "id", "proposal_type", "status", "proposed_by_handle", "proposed_by_agent_id", "channel", "source_ref", "rationale", "payload", "reviewed_by_handle", "reviewed_by_agent_id", "reviewed_at", "review_note", "applied_by_handle", "applied_by_agent_id", "applied_at", "created_at", "updated_at", } ) APPROVAL_FIELDS = frozenset( { "proposal_id", "proposal_type", "payload", "reviewed_by_handle", "reviewed_by_agent_id", "reviewed_by_db_role", "reviewed_at", "review_note", } ) PROPOSAL_TRANSITION_FIELDS = frozenset( {"status", "applied_by_handle", "applied_by_agent_id", "applied_at", "updated_at"} ) PROPOSAL_UUID_FIELDS = frozenset({"id", "proposed_by_agent_id", "reviewed_by_agent_id", "applied_by_agent_id"}) PROPOSAL_TIMESTAMP_FIELDS = frozenset({"reviewed_at", "applied_at", "created_at", "updated_at"}) APPROVAL_UUID_FIELDS = frozenset({"proposal_id", "reviewed_by_agent_id"}) APPROVAL_TIMESTAMP_FIELDS = frozenset({"reviewed_at"}) RECEIPT_PROPOSAL_FIELDS = ( "id", "proposal_type", "status", "payload", "reviewed_by_handle", "reviewed_by_agent_id", "reviewed_at", "review_note", "applied_by_handle", "applied_by_agent_id", "applied_at", ) class ExportError(RuntimeError): """A fail-closed error that contains no proposal payload material.""" def _canonical_json(value: Any) -> str: return json.dumps(value, ensure_ascii=False, separators=(",", ":"), sort_keys=True) def _same_json(left: Any, right: Any) -> bool: return _canonical_json(left) == _canonical_json(right) def _reject_duplicate_keys(pairs: list[tuple[str, Any]]) -> dict[str, Any]: result: dict[str, Any] = {} for key, value in pairs: if key in result: raise ExportError("JSON input contains duplicate object fields") result[key] = value return result def _reject_nonfinite_number(value: str) -> None: raise ExportError(f"JSON input contains invalid numeric constant {value}") def _parse_json_object(raw: str, *, label: str) -> dict[str, Any]: try: value = json.loads( raw, object_pairs_hook=_reject_duplicate_keys, parse_constant=_reject_nonfinite_number, ) except (json.JSONDecodeError, UnicodeError) as exc: raise ExportError(f"{label} is not valid UTF-8 JSON") from exc if not isinstance(value, dict): raise ExportError(f"{label} must contain one JSON object") return value def _absolute_path(value: str | Path) -> Path: path = Path(value).expanduser() if not path.is_absolute(): path = Path.cwd() / path return path def _load_private_receipt(path: Path) -> dict[str, Any]: path = _absolute_path(path) try: metadata = path.lstat() except OSError as exc: raise ExportError("worker replay receipt is unavailable") from exc if path.is_symlink() or not stat.S_ISREG(metadata.st_mode): raise ExportError("worker replay receipt must be a regular non-symlink file") if stat.S_IMODE(metadata.st_mode) != 0o600: raise ExportError("worker replay receipt permissions must be exactly 0600") try: raw = path.read_text(encoding="utf-8") except (OSError, UnicodeError) as exc: raise ExportError("worker replay receipt is unreadable") from exc return _parse_json_object(raw, label="worker replay receipt") def _require_exact_fields(value: Any, expected: frozenset[str], *, label: str) -> dict[str, Any]: if not isinstance(value, dict): raise ExportError(f"{label} must be an object") observed = frozenset(value) if observed != expected: raise ExportError( f"{label} has invalid fields; missing_count={len(expected - observed)}, " f"extra_count={len(observed - expected)}" ) return value def _canonical_uuid(value: Any, *, label: str, nullable: bool = False) -> str | None: if value is None and nullable: return None try: return str(uuid.UUID(str(value))) except (AttributeError, TypeError, ValueError) as exc: raise ExportError(f"{label} must be a UUID") from exc def _canonical_timestamp(value: Any, *, label: str, nullable: bool = False) -> str | None: if value is None and nullable: return None try: return replay_receipt.canonical_utc_timestamp(value, path=label) except ValueError as exc: raise ExportError(f"{label} must be a timezone-aware timestamp") from exc def _normalize_proposal(value: Any, *, label: str) -> dict[str, Any]: row = dict(_require_exact_fields(value, PROPOSAL_FIELDS, label=label)) for field in PROPOSAL_UUID_FIELDS: row[field] = _canonical_uuid( row[field], label=f"{label}.{field}", nullable=field != "id", ) for field in PROPOSAL_TIMESTAMP_FIELDS: row[field] = _canonical_timestamp( row[field], label=f"{label}.{field}", nullable=field in {"reviewed_at", "applied_at"}, ) if not isinstance(row["proposal_type"], str) or not row["proposal_type"]: raise ExportError(f"{label}.proposal_type must be a non-empty string") if not isinstance(row["status"], str) or not row["status"]: raise ExportError(f"{label}.status must be a non-empty string") if not isinstance(row["payload"], dict): raise ExportError(f"{label}.payload must be an object") for field in ( PROPOSAL_FIELDS - PROPOSAL_UUID_FIELDS - PROPOSAL_TIMESTAMP_FIELDS - { "payload", } ): if field in {"proposal_type", "status"}: continue if row[field] is not None and not isinstance(row[field], str): raise ExportError(f"{label}.{field} must be text or null") return row def _normalize_approval(value: Any, *, label: str) -> dict[str, Any]: row = dict(_require_exact_fields(value, APPROVAL_FIELDS, label=label)) for field in APPROVAL_UUID_FIELDS: row[field] = _canonical_uuid(row[field], label=f"{label}.{field}") for field in APPROVAL_TIMESTAMP_FIELDS: row[field] = _canonical_timestamp(row[field], label=f"{label}.{field}") if not isinstance(row["payload"], dict): raise ExportError(f"{label}.payload must be an object") for field in APPROVAL_FIELDS - APPROVAL_UUID_FIELDS - APPROVAL_TIMESTAMP_FIELDS - {"payload"}: if not isinstance(row[field], str) or not row[field].strip(): raise ExportError(f"{label}.{field} must be non-empty text") return row def _parse_timestamp(value: str) -> datetime: return datetime.fromisoformat(value.replace("Z", "+00:00")) def validate_material( material: dict[str, Any], *, expected_proposal_id: str | None = None, expected_sequence: int | None = None, ) -> dict[str, Any]: """Validate and canonicalize the exact seven-field replay material.""" _require_exact_fields(material, MATERIAL_FIELDS, label="replay material") if material["artifact"] != MATERIAL_ARTIFACT: raise ExportError("replay material artifact is unsupported") if type(material["contract_version"]) is not int or material["contract_version"] != MATERIAL_CONTRACT_VERSION: raise ExportError("replay material contract version is unsupported") sequence = material["sequence"] if type(sequence) is not int or sequence <= 0: raise ExportError("replay material sequence must be a positive integer") if expected_sequence is not None and sequence != expected_sequence: raise ExportError("replay material sequence does not match the requested sequence") approved = _normalize_proposal(material["approved_proposal"], label="approved_proposal") approval = _normalize_approval(material["approval_snapshot"], label="approval_snapshot") applied = _normalize_proposal(material["applied_proposal"], label="applied_proposal") proposal_id = _canonical_uuid(applied["id"], label="applied_proposal.id") if expected_proposal_id is not None and proposal_id != _canonical_uuid( expected_proposal_id, label="expected_proposal_id" ): raise ExportError("database proposal id does not match the requested proposal") if approved["id"] != proposal_id or approval["proposal_id"] != proposal_id: raise ExportError("approved, approval, and applied proposal ids do not match") if approved["status"] != "approved" or applied["status"] != "applied": raise ExportError("replay material requires exact approved and applied proposal states") if any(approved[field] is not None for field in ("applied_by_handle", "applied_by_agent_id", "applied_at")): raise ExportError("approved proposal snapshot already contains apply metadata") if not applied["applied_by_handle"] or not applied["applied_by_agent_id"] or not applied["applied_at"]: raise ExportError("applied proposal row lacks exact apply metadata") if not approved["reviewed_by_handle"] or not approved["reviewed_by_agent_id"] or not approved["reviewed_at"]: raise ExportError("approved proposal row lacks exact review metadata") changed_fields = {field for field in PROPOSAL_FIELDS if not _same_json(approved[field], applied[field])} if changed_fields != PROPOSAL_TRANSITION_FIELDS: raise ExportError("proposal rows differ outside or omit the exact guarded apply transition") approval_projection = { "proposal_id": approved["id"], "proposal_type": approved["proposal_type"], "payload": approved["payload"], "reviewed_by_handle": approved["reviewed_by_handle"], "reviewed_by_agent_id": approved["reviewed_by_agent_id"], "reviewed_at": approved["reviewed_at"], "review_note": approved["review_note"], } for field, expected in approval_projection.items(): if not _same_json(approval[field], expected): raise ExportError(f"approval snapshot does not match approved proposal field {field}") receipt = material["replay_receipt"] if not isinstance(receipt, dict): raise ExportError("replay_receipt must be an object") try: replay_receipt.validate_replay_receipt(receipt, expected_proposal_id=proposal_id) except ValueError as exc: raise ExportError("worker replay receipt failed its exact row-level contract") from exc receipt_proposal = receipt.get("proposal") if not isinstance(receipt_proposal, dict): raise ExportError("worker replay receipt has no proposal envelope") applied_envelope = {field: applied[field] for field in RECEIPT_PROPOSAL_FIELDS} if not _same_json(receipt_proposal, applied_envelope): raise ExportError("worker replay receipt does not match the exact applied proposal row") created_at = _parse_timestamp(approved["created_at"]) reviewed_at = _parse_timestamp(approved["reviewed_at"]) approved_updated_at = _parse_timestamp(approved["updated_at"]) applied_at = _parse_timestamp(applied["applied_at"]) applied_updated_at = _parse_timestamp(applied["updated_at"]) if not created_at <= reviewed_at <= approved_updated_at < applied_at <= applied_updated_at: raise ExportError("proposal transition timestamps are not in exact lifecycle order") return { "artifact": MATERIAL_ARTIFACT, "contract_version": MATERIAL_CONTRACT_VERSION, "sequence": sequence, "approved_proposal": approved, "approval_snapshot": approval, "applied_proposal": applied, "replay_receipt": receipt, } def fetch_transition_rows(args: argparse.Namespace, password: str, proposal_id: str) -> dict[str, Any]: sql = f"""begin transaction isolation level repeatable read read only; select kb_stage.export_applied_proposal_replay_rows( {apply_engine.sql_literal(proposal_id)}::uuid )::text; rollback; """ raw = apply_engine.run_psql(args, sql, password, redact_output_on_error=True).strip() if not raw or "\n" in raw: raise ExportError("applied transition export returned an invalid row count") return _parse_json_object(raw, label="applied transition export") def _resolved_output(path: Path) -> Path: absolute = _absolute_path(path) if absolute.name in {"", ".", ".."}: raise ExportError("output path must name a file") return absolute.parent.resolve(strict=False) / absolute.name def _paths_alias(left: Path, right: Path) -> bool: if left.resolve(strict=False) == right.resolve(strict=False): return True try: return left.exists() and right.exists() and os.path.samefile(left, right) except OSError: return False def _assert_output_safe(path: Path, protected_paths: tuple[Path, ...]) -> None: for protected in protected_paths: if _paths_alias(path, protected): raise ExportError("output path aliases a protected input or engine file") try: metadata = path.lstat() except FileNotFoundError: return if path.is_symlink() or not stat.S_ISREG(metadata.st_mode): raise ExportError("output path must be a regular non-symlink file") if metadata.st_nlink != 1: raise ExportError("output path has unsafe hard-link aliases") if stat.S_IMODE(metadata.st_mode) != 0o600: raise ExportError("existing output permissions must be exactly 0600") def _sync_directory(path: Path) -> None: directory_fd = os.open(path, os.O_RDONLY) try: os.fsync(directory_fd) finally: os.close(directory_fd) def write_private_material( path: Path, material: dict[str, Any], *, protected_paths: tuple[Path, ...], ) -> tuple[Path, str]: """Publish once, or accept an exact 0600 rerun without overwriting drift.""" output = _resolved_output(path) output.parent.mkdir(parents=True, exist_ok=True, mode=0o700) _assert_output_safe(output, protected_paths) payload = (json.dumps(material, ensure_ascii=False, indent=2, sort_keys=True) + "\n").encode("utf-8") if output.exists(): if output.read_bytes() != payload: raise ExportError("existing output contains different replay material") return output, "unchanged" fd, temporary_name = tempfile.mkstemp(prefix=f".{output.name}.", dir=str(output.parent)) temporary = Path(temporary_name) published = False try: os.fchmod(fd, 0o600) with os.fdopen(fd, "wb") as handle: handle.write(payload) handle.flush() os.fsync(handle.fileno()) try: os.link(temporary, output) published = True except FileExistsError: _assert_output_safe(output, protected_paths) if output.read_bytes() != payload: raise ExportError("output changed concurrently during replay material export") from None _sync_directory(output.parent) finally: try: temporary.unlink() except FileNotFoundError: pass _assert_output_safe(output, protected_paths) if output.read_bytes() != payload: raise ExportError("published replay material failed exact readback") return output, "created" if published else "unchanged" def _positive_sequence(value: str) -> int: try: sequence = int(value) except ValueError as exc: raise argparse.ArgumentTypeError("sequence must be a positive integer") from exc if sequence <= 0: raise argparse.ArgumentTypeError("sequence must be a positive integer") return sequence def parse_args(argv: list[str]) -> argparse.Namespace: parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("proposal_id", help="UUID of the applied proposal") parser.add_argument("--replay-receipt", required=True, help="private 0600 worker replay receipt") parser.add_argument("--sequence", required=True, type=_positive_sequence, help="positive ledger sequence") parser.add_argument("--output", required=True, help="private replay material output path") parser.add_argument("--secrets-file", default=apply_engine.DEFAULT_SECRETS_FILE) parser.add_argument("--container", default=apply_engine.DEFAULT_CONTAINER) parser.add_argument("--db", default=apply_engine.DEFAULT_DB) parser.add_argument("--host", default=apply_engine.DEFAULT_HOST) parser.add_argument("--role", default=apply_engine.DEFAULT_ROLE) return parser.parse_args(argv) def run(args: argparse.Namespace) -> int: proposal_id = _canonical_uuid(args.proposal_id, label="proposal_id") receipt_path = _absolute_path(args.replay_receipt) receipt = _load_private_receipt(receipt_path) try: replay_receipt.validate_replay_receipt(receipt, expected_proposal_id=proposal_id) except ValueError as exc: raise ExportError("worker replay receipt failed its exact row-level contract") from exc password = apply_engine.load_password(args.secrets_file) rows = fetch_transition_rows(args, password, proposal_id) material = validate_material( { "artifact": MATERIAL_ARTIFACT, "contract_version": MATERIAL_CONTRACT_VERSION, "sequence": args.sequence, "approved_proposal": rows.get("approved_proposal"), "approval_snapshot": rows.get("approval_snapshot"), "applied_proposal": rows.get("applied_proposal"), "replay_receipt": receipt, }, expected_proposal_id=proposal_id, expected_sequence=args.sequence, ) protected_paths = tuple( _absolute_path(path) for path in ( receipt_path, args.secrets_file, Path(__file__), Path(apply_engine.__file__), Path(replay_receipt.__file__), ) ) _, disposition = write_private_material( Path(args.output), material, protected_paths=protected_paths, ) print(f"replay material {disposition}: proposal={proposal_id} sequence={args.sequence}") return 0 def main(argv: list[str] | None = None) -> int: try: return run(parse_args(sys.argv[1:] if argv is None else argv)) except ExportError as exc: print(f"replay material export refused: {exc}", file=sys.stderr) return 1 except OSError: print("replay material export refused: private file operation failed", file=sys.stderr) return 1 if __name__ == "__main__": raise SystemExit(main())