#!/usr/bin/env python3 """Prepare and verify a G8 NAS-preview runtime rollback control-plane probe. This harness has two phases: ``prepare`` Build a deterministic, secret-free release-gate request, human rollback approval, expected executor command, and executor manifest. The manifest uses ``vignette.nas-preview-rollback-manifest.v1`` (executor schema d31b0d). ``execute`` Send the prepared requests to a separately deployed control-plane API and verify the durable ``executed`` lifecycle receipt through the read model. The main preview API must never authorize its own rollback. ``execute`` therefore requires both control-plane and preview base URLs and rejects equal origins. A distinct control-plane API, with its own process lifecycle and an enabled rollback executor, is a hard prerequisite. Tokens, cookies, login identity, and raw HTTP response bodies are never printed. """ from __future__ import annotations import argparse import hashlib import json import os import re import sys import urllib.error import urllib.request from dataclasses import dataclass from http.cookiejar import CookieJar from pathlib import Path from typing import Any, Iterable from urllib.parse import urlsplit from uuid import UUID, uuid5 PLAN_SCHEMA = "vignette.g8-rollback-control-plane-probe-plan.v1" RESULT_SCHEMA = "vignette.g8-rollback-control-plane-probe-result.v1" EXECUTOR_REQUEST_SCHEMA = "oas.rollback-executor.v1" EXECUTOR_MANIFEST_SCHEMA = "vignette.nas-preview-rollback-manifest.v1" DATA_CLASSIFICATION = "synthetic_replay_red_team_coverage_drift" INTERNAL_TOKEN_HEADER = "X-Vignette-Continuous-Improvement-Token" TARGET_PROJECT = "vignette-preview-20260807" TARGET_ROOT = "/volume1/docker/vignette-preview-20260807" TARGET_SERVICES = ["api", "web"] TARGET_HEALTH_URL = "http://127.0.0.1:8088/api/health" RELEASE_GATE_PATH = "/internal/continuous-improvement/release-gates" APPROVAL_PATH = "/continuous-improvement/approvals" READ_MODEL_PATH = "/internal/continuous-improvement" DEV_LOGIN_PATH = "/auth/dev-login" UUID_NAMESPACE = UUID("b9ecf1e8-8d40-5e3d-9ab2-c0d4bb842496") SUFFIX_RE = re.compile(r"^[a-z0-9][a-z0-9-]{0,63}$") SHA256_RE = re.compile(r"^[a-f0-9]{64}$") IMAGE_RE = re.compile(r"^sha256:[a-f0-9]{64}$") ARTIFACT_ID_RE = re.compile(r"^[A-Za-z0-9][A-Za-z0-9._:-]{0,179}$") RELEASE_ID_RE = re.compile(r"^oas-g8-release-[a-z0-9-]+$") PROVENANCE_RE = re.compile(r"^(repo|db|audit)://[a-zA-Z0-9_./:-]+$") SAFE_REF_PREFIXES = ("https://", "audit://", "db://", "repo://") SAFE_EVIDENCE_RE = re.compile(r"^(?:https|audit|db|repo)://[A-Za-z0-9._:/-]+$") MAX_RESPONSE_BYTES = 1_048_576 PLAN_FIELDS = frozenset( { "schema_version", "probe_id", "suffix", "plan_sha256", "release_gate", "approval", "expected_executor_request", "executor_manifest", "safety", } ) RESULT_FIELDS = frozenset( { "schema_version", "probe_id", "plan_sha256", "release_gate_id", "approval_event_id", "lifecycle_event_id", "lifecycle_status", "artifact_record_id", "artifact_id", "artifact_sha256", "executor_receipt_id", "executor_evidence_refs", "release_gate_idempotent_replay", "approval_idempotent_replay", "binding_verified", "control_plane_separation_verified", "contains_secrets", "contains_pii", } ) class ProbeError(RuntimeError): """Fail-closed error with a non-sensitive stable code.""" class _NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request( self, req: urllib.request.Request, fp: Any, code: int, msg: str, headers: Any, newurl: str, ) -> None: return None @dataclass(frozen=True) class ApiResponse: status: int body: Any def _canonical_bytes(value: Any) -> bytes: return json.dumps( value, ensure_ascii=False, separators=(",", ":"), sort_keys=True, ).encode("utf-8") def _sha256_json(value: Any) -> str: return hashlib.sha256(_canonical_bytes(value)).hexdigest() def _manifest_bytes(value: Any) -> bytes: return _canonical_bytes(value) + b"\n" def _stable_uuid(suffix: str, label: str) -> str: return str(uuid5(UUID_NAMESPACE, f"{suffix}:{label}")) def _require_exact_dict(value: Any, fields: Iterable[str], code: str) -> dict[str, Any]: if not isinstance(value, dict) or set(value) != set(fields): raise ProbeError(code) return value def _require_string(value: Any, code: str, *, maximum: int = 500) -> str: if not isinstance(value, str) or not value or len(value) > maximum: raise ProbeError(code) return value def _require_uuid(value: Any, code: str) -> str: text = _require_string(value, code, maximum=36) try: parsed = UUID(text) except ValueError as exc: raise ProbeError(code) from exc if str(parsed) != text: raise ProbeError(code) return text def _require_sha256(value: Any, code: str) -> str: if not isinstance(value, str) or SHA256_RE.fullmatch(value) is None: raise ProbeError(code) return value def _require_image(value: Any, code: str) -> str: if not isinstance(value, str) or IMAGE_RE.fullmatch(value) is None: raise ProbeError(code) return value def _require_artifact_id(value: Any) -> str: text = _require_string(value, "invalid_artifact_id", maximum=180) if ARTIFACT_ID_RE.fullmatch(text) is None: raise ProbeError("invalid_artifact_id") return text def _require_provenance(value: Any) -> str: text = _require_string(value, "invalid_artifact_provenance_uri") if PROVENANCE_RE.fullmatch(text) is None: raise ProbeError("invalid_artifact_provenance_uri") return text def _require_evidence_refs(value: Any) -> list[str]: if not isinstance(value, list) or not 1 <= len(value) <= 100: raise ProbeError("invalid_evidence_refs") refs: list[str] = [] for item in value: ref = _require_string(item, "invalid_evidence_ref") if ( not ref.startswith(SAFE_REF_PREFIXES) or SAFE_EVIDENCE_RE.fullmatch(ref) is None ): raise ProbeError("invalid_evidence_ref") refs.append(ref) if len(refs) != len(set(refs)): raise ProbeError("duplicate_evidence_refs") return refs def _artifact( *, suffix: str, kind: str, artifact_id: str | None = None, artifact_sha256: str | None = None, provenance_uri: str | None = None, ) -> dict[str, str]: return { "artifact_record_id": _stable_uuid(suffix, f"artifact:{kind}"), "artifact_id": artifact_id or f"g8-nas-probe-{kind}-{suffix}", "content_sha256": artifact_sha256 or hashlib.sha256(f"{suffix}:{kind}".encode("ascii")).hexdigest(), "provenance_uri": provenance_uri or f"audit://vignette-nas-preview/g8-rollback-probe/{suffix}/{kind}", } def build_plan( *, suffix: str, artifact_id: str, artifact_sha256: str, artifact_provenance_uri: str, evidence_refs: list[str], compose_sha256: str, env_sha256: str, resolved_config_sha256: str, api_image: str, web_image: str, release_id: str | None = None, ) -> dict[str, Any]: if SUFFIX_RE.fullmatch(suffix) is None: raise ProbeError("invalid_suffix") artifact_id = _require_artifact_id(artifact_id) artifact_sha256 = _require_sha256(artifact_sha256, "invalid_artifact_sha256") artifact_provenance_uri = _require_provenance(artifact_provenance_uri) evidence_refs = _require_evidence_refs(evidence_refs) compose_sha256 = _require_sha256(compose_sha256, "invalid_compose_sha256") env_sha256 = _require_sha256(env_sha256, "invalid_env_sha256") resolved_config_sha256 = _require_sha256( resolved_config_sha256, "invalid_resolved_config_sha256" ) api_image = _require_image(api_image, "invalid_api_image") web_image = _require_image(web_image, "invalid_web_image") release_id = release_id or f"oas-g8-release-nas-rollback-{suffix}" if ( not isinstance(release_id, str) or len(release_id) > 180 or RELEASE_ID_RE.fullmatch(release_id) is None ): raise ProbeError("invalid_release_id") release_submission_id = _stable_uuid(suffix, "release-submission") gate_id = _stable_uuid(suffix, "release-gate") approval_submission_id = _stable_uuid(suffix, "approval-submission") approval_event_id = _stable_uuid(suffix, "approval-event") effect_record_id = _stable_uuid(suffix, "effect-record") rollback_artifact = _artifact( suffix=suffix, kind="rollback", artifact_id=artifact_id, artifact_sha256=artifact_sha256, provenance_uri=artifact_provenance_uri, ) artifacts = { "baseline": _artifact(suffix=suffix, kind="baseline"), "threshold": _artifact(suffix=suffix, kind="threshold"), "provenance": [_artifact(suffix=suffix, kind="provenance")], "rollback": rollback_artifact, } release_payload = { "submission_id": release_submission_id, "gate_id": gate_id, "data_classification": DATA_CLASSIFICATION, "manifest": { "release_id": release_id, "red_green_passed": True, "contract_passed": True, "e2e_passed": True, "runtime_proof_passed": True, "public_proof_passed": True, "ssot_synced": True, "evidence_refs": evidence_refs, }, "artifacts": artifacts, } approval_payload = { "submission_id": approval_submission_id, "approval_event_id": approval_event_id, "effect_record_id": effect_record_id, "target_kind": "release_gate", "target_id": gate_id, "decision": "authorize_rollback", "reason_code": "nas_preview_runtime_rollback_probe", "evidence_refs": evidence_refs, } expected_executor_request = { "schema_version": EXECUTOR_REQUEST_SCHEMA, "idempotency_key": effect_record_id, "approval_event_id": approval_event_id, "rollback_scope": "runtime", "target_kind": "release_gate", "target_id": gate_id, "subject_id": release_id, "rollback_target_id": artifact_id, "artifact_record_id": rollback_artifact["artifact_record_id"], "artifact_id": artifact_id, "artifact_sha256": artifact_sha256, "artifact_provenance_uri": artifact_provenance_uri, "authorization_evidence_refs": evidence_refs, } runtime_root = f"{TARGET_ROOT}/.rollback-executor/runtime/{artifact_id}" manifest_artifact = { "idempotency_key": effect_record_id, "approval_event_id": approval_event_id, "authorization_evidence_refs": evidence_refs, "artifact_id": artifact_id, "artifact_sha256": artifact_sha256, "artifact_record_id": rollback_artifact["artifact_record_id"], "artifact_provenance_uri": artifact_provenance_uri, "target_id": gate_id, "subject_id": release_id, "rollback_target_id": artifact_id, "compose_file": f"{runtime_root}/docker-compose.yml", "compose_sha256": compose_sha256, "env_file": f"{runtime_root}/.env", "env_sha256": env_sha256, "resolved_config_sha256": resolved_config_sha256, "api_image": api_image, "web_image": web_image, } manifest_payload = { "schema_version": EXECUTOR_MANIFEST_SCHEMA, "target": { "compose_project": TARGET_PROJECT, "remote_root": TARGET_ROOT, "services": TARGET_SERVICES, "health_url": TARGET_HEALTH_URL, }, "artifacts": [manifest_artifact], } core: dict[str, Any] = { "schema_version": PLAN_SCHEMA, "probe_id": f"nas-g8-probe-{hashlib.sha256(suffix.encode('ascii')).hexdigest()[:24]}", "suffix": suffix, "release_gate": {"path": RELEASE_GATE_PATH, "payload": release_payload}, "approval": {"path": APPROVAL_PATH, "payload": approval_payload}, "expected_executor_request": expected_executor_request, "executor_manifest": { "file_sha256": hashlib.sha256( _manifest_bytes(manifest_payload) ).hexdigest(), "payload": manifest_payload, }, "safety": { "control_plane_required": True, "main_preview_self_rollback_forbidden": True, "contains_secrets": False, "contains_pii": False, }, } plan = {**core, "plan_sha256": _sha256_json(core)} return validate_plan(plan) def _validate_gate_artifact(value: Any, code: str) -> dict[str, Any]: artifact = _require_exact_dict( value, {"artifact_record_id", "artifact_id", "content_sha256", "provenance_uri"}, code, ) _require_uuid(artifact["artifact_record_id"], code) _require_artifact_id(artifact["artifact_id"]) _require_sha256(artifact["content_sha256"], code) _require_provenance(artifact["provenance_uri"]) return artifact def validate_plan(value: Any) -> dict[str, Any]: plan = _require_exact_dict(value, PLAN_FIELDS, "invalid_plan_shape") if plan["schema_version"] != PLAN_SCHEMA: raise ProbeError("invalid_plan_schema") suffix = _require_string(plan["suffix"], "invalid_suffix", maximum=64) if SUFFIX_RE.fullmatch(suffix) is None: raise ProbeError("invalid_suffix") expected_probe_id = ( f"nas-g8-probe-{hashlib.sha256(suffix.encode('ascii')).hexdigest()[:24]}" ) if plan["probe_id"] != expected_probe_id: raise ProbeError("probe_id_mismatch") supplied_digest = _require_sha256(plan["plan_sha256"], "invalid_plan_sha256") core = {key: item for key, item in plan.items() if key != "plan_sha256"} if supplied_digest != _sha256_json(core): raise ProbeError("plan_sha256_mismatch") release = _require_exact_dict( plan["release_gate"], {"path", "payload"}, "invalid_release_gate_shape" ) approval = _require_exact_dict( plan["approval"], {"path", "payload"}, "invalid_approval_shape" ) if release["path"] != RELEASE_GATE_PATH or approval["path"] != APPROVAL_PATH: raise ProbeError("invalid_api_path") release_payload = _require_exact_dict( release["payload"], {"submission_id", "gate_id", "data_classification", "manifest", "artifacts"}, "invalid_release_payload_shape", ) _require_uuid(release_payload["submission_id"], "invalid_release_submission_id") gate_id = _require_uuid(release_payload["gate_id"], "invalid_gate_id") if release_payload["data_classification"] != DATA_CLASSIFICATION: raise ProbeError("invalid_data_classification") manifest = _require_exact_dict( release_payload["manifest"], { "release_id", "red_green_passed", "contract_passed", "e2e_passed", "runtime_proof_passed", "public_proof_passed", "ssot_synced", "evidence_refs", }, "invalid_release_manifest_shape", ) release_id = _require_string( manifest["release_id"], "invalid_release_id", maximum=180 ) if RELEASE_ID_RE.fullmatch(release_id) is None: raise ProbeError("invalid_release_id") for field in ( "red_green_passed", "contract_passed", "e2e_passed", "runtime_proof_passed", "public_proof_passed", "ssot_synced", ): if manifest[field] is not True: raise ProbeError("release_gate_not_qualified") release_evidence = _require_evidence_refs(manifest["evidence_refs"]) artifacts = _require_exact_dict( release_payload["artifacts"], {"baseline", "threshold", "provenance", "rollback"}, "invalid_gate_artifacts_shape", ) _validate_gate_artifact(artifacts["baseline"], "invalid_baseline_artifact") _validate_gate_artifact(artifacts["threshold"], "invalid_threshold_artifact") if ( not isinstance(artifacts["provenance"], list) or len(artifacts["provenance"]) != 1 ): raise ProbeError("invalid_provenance_artifacts") _validate_gate_artifact(artifacts["provenance"][0], "invalid_provenance_artifact") rollback_artifact = _validate_gate_artifact( artifacts["rollback"], "invalid_rollback_artifact" ) record_ids = [ artifacts["baseline"]["artifact_record_id"], artifacts["threshold"]["artifact_record_id"], artifacts["provenance"][0]["artifact_record_id"], rollback_artifact["artifact_record_id"], ] if len(record_ids) != len(set(record_ids)): raise ProbeError("duplicate_artifact_record_ids") approval_payload = _require_exact_dict( approval["payload"], { "submission_id", "approval_event_id", "effect_record_id", "target_kind", "target_id", "decision", "reason_code", "evidence_refs", }, "invalid_approval_payload_shape", ) _require_uuid(approval_payload["submission_id"], "invalid_approval_submission_id") approval_event_id = _require_uuid( approval_payload["approval_event_id"], "invalid_approval_event_id" ) effect_record_id = _require_uuid( approval_payload["effect_record_id"], "invalid_effect_record_id" ) if ( approval_payload["target_kind"] != "release_gate" or approval_payload["target_id"] != gate_id or approval_payload["decision"] != "authorize_rollback" or approval_payload["reason_code"] != "nas_preview_runtime_rollback_probe" ): raise ProbeError("invalid_approval_binding") approval_evidence = _require_evidence_refs(approval_payload["evidence_refs"]) if approval_evidence != release_evidence: raise ProbeError("release_approval_evidence_mismatch") expected_request = _require_exact_dict( plan["expected_executor_request"], { "schema_version", "idempotency_key", "approval_event_id", "rollback_scope", "target_kind", "target_id", "subject_id", "rollback_target_id", "artifact_record_id", "artifact_id", "artifact_sha256", "artifact_provenance_uri", "authorization_evidence_refs", }, "invalid_executor_request_shape", ) expected_binding = { "schema_version": EXECUTOR_REQUEST_SCHEMA, "idempotency_key": effect_record_id, "approval_event_id": approval_event_id, "rollback_scope": "runtime", "target_kind": "release_gate", "target_id": gate_id, "subject_id": release_id, "rollback_target_id": rollback_artifact["artifact_id"], "artifact_record_id": rollback_artifact["artifact_record_id"], "artifact_id": rollback_artifact["artifact_id"], "artifact_sha256": rollback_artifact["content_sha256"], "artifact_provenance_uri": rollback_artifact["provenance_uri"], "authorization_evidence_refs": approval_evidence, } if expected_request != expected_binding: raise ProbeError("executor_request_binding_mismatch") executor_manifest = _require_exact_dict( plan["executor_manifest"], {"file_sha256", "payload"}, "invalid_executor_manifest_envelope", ) executor_payload = _require_exact_dict( executor_manifest["payload"], {"schema_version", "target", "artifacts"}, "invalid_executor_manifest_shape", ) if executor_payload["schema_version"] != EXECUTOR_MANIFEST_SCHEMA: raise ProbeError("invalid_executor_manifest_schema") if executor_payload["target"] != { "compose_project": TARGET_PROJECT, "remote_root": TARGET_ROOT, "services": TARGET_SERVICES, "health_url": TARGET_HEALTH_URL, }: raise ProbeError("invalid_executor_manifest_target") if ( not isinstance(executor_payload["artifacts"], list) or len(executor_payload["artifacts"]) != 1 ): raise ProbeError("invalid_executor_manifest_artifacts") manifest_artifact = _require_exact_dict( executor_payload["artifacts"][0], { "idempotency_key", "approval_event_id", "authorization_evidence_refs", "artifact_id", "artifact_sha256", "artifact_record_id", "artifact_provenance_uri", "target_id", "subject_id", "rollback_target_id", "compose_file", "compose_sha256", "env_file", "env_sha256", "resolved_config_sha256", "api_image", "web_image", }, "invalid_executor_manifest_artifact_shape", ) for field in expected_binding: if field in {"schema_version", "rollback_scope", "target_kind"}: continue manifest_field = ( "authorization_evidence_refs" if field == "authorization_evidence_refs" else field ) if manifest_artifact.get(manifest_field) != expected_binding[field]: raise ProbeError("executor_manifest_binding_mismatch") expected_root = ( f"{TARGET_ROOT}/.rollback-executor/runtime/{rollback_artifact['artifact_id']}" ) if manifest_artifact["compose_file"] != f"{expected_root}/docker-compose.yml": raise ProbeError("invalid_compose_file") if manifest_artifact["env_file"] != f"{expected_root}/.env": raise ProbeError("invalid_env_file") _require_sha256(manifest_artifact["compose_sha256"], "invalid_compose_sha256") _require_sha256(manifest_artifact["env_sha256"], "invalid_env_sha256") _require_sha256( manifest_artifact["resolved_config_sha256"], "invalid_resolved_config_sha256", ) _require_image(manifest_artifact["api_image"], "invalid_api_image") _require_image(manifest_artifact["web_image"], "invalid_web_image") manifest_digest = _require_sha256( executor_manifest["file_sha256"], "invalid_executor_manifest_sha256" ) if manifest_digest != hashlib.sha256(_manifest_bytes(executor_payload)).hexdigest(): raise ProbeError("executor_manifest_sha256_mismatch") if plan["safety"] != { "control_plane_required": True, "main_preview_self_rollback_forbidden": True, "contains_secrets": False, "contains_pii": False, }: raise ProbeError("invalid_safety_contract") return plan def load_plan(path: Path) -> dict[str, Any]: try: raw = path.read_bytes() except OSError as exc: raise ProbeError("plan_unreadable") from exc if not raw or len(raw) > MAX_RESPONSE_BYTES: raise ProbeError("plan_size_rejected") try: value = json.loads(raw) except (UnicodeDecodeError, json.JSONDecodeError) as exc: raise ProbeError("plan_json_invalid") from exc return validate_plan(value) def _normalize_base_url( value: str, *, label: str, require_secure_transport: bool ) -> tuple[str, str]: parsed = urlsplit(value) hostname = (parsed.hostname or "").lower() local_http = parsed.scheme == "http" and hostname in { "localhost", "127.0.0.1", "::1", } if parsed.scheme not in {"http", "https"}: raise ProbeError(f"invalid_{label}_url") if require_secure_transport and parsed.scheme != "https" and not local_http: raise ProbeError(f"{label}_must_use_https") if ( not parsed.netloc or parsed.username or parsed.password or parsed.query or parsed.fragment ): raise ProbeError(f"invalid_{label}_url") normalized = value.rstrip("/") origin = f"{parsed.scheme.lower()}://{parsed.netloc.lower()}" return normalized, origin class ApiClient: def __init__(self, base_url: str, timeout: float) -> None: self.base_url = base_url.rstrip("/") self.timeout = timeout self._opener = urllib.request.build_opener( urllib.request.ProxyHandler({}), _NoRedirect(), urllib.request.HTTPCookieProcessor(CookieJar()), ) def request( self, method: str, path: str, payload: dict[str, Any] | None = None, *, headers: dict[str, str] | None = None, expected: set[int] | None = None, ) -> ApiResponse: data = None request_headers = {"Accept": "application/json", **(headers or {})} if payload is not None: data = _canonical_bytes(payload) request_headers["Content-Type"] = "application/json" request = urllib.request.Request( f"{self.base_url}{path}", data=data, headers=request_headers, method=method, ) try: with self._opener.open(request, timeout=self.timeout) as response: raw = response.read(MAX_RESPONSE_BYTES + 1) status = response.status except urllib.error.HTTPError as exc: try: exc.read(MAX_RESPONSE_BYTES + 1) finally: exc.close() raise ProbeError(f"http_{exc.code}_{method.lower()}") from None except (urllib.error.URLError, TimeoutError, OSError): raise ProbeError(f"transport_failed_{method.lower()}") from None if status not in (expected or {200}): raise ProbeError(f"unexpected_http_{status}_{method.lower()}") if len(raw) > MAX_RESPONSE_BYTES: raise ProbeError("response_size_rejected") try: body = json.loads(raw) if raw else {} except (UnicodeDecodeError, json.JSONDecodeError): raise ProbeError("response_json_invalid") from None return ApiResponse(status=status, body=body) def _require_response_dict(value: Any, code: str) -> dict[str, Any]: if not isinstance(value, dict): raise ProbeError(code) return value def _single(items: Any, predicate: Any, code: str) -> dict[str, Any]: if not isinstance(items, list): raise ProbeError(code) matches = [item for item in items if isinstance(item, dict) and predicate(item)] if len(matches) != 1: raise ProbeError(code) return matches[0] def execute_plan( *, plan: dict[str, Any], control_plane_base_url: str, preview_base_url: str, internal_token: str, admin_email: str, timeout: float, ) -> dict[str, Any]: plan = validate_plan(plan) control_url, control_origin = _normalize_base_url( control_plane_base_url, label="control_plane", require_secure_transport=True, ) _, preview_origin = _normalize_base_url( preview_base_url, label="preview", require_secure_transport=False, ) if control_origin == preview_origin: raise ProbeError("main_preview_self_rollback_forbidden") if not isinstance(internal_token, str) or len(internal_token) < 32: raise ProbeError("internal_token_unavailable") if ( not isinstance(admin_email, str) or "@" not in admin_email or len(admin_email) > 320 ): raise ProbeError("admin_identity_unavailable") if timeout <= 0 or timeout > 300: raise ProbeError("invalid_timeout") client = ApiClient(control_url, timeout) internal_headers = {INTERNAL_TOKEN_HEADER: internal_token} release = plan["release_gate"] release_payload = release["payload"] created = client.request( "POST", release["path"], release_payload, headers=internal_headers, expected={201}, ).body replayed = client.request( "POST", release["path"], release_payload, headers=internal_headers, expected={201}, ).body for response in (created, replayed): response = _require_response_dict(response, "release_response_invalid") if ( response.get("gate_id") != release_payload["gate_id"] or response.get("qualified") is not True or response.get("state") != "pending_human_approval" or response.get("promotion_executed") is not False ): raise ProbeError("release_response_binding_mismatch") if replayed.get("idempotent_replay") is not True: raise ProbeError("release_gate_not_idempotent") client.request( "POST", DEV_LOGIN_PATH, { "email": admin_email, "role": "admin", "display_name": "G8 Rollback Control Plane Probe", "cohort_ids": [], }, headers={"Origin": control_origin}, expected={200}, ) approval = plan["approval"] approval_payload = approval["payload"] approved = client.request( "POST", approval["path"], approval_payload, expected={201} ).body approval_replay = client.request( "POST", approval["path"], approval_payload, expected={201} ).body for response in (approved, approval_replay): response = _require_response_dict(response, "approval_response_invalid") if ( response.get("approval_event_id") != approval_payload["approval_event_id"] or response.get("effect_record_id") != approval_payload["effect_record_id"] or response.get("target_kind") != "release_gate" or response.get("target_id") != release_payload["gate_id"] or response.get("decision") != "authorize_rollback" ): raise ProbeError("approval_response_binding_mismatch") if approval_replay.get("idempotent_replay") is not True: raise ProbeError("approval_not_idempotent") view = client.request( "GET", READ_MODEL_PATH, headers=internal_headers, expected={200} ).body view = _require_response_dict(view, "read_model_invalid") for flag in ( "silent_auto_promotion_allowed", "raw_transcript_included", "pii_included", "clinical_claim_allowed", ): if view.get(flag) is not False: raise ProbeError("read_model_safety_boundary_failed") gate_id = release_payload["gate_id"] release_gate = _single( view.get("release_gates"), lambda item: item.get("gate_id") == gate_id, "release_gate_read_model_mismatch", ) if ( release_gate.get("release_id") != release_payload["manifest"]["release_id"] or release_gate.get("qualified") is not True or release_gate.get("state") != "pending_human_approval" ): raise ProbeError("release_gate_read_model_mismatch") expected_request = plan["expected_executor_request"] artifact = _single( view.get("gate_artifacts"), lambda item: ( item.get("artifact_record_id") == expected_request["artifact_record_id"] ), "rollback_artifact_read_model_mismatch", ) if ( artifact.get("owner_kind") != "release_gate" or artifact.get("owner_id") != gate_id or artifact.get("artifact_kind") != "rollback" or artifact.get("artifact_id") != expected_request["artifact_id"] or artifact.get("content_sha256") != expected_request["artifact_sha256"] or artifact.get("provenance_uri") != expected_request["artifact_provenance_uri"] ): raise ProbeError("rollback_artifact_read_model_mismatch") approval_event = _single( view.get("approvals"), lambda item: ( item.get("approval_event_id") == approval_payload["approval_event_id"] ), "approval_read_model_mismatch", ) if ( approval_event.get("target_kind") != "release_gate" or approval_event.get("target_id") != gate_id or approval_event.get("decision") != "authorize_rollback" or approval_event.get("evidence_refs") != approval_payload["evidence_refs"] ): raise ProbeError("approval_read_model_mismatch") lifecycle = _single( view.get("lifecycle_events"), lambda item: ( item.get("lifecycle_event_id") == approval_payload["effect_record_id"] ), "rollback_lifecycle_read_model_mismatch", ) executor_refs = lifecycle.get("executor_evidence_refs") lifecycle_refs = lifecycle.get("evidence_refs") receipt_id = lifecycle.get("executor_receipt_id") if ( lifecycle.get("target_kind") != "release_gate" or lifecycle.get("target_id") != gate_id or lifecycle.get("event_type") != "rollback" or lifecycle.get("event_status") != "executed" or lifecycle.get("approval_event_id") != approval_payload["approval_event_id"] or lifecycle.get("artifact_record_id") != expected_request["artifact_record_id"] or not isinstance(receipt_id, str) or not receipt_id or not isinstance(executor_refs, list) or not executor_refs or not isinstance(lifecycle_refs, list) or not set(approval_payload["evidence_refs"]).issubset(lifecycle_refs) or not set(executor_refs).issubset(lifecycle_refs) ): raise ProbeError("rollback_executed_receipt_binding_mismatch") result = { "schema_version": RESULT_SCHEMA, "probe_id": plan["probe_id"], "plan_sha256": plan["plan_sha256"], "release_gate_id": gate_id, "approval_event_id": approval_payload["approval_event_id"], "lifecycle_event_id": approval_payload["effect_record_id"], "lifecycle_status": "executed", "artifact_record_id": expected_request["artifact_record_id"], "artifact_id": expected_request["artifact_id"], "artifact_sha256": expected_request["artifact_sha256"], "executor_receipt_id": receipt_id, "executor_evidence_refs": executor_refs, "release_gate_idempotent_replay": True, "approval_idempotent_replay": True, "binding_verified": True, "control_plane_separation_verified": True, "contains_secrets": False, "contains_pii": False, } if set(result) != RESULT_FIELDS: raise ProbeError("result_shape_invalid") return result def _write_json(path: Path, value: Any) -> None: path.parent.mkdir(parents=True, exist_ok=True) path.write_text( json.dumps(value, ensure_ascii=False, indent=2, sort_keys=True) + "\n", encoding="utf-8", ) def _build_parser() -> argparse.ArgumentParser: parser = argparse.ArgumentParser( description=__doc__, epilog=( "SAFETY: the main preview API must not self-authorize rollback. " "Execute only against a separately deployed control-plane API whose " "rollback executor and immutable d31b0d manifest are provisioned." ), ) subparsers = parser.add_subparsers(dest="command", required=True) prepare = subparsers.add_parser( "prepare", help="emit deterministic request and executor-manifest bindings" ) prepare.add_argument("--suffix", required=True) prepare.add_argument("--release-id") prepare.add_argument("--artifact-id", required=True) prepare.add_argument("--artifact-sha256", required=True) prepare.add_argument("--artifact-provenance-uri", required=True) prepare.add_argument("--evidence-ref", action="append", required=True) prepare.add_argument("--compose-sha256", required=True) prepare.add_argument("--env-sha256", required=True) prepare.add_argument("--resolved-config-sha256", required=True) prepare.add_argument("--api-image", required=True) prepare.add_argument("--web-image", required=True) prepare.add_argument("--output", type=Path) prepare.add_argument("--executor-manifest-output", type=Path) execute = subparsers.add_parser( "execute", help="exercise a separate control-plane API and verify executed receipt binding", ) execute.add_argument("--plan", type=Path, required=True) execute.add_argument("--control-plane-base-url", required=True) execute.add_argument("--preview-base-url", required=True) execute.add_argument( "--internal-token-env", default="VIGNETTE_CONTINUOUS_IMPROVEMENT_INTERNAL_TOKEN", ) execute.add_argument( "--admin-email-env", default="VIGNETTE_G8_ROLLBACK_PROBE_ADMIN_EMAIL" ) execute.add_argument("--timeout", type=float, default=30.0) execute.add_argument("--output", type=Path) return parser def main(argv: list[str] | None = None) -> int: args = _build_parser().parse_args(argv) try: if args.command == "prepare": plan = build_plan( suffix=args.suffix, release_id=args.release_id, artifact_id=args.artifact_id, artifact_sha256=args.artifact_sha256, artifact_provenance_uri=args.artifact_provenance_uri, evidence_refs=args.evidence_ref, compose_sha256=args.compose_sha256, env_sha256=args.env_sha256, resolved_config_sha256=args.resolved_config_sha256, api_image=args.api_image, web_image=args.web_image, ) if args.output: _write_json(args.output, plan) if args.executor_manifest_output: args.executor_manifest_output.parent.mkdir(parents=True, exist_ok=True) args.executor_manifest_output.write_bytes( _manifest_bytes(plan["executor_manifest"]["payload"]) ) result = plan else: plan = load_plan(args.plan) result = execute_plan( plan=plan, control_plane_base_url=args.control_plane_base_url, preview_base_url=args.preview_base_url, internal_token=os.environ.get(args.internal_token_env, ""), admin_email=os.environ.get(args.admin_email_env, ""), timeout=args.timeout, ) if args.output: _write_json(args.output, result) print(json.dumps(result, ensure_ascii=False, indent=2, sort_keys=True)) except ProbeError as exc: print( json.dumps( { "schema_version": "vignette.g8-rollback-control-plane-probe-error.v1", "ok": False, "error": str(exc), }, ensure_ascii=False, sort_keys=True, ), file=sys.stderr, ) return 1 return 0 if __name__ == "__main__": raise SystemExit(main())