teleo-infrastructure/ops/run_local_genesis_ledger_rebuild.py

1759 lines
68 KiB
Python

#!/usr/bin/env python3
"""Deterministically rebuild a disposable Postgres from genesis plus a strict ledger.
The v1 replay boundary supports strict ``add_edge``, ``attach_evidence``,
``approve_claim``, and ``revise_strategy`` receipts. Insert-only actions seed
their receipt-pinned rows before the guarded transition. ``revise_strategy``
instead captures the exact prestate identities, executes the exact hash-matched
guarded SQL, validates the generated delta, and normalizes only that delta to the
receipt-pinned transaction timestamp, IDs, and rows. Every entry must retain the
full approved and applied proposal rows plus its immutable approval snapshot.
Legacy and freeform entries still fail before Docker starts.
"""
from __future__ import annotations
import argparse
import hashlib
import json
import re
import signal
import subprocess
import sys
import time
import uuid
from dataclasses import dataclass
from datetime import UTC, datetime
from pathlib import Path
from typing import Any
try:
from . import run_local_canonical_postgres_rebuild as canonical_rebuild
from .verify_postgres_parity_manifest import load_manifest
except ImportError: # pragma: no cover - direct script execution
import run_local_canonical_postgres_rebuild as canonical_rebuild
from verify_postgres_parity_manifest import load_manifest
REPO_ROOT = Path(__file__).resolve().parents[1]
SCRIPTS_DIR = REPO_ROOT / "scripts"
if str(SCRIPTS_DIR) not in sys.path:
sys.path.insert(0, str(SCRIPTS_DIR))
import apply_proposal as apply_engine # noqa: E402, I001
import kb_apply_replay_receipt as replay_receipt # noqa: E402
LEDGER_ARTIFACT = "teleo_genesis_plus_ledger"
MATERIAL_ARTIFACT = "teleo_strict_proposal_replay_material"
LEDGER_CONTRACT_VERSION = 1
MATERIAL_CONTRACT_VERSION = 1
DEFAULT_REBUILD_IMAGE = (
"docker.io/library/postgres:16-alpine@"
"sha256:57c72fd2a128e416c7fcc499958864df5301e940bca0a56f58fddf30ffc07777"
)
SUPPORTED_PROPOSAL_TYPES = frozenset(
{"add_edge", "attach_evidence", "approve_claim", "revise_strategy"}
)
CLAIM_CEILING = (
"exact genesis plus ordered strict proposal replay; supported types are add_edge, "
"attach_evidence, approve_claim, and revise_strategy; mutating strategy replay "
"captures prestate identities and normalizes the exact receipt-pinned poststate; "
"full approved/applied proposal rows and immutable approval snapshots are required"
)
NOT_PROVEN = (
"legacy or freeform proposal replay",
"source-corpus recompilation from a blank schema",
"VPS, GCP, Telegram, or any live database mutation",
)
APPLY_ENGINE_PATH = SCRIPTS_DIR / "apply_proposal.py"
REPLAY_RECEIPT_ENGINE_PATH = SCRIPTS_DIR / "kb_apply_replay_receipt.py"
GUARD_PREREQUISITES_PATH = SCRIPTS_DIR / "kb_apply_prereqs.sql"
PARITY_SQL_PATH = REPO_ROOT / "ops" / "postgres_parity_manifest.sql"
BASE_REBUILD_PATH = REPO_ROOT / "ops" / "run_local_canonical_postgres_rebuild.py"
GUARD_PREREQUISITES_DESTINATION = "/tmp/kb-apply-prereqs.sql"
FIXED_ENGINE_PATHS = frozenset(
{
APPLY_ENGINE_PATH.resolve(),
REPLAY_RECEIPT_ENGINE_PATH.resolve(),
GUARD_PREREQUISITES_PATH.resolve(),
PARITY_SQL_PATH.resolve(),
BASE_REBUILD_PATH.resolve(),
Path(__file__).resolve(),
}
)
SHA256_RE = re.compile(r"[0-9a-f]{64}\Z")
IMAGE_DIGEST_RE = re.compile(r"\S+@sha256:[0-9a-f]{64}\Z")
PROPOSAL_TRANSITION_FIELDS = frozenset(
{"status", "applied_by_handle", "applied_by_agent_id", "applied_at", "updated_at"}
)
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",
}
)
CANONICAL_TABLE_ORDER = (
"claims",
"sources",
"reasoning_tools",
"claim_evidence",
"claim_edges",
)
STRATEGY_REQUIRED_FIELDS = frozenset(
{
"id",
"agent_id",
"diagnosis",
"guiding_policy",
"proximate_objectives",
"version",
"active",
"created_at",
}
)
STRATEGY_NODE_REQUIRED_FIELDS = frozenset(
{
"id",
"agent_id",
"node_type",
"title",
"body",
"rank",
"status",
"horizon",
"measure",
"source_ref",
"metadata",
"created_at",
"updated_at",
}
)
class ReconstructionError(RuntimeError):
"""A fail-closed error whose message is safe for the public receipt."""
def __init__(self, code: str, safe_message: str) -> None:
super().__init__(safe_message)
self.code = code
self.safe_message = safe_message
@dataclass(frozen=True)
class LedgerEntry:
sequence: int
material_path: Path
material_sha256: str
replay_material_sha256: str
approved_proposal: dict[str, Any]
approval_snapshot: dict[str, Any]
applied_proposal: dict[str, Any]
receipt: dict[str, Any]
current_apply_sql: str
current_apply_sql_sha256: str
@dataclass(frozen=True)
class ReconstructionBundle:
ledger_sha256: str
ledger_bytes: int
genesis_dump_sha256: str
genesis_dump_bytes: int
genesis_manifest: dict[str, Any]
genesis_manifest_sha256: str
genesis_manifest_bytes: int
final_manifest_path: Path
final_manifest: dict[str, Any]
final_manifest_sha256: str
final_manifest_bytes: int
engine_hashes: dict[str, str]
entries: tuple[LedgerEntry, ...]
protected_paths: frozenset[Path]
def _canonical_json(value: Any) -> str:
return json.dumps(value, ensure_ascii=False, separators=(",", ":"), sort_keys=True)
def _sha256_text(value: str) -> str:
return hashlib.sha256(value.encode("utf-8")).hexdigest()
def _validate_sha256(value: Any, field: str) -> str:
if not isinstance(value, str) or not SHA256_RE.fullmatch(value):
raise ReconstructionError("invalid_hash_pin", f"{field} must be a lowercase SHA-256 digest")
return value
def _require_exact_keys(value: Any, expected: frozenset[str], field: str) -> dict[str, Any]:
if not isinstance(value, dict):
raise ReconstructionError("invalid_contract", f"{field} must be an object")
observed = frozenset(value)
if observed != expected:
raise ReconstructionError(
"invalid_contract",
f"{field} fields do not match v1 contract; "
f"missing_count={len(expected - observed)}, extra_count={len(observed - expected)}",
)
return value
def _load_json_object(path: Path, field: str) -> dict[str, Any]:
try:
value = json.loads(path.read_text(encoding="utf-8"))
except (OSError, UnicodeError, json.JSONDecodeError) as exc:
raise ReconstructionError("invalid_json_artifact", f"{field} is not valid UTF-8 JSON") from exc
if not isinstance(value, dict):
raise ReconstructionError("invalid_json_artifact", f"{field} must contain one JSON object")
return value
def _resolve_artifact(ledger_path: Path, declared: Any, field: str) -> Path:
if not isinstance(declared, str) or not declared.strip():
raise ReconstructionError("invalid_contract", f"{field} must be a non-empty path string")
candidate = Path(declared).expanduser()
if not candidate.is_absolute():
candidate = ledger_path.parent / candidate
candidate = candidate.resolve()
if not candidate.is_file():
raise ReconstructionError("missing_artifact", f"{field} is not an existing regular file")
return candidate
def _assert_file_hash(path: Path, expected: Any, field: str) -> str:
pin = _validate_sha256(expected, field)
actual = canonical_rebuild.sha256_file(path)
if actual != pin:
raise ReconstructionError("hash_pin_mismatch", f"{field} does not match the supplied artifact")
return actual
def _engine_hashes() -> dict[str, str]:
paths = {
"reconstruction_command_sha256": Path(__file__),
"base_rebuild_engine_sha256": BASE_REBUILD_PATH,
"apply_engine_sha256": APPLY_ENGINE_PATH,
"replay_receipt_engine_sha256": REPLAY_RECEIPT_ENGINE_PATH,
"guard_prerequisites_sha256": GUARD_PREREQUISITES_PATH,
"parity_sql_sha256": PARITY_SQL_PATH,
}
return {name: canonical_rebuild.sha256_file(path) for name, path in paths.items()}
def _validate_proposal_rows(
approved: dict[str, Any],
applied: dict[str, Any],
approval: dict[str, Any],
receipt_proposal: dict[str, Any],
) -> None:
_require_exact_keys(approved, PROPOSAL_FIELDS, "material.approved_proposal")
_require_exact_keys(applied, PROPOSAL_FIELDS, "material.applied_proposal")
_require_exact_keys(approval, APPROVAL_FIELDS, "material.approval_snapshot")
try:
proposal_id = str(uuid.UUID(str(applied["id"])))
except (TypeError, ValueError, AttributeError) as exc:
raise ReconstructionError("invalid_material", "material proposal id must be a UUID") from exc
if approved["id"] != proposal_id or approval["proposal_id"] != proposal_id:
raise ReconstructionError("material_mismatch", "proposal and approval ids do not match")
if approved["status"] != "approved" or applied["status"] != "applied":
raise ReconstructionError(
"unsupported_proposal_state",
"material requires one exact approved row and one exact applied row",
)
if any(approved[field] is not None for field in ("applied_by_handle", "applied_by_agent_id", "applied_at")):
raise ReconstructionError(
"unsupported_proposal_state", "approved proposal material already contains apply metadata"
)
if not applied["applied_by_handle"] or not applied["applied_by_agent_id"] or not applied["applied_at"]:
raise ReconstructionError("invalid_material", "applied proposal material lacks exact apply metadata")
if not approved["reviewed_by_agent_id"] or not approval["reviewed_by_db_role"]:
raise ReconstructionError(
"invalid_material", "strict replay requires a reviewer agent id and reviewer database role"
)
immutable_fields = PROPOSAL_FIELDS - PROPOSAL_TRANSITION_FIELDS
changed_immutable = sorted(field for field in immutable_fields if approved[field] != applied[field])
if changed_immutable:
raise ReconstructionError(
"material_mismatch",
f"proposal changed outside the guarded apply transition: {changed_immutable}",
)
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 approval[field] != expected:
raise ReconstructionError(
"material_mismatch", f"approval snapshot does not match approved proposal field {field}"
)
envelope_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",
)
if any(receipt_proposal.get(field) != applied[field] for field in envelope_fields):
raise ReconstructionError(
"material_mismatch", "replay receipt proposal envelope does not match applied proposal row"
)
def _parse_aware_timestamp(
value: Any, field: str, *, code: str = "invalid_mutating_receipt"
) -> datetime:
if not isinstance(value, str) or not value:
raise ReconstructionError(code, f"{field} must be an ISO-8601 timestamp")
try:
parsed = datetime.fromisoformat(value.replace("Z", "+00:00"))
except ValueError as exc:
raise ReconstructionError(code, f"{field} must be an ISO-8601 timestamp") from exc
if parsed.tzinfo is None:
raise ReconstructionError(code, f"{field} must include a timezone")
return parsed
def _validated_uuid_list(
value: Any, field: str, *, code: str = "invalid_mutating_state"
) -> list[str]:
if not isinstance(value, list):
raise ReconstructionError(code, f"{field} must be a UUID list")
normalized: list[str] = []
for item in value:
try:
normalized.append(str(uuid.UUID(str(item))))
except (TypeError, ValueError, AttributeError) as exc:
raise ReconstructionError(code, f"{field} must contain only UUIDs") from exc
if len(normalized) != len(set(normalized)):
raise ReconstructionError(code, f"{field} contains duplicate UUIDs")
return normalized
def _validate_revise_strategy_receipt_rows(
proposal: dict[str, Any], canonical_rows: dict[str, Any]
) -> tuple[dict[str, Any], list[dict[str, Any]]]:
payload = proposal.get("payload")
apply_payload = payload.get("apply_payload") if isinstance(payload, dict) else None
if not isinstance(apply_payload, dict):
raise ReconstructionError(
"invalid_mutating_receipt", "revise_strategy receipt requires a strict apply payload"
)
try:
agent_id = str(uuid.UUID(str(apply_payload.get("agent_id"))))
except (TypeError, ValueError, AttributeError) as exc:
raise ReconstructionError(
"invalid_mutating_receipt", "revise_strategy receipt requires one agent UUID"
) from exc
strategies = canonical_rows.get("strategies")
nodes = canonical_rows.get("strategy_nodes")
if not isinstance(strategies, list) or len(strategies) != 1 or not isinstance(strategies[0], dict):
raise ReconstructionError(
"invalid_mutating_receipt", "revise_strategy receipt must pin one fresh strategy row"
)
expected_nodes = apply_payload.get("strategy_nodes")
if (
not isinstance(expected_nodes, list)
or not isinstance(nodes, list)
or len(nodes) != len(expected_nodes)
or any(not isinstance(row, dict) for row in nodes)
):
raise ReconstructionError(
"invalid_mutating_receipt", "revise_strategy receipt must pin every fresh strategy node row"
)
strategy = strategies[0]
if frozenset(strategy) != STRATEGY_REQUIRED_FIELDS:
raise ReconstructionError(
"invalid_mutating_receipt", "revise_strategy receipt strategy row fields are not canonical"
)
if any(frozenset(row) != STRATEGY_NODE_REQUIRED_FIELDS for row in nodes):
raise ReconstructionError(
"invalid_mutating_receipt", "revise_strategy receipt strategy node row fields are not canonical"
)
strategy_id = _validated_uuid_list(
[strategy.get("id")], "receipt strategy id", code="invalid_mutating_receipt"
)[0]
node_ids = _validated_uuid_list(
[row.get("id") for row in nodes],
"receipt strategy node ids",
code="invalid_mutating_receipt",
)
if strategy.get("agent_id") != agent_id or any(row.get("agent_id") != agent_id for row in nodes):
raise ReconstructionError(
"invalid_mutating_receipt", "revise_strategy receipt rows do not belong to the payload agent"
)
if strategy.get("active") is not True or any(row.get("status") != "active" for row in nodes):
raise ReconstructionError(
"invalid_mutating_receipt", "revise_strategy receipt must pin the fresh active poststate"
)
version = strategy.get("version")
if isinstance(version, bool) or not isinstance(version, int) or version < 1:
raise ReconstructionError(
"invalid_mutating_receipt", "revise_strategy receipt strategy version must be positive"
)
transaction_timestamp = strategy.get("created_at")
transaction_time = _parse_aware_timestamp(transaction_timestamp, "receipt strategy created_at")
for row in nodes:
if row.get("created_at") != transaction_timestamp or row.get("updated_at") != transaction_timestamp:
raise ReconstructionError(
"invalid_mutating_receipt",
"revise_strategy fresh strategy and node timestamps must share one transaction timestamp",
)
applied_time = _parse_aware_timestamp(proposal.get("applied_at"), "receipt proposal applied_at")
if transaction_time > applied_time:
raise ReconstructionError(
"invalid_mutating_receipt", "revise_strategy transaction timestamp is after proposal apply time"
)
return {**strategy, "id": strategy_id}, [
{**row, "id": normalized_id} for row, normalized_id in zip(nodes, node_ids, strict=True)
]
def _validate_entry_material(
material: dict[str, Any],
*,
expected_sequence: int,
expected_replay_hash: str,
material_path: Path,
material_sha256: str,
) -> LedgerEntry:
expected_keys = frozenset(
{
"artifact",
"contract_version",
"sequence",
"approved_proposal",
"approval_snapshot",
"applied_proposal",
"replay_receipt",
}
)
_require_exact_keys(material, expected_keys, "ledger entry material")
if material["artifact"] != MATERIAL_ARTIFACT or material["contract_version"] != MATERIAL_CONTRACT_VERSION:
raise ReconstructionError("unsupported_material_contract", "ledger entry material is not v1")
if material["sequence"] != expected_sequence:
raise ReconstructionError("ledger_order_mismatch", "material sequence does not match ledger order")
receipt = material["replay_receipt"]
if not isinstance(receipt, dict):
raise ReconstructionError("invalid_material", "material.replay_receipt must be an object")
proposal = receipt.get("proposal")
if not isinstance(proposal, dict):
raise ReconstructionError("invalid_material", "replay receipt has no proposal object")
proposal_type = proposal.get("proposal_type")
if proposal_type not in SUPPORTED_PROPOSAL_TYPES:
raise ReconstructionError(
"unsupported_proposal_type", "ledger entry proposal type is not supported by v1 reconstruction"
)
if receipt.get("artifact") != "kb_apply_replay_receipt" or receipt.get("replay_ready") is not True:
raise ReconstructionError("invalid_replay_receipt", "ledger entry is not a replay-ready apply receipt")
if receipt.get("receipt_contract_version") != replay_receipt.RECEIPT_CONTRACT_VERSION:
raise ReconstructionError("unsupported_receipt_contract", "replay receipt contract version is unsupported")
apply_metadata = receipt.get("apply_engine")
canonical_rows = receipt.get("canonical_rows")
if not isinstance(apply_metadata, dict) or not isinstance(canonical_rows, dict):
raise ReconstructionError("invalid_replay_receipt", "replay receipt is missing apply or row material")
if proposal_type == "revise_strategy":
_validate_revise_strategy_receipt_rows(proposal, canonical_rows)
try:
rebuilt = replay_receipt.build_replay_receipt(
proposal,
canonical_rows,
apply_sql_sha256=apply_metadata.get("apply_sql_sha256"),
apply_sql_source=apply_metadata.get("source"),
exported_at_utc=receipt.get("exported_at_utc"),
)
except (KeyError, TypeError, ValueError) as exc:
raise ReconstructionError(
"invalid_replay_receipt", "replay receipt failed strict payload and exact-row validation"
) from exc
if receipt.get("hashes") != rebuilt["hashes"]:
raise ReconstructionError("replay_receipt_hash_mismatch", "replay receipt hashes are not recoverable")
if receipt.get("expected_row_counts") != rebuilt["expected_row_counts"]:
raise ReconstructionError("replay_receipt_count_mismatch", "replay receipt expected counts are invalid")
if receipt.get("actual_row_counts") != rebuilt["actual_row_counts"]:
raise ReconstructionError("replay_receipt_count_mismatch", "replay receipt actual counts are invalid")
actual_replay_hash = rebuilt["hashes"]["replay_material_sha256"]
if actual_replay_hash != expected_replay_hash:
raise ReconstructionError(
"replay_material_hash_mismatch", "ledger replay-material pin does not match the private receipt"
)
receipt = {**receipt, "canonical_rows": rebuilt["canonical_rows"]}
approved = material["approved_proposal"]
approval = material["approval_snapshot"]
applied = material["applied_proposal"]
_validate_proposal_rows(approved, applied, approval, proposal)
try:
current_apply_sql = apply_engine.build_apply_sql(proposal, applied["applied_by_handle"])
except (KeyError, TypeError, ValueError) as exc:
raise ReconstructionError(
"current_apply_engine_rejected_material",
"current guarded apply engine rejected the strict replay material",
) from exc
current_apply_sql_sha256 = _sha256_text(current_apply_sql)
if proposal_type == "revise_strategy" and (
apply_metadata.get("source") != "exact_executed_sql"
or apply_metadata.get("apply_sql_sha256") != current_apply_sql_sha256
):
raise ReconstructionError(
"mutating_apply_engine_mismatch",
"revise_strategy replay requires the exact executed SQL to match the current guarded apply engine",
)
return LedgerEntry(
sequence=expected_sequence,
material_path=material_path,
material_sha256=material_sha256,
replay_material_sha256=actual_replay_hash,
approved_proposal=approved,
approval_snapshot=approval,
applied_proposal=applied,
receipt=receipt,
current_apply_sql=current_apply_sql,
current_apply_sql_sha256=current_apply_sql_sha256,
)
def load_bundle(args: argparse.Namespace) -> ReconstructionBundle:
canonical_rebuild.validate_custom_dump(args.genesis_dump)
ledger_sha256 = _assert_file_hash(args.ledger, args.ledger_sha256, "--ledger-sha256")
ledger = _load_json_object(args.ledger, "ledger")
top_level_keys = frozenset({"artifact", "contract_version", "engine", "genesis", "entries", "final_parity"})
_require_exact_keys(ledger, top_level_keys, "ledger")
if ledger["artifact"] != LEDGER_ARTIFACT or ledger["contract_version"] != LEDGER_CONTRACT_VERSION:
raise ReconstructionError("unsupported_ledger_contract", "ledger is not the v1 genesis-plus-ledger contract")
actual_engine_hashes = _engine_hashes()
expected_engine = _require_exact_keys(ledger["engine"], frozenset(actual_engine_hashes), "ledger.engine")
for field, actual_hash in actual_engine_hashes.items():
if _validate_sha256(expected_engine[field], f"ledger.engine.{field}") != actual_hash:
raise ReconstructionError("engine_hash_mismatch", f"ledger engine pin does not match current {field}")
genesis = _require_exact_keys(
ledger["genesis"], frozenset({"dump_sha256", "parity_manifest_sha256"}), "ledger.genesis"
)
genesis_dump_sha256 = _assert_file_hash(args.genesis_dump, genesis["dump_sha256"], "ledger.genesis.dump_sha256")
genesis_manifest_sha256 = _assert_file_hash(
args.genesis_manifest,
genesis["parity_manifest_sha256"],
"ledger.genesis.parity_manifest_sha256",
)
try:
genesis_manifest = load_manifest(args.genesis_manifest)
except (OSError, ValueError, KeyError, TypeError, json.JSONDecodeError) as exc:
raise ReconstructionError("invalid_genesis_manifest", "genesis parity manifest is invalid") from exc
final_spec = _require_exact_keys(ledger["final_parity"], frozenset({"manifest", "sha256"}), "ledger.final_parity")
final_manifest_path = _resolve_artifact(args.ledger, final_spec["manifest"], "final parity manifest")
final_manifest_sha256 = _assert_file_hash(final_manifest_path, final_spec["sha256"], "ledger.final_parity.sha256")
try:
final_manifest = load_manifest(final_manifest_path)
except (OSError, ValueError, KeyError, TypeError, json.JSONDecodeError) as exc:
raise ReconstructionError("invalid_final_manifest", "final parity manifest is invalid") from exc
raw_entries = ledger["entries"]
if not isinstance(raw_entries, list):
raise ReconstructionError("invalid_contract", "ledger.entries must be an ordered list")
entries: list[LedgerEntry] = []
protected_paths = {
args.genesis_dump.resolve(),
args.genesis_manifest.resolve(),
args.ledger.resolve(),
final_manifest_path,
*FIXED_ENGINE_PATHS,
}
seen_proposal_ids: set[str] = set()
for expected_sequence, raw_entry in enumerate(raw_entries, 1):
entry_spec = _require_exact_keys(
raw_entry,
frozenset({"sequence", "material", "sha256", "replay_material_sha256"}),
f"ledger.entries[{expected_sequence - 1}]",
)
if entry_spec["sequence"] != expected_sequence:
raise ReconstructionError(
"ledger_order_mismatch", "ledger entry sequences must be contiguous and start at 1"
)
material_path = _resolve_artifact(
args.ledger, entry_spec["material"], f"ledger entry {expected_sequence} material"
)
material_sha256 = _assert_file_hash(
material_path, entry_spec["sha256"], f"ledger entry {expected_sequence} sha256"
)
expected_replay_hash = _validate_sha256(
entry_spec["replay_material_sha256"],
f"ledger entry {expected_sequence} replay_material_sha256",
)
entry = _validate_entry_material(
_load_json_object(material_path, f"ledger entry {expected_sequence} material"),
expected_sequence=expected_sequence,
expected_replay_hash=expected_replay_hash,
material_path=material_path,
material_sha256=material_sha256,
)
proposal_id = entry.applied_proposal["id"]
if proposal_id in seen_proposal_ids:
raise ReconstructionError("duplicate_proposal", "ledger contains the same proposal more than once")
seen_proposal_ids.add(proposal_id)
entries.append(entry)
protected_paths.add(material_path)
if args.output.resolve() in protected_paths:
raise ReconstructionError("unsafe_output_path", "--output must not overwrite any reconstruction input")
return ReconstructionBundle(
ledger_sha256=ledger_sha256,
ledger_bytes=args.ledger.stat().st_size,
genesis_dump_sha256=genesis_dump_sha256,
genesis_dump_bytes=args.genesis_dump.stat().st_size,
genesis_manifest=genesis_manifest,
genesis_manifest_sha256=genesis_manifest_sha256,
genesis_manifest_bytes=args.genesis_manifest.stat().st_size,
final_manifest_path=final_manifest_path,
final_manifest=final_manifest,
final_manifest_sha256=final_manifest_sha256,
final_manifest_bytes=final_manifest_path.stat().st_size,
engine_hashes=actual_engine_hashes,
entries=tuple(entries),
protected_paths=frozenset(protected_paths),
)
def _jsonb_literal(value: Any) -> str:
return apply_engine.sql_literal(_canonical_json(value)) + "::jsonb"
def build_seed_sql(entry: LedgerEntry) -> str:
statements = [
"begin;",
"set local standard_conforming_strings = on;",
"insert into kb_stage.kb_proposals",
"select seeded.* from jsonb_populate_record(",
f" null::kb_stage.kb_proposals, {_jsonb_literal(entry.approved_proposal)}",
") seeded",
"on conflict (id) do nothing;",
"insert into kb_stage.kb_proposal_approvals",
"select seeded.* from jsonb_populate_record(",
f" null::kb_stage.kb_proposal_approvals, {_jsonb_literal(entry.approval_snapshot)}",
") seeded",
"on conflict (proposal_id) do nothing;",
]
rows = entry.receipt["canonical_rows"]
for table in CANONICAL_TABLE_ORDER:
table_rows = rows.get(table)
if not table_rows:
continue
statements.extend(
[
f"insert into public.{table}",
"select seeded.* from jsonb_populate_recordset(",
f" null::public.{table}, {_jsonb_literal(table_rows)}",
") seeded",
"on conflict do nothing;",
]
)
statements.append("commit;")
return "\n".join(statements) + "\n"
def _uuid_membership_sql(column: str, values: list[str], *, negate: bool = False) -> str:
if not values:
return "true" if negate else "false"
rendered = ", ".join(f"{apply_engine.sql_literal(value)}::uuid" for value in values)
operator = "not in" if negate else "in"
return f"{column} {operator} ({rendered})"
def _uuid_array_sql(values: list[str]) -> str:
if not values:
return "array[]::uuid[]"
rendered = ", ".join(f"{apply_engine.sql_literal(value)}::uuid" for value in values)
return f"array[{rendered}]::uuid[]"
def build_revise_strategy_prestate_sql(entry: LedgerEntry) -> str:
apply_payload = entry.receipt["proposal"]["payload"]["apply_payload"]
agent = apply_engine.sql_literal(apply_payload["agent_id"])
return f"""with strategy_state as (
select coalesce(jsonb_agg(s.id::text order by s.id::text), '[]'::jsonb) as strategy_ids,
coalesce(
jsonb_agg(s.id::text order by s.id::text) filter (where s.active),
'[]'::jsonb
) as active_strategy_ids,
coalesce(max(s.version), 0) as max_strategy_version
from public.strategies s
where s.agent_id = {agent}::uuid
), node_state as (
select coalesce(jsonb_agg(n.id::text order by n.id::text), '[]'::jsonb) as strategy_node_ids,
coalesce(
jsonb_agg(n.id::text order by n.id::text) filter (where n.status <> 'retired'),
'[]'::jsonb
) as non_retired_strategy_node_ids
from public.strategy_nodes n
where n.agent_id = {agent}::uuid
)
select jsonb_build_object(
'strategy_ids', s.strategy_ids,
'active_strategy_ids', s.active_strategy_ids,
'max_strategy_version', s.max_strategy_version,
'strategy_node_ids', n.strategy_node_ids,
'non_retired_strategy_node_ids', n.non_retired_strategy_node_ids
)::text
from strategy_state s cross join node_state n;
"""
def _validate_revise_strategy_prestate(value: Any) -> dict[str, Any]:
expected = frozenset(
{
"strategy_ids",
"active_strategy_ids",
"max_strategy_version",
"strategy_node_ids",
"non_retired_strategy_node_ids",
}
)
state = _require_exact_keys(value, expected, "revise_strategy prestate")
strategy_ids = _validated_uuid_list(state["strategy_ids"], "prestate strategy ids")
active_strategy_ids = _validated_uuid_list(
state["active_strategy_ids"], "prestate active strategy ids"
)
strategy_node_ids = _validated_uuid_list(state["strategy_node_ids"], "prestate strategy node ids")
non_retired_node_ids = _validated_uuid_list(
state["non_retired_strategy_node_ids"], "prestate non-retired strategy node ids"
)
max_version = state["max_strategy_version"]
if isinstance(max_version, bool) or not isinstance(max_version, int) or max_version < 0:
raise ReconstructionError(
"invalid_mutating_state", "prestate maximum strategy version must be a non-negative integer"
)
if len(active_strategy_ids) > 1 or not set(active_strategy_ids).issubset(strategy_ids):
raise ReconstructionError(
"invalid_mutating_state", "prestate active strategies violate the one-active invariant"
)
if not set(non_retired_node_ids).issubset(strategy_node_ids):
raise ReconstructionError(
"invalid_mutating_state", "prestate non-retired strategy nodes are not a subset of all nodes"
)
return {
"strategy_ids": strategy_ids,
"active_strategy_ids": active_strategy_ids,
"max_strategy_version": max_version,
"strategy_node_ids": strategy_node_ids,
"non_retired_strategy_node_ids": non_retired_node_ids,
}
def build_revise_strategy_generated_delta_sql(entry: LedgerEntry, prestate: dict[str, Any]) -> str:
state = _validate_revise_strategy_prestate(prestate)
apply_payload = entry.receipt["proposal"]["payload"]["apply_payload"]
agent = apply_engine.sql_literal(apply_payload["agent_id"])
strategy_filter = _uuid_membership_sql("s.id", state["strategy_ids"], negate=True)
node_filter = _uuid_membership_sql("n.id", state["strategy_node_ids"], negate=True)
return f"""select jsonb_build_object(
'strategies', coalesce((
select jsonb_agg(to_jsonb(s) order by s.id::text)
from public.strategies s
where s.agent_id = {agent}::uuid and {strategy_filter}
), '[]'::jsonb),
'strategy_nodes', coalesce((
select jsonb_agg(to_jsonb(n) order by n.id::text)
from public.strategy_nodes n
where n.agent_id = {agent}::uuid and {node_filter}
), '[]'::jsonb)
)::text;
"""
def _validate_revise_strategy_generated_delta(
entry: LedgerEntry,
prestate: dict[str, Any],
generated_rows: Any,
) -> tuple[dict[str, Any], list[dict[str, Any]]]:
state = _validate_revise_strategy_prestate(prestate)
if not isinstance(generated_rows, dict):
raise ReconstructionError(
"invalid_mutating_delta", "revise_strategy generated delta must be a row object"
)
try:
replay_receipt.build_replay_receipt(
entry.receipt["proposal"],
generated_rows,
apply_sql_sha256=entry.current_apply_sql_sha256,
apply_sql_source="exact_executed_sql",
exported_at_utc=entry.receipt.get("exported_at_utc"),
)
except (KeyError, TypeError, ValueError) as exc:
raise ReconstructionError(
"invalid_mutating_delta",
"revise_strategy generated rows do not match the strict payload",
) from exc
strategies = generated_rows.get("strategies")
nodes = generated_rows.get("strategy_nodes")
if not isinstance(strategies, list) or len(strategies) != 1 or not isinstance(strategies[0], dict):
raise ReconstructionError(
"invalid_mutating_delta", "revise_strategy apply did not generate exactly one strategy"
)
if not isinstance(nodes, list) or any(not isinstance(row, dict) for row in nodes):
raise ReconstructionError(
"invalid_mutating_delta", "revise_strategy apply generated invalid strategy nodes"
)
strategy = strategies[0]
if strategy.get("version") != state["max_strategy_version"] + 1:
raise ReconstructionError(
"invalid_mutating_delta", "revise_strategy generated an unexpected strategy version"
)
transaction_timestamp = strategy.get("created_at")
_parse_aware_timestamp(
transaction_timestamp, "generated strategy created_at", code="invalid_mutating_delta"
)
if any(
row.get("created_at") != transaction_timestamp or row.get("updated_at") != transaction_timestamp
for row in nodes
):
raise ReconstructionError(
"invalid_mutating_delta",
"revise_strategy generated rows do not share one transaction timestamp",
)
_validated_uuid_list(
[strategy.get("id")], "generated strategy id", code="invalid_mutating_delta"
)
_validated_uuid_list(
[row.get("id") for row in nodes],
"generated strategy node ids",
code="invalid_mutating_delta",
)
return strategy, nodes
def build_revise_strategy_normalization_sql(
entry: LedgerEntry,
prestate: dict[str, Any],
generated_rows: dict[str, Any],
) -> str:
state = _validate_revise_strategy_prestate(prestate)
generated_strategy, generated_nodes = _validate_revise_strategy_generated_delta(
entry, state, generated_rows
)
receipt_strategy, receipt_nodes = _validate_revise_strategy_receipt_rows(
entry.receipt["proposal"], entry.receipt["canonical_rows"]
)
if receipt_strategy["version"] != state["max_strategy_version"] + 1:
raise ReconstructionError(
"mutating_receipt_version_mismatch",
"revise_strategy receipt version does not follow the observed prestate",
)
if receipt_strategy["id"] in state["strategy_ids"] or set(
row["id"] for row in receipt_nodes
).intersection(state["strategy_node_ids"]):
raise ReconstructionError(
"mutating_receipt_id_collision", "revise_strategy receipt IDs collide with the observed prestate"
)
apply_payload = entry.receipt["proposal"]["payload"]["apply_payload"]
agent = apply_engine.sql_literal(apply_payload["agent_id"])
generated_strategy_id = str(uuid.UUID(str(generated_strategy["id"])))
generated_node_ids = [str(uuid.UUID(str(row["id"]))) for row in generated_nodes]
previous_active = state["active_strategy_ids"]
changed_node_ids = state["non_retired_strategy_node_ids"]
transaction_timestamp = apply_engine.sql_literal(receipt_strategy["created_at"])
generated_node_array = _uuid_array_sql(generated_node_ids)
previous_active_array = _uuid_array_sql(previous_active)
changed_node_array = _uuid_array_sql(changed_node_ids)
prior_strategy_assertion = ""
if previous_active:
prior_strategy_assertion = f"""
if (select count(*) from public.strategies
where agent_id = {agent}::uuid and id = any(previous_active_ids) and not active) <> {len(previous_active)} then
raise exception 'revise_strategy prior active strategy transition mismatch';
end if;"""
prior_node_normalization = ""
if changed_node_ids:
prior_node_normalization = f"""
if (select count(*) from public.strategy_nodes
where agent_id = {agent}::uuid and id = any(changed_node_ids) and status = 'retired') <> {len(changed_node_ids)} then
raise exception 'revise_strategy prior node transition mismatch';
end if;
update public.strategy_nodes
set status = 'retired', updated_at = {transaction_timestamp}::timestamptz
where agent_id = {agent}::uuid and id = any(changed_node_ids);
get diagnostics changed_rows = row_count;
if changed_rows <> {len(changed_node_ids)} then
raise exception 'revise_strategy prior node normalization mismatch';
end if;"""
return f"""begin;
set local standard_conforming_strings = on;
do $reconstruction$
declare
changed_rows integer;
generated_strategy_id constant uuid := {apply_engine.sql_literal(generated_strategy_id)}::uuid;
generated_node_ids constant uuid[] := {generated_node_array};
previous_active_ids constant uuid[] := {previous_active_array};
changed_node_ids constant uuid[] := {changed_node_array};
begin{prior_strategy_assertion}
if exists (
select 1 from public.strategy_node_anchors
where from_node_id = any(generated_node_ids) or to_node_id = any(generated_node_ids)
) then
raise exception 'revise_strategy generated nodes unexpectedly have anchors';
end if;
delete from public.strategy_nodes
where agent_id = {agent}::uuid and id = any(generated_node_ids);
get diagnostics changed_rows = row_count;
if changed_rows <> {len(generated_node_ids)} then
raise exception 'revise_strategy generated node delta mismatch';
end if;
delete from public.strategies
where agent_id = {agent}::uuid and id = generated_strategy_id;
get diagnostics changed_rows = row_count;
if changed_rows <> 1 then
raise exception 'revise_strategy generated strategy delta mismatch';
end if;{prior_node_normalization}
insert into public.strategies
select seeded.* from jsonb_populate_record(
null::public.strategies, {_jsonb_literal(receipt_strategy)}
) seeded;
insert into public.strategy_nodes
select seeded.* from jsonb_populate_recordset(
null::public.strategy_nodes, {_jsonb_literal(receipt_nodes)}
) seeded;
if (select count(*) from public.strategies
where agent_id = {agent}::uuid and active) <> 1 then
raise exception 'revise_strategy normalized one-active invariant mismatch';
end if;
end
$reconstruction$;
commit;
"""
def build_proposal_readback_sql(proposal_id: str) -> str:
literal = apply_engine.sql_literal(proposal_id)
return f"""select jsonb_build_object(
'proposal', (select to_jsonb(p) from kb_stage.kb_proposals p
where p.id = {literal}::uuid),
'approval_snapshot', (select to_jsonb(a) from kb_stage.kb_proposal_approvals a
where a.proposal_id = {literal}::uuid)
)::text;
"""
def build_applied_timestamp_normalization_sql(entry: LedgerEntry) -> str:
applied = entry.applied_proposal
return f"""do $reconstruction$
declare
changed_rows integer;
begin
update kb_stage.kb_proposals
set applied_at = {apply_engine.sql_literal(applied["applied_at"])}::timestamptz,
updated_at = {apply_engine.sql_literal(applied["updated_at"])}::timestamptz
where id = {apply_engine.sql_literal(applied["id"])}::uuid
and status = 'applied'
and applied_by_handle = {apply_engine.sql_literal(applied["applied_by_handle"])}
and applied_by_agent_id is not distinct from
{apply_engine.sql_literal(applied["applied_by_agent_id"])}::uuid;
get diagnostics changed_rows = row_count;
if changed_rows <> 1 then
raise exception 'deterministic proposal timestamp normalization failed';
end if;
end
$reconstruction$;
"""
def _run_stdin(
command: list[str],
*,
stdin: str,
timeout: float,
) -> subprocess.CompletedProcess[str]:
return subprocess.run(
command,
input=stdin,
text=True,
capture_output=True,
check=False,
timeout=timeout,
)
def _diagnostic_fingerprint(completed: subprocess.CompletedProcess[str]) -> str:
output = (completed.stderr or "") + "\n" + (completed.stdout or "")
return _sha256_text(output)
def _require_command_success(
completed: subprocess.CompletedProcess[str],
*,
code: str,
action: str,
) -> str:
if completed.returncode != 0:
fingerprint = _diagnostic_fingerprint(completed)
raise ReconstructionError(
code,
f"{action} failed; private command output withheld; diagnostic_sha256={fingerprint}",
)
return completed.stdout
def _psql(
args: argparse.Namespace,
container: str,
sql: str,
*,
role: str,
timeout: float,
code: str,
action: str,
) -> str:
command = [
args.docker_bin,
"exec",
"-i",
container,
"psql",
"-X",
"-U",
role,
"-h",
"127.0.0.1",
"-d",
args.database,
"-Atq",
"-v",
"ON_ERROR_STOP=1",
]
try:
completed = _run_stdin(command, stdin=sql, timeout=timeout)
except (OSError, subprocess.TimeoutExpired) as exc:
raise ReconstructionError(code, f"{action} did not complete; private details withheld") from exc
return _require_command_success(completed, code=code, action=action)
def _read_json(
args: argparse.Namespace,
container: str,
sql: str,
*,
code: str,
action: str,
) -> Any:
raw = _psql(
args,
container,
sql,
role="postgres",
timeout=args.command_timeout,
code=code,
action=action,
).strip()
try:
return json.loads(raw)
except json.JSONDecodeError as exc:
raise ReconstructionError(code, f"{action} returned an invalid private JSON readback") from exc
def _assert_exact_json(actual: Any, expected: Any, *, code: str, action: str) -> None:
if _canonical_json(actual) != _canonical_json(expected):
raise ReconstructionError(code, f"{action} did not match the hash-pinned material")
def _entry_summary(entry: LedgerEntry) -> dict[str, Any]:
proposal = entry.applied_proposal
return {
"sequence": entry.sequence,
"proposal_id": proposal["id"],
"proposal_type": proposal["proposal_type"],
"material_sha256": entry.material_sha256,
"replay_material_sha256": entry.replay_material_sha256,
"original_apply_sql_sha256": entry.receipt["apply_engine"]["apply_sql_sha256"],
"current_apply_sql_sha256": entry.current_apply_sql_sha256,
"expected_row_counts": entry.receipt["expected_row_counts"],
"seed_exact": False,
"proposal_seed_exact": False,
"canonical_seed_exact": False,
"guarded_apply_executed": False,
"mutating_prestate_captured": False,
"mutating_delta_validated": False,
"mutating_poststate_normalized": False,
"proposal_exact": False,
"canonical_rows_exact": False,
"status": "pending",
}
def apply_entry(
args: argparse.Namespace,
container: str,
entry: LedgerEntry,
summary: dict[str, Any],
) -> None:
mutating_prestate: dict[str, Any] | None = None
try:
_psql(
args,
container,
build_seed_sql(entry),
role="postgres",
timeout=args.apply_timeout,
code="entry_seed_failed",
action=f"ledger entry {entry.sequence} exact seed",
)
proposal_readback = _read_json(
args,
container,
build_proposal_readback_sql(entry.approved_proposal["id"]),
code="entry_seed_readback_failed",
action=f"ledger entry {entry.sequence} proposal seed readback",
)
_assert_exact_json(
proposal_readback,
{
"proposal": entry.approved_proposal,
"approval_snapshot": entry.approval_snapshot,
},
code="entry_seed_mismatch",
action=f"ledger entry {entry.sequence} proposal seed",
)
summary["proposal_seed_exact"] = True
if entry.applied_proposal["proposal_type"] == "revise_strategy":
mutating_prestate = _validate_revise_strategy_prestate(
_read_json(
args,
container,
build_revise_strategy_prestate_sql(entry),
code="mutating_prestate_readback_failed",
action=f"ledger entry {entry.sequence} revise_strategy prestate readback",
)
)
summary["mutating_prestate_captured"] = True
else:
seeded_rows = _read_json(
args,
container,
replay_receipt.build_postflight_sql(entry.receipt["proposal"]),
code="entry_seed_readback_failed",
action=f"ledger entry {entry.sequence} canonical seed readback",
)
_assert_exact_json(
seeded_rows,
entry.receipt["canonical_rows"],
code="entry_seed_mismatch",
action=f"ledger entry {entry.sequence} canonical seed",
)
summary["canonical_seed_exact"] = True
summary["seed_exact"] = summary["proposal_seed_exact"] and summary["canonical_seed_exact"]
_psql(
args,
container,
entry.current_apply_sql,
role="kb_apply",
timeout=args.apply_timeout,
code="guarded_apply_failed",
action=f"ledger entry {entry.sequence} guarded apply",
)
summary["guarded_apply_executed"] = True
if mutating_prestate is not None:
generated_rows = _read_json(
args,
container,
build_revise_strategy_generated_delta_sql(entry, mutating_prestate),
code="mutating_delta_readback_failed",
action=f"ledger entry {entry.sequence} revise_strategy generated delta readback",
)
normalization_sql = build_revise_strategy_normalization_sql(
entry, mutating_prestate, generated_rows
)
summary["mutating_delta_validated"] = True
_psql(
args,
container,
normalization_sql,
role="postgres",
timeout=args.apply_timeout,
code="mutating_poststate_normalization_failed",
action=f"ledger entry {entry.sequence} revise_strategy exact poststate normalization",
)
summary["mutating_poststate_normalized"] = True
_psql(
args,
container,
build_applied_timestamp_normalization_sql(entry),
role="postgres",
timeout=args.command_timeout,
code="proposal_timestamp_normalization_failed",
action=f"ledger entry {entry.sequence} deterministic ledger timestamp normalization",
)
proposal_readback = _read_json(
args,
container,
build_proposal_readback_sql(entry.applied_proposal["id"]),
code="entry_postflight_failed",
action=f"ledger entry {entry.sequence} applied proposal readback",
)
_assert_exact_json(
proposal_readback,
{
"proposal": entry.applied_proposal,
"approval_snapshot": entry.approval_snapshot,
},
code="entry_postflight_mismatch",
action=f"ledger entry {entry.sequence} applied proposal",
)
summary["proposal_exact"] = True
final_rows = _read_json(
args,
container,
replay_receipt.build_postflight_sql(entry.receipt["proposal"]),
code="entry_postflight_failed",
action=f"ledger entry {entry.sequence} exact row readback",
)
_assert_exact_json(
final_rows,
entry.receipt["canonical_rows"],
code="entry_postflight_mismatch",
action=f"ledger entry {entry.sequence} canonical rows",
)
summary["canonical_rows_exact"] = True
summary["status"] = "pass"
except ReconstructionError:
summary["status"] = "fail"
raise
def _sanitize_parity(result: dict[str, Any]) -> dict[str, Any]:
source = dict(result["source_manifest"])
source.pop("path", None)
return {
"status": result["status"],
"verifier": result["verifier"],
"source_manifest": source,
"target_manifest": result["target_manifest"],
"problems": result["problems"],
"details": result["details"],
}
def _run_parity(
args: argparse.Namespace,
container: str,
manifest_path: Path,
manifest: dict[str, Any],
*,
code: str,
action: str,
) -> dict[str, Any]:
try:
result = canonical_rebuild.run_parity_check(
args.docker_bin,
container,
args.database,
source_manifest_path=manifest_path,
source_manifest=manifest,
manifest_sql=PARITY_SQL_PATH,
command_timeout=args.manifest_timeout,
max_target_query_ms=args.max_target_query_ms,
max_target_source_ratio=args.max_target_source_ratio,
)
except (OSError, ValueError, RuntimeError, subprocess.TimeoutExpired, json.JSONDecodeError) as exc:
raise ReconstructionError(code, f"{action} could not complete; private details withheld") from exc
sanitized = _sanitize_parity(result)
if sanitized["status"] != "pass":
raise ReconstructionError(code, f"{action} found a manifest mismatch")
return sanitized
def _initial_receipt(args: argparse.Namespace, container: str) -> dict[str, Any]:
return {
"artifact": "local_genesis_plus_ledger_reconstruction",
"contract_version": 1,
"generated_at_utc": datetime.now(UTC).isoformat(),
"status": "running",
"claim_ceiling": CLAIM_CEILING,
"not_proven": list(NOT_PROVEN),
"database": args.database,
"inputs": {
"genesis_dump": {"bytes": None, "sha256": None, "pin_verified": False},
"genesis_manifest": {"bytes": None, "sha256": None, "pin_verified": False},
"ledger": {"bytes": None, "sha256": None, "pin_verified": False},
"final_manifest": {"bytes": None, "sha256": None, "pin_verified": False},
"engine_hashes": {},
},
"container": {
"name": container,
"image": args.image,
"network_mode_required": "none",
"postgres_data_tmpfs_required": (f"rw,nosuid,nodev,noexec,size={args.tmpfs_mb}m"),
"preexisting_absence_proven": False,
"started": False,
"isolation": None,
},
"guard_prerequisites": {"status": "pending", "sha256": None},
"genesis_parity": {"status": "pending"},
"genesis_key_counts": {},
"ledger": {"entry_count": None, "entries": []},
"final_parity": {"status": "pending"},
"final_key_counts": {},
"cleanup": {
"attempted": False,
"container": container,
"container_absent": None,
"status": "pending",
},
"timings_seconds": {},
"safety": {
"production_database_touched": False,
"network_access_available_to_container": False,
"persistent_postgres_storage_used": False,
"private_replay_material_emitted": False,
"vps_gcp_telegram_or_live_db_used": False,
},
"error": None,
}
def run_reconstruction(args: argparse.Namespace) -> dict[str, Any]:
started_at = time.monotonic()
container = canonical_rebuild.new_container_name(args.container_prefix)
receipt = _initial_receipt(args, container)
phase = "input_preflight"
primary_error: dict[str, Any] | None = None
try:
bundle = load_bundle(args)
receipt["inputs"] = {
"genesis_dump": {
"bytes": bundle.genesis_dump_bytes,
"sha256": bundle.genesis_dump_sha256,
"pin_verified": True,
},
"genesis_manifest": {
"bytes": bundle.genesis_manifest_bytes,
"sha256": bundle.genesis_manifest_sha256,
"pin_verified": True,
},
"ledger": {
"bytes": bundle.ledger_bytes,
"sha256": bundle.ledger_sha256,
"pin_verified": True,
},
"final_manifest": {
"bytes": bundle.final_manifest_bytes,
"sha256": bundle.final_manifest_sha256,
"pin_verified": True,
},
"engine_hashes": bundle.engine_hashes,
}
receipt["ledger"]["entry_count"] = len(bundle.entries)
receipt["ledger"]["entries"] = [_entry_summary(entry) for entry in bundle.entries]
phase = "container_name_preflight"
absent, absence_error = canonical_rebuild.inspect_container_absence(
args.docker_bin, container, timeout=args.command_timeout
)
if absent is not True:
raise ReconstructionError(
"container_name_not_proven_unused",
"generated disposable container name could not be proven unused",
) from RuntimeError(absence_error or "container exists")
receipt["container"]["preexisting_absence_proven"] = True
phase = "container_start"
tmpfs_options = f"{canonical_rebuild.DATA_DIR}:rw,nosuid,nodev,noexec,size={args.tmpfs_mb}m"
completed = canonical_rebuild._run(
[
args.docker_bin,
"run",
"--detach",
"--rm",
"--network",
"none",
"--name",
container,
"--label",
f"{canonical_rebuild.CANARY_LABEL}={container}",
"--tmpfs",
tmpfs_options,
"--env",
f"POSTGRES_DB={args.database}",
"--env",
"POSTGRES_HOST_AUTH_METHOD=trust",
args.image,
],
timeout=args.command_timeout,
)
container_id = _require_command_success(
completed,
code="container_start_failed",
action="networkless disposable PostgreSQL start",
).strip()
if not container_id:
raise ReconstructionError("container_start_failed", "container start returned no id")
receipt["container"]["started"] = True
receipt["container"]["id_prefix"] = container_id[:12]
receipt["container"]["isolation"] = canonical_rebuild.inspect_container_isolation(
args.docker_bin,
container,
expected_image=args.image,
expected_label=container,
timeout=args.command_timeout,
)
phase = "database_readiness"
receipt["container"]["named_database_psql_attempts"] = canonical_rebuild.wait_for_named_database(
args.docker_bin,
container,
args.database,
startup_timeout=args.startup_timeout,
command_timeout=args.command_timeout,
poll_interval=args.poll_interval,
)
receipt["container"]["named_database_psql_ready"] = True
phase = "application_role_bootstrap"
receipt["application_roles_bootstrapped"] = canonical_rebuild.execute_application_role_bootstrap(
args.docker_bin,
container,
args.database,
bundle.genesis_manifest,
timeout=args.command_timeout,
)
phase = "genesis_restore"
completed = canonical_rebuild._run(
[
args.docker_bin,
"cp",
str(args.genesis_dump.resolve()),
f"{container}:{canonical_rebuild.DUMP_DESTINATION}",
],
timeout=args.command_timeout,
)
_require_command_success(completed, code="genesis_copy_failed", action="genesis dump copy")
completed = canonical_rebuild._run(
[
args.docker_bin,
"exec",
container,
"pg_restore",
"-U",
"postgres",
"-d",
args.database,
"--no-owner",
"--no-privileges",
"--exit-on-error",
canonical_rebuild.DUMP_DESTINATION,
],
timeout=args.restore_timeout,
)
_require_command_success(completed, code="genesis_restore_failed", action="genesis restore")
phase = "guard_prerequisites"
completed = canonical_rebuild._run(
[
args.docker_bin,
"cp",
str(GUARD_PREREQUISITES_PATH),
f"{container}:{GUARD_PREREQUISITES_DESTINATION}",
],
timeout=args.command_timeout,
)
_require_command_success(
completed,
code="guard_prerequisites_copy_failed",
action="guarded apply prerequisites copy",
)
completed = canonical_rebuild._run(
[
args.docker_bin,
"exec",
container,
"psql",
"-X",
"-U",
"postgres",
"-d",
args.database,
"-Atq",
"-v",
"ON_ERROR_STOP=1",
"-f",
GUARD_PREREQUISITES_DESTINATION,
],
timeout=args.apply_timeout,
)
_require_command_success(
completed,
code="guard_prerequisites_failed",
action="guarded apply prerequisites",
)
receipt["guard_prerequisites"] = {
"status": "pass",
"sha256": bundle.engine_hashes["guard_prerequisites_sha256"],
}
phase = "genesis_parity"
receipt["genesis_parity"] = _run_parity(
args,
container,
args.genesis_manifest,
bundle.genesis_manifest,
code="genesis_parity_failed",
action="genesis parity verification",
)
receipt["genesis_key_counts"], _ = canonical_rebuild.collect_key_counts(
args.docker_bin,
container,
args.database,
timeout=args.command_timeout,
)
phase = "ordered_ledger_replay"
for entry, summary in zip(bundle.entries, receipt["ledger"]["entries"], strict=True):
apply_entry(args, container, entry, summary)
phase = "final_parity"
receipt["final_parity"] = _run_parity(
args,
container,
bundle.final_manifest_path,
bundle.final_manifest,
code="final_parity_failed",
action="final parity verification",
)
receipt["final_key_counts"], _ = canonical_rebuild.collect_key_counts(
args.docker_bin,
container,
args.database,
timeout=args.command_timeout,
)
receipt["status"] = "pass"
except ReconstructionError as exc:
primary_error = {
"phase": phase,
"code": exc.code,
"type": type(exc).__name__,
"message": exc.safe_message,
}
receipt["status"] = "fail"
receipt["error"] = primary_error
except (
OSError,
ValueError,
RuntimeError,
subprocess.TimeoutExpired,
json.JSONDecodeError,
KeyboardInterrupt,
canonical_rebuild.TerminationRequested,
) as exc:
primary_error = {
"phase": phase,
"code": f"{phase}_failed",
"type": type(exc).__name__,
"message": f"{phase} failed; private details withheld",
}
receipt["status"] = "fail"
receipt["error"] = primary_error
finally:
cleanup_started = time.monotonic()
cleanup = canonical_rebuild.cleanup_container(
args.docker_bin,
container,
command_timeout=args.command_timeout,
poll_interval=args.poll_interval,
)
receipt["cleanup"] = cleanup
receipt["timings_seconds"]["cleanup"] = round(time.monotonic() - cleanup_started, 6)
receipt["timings_seconds"]["total"] = round(time.monotonic() - started_at, 6)
if cleanup.get("container_absent") is not True:
receipt["status"] = "fail"
cleanup_error = {
"phase": "cleanup",
"code": "cleanup_absence_unproven",
"type": "CleanupProofError",
"message": "disposable container absence was not independently proven",
}
if primary_error is None:
receipt["error"] = cleanup_error
else:
receipt["cleanup_error"] = cleanup_error
if cleanup.get("interrupted"):
receipt["status"] = "fail"
interruption_error = {
"phase": "cleanup",
"code": "cleanup_interrupted",
"type": "TerminationRequested",
"message": "cleanup was interrupted; private details withheld",
}
if primary_error is None:
receipt["error"] = interruption_error
else:
receipt["cleanup_interruption"] = interruption_error
return receipt
def _declared_artifact_paths(ledger_path: Path) -> set[Path]:
ledger = _load_json_object(ledger_path, "ledger")
paths = {ledger_path.resolve()}
final = ledger.get("final_parity")
if isinstance(final, dict) and isinstance(final.get("manifest"), str):
paths.add(_resolve_artifact(ledger_path, final["manifest"], "final parity manifest"))
entries = ledger.get("entries")
if isinstance(entries, list):
for index, entry in enumerate(entries):
if isinstance(entry, dict) and isinstance(entry.get("material"), str):
paths.add(_resolve_artifact(ledger_path, entry["material"], f"entry {index + 1} material"))
return paths
def parse_args(argv: list[str] | None = None) -> argparse.Namespace:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--genesis-dump", required=True, type=Path)
parser.add_argument("--genesis-manifest", required=True, type=Path)
parser.add_argument("--ledger", required=True, type=Path)
parser.add_argument("--ledger-sha256", required=True)
parser.add_argument("--database", default="teleo")
parser.add_argument("--image", default=DEFAULT_REBUILD_IMAGE)
parser.add_argument("--container-prefix", default="teleo-genesis-ledger-rebuild")
parser.add_argument("--tmpfs-mb", default=1024, type=int)
parser.add_argument("--startup-timeout", default=60.0, type=float)
parser.add_argument("--restore-timeout", default=300.0, type=float)
parser.add_argument("--apply-timeout", default=180.0, type=float)
parser.add_argument("--manifest-timeout", default=180.0, type=float)
parser.add_argument("--command-timeout", default=30.0, type=float)
parser.add_argument("--poll-interval", default=0.25, type=float)
parser.add_argument("--max-target-query-ms", default=2000.0, type=float)
parser.add_argument("--max-target-source-ratio", default=20.0, type=float)
parser.add_argument("--docker-bin", default="docker")
parser.add_argument("--output", required=True, type=Path)
args = parser.parse_args(argv)
if not canonical_rebuild.DATABASE_NAME_RE.fullmatch(args.database):
parser.error("--database must be a safe PostgreSQL identifier up to 63 characters")
if not canonical_rebuild.CONTAINER_PREFIX_RE.fullmatch(args.container_prefix):
parser.error("--container-prefix must be 1-40 Docker-safe characters")
if not IMAGE_DIGEST_RE.fullmatch(args.image):
parser.error("--image must be pinned by a lowercase sha256 digest")
if not args.docker_bin or any(character.isspace() for character in args.docker_bin):
parser.error("--docker-bin must be one executable name or path without whitespace")
if not 64 <= args.tmpfs_mb <= 65536:
parser.error("--tmpfs-mb must be between 64 and 65536")
for value, flag in (
(args.startup_timeout, "--startup-timeout"),
(args.restore_timeout, "--restore-timeout"),
(args.apply_timeout, "--apply-timeout"),
(args.manifest_timeout, "--manifest-timeout"),
(args.command_timeout, "--command-timeout"),
(args.poll_interval, "--poll-interval"),
(args.max_target_query_ms, "--max-target-query-ms"),
(args.max_target_source_ratio, "--max-target-source-ratio"),
):
if value <= 0:
parser.error(f"{flag} must be positive")
for path, flag in (
(args.genesis_dump, "--genesis-dump"),
(args.genesis_manifest, "--genesis-manifest"),
(args.ledger, "--ledger"),
):
if not path.is_file():
parser.error(f"{flag} must be an existing regular file")
if not SHA256_RE.fullmatch(args.ledger_sha256):
parser.error("--ledger-sha256 must be a lowercase SHA-256 digest")
try:
protected = {
args.genesis_dump.resolve(),
args.genesis_manifest.resolve(),
*_declared_artifact_paths(args.ledger),
*FIXED_ENGINE_PATHS,
}
except ReconstructionError as exc:
parser.error(exc.safe_message)
if args.output.resolve() in protected:
parser.error("--output must not overwrite a reconstruction input")
return args
def main(argv: list[str] | None = None) -> int:
args = parse_args(argv)
previous_handlers: dict[signal.Signals, Any] = {}
for watched_signal in (signal.SIGINT, signal.SIGTERM):
previous_handlers[watched_signal] = signal.getsignal(watched_signal)
signal.signal(watched_signal, canonical_rebuild._termination_handler)
try:
payload = run_reconstruction(args)
finally:
for watched_signal, previous in previous_handlers.items():
signal.signal(watched_signal, previous)
try:
canonical_rebuild.write_receipt(args.output, payload)
except OSError:
payload["status"] = "fail"
payload["receipt_write_error"] = "receipt write failed; private details withheld"
print(json.dumps(payload, indent=2, sort_keys=True))
return 0 if payload["status"] == "pass" else 1
if __name__ == "__main__":
raise SystemExit(main())