Files
ai-contract-platform/yuqei-infra/deploy/plan-first-release-snapshot-restore.py
T

2217 lines
91 KiB
Python

#!/usr/bin/env python3
"""Verify first-release snapshot evidence and emit a non-writing restore plan.
This command intentionally has no apply mode. A whole-host restore must run
from an independently authorized external control plane; this planner only
verifies immutable evidence, detached signatures, and the exact recovery scope.
"""
from __future__ import annotations
import argparse
import base64
import hashlib
import importlib.util
import json
import os
import re
import stat
import sys
from datetime import UTC, datetime, timedelta
from pathlib import Path, PurePosixPath
from typing import Any, Callable, Mapping
from cryptography.exceptions import InvalidSignature
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PublicKey
EVIDENCE_SCHEMA = "yuqei.first-release-snapshot-evidence.v2"
KEYRING_SCHEMA = "yuqei.first-release-recovery-keyring.v2"
APPROVAL_SCHEMA = "yuqei.first-release-recovery-approval.v2"
NO_PRIOR_RELEASE_SCHEMA = "yuqei.no-prior-successful-release.v1"
SNAPSHOT_RECEIPT_SCHEMA = "yuqei.host-snapshot-receipt.v1"
BACKUP_MANIFEST_SCHEMA_V2 = "yuqei-production-backup.v2"
BACKUP_MANIFEST_SCHEMA = "yuqei-production-backup.v3"
READ_ONLY_BACKUP_MANIFEST_SCHEMAS = frozenset(
{BACKUP_MANIFEST_SCHEMA_V2, BACKUP_MANIFEST_SCHEMA}
)
SOURCE_BACKUP_MANIFEST_SCHEMA = "yuqei-production-backup-artifacts.v2"
RESTORE_TEST_SCHEMA = "yuqei.first-release-restore-test.v1"
PLAN_SCHEMA = "yuqei.first-release-snapshot-restore-plan.v2"
READINESS_RECEIPT_SCHEMA = "yuqei.first-release-recovery-readiness-receipt.v2"
IMAGE_IDENTITY_SCHEMA = "yuqei.release-image-identity.v2"
PACKAGE_SCHEMA = "yuqei.release-package.v2"
READ_ONLY_PACKAGE_SCHEMAS = frozenset(
{"yuqei.release-package.v1", PACKAGE_SCHEMA}
)
SOURCE_MANIFEST_SCHEMA = "yuqei.release-source-manifest.v1"
CHECKSUM_MANIFEST_SCHEMA = "yuqei.release-package-checksums.v1"
INSTALLABLE_MANIFEST_SCHEMA = "yuqei.installable-release-manifest.v1"
RECOVERY_STRATEGY = "first_release_snapshot_restore"
RECOVERY_EVIDENCE_STATUS = "runtime_signed_evidence_required"
REQUIRED_ROLES = frozenset({"release_owner"})
REQUIRED_NAMED_REVIEWERS = 1
SOLE_REVIEWER_ID = "chen-yuqi"
SOLE_REVIEWER_NAME = "陈钰麒"
SOLE_REVIEWER_TITLE = "总经理"
REQUIRED_COMPONENTS = (
"host_runtime_state",
"postgres_consistent_backup",
"object_storage_state",
"release_environment_and_tls_bindings",
)
REQUIRED_DATABASES = ("contract", "ai", "operations")
REQUIRED_STORAGE_TARGETS = ("contract_files_volume", "minio_object_store")
APPLICATION_IMAGE_KEYS = (
"CONTRACT_API_IMAGE",
"CONTRACT_WEB_IMAGE",
"AI_PLATFORM_API_IMAGE",
"AI_WORKER_IMAGE",
)
INFRASTRUCTURE_IMAGE_KEYS = (
"POSTGRES_IMAGE",
"POSTGRES_CLIENT_IMAGE",
"REDIS_IMAGE",
"MINIO_IMAGE",
"MINIO_MC_IMAGE",
"NGINX_IMAGE",
"BACKUP_UTILITY_IMAGE",
)
PACKAGE_FILE = "release-package.json"
REPO_ROOT = Path(__file__).resolve().parents[2]
PACKAGE_VALIDATOR_SOURCE_CLOSURE = (
"yuqei-infra/deploy/plan-first-release-snapshot-restore.py",
"yuqei-infra/deploy/release-package.py",
"yuqei-infra/backup/run-production-backup.py",
"yuqei-infra/backup/verify-production-backup.py",
"yuqei-infra/deploy/preflight-production.py",
"yuqei-infra/deploy/verify-production-compose.py",
"yuqei-infra/scripts/build-installable-release.py",
"yuqei-infra/scripts/build-infrastructure-image-bundle.py",
"yuqei-infra/scripts/build-third-party-materials.py",
"yuqei-infra/release-trust/third-party-materials-baseline.json",
"yuqei-infra/scripts/linux_release_common.py",
)
BACKUP_APPLICATION_IMAGE_KEYS = (
"CONTRACT_API_IMAGE",
"AI_PLATFORM_API_IMAGE",
"AI_WORKER_IMAGE",
)
BACKUP_OBJECT_STORAGE_IMAGE_KEYS = ("MINIO_IMAGE", "MINIO_MC_IMAGE")
MAX_EVIDENCE_AGE = timedelta(hours=24)
MAX_CLOCK_SKEW = timedelta(minutes=5)
MAX_JSON_BYTES = 8 * 1024 * 1024
SHA256_RE = re.compile(r"^[0-9a-f]{64}$")
DIGEST_RE = re.compile(r"^sha256:[0-9a-f]{64}$")
IMAGE_REFERENCE_RE = re.compile(
r"^[a-z0-9](?:[a-z0-9._:/-]{0,510}[a-z0-9])?@sha256:[0-9a-f]{64}$"
)
IDENTIFIER_RE = re.compile(r"^[A-Za-z0-9][A-Za-z0-9._:/+-]{0,255}$")
TRUSTED_ROOT_KEY_ID = "yuqei-first-release-recovery-root-v1"
# Provision these pins together in a separately reviewed frozen release. Empty
# values intentionally keep the planner fail closed. A caller must never be
# able to nominate its own root or keyring revocation state on the command line.
TRUSTED_ROOT_PUBLIC_KEY_SHA256 = ""
TRUSTED_CURRENT_KEYRING_PAYLOAD_SHA256 = ""
MIN_KEYRING_EPOCH: int | None = None
class SnapshotRestorePlanError(RuntimeError):
"""Raised when first-release recovery evidence is unsafe or incomplete."""
def canonical_json_bytes(value: object) -> bytes:
try:
return json.dumps(
value,
ensure_ascii=False,
sort_keys=True,
separators=(",", ":"),
allow_nan=False,
).encode("utf-8")
except (TypeError, ValueError) as exc:
raise SnapshotRestorePlanError("value cannot be encoded as canonical JSON") from exc
def canonical_sha256(value: object) -> str:
return hashlib.sha256(canonical_json_bytes(value)).hexdigest()
def runtime_host_identity_sha256(
host_id: object,
docker_daemon_identity: Mapping[str, object],
) -> str:
"""Derive the recovery host identity from stable local Docker/host fields."""
normalized_host_id = _identifier(host_id, label="host_id")
required = ("docker_host", "daemon_id", "daemon_name", "docker_root_dir")
values: dict[str, str] = {}
for field in required:
value = docker_daemon_identity.get(field)
if not isinstance(value, str) or not value.strip():
raise SnapshotRestorePlanError(
f"Docker daemon identity field is missing for host binding: {field}"
)
values[field] = value
return canonical_sha256({"host_id": normalized_host_id, **values})
def file_sha256(path: Path) -> str:
digest = hashlib.sha256()
with path.open("rb") as stream:
for chunk in iter(lambda: stream.read(1024 * 1024), b""):
digest.update(chunk)
return digest.hexdigest()
def _reject_constant(value: str) -> None:
raise SnapshotRestorePlanError(f"non-finite JSON value is forbidden: {value}")
def _reject_duplicate_pairs(pairs: list[tuple[str, Any]]) -> dict[str, Any]:
result: dict[str, Any] = {}
for key, value in pairs:
if key in result:
raise SnapshotRestorePlanError(f"duplicate JSON key is forbidden: {key}")
result[key] = value
return result
def _absolute(path: Path) -> Path:
return Path(os.path.abspath(os.fspath(path)))
def _metadata_is_reparse(metadata: os.stat_result) -> bool:
reparse_attribute = int(getattr(stat, "FILE_ATTRIBUTE_REPARSE_POINT", 0x400))
return bool(
stat.S_ISLNK(metadata.st_mode)
or int(getattr(metadata, "st_file_attributes", 0)) & reparse_attribute
or int(getattr(metadata, "st_reparse_tag", 0)) != 0
)
def _reject_symlink_components(path: Path, *, label: str) -> None:
absolute = _absolute(path)
current = Path(absolute.anchor)
for part in absolute.parts[1:]:
current /= part
try:
metadata = os.lstat(current)
if _metadata_is_reparse(metadata):
raise SnapshotRestorePlanError(f"{label} contains a symbolic link")
except FileNotFoundError:
continue
except OSError as exc:
raise SnapshotRestorePlanError(f"cannot inspect {label} path") from exc
def _regular_file(path: Path, *, label: str, max_bytes: int = MAX_JSON_BYTES) -> Path:
lexical = _absolute(path)
_reject_symlink_components(lexical, label=label)
try:
resolved = lexical.resolve(strict=True)
metadata = resolved.stat()
except OSError as exc:
raise SnapshotRestorePlanError(f"{label} is missing") from exc
if lexical.is_symlink() or not stat.S_ISREG(metadata.st_mode):
raise SnapshotRestorePlanError(f"{label} must be a regular file")
if metadata.st_nlink != 1:
raise SnapshotRestorePlanError(f"{label} must not be hard linked")
if metadata.st_size <= 0 or metadata.st_size > max_bytes:
raise SnapshotRestorePlanError(f"{label} has an invalid size")
return resolved
def _regular_directory(path: Path, *, label: str) -> Path:
lexical = _absolute(path)
_reject_symlink_components(lexical, label=label)
try:
resolved = lexical.resolve(strict=True)
except OSError as exc:
raise SnapshotRestorePlanError(f"{label} is missing") from exc
if lexical.is_symlink() or not resolved.is_dir():
raise SnapshotRestorePlanError(f"{label} must be a real directory")
return resolved
def load_json(path: Path, *, label: str) -> dict[str, Any]:
target = _regular_file(path, label=label)
try:
payload = json.loads(
target.read_text(encoding="utf-8-sig"),
object_pairs_hook=_reject_duplicate_pairs,
parse_constant=_reject_constant,
)
except SnapshotRestorePlanError:
raise
except (OSError, UnicodeError, ValueError) as exc:
raise SnapshotRestorePlanError(f"{label} is not strict UTF-8 JSON") from exc
if not isinstance(payload, dict):
raise SnapshotRestorePlanError(f"{label} must be a JSON object")
return payload
def _metadata_state(metadata: os.stat_result) -> tuple[int, ...]:
"""Return metadata that must stay stable for one already-open file view."""
return (
metadata.st_dev,
metadata.st_ino,
metadata.st_mode,
metadata.st_nlink,
metadata.st_size,
metadata.st_mtime_ns,
metadata.st_ctime_ns,
int(getattr(metadata, "st_file_attributes", 0)),
int(getattr(metadata, "st_reparse_tag", 0)),
)
def _assert_external_file_metadata(
metadata: os.stat_result,
*,
label: str,
max_bytes: int,
) -> None:
if (
not stat.S_ISREG(metadata.st_mode)
or metadata.st_nlink != 1
or metadata.st_ino == 0
or _metadata_is_reparse(metadata)
):
raise SnapshotRestorePlanError(
f"{label} must be a single-link non-reparse regular file"
)
if metadata.st_size <= 0 or metadata.st_size > max_bytes:
raise SnapshotRestorePlanError(f"{label} has an invalid size")
def _strict_json_object_from_bytes(raw: bytes, *, label: str) -> dict[str, Any]:
try:
payload = json.loads(
raw.decode("utf-8-sig"),
object_pairs_hook=_reject_duplicate_pairs,
parse_constant=_reject_constant,
)
except SnapshotRestorePlanError:
raise
except (UnicodeError, ValueError) as exc:
raise SnapshotRestorePlanError(f"{label} is not strict UTF-8 JSON") from exc
if not isinstance(payload, dict):
raise SnapshotRestorePlanError(f"{label} must be a JSON object")
return payload
def _read_stable_external_json(
path: Path,
*,
label: str,
expected_sha256: str,
max_bytes: int = MAX_JSON_BYTES,
) -> tuple[dict[str, Any], Path]:
"""Read, hash, parse, and revalidate an external record via one descriptor."""
lexical = _absolute(path)
_reject_symlink_components(lexical, label=label)
try:
path_before = os.lstat(lexical)
except OSError as exc:
raise SnapshotRestorePlanError(f"{label} is missing") from exc
_assert_external_file_metadata(path_before, label=label, max_bytes=max_bytes)
flags = (
os.O_RDONLY
| getattr(os, "O_BINARY", 0)
| getattr(os, "O_NOFOLLOW", 0)
)
try:
descriptor = os.open(lexical, flags)
except OSError as exc:
raise SnapshotRestorePlanError(f"{label} cannot be opened safely") from exc
try:
opened_before = os.fstat(descriptor)
_assert_external_file_metadata(opened_before, label=label, max_bytes=max_bytes)
if (
not os.path.samestat(path_before, opened_before)
or path_before.st_size != opened_before.st_size
or path_before.st_mtime_ns != opened_before.st_mtime_ns
):
raise SnapshotRestorePlanError(f"{label} path identity changed before reading")
chunks: list[bytes] = []
total = 0
while True:
try:
chunk = os.read(descriptor, min(1024 * 1024, max_bytes + 1 - total))
except OSError as exc:
raise SnapshotRestorePlanError(f"{label} cannot be read safely") from exc
if not chunk:
break
chunks.append(chunk)
total += len(chunk)
if total > max_bytes:
raise SnapshotRestorePlanError(f"{label} has an invalid size")
raw = b"".join(chunks)
if len(raw) != opened_before.st_size:
raise SnapshotRestorePlanError(f"{label} size changed while reading")
if hashlib.sha256(raw).hexdigest() != expected_sha256:
raise SnapshotRestorePlanError(f"{label} SHA-256 mismatch")
payload = _strict_json_object_from_bytes(raw, label=label)
opened_after = os.fstat(descriptor)
_assert_external_file_metadata(opened_after, label=label, max_bytes=max_bytes)
if _metadata_state(opened_after) != _metadata_state(opened_before):
raise SnapshotRestorePlanError(f"{label} metadata changed while reading")
try:
path_after = os.lstat(lexical)
except OSError as exc:
raise SnapshotRestorePlanError(f"{label} path changed while reading") from exc
_assert_external_file_metadata(path_after, label=label, max_bytes=max_bytes)
if (
_metadata_state(path_after) != _metadata_state(path_before)
or not os.path.samestat(path_after, opened_after)
or path_after.st_size != opened_after.st_size
or path_after.st_mtime_ns != opened_after.st_mtime_ns
):
raise SnapshotRestorePlanError(f"{label} path identity changed while reading")
_reject_symlink_components(lexical, label=label)
return payload, lexical
finally:
os.close(descriptor)
def _exact(value: object, fields: set[str], *, label: str) -> dict[str, Any]:
if not isinstance(value, dict) or set(value) != fields:
raise SnapshotRestorePlanError(f"{label} fields are missing or unknown")
return dict(value)
def _sha(value: object, *, label: str) -> str:
if not isinstance(value, str) or not SHA256_RE.fullmatch(value):
raise SnapshotRestorePlanError(f"{label} must be a lowercase SHA-256")
return value
def _identifier(value: object, *, label: str) -> str:
if not isinstance(value, str) or not IDENTIFIER_RE.fullmatch(value):
raise SnapshotRestorePlanError(f"{label} is missing or unsafe")
return value
def _timestamp(value: object, *, label: str) -> datetime:
if not isinstance(value, str):
raise SnapshotRestorePlanError(f"{label} must be RFC3339 text")
try:
parsed = datetime.fromisoformat(value.replace("Z", "+00:00"))
except ValueError as exc:
raise SnapshotRestorePlanError(f"{label} is not RFC3339") from exc
if parsed.tzinfo is None:
raise SnapshotRestorePlanError(f"{label} must include a timezone")
return parsed.astimezone(UTC)
def _embedded_hash(payload: Mapping[str, Any], field: str, *, label: str) -> None:
expected = _sha(payload.get(field), label=f"{label} {field}")
unsigned = {key: value for key, value in payload.items() if key != field}
if canonical_sha256(unsigned) != expected:
raise SnapshotRestorePlanError(f"{label} payload hash mismatch")
def _safe_relative(value: object, *, label: str) -> str:
if not isinstance(value, str) or not value or "\\" in value or "\x00" in value:
raise SnapshotRestorePlanError(f"{label} must be a canonical relative path")
pure = PurePosixPath(value)
if pure.is_absolute() or pure.as_posix() != value or any(
part in {"", ".", ".."} for part in pure.parts
):
raise SnapshotRestorePlanError(f"{label} is unsafe")
return value
def _package_artifact(
package_file: Path,
package: Mapping[str, Any],
name: str,
) -> tuple[Path, str]:
artifacts = package.get("artifacts")
if not isinstance(artifacts, dict):
raise SnapshotRestorePlanError("release package has no artifact inventory")
record = _exact(artifacts.get(name), {"path", "sha256"}, label=f"artifact {name}")
relative = _safe_relative(record.get("path"), label=f"artifact {name} path")
expected = _sha(record.get("sha256"), label=f"artifact {name} SHA-256")
root = package_file.parent.resolve()
target = _regular_file(root.joinpath(*PurePosixPath(relative).parts), label=f"artifact {name}")
try:
target.relative_to(root)
except ValueError as exc:
raise SnapshotRestorePlanError(f"artifact {name} escapes the package") from exc
if file_sha256(target) != expected:
raise SnapshotRestorePlanError(f"artifact {name} SHA-256 mismatch")
return target, expected
def _external_record(value: object, *, label: str) -> tuple[dict[str, Any], str, Path]:
record = _exact(value, {"path", "sha256"}, label=f"{label} record")
raw_path = record.get("path")
if not isinstance(raw_path, str) or not Path(raw_path).is_absolute():
raise SnapshotRestorePlanError(f"{label} path must be absolute")
expected = _sha(record.get("sha256"), label=f"{label} external SHA-256")
payload, path = _read_stable_external_json(
Path(raw_path),
label=label,
expected_sha256=expected,
)
return payload, expected, path
def _validate_source_closure(
release_manifest: Mapping[str, Any],
*,
repo_root: Path,
) -> None:
if release_manifest.get("schema_version") != SOURCE_MANIFEST_SCHEMA:
raise SnapshotRestorePlanError("unsupported release source manifest schema")
_embedded_hash(release_manifest, "manifest_payload_sha256", label="release source manifest")
files = release_manifest.get("files")
items = files.get("items") if isinstance(files, dict) else None
if not isinstance(items, list) or files.get("count") != len(items):
raise SnapshotRestorePlanError("release source manifest file inventory is invalid")
by_path: dict[str, Mapping[str, Any]] = {}
for raw in items:
if not isinstance(raw, dict):
raise SnapshotRestorePlanError("release source manifest has an invalid file record")
relative = _safe_relative(raw.get("path"), label="release source path")
if relative in by_path:
raise SnapshotRestorePlanError("release source manifest repeats a path")
by_path[relative] = raw
root = _regular_directory(repo_root, label="repository root")
for relative in PACKAGE_VALIDATOR_SOURCE_CLOSURE:
record = by_path.get(relative)
if record is None:
raise SnapshotRestorePlanError(
f"release source manifest does not bind validator source: {relative}"
)
expected_sha = _sha(record.get("sha256"), label=f"{relative} source SHA-256")
expected_size = record.get("size")
if not isinstance(expected_size, int) or isinstance(expected_size, bool) or expected_size <= 0:
raise SnapshotRestorePlanError(f"{relative} source size is invalid")
target = _regular_file(
root.joinpath(*PurePosixPath(relative).parts),
label=f"validator source {relative}",
)
try:
target.relative_to(root)
except ValueError as exc:
raise SnapshotRestorePlanError("validator source escapes the repository") from exc
if target.stat().st_size != expected_size or file_sha256(target) != expected_sha:
raise SnapshotRestorePlanError(
f"repository validator source differs from the release manifest: {relative}"
)
def _bootstrap_release_package(
package_dir: Path,
expected_package_sha256: str,
*,
repo_root: Path,
) -> tuple[Path, dict[str, Any], str, dict[str, Any], str]:
root = _regular_directory(package_dir, label="release package directory")
package_path = _regular_file(root / PACKAGE_FILE, label="release package")
expected = _sha(expected_package_sha256, label="expected package SHA-256")
actual = file_sha256(package_path)
if actual != expected:
raise SnapshotRestorePlanError("release package does not match the approved SHA-256")
package = load_json(package_path, label="release package")
_embedded_hash(package, "package_payload_sha256", label="release package")
release_path, release_sha = _package_artifact(
package_path, package, "release_manifest"
)
release_manifest = load_json(release_path, label="release source manifest")
_validate_source_closure(release_manifest, repo_root=repo_root)
return root, package, actual, release_manifest, release_sha
def _load_release_package_validator(repo_root: Path) -> Callable[..., Any]:
root = _regular_directory(repo_root, label="repository root")
relative = "yuqei-infra/deploy/release-package.py"
path = _regular_file(
root.joinpath(*PurePosixPath(relative).parts),
label="official release-package validator",
)
expected_common = root / "yuqei-infra/scripts/linux_release_common.py"
ambient_common = sys.modules.get("linux_release_common")
if ambient_common is not None:
ambient_file = getattr(ambient_common, "__file__", None)
if ambient_file is None or Path(ambient_file).resolve() != expected_common.resolve():
raise SnapshotRestorePlanError("ambient linux_release_common module is forbidden")
module_name = "yuqei_first_release_official_release_package"
spec = importlib.util.spec_from_file_location(module_name, path)
if spec is None or spec.loader is None:
raise SnapshotRestorePlanError("official release-package validator cannot be loaded")
module = importlib.util.module_from_spec(spec)
sys.modules[module_name] = module
previous = sys.dont_write_bytecode
sys.dont_write_bytecode = True
try:
spec.loader.exec_module(module)
except Exception as exc:
sys.modules.pop(module_name, None)
raise SnapshotRestorePlanError("official release-package validator failed to load") from exc
finally:
sys.dont_write_bytecode = previous
validator = getattr(module, "validate_release_package", None)
if not callable(validator):
raise SnapshotRestorePlanError("official release-package validator entrypoint is missing")
return validator
def _validate_image_map(
value: object,
expected_keys: tuple[str, ...],
*,
label: str,
) -> dict[str, dict[str, str]]:
if not isinstance(value, dict) or set(value) != set(expected_keys):
raise SnapshotRestorePlanError(f"{label} does not cover the fixed image inventory")
result: dict[str, dict[str, str]] = {}
for key in expected_keys:
record = value.get(key)
if not isinstance(record, dict) or set(record) != {"reference", "digest"}:
raise SnapshotRestorePlanError(f"{label}.{key} identity is incomplete")
reference = record.get("reference")
digest = record.get("digest")
if (
not isinstance(reference, str)
or not IMAGE_REFERENCE_RE.fullmatch(reference)
or not isinstance(digest, str)
or not DIGEST_RE.fullmatch(digest)
or reference.rsplit("@", 1)[1] != digest
):
raise SnapshotRestorePlanError(f"{label}.{key} identity is invalid")
result[key] = {"reference": reference, "digest": digest}
return result
def _validate_offline_image_ids(
value: object,
*,
application_images: Mapping[str, Mapping[str, str]],
infrastructure_images: Mapping[str, Mapping[str, str]],
) -> dict[str, str]:
expected_images = {**application_images, **infrastructure_images}
if not isinstance(value, (list, tuple)) or len(value) != len(expected_images):
raise SnapshotRestorePlanError(
"official validator Docker import plan does not cover eleven images"
)
offline_image_ids: dict[str, str] = {}
for raw in value:
if not isinstance(raw, Mapping):
raise SnapshotRestorePlanError(
"official validator Docker import plan entry is invalid"
)
environment_key = raw.get("environment_key")
if not isinstance(environment_key, str) or environment_key not in expected_images:
raise SnapshotRestorePlanError(
"official validator Docker import plan image key is invalid"
)
if environment_key in offline_image_ids:
raise SnapshotRestorePlanError(
"official validator Docker import plan repeats an image key"
)
expected = expected_images[environment_key]
loaded_image_id = raw.get("expected_loaded_image_id")
if (
not isinstance(loaded_image_id, str)
or not DIGEST_RE.fullmatch(loaded_image_id)
or raw.get("offline_compose_override") != loaded_image_id
or raw.get("registry_reference_for_online_release")
!= expected["reference"]
or raw.get("expected_oci_manifest_digest") != expected["digest"]
):
raise SnapshotRestorePlanError(
"official validator Docker import plan identity drifted"
)
offline_image_ids[environment_key] = loaded_image_id
if set(offline_image_ids) != set(expected_images):
raise SnapshotRestorePlanError(
"official validator Docker import plan image inventory is incomplete"
)
return offline_image_ids
def _validate_package(
package_dir: Path,
expected_package_sha256: str,
*,
repo_root: Path,
package_validator: Callable[..., Any] | None = None,
require_installable_package_v2: bool = False,
) -> dict[str, Any]:
root, package, actual, bootstrapped_release, release_sha = _bootstrap_release_package(
package_dir,
expected_package_sha256,
repo_root=repo_root,
)
validator = package_validator or _load_release_package_validator(repo_root)
try:
validated = validator(
root,
expected_package_sha256=expected_package_sha256,
repo_root=_regular_directory(repo_root, label="repository root"),
)
except Exception as exc:
raise SnapshotRestorePlanError(
"official release-package validation rejected the candidate"
) from exc
if getattr(validated, "root", None) != root:
raise SnapshotRestorePlanError("official validator returned another package root")
if getattr(validated, "package_sha256", None) != actual:
raise SnapshotRestorePlanError("official validator package hash drifted")
validated_package = getattr(validated, "package", None)
package_schema = package.get("schema_version")
if (
validated_package != package
or package_schema not in READ_ONLY_PACKAGE_SCHEMAS
or (require_installable_package_v2 and package_schema != PACKAGE_SCHEMA)
):
raise SnapshotRestorePlanError("official validator package payload/schema drifted")
release_manifest = getattr(validated, "release_manifest", None)
if release_manifest != bootstrapped_release:
raise SnapshotRestorePlanError("official validator release source manifest drifted")
if getattr(validated, "release_manifest_sha256", None) != release_sha:
raise SnapshotRestorePlanError("official validator release source hash drifted")
bound_artifacts: dict[str, tuple[dict[str, Any], str]] = {}
for artifact_name, attribute, schema in (
("checksum_manifest", "checksum_manifest", CHECKSUM_MANIFEST_SCHEMA),
("image_identity", "image_identity", IMAGE_IDENTITY_SCHEMA),
("installable_manifest", "installable_manifest", INSTALLABLE_MANIFEST_SCHEMA),
):
path, digest = _package_artifact(root / PACKAGE_FILE, package, artifact_name)
raw = load_json(path, label=artifact_name.replace("_", " "))
if raw.get("schema_version") != schema or getattr(validated, attribute, None) != raw:
raise SnapshotRestorePlanError(f"official validator {artifact_name} drifted")
if getattr(validated, f"{attribute}_sha256", None) != digest:
raise SnapshotRestorePlanError(f"official validator {artifact_name} hash drifted")
bound_artifacts[artifact_name] = (raw, digest)
identity, image_sha = bound_artifacts["image_identity"]
_embedded_hash(identity, "identity_payload_sha256", label="image identity")
release_id = _identifier(package.get("release_id"), label="release_id")
checksum_manifest = bound_artifacts["checksum_manifest"][0]
if checksum_manifest.get("release_id") != release_id:
raise SnapshotRestorePlanError("checksum manifest release binding is invalid")
checksum_files = checksum_manifest.get("files")
if not isinstance(checksum_files, list):
raise SnapshotRestorePlanError("checksum manifest file inventory is missing")
checksums_by_path = {
item.get("path"): item
for item in checksum_files
if isinstance(item, dict) and isinstance(item.get("path"), str)
}
for artifact_name in ("release_manifest", "image_identity", "installable_manifest"):
artifact_record = package["artifacts"].get(artifact_name)
checksum_record = (
checksums_by_path.get(artifact_record.get("path"))
if isinstance(artifact_record, dict)
else None
)
if (
not isinstance(checksum_record, dict)
or checksum_record.get("sha256") != artifact_record.get("sha256")
):
raise SnapshotRestorePlanError(
f"checksum manifest does not bind {artifact_name}"
)
if identity.get("release_id") != release_id:
raise SnapshotRestorePlanError("image identity release differs from the package")
source_commit = identity.get("source_commit")
source_candidate = release_manifest.get("candidate")
if (
not isinstance(source_commit, str)
or not re.fullmatch(r"[0-9a-f]{40}|[0-9a-f]{64}", source_commit)
or not isinstance(source_candidate, dict)
or source_candidate.get("commit") != source_commit
or identity.get("source_manifest_sha256") != release_sha
):
raise SnapshotRestorePlanError("image identity source binding is invalid")
application_images = _validate_image_map(
identity.get("application_images"),
APPLICATION_IMAGE_KEYS,
label="application_images",
)
infrastructure_images = _validate_image_map(
identity.get("infrastructure_images"),
INFRASTRUCTURE_IMAGE_KEYS,
label="infrastructure_images",
)
if (
getattr(validated, "application_images", None) != application_images
or getattr(validated, "infrastructure_images", None) != infrastructure_images
):
raise SnapshotRestorePlanError("official validator eleven-image identity drifted")
offline_image_ids = _validate_offline_image_ids(
getattr(validated, "docker_import_plan", None),
application_images=application_images,
infrastructure_images=infrastructure_images,
)
installable = bound_artifacts["installable_manifest"][0]
installable_candidate = installable.get("candidate")
if not isinstance(installable_candidate, dict) or installable_candidate.get("commit") != source_commit:
raise SnapshotRestorePlanError("installable manifest candidate binding is invalid")
installable_images = installable.get("images")
if not isinstance(installable_images, list) or len(installable_images) != len(
APPLICATION_IMAGE_KEYS
):
raise SnapshotRestorePlanError("installable manifest four-image inventory is incomplete")
installable_by_key: dict[str, Mapping[str, Any]] = {}
for raw in installable_images:
if not isinstance(raw, dict) or not isinstance(raw.get("environment_key"), str):
raise SnapshotRestorePlanError("installable manifest image record is invalid")
key = raw["environment_key"]
if key in installable_by_key:
raise SnapshotRestorePlanError("installable manifest repeats an image key")
installable_by_key[key] = raw
if set(installable_by_key) != set(APPLICATION_IMAGE_KEYS):
raise SnapshotRestorePlanError("installable manifest four-image keys are incomplete")
for key, identity_record in application_images.items():
installable_record = installable_by_key[key]
if (
installable_record.get("registry_reference") != identity_record["reference"]
or installable_record.get("manifest_digest") != identity_record["digest"]
):
raise SnapshotRestorePlanError("installable/application image identity drifted")
recovery = identity.get("recovery")
recovery_fields = {
"strategy",
"evidence_status",
"first_release_only",
"predecessor_package_required",
"database_downgrade_allowed",
"required_restore_scopes",
"required_postgres_databases",
"required_storage_targets",
}
if not isinstance(recovery, dict) or set(recovery) != recovery_fields:
raise SnapshotRestorePlanError("package first-release recovery fields are incomplete")
if (
recovery.get("strategy") != RECOVERY_STRATEGY
or recovery.get("evidence_status") != RECOVERY_EVIDENCE_STATUS
or recovery.get("first_release_only") is not True
or recovery.get("predecessor_package_required") is not False
or recovery.get("database_downgrade_allowed") is not False
or recovery.get("required_restore_scopes") != list(REQUIRED_COMPONENTS)
or recovery.get("required_postgres_databases") != list(REQUIRED_DATABASES)
or recovery.get("required_storage_targets") != list(REQUIRED_STORAGE_TARGETS)
):
raise SnapshotRestorePlanError("package first-release recovery policy is unsafe")
trusted_receipt = _sha(
package.get("trusted_build_receipt_sha256"),
label="trusted build receipt SHA-256",
)
installable_receipt = installable.get("build_receipt")
if (
not isinstance(installable_receipt, dict)
or installable_receipt.get("sha256") != trusted_receipt
):
raise SnapshotRestorePlanError("installable trusted build receipt binding is invalid")
if getattr(validated, "trusted_build_receipt_sha256", None) != trusted_receipt:
raise SnapshotRestorePlanError("official validator trusted build receipt drifted")
return {
"release_id": release_id,
"package_schema_version": package_schema,
"package_sha256": actual,
"source_commit": source_commit,
"release_manifest_sha256": release_sha,
"checksum_manifest_sha256": bound_artifacts["checksum_manifest"][1],
"image_identity_sha256": image_sha,
"installable_manifest_sha256": bound_artifacts["installable_manifest"][1],
"trusted_build_receipt_sha256": trusted_receipt,
"compose_project_name": package.get("compose_project_name"),
"application_image_count": len(application_images),
"infrastructure_image_count": len(infrastructure_images),
"application_images": application_images,
"infrastructure_images": infrastructure_images,
"offline_image_ids": offline_image_ids,
}
def _validate_keyring(
keyring: Mapping[str, Any],
*,
root_public_key: Ed25519PublicKey,
now: datetime,
) -> dict[str, dict[str, Any]]:
_exact(
keyring,
{
"schema_version",
"root_key_id",
"epoch",
"issued_at",
"expires_at",
"keys",
"keyring_payload_sha256",
"root_signature_base64",
},
label="recovery keyring",
)
if keyring.get("schema_version") != KEYRING_SCHEMA:
raise SnapshotRestorePlanError("unsupported recovery keyring schema")
unsigned = {
key: keyring[key]
for key in (
"schema_version",
"root_key_id",
"epoch",
"issued_at",
"expires_at",
"keys",
)
}
embedded = _sha(
keyring.get("keyring_payload_sha256"), label="keyring payload hash"
)
if canonical_sha256(unsigned) != embedded:
raise SnapshotRestorePlanError("recovery keyring payload hash mismatch")
if not SHA256_RE.fullmatch(TRUSTED_CURRENT_KEYRING_PAYLOAD_SHA256):
raise SnapshotRestorePlanError(
"current first-release recovery keyring pin is not provisioned"
)
if (
not isinstance(MIN_KEYRING_EPOCH, int)
or isinstance(MIN_KEYRING_EPOCH, bool)
or MIN_KEYRING_EPOCH < 1
):
raise SnapshotRestorePlanError(
"minimum first-release recovery keyring epoch is not provisioned"
)
if keyring.get("root_key_id") != TRUSTED_ROOT_KEY_ID:
raise SnapshotRestorePlanError("recovery keyring uses the wrong root key")
try:
root_public_key.verify(
base64.b64decode(keyring.get("root_signature_base64"), validate=True),
canonical_json_bytes(unsigned),
)
except (TypeError, ValueError, InvalidSignature) as exc:
raise SnapshotRestorePlanError("recovery keyring root signature is invalid") from exc
epoch = keyring.get("epoch")
if not isinstance(epoch, int) or isinstance(epoch, bool) or epoch < 1:
raise SnapshotRestorePlanError("recovery keyring epoch is invalid")
if epoch < MIN_KEYRING_EPOCH:
raise SnapshotRestorePlanError("recovery keyring epoch is older than the trusted minimum")
if embedded != TRUSTED_CURRENT_KEYRING_PAYLOAD_SHA256:
raise SnapshotRestorePlanError(
"recovery keyring is not the currently trusted revocation state"
)
issued = _timestamp(keyring.get("issued_at"), label="keyring issued_at")
expires = _timestamp(keyring.get("expires_at"), label="keyring expires_at")
if issued > now + MAX_CLOCK_SKEW or now >= expires or expires <= issued:
raise SnapshotRestorePlanError("recovery keyring is expired or not yet valid")
raw_keys = keyring.get("keys")
if not isinstance(raw_keys, list) or not raw_keys:
raise SnapshotRestorePlanError("recovery keyring has insufficient keys")
result: dict[str, dict[str, Any]] = {}
public_keys: set[bytes] = set()
root_public_raw = root_public_key.public_bytes(
serialization.Encoding.Raw,
serialization.PublicFormat.Raw,
)
for raw in raw_keys:
item = _exact(
raw,
{
"key_id",
"approver_id",
"role",
"public_key_base64",
"not_before",
"not_after",
"revoked",
},
label="recovery key",
)
key_id = _identifier(item.get("key_id"), label="key_id")
if key_id in result:
raise SnapshotRestorePlanError("recovery keyring repeats a key_id")
if _identifier(item.get("approver_id"), label="approver_id") != SOLE_REVIEWER_ID:
raise SnapshotRestorePlanError("recovery keyring contains an unauthorized reviewer")
role = item.get("role")
if role not in REQUIRED_ROLES:
raise SnapshotRestorePlanError("recovery key has an unsupported role")
if item.get("revoked") is not False:
raise SnapshotRestorePlanError("revoked recovery keys are forbidden")
try:
public_raw = base64.b64decode(item.get("public_key_base64"), validate=True)
public_key = Ed25519PublicKey.from_public_bytes(public_raw)
except (TypeError, ValueError) as exc:
raise SnapshotRestorePlanError("recovery public key is invalid") from exc
if public_raw in public_keys:
raise SnapshotRestorePlanError(
"recovery keyring reuses an Ed25519 public key across recovery roles"
)
if public_raw == root_public_raw:
raise SnapshotRestorePlanError(
"recovery approval key must differ from the offline root key"
)
public_keys.add(public_raw)
not_before = _timestamp(item.get("not_before"), label="key not_before")
not_after = _timestamp(item.get("not_after"), label="key not_after")
if not_after <= not_before:
raise SnapshotRestorePlanError("recovery key validity window is invalid")
result[key_id] = {
**item,
"public_key": public_key,
"not_before_parsed": not_before,
"not_after_parsed": not_after,
}
return result
def _validate_no_prior_release(
value: object,
*,
environment_id: str,
release_id: str,
) -> tuple[dict[str, Any], str, datetime]:
payload, digest, _path = _external_record(value, label="no-prior-release receipt")
_exact(
payload,
{
"schema_version",
"environment_id",
"candidate_release_id",
"successful_release_count",
"status",
"observed_at",
"receipt_payload_sha256",
},
label="no-prior-release receipt",
)
if payload.get("schema_version") != NO_PRIOR_RELEASE_SCHEMA:
raise SnapshotRestorePlanError("unsupported no-prior-release receipt schema")
_embedded_hash(payload, "receipt_payload_sha256", label="no-prior-release receipt")
if (
payload.get("environment_id") != environment_id
or payload.get("candidate_release_id") != release_id
or payload.get("successful_release_count") != 0
or payload.get("status") != "confirmed_no_prior_successful_release"
):
raise SnapshotRestorePlanError("no-prior-release receipt does not prove a first release")
return payload, digest, _timestamp(payload.get("observed_at"), label="no-prior observed_at")
def _validate_snapshot_component(
value: object,
*,
host_id: str,
consistency_set_id: str,
) -> tuple[dict[str, Any], str, datetime]:
component = _exact(
value,
{"provider_receipt", "off_host", "immutable", "status"},
label="host_runtime_state",
)
if component.get("off_host") is not True or component.get("immutable") is not True or component.get("status") != "available":
raise SnapshotRestorePlanError("host snapshot must be off-host, immutable, and available")
receipt, digest, _path = _external_record(
component.get("provider_receipt"), label="host snapshot provider receipt"
)
_exact(
receipt,
{
"schema_version",
"provider",
"control_plane_id",
"snapshot_id",
"source_host_id",
"consistency_set_id",
"off_host",
"immutable",
"status",
"created_at",
"receipt_payload_sha256",
},
label="host snapshot provider receipt",
)
if receipt.get("schema_version") != SNAPSHOT_RECEIPT_SCHEMA:
raise SnapshotRestorePlanError("unsupported host snapshot receipt schema")
_embedded_hash(receipt, "receipt_payload_sha256", label="host snapshot receipt")
if (
receipt.get("source_host_id") != host_id
or receipt.get("consistency_set_id") != consistency_set_id
or receipt.get("off_host") is not True
or receipt.get("immutable") is not True
or receipt.get("status") != "available"
):
raise SnapshotRestorePlanError("host snapshot receipt does not match the recovery target")
_identifier(receipt.get("provider"), label="snapshot provider")
_identifier(receipt.get("control_plane_id"), label="snapshot control_plane_id")
_identifier(receipt.get("snapshot_id"), label="snapshot_id")
return receipt, digest, _timestamp(receipt.get("created_at"), label="snapshot created_at")
def _validate_source_backup_manifest_binding(
manifest: Mapping[str, Any],
*,
package_binding: Mapping[str, Any],
) -> tuple[dict[str, Any], str]:
source, source_sha, _source_path = _external_record(
manifest.get("source_backup_manifest"),
label="source artifacts.v2 backup manifest",
)
if (
source_sha != manifest.get("source_backup_manifest_sha256")
or source.get("schema_version") != SOURCE_BACKUP_MANIFEST_SCHEMA
or manifest.get("source_backup_schema") != SOURCE_BACKUP_MANIFEST_SCHEMA
):
raise SnapshotRestorePlanError(
"formal backup manifest source artifacts.v2 binding is invalid"
)
_exact(
source,
{
"schema_version",
"backup_id",
"created_at",
"release_id",
"compose_project_name",
"consistency_mode",
"database_names",
"images",
"object_storage_mirror",
"artifacts",
},
label="source artifacts.v2 backup manifest",
)
if (
source.get("backup_id") != manifest.get("backup_id")
or source.get("created_at") != manifest.get("completed_at")
or source.get("release_id") != manifest.get("release_id")
or source.get("consistency_mode") != "quiesced"
or source.get("compose_project_name")
!= package_binding.get("compose_project_name")
):
raise SnapshotRestorePlanError(
"source artifacts.v2 backup manifest differs from the recovery set"
)
_timestamp(source.get("created_at"), label="source backup created_at")
database_names = _exact(
source.get("database_names"),
{"contract", "ai_platform", "operations"},
label="source backup database names",
)
for value in database_names.values():
_identifier(value, label="source backup database name")
source_images = _exact(
source.get("images"),
{"contract_api", "ai_platform_api", "ai_worker"},
label="source backup application images",
)
expected_application = {
"CONTRACT_API_IMAGE": source_images["contract_api"],
"AI_PLATFORM_API_IMAGE": source_images["ai_platform_api"],
"AI_WORKER_IMAGE": source_images["ai_worker"],
}
if manifest.get("application_runtime_images") != expected_application:
raise SnapshotRestorePlanError(
"formal backup application images differ from source artifacts.v2"
)
object_storage = _exact(
source.get("object_storage_mirror"),
{
"engine",
"engine_version",
"engine_image",
"client",
"client_version",
"client_image",
"engine_runtime_image",
"engine_resolved_image_id",
"client_resolved_image_id",
"bucket",
"path",
"object_count",
"total_size_bytes",
"content_index_sha256",
},
label="source backup object-storage mirror",
)
if (
object_storage.get("engine") != "rustfs"
or object_storage.get("engine_version") != "1.0.0"
or object_storage.get("client") != "rustfs-rc"
or object_storage.get("client_version") != "0.1.36"
or object_storage.get("path") != "minio"
):
raise SnapshotRestorePlanError(
"source backup object-storage implementation is unsupported"
)
offline_runtime = isinstance(
object_storage.get("engine_runtime_image"), str
) and DIGEST_RE.fullmatch(str(object_storage["engine_runtime_image"]))
expected_object_storage = {
"MINIO_IMAGE": {
"candidate_reference": object_storage.get("engine_image"),
"runtime_reference": object_storage.get("engine_runtime_image"),
"resolved_image_id": object_storage.get("engine_resolved_image_id"),
},
"MINIO_MC_IMAGE": {
"candidate_reference": object_storage.get("client_image"),
"runtime_reference": (
object_storage.get("client_resolved_image_id")
if offline_runtime
else object_storage.get("client_image")
),
"resolved_image_id": object_storage.get("client_resolved_image_id"),
},
}
if manifest.get("object_storage_runtime_images") != expected_object_storage:
raise SnapshotRestorePlanError(
"formal backup object-storage images differ from source artifacts.v2"
)
raw_artifacts = source.get("artifacts")
if not isinstance(raw_artifacts, list) or not raw_artifacts:
raise SnapshotRestorePlanError("source backup artifact inventory is missing")
artifacts: dict[str, dict[str, Any]] = {}
folded_artifact_paths: set[str] = set()
for raw in raw_artifacts:
record = _exact(
raw,
{"path", "size_bytes", "sha256"},
label="source backup artifact",
)
relative = _safe_relative(record.get("path"), label="source backup artifact path")
size = record.get("size_bytes")
digest = record.get("sha256")
folded_relative = relative.casefold()
if (
relative in artifacts
or folded_relative in folded_artifact_paths
or not isinstance(size, int)
or isinstance(size, bool)
or size < 0
):
raise SnapshotRestorePlanError("source backup artifact inventory is invalid")
_sha(digest, label="source backup artifact SHA-256")
artifacts[relative] = dict(record)
folded_artifact_paths.add(folded_relative)
database_paths = {
"contract": "postgres-contract.dump",
"ai": "postgres-ai_platform.dump",
"operations": "postgres-operations.dump",
}
databases = manifest.get("databases")
if not isinstance(databases, Mapping):
raise SnapshotRestorePlanError("formal backup database inventory is missing")
for name, relative in database_paths.items():
artifact = artifacts.get(relative)
record = databases.get(name)
if (
not isinstance(artifact, Mapping)
or not isinstance(record, Mapping)
or record.get("artifact_sha256") != artifact.get("sha256")
or record.get("size_bytes") != artifact.get("size_bytes")
):
raise SnapshotRestorePlanError(
f"formal backup database {name} differs from source artifacts.v2"
)
contract_artifact = artifacts.get("contract-files.tar.gz")
contract_files = manifest.get("contract_files")
if (
not isinstance(contract_artifact, Mapping)
or not isinstance(contract_files, Mapping)
or contract_files.get("archive_sha256") != contract_artifact.get("sha256")
or contract_files.get("size_bytes") != contract_artifact.get("size_bytes")
):
raise SnapshotRestorePlanError(
"formal backup contract files differ from source artifacts.v2"
)
minio_records = [
record
for path, record in artifacts.items()
if path.startswith("minio/")
]
minio_records.sort(key=lambda item: str(item["path"]).encode("utf-8"))
minio = manifest.get("minio")
if (
not isinstance(minio, Mapping)
or minio.get("object_count") != len(minio_records)
or minio.get("total_size_bytes")
!= sum(int(item["size_bytes"]) for item in minio_records)
or minio.get("content_index_sha256") != canonical_sha256(minio_records)
or minio.get("object_count") != object_storage.get("object_count")
or minio.get("total_size_bytes") != object_storage.get("total_size_bytes")
or minio.get("content_index_sha256")
!= object_storage.get("content_index_sha256")
):
raise SnapshotRestorePlanError(
"formal backup object inventory differs from source artifacts.v2"
)
return source, source_sha
def _validate_formal_backup_image_binding(
manifest: Mapping[str, Any],
*,
package_binding: Mapping[str, Any],
) -> dict[str, Any]:
_source, source_backup_sha = _validate_source_backup_manifest_binding(
manifest,
package_binding=package_binding,
)
application_images = package_binding.get("application_images")
infrastructure_images = package_binding.get("infrastructure_images")
offline_image_ids = package_binding.get("offline_image_ids")
if (
not isinstance(application_images, Mapping)
or not isinstance(infrastructure_images, Mapping)
or not isinstance(offline_image_ids, Mapping)
):
raise SnapshotRestorePlanError(
"release package does not expose candidate runtime image identities"
)
application_runtime = _exact(
manifest.get("application_runtime_images"),
set(BACKUP_APPLICATION_IMAGE_KEYS),
label="backup application runtime images",
)
runtime_modes: set[str] = set()
normalized_application: dict[str, str] = {}
for key in BACKUP_APPLICATION_IMAGE_KEYS:
candidate = application_images.get(key)
offline_image_id = offline_image_ids.get(key)
runtime_identity = application_runtime.get(key)
if (
not isinstance(candidate, Mapping)
or not isinstance(candidate.get("reference"), str)
or not isinstance(offline_image_id, str)
or not DIGEST_RE.fullmatch(offline_image_id)
or not isinstance(runtime_identity, str)
or runtime_identity not in {candidate["reference"], offline_image_id}
):
raise SnapshotRestorePlanError(
f"backup application runtime image {key} differs from the release package"
)
runtime_modes.add(
"online" if runtime_identity == candidate["reference"] else "offline"
)
normalized_application[key] = runtime_identity
object_storage_runtime = _exact(
manifest.get("object_storage_runtime_images"),
set(BACKUP_OBJECT_STORAGE_IMAGE_KEYS),
label="backup object-storage runtime images",
)
normalized_object_storage: dict[str, dict[str, str]] = {}
for key in BACKUP_OBJECT_STORAGE_IMAGE_KEYS:
record = _exact(
object_storage_runtime.get(key),
{"candidate_reference", "runtime_reference", "resolved_image_id"},
label=f"backup object-storage runtime image {key}",
)
candidate = infrastructure_images.get(key)
offline_image_id = offline_image_ids.get(key)
if (
not isinstance(candidate, Mapping)
or not isinstance(candidate.get("reference"), str)
or record.get("candidate_reference") != candidate["reference"]
or not isinstance(offline_image_id, str)
or not DIGEST_RE.fullmatch(offline_image_id)
or record.get("resolved_image_id") != offline_image_id
or record.get("runtime_reference")
not in {candidate["reference"], offline_image_id}
):
raise SnapshotRestorePlanError(
f"backup object-storage runtime image {key} differs from the release package"
)
runtime_modes.add(
"online"
if record["runtime_reference"] == candidate["reference"]
else "offline"
)
normalized_object_storage[key] = dict(record)
if len(runtime_modes) != 1:
raise SnapshotRestorePlanError(
"backup runtime image identities mix online and offline release modes"
)
return {
"source_backup_manifest_sha256": source_backup_sha,
"runtime_mode": next(iter(runtime_modes)),
"application_runtime_images": normalized_application,
"object_storage_runtime_images": normalized_object_storage,
}
def _validate_backup_components(
postgres_value: object,
object_value: object,
*,
package_binding: Mapping[str, Any],
environment_id: str,
consistency_set_id: str,
require_formal_backup_manifest: bool = False,
) -> tuple[dict[str, Any], str, datetime]:
postgres = _exact(
postgres_value,
{"backup_manifest", "consistency_mode", "databases"},
label="postgres_consistent_backup",
)
if postgres.get("consistency_mode") != "quiesced" or postgres.get("databases") != list(REQUIRED_DATABASES):
raise SnapshotRestorePlanError("postgres backup must be quiesced and cover contract/ai/operations")
objects = _exact(
object_value,
{"backup_manifest", "stores"},
label="object_storage_state",
)
if objects.get("stores") != list(REQUIRED_STORAGE_TARGETS):
raise SnapshotRestorePlanError(
"object storage state must cover contract_files_volume and minio_object_store"
)
if objects.get("backup_manifest") != postgres.get("backup_manifest"):
raise SnapshotRestorePlanError("database and object storage must share one backup manifest")
manifest, digest, _path = _external_record(
postgres.get("backup_manifest"), label="consistent backup manifest"
)
schema_version = manifest.get("schema_version")
if schema_version not in READ_ONLY_BACKUP_MANIFEST_SCHEMAS:
raise SnapshotRestorePlanError("unsupported consistent backup manifest schema")
if require_formal_backup_manifest and schema_version != BACKUP_MANIFEST_SCHEMA:
raise SnapshotRestorePlanError(
"legacy v2 backup manifest is read-only and cannot mint new release evidence"
)
expected_fields = {
"schema_version",
"backup_id",
"release_id",
"environment_id",
"consistency_set_id",
"consistency_mode",
"databases",
"contract_files",
"minio",
"completed_at",
"status",
"manifest_payload_sha256",
}
if schema_version == BACKUP_MANIFEST_SCHEMA:
expected_fields.update(
{
"source_backup_schema",
"source_backup_manifest_sha256",
"source_backup_manifest",
"application_runtime_images",
"object_storage_runtime_images",
}
)
_exact(
manifest,
expected_fields,
label="consistent backup manifest",
)
_embedded_hash(manifest, "manifest_payload_sha256", label="consistent backup manifest")
release_id = _identifier(package_binding.get("release_id"), label="release_id")
if (
manifest.get("release_id") != release_id
or manifest.get("environment_id") != environment_id
or manifest.get("consistency_set_id") != consistency_set_id
or manifest.get("consistency_mode") != "quiesced"
or manifest.get("status") != "complete"
):
raise SnapshotRestorePlanError("consistent backup manifest is bound to another recovery set")
databases = manifest.get("databases")
if (
not isinstance(databases, dict)
or len(databases) != len(REQUIRED_DATABASES)
or set(databases) != set(REQUIRED_DATABASES)
):
raise SnapshotRestorePlanError("consistent backup manifest database inventory is incomplete")
for name in REQUIRED_DATABASES:
record = _exact(databases[name], {"artifact_sha256", "size_bytes", "schema_revision"}, label=f"{name} backup")
_sha(record.get("artifact_sha256"), label=f"{name} backup SHA-256")
if not isinstance(record.get("size_bytes"), int) or isinstance(record.get("size_bytes"), bool) or record["size_bytes"] <= 0:
raise SnapshotRestorePlanError(f"{name} backup size is invalid")
_identifier(record.get("schema_revision"), label=f"{name} schema revision")
contract_files = _exact(
manifest.get("contract_files"),
{"archive_sha256", "size_bytes", "file_count"},
label="contract_files backup",
)
_sha(contract_files.get("archive_sha256"), label="contract_files archive SHA-256")
minio = _exact(
manifest.get("minio"),
{"content_index_sha256", "object_count", "total_size_bytes"},
label="MinIO backup",
)
_sha(minio.get("content_index_sha256"), label="MinIO content index SHA-256")
for record, fields, label in (
(contract_files, ("size_bytes", "file_count"), "contract_files"),
(minio, ("object_count", "total_size_bytes"), "MinIO"),
):
if any(not isinstance(record.get(field), int) or isinstance(record.get(field), bool) or record[field] < 0 for field in fields):
raise SnapshotRestorePlanError(f"{label} backup counters are invalid")
if schema_version == BACKUP_MANIFEST_SCHEMA:
_validate_formal_backup_image_binding(
manifest,
package_binding=package_binding,
)
return manifest, digest, _timestamp(manifest.get("completed_at"), label="backup completed_at")
def _validate_bindings(value: object) -> dict[str, str]:
bindings = _exact(
value,
{
"production_env_sha256",
"tls_certificate_sha256",
"tls_private_key_sha256",
"tls_trusted_ca_sha256",
},
label="release_environment_and_tls_bindings",
)
return {key: _sha(bindings[key], label=key) for key in sorted(bindings)}
def _validate_restore_test(
value: object,
*,
release_id: str,
environment_id: str,
consistency_set_id: str,
snapshot_receipt_sha256: str,
backup_manifest_sha256: str,
bindings_sha256: str,
) -> tuple[dict[str, Any], str, datetime]:
payload, digest, _path = _external_record(value, label="independent restore test")
_exact(
payload,
{
"schema_version",
"release_id",
"source_environment_id",
"restore_environment_id",
"restore_environment_class",
"consistency_set_id",
"snapshot_receipt_sha256",
"backup_manifest_sha256",
"release_environment_and_tls_bindings_sha256",
"host_snapshot_restored",
"databases_restored",
"contract_files_restored",
"minio_restored",
"environment_tls_bindings_verified",
"database_downgrade_performed",
"status",
"tested_at",
"evidence_payload_sha256",
},
label="independent restore test",
)
if payload.get("schema_version") != RESTORE_TEST_SCHEMA:
raise SnapshotRestorePlanError("unsupported independent restore-test schema")
_embedded_hash(payload, "evidence_payload_sha256", label="independent restore test")
if (
payload.get("release_id") != release_id
or payload.get("source_environment_id") != environment_id
or payload.get("restore_environment_id") == environment_id
or payload.get("restore_environment_class") != "isolated_uat"
or payload.get("consistency_set_id") != consistency_set_id
or payload.get("snapshot_receipt_sha256") != snapshot_receipt_sha256
or payload.get("backup_manifest_sha256") != backup_manifest_sha256
or payload.get("release_environment_and_tls_bindings_sha256")
!= bindings_sha256
or payload.get("host_snapshot_restored") is not True
or payload.get("databases_restored") != list(REQUIRED_DATABASES)
or payload.get("contract_files_restored") is not True
or payload.get("minio_restored") is not True
or payload.get("environment_tls_bindings_verified") is not True
or payload.get("database_downgrade_performed") is not False
or payload.get("status") != "passed"
):
raise SnapshotRestorePlanError("independent restore test is incomplete or mismatched")
_identifier(payload.get("restore_environment_id"), label="restore environment_id")
return payload, digest, _timestamp(payload.get("tested_at"), label="restore tested_at")
def _validate_approvals(
value: object,
*,
evidence_payload_sha256: str,
trusted_keys: Mapping[str, Mapping[str, Any]],
evidence_not_before: datetime,
now: datetime,
) -> list[dict[str, Any]]:
if not isinstance(value, list) or len(value) != 1:
raise SnapshotRestorePlanError("exactly one recovery approval is required")
roles: set[str] = set()
people: set[str] = set()
key_ids: set[str] = set()
approval_ids: set[str] = set()
approvals: list[dict[str, Any]] = []
fields = {
"schema_version",
"approval_id",
"approver_id",
"role",
"key_id",
"signed_at",
"expires_at",
"evidence_payload_sha256",
"signature_base64",
}
for raw in value:
item = _exact(raw, fields, label="recovery approval")
if item.get("schema_version") != APPROVAL_SCHEMA:
raise SnapshotRestorePlanError("unsupported recovery approval schema")
approval_id = _identifier(item.get("approval_id"), label="approval_id")
if approval_id in approval_ids:
raise SnapshotRestorePlanError("recovery approvals repeat an approval_id")
approval_ids.add(approval_id)
person = _identifier(item.get("approver_id"), label="approver_id")
if person != SOLE_REVIEWER_ID:
raise SnapshotRestorePlanError("recovery approval uses the wrong named reviewer")
role = item.get("role")
if role not in REQUIRED_ROLES:
raise SnapshotRestorePlanError("recovery approval role is invalid")
key_id = _identifier(item.get("key_id"), label="approval key_id")
key = trusted_keys.get(key_id)
if key is None or key["approver_id"] != person or key["role"] != role:
raise SnapshotRestorePlanError("recovery approval does not match a trusted key")
if item.get("evidence_payload_sha256") != evidence_payload_sha256:
raise SnapshotRestorePlanError("recovery approval is bound to different evidence")
signed_at = _timestamp(item.get("signed_at"), label="approval signed_at")
expires_at = _timestamp(item.get("expires_at"), label="approval expires_at")
if (
signed_at < evidence_not_before
or signed_at > now + MAX_CLOCK_SKEW
or expires_at <= signed_at
or now >= expires_at
or signed_at < key["not_before_parsed"]
or expires_at > key["not_after_parsed"]
):
raise SnapshotRestorePlanError("recovery approval validity window is invalid")
signed = {field: item[field] for field in item if field != "signature_base64"}
try:
signature = base64.b64decode(item.get("signature_base64"), validate=True)
key["public_key"].verify(signature, canonical_json_bytes(signed))
except (TypeError, ValueError, InvalidSignature) as exc:
raise SnapshotRestorePlanError("recovery approval signature is invalid") from exc
roles.add(str(role))
people.add(person)
key_ids.add(key_id)
approvals.append(item)
if (
roles != REQUIRED_ROLES
or len(people) != REQUIRED_NAMED_REVIEWERS
or len(key_ids) != len(REQUIRED_ROLES)
):
raise SnapshotRestorePlanError(
"recovery approval requires the sole release owner and one operational key"
)
return approvals
def validate_evidence(
evidence: Mapping[str, Any],
*,
package_binding: Mapping[str, Any],
keyring: Mapping[str, Any],
trusted_root_public_key: Ed25519PublicKey,
now: datetime,
require_formal_backup_manifest: bool = False,
) -> dict[str, Any]:
if now.tzinfo is None:
raise SnapshotRestorePlanError("validation time must include a timezone")
current = now.astimezone(UTC)
_exact(
evidence,
{
"schema_version",
"evidence_id",
"mode",
"release_binding",
"target",
"consistency_set_id",
"captured_at",
"expires_at",
"no_prior_successful_release_receipt",
"components",
"independent_restore_test",
"evidence_payload_sha256",
"approvals",
},
label="first-release recovery evidence",
)
if evidence.get("schema_version") != EVIDENCE_SCHEMA or evidence.get("mode") != "FIRST_RELEASE_PREINSTALL_SNAPSHOT_RESTORE":
raise SnapshotRestorePlanError("unsupported first-release recovery evidence")
evidence_id = _identifier(evidence.get("evidence_id"), label="evidence_id")
release_binding = _exact(
evidence.get("release_binding"),
{"release_id", "package_sha256", "source_commit", "image_identity_sha256"},
label="release binding",
)
if release_binding != {
key: package_binding[key]
for key in ("release_id", "package_sha256", "source_commit", "image_identity_sha256")
}:
raise SnapshotRestorePlanError("recovery evidence is bound to a different package")
target = _exact(
evidence.get("target"),
{
"environment_id",
"environment_class",
"host_id",
"host_identity_sha256",
"docker_daemon_identity_sha256",
"compose_project_name",
},
label="recovery target",
)
environment_id = _identifier(target.get("environment_id"), label="environment_id")
host_id = _identifier(target.get("host_id"), label="host_id")
if target.get("environment_class") not in {"isolated_uat", "production"}:
raise SnapshotRestorePlanError("recovery target environment class is invalid")
if target.get("compose_project_name") != package_binding.get("compose_project_name"):
raise SnapshotRestorePlanError("recovery target Compose project differs from the package")
_sha(target.get("host_identity_sha256"), label="host identity SHA-256")
_sha(
target.get("docker_daemon_identity_sha256"),
label="Docker daemon identity SHA-256",
)
consistency_set_id = _identifier(
evidence.get("consistency_set_id"), label="consistency_set_id"
)
captured_at = _timestamp(evidence.get("captured_at"), label="captured_at")
expires_at = _timestamp(evidence.get("expires_at"), label="expires_at")
if (
captured_at > current + MAX_CLOCK_SKEW
or current - captured_at > MAX_EVIDENCE_AGE
or expires_at <= captured_at
or current >= expires_at
or expires_at - captured_at > MAX_EVIDENCE_AGE
):
raise SnapshotRestorePlanError("first-release recovery evidence is stale or expired")
no_prior, no_prior_sha, no_prior_at = _validate_no_prior_release(
evidence.get("no_prior_successful_release_receipt"),
environment_id=environment_id,
release_id=package_binding["release_id"],
)
components = _exact(
evidence.get("components"), set(REQUIRED_COMPONENTS), label="recovery components"
)
snapshot, snapshot_sha, snapshot_at = _validate_snapshot_component(
components["host_runtime_state"],
host_id=host_id,
consistency_set_id=consistency_set_id,
)
backup, backup_sha, backup_at = _validate_backup_components(
components["postgres_consistent_backup"],
components["object_storage_state"],
package_binding=package_binding,
environment_id=environment_id,
consistency_set_id=consistency_set_id,
require_formal_backup_manifest=require_formal_backup_manifest,
)
bindings = _validate_bindings(components["release_environment_and_tls_bindings"])
bindings_sha = canonical_sha256(bindings)
restore, restore_sha, restored_at = _validate_restore_test(
evidence.get("independent_restore_test"),
release_id=package_binding["release_id"],
environment_id=environment_id,
consistency_set_id=consistency_set_id,
snapshot_receipt_sha256=snapshot_sha,
backup_manifest_sha256=backup_sha,
bindings_sha256=bindings_sha,
)
evidence_times = (no_prior_at, snapshot_at, backup_at, restored_at)
if any(item > current + MAX_CLOCK_SKEW for item in evidence_times):
raise SnapshotRestorePlanError("recovery component timestamp is in the future")
if any(current - item > MAX_EVIDENCE_AGE for item in evidence_times):
raise SnapshotRestorePlanError("recovery component evidence is stale")
if restored_at < max(snapshot_at, backup_at) or captured_at < max(evidence_times):
raise SnapshotRestorePlanError("restore test or capture predates the bound snapshot/backup")
unsigned = {
key: evidence[key]
for key in evidence
if key not in {"evidence_payload_sha256", "approvals"}
}
evidence_hash = _sha(
evidence.get("evidence_payload_sha256"), label="evidence payload hash"
)
if canonical_sha256(unsigned) != evidence_hash:
raise SnapshotRestorePlanError("first-release evidence payload hash mismatch")
trusted_keys = _validate_keyring(
keyring, root_public_key=trusted_root_public_key, now=current
)
approvals = _validate_approvals(
evidence.get("approvals"),
evidence_payload_sha256=evidence_hash,
trusted_keys=trusted_keys,
evidence_not_before=max(captured_at, no_prior_at, snapshot_at, backup_at, restored_at),
now=current,
)
return {
"evidence_id": evidence_id,
"evidence_payload_sha256": evidence_hash,
"release_binding": release_binding,
"target": target,
"consistency_set_id": consistency_set_id,
"captured_at": evidence["captured_at"],
"expires_at": evidence["expires_at"],
"freshness": {
"maximum_age_seconds": int(MAX_EVIDENCE_AGE.total_seconds()),
"no_prior_observed_at": no_prior["observed_at"],
"snapshot_created_at": snapshot["created_at"],
"backup_completed_at": backup["completed_at"],
"restore_tested_at": restore["tested_at"],
},
"no_prior_release_receipt_sha256": no_prior_sha,
"snapshot": {
"provider": snapshot["provider"],
"control_plane_id": snapshot["control_plane_id"],
"snapshot_id": snapshot["snapshot_id"],
"provider_receipt_sha256": snapshot_sha,
},
"backup": {
"backup_id": backup["backup_id"],
"schema_version": backup["schema_version"],
"manifest_sha256": backup_sha,
"source_backup_manifest_sha256": backup.get(
"source_backup_manifest_sha256"
),
"databases": list(REQUIRED_DATABASES),
"storage_targets": list(REQUIRED_STORAGE_TARGETS),
},
"bindings": {**bindings, "bindings_payload_sha256": bindings_sha},
"restore_test": {
"restore_environment_id": restore["restore_environment_id"],
"evidence_sha256": restore_sha,
"tested_at": restore["tested_at"],
},
"reviewer": {
"approver_id": SOLE_REVIEWER_ID,
"name": SOLE_REVIEWER_NAME,
"title": SOLE_REVIEWER_TITLE,
},
"approvals": approvals,
"keyring": {
"epoch": keyring["epoch"],
"payload_sha256": keyring["keyring_payload_sha256"],
"expires_at": keyring["expires_at"],
},
}
def build_plan(
*,
package_binding: Mapping[str, Any],
validated_evidence: Mapping[str, Any],
trusted_root_public_key_sha256: str,
generated_at: datetime,
) -> dict[str, Any]:
if generated_at.tzinfo is None:
raise SnapshotRestorePlanError("plan time must include a timezone")
unsigned: dict[str, Any] = {
"schema_version": PLAN_SCHEMA,
"status": "signature_verified_against_frozen_pins_plan_only",
"generated_at": generated_at.astimezone(UTC).isoformat().replace("+00:00", "Z"),
"mode": "FIRST_RELEASE_PREINSTALL_SNAPSHOT_RESTORE",
"release_binding": dict(validated_evidence["release_binding"]),
"target": dict(validated_evidence["target"]),
"consistency_set_id": validated_evidence["consistency_set_id"],
"evidence_id": validated_evidence["evidence_id"],
"evidence_payload_sha256": validated_evidence["evidence_payload_sha256"],
"trusted_root_public_key_sha256": _sha(
trusted_root_public_key_sha256, label="trusted root public key SHA-256"
),
"keyring": dict(validated_evidence["keyring"]),
"reviewer": dict(validated_evidence["reviewer"]),
"approval_ids": sorted(
item["approval_id"] for item in validated_evidence["approvals"]
),
"snapshot": dict(validated_evidence["snapshot"]),
"backup": dict(validated_evidence["backup"]),
"bindings": dict(validated_evidence["bindings"]),
"independent_restore_test": dict(validated_evidence["restore_test"]),
"restore_sequence": [
"obtain_separate_destructive_restore_authorization",
"confirm_no_public_traffic_or_confirm_old_service_switchback",
"isolate_and_power_off_exact_target_host",
"restore_immutable_host_runtime_snapshot_from_external_control_plane",
"verify_contract_ai_operations_database_state",
"verify_contract_files_and_minio_state",
"verify_release_environment_and_tls_hash_bindings",
"run_health_and_main_journey_acceptance",
],
"required_components": list(REQUIRED_COMPONENTS),
"database_downgrade_allowed": False,
"fake_predecessor_package_allowed": False,
"execution_entrypoint_available": False,
"apply_allowed": False,
"writes_performed": False,
"docker_invoked": False,
"remote_connection_attempted": False,
"traffic_or_dns_change_performed": False,
"claims_not_made": [
"No snapshot was created or restored by this plan-only command.",
"Temporary test keys do not establish a production trust anchor.",
"This plan is not UAT, production, pilot, public-release, or legal approval.",
],
}
unsigned["plan_payload_sha256"] = canonical_sha256(unsigned)
return unsigned
def build_install_readiness_receipt(
*,
package_binding: Mapping[str, Any],
validated_evidence: Mapping[str, Any],
trusted_root_public_key_sha256: str,
generated_at: datetime,
) -> dict[str, Any]:
if generated_at.tzinfo is None:
raise SnapshotRestorePlanError("receipt time must include a timezone")
backup = validated_evidence.get("backup")
if (
not isinstance(backup, Mapping)
or backup.get("schema_version") != BACKUP_MANIFEST_SCHEMA
or not isinstance(backup.get("source_backup_manifest_sha256"), str)
):
raise SnapshotRestorePlanError(
"formal install readiness requires a verified v3 backup manifest"
)
approval_expiries = [
_timestamp(item["expires_at"], label="approval expires_at")
for item in validated_evidence["approvals"]
]
freshness_expiries = [
_timestamp(value, label=f"freshness {key}") + MAX_EVIDENCE_AGE
for key, value in validated_evidence["freshness"].items()
if key.endswith("_at")
]
expires_at = min(
_timestamp(validated_evidence["expires_at"], label="evidence expires_at"),
_timestamp(
validated_evidence["keyring"]["expires_at"],
label="keyring expires_at",
),
*approval_expiries,
*freshness_expiries,
)
moment = generated_at.astimezone(UTC)
if moment >= expires_at:
raise SnapshotRestorePlanError("recovery readiness receipt would already be expired")
release_binding = {
key: package_binding[key]
for key in (
"release_id",
"package_sha256",
"source_commit",
"release_manifest_sha256",
"checksum_manifest_sha256",
"image_identity_sha256",
"installable_manifest_sha256",
"trusted_build_receipt_sha256",
)
}
unsigned: dict[str, Any] = {
"schema_version": READINESS_RECEIPT_SCHEMA,
"result": "PASS_RECOVERY_READINESS",
"purpose": "release_package_install_apply_gate",
"install_apply_gate": True,
"snapshot_restore_apply_allowed": False,
"generated_at": moment.isoformat().replace("+00:00", "Z"),
"expires_at": expires_at.isoformat().replace("+00:00", "Z"),
"release_binding": release_binding,
"target": dict(validated_evidence["target"]),
"image_inventory": {
"application_image_count": package_binding["application_image_count"],
"infrastructure_image_count": package_binding["infrastructure_image_count"],
"total_image_count": package_binding["application_image_count"]
+ package_binding["infrastructure_image_count"],
},
"evidence": {
"evidence_id": validated_evidence["evidence_id"],
"evidence_payload_sha256": validated_evidence["evidence_payload_sha256"],
"evidence_artifact_sha256": validated_evidence["raw_artifact_sha256"][
"evidence"
],
"captured_at": validated_evidence["captured_at"],
"expires_at": validated_evidence["expires_at"],
"freshness": dict(validated_evidence["freshness"]),
"no_prior_successful_release_receipt_sha256": validated_evidence[
"no_prior_release_receipt_sha256"
],
},
"trust": {
"root_key_id": TRUSTED_ROOT_KEY_ID,
"trusted_root_public_key_sha256": _sha(
trusted_root_public_key_sha256,
label="trusted root public key SHA-256",
),
"keyring_epoch": validated_evidence["keyring"]["epoch"],
"keyring_payload_sha256": validated_evidence["keyring"]["payload_sha256"],
"keyring_artifact_sha256": validated_evidence["raw_artifact_sha256"][
"keyring"
],
},
"reviewer": dict(validated_evidence["reviewer"]),
"approvals": [dict(item) for item in validated_evidence["approvals"]],
"snapshot": dict(validated_evidence["snapshot"]),
"backup": dict(validated_evidence["backup"]),
"independent_restore_test": dict(validated_evidence["restore_test"]),
"required_restore_scopes": list(REQUIRED_COMPONENTS),
"required_postgres_databases": list(REQUIRED_DATABASES),
"required_storage_targets": list(REQUIRED_STORAGE_TARGETS),
"release_environment_and_tls_bindings": dict(
validated_evidence["bindings"]
),
"claims_not_made": [
"This receipt does not authorize or execute snapshot restoration.",
"This receipt does not authorize traffic, DNS, UAT, pilot, production, or public release.",
"The release-package install gate must revalidate the raw package and evidence inputs before use.",
],
}
unsigned["receipt_payload_sha256"] = canonical_sha256(unsigned)
return unsigned
def _read_root_public_key(path: Path) -> tuple[Ed25519PublicKey, str]:
if not SHA256_RE.fullmatch(TRUSTED_ROOT_PUBLIC_KEY_SHA256):
raise SnapshotRestorePlanError(
"organization first-release recovery root key is not provisioned"
)
target = _regular_file(path, label="trusted root public key", max_bytes=32)
raw = target.read_bytes()
if len(raw) != 32:
raise SnapshotRestorePlanError("trusted root public key must contain 32 raw bytes")
actual = hashlib.sha256(raw).hexdigest()
if actual != TRUSTED_ROOT_PUBLIC_KEY_SHA256:
raise SnapshotRestorePlanError(
"trusted root public key does not match the frozen trust anchor"
)
try:
return Ed25519PublicKey.from_public_bytes(raw), actual
except ValueError as exc:
raise SnapshotRestorePlanError("trusted root public key is invalid") from exc
def _verify_raw_inputs(
*,
package_dir: Path,
expected_package_sha256: str,
repo_root: Path,
evidence_file: Path,
keyring_file: Path,
trusted_root_public_key_file: Path,
now: datetime,
package_validator_for_tests: Callable[..., Any] | None = None,
require_installable_package_v2: bool = False,
require_formal_backup_manifest: bool = False,
) -> tuple[dict[str, Any], dict[str, Any], str]:
package_binding = _validate_package(
package_dir,
expected_package_sha256,
repo_root=repo_root,
package_validator=package_validator_for_tests,
require_installable_package_v2=require_installable_package_v2,
)
evidence = load_json(evidence_file, label="first-release recovery evidence")
keyring = load_json(keyring_file, label="recovery keyring")
root_key, root_sha = _read_root_public_key(trusted_root_public_key_file)
validated = validate_evidence(
evidence,
package_binding=package_binding,
keyring=keyring,
trusted_root_public_key=root_key,
now=now,
require_formal_backup_manifest=require_formal_backup_manifest,
)
validated["raw_artifact_sha256"] = {
"evidence": file_sha256(
_regular_file(evidence_file, label="first-release recovery evidence")
),
"keyring": file_sha256(_regular_file(keyring_file, label="recovery keyring")),
"trusted_root_public_key": root_sha,
}
return package_binding, validated, root_sha
def verify_and_build_output(
*,
package_dir: Path,
expected_package_sha256: str,
repo_root: Path,
evidence_file: Path,
keyring_file: Path,
trusted_root_public_key_file: Path,
output_kind: str,
now: datetime,
package_validator_for_tests: Callable[..., Any] | None = None,
) -> dict[str, Any]:
"""Revalidate every raw input and return one non-writing output document.
``package_validator_for_tests`` exists only for isolated unit fixtures.
The CLI never exposes or supplies it and therefore always loads the
source-bound official validator and its complete trust-input closure.
"""
if output_kind not in {"plan", "install-readiness-receipt"}:
raise SnapshotRestorePlanError("unsupported output kind")
package_binding, validated, root_sha = _verify_raw_inputs(
package_dir=package_dir,
expected_package_sha256=expected_package_sha256,
repo_root=repo_root,
evidence_file=evidence_file,
keyring_file=keyring_file,
trusted_root_public_key_file=trusted_root_public_key_file,
now=now,
package_validator_for_tests=package_validator_for_tests,
require_installable_package_v2=(output_kind == "install-readiness-receipt"),
require_formal_backup_manifest=(output_kind == "install-readiness-receipt"),
)
if output_kind == "plan":
return build_plan(
package_binding=package_binding,
validated_evidence=validated,
trusted_root_public_key_sha256=root_sha,
generated_at=now,
)
return build_install_readiness_receipt(
package_binding=package_binding,
validated_evidence=validated,
trusted_root_public_key_sha256=root_sha,
generated_at=now,
)
def validate_install_readiness(
*,
package_file: Path,
expected_package_sha256: str,
evidence_file: Path,
keyring_file: Path,
trusted_root_public_key_file: Path,
receipt_file: Path,
expected_receipt_sha256: str,
now: datetime,
) -> dict[str, Any]:
"""Rebuild and verify an install-gate receipt from every raw input.
This adapter is intentionally read-only and is the only receipt interface
intended for the release-package install gate. It never trusts a receipt
without re-running the official package validator and all recovery checks.
"""
package_path = _regular_file(package_file, label="release package")
if package_path.name != PACKAGE_FILE:
raise SnapshotRestorePlanError(f"package file must be named {PACKAGE_FILE}")
receipt_path = _regular_file(receipt_file, label="recovery readiness receipt")
expected = _sha(expected_receipt_sha256, label="expected readiness receipt SHA-256")
if file_sha256(receipt_path) != expected:
raise SnapshotRestorePlanError("recovery readiness receipt SHA-256 mismatch")
receipt = load_json(receipt_path, label="recovery readiness receipt")
_embedded_hash(receipt, "receipt_payload_sha256", label="recovery readiness receipt")
canonical = canonical_json_bytes(receipt)
if receipt_path.read_bytes() not in {canonical, canonical + b"\n"}:
raise SnapshotRestorePlanError("recovery readiness receipt is not canonical JSON")
if (
receipt.get("schema_version") != READINESS_RECEIPT_SCHEMA
or receipt.get("result") != "PASS_RECOVERY_READINESS"
or receipt.get("purpose") != "release_package_install_apply_gate"
or receipt.get("install_apply_gate") is not True
or receipt.get("snapshot_restore_apply_allowed") is not False
):
raise SnapshotRestorePlanError("document is not an install readiness receipt")
if now.tzinfo is None:
raise SnapshotRestorePlanError("validation time must include a timezone")
if now.astimezone(UTC) >= _timestamp(receipt.get("expires_at"), label="receipt expires_at"):
raise SnapshotRestorePlanError("recovery readiness receipt is expired")
generated_at = _timestamp(receipt.get("generated_at"), label="receipt generated_at")
if generated_at > now.astimezone(UTC) + MAX_CLOCK_SKEW:
raise SnapshotRestorePlanError("recovery readiness receipt is from the future")
package_binding, validated, root_sha = _verify_raw_inputs(
package_dir=package_path.parent,
expected_package_sha256=expected_package_sha256,
repo_root=REPO_ROOT,
evidence_file=evidence_file,
keyring_file=keyring_file,
trusted_root_public_key_file=trusted_root_public_key_file,
now=now,
require_installable_package_v2=True,
require_formal_backup_manifest=True,
)
rebuilt = build_install_readiness_receipt(
package_binding=package_binding,
validated_evidence=validated,
trusted_root_public_key_sha256=root_sha,
generated_at=generated_at,
)
if rebuilt != receipt:
raise SnapshotRestorePlanError(
"recovery readiness receipt differs from revalidated raw inputs"
)
return rebuilt
def build_parser() -> argparse.ArgumentParser:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--package-dir", type=Path, required=True)
parser.add_argument("--expected-package-sha256", required=True)
parser.add_argument("--repo-root", type=Path, required=True)
parser.add_argument("--evidence", type=Path, required=True)
parser.add_argument("--keyring", type=Path, required=True)
parser.add_argument("--trusted-root-public-key", type=Path, required=True)
parser.add_argument(
"--output-kind",
choices=("plan", "install-readiness-receipt"),
default="plan",
)
return parser
def main(argv: list[str] | None = None) -> int:
args = build_parser().parse_args(argv)
try:
now = datetime.now(UTC)
output = verify_and_build_output(
package_dir=args.package_dir,
expected_package_sha256=args.expected_package_sha256,
repo_root=args.repo_root,
evidence_file=args.evidence,
keyring_file=args.keyring,
trusted_root_public_key_file=args.trusted_root_public_key,
output_kind=args.output_kind,
now=now,
)
except (OSError, SnapshotRestorePlanError) as exc:
schema = (
READINESS_RECEIPT_SCHEMA
if args.output_kind == "install-readiness-receipt"
else PLAN_SCHEMA
)
print(
canonical_json_bytes(
{"schema_version": schema, "status": "rejected", "error": str(exc)}
).decode("utf-8"),
file=sys.stderr,
)
return 2
print(canonical_json_bytes(output).decode("utf-8"))
return 0
if __name__ == "__main__":
raise SystemExit(main())