Files
aios/active-runtime/app/aios_baseline/runtime_contract.py
T

376 lines
15 KiB
Python

"""Pure validation and deployment-truth evaluation for runtime contracts.
Runtime contracts describe stable structure. A release-owned deployment target
is supplied separately so that changing a release does not rewrite structural
configuration or turn a source branch into runtime evidence.
"""
from __future__ import annotations
from copy import deepcopy
from datetime import datetime, timezone
import re
from pathlib import PurePosixPath
from typing import Any, Mapping, Sequence
CONTRACT_SCHEMA = "aios.runtime-contract.v1"
LIFECYCLES = frozenset({"active", "candidate", "legacy", "example"})
_DYNAMIC_FIELDS = frozenset(
{"desired", "desired_commit", "desired_release", "desired_target", "desired_targets"}
)
_SECRET_FIELDS = frozenset(
{"secret", "secret_bytes", "token", "password", "private_key", "credential_value", "value"}
)
_DIGEST = re.compile(r"^(?:sha256:)?[0-9a-fA-F]{64}$")
_SAFE_SYMBOLIC_REF = re.compile(r"^[A-Za-z][A-Za-z0-9+.-]*:[/A-Za-z0-9_.:@+-]+$")
def _error(code: str, path: str = "") -> ValueError:
return ValueError(f"{code}:{path}" if path else code)
def _nonempty_string(value: object, path: str) -> str:
if not isinstance(value, str) or not value.strip():
raise _error("field_invalid", path)
return value.strip()
def _positive_int(value: object, path: str) -> int:
if isinstance(value, bool) or not isinstance(value, int) or value < 1:
raise _error("field_invalid", path)
return value
def _reject_dynamic_fields(value: object, path: str = "contract") -> None:
if isinstance(value, Mapping):
for key, child in value.items():
if not isinstance(key, str):
raise _error("field_name_invalid", path)
child_path = f"{path}.{key}"
if key.lower() in _DYNAMIC_FIELDS:
raise _error("dynamic_field_forbidden", child_path)
_reject_dynamic_fields(child, child_path)
elif isinstance(value, Sequence) and not isinstance(value, (str, bytes, bytearray)):
for index, child in enumerate(value):
_reject_dynamic_fields(child, f"{path}[{index}]")
def _safe_path_ref(value: object, path: str) -> str:
text = _nonempty_string(value, path)
if "\x00" in text or "\\" in text:
raise _error("unsafe_path_ref", path)
candidate = PurePosixPath(text)
if candidate.is_absolute() or any(part in {"", ".", ".."} for part in candidate.parts):
raise _error("unsafe_path_ref", path)
return text
def _symbolic_ref(value: object, path: str) -> str:
text = _nonempty_string(value, path)
if "\x00" in text or ".." in text or not _SAFE_SYMBOLIC_REF.fullmatch(text):
raise _error("resolver_ref_invalid", path)
return text
def _credential_ref(value: object, path: str) -> str:
text = _nonempty_string(value, path)
allowed = (
text.startswith("credential://")
or text.startswith("secret://")
or text.startswith("systemd-credential:")
)
if not allowed or "\x00" in text or ".." in text or text.startswith("/run/credentials/"):
raise _error("credential_ref_invalid", path)
return text
def _validate_binding_group(
group: object,
group_name: str,
component_id: str,
environment: str,
tenant_scope: str,
) -> dict[str, Any]:
if not isinstance(group, Mapping):
raise _error("binding_group_invalid", group_name)
normalized: dict[str, Any] = {}
for name, raw in group.items():
item_path = f"{group_name}.{name}"
_nonempty_string(name, f"{group_name}.name")
if isinstance(raw, str):
reference_field = (
"credential_ref"
if group_name == "credential_refs"
else "path_ref"
if group_name == "resource_refs" and ":" not in raw
else "resolver_ref"
)
item = {
"component_id": component_id,
"environment": environment,
reference_field: raw,
}
elif isinstance(raw, Mapping):
item = deepcopy(dict(raw))
else:
raise _error("binding_invalid", item_path)
if _nonempty_string(item.get("component_id"), f"{item_path}.component_id") != component_id:
raise _error("binding_component_mismatch", item_path)
if _nonempty_string(item.get("environment"), f"{item_path}.environment") != environment:
raise _error("binding_environment_mismatch", item_path)
if "tenant_scope" in item and (
_nonempty_string(item["tenant_scope"], f"{item_path}.tenant_scope") != tenant_scope
):
raise _error("binding_tenant_mismatch", item_path)
reference_fields = [key for key in ("resolver_ref", "path_ref", "credential_ref") if key in item]
if len(reference_fields) != 1:
raise _error("binding_reference_invalid", item_path)
reference_field = reference_fields[0]
reference_path = f"{item_path}.{reference_field}"
if group_name == "credential_refs" or reference_field == "credential_ref":
for key in item:
if key.lower() in _SECRET_FIELDS:
raise _error("secret_material_forbidden", f"{item_path}.{key}")
item[reference_field] = _credential_ref(item[reference_field], reference_path)
elif reference_field == "path_ref":
item[reference_field] = _safe_path_ref(item[reference_field], reference_path)
else:
item[reference_field] = _symbolic_ref(item[reference_field], reference_path)
normalized[str(name)] = item
return normalized
def validate_contract(document: Mapping[str, Any]) -> dict[str, Any]:
"""Validate and copy a stable ``aios.runtime-contract.v1`` document.
The returned mapping owns its nested containers, so callers cannot mutate the
source after validation. Invalid contracts raise ``ValueError`` with a stable
machine-readable code prefix.
"""
if not isinstance(document, Mapping):
raise _error("contract_invalid")
_reject_dynamic_fields(document)
result = deepcopy(dict(document))
if result.get("contract_schema") != CONTRACT_SCHEMA:
raise _error("contract_schema_invalid", "contract_schema")
result["contract_revision"] = _positive_int(result.get("contract_revision"), "contract_revision")
component_id = _nonempty_string(result.get("component_id"), "component_id")
environment = _nonempty_string(result.get("environment"), "environment")
result["component_id"] = component_id
result["environment"] = environment
tenant_scope = _nonempty_string(result.get("tenant_scope"), "tenant_scope")
result["tenant_scope"] = tenant_scope
lifecycle = _nonempty_string(result.get("lifecycle"), "lifecycle").lower()
if lifecycle not in LIFECYCLES:
raise _error("lifecycle_invalid", "lifecycle")
result["lifecycle"] = lifecycle
if "resources" in result and "resource_refs" in result:
raise _error("duplicate_resource_group")
if "resources" in result:
result["resource_refs"] = result.pop("resources")
for group_name in ("entrypoints", "bindings", "resource_refs", "credential_refs"):
result[group_name] = _validate_binding_group(
result.get(group_name), group_name, component_id, environment, tenant_scope
)
operations = result.get("operations")
if (
not isinstance(operations, Sequence)
or isinstance(operations, (str, bytes, bytearray))
or not operations
):
raise _error("operations_invalid", "operations")
normalized_operations = [
_nonempty_string(operation, f"operations[{index}]")
for index, operation in enumerate(operations)
]
if len(set(normalized_operations)) != len(normalized_operations):
raise _error("duplicate_operation", "operations")
result["operations"] = normalized_operations
return result
def _unknown(*reasons: str) -> dict[str, Any]:
return {
"status": "UNKNOWN",
"state": "UNKNOWN",
"verification": "UNKNOWN",
"converged": False,
"reasons": list(dict.fromkeys(reasons)),
}
def _drift(reasons: list[str]) -> dict[str, Any]:
return {
"status": "DRIFT",
"state": "DRIFT",
"verification": "FAILED",
"converged": False,
"reasons": list(dict.fromkeys(reasons)),
}
def _parse_time(value: object) -> datetime | None:
if isinstance(value, datetime):
parsed = value
elif isinstance(value, str):
try:
parsed = datetime.fromisoformat(value.strip().replace("Z", "+00:00"))
except ValueError:
return None
else:
return None
if parsed.tzinfo is None or parsed.utcoffset() is None:
return None
return parsed.astimezone(timezone.utc)
def _digest_value(document: Mapping[str, Any], *names: str) -> object:
for name in names:
if name in document:
return document[name]
return None
def _valid_digest(value: object) -> bool:
return isinstance(value, str) and bool(_DIGEST.fullmatch(value))
def evaluate_deployment(
contract: Mapping[str, Any],
target: Mapping[str, Any] | None,
observation: Mapping[str, Any] | None,
provenance: Mapping[str, Any] | None,
now: datetime | str,
) -> dict[str, Any]:
"""Evaluate one target using fresh runtime and provenance evidence.
Missing or malformed evidence is ``UNKNOWN``. ``DRIFT`` is reserved for a
complete evidence set that proves an incompatible target or runtime fact.
Repository HEAD is intentionally not an input to this decision.
"""
stable = validate_contract(contract)
missing: list[str] = []
if target is None:
missing.append("target_missing")
elif not isinstance(target, Mapping):
missing.append("target_invalid")
if observation is None:
missing.append("observation_missing")
elif not isinstance(observation, Mapping):
missing.append("observation_invalid")
if provenance is None:
missing.append("provenance_missing")
elif not isinstance(provenance, Mapping):
missing.append("provenance_invalid")
current_time = _parse_time(now)
if current_time is None:
missing.append("now_invalid")
if missing:
return _unknown(*missing)
assert target is not None and observation is not None and provenance is not None
assert current_time is not None
target_errors: list[str] = []
for field in ("component_id", "environment", "release_id", "deployment_id"):
if not isinstance(target.get(field), str) or not str(target[field]).strip():
target_errors.append(f"target_{field}_invalid")
if "tenant_scope" in target and (
not isinstance(target["tenant_scope"], str) or not target["tenant_scope"].strip()
):
target_errors.append("target_tenant_scope_invalid")
target_artifact = target.get("artifact_digest")
if not _valid_digest(target_artifact):
target_errors.append("target_artifact_digest_invalid")
if isinstance(target.get("revision"), bool) or not isinstance(target.get("revision"), int) or target.get("revision", 0) < 1:
target_errors.append("target_revision_invalid")
observed_at = _parse_time(observation.get("observed_at"))
max_age = observation.get("max_age_seconds", observation.get("max_age"))
observed_artifact = _digest_value(
observation, "actual_artifact_digest", "actual_artifact", "artifact_digest"
)
observation_errors: list[str] = []
if observed_at is None:
observation_errors.append("observation_observed_at_invalid")
if isinstance(max_age, bool) or not isinstance(max_age, (int, float)) or max_age <= 0:
observation_errors.append("observation_max_age_invalid")
if not isinstance(observation.get("source"), str) or not observation.get("source", "").strip():
observation_errors.append("observation_source_invalid")
if not _valid_digest(observed_artifact):
observation_errors.append("observation_actual_artifact_invalid")
if not isinstance(observation.get("healthy"), bool):
observation_errors.append("observation_healthy_invalid")
for field in ("component_id", "environment", "tenant_scope", "deployment_id"):
if field in observation and (
not isinstance(observation[field], str) or not observation[field].strip()
):
observation_errors.append(f"observation_{field}_invalid")
if "revision" in observation and (
isinstance(observation["revision"], bool)
or not isinstance(observation["revision"], int)
or observation["revision"] < 1
):
observation_errors.append("observation_revision_invalid")
artifact_provenance = _digest_value(provenance, "artifact_digest", "artifact_hash")
source_provenance = _digest_value(provenance, "source_digest", "source_hash")
builder_provenance = _digest_value(provenance, "builder_digest", "builder_hash")
provenance_errors: list[str] = []
if not _valid_digest(artifact_provenance):
provenance_errors.append("provenance_artifact_digest_invalid")
if not _valid_digest(source_provenance):
provenance_errors.append("provenance_source_digest_invalid")
if not _valid_digest(builder_provenance):
provenance_errors.append("provenance_builder_digest_invalid")
evidence_errors = target_errors + observation_errors + provenance_errors
if evidence_errors:
return _unknown(*evidence_errors)
assert observed_at is not None and isinstance(max_age, (int, float))
age = (current_time - observed_at).total_seconds()
if age < 0:
return _unknown("observation_from_future")
if age > max_age:
return _unknown("observation_stale")
drift: list[str] = []
if target["component_id"] != stable["component_id"]:
drift.append("target_component_id_mismatch")
if target["environment"] != stable["environment"]:
drift.append("target_environment_mismatch")
if "tenant_scope" in target and target["tenant_scope"] != stable["tenant_scope"]:
drift.append("target_tenant_scope_mismatch")
if "tenant_scope" in observation and observation["tenant_scope"] != stable["tenant_scope"]:
drift.append("observation_tenant_scope_mismatch")
for field in ("component_id", "environment", "deployment_id", "revision"):
if field in observation and observation[field] != target[field]:
drift.append(f"observation_{field}_mismatch")
if observed_artifact != target_artifact:
drift.append("artifact_mismatch")
if artifact_provenance != target_artifact:
drift.append("provenance_artifact_mismatch")
if observation["healthy"] is False:
drift.append("observation_unhealthy")
if drift:
return _drift(drift)
return {
"status": "VERIFIED",
"state": "CONVERGED",
"verification": "VERIFIED",
"converged": True,
"reasons": [],
"component_id": target["component_id"],
"environment": target["environment"],
"release_id": target["release_id"],
"deployment_id": target["deployment_id"],
"artifact_digest": target_artifact,
"revision": target["revision"],
"observed_at": observation["observed_at"],
"observation_source": observation["source"],
}