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

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