#!/usr/bin/env python3 """Direct, value-safe owner review interface for RAILIANCE-WP-0024. The executable surface is intentionally closed: contract files select named read-only checks, never shell commands. Approvals are bound to the canonical contract digest and the SHA-256 digests of the owner's reviewed artifacts. """ from __future__ import annotations import argparse import hashlib import json import os import re import subprocess import sys import urllib.error import urllib.parse import urllib.request from datetime import UTC, datetime from pathlib import Path from typing import Any, Callable ROOT = Path(__file__).resolve().parents[1] DEFAULT_CONTRACT = ROOT / "interfaces" / "RAILIANCE-WP-0024-owner-reviews.json" DEFAULT_API_BASE = os.environ.get("STATE_HUB_URL", "http://127.0.0.1:8000") RECEIPT_PREFIX = "WP0024-OWNER-RECEIPT" KNOWN_CHECKS = { "focused-unit-tests", "database-lease-preflight", "node-reboot-preflight", } class ReviewError(RuntimeError): pass def canonical_json(value: Any) -> str: return json.dumps(value, sort_keys=True, separators=(",", ":"), ensure_ascii=True) def sha256_bytes(value: bytes) -> str: return hashlib.sha256(value).hexdigest() def file_sha256(path: Path) -> str: digest = hashlib.sha256() with path.open("rb") as handle: for block in iter(lambda: handle.read(1024 * 1024), b""): digest.update(block) return digest.hexdigest() def load_contract(path: Path = DEFAULT_CONTRACT) -> dict[str, Any]: try: value = json.loads(path.read_text(encoding="utf-8")) except (OSError, json.JSONDecodeError) as exc: raise ReviewError(f"review contract is unavailable or invalid: {path}") from exc if not isinstance(value, dict): raise ReviewError("review contract must be a JSON object") if value.get("interface") != "railiance.owner-review" or value.get("version") != 1: raise ReviewError("unsupported owner-review interface/version") owners = value.get("owners") hashes = value.get("artifact_sha256") task_owners = value.get("task_owners") if ( not isinstance(owners, dict) or not owners or not isinstance(hashes, dict) or not isinstance(task_owners, dict) or not task_owners ): raise ReviewError( "review contract requires owners, task_owners and artifact_sha256 maps" ) for owner, review in owners.items(): if not isinstance(owner, str) or not isinstance(review, dict): raise ReviewError("invalid owner review entry") artifacts = review.get("artifacts") checks = review.get("checks") assertions = review.get("assertions") if not all(isinstance(item, str) for item in artifacts or []): raise ReviewError(f"{owner}: artifacts must be strings") if not artifacts or any(item not in hashes for item in artifacts): raise ReviewError(f"{owner}: every artifact requires a pinned digest") if not checks or any(item not in KNOWN_CHECKS for item in checks): raise ReviewError(f"{owner}: unknown or missing read-only check") if not assertions or not all(isinstance(item, str) for item in assertions): raise ReviewError(f"{owner}: assertions must be non-empty strings") expected_tasks = sorted( task for task, required in task_owners.items() if owner in required ) if sorted(review.get("tasks") or []) != expected_tasks: raise ReviewError(f"{owner}: tasks disagree with task_owners") for task, required in task_owners.items(): if not isinstance(task, str) or not isinstance(required, list) or not required: raise ReviewError("task_owners entries must be non-empty owner lists") if len(set(required)) != len(required) or any(owner not in owners for owner in required): raise ReviewError(f"{task}: task_owners contains unknown or duplicate owners") return value def contract_digest(contract: dict[str, Any]) -> str: return sha256_bytes(canonical_json(contract).encode("utf-8")) def owner_review(contract: dict[str, Any], owner: str) -> dict[str, Any]: review = contract["owners"].get(owner) if not isinstance(review, dict): choices = ", ".join(sorted(contract["owners"])) raise ReviewError(f"unknown owner {owner!r}; choose one of: {choices}") return review def artifact_state( contract: dict[str, Any], owner: str, *, root: Path = ROOT ) -> dict[str, dict[str, Any]]: review = owner_review(contract, owner) result: dict[str, dict[str, Any]] = {} for relative in review["artifacts"]: expected = contract["artifact_sha256"][relative] target = (root / relative).resolve() try: target.relative_to(root.resolve()) except ValueError as exc: raise ReviewError(f"artifact escapes repository root: {relative}") from exc try: observed = file_sha256(target) except OSError: observed = None result[relative] = { "expected_sha256": expected, "observed_sha256": observed, "matches": observed == expected, } return result def check_command(check_id: str) -> list[str]: if check_id == "focused-unit-tests": return [ sys.executable, "-m", "unittest", "tests/test_audit_core_recovery_preflight.py", "tests/test_audit_core_database_lease_recovery.py", "tests/test_wp0024_owner_review.py", ] if check_id == "database-lease-preflight": return [sys.executable, "scripts/audit-core-recovery-preflight.py", "database-lease"] if check_id == "node-reboot-preflight": return [sys.executable, "scripts/audit-core-recovery-preflight.py", "node-reboot"] raise ReviewError(f"unknown check id: {check_id}") def run_named_check( check_id: str, *, runner: Callable[..., subprocess.CompletedProcess[str]] = subprocess.run, ) -> dict[str, Any]: completed = runner( check_command(check_id), cwd=ROOT, text=True, capture_output=True, check=False, ) result: dict[str, Any] = { "id": check_id, "passed": completed.returncode == 0, "exit_code": completed.returncode, "read_only": True, } if check_id.endswith("-preflight") and completed.returncode == 0: try: payload = json.loads(completed.stdout) except json.JSONDecodeError: result["passed"] = False result["detail"] = "preflight returned invalid JSON" else: safe = ( isinstance(payload, dict) and payload.get("automated_checks_passed") is True and payload.get("secret_values_observed") is False ) result["passed"] = bool(safe) result["automated_checks_passed"] = payload.get("automated_checks_passed") result["ready_for_live_execution"] = payload.get("ready_for_live_execution") result["secret_values_observed"] = payload.get("secret_values_observed") if not safe: result["detail"] = "preflight safety/automated gate did not pass" elif completed.returncode != 0: result["detail"] = "check failed; run the named interface verify command locally for diagnosis" return result def verify( contract: dict[str, Any], owner: str, *, root: Path = ROOT, check_runner: Callable[[str], dict[str, Any]] = run_named_check, ) -> dict[str, Any]: artifacts = artifact_state(contract, owner, root=root) checks: list[dict[str, Any]] = [] if all(item["matches"] for item in artifacts.values()): checks = [check_runner(item) for item in owner_review(contract, owner)["checks"]] return { "interface": contract["interface"], "version": contract["version"], "workplan_id": contract["workplan_id"], "owner": owner, "contract_digest": contract_digest(contract), "artifacts": artifacts, "checks": checks, "passed": bool(checks) and all(item["matches"] for item in artifacts.values()) and all(item["passed"] for item in checks), "secret_values_observed": False, } def validate_reviewer(value: str) -> str: if not re.fullmatch(r"[A-Za-z0-9][A-Za-z0-9._:@/+\-]{1,127}", value): raise ReviewError("reviewer must be a stable 2-128 character identifier") return value def validate_note(value: str) -> str: note = value.strip() if not note or len(note) > 2000: raise ReviewError("request-changes requires a note of 1-2000 characters") forbidden = ("BEGIN PRIVATE KEY", "BEGIN OPENSSH PRIVATE KEY", "AGE-SECRET-KEY-1", "hvs.") if any(marker in note for marker in forbidden): raise ReviewError("note appears to contain credential material; do not submit it") return note def receipt_subject(receipt: dict[str, Any]) -> str: return "/".join( ( RECEIPT_PREFIX, f"v{receipt['version']}", receipt["owner"], receipt["decision"], receipt["contract_digest"], ) ) def build_receipt( contract: dict[str, Any], owner: str, decision: str, reviewer: str, *, verification: dict[str, Any] | None = None, note: str | None = None, now: datetime | None = None, ) -> dict[str, Any]: if decision not in contract["decisions"]: raise ReviewError(f"unsupported decision: {decision}") if decision == "approve" and (not verification or verification.get("passed") is not True): raise ReviewError("approval requires a passing direct verification") if decision == "request-changes": note = validate_note(note or "") review = owner_review(contract, owner) timestamp = (now or datetime.now(UTC)).astimezone(UTC).isoformat().replace("+00:00", "Z") receipt: dict[str, Any] = { "interface": contract["interface"], "version": contract["version"], "workplan_id": contract["workplan_id"], "workplan_uuid": contract["workplan_uuid"], "owner": owner, "reviewer": validate_reviewer(reviewer), "decision": decision, "created_at": timestamp, "contract_digest": contract_digest(contract), "artifact_sha256": { path: contract["artifact_sha256"][path] for path in review["artifacts"] }, "assertion_count": len(review["assertions"]), "secret_values_observed": False, } if verification: receipt["checks"] = [ {"id": item["id"], "passed": item["passed"], "read_only": True} for item in verification["checks"] ] if note is not None: receipt["note"] = note return receipt def http_json( method: str, url: str, payload: dict[str, Any] | None = None, *, opener: Callable[..., Any] = urllib.request.urlopen, ) -> Any: data = canonical_json(payload).encode("utf-8") if payload is not None else None request = urllib.request.Request( url, data=data, method=method, headers={"Content-Type": "application/json"} if data else {}, ) try: with opener(request, timeout=10) as response: return json.loads(response.read().decode("utf-8")) except (OSError, urllib.error.URLError, json.JSONDecodeError) as exc: raise ReviewError(f"State Hub request failed: {method} {url}") from exc def post_receipt(receipt: dict[str, Any], api_base: str) -> dict[str, Any]: payload = { "from_agent": receipt["owner"], "to_agent": "railiance-platform", "subject": receipt_subject(receipt), "body": canonical_json(receipt), } response = http_json("POST", f"{api_base.rstrip('/')}/messages/", payload) if not isinstance(response, dict): raise ReviewError("State Hub returned an invalid receipt response") return response def parse_receipt_message(message: dict[str, Any]) -> dict[str, Any] | None: subject = message.get("subject") body = message.get("body") if not isinstance(subject, str) or not subject.startswith(RECEIPT_PREFIX + "/"): return None if not isinstance(body, str): return None try: receipt = json.loads(body) except json.JSONDecodeError: return None if not isinstance(receipt, dict): return None required = { "interface": str, "version": int, "workplan_id": str, "workplan_uuid": str, "owner": str, "reviewer": str, "decision": str, "created_at": str, "contract_digest": str, "artifact_sha256": dict, "assertion_count": int, "secret_values_observed": bool, } if any(not isinstance(receipt.get(key), kind) for key, kind in required.items()): return None if ( receipt["interface"] != "railiance.owner-review" or receipt["version"] != 1 or receipt["workplan_id"] != "RAILIANCE-WP-0024" or receipt["decision"] not in {"approve", "request-changes"} or receipt["secret_values_observed"] is not False or not re.fullmatch(r"[0-9a-f]{64}", receipt["contract_digest"]) ): return None try: datetime.fromisoformat(receipt["created_at"].replace("Z", "+00:00")) expected_subject = receipt_subject(receipt) except (KeyError, TypeError, ValueError): return None if expected_subject != subject: return None if message.get("from_agent") != receipt.get("owner"): return None return receipt def aggregate_status( contract: dict[str, Any], messages: list[dict[str, Any]] ) -> dict[str, Any]: digest = contract_digest(contract) candidates: dict[str, list[dict[str, Any]]] = { owner: [] for owner in contract["owners"] } stale: dict[str, int] = {owner: 0 for owner in contract["owners"]} for message in messages: receipt = parse_receipt_message(message) if not receipt or receipt.get("owner") not in candidates: continue owner = receipt["owner"] if receipt.get("contract_digest") != digest: stale[owner] += 1 continue expected = { path: contract["artifact_sha256"][path] for path in contract["owners"][owner]["artifacts"] } review = contract["owners"][owner] try: validate_reviewer(receipt["reviewer"]) except ReviewError: stale[owner] += 1 continue if ( receipt.get("interface") != contract["interface"] or receipt.get("version") != contract["version"] or receipt.get("workplan_id") != contract["workplan_id"] or receipt.get("workplan_uuid") != contract["workplan_uuid"] or receipt.get("artifact_sha256") != expected or receipt.get("assertion_count") != len(review["assertions"]) ): stale[owner] += 1 continue if receipt["decision"] == "approve": checks = receipt.get("checks") if ( not isinstance(checks, list) or [item.get("id") for item in checks if isinstance(item, dict)] != review["checks"] or any( not isinstance(item, dict) or item.get("passed") is not True or item.get("read_only") is not True for item in checks ) ): stale[owner] += 1 continue candidates[owner].append(receipt) owners: dict[str, Any] = {} for owner, receipts in candidates.items(): latest = max(receipts, key=lambda item: item.get("created_at", ""), default=None) owners[owner] = { "decision": latest.get("decision") if latest else "missing", "reviewer": latest.get("reviewer") if latest else None, "created_at": latest.get("created_at") if latest else None, "stale_receipt_count": stale[owner], } tasks = { task: { "required_owners": required, "decisions": {owner: owners[owner]["decision"] for owner in required}, "all_approved": all(owners[owner]["decision"] == "approve" for owner in required), } for task, required in contract["task_owners"].items() } try: artifact_current = all( file_sha256(ROOT / path) == expected for path, expected in contract["artifact_sha256"].items() ) except OSError: artifact_current = False return { "interface": contract["interface"], "version": contract["version"], "workplan_id": contract["workplan_id"], "contract_digest": digest, "contract_artifacts_current": artifact_current, "owners": owners, "tasks": tasks, "all_approved": artifact_current and all(item["all_approved"] for item in tasks.values()), } def fetch_status(contract: dict[str, Any], api_base: str) -> dict[str, Any]: query = urllib.parse.urlencode({"to_agent": "railiance-platform", "limit": 500}) value = http_json("GET", f"{api_base.rstrip('/')}/messages/?{query}") if not isinstance(value, list): raise ReviewError("State Hub messages response is not a list") return aggregate_status(contract, value) def parser() -> argparse.ArgumentParser: result = argparse.ArgumentParser(description=__doc__) result.add_argument("--contract", type=Path, default=DEFAULT_CONTRACT) result.add_argument("--api-base", default=DEFAULT_API_BASE) subparsers = result.add_subparsers(dest="command", required=True) for command in ("show", "verify"): child = subparsers.add_parser(command) child.add_argument("--owner", required=True) approve = subparsers.add_parser("approve") approve.add_argument("--owner", required=True) approve.add_argument("--reviewer", required=True) changes = subparsers.add_parser("request-changes") changes.add_argument("--owner", required=True) changes.add_argument("--reviewer", required=True) changes.add_argument("--note", required=True) subparsers.add_parser("status") return result def main(argv: list[str] | None = None) -> int: args = parser().parse_args(argv) try: contract = load_contract(args.contract) if args.command == "show": review = owner_review(contract, args.owner) output = { "interface": contract["interface"], "version": contract["version"], "workplan_id": contract["workplan_id"], "owner": args.owner, "contract_digest": contract_digest(contract), **review, "artifacts_state": artifact_state(contract, args.owner), "decisions": contract["decisions"], } elif args.command == "verify": output = verify(contract, args.owner) elif args.command == "approve": verification = verify(contract, args.owner) receipt = build_receipt( contract, args.owner, "approve", args.reviewer, verification=verification ) posted = post_receipt(receipt, args.api_base) output = { "submitted": True, "message_id": posted.get("id"), "receipt": receipt, } elif args.command == "request-changes": receipt = build_receipt( contract, args.owner, "request-changes", args.reviewer, note=args.note, ) posted = post_receipt(receipt, args.api_base) output = { "submitted": True, "message_id": posted.get("id"), "receipt": receipt, } else: output = fetch_status(contract, args.api_base) except ReviewError as exc: print(canonical_json({"ok": False, "error": str(exc)}), file=sys.stderr) return 2 print(json.dumps(output, indent=2, sort_keys=True)) return 0 if __name__ == "__main__": raise SystemExit(main())