teleo-infrastructure/ops/run_local_genesis_ledger_rebuild.py
2026-07-15 00:33:29 +02:00

1284 lines
48 KiB
Python

#!/usr/bin/env python3
"""Deterministically rebuild a disposable Postgres from genesis plus a strict ledger.
The v1 replay boundary is intentionally narrow. It supports strict, insert-only
``add_edge``, ``attach_evidence``, and ``approve_claim`` receipts. Each ledger
entry must also retain the exact approved and applied proposal rows plus the
immutable approval snapshot. Legacy/freeform entries and ``revise_strategy``
fail before Docker starts because their retained material is not a complete
database delta.
"""
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
SUPPORTED_PROPOSAL_TYPES = frozenset({"add_edge", "attach_evidence", "approve_claim"})
CLAIM_CEILING = (
"exact genesis plus ordered strict insert-only proposal replay; supported types are "
"add_edge, attach_evidence, and approve_claim; full approved/applied proposal rows "
"and immutable approval snapshots are required"
)
NOT_PROVEN = (
"legacy or freeform proposal replay",
"revise_strategy replay because prior-row mutations are absent from v1 receipts",
"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")
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",
)
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 _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 == "revise_strategy":
raise ReconstructionError(
"unsupported_mutating_receipt",
"revise_strategy receipts omit prior strategy and node mutations and cannot replay exactly",
)
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")
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"
)
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
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=_sha256_text(current_apply_sql),
)
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 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,
"guarded_apply_executed": 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:
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",
)
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["seed_exact"] = True
_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
_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=canonical_rebuild.DEFAULT_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 args.image or any(character.isspace() for character in args.image):
parser.error("--image must be a non-empty Docker image reference without whitespace")
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())