#!/usr/bin/env python3 """Security boundary for one-approval silent release execution. The signed plan binds immutable release evidence. Each capability is consumed independently so runtime deployment, tag publication, and release finalization remain separate controllers and separately recoverable Saga steps. """ from __future__ import annotations import base64 import hashlib import json import os import re import tempfile import time import uuid from dataclasses import asdict, dataclass, field from datetime import datetime, timezone from enum import Enum from pathlib import Path from typing import Any, Callable, Iterable, Mapping, Protocol from cryptography.exceptions import InvalidSignature from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PublicKey from silent_release_controller import ( CANDIDATE_ID_RE, OID_RE, SHA256_RE, ReleaseCandidate, ReleaseCandidateStore, _atomic_json, _parse_utc, _sanitize, utc_now, ) IDENTIFIER_RE = re.compile(r"^[A-Za-z0-9][A-Za-z0-9_.:-]{2,255}$") NONCE_RE = re.compile(r"^[A-Za-z0-9][A-Za-z0-9_.:-]{15,255}$") ALLOWED_CAPABILITIES = frozenset( { "runtime_switch", "tag_publish", "baseline_registry_finalize", } ) FORBIDDEN_CAPABILITIES = frozenset( { "force_push", "tag_rewrite", "direct_main_push", "baseline_mutation", "registry_mutation", } ) class CapabilityAuthorizationError(RuntimeError): """The signed plan cannot authorize the requested operation.""" class StaleApprovalError(CapabilityAuthorizationError): """Live release evidence no longer matches the signed approval.""" class SagaConflict(RuntimeError): """A stale controller attempted a non-current Saga transition.""" class JournalIntegrityError(RuntimeError): """The durable Saga journal is truncated, reordered, or modified.""" class ClaimProbeResult(str, Enum): NOT_APPLIED = "NOT_APPLIED" APPLIED_VERIFIED = "APPLIED_VERIFIED" INDETERMINATE = "INDETERMINATE" CONFLICT = "CONFLICT" JOURNAL_SCHEMA_VERSION = 1 JOURNAL_GENESIS_HASH = "0" * 64 def _fsync_directory(path: Path) -> None: descriptor = os.open(Path(path), os.O_RDONLY) try: os.fsync(descriptor) finally: os.close(descriptor) def _durable_json(path: Path, payload: Mapping[str, Any]) -> None: _atomic_json(path, dict(payload)) _fsync_directory(path.parent) def _validate_identifier(name: str, value: str) -> str: normalized = str(value or "") if not IDENTIFIER_RE.fullmatch(normalized): raise ValueError(f"invalid {name}") return normalized def _validate_oid(name: str, value: str) -> str: normalized = str(value or "").lower() if not OID_RE.fullmatch(normalized): raise ValueError(f"{name} must be a full 40-character OID") return normalized def _validate_sha(name: str, value: str) -> str: normalized = str(value or "").lower() if not SHA256_RE.fullmatch(normalized): raise ValueError(f"{name} must be a SHA-256 digest") return normalized def _canonical_json(payload: Mapping[str, Any]) -> bytes: return json.dumps( payload, sort_keys=True, separators=(",", ":"), ensure_ascii=True, ).encode("utf-8") def _safe_child(root: Path, name: str, suffix: str = ".json") -> Path: _validate_identifier("storage key", name) resolved_root = Path(root).resolve() candidate = (resolved_root / f"{name}{suffix}").resolve() if candidate.parent != resolved_root: raise ValueError("storage path escapes root") return candidate @dataclass(frozen=True) class DeploymentPlan: service: str release_path: str release_root: str dropin_path: str dropin_sha256: str rollback_path: str rollback_sha256: str source_commit: str artifact_sha256: str manifest_sha256: str policy_sha256: str health_contract: dict[str, Any] def __post_init__(self) -> None: _validate_identifier("service", self.service) _validate_oid("source_commit", self.source_commit) for name in ( "dropin_sha256", "rollback_sha256", "artifact_sha256", "manifest_sha256", "policy_sha256", ): _validate_sha(name, getattr(self, name)) if not isinstance(self.health_contract, dict): raise ValueError("health_contract must be an object") @property def canonical_payload(self) -> dict[str, Any]: return { "artifact_sha256": self.artifact_sha256, "dropin_path": self.dropin_path, "dropin_sha256": self.dropin_sha256, "health_contract": self.health_contract, "manifest_sha256": self.manifest_sha256, "policy_sha256": self.policy_sha256, "release_path": self.release_path, "release_root": self.release_root, "rollback_path": self.rollback_path, "rollback_sha256": self.rollback_sha256, "service": self.service, "source_commit": self.source_commit, } @property def sha256(self) -> str: return hashlib.sha256( _canonical_json(self.canonical_payload) ).hexdigest() def validate( self, *, service_allowlist: Iterable[str], dropin_allowlist: Iterable[str], ) -> "DeploymentPlan": if self.service not in frozenset(service_allowlist): raise ValueError("service is not allowlisted") release_root = Path(self.release_root).resolve() release_path = Path(self.release_path).resolve() try: release_path.relative_to(release_root) except ValueError as exc: raise ValueError("release path escapes release root") from exc if release_path == release_root: raise ValueError("release path must be a release child") dropin = Path(self.dropin_path).resolve() try: dropin.relative_to(release_path) except ValueError as exc: raise ValueError("drop-in escapes release path") from exc allowed_dropins = { str(Path(value).resolve()) for value in dropin_allowlist } if str(dropin) not in allowed_dropins: raise ValueError("drop-in is not allowlisted") return self def to_dict(self) -> dict[str, Any]: return _sanitize({**self.canonical_payload, "sha256": self.sha256}) @classmethod def from_dict(cls, payload: Mapping[str, Any]) -> "DeploymentPlan": values = dict(payload) values.pop("sha256", None) return cls(**values) class DeploymentPlanStore: def __init__(self, root: Path): self.root = Path(root) def save(self, candidate_id: str, plan: DeploymentPlan) -> Path: path = _safe_child(self.root, candidate_id) _atomic_json(path, plan.to_dict()) return path def load(self, candidate_id: str) -> DeploymentPlan: return DeploymentPlan.from_dict( json.loads( _safe_child(self.root, candidate_id).read_text( encoding="utf-8" ) ) ) @dataclass(frozen=True) class SignedReleasePlanApproval: release_plan_id: str candidate_id: str source_commit: str tree_oid: str artifact_sha256: str manifest_sha256: str deployment_plan_sha256: str policy_sha256: str approval_plan_id: str evidence_closure_oid: str owner_approval_record_oid: str capabilities: tuple[str, ...] expires_at: str nonce: str key_id: str signature: str def __post_init__(self) -> None: for name in ( "release_plan_id", "candidate_id", "approval_plan_id", "key_id", ): _validate_identifier(name, getattr(self, name)) if not NONCE_RE.fullmatch(str(self.nonce or "")): raise ValueError("invalid nonce") for name in ( "source_commit", "tree_oid", "evidence_closure_oid", "owner_approval_record_oid", ): _validate_oid(name, getattr(self, name)) for name in ( "artifact_sha256", "manifest_sha256", "deployment_plan_sha256", "policy_sha256", ): _validate_sha(name, getattr(self, name)) capabilities = tuple(str(item) for item in self.capabilities) object.__setattr__(self, "capabilities", capabilities) if ( not capabilities or len(capabilities) != len(set(capabilities)) or not set(capabilities).issubset(ALLOWED_CAPABILITIES) ): raise ValueError("approval contains invalid capabilities") _parse_utc(self.expires_at) @property def canonical_payload(self) -> dict[str, Any]: return { "approval_plan_id": self.approval_plan_id, "artifact_sha256": self.artifact_sha256, "candidate_id": self.candidate_id, "capabilities": list(self.capabilities), "deployment_plan_sha256": self.deployment_plan_sha256, "evidence_closure_oid": self.evidence_closure_oid, "expires_at": self.expires_at, "key_id": self.key_id, "manifest_sha256": self.manifest_sha256, "nonce": self.nonce, "owner_approval_record_oid": self.owner_approval_record_oid, "policy_sha256": self.policy_sha256, "release_plan_id": self.release_plan_id, "source_commit": self.source_commit, "tree_oid": self.tree_oid, } def canonical_bytes(self) -> bytes: return _canonical_json(self.canonical_payload) @property def fingerprint(self) -> str: return hashlib.sha256(self.canonical_bytes()).hexdigest() def to_dict(self) -> dict[str, Any]: return { **self.canonical_payload, "fingerprint": self.fingerprint, "signature": self.signature, } @classmethod def from_dict( cls, payload: Mapping[str, Any] ) -> "SignedReleasePlanApproval": values = dict(payload) values.pop("fingerprint", None) values["capabilities"] = tuple(values.get("capabilities") or ()) return cls(**values) class ApprovalKeyRing: """Public-only Ed25519 key ring with in-process rotation/revocation.""" def __init__(self, public_keys: Mapping[str, bytes]): self._keys: dict[str, bytes] = {} self._revoked: set[str] = set() for key_id, public_key in public_keys.items(): self.add(key_id, public_key) def add(self, key_id: str, public_key: bytes) -> None: key_id = _validate_identifier("key_id", key_id) raw = bytes(public_key) if len(raw) != 32: raise ValueError("Ed25519 public key must contain 32 bytes") Ed25519PublicKey.from_public_bytes(raw) self._keys[key_id] = raw self._revoked.discard(key_id) def revoke(self, key_id: str) -> None: if key_id not in self._keys: raise KeyError("unknown approval signing key") self._revoked.add(key_id) def verify( self, key_id: str, signature: bytes, message: bytes ) -> None: if key_id not in self._keys or key_id in self._revoked: raise CapabilityAuthorizationError( "approval signing key is unavailable or revoked" ) try: Ed25519PublicKey.from_public_bytes( self._keys[key_id] ).verify(signature, message) except InvalidSignature as exc: raise CapabilityAuthorizationError( "owner approval signature is invalid" ) from exc def save_public(self, path: Path) -> None: payload = { "algorithm": "Ed25519", "keys": { key_id: base64.b64encode(value).decode("ascii") for key_id, value in sorted(self._keys.items()) }, "revoked": sorted(self._revoked), } _atomic_json(Path(path), payload) @classmethod def from_file(cls, path: Path) -> "ApprovalKeyRing": payload = json.loads(Path(path).read_text(encoding="utf-8")) if payload.get("algorithm") != "Ed25519": raise ValueError("unsupported approval signature algorithm") ring = cls( { key_id: base64.b64decode(encoded, validate=True) for key_id, encoded in payload.get("keys", {}).items() } ) for key_id in payload.get("revoked", []): ring.revoke(key_id) return ring class ApprovalRevocationStore: def __init__(self, root: Path): self.root = Path(root) def revoke(self, approval_plan_id: str, reason: str) -> dict[str, Any]: path = _safe_child(self.root, approval_plan_id) payload = { "approval_plan_id": approval_plan_id, "reason": str(reason or "revoked"), "revoked_at": utc_now(), } _atomic_json(path, payload) return payload def is_revoked(self, approval_plan_id: str) -> bool: return _safe_child(self.root, approval_plan_id).is_file() class SignedApprovalVerifier: def __init__( self, keyring: ApprovalKeyRing, revocations: ApprovalRevocationStore, ): self.keyring = keyring self.revocations = revocations def verify( self, approval: SignedReleasePlanApproval, candidate: ReleaseCandidate, deployment_plan: DeploymentPlan, policy_sha256: str, ) -> SignedReleasePlanApproval: try: if _parse_utc(approval.expires_at) <= datetime.now(timezone.utc): raise CapabilityAuthorizationError( "owner approval has expired" ) if self.revocations.is_revoked(approval.approval_plan_id): raise CapabilityAuthorizationError( "owner approval has been revoked" ) expected = ( candidate.candidate_id, candidate.source_commit, candidate.tree_oid, candidate.artifact_sha256, candidate.manifest_sha256, deployment_plan.sha256, _validate_sha("policy_sha256", policy_sha256), ) actual = ( approval.candidate_id, approval.source_commit, approval.tree_oid, approval.artifact_sha256, approval.manifest_sha256, approval.deployment_plan_sha256, approval.policy_sha256, ) if actual != expected: raise CapabilityAuthorizationError( "owner approval binding does not match release evidence" ) try: signature = base64.b64decode( approval.signature, validate=True ) except (ValueError, TypeError) as exc: raise CapabilityAuthorizationError( "owner approval signature encoding is invalid" ) from exc self.keyring.verify( approval.key_id, signature, approval.canonical_bytes(), ) return approval except CapabilityAuthorizationError: raise except Exception as exc: raise CapabilityAuthorizationError( "owner approval verification failed" ) from exc class CapabilityLedger: """Durable exclusive claims with leases and fail-closed recovery probes.""" STATES = frozenset( { "PENDING", "CLAIMED", "COMPLETED", "FAILED_RECOVERABLE", "MANUAL_OWNER_REVIEW_REQUIRED", } ) def __init__(self, root: Path): self.root = Path(root) def _operation_path( self, approval: SignedReleasePlanApproval, capability: str, operation: str, ) -> Path: key = hashlib.sha256( "\0".join( (approval.release_plan_id, capability, operation) ).encode("utf-8") ).hexdigest() return _safe_child(self.root, key) def _nonce_path(self, nonce: str) -> Path: key = hashlib.sha256(nonce.encode("utf-8")).hexdigest() return _safe_child(self.root / "nonces", key) def _lock(self, path: Path): lock_path = self.root / ".locks" / f"{path.stem}.lock" lock_path.parent.mkdir(parents=True, exist_ok=True) os.chmod(lock_path.parent, 0o700) handle = lock_path.open("a+", encoding="utf-8") os.chmod(lock_path, 0o600) try: import fcntl fcntl.flock(handle.fileno(), fcntl.LOCK_EX) except ImportError: pass return handle @staticmethod def _unlock(handle) -> None: try: import fcntl fcntl.flock(handle.fileno(), fcntl.LOCK_UN) except ImportError: pass handle.close() @staticmethod def _assert_authorized( approval: SignedReleasePlanApproval, capability: str, operation: str, ) -> None: if ( capability in FORBIDDEN_CAPABILITIES or capability not in ALLOWED_CAPABILITIES or capability not in approval.capabilities ): raise CapabilityAuthorizationError( "capability is not authorized by owner approval" ) _validate_identifier("operation", operation) @staticmethod def _immutable_payload( approval: SignedReleasePlanApproval, capability: str, operation: str, ) -> dict[str, Any]: operation_digest = hashlib.sha256( _canonical_json( { "approval_fingerprint": approval.fingerprint, "capability": capability, "operation": operation, "release_plan_id": approval.release_plan_id, } ) ).hexdigest() return { "approval_fingerprint": approval.fingerprint, "approval_plan_id": approval.approval_plan_id, "capability": capability, "operation": operation, "operation_digest": operation_digest, "release_plan_id": approval.release_plan_id, } @staticmethod def _assert_record_matches( existing: Mapping[str, Any], expected: Mapping[str, Any], ) -> None: if {key: existing.get(key) for key in expected} != dict(expected): raise CapabilityAuthorizationError( "capability replay conflicts with durable record" ) def _write(self, path: Path, payload: Mapping[str, Any]) -> None: path.parent.mkdir(parents=True, exist_ok=True) os.chmod(path.parent, 0o700) _durable_json(path, payload) os.chmod(path, 0o600) def _bind_nonce(self, approval: SignedReleasePlanApproval) -> None: path = self._nonce_path(approval.nonce) expected = { "fingerprint": approval.fingerprint, "release_plan_id": approval.release_plan_id, } path.parent.mkdir(parents=True, exist_ok=True) os.chmod(path.parent, 0o700) try: descriptor = os.open( path, os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600, ) except FileExistsError: actual = json.loads(path.read_text(encoding="utf-8")) if actual != expected: raise CapabilityAuthorizationError( "approval nonce replay does not match original plan" ) return with os.fdopen(descriptor, "w", encoding="utf-8") as output: json.dump(expected, output, sort_keys=True) output.write("\n") output.flush() os.fsync(output.fileno()) _fsync_directory(path.parent) def claim( self, approval: SignedReleasePlanApproval, capability: str, *, operation: str, executor_id: str, lease_seconds: float, now: float | None = None, side_effect_probe: Callable[[], ClaimProbeResult] | None = None, ) -> dict[str, Any]: self._assert_authorized(approval, capability, operation) _validate_identifier("executor_id", executor_id) if lease_seconds <= 0: raise ValueError("lease_seconds must be positive") timestamp = time.time() if now is None else float(now) path = self._operation_path(approval, capability, operation) path.parent.mkdir(parents=True, exist_ok=True) self._bind_nonce(approval) immutable = self._immutable_payload( approval, capability, operation ) lock = self._lock(path) try: if path.exists(): record = json.loads(path.read_text(encoding="utf-8")) self._assert_record_matches(record, immutable) else: record = { **immutable, "attempt": 0, "state": "PENDING", } state = str(record.get("state") or "PENDING") if state == "COMPLETED": return {**record, "status": "IDEMPOTENT"} if state == "MANUAL_OWNER_REVIEW_REQUIRED": return {**record, "status": state} if state == "CLAIMED": lease_until = float(record.get("lease_until") or 0) if lease_until > timestamp: if record.get("executor_id") == executor_id: return {**record, "status": "CLAIMED"} return {**record, "status": "BUSY"} if side_effect_probe is None: return {**record, "status": "PROBE_REQUIRED"} probe = ClaimProbeResult(side_effect_probe()) if probe == ClaimProbeResult.APPLIED_VERIFIED: record.update( { "completed_at": utc_now(), "probe_result": probe.value, "state": "COMPLETED", } ) self._write(path, record) return {**record, "status": "IDEMPOTENT"} if probe in { ClaimProbeResult.INDETERMINATE, ClaimProbeResult.CONFLICT, }: record.update( { "probe_result": probe.value, "state": "MANUAL_OWNER_REVIEW_REQUIRED", } ) self._write(path, record) return {**record, "status": record["state"]} claim_id = uuid.uuid4().hex claimed = { **record, **immutable, "attempt": int(record.get("attempt") or 0) + 1, "claim_id": claim_id, "claimed_at": timestamp, "executor_id": executor_id, "lease_until": timestamp + float(lease_seconds), "state": "CLAIMED", } self._write(path, claimed) return {**claimed, "status": "CLAIMED"} finally: self._unlock(lock) def complete( self, approval: SignedReleasePlanApproval, capability: str, *, operation: str, claim_id: str | None = None, evidence: Mapping[str, Any] | None = None, ) -> dict[str, Any]: self._assert_authorized(approval, capability, operation) path = self._operation_path(approval, capability, operation) immutable = self._immutable_payload( approval, capability, operation ) lock = self._lock(path) try: record = json.loads(path.read_text(encoding="utf-8")) self._assert_record_matches(record, immutable) if record.get("state") == "COMPLETED": return {**record, "status": "IDEMPOTENT"} if ( record.get("state") != "CLAIMED" or not claim_id or record.get("claim_id") != claim_id ): raise CapabilityAuthorizationError( "capability completion claim_id mismatch" ) record.update( { "completed_at": utc_now(), "evidence": _sanitize(dict(evidence or {})), "state": "COMPLETED", } ) self._write(path, record) return {**record, "status": "COMPLETED"} finally: self._unlock(lock) def fail_recoverable( self, approval: SignedReleasePlanApproval, capability: str, *, operation: str, claim_id: str, reason: str, ) -> dict[str, Any]: path = self._operation_path(approval, capability, operation) lock = self._lock(path) try: record = json.loads(path.read_text(encoding="utf-8")) if record.get("claim_id") != claim_id: raise CapabilityAuthorizationError( "capability failure claim_id mismatch" ) record.update( { "failure_reason": str(reason), "state": "FAILED_RECOVERABLE", } ) self._write(path, record) return record finally: self._unlock(lock) def reserve( self, approval: SignedReleasePlanApproval, capability: str, *, operation: str, ) -> dict[str, Any]: """Compatibility wrapper; production controllers use claim().""" path = self._operation_path(approval, capability, operation) if path.exists(): record = json.loads(path.read_text(encoding="utf-8")) if record.get("state") == "COMPLETED": return {**record, "status": "IDEMPOTENT"} if record.get("state") in { "CLAIMED", "FAILED_RECOVERABLE", }: return {**record, "status": "RESUME"} result = self.claim( approval, capability, operation=operation, executor_id="compatibility-reservation", lease_seconds=300, ) aliases = { "CLAIMED": "RESERVED", "BUSY": "RESUME", "IDEMPOTENT": "IDEMPOTENT", } return {**result, "status": aliases.get(result["status"], result["status"])} def consume( self, approval: SignedReleasePlanApproval, capability: str, *, operation: str, ) -> dict[str, Any]: reservation = self.reserve( approval, capability, operation=operation ) if reservation["status"] == "IDEMPOTENT": return reservation completed = self.complete( approval, capability, operation=operation, claim_id=reservation["claim_id"], ) return {**completed, "status": "CONSUMED"} class SagaState(str, Enum): PREPARING = "PREPARING" VERIFYING = "VERIFYING" READY_FOR_APPROVAL = "READY_FOR_APPROVAL" APPROVED = "APPROVED" PRE_SWITCH_REVALIDATING = "PRE_SWITCH_REVALIDATING" SWITCH_PENDING = "SWITCH_PENDING" SWITCHED = "SWITCHED" RUNTIME_VERIFYING = "RUNTIME_VERIFYING" RUNTIME_VERIFIED = "RUNTIME_VERIFIED" TAG_PENDING = "TAG_PENDING" TAG_VERIFIED = "TAG_VERIFIED" BASELINE_REGISTRY_PENDING = "BASELINE_REGISTRY_PENDING" COMPLETED = "COMPLETED" ROLLBACK_PENDING = "ROLLBACK_PENDING" ROLLED_BACK = "ROLLED_BACK" RECONCILIATION_REQUIRED = "RECONCILIATION_REQUIRED" MANUAL_OWNER_REVIEW_REQUIRED = "MANUAL_OWNER_REVIEW_REQUIRED" @dataclass class ReleaseSaga: candidate_id: str release_plan_id: str state: SagaState = SagaState.PREPARING revision: int = 0 events: list[dict[str, Any]] = field(default_factory=list) journal_sequence: int = 0 journal_head_hash: str = JOURNAL_GENESIS_HASH journal_integrity: bool = True created_at: str = field(default_factory=utc_now) updated_at: str = field(default_factory=utc_now) def to_dict(self) -> dict[str, Any]: payload = asdict(self) payload["state"] = self.state.value return _sanitize(payload) @classmethod def from_dict(cls, payload: Mapping[str, Any]) -> "ReleaseSaga": values = dict(payload) values["state"] = SagaState(values["state"]) return cls(**values) class ReleaseSagaStore: """Hash-chained journal is truth; atomic snapshot is a rebuildable cache.""" RECOVERY_ACTIONS = { SagaState.SWITCH_PENDING: "resume_runtime_switch", SagaState.SWITCHED: "verify_runtime", SagaState.RUNTIME_VERIFYING: "verify_runtime", SagaState.TAG_PENDING: "reconcile_tag", SagaState.TAG_VERIFIED: "reconcile_baseline_registry", SagaState.BASELINE_REGISTRY_PENDING: "reconcile_baseline_registry", SagaState.ROLLBACK_PENDING: "resume_rollback", SagaState.RECONCILIATION_REQUIRED: "reconcile_release", SagaState.MANUAL_OWNER_REVIEW_REQUIRED: "manual_owner_review", } def __init__( self, root: Path, *, after_journal_hook: Callable[[Mapping[str, Any]], None] | None = None, ): self.root = Path(root) self.after_journal_hook = after_journal_hook def _path(self, candidate_id: str) -> Path: if not CANDIDATE_ID_RE.fullmatch(str(candidate_id or "")): raise ValueError("invalid candidate_id") return _safe_child(self.root, candidate_id) def _journal(self, candidate_id: str) -> Path: if not CANDIDATE_ID_RE.fullmatch(str(candidate_id or "")): raise ValueError("invalid candidate_id") return _safe_child(self.root, candidate_id, ".events.jsonl") def _lock(self, candidate_id: str): path = _safe_child(self.root / ".locks", candidate_id, ".lock") path.parent.mkdir(parents=True, exist_ok=True) handle = path.open("a+", encoding="utf-8") try: import fcntl fcntl.flock(handle.fileno(), fcntl.LOCK_EX) except ImportError: pass return handle @staticmethod def _unlock(handle) -> None: try: import fcntl fcntl.flock(handle.fileno(), fcntl.LOCK_UN) except ImportError: pass handle.close() def create( self, candidate_id: str, release_plan_id: str ) -> ReleaseSaga: _validate_identifier("release_plan_id", release_plan_id) path = self._path(candidate_id) path.parent.mkdir(parents=True, exist_ok=True) lock = self._lock(candidate_id) try: if path.exists() or self._journal(candidate_id).exists(): existing = self._load_locked(candidate_id) if existing.release_plan_id != release_plan_id: raise SagaConflict( "candidate is bound to a different release plan" ) return existing saga = ReleaseSaga( candidate_id=candidate_id, release_plan_id=release_plan_id, ) payload = { "candidate_id": candidate_id, "details": {}, "event": "saga_created", "release_plan_id": release_plan_id, "revision": 0, "state": saga.state.value, "timestamp": saga.created_at, } record = self._append_event( candidate_id, payload, sequence=1, previous_hash=JOURNAL_GENESIS_HASH, ) saga.events.append(payload) saga.journal_sequence = 1 saga.journal_head_hash = record["event_hash"] self._after_journal(record) _durable_json(path, saga.to_dict()) return saga finally: self._unlock(lock) def load(self, candidate_id: str) -> ReleaseSaga: lock = self._lock(candidate_id) try: try: return self._load_locked(candidate_id) except JournalIntegrityError as exc: return self._manual_review_snapshot(candidate_id, str(exc)) finally: self._unlock(lock) def _load_locked(self, candidate_id: str) -> ReleaseSaga: records = self.verify_journal(candidate_id) saga = self._rebuild(records) path = self._path(candidate_id) cached = None if path.exists(): try: cached = ReleaseSaga.from_dict( json.loads(path.read_text(encoding="utf-8")) ) except Exception: cached = None if cached is not None and ( cached.journal_sequence > saga.journal_sequence or ( cached.journal_sequence == saga.journal_sequence and cached.journal_head_hash != saga.journal_head_hash ) ): raise JournalIntegrityError( "Saga snapshot references a missing or different journal head" ) if cached is None or cached.to_dict() != saga.to_dict(): _durable_json(path, saga.to_dict()) return saga def compare_and_set( self, candidate_id: str, *, expected_revision: int, expected_states: Iterable[SagaState], target: SagaState, event: str, details: Mapping[str, Any] | None = None, ) -> ReleaseSaga: lock = self._lock(candidate_id) try: try: saga = self._load_locked(candidate_id) except JournalIntegrityError as exc: return self._manual_review_snapshot(candidate_id, str(exc)) states = {SagaState(value) for value in expected_states} if ( saga.revision != expected_revision or saga.state not in states ): raise SagaConflict("Saga compare-and-set conflict") saga.revision += 1 saga.state = SagaState(target) saga.updated_at = utc_now() payload = { "candidate_id": saga.candidate_id, "details": _sanitize(dict(details or {})), "event": str(event), "release_plan_id": saga.release_plan_id, "revision": saga.revision, "state": saga.state.value, "timestamp": saga.updated_at, } record = self._append_event( candidate_id, payload, sequence=saga.journal_sequence + 1, previous_hash=saga.journal_head_hash, ) saga.events.append(payload) saga.journal_sequence = record["sequence"] saga.journal_head_hash = record["event_hash"] self._after_journal(record) _durable_json(self._path(candidate_id), saga.to_dict()) return saga finally: self._unlock(lock) def force_state_for_recovery_test( self, candidate_id: str, state: SagaState ) -> ReleaseSaga: saga = self.load(candidate_id) return self.compare_and_set( candidate_id, expected_revision=saga.revision, expected_states={saga.state}, target=state, event="test_recovery_checkpoint", ) def bind_release_plan( self, candidate_id: str, release_plan_id: str ) -> ReleaseSaga: _validate_identifier("release_plan_id", release_plan_id) lock = self._lock(candidate_id) try: saga = self._load_locked(candidate_id) pending = f"pending-{candidate_id}" if saga.release_plan_id not in {pending, release_plan_id}: raise SagaConflict( "Saga is already bound to another release plan" ) if saga.release_plan_id == release_plan_id: return saga saga.release_plan_id = release_plan_id saga.revision += 1 saga.updated_at = utc_now() payload = { "candidate_id": saga.candidate_id, "details": {"release_plan_id": release_plan_id}, "event": "owner_approval_bound", "release_plan_id": release_plan_id, "revision": saga.revision, "state": saga.state.value, "timestamp": saga.updated_at, } record = self._append_event( candidate_id, payload, sequence=saga.journal_sequence + 1, previous_hash=saga.journal_head_hash, ) saga.events.append(payload) saga.journal_sequence = record["sequence"] saga.journal_head_hash = record["event_hash"] self._after_journal(record) _durable_json(self._path(candidate_id), saga.to_dict()) return saga finally: self._unlock(lock) def _append_event( self, candidate_id: str, payload: Mapping[str, Any], *, sequence: int, previous_hash: str, ) -> dict[str, Any]: path = self._journal(candidate_id) path.parent.mkdir(parents=True, exist_ok=True) core = { "sequence": int(sequence), "previous_hash": str(previous_hash), "payload": _sanitize(dict(payload)), } record = { "schema_version": JOURNAL_SCHEMA_VERSION, **core, "event_hash": hashlib.sha256( _canonical_json(core) ).hexdigest(), } with path.open("a", encoding="utf-8") as output: output.write( json.dumps( record, sort_keys=True, separators=(",", ":"), ) + "\n" ) output.flush() os.fsync(output.fileno()) _fsync_directory(path.parent) return record def _after_journal(self, record: Mapping[str, Any]) -> None: if self.after_journal_hook is not None: self.after_journal_hook(record) def verify_journal( self, candidate_id: str ) -> list[dict[str, Any]]: path = self._journal(candidate_id) if not path.is_file(): raise JournalIntegrityError("Saga journal is missing") records: list[dict[str, Any]] = [] previous = JOURNAL_GENESIS_HASH try: lines = path.read_text(encoding="utf-8").splitlines() except OSError as exc: raise JournalIntegrityError("Saga journal is unreadable") from exc if not lines: raise JournalIntegrityError("Saga journal is empty") for expected_sequence, line in enumerate(lines, 1): try: record = json.loads(line) except json.JSONDecodeError as exc: raise JournalIntegrityError( "Saga journal contains invalid JSON" ) from exc if set(record) != { "schema_version", "sequence", "previous_hash", "payload", "event_hash", }: raise JournalIntegrityError( "Saga journal record schema is invalid" ) core = { "sequence": record["sequence"], "previous_hash": record["previous_hash"], "payload": record["payload"], } expected_hash = hashlib.sha256( _canonical_json(core) ).hexdigest() if ( record["schema_version"] != JOURNAL_SCHEMA_VERSION or record["sequence"] != expected_sequence or record["previous_hash"] != previous or record["event_hash"] != expected_hash ): raise JournalIntegrityError( "Saga journal hash chain verification failed" ) previous = record["event_hash"] records.append(record) return records @staticmethod def _rebuild(records: list[Mapping[str, Any]]) -> ReleaseSaga: first = dict(records[0]["payload"]) saga = ReleaseSaga( candidate_id=str(first["candidate_id"]), release_plan_id=str(first["release_plan_id"]), state=SagaState(first["state"]), revision=int(first["revision"]), events=[], created_at=str(first["timestamp"]), updated_at=str(first["timestamp"]), ) for record in records: payload = dict(record["payload"]) if payload.get("candidate_id") != saga.candidate_id: raise JournalIntegrityError( "Saga journal candidate identity changed" ) saga.release_plan_id = str(payload["release_plan_id"]) saga.state = SagaState(payload["state"]) saga.revision = int(payload["revision"]) saga.updated_at = str(payload["timestamp"]) saga.events.append(_sanitize(payload)) saga.journal_sequence = int(record["sequence"]) saga.journal_head_hash = str(record["event_hash"]) return saga def _manual_review_snapshot( self, candidate_id: str, reason: str ) -> ReleaseSaga: path = self._path(candidate_id) try: cached = ReleaseSaga.from_dict( json.loads(path.read_text(encoding="utf-8")) ) except Exception: cached = ReleaseSaga( candidate_id=candidate_id, release_plan_id=f"corrupt-{candidate_id}", ) cached.state = SagaState.MANUAL_OWNER_REVIEW_REQUIRED cached.journal_integrity = False cached.updated_at = utc_now() cached.events.append( { "event": "journal_integrity_failure", "reason": str(reason), "timestamp": cached.updated_at, } ) _durable_json(path, cached.to_dict()) return cached @classmethod def recovery_action(cls, state: SagaState) -> str: return cls.RECOVERY_ACTIONS.get( SagaState(state), "no_automatic_action" ) class PreSwitchProbe(Protocol): def collect( self, release_candidate: ReleaseCandidate, deployment_plan: DeploymentPlan, ) -> Mapping[str, Any]: ... class PreSwitchValidator: def __init__(self, probe: PreSwitchProbe): self.probe = probe def validate( self, candidate: ReleaseCandidate, approval: SignedReleasePlanApproval, plan: DeploymentPlan, *, expected_repository: str, ) -> dict[str, Any]: expected = { "source_commit": candidate.source_commit, "tree_oid": candidate.tree_oid, "artifact_sha256": candidate.artifact_sha256, "manifest_sha256": candidate.manifest_sha256, "deployment_plan_sha256": approval.deployment_plan_sha256, "policy_sha256": approval.policy_sha256, "rollback_ready": True, "git_refs_consistent": True, "repository_identity": expected_repository, "writer_disabled": True, } plan_binding = ( approval.candidate_id == candidate.candidate_id and approval.source_commit == candidate.source_commit and approval.tree_oid == candidate.tree_oid and approval.artifact_sha256 == candidate.artifact_sha256 and approval.manifest_sha256 == candidate.manifest_sha256 and approval.deployment_plan_sha256 == plan.sha256 and plan.source_commit == candidate.source_commit and plan.artifact_sha256 == candidate.artifact_sha256 and plan.manifest_sha256 == candidate.manifest_sha256 and plan.policy_sha256 == approval.policy_sha256 ) if not plan_binding: raise StaleApprovalError( "APPROVAL_STALE_REAPPROVAL_REQUIRED: " "deployment plan binding changed" ) actual = dict(self.probe.collect(candidate, plan)) failed = [ name for name, expected_value in expected.items() if actual.get(name) != expected_value ] if failed: raise StaleApprovalError( "APPROVAL_STALE_REAPPROVAL_REQUIRED: " + ", ".join(sorted(failed)) ) return { "valid": True, "checks": {name: True for name in expected}, "validated_at": utc_now(), } class CapabilityController: """One narrow controller consumes one capability and runs one adapter.""" def __init__( self, *, capability: str, ledger: CapabilityLedger, adapter: Callable[..., Mapping[str, Any]], executor_id: str = "silent-release-controller", lease_seconds: float = 300, recovery_probe: Callable[[], ClaimProbeResult] | None = None, ): if capability not in ALLOWED_CAPABILITIES: raise ValueError("invalid release capability") self.capability = capability self.ledger = ledger self.adapter = adapter self.executor_id = executor_id self.lease_seconds = lease_seconds self.recovery_probe = recovery_probe def execute( self, approval: SignedReleasePlanApproval, *, operation: str, **kwargs: Any, ) -> dict[str, Any]: claim = self.ledger.claim( approval, self.capability, operation=operation, executor_id=self.executor_id, lease_seconds=self.lease_seconds, side_effect_probe=self.recovery_probe, ) if claim["status"] == "IDEMPOTENT": return { "capability": self.capability, "status": "IDEMPOTENT", } if claim["status"] == "BUSY": return { "capability": self.capability, "claim_id": claim.get("claim_id"), "status": "BUSY", } if claim["status"] != "CLAIMED": raise CapabilityAuthorizationError( f"capability claim is not executable: {claim['status']}" ) try: result = dict(self.adapter(**kwargs)) self.validate_result(approval, result) self.ledger.complete( approval, self.capability, operation=operation, claim_id=claim["claim_id"], evidence={ "result_sha256": hashlib.sha256( _canonical_json(_sanitize(result)) ).hexdigest() }, ) except Exception as exc: self.ledger.fail_recoverable( approval, self.capability, operation=operation, claim_id=claim["claim_id"], reason=type(exc).__name__, ) raise return { "capability": self.capability, "result": _sanitize(result), "status": "EXECUTED", } def validate_result( self, approval: SignedReleasePlanApproval, result: Mapping[str, Any], ) -> None: if result.get("verified") is not True: raise CapabilityAuthorizationError( "capability result is not verified" ) class RuntimeSwitchController(CapabilityController): def __init__(self, ledger: CapabilityLedger, adapter: Callable[..., Any]): super().__init__( capability="runtime_switch", ledger=ledger, adapter=adapter, ) class HardenedTagController(CapabilityController): """Tag adapter must independently verify Git, Gitea API, and listing.""" REQUIRED_PROOF = frozenset( { "evidence_closure_oid", "approval_plan_id", "repository_identity", "remote_tag_oid", "remote_peeled_target_oid", "gitea_api_tag_oid", "gitea_api_peeled_target_oid", "gitea_listing_visible", } ) def __init__( self, ledger: CapabilityLedger, adapter: Callable[..., Any], *, expected_repository: str = "", ): self.expected_repository = expected_repository super().__init__( capability="tag_publish", ledger=ledger, adapter=adapter, ) def validate_result(self, approval, proof): if ( not self.REQUIRED_PROOF.issubset(proof) or proof.get("evidence_closure_oid") != approval.evidence_closure_oid or proof.get("approval_plan_id") != approval.approval_plan_id or proof.get("gitea_listing_visible") is not True or proof.get("remote_peeled_target_oid") != approval.source_commit or proof.get("gitea_api_peeled_target_oid") != approval.source_commit or proof.get("gitea_api_tag_oid") != proof.get("remote_tag_oid") or ( self.expected_repository and proof.get("repository_identity") != self.expected_repository ) ): raise CapabilityAuthorizationError( "tag publication proof is incomplete" ) class BaselineRegistryFinalizeController(CapabilityController): def __init__(self, ledger: CapabilityLedger, adapter: Callable[..., Any]): super().__init__( capability="baseline_registry_finalize", ledger=ledger, adapter=adapter, ) class BaselineController(BaselineRegistryFinalizeController): """Narrow Baseline half of the governed finalization capability.""" def validate_result(self, approval, result): required = { "verified": True, "baseline_target_oid": approval.source_commit, "evidence_closure_oid": approval.evidence_closure_oid, "approval_plan_id": approval.approval_plan_id, } if any(result.get(key) != value for key, value in required.items()): raise CapabilityAuthorizationError( "Baseline finalization proof is inconsistent" ) class RegistryController(BaselineRegistryFinalizeController): """Narrow Registry half of the governed finalization capability.""" def validate_result(self, approval, result): required = { "verified": True, "registry_target_oid": approval.source_commit, "evidence_closure_oid": approval.evidence_closure_oid, "approval_plan_id": approval.approval_plan_id, } if any(result.get(key) != value for key, value in required.items()): raise CapabilityAuthorizationError( "Registry finalization proof is inconsistent" ) class SilentReleaseActionService: """Durable control-plane facade; execution is shadow-disabled by default.""" def __init__( self, root: Path, *, execution_enabled: bool = False, runtime_executor: Any = None, approval_verifier: SignedApprovalVerifier | None = None, pre_switch_validator: PreSwitchValidator | None = None, expected_repository: str = "", policy_sha256: str = "", ): self.root = Path(root) self.execution_enabled = bool(execution_enabled) self.runtime_executor = runtime_executor self.approval_verifier = approval_verifier self.pre_switch_validator = pre_switch_validator self.expected_repository = expected_repository self.policy_sha256 = policy_sha256 self.candidates = ReleaseCandidateStore( self.root / "candidates" ) self.plans = DeploymentPlanStore(self.root / "plans") self.sagas = ReleaseSagaStore(self.root / "sagas") self.approvals_root = self.root / "approvals" @classmethod def shadow( cls, root: Path, *, runtime_executor: Any = None ) -> "SilentReleaseActionService": return cls( root, execution_enabled=False, runtime_executor=runtime_executor, ) def create_release_candidate( self, payload: Mapping[str, Any] ) -> dict[str, Any]: values = dict(payload) raw_plan = values.pop("deployment_plan", None) candidate = ReleaseCandidate.from_dict(values) try: existing = self.candidates.load(candidate.candidate_id) except FileNotFoundError: self.candidates.save(candidate) existing = candidate else: if existing.fingerprint != candidate.fingerprint: raise SagaConflict( "candidate id is already bound to other evidence" ) self.sagas.create( existing.candidate_id, f"pending-{existing.candidate_id}", ) result = existing.to_dict() if raw_plan is not None: plan = DeploymentPlan.from_dict(raw_plan) if ( plan.source_commit != existing.source_commit or plan.artifact_sha256 != existing.artifact_sha256 or plan.manifest_sha256 != existing.manifest_sha256 ): raise StaleApprovalError( "deployment plan does not match release candidate" ) try: stored = self.plans.load(existing.candidate_id) except FileNotFoundError: self.plans.save(existing.candidate_id, plan) else: if stored.sha256 != plan.sha256: raise SagaConflict( "candidate has a different deployment plan" ) result["deployment_plan_sha256"] = plan.sha256 return result def get_release_candidate(self, candidate_id: str) -> dict[str, Any]: return self.candidates.load(candidate_id).to_dict() def get_release_saga(self, candidate_id: str) -> dict[str, Any]: return self.sagas.load(candidate_id).to_dict() def get_approval_envelope(self, candidate_id: str) -> dict[str, Any]: candidate = self.candidates.load(candidate_id) try: deployment_plan_sha256 = self.plans.load( candidate_id ).sha256 except FileNotFoundError: deployment_plan_sha256 = None return { "candidate_id": candidate.candidate_id, "candidate_fingerprint": candidate.fingerprint, "execution_enabled": self.execution_enabled, "required_capabilities": sorted(ALLOWED_CAPABILITIES), "signature_algorithm": "Ed25519", "deployment_plan_sha256": deployment_plan_sha256, "policy_sha256": self.policy_sha256 or None, "status": "READY_FOR_OWNER_SIGNATURE", } def submit_owner_approval( self, payload: Mapping[str, Any] ) -> dict[str, Any]: approval = SignedReleasePlanApproval.from_dict(payload) if self.approval_verifier is None: raise CapabilityAuthorizationError( "owner approval verifier is not configured" ) candidate = self.candidates.load(approval.candidate_id) plan = self.plans.load(approval.candidate_id) self.approval_verifier.verify( approval, candidate, plan, self.policy_sha256, ) path = _safe_child( self.approvals_root, approval.release_plan_id ) if path.exists(): existing = SignedReleasePlanApproval.from_dict( json.loads(path.read_text(encoding="utf-8")) ) if existing.fingerprint != approval.fingerprint: raise CapabilityAuthorizationError( "release plan approval conflicts with stored approval" ) result = { "fingerprint": existing.fingerprint, "status": "IDEMPOTENT", } self.sagas.bind_release_plan( approval.candidate_id, approval.release_plan_id ) return result _atomic_json(path, approval.to_dict()) self.sagas.bind_release_plan( approval.candidate_id, approval.release_plan_id ) return { "fingerprint": approval.fingerprint, "status": "VERIFIED_AND_STORED", } def start_deployment( self, candidate_id: str, *, approval: SignedReleasePlanApproval | None = None, ) -> dict[str, Any]: self.candidates.load(candidate_id) if not self.execution_enabled: return { "candidate_id": candidate_id, "execution_enabled": False, "side_effects": [], "status": "SHADOW_MODE", } if approval is None: raise CapabilityAuthorizationError( "signed owner approval is required" ) if self.approval_verifier is None: raise CapabilityAuthorizationError( "owner approval verifier is not configured" ) candidate = self.candidates.load(candidate_id) plan = self.plans.load(candidate_id) self.approval_verifier.verify( approval, candidate, plan, self.policy_sha256, ) if self.pre_switch_validator is None: raise CapabilityAuthorizationError( "pre-switch validator is not configured" ) self.pre_switch_validator.validate( candidate, approval, plan, expected_repository=self.expected_repository, ) if self.runtime_executor is None: raise CapabilityAuthorizationError( "runtime execution adapter is not configured" ) return _sanitize( dict( self.runtime_executor.execute( candidate_id=candidate_id, approval=approval, ) ) )