Files
aios/active-runtime/app/silent_release_hardening.py
T

3668 lines
127 KiB
Python

#!/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,
ReleaseState,
_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"
@dataclass(frozen=True)
class ProbeEvidence:
"""Fresh, operation-bound evidence for a side-effect recovery decision."""
result: ClaimProbeResult
operation_digest: str
observed_at: float
evidence: Mapping[str, Any] = field(default_factory=dict)
def validate(
self,
*,
expected_operation_digest: str,
now: float,
maximum_age_seconds: float,
) -> "ProbeEvidence":
if self.operation_digest != expected_operation_digest:
raise CapabilityAuthorizationError(
"probe evidence operation digest mismatch"
)
age = float(now) - float(self.observed_at)
if age < 0 or age > float(maximum_age_seconds):
raise CapabilityAuthorizationError(
"probe evidence is outside its freshness window"
)
return self
def to_dict(self) -> dict[str, Any]:
return {
"evidence": _sanitize(dict(self.evidence)),
"evidence_sha256": hashlib.sha256(
_canonical_json(_sanitize(dict(self.evidence)))
).hexdigest(),
"observed_at": float(self.observed_at),
"operation_digest": self.operation_digest,
"result": ClaimProbeResult(self.result).value,
}
@dataclass(frozen=True)
class TagPublicationProbeEvidence:
"""Independent Git/Gitea observations used to classify TAG_PENDING."""
repository_identity: str | None
remote_reachable: bool
gitea_api_reachable: bool
gitea_listing_reachable: bool
remote_tag_oid: str | None = None
annotated_tag_object_oid: str | None = None
remote_peeled_target_oid: str | None = None
gitea_api_tag_oid: str | None = None
gitea_api_peeled_target_oid: str | None = None
gitea_listing_visible: bool | None = None
gitea_listing_tag_oid: str | None = None
gitea_listing_peeled_target_oid: str | None = None
observed_at: float = field(default_factory=time.time)
def to_dict(self) -> dict[str, Any]:
return _sanitize(asdict(self))
def classify_tag_publication(
evidence: TagPublicationProbeEvidence,
plan: "TagPlan",
) -> ClaimProbeResult:
"""Classify a tag without mutating, deleting, or rewriting any ref."""
if (
not evidence.remote_reachable
or not evidence.gitea_api_reachable
or not evidence.gitea_listing_reachable
or evidence.repository_identity is None
):
return ClaimProbeResult.INDETERMINATE
if evidence.repository_identity != plan.repository_identity:
return ClaimProbeResult.CONFLICT
tag_oids = (
evidence.remote_tag_oid,
evidence.gitea_api_tag_oid,
evidence.gitea_listing_tag_oid,
)
target_oids = (
evidence.remote_peeled_target_oid,
evidence.gitea_api_peeled_target_oid,
evidence.gitea_listing_peeled_target_oid,
)
if (
all(value is None for value in tag_oids)
and all(value is None for value in target_oids)
and evidence.annotated_tag_object_oid is None
and evidence.gitea_listing_visible is False
):
return ClaimProbeResult.NOT_APPLIED
observed_tag_oids = tuple(value for value in tag_oids if value is not None)
observed_targets = tuple(
value for value in target_oids if value is not None
)
if (
any(value != plan.expected_tag_oid for value in observed_tag_oids)
or any(value != plan.expected_target_oid for value in observed_targets)
or (
plan.tag_type == "annotated"
and evidence.annotated_tag_object_oid is not None
and evidence.annotated_tag_object_oid != plan.expected_tag_oid
)
):
return ClaimProbeResult.CONFLICT
exact = (
tag_oids
== (
plan.expected_tag_oid,
plan.expected_tag_oid,
plan.expected_tag_oid,
)
and target_oids
== (
plan.expected_target_oid,
plan.expected_target_oid,
plan.expected_target_oid,
)
and evidence.gitea_listing_visible is True
and (
plan.tag_type != "annotated"
or evidence.annotated_tag_object_oid == plan.expected_tag_oid
)
)
return (
ClaimProbeResult.APPLIED_VERIFIED
if exact
else ClaimProbeResult.INDETERMINATE
)
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]
cutover_link: str = ""
systemd_unit_sha256: str = ""
tree_oid: str = ""
package_closure_sha256: str = ""
authoritative_git_dir: str = ""
repository_identity: str = ""
writer_expected: bool = False
baseline_before_sha256: str = ""
registry_before_sha256: str = ""
production_tag_refset_before_sha256: str = ""
health_contract_version: str = ""
timeout_policy: dict[str, Any] = field(default_factory=dict)
retry_policy: dict[str, Any] = field(default_factory=dict)
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 self.tree_oid:
_validate_oid("tree_oid", self.tree_oid)
for name in (
"systemd_unit_sha256",
"package_closure_sha256",
"baseline_before_sha256",
"registry_before_sha256",
"production_tag_refset_before_sha256",
):
if getattr(self, name):
_validate_sha(name, getattr(self, name))
if not isinstance(self.health_contract, dict):
raise ValueError("health_contract must be an object")
if not isinstance(self.timeout_policy, dict):
raise ValueError("timeout_policy must be an object")
if not isinstance(self.retry_policy, dict):
raise ValueError("retry_policy must be an object")
if self.writer_expected is not False:
raise ValueError("production writer must remain disabled")
@property
def canonical_payload(self) -> dict[str, Any]:
return {
"artifact_sha256": self.artifact_sha256,
"authoritative_git_dir": self.authoritative_git_dir,
"baseline_before_sha256": self.baseline_before_sha256,
"cutover_link": self.cutover_link,
"dropin_path": self.dropin_path,
"dropin_sha256": self.dropin_sha256,
"health_contract": self.health_contract,
"health_contract_version": self.health_contract_version,
"manifest_sha256": self.manifest_sha256,
"package_closure_sha256": self.package_closure_sha256,
"policy_sha256": self.policy_sha256,
"production_tag_refset_before_sha256": (
self.production_tag_refset_before_sha256
),
"release_path": self.release_path,
"release_root": self.release_root,
"repository_identity": self.repository_identity,
"retry_policy": self.retry_policy,
"rollback_path": self.rollback_path,
"rollback_sha256": self.rollback_sha256,
"service": self.service,
"source_commit": self.source_commit,
"systemd_unit_sha256": self.systemd_unit_sha256,
"timeout_policy": self.timeout_policy,
"tree_oid": self.tree_oid,
"writer_expected": self.writer_expected,
"registry_before_sha256": self.registry_before_sha256,
}
@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 validate_complete(self) -> "DeploymentPlan":
for name in (
"cutover_link",
"authoritative_git_dir",
"repository_identity",
"health_contract_version",
):
if not str(getattr(self, name) or ""):
raise ValueError(f"{name} is required")
_validate_oid("tree_oid", self.tree_oid)
for name in (
"systemd_unit_sha256",
"package_closure_sha256",
"baseline_before_sha256",
"registry_before_sha256",
"production_tag_refset_before_sha256",
):
_validate_sha(name, getattr(self, name))
if not self.timeout_policy or not self.retry_policy:
raise ValueError("timeout and retry policies are required")
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)
@dataclass(frozen=True)
class TagPlan:
version: str
tag_type: str
expected_tag_oid: str
expected_target_oid: str
evidence_closure_oid: str
approval_plan_id: str
repository_identity: str
gitea_owner: str
gitea_repository: str
current_remote_tag_oid: str | None
remote_absent_evidence_sha256: str | None
verification_contract: str
force_push_allowed: bool = False
tag_rewrite_allowed: bool = False
def __post_init__(self) -> None:
_validate_identifier("version", self.version)
if self.tag_type not in {"annotated", "lightweight"}:
raise ValueError("unsupported tag type")
for name in (
"expected_tag_oid",
"expected_target_oid",
"evidence_closure_oid",
):
_validate_oid(name, getattr(self, name))
_validate_identifier("approval_plan_id", self.approval_plan_id)
_validate_identifier("gitea_owner", self.gitea_owner)
_validate_identifier("gitea_repository", self.gitea_repository)
present = self.current_remote_tag_oid is not None
absent = self.remote_absent_evidence_sha256 is not None
if present == absent:
raise ValueError(
"tag plan must bind either current tag or signed absence"
)
if present:
_validate_oid(
"current_remote_tag_oid", self.current_remote_tag_oid or ""
)
if absent:
_validate_sha(
"remote_absent_evidence_sha256",
self.remote_absent_evidence_sha256 or "",
)
if not self.repository_identity or not self.verification_contract:
raise ValueError("tag repository and verification are required")
if self.force_push_allowed or self.tag_rewrite_allowed:
raise ValueError("force push and tag rewrite are forbidden")
@property
def canonical_payload(self) -> dict[str, Any]:
return asdict(self)
@property
def sha256(self) -> str:
return hashlib.sha256(
_canonical_json(self.canonical_payload)
).hexdigest()
def to_dict(self) -> dict[str, Any]:
return _sanitize({**self.canonical_payload, "sha256": self.sha256})
@classmethod
def from_dict(cls, payload: Mapping[str, Any]) -> "TagPlan":
values = dict(payload)
values.pop("sha256", None)
return cls(**values)
@dataclass(frozen=True)
class FinalizationPlan:
version: str
expected_tag_oid: str
expected_target_oid: str
expected_previous_baseline_sha256: str
registry_expected_record: dict[str, Any]
baseline_expected_record: dict[str, Any]
final_consistency_contract: str
def __post_init__(self) -> None:
_validate_identifier("version", self.version)
_validate_oid("expected_tag_oid", self.expected_tag_oid)
_validate_oid("expected_target_oid", self.expected_target_oid)
_validate_sha(
"expected_previous_baseline_sha256",
self.expected_previous_baseline_sha256,
)
if not isinstance(self.registry_expected_record, dict):
raise ValueError("registry expected record must be an object")
if not isinstance(self.baseline_expected_record, dict):
raise ValueError("baseline expected record must be an object")
if not self.final_consistency_contract:
raise ValueError("final consistency contract is required")
@property
def canonical_payload(self) -> dict[str, Any]:
return asdict(self)
@property
def sha256(self) -> str:
return hashlib.sha256(
_canonical_json(self.canonical_payload)
).hexdigest()
def to_dict(self) -> dict[str, Any]:
return _sanitize({**self.canonical_payload, "sha256": self.sha256})
@classmethod
def from_dict(cls, payload: Mapping[str, Any]) -> "FinalizationPlan":
values = dict(payload)
values.pop("sha256", None)
return cls(**values)
@dataclass(frozen=True)
class ReleasePlan:
deployment: DeploymentPlan
tag: TagPlan
finalization: FinalizationPlan
policy_sha256: str
def __post_init__(self) -> None:
_validate_sha("policy_sha256", self.policy_sha256)
def validate(self) -> "ReleasePlan":
self.deployment.validate_complete()
if self.deployment.policy_sha256 != self.policy_sha256:
raise ValueError("deployment policy does not match release plan")
if self.deployment.repository_identity != self.tag.repository_identity:
raise ValueError("release repository identity is inconsistent")
if self.deployment.source_commit != self.tag.expected_target_oid:
raise ValueError("deployment commit and tag target are inconsistent")
if self.tag.version != self.finalization.version:
raise ValueError("release version is inconsistent")
if self.tag.expected_tag_oid != self.finalization.expected_tag_oid:
raise ValueError("release tag OID is inconsistent")
if (
self.tag.expected_target_oid
!= self.finalization.expected_target_oid
):
raise ValueError("release target OID is inconsistent")
if (
self.deployment.baseline_before_sha256
!= self.finalization.expected_previous_baseline_sha256
):
raise ValueError("previous baseline is inconsistent")
return self
@property
def canonical_payload(self) -> dict[str, Any]:
return {
"deployment": self.deployment.canonical_payload,
"finalization": self.finalization.canonical_payload,
"policy_sha256": self.policy_sha256,
"tag": self.tag.canonical_payload,
}
@property
def sha256(self) -> str:
return hashlib.sha256(
_canonical_json(self.canonical_payload)
).hexdigest()
def to_dict(self) -> dict[str, Any]:
return _sanitize({**self.canonical_payload, "sha256": self.sha256})
@classmethod
def from_dict(cls, payload: Mapping[str, Any]) -> "ReleasePlan":
values = dict(payload)
values.pop("sha256", None)
return cls(
deployment=DeploymentPlan.from_dict(values["deployment"]),
tag=TagPlan.from_dict(values["tag"]),
finalization=FinalizationPlan.from_dict(
values["finalization"]
),
policy_sha256=values["policy_sha256"],
)
@dataclass(frozen=True)
class TrustedReleasePolicy:
services: frozenset[str]
release_roots: frozenset[str]
cutover_links: frozenset[str]
dropin_paths: frozenset[str]
authoritative_git_dirs: frozenset[str]
repository_identities: frozenset[str]
def validate_deployment(self, plan: DeploymentPlan) -> DeploymentPlan:
plan.validate_complete()
checks = (
(plan.service, self.services, "service"),
(plan.release_root, self.release_roots, "release root"),
(plan.cutover_link, self.cutover_links, "cutover link"),
(plan.dropin_path, self.dropin_paths, "drop-in"),
(
plan.authoritative_git_dir,
self.authoritative_git_dirs,
"authoritative Git directory",
),
(
plan.repository_identity,
self.repository_identities,
"repository identity",
),
)
for value, allowlist, label in checks:
if value not in allowlist:
raise ValueError(f"{label} is not allowlisted")
plan.validate(
service_allowlist=self.services,
dropin_allowlist=self.dropin_paths,
)
return plan
class ReleasePlanStore:
def __init__(self, root: Path):
self.root = Path(root)
def save(self, candidate_id: str, plan: ReleasePlan) -> Path:
path = _safe_child(self.root, candidate_id)
if path.exists():
existing = self.load(candidate_id)
if existing.sha256 != plan.sha256:
raise SagaConflict(
"candidate is bound to another release plan"
)
return path
_durable_json(path, plan.to_dict())
return path
def load(self, candidate_id: str) -> ReleasePlan:
return ReleasePlan.from_dict(
json.loads(
_safe_child(self.root, candidate_id).read_text(
encoding="utf-8"
)
)
)
class SignedApprovalStore:
def __init__(self, root: Path):
self.root = Path(root)
def save(
self, candidate_id: str, approval: "SignedReleasePlanApproval"
) -> tuple[Path, bool]:
path = _safe_child(self.root, candidate_id)
if path.exists():
existing = self.load(candidate_id)
if existing.fingerprint != approval.fingerprint:
raise CapabilityAuthorizationError(
"candidate has a conflicting owner approval"
)
return path, False
_durable_json(path, approval.to_dict())
return path, True
def load(self, candidate_id: str) -> "SignedReleasePlanApproval":
return SignedReleasePlanApproval.from_dict(
json.loads(
_safe_child(self.root, candidate_id).read_text(
encoding="utf-8"
)
)
)
@dataclass(frozen=True)
class BuilderAttestation:
builder_attestation_id: str
builder_key_id: str
source_commit: str
tree_oid: str
artifact_sha256: str
manifest_sha256: str
package_closure_sha256: str
test_evidence_sha256: str
canary_evidence_sha256: str
issued_at: str
signature: str
def __post_init__(self) -> None:
_validate_identifier(
"builder_attestation_id", self.builder_attestation_id
)
_validate_identifier("builder_key_id", self.builder_key_id)
_validate_oid("source_commit", self.source_commit)
_validate_oid("tree_oid", self.tree_oid)
for name in (
"artifact_sha256",
"manifest_sha256",
"package_closure_sha256",
"test_evidence_sha256",
"canary_evidence_sha256",
):
_validate_sha(name, getattr(self, name))
_parse_utc(self.issued_at)
@property
def canonical_payload(self) -> dict[str, Any]:
values = asdict(self)
values.pop("signature")
return values
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]) -> "BuilderAttestation":
values = dict(payload)
values.pop("fingerprint", None)
return cls(**values)
class BuilderAttestationVerifier:
"""Public-key-only verifier; no builder private material is accepted."""
def __init__(self, public_keys: Mapping[str, bytes]):
self.public_keys = {
_validate_identifier("builder_key_id", key_id): bytes(value)
for key_id, value in public_keys.items()
}
def verify(
self,
attestation: BuilderAttestation,
candidate: ReleaseCandidate,
release_plan: ReleasePlan,
) -> BuilderAttestation:
expected = (
candidate.source_commit,
candidate.tree_oid,
candidate.artifact_sha256,
candidate.manifest_sha256,
release_plan.deployment.package_closure_sha256,
)
actual = (
attestation.source_commit,
attestation.tree_oid,
attestation.artifact_sha256,
attestation.manifest_sha256,
attestation.package_closure_sha256,
)
if actual != expected:
raise CapabilityAuthorizationError(
"builder attestation does not bind candidate evidence"
)
key = self.public_keys.get(attestation.builder_key_id)
if key is None:
raise CapabilityAuthorizationError(
"builder attestation key is not trusted"
)
try:
signature = base64.b64decode(
attestation.signature, validate=True
)
Ed25519PublicKey.from_public_bytes(key).verify(
signature, attestation.canonical_bytes()
)
except (InvalidSignature, ValueError, TypeError) as exc:
raise CapabilityAuthorizationError(
"builder attestation signature is invalid"
) from exc
return attestation
@dataclass(frozen=True)
class TrustedCandidateBundle:
candidate: ReleaseCandidate
release_plan: ReleasePlan
checks: Mapping[str, bool]
attestation: BuilderAttestation | None = None
REQUIRED_CHECKS = frozenset(
{
"authoritative_git",
"artifact",
"canary",
"gitea",
"manifest",
"package_closure",
"rollback",
"secret_scan",
"tests",
}
)
def validate(
self,
attestation_verifier: BuilderAttestationVerifier | None = None,
) -> "TrustedCandidateBundle":
missing = self.REQUIRED_CHECKS.difference(self.checks)
if missing or any(self.checks.get(name) is not True for name in self.REQUIRED_CHECKS):
raise CapabilityAuthorizationError(
"trusted builder acceptance is incomplete"
)
candidate = self.candidate
if (
candidate.state != ReleaseState.READY_FOR_APPROVAL
or candidate.tests.get("passed") is not True
or candidate.canary.get("passed") is not True
or candidate.rollback_ready is not True
or candidate.git_consistency.get("consistent") is not True
or candidate.gitea_consistency.get("consistent") is not True
):
raise CapabilityAuthorizationError(
"candidate is not trusted release evidence"
)
plan = self.release_plan.validate()
if (
plan.deployment.source_commit != candidate.source_commit
or plan.deployment.tree_oid != candidate.tree_oid
or plan.deployment.artifact_sha256
!= candidate.artifact_sha256
or plan.deployment.manifest_sha256
!= candidate.manifest_sha256
):
raise CapabilityAuthorizationError(
"trusted candidate and release plan are inconsistent"
)
if attestation_verifier is not None:
if self.attestation is None:
raise CapabilityAuthorizationError(
"trusted builder attestation is required"
)
attestation_verifier.verify(
self.attestation, candidate, self.release_plan
)
return self
def acceptance_record(self) -> dict[str, Any]:
return {
"attestation": (
self.attestation.to_dict() if self.attestation else None
),
"candidate_id": self.candidate.candidate_id,
"checks": _sanitize(dict(self.checks)),
"release_plan_sha256": self.release_plan.sha256,
"status": "ACCEPTED",
}
class TrustedCandidateBuilder(Protocol):
def build(self, intent: Mapping[str, Any]) -> TrustedCandidateBundle:
...
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
owner_identity: str = ""
trust_domain: str = ""
issued_at: str = "1970-01-01T00:00:00Z"
not_before: str = "1970-01-01T00:00:00Z"
release_plan_sha256: 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))
if self.release_plan_sha256:
_validate_sha(
"release_plan_sha256", self.release_plan_sha256
)
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)
_parse_utc(self.issued_at)
_parse_utc(self.not_before)
@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,
"not_before": self.not_before,
"owner_identity": self.owner_identity,
"owner_approval_record_oid": self.owner_approval_record_oid,
"policy_sha256": self.policy_sha256,
"release_plan_sha256": self.release_plan_sha256,
"release_plan_id": self.release_plan_id,
"source_commit": self.source_commit,
"tree_oid": self.tree_oid,
"trust_domain": self.trust_domain,
"issued_at": self.issued_at,
}
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,
*,
allowed_trust_domains: Iterable[str] = (),
maximum_approval_lifetime_seconds: int = 3600,
):
self.keyring = keyring
self.revocations = revocations
self.allowed_trust_domains = frozenset(allowed_trust_domains)
self.maximum_approval_lifetime_seconds = int(
maximum_approval_lifetime_seconds
)
def _verify_signature(
self, approval: SignedReleasePlanApproval
) -> None:
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(),
)
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"
)
self._verify_signature(approval)
return approval
except CapabilityAuthorizationError:
raise
except Exception as exc:
raise CapabilityAuthorizationError(
"owner approval verification failed"
) from exc
def verify_release_plan(
self,
approval: SignedReleasePlanApproval,
candidate: ReleaseCandidate,
release_plan: ReleasePlan,
policy_sha256: str,
*,
now: str | datetime | None = None,
) -> SignedReleasePlanApproval:
try:
release_plan.validate()
policy_sha256 = _validate_sha(
"policy_sha256", policy_sha256
)
instant = (
datetime.now(timezone.utc)
if now is None
else _parse_utc(now)
if isinstance(now, str)
else now
)
if instant.tzinfo is None:
raise CapabilityAuthorizationError(
"verification time must be timezone-aware"
)
issued_at = _parse_utc(approval.issued_at)
not_before = _parse_utc(approval.not_before)
expires_at = _parse_utc(approval.expires_at)
if not issued_at <= not_before <= expires_at:
raise CapabilityAuthorizationError(
"owner approval time window is invalid"
)
if not not_before <= instant <= expires_at:
raise CapabilityAuthorizationError(
"owner approval is outside its validity window"
)
lifetime = (expires_at - issued_at).total_seconds()
if (
lifetime <= 0
or lifetime > self.maximum_approval_lifetime_seconds
):
raise CapabilityAuthorizationError(
"owner approval lifetime exceeds policy"
)
if (
not approval.owner_identity
or not approval.trust_domain
or (
self.allowed_trust_domains
and approval.trust_domain
not in self.allowed_trust_domains
)
):
raise CapabilityAuthorizationError(
"owner approval trust binding is invalid"
)
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,
release_plan.deployment.sha256,
policy_sha256,
release_plan.sha256,
release_plan.tag.approval_plan_id,
)
actual = (
approval.candidate_id,
approval.source_commit,
approval.tree_oid,
approval.artifact_sha256,
approval.manifest_sha256,
approval.deployment_plan_sha256,
approval.policy_sha256,
approval.release_plan_sha256,
approval.approval_plan_id,
)
if actual != expected:
raise CapabilityAuthorizationError(
"owner approval does not bind the complete release plan"
)
self._verify_signature(approval)
return approval
except CapabilityAuthorizationError:
raise
except Exception as exc:
raise CapabilityAuthorizationError(
"owner release plan verification failed"
) from exc
class CapabilityLedger:
"""Durable exclusive claims with leases and fail-closed recovery probes."""
STATES = frozenset(
{
"PENDING",
"CLAIMED",
"COMPLETED",
"FAILED_RECOVERABLE",
"RECONCILIATION_REQUIRED",
"MANUAL_OWNER_REVIEW_REQUIRED",
}
)
def __init__(
self,
root: Path,
*,
maximum_probe_age_seconds: float = 60,
):
self.root = Path(root)
self.maximum_probe_age_seconds = float(maximum_probe_age_seconds)
if self.maximum_probe_age_seconds <= 0:
raise ValueError("maximum_probe_age_seconds must be positive")
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 _probe_journal_path(self, operation_path: Path) -> Path:
return _safe_child(
self.root / ".probe-journal",
operation_path.stem,
".events.jsonl",
)
def _append_probe_event(
self,
operation_path: Path,
*,
probe: ProbeEvidence,
) -> dict[str, Any]:
path = self._probe_journal_path(operation_path)
path.parent.mkdir(parents=True, exist_ok=True)
os.chmod(path.parent, 0o700)
records = self.verify_probe_journal(operation_path, missing_ok=True)
previous_hash = (
records[-1]["event_hash"] if records else JOURNAL_GENESIS_HASH
)
core = {
"sequence": len(records) + 1,
"previous_hash": previous_hash,
"payload": probe.to_dict(),
}
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())
os.chmod(path, 0o600)
_fsync_directory(path.parent)
return record
def verify_probe_journal(
self,
operation_path: Path,
*,
missing_ok: bool = False,
) -> list[dict[str, Any]]:
path = self._probe_journal_path(operation_path)
if not path.exists():
if missing_ok:
return []
raise JournalIntegrityError("capability probe journal is missing")
records: list[dict[str, Any]] = []
previous = JOURNAL_GENESIS_HASH
for sequence, line in enumerate(
path.read_text(encoding="utf-8").splitlines(), 1
):
try:
record = json.loads(line)
except json.JSONDecodeError as exc:
raise JournalIntegrityError(
"capability probe journal contains invalid JSON"
) from exc
if set(record) != {
"schema_version",
"sequence",
"previous_hash",
"payload",
"event_hash",
}:
raise JournalIntegrityError(
"capability probe journal 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"] != sequence
or record["previous_hash"] != previous
or record["event_hash"] != expected_hash
):
raise JournalIntegrityError(
"capability probe journal hash chain verification failed"
)
previous = record["event_hash"]
records.append(record)
return records
@staticmethod
def _coerce_probe(
value: ClaimProbeResult | ProbeEvidence | Mapping[str, Any],
*,
operation_digest: str,
now: float,
) -> ProbeEvidence:
if isinstance(value, ProbeEvidence):
return value
if isinstance(value, Mapping):
return ProbeEvidence(
result=ClaimProbeResult(value["result"]),
operation_digest=str(value["operation_digest"]),
observed_at=float(value["observed_at"]),
evidence=dict(value.get("evidence") or {}),
)
return ProbeEvidence(
result=ClaimProbeResult(value),
operation_digest=operation_digest,
observed_at=now,
evidence={},
)
def _run_probe_locked(
self,
path: Path,
record: dict[str, Any],
side_effect_probe: Callable[
[], ClaimProbeResult | ProbeEvidence | Mapping[str, Any]
],
*,
now: float,
) -> tuple[dict[str, Any], ProbeEvidence]:
probe = self._coerce_probe(
side_effect_probe(),
operation_digest=str(record["operation_digest"]),
now=now,
).validate(
expected_operation_digest=str(record["operation_digest"]),
now=now,
maximum_age_seconds=self.maximum_probe_age_seconds,
)
event = self._append_probe_event(path, probe=probe)
record.update(
{
"last_probe": probe.to_dict(),
"probe_journal_event_hash": event["event_hash"],
"probe_result": probe.result.value,
}
)
return record, probe
def read(
self,
approval: SignedReleasePlanApproval,
capability: str,
*,
operation: str,
) -> dict[str, Any] | None:
path = self._operation_path(approval, capability, operation)
if not path.is_file():
return None
record = json.loads(path.read_text(encoding="utf-8"))
self._assert_record_matches(
record,
self._immutable_payload(approval, capability, operation),
)
return _sanitize(record)
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 | ProbeEvidence | Mapping[str, Any]
]
| 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 in {
"RECONCILIATION_REQUIRED",
"MANUAL_OWNER_REVIEW_REQUIRED",
}:
return {**record, "status": state}
recovery_required = state == "FAILED_RECOVERABLE"
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"}
recovery_required = True
if recovery_required:
if side_effect_probe is None:
return {**record, "status": "PROBE_REQUIRED"}
record, probe = self._run_probe_locked(
path,
record,
side_effect_probe,
now=timestamp,
)
if probe.result == ClaimProbeResult.APPLIED_VERIFIED:
record.update(
{
"completed_at": utc_now(),
"state": "COMPLETED",
}
)
self._write(path, record)
return {**record, "status": "IDEMPOTENT"}
if probe.result == ClaimProbeResult.INDETERMINATE:
record.update(
{
"state": "RECONCILIATION_REQUIRED",
}
)
self._write(path, record)
return {**record, "status": record["state"]}
if probe.result == ClaimProbeResult.CONFLICT:
record.update(
{"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]:
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") != "CLAIMED"
or 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]:
recovery_probe = kwargs.pop("_recovery_probe", self.recovery_probe)
stop_after_not_applied_probe = bool(
kwargs.pop("_stop_after_not_applied_probe", False)
)
claim = self.ledger.claim(
approval,
self.capability,
operation=operation,
executor_id=self.executor_id,
lease_seconds=self.lease_seconds,
side_effect_probe=recovery_probe,
)
if (
stop_after_not_applied_probe
and claim["status"] == "CLAIMED"
and claim.get("probe_result")
== ClaimProbeResult.NOT_APPLIED.value
):
self.ledger.fail_recoverable(
approval,
self.capability,
operation=operation,
claim_id=claim["claim_id"],
reason="verified_not_applied",
)
return {
"capability": self.capability,
"probe_result": ClaimProbeResult.NOT_APPLIED.value,
"status": "NOT_APPLIED",
}
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"] in {
"PROBE_REQUIRED",
"RECONCILIATION_REQUIRED",
"MANUAL_OWNER_REVIEW_REQUIRED",
}:
return {
"capability": self.capability,
"probe_result": claim.get("probe_result"),
"status": claim["status"],
}
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],
*,
recovery_probe: Callable[
[], ClaimProbeResult | ProbeEvidence | Mapping[str, Any]
]
| None = None,
executor_id: str = "silent-release-runtime-switch",
lease_seconds: float = 300,
):
super().__init__(
capability="runtime_switch",
ledger=ledger,
adapter=adapter,
recovery_probe=recovery_probe,
executor_id=executor_id,
lease_seconds=lease_seconds,
)
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 = "",
publication_probe: Callable[
[TagPlan], TagPublicationProbeEvidence
]
| None = None,
executor_id: str = "silent-release-tag-publish",
lease_seconds: float = 300,
):
self.expected_repository = expected_repository
self.publication_probe = publication_probe
super().__init__(
capability="tag_publish",
ledger=ledger,
adapter=adapter,
executor_id=executor_id,
lease_seconds=lease_seconds,
)
def execute(
self,
approval: SignedReleasePlanApproval,
*,
operation: str,
tag_plan: TagPlan | None = None,
**kwargs: Any,
) -> dict[str, Any]:
recovery_probe = None
if self.publication_probe is not None and tag_plan is not None:
immutable = self.ledger._immutable_payload(
approval, self.capability, operation
)
def recovery_probe() -> ProbeEvidence:
evidence = self.publication_probe(tag_plan)
return ProbeEvidence(
result=classify_tag_publication(evidence, tag_plan),
operation_digest=immutable["operation_digest"],
observed_at=evidence.observed_at,
evidence=evidence.to_dict(),
)
adapter_kwargs = dict(kwargs)
if tag_plan is not None:
adapter_kwargs["tag_plan"] = tag_plan
result = super().execute(
approval,
operation=operation,
_recovery_probe=recovery_probe,
_stop_after_not_applied_probe=True,
**adapter_kwargs,
)
if (
result["status"] == "EXECUTED"
and result.get("result", {}).get("classification")
in {member.value for member in ClaimProbeResult}
):
classification = ClaimProbeResult(
result["result"]["classification"]
)
if classification == ClaimProbeResult.CONFLICT:
raise TagPublicationConflict(
"tag publication conflicts with the signed tag plan"
)
if classification == ClaimProbeResult.INDETERMINATE:
return {
"capability": self.capability,
"probe_result": classification.value,
"status": "RECONCILIATION_REQUIRED",
}
return result
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],
*,
recovery_probe: Callable[
[], ClaimProbeResult | ProbeEvidence | Mapping[str, Any]
]
| None = None,
executor_id: str = "silent-release-finalization",
lease_seconds: float = 300,
):
super().__init__(
capability="baseline_registry_finalize",
ledger=ledger,
adapter=adapter,
recovery_probe=recovery_probe,
executor_id=executor_id,
lease_seconds=lease_seconds,
)
def validate_result(self, approval, result):
required = {
"verified": True,
"tag_verified": True,
"remote_peeled_target_oid": approval.source_commit,
"gitea_api_peeled_target_oid": approval.source_commit,
"gitea_listing_visible": True,
"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(
"finalization is blocked until exact tag proof is verified"
)
class BaselineController(BaselineRegistryFinalizeController):
"""Narrow Baseline half of the governed finalization capability."""
def validate_result(self, approval, result):
super().validate_result(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):
super().validate_result(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 GovernedFinalizationCoordinator:
"""Registry is verified first; Baseline is the final mutation."""
def __init__(
self,
registry: RegistryController,
baseline: BaselineController,
):
self.registry = registry
self.baseline = baseline
def execute(
self,
approval: SignedReleasePlanApproval,
*,
operation: str,
finalization_plan: FinalizationPlan,
) -> dict[str, Any]:
registry = self.registry.execute(
approval,
operation=f"{operation}:registry",
finalization_plan=finalization_plan,
)
if registry["status"] in {
"BUSY",
"PROBE_REQUIRED",
"RECONCILIATION_REQUIRED",
"MANUAL_OWNER_REVIEW_REQUIRED",
}:
return registry
baseline = self.baseline.execute(
approval,
operation=f"{operation}:baseline",
finalization_plan=finalization_plan,
)
if baseline["status"] in {
"BUSY",
"PROBE_REQUIRED",
"RECONCILIATION_REQUIRED",
"MANUAL_OWNER_REVIEW_REQUIRED",
}:
return baseline
return {
"baseline_status": baseline["status"],
"registry_status": registry["status"],
"result": baseline.get("result") or {},
"status": "EXECUTED",
}
class RecoverableReleaseError(RuntimeError):
"""A side effect is unknown or temporarily unverifiable."""
class TagPublicationConflict(RuntimeError):
"""An existing tag disagrees with the signed immutable tag plan."""
class ReleaseOrchestrator:
"""Durable one-approval execution with Baseline/Registry finalized last."""
def __init__(
self,
*,
candidates: ReleaseCandidateStore,
plans: ReleasePlanStore,
approvals: SignedApprovalStore,
sagas: ReleaseSagaStore,
approval_verifier: SignedApprovalVerifier,
trusted_policy: TrustedReleasePolicy,
actual_plan_factory: Callable[[ReleaseCandidate], ReleasePlan],
pre_switch_validator: PreSwitchValidator,
runtime_switch: RuntimeSwitchController,
runtime_verify: Callable[..., Mapping[str, Any]],
runtime_rollback: Callable[..., Mapping[str, Any]],
tag_publish: HardenedTagController,
finalizer: GovernedFinalizationCoordinator,
):
self.candidates = candidates
self.plans = plans
self.approvals = approvals
self.sagas = sagas
self.approval_verifier = approval_verifier
self.trusted_policy = trusted_policy
self.actual_plan_factory = actual_plan_factory
self.pre_switch_validator = pre_switch_validator
self.runtime_switch = runtime_switch
self.runtime_verify = runtime_verify
self.runtime_rollback = runtime_rollback
self.tag_publish = tag_publish
self.finalizer = finalizer
def _transition(
self,
saga: ReleaseSaga,
target: SagaState,
event: str,
details: Mapping[str, Any] | None = None,
) -> ReleaseSaga:
return self.sagas.compare_and_set(
saga.candidate_id,
expected_revision=saga.revision,
expected_states={saga.state},
target=target,
event=event,
details=details,
)
def _load_and_reverify(
self, candidate_id: str
) -> tuple[
ReleaseCandidate,
ReleasePlan,
SignedReleasePlanApproval,
ReleaseSaga,
]:
candidate = self.candidates.load(candidate_id)
plan = self.plans.load(candidate_id)
approval = self.approvals.load(candidate_id)
saga = self.sagas.load(candidate_id)
if not saga.journal_integrity:
raise JournalIntegrityError(
"Saga journal requires manual owner review"
)
actual_plan = self.actual_plan_factory(candidate)
self.trusted_policy.validate_deployment(actual_plan.deployment)
if actual_plan.sha256 != plan.sha256:
raise StaleApprovalError(
"actual deployment configuration changed after approval"
)
self.approval_verifier.verify_release_plan(
approval,
candidate,
plan,
plan.policy_sha256,
)
return candidate, plan, approval, saga
@staticmethod
def _runtime_proof_matches(
proof: Mapping[str, Any],
candidate: ReleaseCandidate,
plan: ReleasePlan,
) -> bool:
expected = {
"verified": True,
"source_commit": candidate.source_commit,
"tree_oid": candidate.tree_oid,
"artifact_sha256": candidate.artifact_sha256,
"manifest_sha256": candidate.manifest_sha256,
"repository_identity": plan.deployment.repository_identity,
"authoritative_git_dir": (
plan.deployment.authoritative_git_dir
),
"writer_disabled": True,
}
return all(proof.get(key) == value for key, value in expected.items())
@staticmethod
def _finalization_proof_matches(
proof: Mapping[str, Any], plan: ReleasePlan
) -> bool:
expected = {
"verified": True,
"remote_tag_oid": plan.tag.expected_tag_oid,
"remote_peeled_target_oid": plan.tag.expected_target_oid,
"registry_record": (
plan.finalization.registry_expected_record
),
"baseline_record": (
plan.finalization.baseline_expected_record
),
"baseline_written_last": True,
}
return all(proof.get(key) == value for key, value in expected.items())
def _rollback(
self,
saga: ReleaseSaga,
candidate: ReleaseCandidate,
plan: ReleasePlan,
reason: str,
) -> dict[str, Any]:
if saga.state != SagaState.ROLLBACK_PENDING:
saga = self._transition(
saga,
SagaState.ROLLBACK_PENDING,
"runtime_rollback_pending",
{"reason": reason},
)
proof = dict(
self.runtime_rollback(candidate=candidate, plan=plan)
)
if proof.get("verified") is not True:
return {
"candidate_id": candidate.candidate_id,
"state": saga.state.value,
"status": "ROLLBACK_PENDING",
}
saga = self._transition(
saga,
SagaState.ROLLED_BACK,
"runtime_rollback_verified",
{"proof": proof},
)
return {
"candidate_id": candidate.candidate_id,
"state": saga.state.value,
"status": "ROLLED_BACK",
}
def _manual(
self, saga: ReleaseSaga, reason: str
) -> dict[str, Any]:
if saga.state != SagaState.MANUAL_OWNER_REVIEW_REQUIRED:
saga = self._transition(
saga,
SagaState.MANUAL_OWNER_REVIEW_REQUIRED,
"manual_owner_review_required",
{"reason": reason},
)
return {
"candidate_id": saga.candidate_id,
"state": saga.state.value,
"status": saga.state.value,
}
def _reconciliation(
self, saga: ReleaseSaga, reason: str
) -> dict[str, Any]:
if saga.state != SagaState.RECONCILIATION_REQUIRED:
saga = self._transition(
saga,
SagaState.RECONCILIATION_REQUIRED,
"release_reconciliation_required",
{"reason": reason},
)
return {
"candidate_id": saga.candidate_id,
"state": saga.state.value,
"status": saga.state.value,
}
def execute(self, candidate_id: str) -> dict[str, Any]:
candidate, plan, approval, saga = self._load_and_reverify(
candidate_id
)
if saga.state == SagaState.MANUAL_OWNER_REVIEW_REQUIRED:
return self._manual(saga, "existing manual review gate")
if saga.state in {SagaState.COMPLETED, SagaState.ROLLED_BACK}:
return {
"candidate_id": candidate_id,
"state": saga.state.value,
"status": "IDEMPOTENT",
}
if saga.state == SagaState.ROLLBACK_PENDING:
return self._rollback(
saga, candidate, plan, "resuming rollback"
)
if saga.state == SagaState.RECONCILIATION_REQUIRED:
return self._reconciliation(
saga, "automatic reconciliation is forbidden"
)
runtime_has_switched = saga.state in {
SagaState.SWITCH_PENDING,
SagaState.SWITCHED,
SagaState.RUNTIME_VERIFYING,
SagaState.RUNTIME_VERIFIED,
SagaState.TAG_PENDING,
SagaState.TAG_VERIFIED,
SagaState.BASELINE_REGISTRY_PENDING,
}
try:
if saga.state in {
SagaState.APPROVED,
SagaState.PRE_SWITCH_REVALIDATING,
}:
if saga.state == SagaState.APPROVED:
saga = self._transition(
saga,
SagaState.PRE_SWITCH_REVALIDATING,
"pre_switch_revalidation_started",
)
self.pre_switch_validator.validate(
candidate,
approval,
plan.deployment,
expected_repository=(
plan.deployment.repository_identity
),
)
saga = self._transition(
saga,
SagaState.SWITCH_PENDING,
"runtime_switch_pending",
)
if saga.state == SagaState.SWITCH_PENDING:
result = self.runtime_switch.execute(
approval,
operation=f"runtime:{plan.sha256}",
candidate=candidate,
plan=plan,
)
if result["status"] == "BUSY":
return {
"candidate_id": candidate_id,
"state": saga.state.value,
"status": "BUSY",
}
if result["status"] == "RECONCILIATION_REQUIRED":
return self._reconciliation(
saga, "runtime switch probe is indeterminate"
)
if result["status"] == "MANUAL_OWNER_REVIEW_REQUIRED":
return self._manual(
saga, "runtime switch probe found a conflict"
)
if result["status"] == "PROBE_REQUIRED":
return self._reconciliation(
saga, "runtime switch probe is required"
)
runtime_has_switched = True
saga = self._transition(
saga,
SagaState.SWITCHED,
"runtime_switch_completed",
{"capability_status": result["status"]},
)
if saga.state in {
SagaState.SWITCHED,
SagaState.RUNTIME_VERIFYING,
}:
if saga.state == SagaState.SWITCHED:
saga = self._transition(
saga,
SagaState.RUNTIME_VERIFYING,
"runtime_verification_started",
)
proof = dict(
self.runtime_verify(candidate=candidate, plan=plan)
)
if not self._runtime_proof_matches(
proof, candidate, plan
):
raise RecoverableReleaseError(
"runtime verification failed"
)
saga = self._transition(
saga,
SagaState.RUNTIME_VERIFIED,
"runtime_verification_completed",
{"proof": proof},
)
if saga.state == SagaState.RUNTIME_VERIFIED:
saga = self._transition(
saga,
SagaState.TAG_PENDING,
"tag_publication_pending",
)
if saga.state == SagaState.TAG_PENDING:
result = self.tag_publish.execute(
approval,
operation=f"tag:{plan.tag.sha256}",
tag_plan=plan.tag,
)
if result["status"] == "BUSY":
return {
"candidate_id": candidate_id,
"state": saga.state.value,
"status": "BUSY",
}
if result["status"] == "RECONCILIATION_REQUIRED":
return self._reconciliation(
saga, "tag publication is indeterminate"
)
if result["status"] == "MANUAL_OWNER_REVIEW_REQUIRED":
return self._manual(
saga, "tag publication conflicts with signed plan"
)
if result["status"] == "PROBE_REQUIRED":
return self._reconciliation(
saga, "tag publication probe is required"
)
if result["status"] == "NOT_APPLIED":
return self._rollback(
saga,
candidate,
plan,
"tag publication was verified absent",
)
saga = self._transition(
saga,
SagaState.TAG_VERIFIED,
"tag_publication_verified",
{"capability_status": result["status"]},
)
if saga.state == SagaState.TAG_VERIFIED:
saga = self._transition(
saga,
SagaState.BASELINE_REGISTRY_PENDING,
"baseline_registry_finalization_pending",
)
if saga.state == SagaState.BASELINE_REGISTRY_PENDING:
result = self.finalizer.execute(
approval,
operation=f"finalize:{plan.finalization.sha256}",
finalization_plan=plan.finalization,
)
if result["status"] == "BUSY":
return {
"candidate_id": candidate_id,
"state": saga.state.value,
"status": "BUSY",
}
if result["status"] == "RECONCILIATION_REQUIRED":
return self._reconciliation(
saga, "finalization probe is indeterminate"
)
if result["status"] == "MANUAL_OWNER_REVIEW_REQUIRED":
return self._manual(
saga, "finalization probe found a conflict"
)
if result["status"] == "PROBE_REQUIRED":
return self._reconciliation(
saga, "finalization probe is required"
)
proof = result.get("result") or {}
if not self._finalization_proof_matches(proof, plan):
raise RecoverableReleaseError(
"final consistency verification failed"
)
saga = self._transition(
saga,
SagaState.COMPLETED,
"release_consistency_verified",
{"proof": proof},
)
return {
"candidate_id": candidate_id,
"state": saga.state.value,
"status": "VERIFIED",
}
except TagPublicationConflict as exc:
return self._manual(saga, str(exc))
except Exception as exc:
if saga.state in {
SagaState.TAG_PENDING,
SagaState.TAG_VERIFIED,
SagaState.BASELINE_REGISTRY_PENDING,
}:
return self._reconciliation(saga, type(exc).__name__)
if runtime_has_switched:
return self._rollback(
saga, candidate, plan, type(exc).__name__
)
raise
class ReleaseRecoveryWorker:
def __init__(
self,
saga_store: ReleaseSagaStore,
orchestrator: ReleaseOrchestrator,
*,
shadow_mode: bool,
):
self.saga_store = saga_store
self.orchestrator = orchestrator
self.shadow_mode = bool(shadow_mode)
def run_once(self) -> dict[str, Any]:
reports: list[dict[str, Any]] = []
root = self.saga_store.root
for journal in sorted(root.glob("*.events.jsonl")):
candidate_id = journal.name[: -len(".events.jsonl")]
saga = self.saga_store.load(candidate_id)
if saga.state == SagaState.MANUAL_OWNER_REVIEW_REQUIRED:
reports.append(
{
"candidate_id": candidate_id,
"status": "SKIPPED_MANUAL_REVIEW",
}
)
elif self.shadow_mode:
reports.append(
{
"candidate_id": candidate_id,
"state": saga.state.value,
"status": "SHADOW_REPORTED",
}
)
elif saga.state not in {
SagaState.COMPLETED,
SagaState.ROLLED_BACK,
}:
reports.append(self.orchestrator.execute(candidate_id))
return {
"shadow_mode": self.shadow_mode,
"reports": reports,
}
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,
candidate_builder: TrustedCandidateBuilder | None = None,
builder_attestation_verifier: BuilderAttestationVerifier | None = None,
trusted_policy: TrustedReleasePolicy | None = None,
orchestrator: ReleaseOrchestrator | None = None,
allow_legacy_candidate_input: bool = False,
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.candidate_builder = candidate_builder
self.builder_attestation_verifier = builder_attestation_verifier
self.trusted_policy = trusted_policy
self.orchestrator = orchestrator
self.allow_legacy_candidate_input = bool(
allow_legacy_candidate_input
)
self.expected_repository = expected_repository
self.policy_sha256 = policy_sha256
self.candidates = ReleaseCandidateStore(
self.root / "candidates"
)
self.plans = DeploymentPlanStore(self.root / "plans")
self.release_plans = ReleasePlanStore(
self.root / "release-plans"
)
self.sagas = ReleaseSagaStore(self.root / "sagas")
self.approvals_root = self.root / "approvals"
self.approvals = SignedApprovalStore(self.approvals_root)
self.intents_root = self.root / "intents"
self.acceptance_root = self.root / "acceptance"
self.notifications_root = self.root / "notifications"
self.capabilities_root = self.root / "capabilities"
if self.execution_enabled:
missing = [
name
for name, component in (
("approval_verifier", approval_verifier),
("candidate_builder", candidate_builder),
(
"builder_attestation_verifier",
builder_attestation_verifier,
),
("orchestrator", orchestrator),
("pre_switch_validator", pre_switch_validator),
("trusted_policy", trusted_policy),
)
if component is None
]
if missing:
raise CapabilityAuthorizationError(
"production execution components are missing: "
+ ", ".join(missing)
)
def create_release_intent(
self, payload: Mapping[str, Any]
) -> dict[str, Any]:
allowed = {"event_id", "source_commit"}
unknown = set(payload).difference(allowed)
if unknown:
raise CapabilityAuthorizationError(
"release intent contains caller-supplied evidence"
)
event_id = _validate_identifier(
"event_id", str(payload.get("event_id") or "")
)
source_commit = _validate_oid(
"source_commit", str(payload.get("source_commit") or "")
)
intent_id = "intent-" + hashlib.sha256(
f"{event_id}\0{source_commit}".encode("utf-8")
).hexdigest()[:24]
path = _safe_child(self.intents_root, intent_id)
record = {
"created_at": utc_now(),
"event_id": event_id,
"intent_id": intent_id,
"source_commit": source_commit,
"status": (
"BUILDING"
if self.candidate_builder is not None
else "BUILDER_UNAVAILABLE"
),
}
if path.exists():
existing = json.loads(path.read_text(encoding="utf-8"))
if (
existing.get("event_id") != event_id
or existing.get("source_commit") != source_commit
):
raise SagaConflict(
"release intent id is bound to different evidence"
)
return {
**_sanitize(existing),
"idempotent": True,
"execution_enabled": self.execution_enabled,
"shadow_mode": not self.execution_enabled,
"side_effects_executed": False,
}
_durable_json(path, record)
result = dict(record)
if self.candidate_builder is not None:
candidate = self.create_release_candidate(
{
"event_id": event_id,
"intent_id": intent_id,
"source_commit": source_commit,
}
)
result.update(
{
"candidate_id": candidate["candidate_id"],
"status": candidate["saga_state"],
}
)
_durable_json(path, result)
return {
**_sanitize(result),
"idempotent": False,
"execution_enabled": self.execution_enabled,
"shadow_mode": not self.execution_enabled,
"side_effects_executed": False,
}
@classmethod
def shadow(
cls, root: Path, *, runtime_executor: Any = None
) -> "SilentReleaseActionService":
return cls(
root,
execution_enabled=False,
runtime_executor=runtime_executor,
allow_legacy_candidate_input=True,
)
def create_release_candidate(
self, payload: Mapping[str, Any]
) -> dict[str, Any]:
if self.candidate_builder is not None:
forbidden_evidence = {
"artifact_sha256",
"canary",
"deployment_plan",
"git_consistency",
"gitea_consistency",
"manifest_sha256",
"release_plan",
"rollback_ready",
"tests",
"tree_oid",
}
if forbidden_evidence.intersection(payload):
raise CapabilityAuthorizationError(
"candidate evidence must come from the trusted builder"
)
bundle = self.candidate_builder.build(dict(payload)).validate(
self.builder_attestation_verifier
)
if self.trusted_policy is None:
raise CapabilityAuthorizationError(
"trusted release policy is not configured"
)
self.trusted_policy.validate_deployment(
bundle.release_plan.deployment
)
if (
bundle.release_plan.policy_sha256
!= self.policy_sha256
):
raise CapabilityAuthorizationError(
"trusted builder used a different release policy"
)
candidate = bundle.candidate
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.release_plans.save(
existing.candidate_id, bundle.release_plan
)
_durable_json(
_safe_child(self.acceptance_root, existing.candidate_id),
bundle.acceptance_record(),
)
saga = self.sagas.create(
existing.candidate_id,
f"pending-{existing.candidate_id}",
)
if saga.state == SagaState.PREPARING:
saga = self.sagas.compare_and_set(
existing.candidate_id,
expected_revision=saga.revision,
expected_states={SagaState.PREPARING},
target=SagaState.READY_FOR_APPROVAL,
event="trusted_candidate_ready_for_approval",
details={
"release_plan_sha256": (
bundle.release_plan.sha256
)
},
)
return {
**existing.to_dict(),
"release_plan_sha256": bundle.release_plan.sha256,
"saga_state": saga.state.value,
"builder_attestation_id": (
bundle.attestation.builder_attestation_id
if bundle.attestation
else None
),
"execution_enabled": self.execution_enabled,
"shadow_mode": not self.execution_enabled,
"side_effects_executed": False,
}
if not self.allow_legacy_candidate_input:
raise CapabilityAuthorizationError(
"trusted candidate builder is not configured"
)
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(),
"execution_enabled": self.execution_enabled,
"shadow_mode": not self.execution_enabled,
"side_effects_executed": False,
}
def get_release_saga(self, candidate_id: str) -> dict[str, Any]:
return {
**self.sagas.load(candidate_id).to_dict(),
"execution_enabled": self.execution_enabled,
"shadow_mode": not self.execution_enabled,
"side_effects_executed": False,
}
def get_release_plan(self, candidate_id: str) -> dict[str, Any]:
return {
**self.release_plans.load(candidate_id).to_dict(),
"candidate_id": candidate_id,
"execution_enabled": self.execution_enabled,
"shadow_mode": not self.execution_enabled,
"side_effects_executed": False,
}
def get_acceptance(self, candidate_id: str) -> dict[str, Any]:
record = json.loads(
_safe_child(self.acceptance_root, candidate_id).read_text(
encoding="utf-8"
)
)
return {
**_sanitize(record),
"execution_enabled": self.execution_enabled,
"shadow_mode": not self.execution_enabled,
"side_effects_executed": False,
}
def get_capability_status(
self, candidate_id: str
) -> dict[str, Any]:
self.candidates.load(candidate_id)
records: list[dict[str, Any]] = []
for path in sorted(self.capabilities_root.glob("*.json")):
try:
record = json.loads(path.read_text(encoding="utf-8"))
except (OSError, json.JSONDecodeError):
continue
if record.get("release_plan_id") == self.sagas.load(
candidate_id
).release_plan_id:
records.append(_sanitize(record))
return {
"candidate_id": candidate_id,
"capabilities": records,
"execution_enabled": self.execution_enabled,
"shadow_mode": not self.execution_enabled,
"side_effects_executed": False,
}
def get_recovery_status(self) -> dict[str, Any]:
reports: list[dict[str, Any]] = []
for journal in sorted(self.sagas.root.glob("*.events.jsonl")):
candidate_id = journal.name[: -len(".events.jsonl")]
saga = self.sagas.load(candidate_id)
reports.append(
{
"candidate_id": candidate_id,
"recovery_action": self.sagas.recovery_action(
saga.state
),
"state": saga.state.value,
}
)
return {
"execution_enabled": self.execution_enabled,
"shadow_mode": not self.execution_enabled,
"side_effects_executed": False,
"sagas": reports,
}
def ensure_owner_notification(
self, candidate_id: str
) -> dict[str, Any]:
self.candidates.load(candidate_id)
path = _safe_child(self.notifications_root, candidate_id)
payload = {
"candidate_id": candidate_id,
"created_at": utc_now(),
"notification_id": "notification-"
+ hashlib.sha256(candidate_id.encode("utf-8")).hexdigest()[:24],
"status": "OWNER_APPROVAL_REQUESTED",
}
path.parent.mkdir(parents=True, exist_ok=True)
try:
descriptor = os.open(
path, os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600
)
except FileExistsError:
existing = json.loads(path.read_text(encoding="utf-8"))
return {**_sanitize(existing), "idempotent": True}
with os.fdopen(descriptor, "w", encoding="utf-8") as output:
json.dump(payload, output, sort_keys=True)
output.write("\n")
output.flush()
os.fsync(output.fileno())
_fsync_directory(path.parent)
return {**payload, "idempotent": False}
def get_approval_envelope(self, candidate_id: str) -> dict[str, Any]:
candidate = self.candidates.load(candidate_id)
try:
release_plan = self.release_plans.load(candidate_id)
deployment_plan_sha256 = release_plan.deployment.sha256
release_plan_sha256 = release_plan.sha256
except FileNotFoundError:
try:
deployment_plan_sha256 = self.plans.load(
candidate_id
).sha256
except FileNotFoundError:
deployment_plan_sha256 = None
release_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,
"release_plan_sha256": release_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)
try:
release_plan = self.release_plans.load(
approval.candidate_id
)
except FileNotFoundError:
plan = self.plans.load(approval.candidate_id)
self.approval_verifier.verify(
approval,
candidate,
plan,
self.policy_sha256,
)
else:
self.approval_verifier.verify_release_plan(
approval,
candidate,
release_plan,
self.policy_sha256,
)
_, created = self.approvals.save(
approval.candidate_id, approval
)
saga = self.sagas.bind_release_plan(
approval.candidate_id, approval.release_plan_id
)
if saga.state == SagaState.READY_FOR_APPROVAL:
saga = self.sagas.compare_and_set(
approval.candidate_id,
expected_revision=saga.revision,
expected_states={SagaState.READY_FOR_APPROVAL},
target=SagaState.APPROVED,
event="signed_owner_approval_accepted",
details={
"approval_fingerprint": approval.fingerprint,
"release_plan_sha256": (
approval.release_plan_sha256 or None
),
},
)
return {
"fingerprint": approval.fingerprint,
"saga_state": saga.state.value,
"status": (
"VERIFIED_AND_STORED" if created else "IDEMPOTENT"
),
"execution_enabled": self.execution_enabled,
"shadow_mode": not self.execution_enabled,
"side_effects_executed": False,
}
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,
"shadow_mode": True,
"side_effects": [],
"side_effects_executed": False,
"status": "SHADOW_COMPLETED",
}
if self.orchestrator is None:
raise CapabilityAuthorizationError(
"release orchestrator is not configured"
)
# Caller-supplied approvals are deliberately ignored. The orchestrator
# loads the durable approval bound during submit_owner_approval().
return _sanitize(dict(self.orchestrator.execute(candidate_id)))