Files
Unraid-AppOps-Gateway-Public/backend/app/services/repopulse_legacy_adoption_protocol.py
T

985 lines
38 KiB
Python

"""Pure state and evidence rules for the fixed RepoPulse legacy adoption.
This module deliberately has no Portainer, filesystem, Docker, database, or
controller client. The integrated operator runner uses these rules only after
the fixed managed profile and controller-owned durable intent are verified.
"""
from __future__ import annotations
import hashlib
import json
import re
from copy import deepcopy
from typing import Any
from backend.app.services.enrollment_profiles import PROFILE_VERSION
PROTOCOL_VERSION = "itworx.repopulse-legacy-adoption/v1"
INTENT_VERSION = "itworx.repopulse-legacy-adoption-intent/v1"
CONTROLLER_RECEIPT_VERSION = "itworx.repopulse-legacy-adoption-receipt/v1"
REPOSITORY = "jens/repopulse"
APPLICATION_ID = "repopulse"
LEGACY_VOLUME = "repopulse_data"
EXECUTION_ENABLED = True
_SHA256 = re.compile(r"^[0-9a-f]{64}$")
_SHA1 = re.compile(r"^[0-9a-f]{40}$")
_IDEMPOTENCY_KEY = re.compile(r"^[A-Za-z0-9._:-]{8,128}$")
_PROFILE_ID = "repopulse-authenticated"
_ENROLLMENT_ID = re.compile(r"^enr-[0-9a-f]{20}$")
_INTENT_ID = re.compile(r"^rpa-[0-9a-f]{20}$")
_LEGACY_CHECKS = {
"legacy_registry",
"controller_state",
"gitea_ancestry",
"runtime_identity_and_health",
"legacy_storage_topology",
"legacy_appdata",
"product_api",
"database_health",
"queue_quiescence",
"database_baseline",
}
_PHASES = (
"reserved",
"target_gates_ready",
"quiesced",
"snapshot_verified",
"target_registered",
"appops_owned",
"completed",
)
_STEP_CONTRACTS: dict[str, tuple[str, str]] = {
"target_gates": ("reserved", "target_gates_ready"),
"quiescence": ("target_gates_ready", "quiesced"),
"snapshot": ("quiesced", "snapshot_verified"),
"controller_registration": ("snapshot_verified", "target_registered"),
"appops_ownership": ("target_registered", "appops_owned"),
}
_RECOVERY_REASONS = {
"controller_unavailable",
"database_backup_failed",
"registry_receipt_uncertain",
"target_health_failed",
"appops_commit_uncertain",
"preflight_drift",
"operator_interruption",
"unknown_external_effect",
}
_STEP_EVIDENCE_KEYS = {
"target_gates": "targetGates",
"quiescence": "quiescence",
"snapshot": "snapshot",
"controller_registration": "controllerRegistration",
"appops_ownership": "appopsOwnership",
"completion": "completion",
}
class AdoptionProtocolError(ValueError):
"""A fail-closed protocol or evidence validation error."""
def __init__(self, code: str):
super().__init__(code)
self.code = code
def _canonical(value: object) -> bytes:
try:
return json.dumps(
value,
sort_keys=True,
separators=(",", ":"),
ensure_ascii=True,
allow_nan=False,
).encode("ascii")
except (TypeError, ValueError, UnicodeEncodeError) as exc:
raise AdoptionProtocolError("invalid_evidence_encoding") from exc
def _digest(value: object) -> str:
return hashlib.sha256(_canonical(value)).hexdigest()
def _is_count(value: object) -> bool:
return isinstance(value, int) and not isinstance(value, bool) and value >= 0
def _validate_preflight_projection(value: object) -> dict[str, Any]:
required = {
"repository",
"main_revision",
"live_revision",
"container_id_sha256",
"container_image_sha256",
"queue",
"database",
"container_state",
"container_health",
}
_need(isinstance(value, dict) and set(value) == required, "intent_preflight_invalid")
projection = value
_need(
projection.get("repository") == REPOSITORY
and isinstance(projection.get("main_revision"), str)
and _SHA1.fullmatch(projection["main_revision"]) is not None
and isinstance(projection.get("live_revision"), str)
and _SHA1.fullmatch(projection["live_revision"]) is not None
and isinstance(projection.get("container_id_sha256"), str)
and _SHA256.fullmatch(projection["container_id_sha256"]) is not None
and isinstance(projection.get("container_image_sha256"), str)
and _SHA256.fullmatch(projection["container_image_sha256"]) is not None
and projection.get("container_state") == "running"
and projection.get("container_health") == "healthy",
"intent_preflight_invalid",
)
queue = projection.get("queue")
database = projection.get("database")
_need(
isinstance(queue, dict)
and set(queue) == {"queued", "active", "completed", "failed"}
and all(_is_count(queue.get(key)) for key in queue)
and queue.get("queued") == 0
and queue.get("active") == 0,
"intent_preflight_queue_invalid",
)
_need(
isinstance(database, dict)
and set(database)
== {"database_bytes", "journal_bytes", "scan_count", "compacted_scan_count"}
and all(_is_count(database.get(key)) for key in database)
and database.get("database_bytes", 0) > 0
and database.get("scan_count", -1) >= database.get("compacted_scan_count", 0),
"intent_preflight_database_invalid",
)
return projection
def _need(condition: bool, code: str) -> None:
if not condition:
raise AdoptionProtocolError(code)
def _safe_preflight_projection(report: object) -> dict[str, Any]:
"""Accept only the existing read-only preflight's allowlisted identity facts."""
_need(isinstance(report, dict), "legacy_preflight_missing")
source = report
_need(
source.get("apiVersion") == "itworx.repopulse-legacy-adoption-preflight/v1"
and source.get("repository") == REPOSITORY
and source.get("status") == "preflight_passed"
and source.get("readOnly") is True
and source.get("adoptionAuthorized") is False,
"legacy_preflight_not_compatible",
)
compatibility = source.get("legacyCompatibility")
_need(
isinstance(compatibility, dict)
and compatibility.get("status") == "passed",
"legacy_preflight_not_compatible",
)
checks = compatibility.get("checks")
_need(isinstance(checks, list), "legacy_preflight_checks_missing")
observed: dict[str, str] = {}
for check in checks:
_need(isinstance(check, dict), "legacy_preflight_check_invalid")
code = check.get("code")
_need(
isinstance(code, str)
and code in _LEGACY_CHECKS
and check.get("gate") == "legacy_compatibility"
and check.get("status") == "passed"
and code not in observed,
"legacy_preflight_check_failed",
)
observed[code] = "passed"
_need(set(observed) == _LEGACY_CHECKS, "legacy_preflight_check_incomplete")
evidence = source.get("evidence")
_need(isinstance(evidence, dict), "legacy_preflight_evidence_missing")
main_revision = evidence.get("main_revision")
live_revision = evidence.get("live_revision")
queue = evidence.get("queue")
database = evidence.get("database")
_need(
isinstance(main_revision, str)
and _SHA1.fullmatch(main_revision) is not None
and isinstance(live_revision, str)
and _SHA1.fullmatch(live_revision) is not None,
"legacy_preflight_revision_invalid",
)
_need(
isinstance(queue, dict)
and set(queue) == {"queued", "active", "completed", "failed"}
and all(_is_count(queue.get(key)) for key in queue)
and queue.get("queued") == 0
and queue.get("active") == 0,
"legacy_preflight_queue_not_quiescent",
)
_need(
isinstance(database, dict)
and all(
_is_count(database.get(key))
for key in (
"database_bytes",
"journal_bytes",
"scan_count",
"compacted_scan_count",
)
)
and database.get("database_bytes", 0) > 0
and database.get("scan_count", -1)
>= database.get("compacted_scan_count", 0),
"legacy_preflight_database_facts_invalid",
)
_need(
evidence.get("container_state") == "running"
and evidence.get("container_health") == "healthy",
"legacy_preflight_runtime_invalid",
)
return {
"repository": REPOSITORY,
"main_revision": main_revision,
"live_revision": live_revision,
"container_id_sha256": evidence.get("container_id_sha256"),
"container_image_sha256": evidence.get("container_image_sha256"),
"queue": {
key: queue[key]
for key in ("queued", "active", "completed", "failed")
},
"database": {
key: database[key]
for key in (
"database_bytes",
"journal_bytes",
"scan_count",
"compacted_scan_count",
)
},
"container_state": "running",
"container_health": "healthy",
}
def create_intent(report: object, idempotency_key: str) -> dict[str, Any]:
"""Create deterministic fixed-identity intent data from a passing preflight.
Persistence is deliberately left to the future AppOps/controller adapters;
this pure constructor never writes a database or controller state file.
"""
_need(
isinstance(idempotency_key, str)
and _IDEMPOTENCY_KEY.fullmatch(idempotency_key) is not None,
"idempotency_key_invalid",
)
projection = _safe_preflight_projection(report)
idempotency_sha256 = hashlib.sha256(
f"{PROTOCOL_VERSION}:{REPOSITORY}:{idempotency_key}".encode("ascii")
).hexdigest()
intent_id = f"rpa-{idempotency_sha256[:20]}"
intent = {
"apiVersion": INTENT_VERSION,
"protocolVersion": PROTOCOL_VERSION,
"intentId": intent_id,
"idempotencySha256": idempotency_sha256,
"repository": REPOSITORY,
"applicationId": APPLICATION_ID,
"phase": "reserved",
"status": "in_progress",
"preflightSha256": _digest(projection),
"legacyVolume": LEGACY_VOLUME,
"legacyVolumePreserved": True,
"evidence": {"preflight": projection},
"receipts": {},
"recovery": None,
}
return _validate_intent(intent)
def reserve_or_replay(
existing_intent: object | None, report: object, idempotency_key: str
) -> dict[str, Any]:
"""Enforce the single fixed adoption slot and exact retry identity."""
candidate = create_intent(report, idempotency_key)
if existing_intent is None:
return candidate
existing = _validate_intent(existing_intent)
_need(
existing["idempotencySha256"] == candidate["idempotencySha256"],
"one_time_adoption_already_reserved",
)
_need(
existing["preflightSha256"] == candidate["preflightSha256"],
"idempotency_conflict",
)
return deepcopy(existing)
def _validate_intent(intent: object) -> dict[str, Any]:
required = {
"apiVersion",
"protocolVersion",
"intentId",
"idempotencySha256",
"repository",
"applicationId",
"phase",
"status",
"preflightSha256",
"legacyVolume",
"legacyVolumePreserved",
"evidence",
"receipts",
"recovery",
}
_need(isinstance(intent, dict) and set(intent) == required, "intent_shape_invalid")
value = intent
_need(
value.get("apiVersion") == INTENT_VERSION
and value.get("protocolVersion") == PROTOCOL_VERSION
and value.get("repository") == REPOSITORY
and value.get("applicationId") == APPLICATION_ID
and value.get("legacyVolume") == LEGACY_VOLUME
and value.get("legacyVolumePreserved") is True
and isinstance(value.get("intentId"), str)
and _INTENT_ID.fullmatch(value["intentId"]) is not None
and isinstance(value.get("idempotencySha256"), str)
and _SHA256.fullmatch(value["idempotencySha256"]) is not None
and value["intentId"] == f"rpa-{value['idempotencySha256'][:20]}"
and isinstance(value.get("preflightSha256"), str)
and _SHA256.fullmatch(value["preflightSha256"]) is not None
and value.get("phase") in _PHASES
and value.get("status") in {"in_progress", "recovery_required", "completed"}
and isinstance(value.get("evidence"), dict)
and isinstance(value.get("receipts"), dict),
"intent_identity_invalid",
)
_need(
value.get("status") != "completed" or value.get("phase") == "completed",
"intent_terminal_state_invalid",
)
evidence = value["evidence"]
_need(
set(evidence) <= {"preflight", *_STEP_EVIDENCE_KEYS.values()}
and isinstance(evidence.get("preflight"), dict)
and _digest(evidence["preflight"]) == value["preflightSha256"],
"intent_evidence_invalid",
)
_validate_preflight_projection(evidence["preflight"])
receipts = value["receipts"]
phase = value["phase"]
expected_steps = {
"reserved": (),
"target_gates_ready": ("target_gates",),
"quiesced": ("target_gates", "quiescence"),
"snapshot_verified": ("target_gates", "quiescence", "snapshot"),
"target_registered": ("target_gates", "quiescence", "snapshot", "controller_registration"),
"appops_owned": tuple(key for key in _STEP_EVIDENCE_KEYS if key != "completion"),
"completed": tuple(_STEP_EVIDENCE_KEYS),
}[phase]
_need(set(receipts) == set(expected_steps), "intent_receipts_incomplete")
for step, record in receipts.items():
_need(
isinstance(record, dict)
and set(record) == {"evidenceSha256"}
and isinstance(record.get("evidenceSha256"), str)
and _SHA256.fullmatch(record["evidenceSha256"]) is not None,
"intent_receipt_invalid",
)
evidence_key = _STEP_EVIDENCE_KEYS[step]
_need(
evidence_key in evidence
and _digest(evidence[evidence_key]) == record["evidenceSha256"],
"intent_receipt_evidence_mismatch",
)
if value["status"] == "recovery_required":
recovery = value.get("recovery")
_need(
isinstance(recovery, dict)
and set(recovery)
== {
"reason",
"afterPhase",
"failureEvidenceSha256",
"controllerReconciliationRequired",
"legacyVolumePreserved",
}
and recovery.get("reason") in _RECOVERY_REASONS
and recovery.get("afterPhase") == phase
and isinstance(recovery.get("failureEvidenceSha256"), str)
and _SHA256.fullmatch(recovery["failureEvidenceSha256"]) is not None
and recovery.get("controllerReconciliationRequired") is True
and recovery.get("legacyVolumePreserved") is True,
"intent_recovery_marker_invalid",
)
else:
_need(value.get("recovery") is None, "intent_recovery_marker_unexpected")
return value
def _intent_binding(intent: dict[str, Any]) -> str:
return _digest(
{
"intentId": intent["intentId"],
"repository": REPOSITORY,
"applicationId": APPLICATION_ID,
"preflightSha256": intent["preflightSha256"],
"idempotencySha256": intent["idempotencySha256"],
}
)
def _target_gates(evidence: object) -> dict[str, Any]:
required = {
"profileId",
"profileVersion",
"planSha256",
"profileReady",
"controllerTrustReady",
"controllerBootstrapSecretImported",
"typedEnrollmentTrustReady",
"targetSecretReady",
"exactPolicyReady",
"authenticatedRouteReady",
"accessListReady",
"mutationBoundaryReady",
"managedRuntimeReady",
"scanSourceMatches",
"scanMountReadOnly",
"scanPermissionPolicyPersistent",
"scanAccessForRuntimeIdentityReady",
"ownedDataTargetReady",
"managedEnvironmentReady",
"legacyEnvironmentRetained",
"maintenanceAuthReady",
"controllerAdoptionCapabilityReady",
"controllerRegistryWriterReady",
"controllerSecretWriterReady",
}
_need(isinstance(evidence, dict) and set(evidence) == required, "target_gates_invalid")
value = evidence
_need(
value.get("profileId") == _PROFILE_ID
and value.get("profileVersion") == PROFILE_VERSION
and isinstance(value.get("planSha256"), str)
and _SHA256.fullmatch(value["planSha256"]) is not None
and all(value.get(key) is True for key in required - {"profileId", "profileVersion", "planSha256"}),
"target_gates_not_ready",
)
return {key: value[key] for key in sorted(required)}
def _quiescence_evidence(evidence: object, intent: dict[str, Any]) -> dict[str, Any]:
required = {
"intentId",
"globalDeployLockHeld",
"projectLockHeld",
"maintenanceModeActive",
"queueFrozen",
"queuedJobs",
"activeJobs",
"maintenanceModeAuthenticated",
"containerName",
"containerIdSha256",
"containerImageSha256",
"containerState",
"legacyVolume",
"controllerIntentDurable",
"controllerIntentBindingSha256",
}
_need(isinstance(evidence, dict) and set(evidence) == required, "quiescence_evidence_invalid")
value = evidence
_need(
value.get("intentId") == intent["intentId"]
and value.get("globalDeployLockHeld") is True
and value.get("projectLockHeld") is True
and value.get("maintenanceModeActive") is True
and value.get("queueFrozen") is True
and value.get("maintenanceModeAuthenticated") is True
and _is_count(value.get("queuedJobs"))
and value.get("queuedJobs") == 0
and _is_count(value.get("activeJobs"))
and value.get("activeJobs") == 0
and value.get("containerName") == "repopulse"
and isinstance(value.get("containerIdSha256"), str)
and _SHA256.fullmatch(value["containerIdSha256"]) is not None
and value.get("containerIdSha256") == intent["evidence"]["preflight"]["container_id_sha256"]
and isinstance(value.get("containerImageSha256"), str)
and _SHA256.fullmatch(value["containerImageSha256"]) is not None
and value.get("containerImageSha256") == intent["evidence"]["preflight"]["container_image_sha256"]
and value.get("containerState") == "exited"
and value.get("legacyVolume") == LEGACY_VOLUME
and value.get("controllerIntentDurable") is True,
"quiescence_not_proven",
)
_need(
isinstance(value.get("controllerIntentBindingSha256"), str)
and _SHA256.fullmatch(value["controllerIntentBindingSha256"]) is not None
and value["controllerIntentBindingSha256"] == _intent_binding(intent),
"controller_intent_binding_mismatch",
)
return {key: value[key] for key in sorted(required)}
def _snapshot_evidence(evidence: object, intent: dict[str, Any]) -> dict[str, Any]:
required = {
"intentId",
"globalDeployLockHeld",
"projectLockHeld",
"legacyContainerStopped",
"legacyVolume",
"legacyVolumeRetained",
"sqliteBackupMethod",
"quickCheck",
"integrityCheck",
"sourceLogicalSha256",
"snapshotLogicalSha256",
"snapshotFileSha256",
"sourceScanCount",
"snapshotScanCount",
"sourceCompactedScanCount",
"snapshotCompactedScanCount",
"sourceQueueSha256",
"snapshotQueueSha256",
"sourceAuxiliaryTreeSha256",
"sourceAuxiliaryFileCount",
"targetDataPathWasAbsent",
"snapshotReceiptDurable",
"legacyEnvironmentRetained",
"managedEnvironmentFilePrivate",
"controllerIntentBindingSha256",
}
_need(isinstance(evidence, dict) and set(evidence) == required, "snapshot_evidence_invalid")
value = evidence
_need(
value.get("intentId") == intent["intentId"]
and value.get("globalDeployLockHeld") is True
and value.get("projectLockHeld") is True
and value.get("legacyContainerStopped") is True
and value.get("legacyVolume") == LEGACY_VOLUME
and value.get("legacyVolumeRetained") is True
and value.get("sqliteBackupMethod") == "sqlite_online_backup"
and value.get("quickCheck") == "ok"
and value.get("integrityCheck") == "ok"
and isinstance(value.get("sourceLogicalSha256"), str)
and _SHA256.fullmatch(value["sourceLogicalSha256"]) is not None
and value.get("sourceLogicalSha256") == value.get("snapshotLogicalSha256")
and isinstance(value.get("snapshotFileSha256"), str)
and _SHA256.fullmatch(value["snapshotFileSha256"]) is not None
and _is_count(value.get("sourceScanCount"))
and value.get("sourceScanCount") == value.get("snapshotScanCount")
and _is_count(value.get("sourceCompactedScanCount"))
and value.get("sourceCompactedScanCount") == value.get("snapshotCompactedScanCount")
and isinstance(value.get("sourceQueueSha256"), str)
and _SHA256.fullmatch(value["sourceQueueSha256"]) is not None
and value.get("sourceQueueSha256") == value.get("snapshotQueueSha256")
and isinstance(value.get("sourceAuxiliaryTreeSha256"), str)
and _SHA256.fullmatch(value["sourceAuxiliaryTreeSha256"]) is not None
and _is_count(value.get("sourceAuxiliaryFileCount"))
and value.get("targetDataPathWasAbsent") is True
and value.get("snapshotReceiptDurable") is True
and value.get("legacyEnvironmentRetained") is True
and value.get("managedEnvironmentFilePrivate") is True,
"sqlite_snapshot_not_verified",
)
_need(
isinstance(value.get("controllerIntentBindingSha256"), str)
and _SHA256.fullmatch(value["controllerIntentBindingSha256"]) is not None
and value["controllerIntentBindingSha256"] == _intent_binding(intent),
"controller_intent_binding_mismatch",
)
return {key: value[key] for key in sorted(required)}
def _controller_registration_evidence(
evidence: object, intent: dict[str, Any]
) -> dict[str, Any]:
required = {
"receiptVersion",
"intentId",
"receiptSha256",
"repository",
"applicationId",
"enrollmentId",
"profileId",
"planSha256",
"snapshotFileSha256",
"targetLogicalSha256",
"targetScanCount",
"targetCompactedScanCount",
"targetQueueSha256",
"targetAuxiliaryTreeSha256",
"targetAuxiliaryFileCount",
"targetQueuedJobs",
"targetActiveJobs",
"targetQuickCheck",
"targetIntegrityCheck",
"targetRecoveryStatus",
"targetDataUid",
"targetDataGid",
"managedDataMountMatches",
"copySourceIsVerifiedSnapshot",
"registryTargetMatches",
"controllerStateReceiptVerified",
"deploymentReceiptSchema",
"deploymentReceiptSha256",
"deploymentReceiptVerified",
"runtimeHealthy",
"publishedHostPorts",
"globalDeployLockHeld",
"projectLockHeld",
"legacyVolume",
"legacyVolumeRetained",
"legacyEnvironmentRetained",
"managedEnvironmentFilePrivate",
"scanSourceMatches",
"scanMountReadOnly",
"scanPermissionPolicyPersistent",
"scanAccessForRuntimeIdentityReady",
"controllerIntentDurable",
"controllerIntentBindingSha256",
}
_need(
isinstance(evidence, dict) and set(evidence) == required,
"controller_receipt_evidence_invalid",
)
value = evidence
gates = intent["evidence"].get("targetGates")
snapshot = intent["evidence"].get("snapshot")
_need(isinstance(gates, dict) and isinstance(snapshot, dict), "adoption_prerequisite_missing")
_need(
value.get("receiptVersion") == CONTROLLER_RECEIPT_VERSION
and value.get("intentId") == intent["intentId"]
and isinstance(value.get("receiptSha256"), str)
and _SHA256.fullmatch(value["receiptSha256"]) is not None
and value.get("repository") == REPOSITORY
and value.get("applicationId") == APPLICATION_ID
and isinstance(value.get("enrollmentId"), str)
and _ENROLLMENT_ID.fullmatch(value["enrollmentId"]) is not None
and value.get("profileId") == gates["profileId"]
and value.get("planSha256") == gates["planSha256"]
and value.get("snapshotFileSha256") == snapshot["snapshotFileSha256"]
and isinstance(value.get("targetLogicalSha256"), str)
and value.get("targetLogicalSha256") == snapshot["snapshotLogicalSha256"]
and _is_count(value.get("targetScanCount"))
and value.get("targetScanCount") == snapshot["snapshotScanCount"]
and _is_count(value.get("targetCompactedScanCount"))
and value.get("targetCompactedScanCount") == snapshot["snapshotCompactedScanCount"]
and value.get("targetQueueSha256") == snapshot["snapshotQueueSha256"]
and value.get("targetAuxiliaryTreeSha256") == snapshot["sourceAuxiliaryTreeSha256"]
and value.get("targetAuxiliaryFileCount") == snapshot["sourceAuxiliaryFileCount"]
and _is_count(value.get("targetQueuedJobs"))
and value.get("targetQueuedJobs") == 0
and _is_count(value.get("targetActiveJobs"))
and value.get("targetActiveJobs") == 0
and value.get("targetQuickCheck") == "ok"
and value.get("targetIntegrityCheck") == "ok"
and value.get("targetRecoveryStatus") in {"healthy", "acknowledged"}
and value.get("targetDataUid") == 10001
and value.get("targetDataGid") == 10001
and value.get("managedDataMountMatches") is True
and value.get("copySourceIsVerifiedSnapshot") is True
and value.get("registryTargetMatches") is True
and value.get("controllerStateReceiptVerified") is True
and value.get("deploymentReceiptSchema") == "gitea-deploy-receipt.v1"
and isinstance(value.get("deploymentReceiptSha256"), str)
and _SHA256.fullmatch(value["deploymentReceiptSha256"]) is not None
and value.get("deploymentReceiptVerified") is True
and value.get("runtimeHealthy") is True
and value.get("publishedHostPorts") == []
and value.get("globalDeployLockHeld") is True
and value.get("projectLockHeld") is True
and value.get("legacyVolume") == LEGACY_VOLUME
and value.get("legacyVolumeRetained") is True
and value.get("legacyEnvironmentRetained") is True
and value.get("managedEnvironmentFilePrivate") is True
and value.get("scanSourceMatches") is True
and value.get("scanMountReadOnly") is True
and value.get("scanPermissionPolicyPersistent") is True
and value.get("scanAccessForRuntimeIdentityReady") is True
and value.get("controllerIntentDurable") is True
and value.get("controllerIntentBindingSha256") == _intent_binding(intent),
"controller_adoption_receipt_not_verified",
)
return {key: value[key] for key in sorted(required)}
def validate_controller_registration_evidence(
intent: object, evidence: object
) -> dict[str, Any]:
"""Validate fresh controller registration evidence against the intent.
This pure projection can validate receipt replays after AppOps reaches its
terminal `completed` phase. Controller response authentication remains the
caller's responsibility.
"""
current = _validate_intent(intent)
return _controller_registration_evidence(evidence, current)
def _appops_ownership_evidence(
evidence: object, intent: dict[str, Any]
) -> dict[str, Any]:
required = {
"repository",
"applicationId",
"enrollmentId",
"profileId",
"planSha256",
"controllerReceiptSha256",
"enrollmentPersisted",
"exactApplyPolicyPersisted",
"exactRollbackPolicyPersisted",
"secretReferenceReady",
}
_need(isinstance(evidence, dict) and set(evidence) == required, "appops_ownership_evidence_invalid")
value = evidence
registration = intent["evidence"].get("controllerRegistration")
_need(isinstance(registration, dict), "controller_registration_missing")
gates = intent["evidence"].get("targetGates")
_need(isinstance(gates, dict), "target_gates_missing")
_need(
value.get("repository") == REPOSITORY
and value.get("applicationId") == APPLICATION_ID
and value.get("enrollmentId") == registration["enrollmentId"]
and value.get("profileId") == registration["profileId"]
and value.get("planSha256") == gates["planSha256"]
and value.get("controllerReceiptSha256") == registration["receiptSha256"]
and all(
value.get(key) is True
for key in (
"enrollmentPersisted",
"exactApplyPolicyPersisted",
"exactRollbackPolicyPersisted",
"secretReferenceReady",
)
),
"appops_ownership_not_verified",
)
return {key: value[key] for key in sorted(required)}
def validate_appops_ownership_evidence(
intent: object, evidence: object
) -> dict[str, Any]:
"""Validate fixed ownership evidence for an idempotent completed replay."""
current = _validate_intent(intent)
return _appops_ownership_evidence(evidence, current)
def advance_intent(
intent: object, step: str, evidence: object
) -> dict[str, Any]:
"""Purely validate and record one forward protocol checkpoint.
The exact evidence digest makes retries idempotent. Replaying a step with
different evidence is a conflict and must enter recovery review.
"""
current = _validate_intent(intent)
_need(current.get("status") == "in_progress", "intent_requires_recovery")
_need(step in _STEP_CONTRACTS, "adoption_step_unknown")
source_phase, destination_phase = _STEP_CONTRACTS[step]
checked = {
"target_gates": _target_gates,
"quiescence": lambda value: _quiescence_evidence(value, current),
"snapshot": lambda value: _snapshot_evidence(value, current),
"controller_registration": lambda value: _controller_registration_evidence(value, current),
"appops_ownership": lambda value: _appops_ownership_evidence(value, current),
}[step](evidence)
digest = _digest(checked)
receipts = current["receipts"]
previous = receipts.get(step)
if previous is not None:
_need(
current.get("phase") in _PHASES[_PHASES.index(destination_phase):]
and isinstance(previous, dict)
and previous.get("evidenceSha256") == digest,
"idempotency_conflict",
)
return deepcopy(current)
_need(current.get("phase") == source_phase, "adoption_step_out_of_order")
updated = deepcopy(current)
updated["phase"] = destination_phase
updated["recovery"] = None
updated["receipts"][step] = {"evidenceSha256": digest}
evidence_key = _STEP_EVIDENCE_KEYS[step]
updated["evidence"][evidence_key] = checked
_validate_intent(updated)
return updated
def complete_intent(intent: object, evidence: object) -> dict[str, Any]:
"""Finalize only after re-reading both AppOps ownership and controller receipt."""
current = _validate_intent(intent)
_need(
current.get("status") == "in_progress"
and current.get("phase") == "appops_owned",
"adoption_not_ready_to_complete",
)
required = {
"intentId",
"repository",
"applicationId",
"enrollmentId",
"controllerReceiptSha256",
"enrollmentStillPersisted",
"exactApplyPolicyStillPersisted",
"exactRollbackPolicyStillPersisted",
"controllerReceiptStillMatches",
"legacyVolumeRetained",
}
_need(isinstance(evidence, dict) and set(evidence) == required, "completion_evidence_invalid")
value = evidence
ownership = current["evidence"].get("appopsOwnership")
registration = current["evidence"].get("controllerRegistration")
_need(isinstance(ownership, dict) and isinstance(registration, dict), "adoption_prerequisite_missing")
_need(
value.get("intentId") == current["intentId"]
and value.get("repository") == REPOSITORY
and value.get("applicationId") == APPLICATION_ID
and value.get("enrollmentId") == registration["enrollmentId"]
and value.get("controllerReceiptSha256") == registration["receiptSha256"]
and value.get("controllerReceiptSha256") == ownership["controllerReceiptSha256"]
and all(
value.get(key) is True
for key in (
"enrollmentStillPersisted",
"exactApplyPolicyStillPersisted",
"exactRollbackPolicyStillPersisted",
"controllerReceiptStillMatches",
"legacyVolumeRetained",
)
),
"completion_not_verified",
)
checked = {key: value[key] for key in sorted(required)}
updated = deepcopy(current)
updated["phase"] = "completed"
updated["status"] = "completed"
updated["evidence"]["completion"] = checked
updated["receipts"]["completion"] = {"evidenceSha256": _digest(checked)}
return _validate_intent(updated)
def mark_recovery_required(
intent: object, reason: str, failure_evidence: object
) -> dict[str, Any]:
"""Persist a sanitized recovery marker without compensating external state."""
current = _validate_intent(intent)
_need(current.get("status") != "completed", "intent_already_completed")
_need(reason in _RECOVERY_REASONS, "recovery_reason_invalid")
_need(isinstance(failure_evidence, dict), "recovery_evidence_invalid")
# Only bind the evidence; arbitrary external messages/paths/secret values
# are never copied into the journal by this pure protocol helper.
updated = deepcopy(current)
updated["status"] = "recovery_required"
updated["recovery"] = {
"reason": reason,
"afterPhase": current["phase"],
"failureEvidenceSha256": _digest(failure_evidence),
"controllerReconciliationRequired": True,
"legacyVolumePreserved": True,
}
return updated
def resume_intent(intent: object, controller_receipt: object) -> dict[str, Any]:
"""Resume only from an authenticated controller receipt bound to this intent.
The adapter that calls this function must authenticate the controller
response. This pure helper validates its exact content and only reconciles
the single ambiguous boundary: controller registration committed while the
AppOps intent checkpoint was interrupted.
"""
current = _validate_intent(intent)
_need(current.get("status") == "recovery_required", "intent_not_in_recovery")
required = {
"apiVersion",
"intentId",
"phase",
"receiptSha256",
"repository",
"applicationId",
"legacyVolumeRetained",
"intentDurable",
"intentBindingSha256",
"registrationEvidence",
}
_need(
isinstance(controller_receipt, dict)
and set(controller_receipt) == required,
"controller_recovery_receipt_invalid",
)
receipt = controller_receipt
_need(
receipt.get("apiVersion") == CONTROLLER_RECEIPT_VERSION
and receipt.get("intentId") == current["intentId"]
and receipt.get("repository") == REPOSITORY
and receipt.get("applicationId") == APPLICATION_ID
and receipt.get("legacyVolumeRetained") is True
and receipt.get("intentDurable") is True
and receipt.get("intentBindingSha256") == _intent_binding(current)
and isinstance(receipt.get("receiptSha256"), str)
and _SHA256.fullmatch(receipt["receiptSha256"]) is not None,
"controller_recovery_receipt_unbound",
)
phase = receipt.get("phase")
if phase == current["phase"]:
_need(
current["phase"] in {"reserved", "target_gates_ready", "quiesced", "snapshot_verified", "target_registered", "appops_owned"},
"controller_recovery_phase_invalid",
)
if phase in {"target_registered", "appops_owned"}:
registration = _controller_registration_evidence(
receipt.get("registrationEvidence"), current
)
_need(
receipt.get("receiptSha256") == registration["receiptSha256"]
and registration == current["evidence"].get("controllerRegistration"),
"controller_recovery_receipt_conflict",
)
else:
_need(
receipt.get("registrationEvidence") is None,
"controller_recovery_receipt_conflict",
)
updated = deepcopy(current)
updated["status"] = "in_progress"
updated["recovery"] = None
return updated
_need(
current.get("phase") == "snapshot_verified" and phase == "target_registered",
"controller_recovery_phase_conflict",
)
registration = _controller_registration_evidence(receipt.get("registrationEvidence"), current)
registration_digest = _digest(registration)
_need(receipt.get("receiptSha256") == registration["receiptSha256"], "controller_recovery_receipt_digest_mismatch")
updated = deepcopy(current)
updated["phase"] = "target_registered"
updated["status"] = "in_progress"
updated["recovery"] = None
updated["receipts"]["controller_registration"] = {"evidenceSha256": registration_digest}
updated["evidence"]["controllerRegistration"] = registration
_validate_intent(updated)
return updated
def intent_binding_sha256(intent: object) -> str:
"""Return the immutable root-controller journal binding for an intent."""
current = _validate_intent(intent)
return _intent_binding(current)
def execution_status() -> dict[str, Any]:
"""Report the code gate without claiming live target readiness."""
return {
"apiVersion": PROTOCOL_VERSION,
"repository": REPOSITORY,
"status": "enabled" if EXECUTION_ENABLED is True else "disabled",
"reason": None if EXECUTION_ENABLED is True else "controller_adoption_execution_disabled",
"executionEnabled": EXECUTION_ENABLED,
}