Publish exact C5 issuer V3 bootstrap source
autonomy-smoke-tests Exact governed tree; evidence 7dbba7ff168e72af
autonomy-unit-tests Exact governed tree; evidence 7dbba7ff168e72af
campaign-approval-gate Exact governed tree; evidence 7dbba7ff168e72af
git-diff-check Exact governed tree; evidence 7dbba7ff168e72af
py-compile Exact governed tree; evidence 7dbba7ff168e72af
reconciliation-contract Exact governed tree; evidence 7dbba7ff168e72af
release-state-machine Exact governed tree; evidence 7dbba7ff168e72af
secret-scan Exact governed tree; evidence 7029b3eafb209066
signer-validator-contract Exact governed tree; evidence 7dbba7ff168e72af

This commit was merged in pull request #89.
This commit is contained in:
AIOS Governed Continuation
2026-10-01 23:09:33 +08:00
parent e33664f033
commit 540e28a724
43 changed files with 9446 additions and 106 deletions
@@ -0,0 +1,167 @@
"""Read-only V3 issuer scope over the admitted producer's durable receipt.
The V1/V2 provider remains authoritative for those schemas. V3 validation
and Job readback are delegated to the existing admission producer contract;
this module never reserves a Job or changes the canonical plan digest.
"""
from __future__ import annotations
from pathlib import Path
import stat
from typing import Any, Callable, Mapping
from aios_governed_task_bootstrap import GovernedTaskBootstrap
from aios_task_admission_bootstrap import TaskAdmissionError
from aios_task_admission_live_adapters import (
ProducerReceiptError,
_FLProducerReceiptReserver,
load_v3_plan,
validate_v3_plan,
)
from governed_runtime_scope import (
GovernedScopeError,
RuntimeScope,
_branch,
_generation,
_hex64,
_job_id,
_normalize_caps,
_normalize_files,
_oid40,
_task_id,
_trust_digest,
)
def _deny_mutation(*_args: Any, **_kwargs: Any) -> None:
raise GovernedScopeError("ISSUER_PRODUCER_MUTATION_FORBIDDEN")
def _read_only_bootstrap(root: Path) -> GovernedTaskBootstrap:
"""Construct only the read-only binding view; __init__ creates directories."""
root = Path(root)
for path in (root, root / "task-bindings", root / "jobs"):
metadata = path.lstat()
if (stat.S_ISLNK(metadata.st_mode) or not stat.S_ISDIR(metadata.st_mode)
or stat.S_IMODE(metadata.st_mode) & 0o022):
raise GovernedScopeError("PRODUCER_STATE_ROOT_INVALID")
view = object.__new__(GovernedTaskBootstrap)
view.root = root
view.task_meta_root = root / "task-bindings"
view.job_root = root / "jobs"
view.create = _deny_mutation
view._store = _deny_mutation
view._write = _deny_mutation
return view
class IssuerV3ReceiptScopeProvider:
"""Delegate legacy plans, and resolve V3 with an existing Job only."""
def __init__(
self, *, legacy: Any, producer_state_root: Path, plan_root: Path,
grant_reader: Callable[..., Mapping[str, Any]],
plan_loader: Callable[[Path, Mapping[str, Any]], Mapping[str, Any]] = load_v3_plan,
) -> None:
self.legacy = legacy
self.producer_state_root = Path(producer_state_root)
self.plan_root = Path(plan_root)
self.grant_reader = grant_reader
self.plan_loader = plan_loader
def inspect_v3_binding(
self, *, grant_raw: Mapping[str, Any], plan: Mapping[str, Any],
) -> tuple[str, Mapping[str, Any]]:
"""Validate original bytes and durable receipt without Task mutation.
This method can inspect an expired historical fixture. ``resolve``
separately enforces live Task and grant state before issuing scope.
"""
try:
original = self.plan_loader(self.plan_root, grant_raw)
if dict(original) != dict(plan):
raise GovernedScopeError("EXECUTION_PLAN_READBACK_CONFLICT")
# Keep the main V3 validator in the issuer contract even when a
# test supplies an isolated loader. It binds the original plan,
# including risk, hashes, capabilities and execution digest.
from aios_task_admission_live_adapters import canonical_json_bytes
validate_v3_plan(grant=grant_raw, raw=canonical_json_bytes(original))
bootstrap = _read_only_bootstrap(self.producer_state_root)
runtime = type("ReadOnlyIssuerRuntime", (), {
"bootstrap": bootstrap, "bindings": {},
})()
reserver = _FLProducerReceiptReserver(runtime)
job_id = reserver.existing_v3_job(grant_raw, original)
if job_id is None:
raise GovernedScopeError("PRODUCER_RECEIPT_MISSING")
readback = reserver.readbacks.get(str(grant_raw["task_id"]))
proof = readback.get("reservation_readback") if isinstance(readback, Mapping) else None
if not isinstance(proof, Mapping) or proof.get("job_id") != job_id:
raise GovernedScopeError("PRODUCER_RECEIPT_BINDING_INVALID")
return job_id, dict(proof)
except (TaskAdmissionError, ProducerReceiptError, OSError, KeyError, TypeError, ValueError) as exc:
raise GovernedScopeError(str(exc) or "PRODUCER_RECEIPT_INVALID") from exc
def resolve(
self, *, task: Any, execution: Any, grant: Any,
plan: Mapping[str, Any], trust: Any, now: int,
) -> RuntimeScope:
if plan.get("schema") != "aios.task-admission-execution-plan.v3":
return self.legacy.resolve(
task=task, execution=execution, grant=grant,
plan=plan, trust=trust, now=now,
)
task_id = _task_id(task.task_id)
exec_job = _job_id(execution.external_job_id)
exec_digest = _hex64(execution.execution_digest, "execution_digest")
exec_branch = _branch(execution.target_branch)
exec_generation = _generation(execution.generation)
grant_raw = self.grant_reader(
task_id=task_id, execution_digest=exec_digest,
repository=execution.repository, target_branch=exec_branch,
generation=exec_generation, now=int(now),
)
producer_job, _proof = self.inspect_v3_binding(
grant_raw=grant_raw, plan=plan,
)
if exec_job != producer_job:
raise GovernedScopeError("EXECUTION_PRODUCER_JOB_MISMATCH")
if str(task.status).upper() not in {"CLAIMED", "RUNNING"}:
raise GovernedScopeError("TASK_NOT_CLAIMED")
if grant.status != "ADMITTED" or int(grant.expires_at) <= int(now):
raise GovernedScopeError("GRANT_EXPIRED_OR_NOT_ADMITTED")
expected = {
"task_id": task_id,
"repository": execution.repository,
"target_branch": exec_branch,
"generation": exec_generation,
"execution_digest": exec_digest,
"authority_base_oid": _oid40(grant.authority_base_oid, "authority_base_oid"),
"grant_id": grant.grant_id,
}
if (execution.repository != "aios/aios"
or execution.task_id != task_id
or any(grant_raw.get(key) != value for key, value in expected.items())):
raise GovernedScopeError("V3_GRANT_EXECUTION_BINDING_MISMATCH")
caps = _normalize_caps(grant.requested_capabilities)
files = _normalize_files(grant.file_allowlist)
if (caps != _normalize_caps(grant_raw["capabilities"])
or files != _normalize_files(grant_raw["file_allowlist"])):
raise GovernedScopeError("V3_GRANT_SCOPE_MISMATCH")
if (not trust.issuer_id or not trust.key_id or not trust.public_key
or "TASK_PRINCIPAL" not in str(trust.purpose).upper().replace("-", "_")
or not str(trust.bridge_id).strip()):
raise GovernedScopeError("TRUST_IDENTITY_INVALID")
trust_digest = _trust_digest(trust)
if (trust.scope_manifest_sha256 is not None
and _hex64(trust.scope_manifest_sha256, "scope_manifest_sha256") != trust_digest):
raise GovernedScopeError("TRUST_MANIFEST_DIGEST_MISMATCH")
return RuntimeScope(
scope_id=f"governed-runtime:{task_id}:g{exec_generation}:{exec_digest[:16]}",
task_id=task_id, external_job_id=producer_job,
repository=execution.repository, target_branch=exec_branch,
generation=exec_generation, execution_digest=exec_digest,
capabilities=caps, trust_manifest_sha256=trust_digest,
grant_id=grant.grant_id, grant_expires_at=int(grant.expires_at),
)
@@ -24,6 +24,19 @@ class BuilderError(RuntimeError):
"""A stable, non-sensitive error code returned by the builder boundary."""
C5_INTEGRATION_CONTRACT_FIELDS = frozenset({
"integration_source_commit", "integration_tree_oid",
"publication_source_commit", "publication_tree_oid",
"publication_plan_sha256", "repair_source_commit", "repair_tree_oid",
})
C5_INTEGRATION_FILES = {
"active-runtime/app/aios_capability_contract.py": ("publication_source_commit", "67d8a3a296110e20a4ea59863cd7982b6bdada71abd457c0f7cec480b06d278a"),
"tests/test_v2r2c5_capability_contract.py": ("publication_source_commit", "f91af641528ea9a625ca45a46b6c26f5dba568e6b69cba819ca33ab5cba28de3"),
"active-runtime/app/aios_baseline/service.py": ("repair_source_commit", "3c3918aec5503947b48ef4c73e34cac8f0d33fdfb99cfcd5ca8af699b663da72"),
"tests/baseline_v1_1/test_loop.py": ("repair_source_commit", "f867e92b9ec9b4dff248e6ca38280848458a535c610dc6d9fea2b270c29b664a"),
}
def _canonical_json(value: Mapping[str, Any]) -> bytes:
return json.dumps(value, sort_keys=True, separators=(",", ":"), ensure_ascii=True).encode("utf-8")
@@ -135,6 +148,7 @@ class TrustedCandidateBuilderConfig:
policy_sha256: str
source_admission_resolver: Callable[[str, str | None, str], Mapping[str, str]] | None = None
migration_plan_resolver: Callable[[str, str, str], Any] | None = None
c5_contract: Mapping[str, str] | None = None
@classmethod
def from_policy(cls, policy: Mapping[str, Any]) -> "TrustedCandidateBuilderConfig":
@@ -145,16 +159,52 @@ class TrustedCandidateBuilderConfig:
"allowed_peer_runtime_prefix", "allowed_peer_runtime_suffix",
"required_checks", "evidence_layout", "recipes", "governed_recipes",
}
if set(policy) != required or policy.get("schema") != "aios.trusted-candidate-builder-policy.v1":
is_integration = policy.get("schema") == "aios.trusted-candidate-builder-policy.c5-integration.v1"
is_c5 = is_integration or policy.get("schema") == "aios.trusted-candidate-builder-policy.c5.v1"
if set(policy) != (required | {"c5_contract"} if is_c5 else required) or policy.get("schema") not in {"aios.trusted-candidate-builder-policy.v1", "aios.trusted-candidate-builder-policy.c5.v1", "aios.trusted-candidate-builder-policy.c5-integration.v1"}:
raise BuilderError("POLICY_SCHEMA_INVALID")
recipes = policy.get("recipes")
if not isinstance(recipes, Mapping) or set(recipes) != {"V2R2C2", "V2R2C3"}:
expected_recipes = {"V2R2C2", "V2R2C3", "V2R2C5"} if is_c5 else {"V2R2C2", "V2R2C3"}
if not isinstance(recipes, Mapping) or set(recipes) != expected_recipes:
raise BuilderError("POLICY_RECIPES_INVALID")
c2, c3 = recipes["V2R2C2"], recipes["V2R2C3"]
if not isinstance(c2, Mapping) or set(c2) != {"mode", "task_id", "candidate_id", "base_oid", "source_commit", "version", "purpose"} or c2.get("mode") != "fixed":
raise BuilderError("POLICY_V2R2C2_INVALID")
if not isinstance(c3, Mapping) or set(c3) != {"mode", "derives_from", "version", "purpose"} or c3.get("mode") != "governed-derived-only" or c3.get("derives_from") != "V2R2C2":
raise BuilderError("POLICY_V2R2C3_INVALID")
c5_contract = None
if is_c5:
c5 = recipes["V2R2C5"]
expected_mode = "governed-source-integration-only" if is_integration else "governed-source-admission-only"
if not isinstance(c5, Mapping) or set(c5) != {"mode", "version", "purpose"} or c5.get("mode") != expected_mode or c5.get("version") != "V2R2C5" or c5.get("purpose") != "AIOS_TRUSTED_RELEASE_BUILDER":
raise BuilderError("POLICY_V2R2C5_INVALID")
required_c5 = {"task_id", "authority_base_oid", "accepted_base_oid", "rollback_version", "rollback_target_oid", "rollback_artifact_sha256"}
if is_integration:
required_c5 |= C5_INTEGRATION_CONTRACT_FIELDS
value = policy.get("c5_contract")
if not isinstance(value, Mapping) or set(value) != required_c5:
raise BuilderError("POLICY_V2R2C5_CONTRACT_INVALID")
if any(not isinstance(v, str) for v in value.values()):
raise BuilderError("POLICY_V2R2C5_CONTRACT_INVALID")
c5_contract = dict(value)
oid_fields = {"authority_base_oid", "accepted_base_oid", "rollback_target_oid"}
if is_integration:
oid_fields |= {
"integration_source_commit", "integration_tree_oid",
"publication_source_commit", "publication_tree_oid",
"repair_source_commit", "repair_tree_oid",
}
sha_fields = {"rollback_artifact_sha256"}
if is_integration:
sha_fields.add("publication_plan_sha256")
if (
not c5_contract["task_id"]
or c5_contract["rollback_version"] != "V2R2C4"
or c5_contract["accepted_base_oid"] != c5_contract["rollback_target_oid"]
or any(len(c5_contract[name]) != 40 or any(ch not in "0123456789abcdef" for ch in c5_contract[name]) for name in oid_fields)
or any(len(c5_contract[name]) != 64 or any(ch not in "0123456789abcdef" for ch in c5_contract[name]) for name in sha_fields)
):
raise BuilderError("POLICY_V2R2C5_CONTRACT_INVALID")
if (
policy["repository"] != "aios/aios"
or policy["operation"] != "BuildTrustedCandidateV1"
@@ -210,6 +260,7 @@ class TrustedCandidateBuilderConfig:
recipes={name: dict(value) for name, value in recipes.items()},
governed_recipes={name: dict(value) for name, value in policy["governed_recipes"].items()} if isinstance(policy["governed_recipes"], Mapping) else _invalid_governed_recipes(),
policy_sha256=hashlib.sha256(_canonical_json(dict(policy))).hexdigest(),
c5_contract=c5_contract,
)
def fixed_recipe(self, name: str) -> Mapping[str, str]:
@@ -231,6 +282,36 @@ class TrustedCandidateBuilderConfig:
def resolve_recipe(self, version: str, candidate_id: str | None, source_commit: str) -> Mapping[str, str]:
recipe = self.recipes.get(version)
if recipe is not None:
if recipe.get("mode") in {"governed-source-admission-only", "governed-source-integration-only"}:
if version != "V2R2C5" or self.c5_contract is None:
raise BuilderError("SOURCE_ADMISSION_REQUIRED")
resolver = self.source_admission_resolver
if resolver is None:
raise BuilderError("SOURCE_ADMISSION_REQUIRED")
try:
admitted = dict(resolver(version, candidate_id, source_commit))
except Exception as exc:
raise BuilderError("SOURCE_ADMISSION_REQUIRED") from exc
required = {"mode", "task_id", "candidate_id", "base_oid", "source_commit", "version", "purpose", "tree_oid"}
if set(admitted) != required or admitted.get("mode") != "governed-source-admission" or admitted.get("version") != version or admitted.get("source_commit") != source_commit or admitted.get("purpose") != "AIOS_TRUSTED_RELEASE_BUILDER":
raise BuilderError("SOURCE_ADMISSION_INVALID")
if (not isinstance(admitted.get("candidate_id"), str) or not admitted.get("candidate_id")
or admitted.get("task_id") != self.c5_contract["task_id"]
or admitted.get("base_oid") != self.c5_contract["authority_base_oid"]):
raise BuilderError("SOURCE_ADMISSION_BINDING_INVALID")
effective = str(candidate_id or admitted["candidate_id"])
if effective != admitted["candidate_id"]:
raise BuilderError("REQUEST_RELEASE_BINDING_INVALID")
for name in ("base_oid", "source_commit", "tree_oid"):
value = admitted.get(name)
if not isinstance(value, str) or len(value) != 40 or any(char not in "0123456789abcdef" for char in value.lower()):
raise BuilderError("SOURCE_ADMISSION_INVALID")
if recipe.get("mode") == "governed-source-integration-only" and (
source_commit != self.c5_contract["integration_source_commit"]
or admitted["tree_oid"] != self.c5_contract["integration_tree_oid"]
):
raise BuilderError("SOURCE_INTEGRATION_BINDING_INVALID")
return {**admitted, **self.c5_contract}
resolved = self.fixed_recipe(version)
# Fixed/governed-derived policy recipes remain immutable. A
# source admission can never override one of these identities.
@@ -247,6 +328,8 @@ class TrustedCandidateBuilderConfig:
raise BuilderError("REQUEST_RELEASE_BINDING_INVALID")
return resolved
if self.c5_contract is not None:
raise BuilderError("RECIPE_UNKNOWN")
resolver = self.source_admission_resolver
if resolver is None:
raise BuilderError("RECIPE_UNKNOWN")
@@ -263,8 +346,9 @@ class TrustedCandidateBuilderConfig:
"version",
"purpose",
}
allowed_fields = (required, required | {"tree_oid"}) if version == "V2R2C5" else (required,)
if (
set(admitted) != required
set(admitted) not in allowed_fields
or admitted.get("mode") != "governed-source-admission"
or admitted.get("version") != version
or admitted.get("source_commit") != source_commit
@@ -275,7 +359,7 @@ class TrustedCandidateBuilderConfig:
effective_candidate_id = str(candidate_id or admitted["candidate_id"])
if effective_candidate_id != admitted["candidate_id"]:
raise BuilderError("REQUEST_RELEASE_BINDING_INVALID")
for name in ("base_oid", "source_commit"):
for name in ("base_oid", "source_commit", *(["tree_oid"] if "tree_oid" in admitted else [])):
value = admitted.get(name)
if (
not isinstance(value, str)
@@ -395,7 +479,44 @@ class ServerOwnedEvidenceSource:
raise BuilderError("COLLECTOR_EVIDENCE_INVALID")
return value
def _validate_integration_source(self, recipe: Mapping[str, str]) -> None:
if "integration_source_commit" not in recipe:
return
def read(*arguments: str, binary: bool = False):
try:
result = self._git_runner(
["git", f"--git-dir={self._authority_git_dir}", *arguments],
check=True, capture_output=True, text=not binary, timeout=15,
env={"PATH": os.environ.get("PATH", "")},
)
return result.stdout if binary else result.stdout.strip()
except (OSError, subprocess.SubprocessError) as exc:
raise BuilderError("COLLECTOR_INTEGRATION_UNAVAILABLE") from exc
source = recipe["source_commit"]
if source != recipe["integration_source_commit"]:
raise BuilderError("COLLECTOR_INTEGRATION_MISMATCH")
for commit_field, tree_field in (
("integration_source_commit", "integration_tree_oid"),
("publication_source_commit", "publication_tree_oid"),
("repair_source_commit", "repair_tree_oid"),
):
if read("rev-parse", f"{recipe[commit_field]}^{{tree}}") != recipe[tree_field]:
raise BuilderError("COLLECTOR_INTEGRATION_MISMATCH")
if read("show", "-s", "--format=%P", source) != recipe["authority_base_oid"]:
raise BuilderError("COLLECTOR_INTEGRATION_MISMATCH")
changed = read("diff", "--name-only", recipe["authority_base_oid"], source).splitlines()
if sorted(changed) != sorted(C5_INTEGRATION_FILES):
raise BuilderError("COLLECTOR_INTEGRATION_FILE_SET_MISMATCH")
for path, (component, digest) in C5_INTEGRATION_FILES.items():
integrated = read("show", f"{source}:{path}", binary=True)
original = read("show", f"{recipe[component]}:{path}", binary=True)
if integrated != original or hashlib.sha256(integrated).hexdigest() != digest:
raise BuilderError("COLLECTOR_INTEGRATION_BYTES_MISMATCH")
def __call__(self, recipe_name: str, recipe: Mapping[str, str]) -> Mapping[str, Any]:
self._validate_integration_source(recipe)
try:
tree_result = self._git_runner(
["git", f"--git-dir={self._authority_git_dir}", "rev-parse", f"{recipe['source_commit']}^{{tree}}"],
@@ -412,6 +533,16 @@ class ServerOwnedEvidenceSource:
raise BuilderError("COLLECTOR_EVIDENCE_UNAVAILABLE") from exc
if getattr(ancestry_result, "returncode", 1) != 0:
raise BuilderError("COLLECTOR_GIT_MISMATCH")
if recipe.get("tree_oid") is not None and recipe.get("tree_oid") != tree_oid:
raise BuilderError("COLLECTOR_GIT_MISMATCH")
if recipe.get("accepted_base_oid") is not None:
accepted_ancestry = self._git_runner(
["git", f"--git-dir={self._authority_git_dir}", "merge-base", "--is-ancestor", recipe["accepted_base_oid"], recipe["source_commit"]],
check=False, capture_output=True, text=True, timeout=15,
env={"PATH": os.environ.get("PATH", "")},
)
if getattr(accepted_ancestry, "returncode", 1) != 0:
raise BuilderError("COLLECTOR_GIT_MISMATCH")
receipt = self._json(self._regular_path(f"{recipe_name}/receipt.json"))
if set(receipt) != {"schema", "version", "candidate", "release_plan"}:
@@ -488,6 +619,13 @@ class ServerOwnedEvidenceSource:
or rollback.get("rollback_sha256") != deployment.get("rollback_sha256")
):
raise BuilderError("COLLECTOR_ROLLBACK_MISMATCH")
if recipe.get("rollback_version") is not None:
if deployment.get("rollback_target") != {
"version": recipe["rollback_version"],
"target_oid": recipe["rollback_target_oid"],
"artifact_sha256": recipe["rollback_artifact_sha256"],
}:
raise BuilderError("COLLECTOR_ROLLBACK_MISMATCH")
if (
digests["gitea"] != hashlib.sha256(_canonical_json(dict(gitea))).hexdigest()
or digests["rollback"] != hashlib.sha256(_canonical_json(dict(rollback))).hexdigest()
@@ -18,7 +18,10 @@ from __future__ import annotations
import argparse
import dataclasses
import fcntl
try:
import fcntl
except ModuleNotFoundError: # pragma: no cover - production host is POSIX
fcntl = None
import hashlib
import json
import os
@@ -376,6 +379,8 @@ class FileLock:
self.handle = None
def __enter__(self) -> "FileLock":
if fcntl is None:
raise ReleaseBlocked("production executor file locking requires POSIX")
self.path.parent.mkdir(parents=True, exist_ok=True)
self.handle = self.path.open("a+", encoding="utf-8")
os.chmod(self.path, 0o600)
@@ -388,7 +393,7 @@ class FileLock:
return self
def __exit__(self, exc_type, exc, tb) -> None:
if self.handle is not None:
if self.handle is not None and fcntl is not None:
fcntl.flock(self.handle.fileno(), fcntl.LOCK_UN)
self.handle.close()
+74
View File
@@ -0,0 +1,74 @@
"""Fixed registered composition of the existing shadow API and renewal store."""
import base64
import importlib.util
import json
from pathlib import Path
import runpy
import sys
ORIGINAL_SHA = 'f6393fe614239036a1d8235c98c1f455dc32b700107d88f48190c4f61dae617e'
def service_type(types, renewal, public_key):
class RegisteredService(types.SilentReleaseActionService):
def __init__(self, root, **kwargs):
if 'completion_approvals' in kwargs:
raise renewal.RenewalRejected('FIXED_COMPLETION_DEPENDENCY_REQUIRED')
original = types.SignedApprovalStore(Path(root) / 'approvals')
completion = renewal.RegisteredCompletionApprovalStore(
original, public_key, types.SignedReleasePlanApproval,
)
super().__init__(root, completion_approvals=completion, **kwargs)
return RegisteredService
def main():
# Original Round3 startup and verifier remain loaded before the new root
# is added. The C5 types are a separate registered module as in R2.
import silent_release_hardening as original
import hashlib
if hashlib.sha256(Path(original.__file__).read_bytes()).hexdigest() != ORIGINAL_SHA:
raise RuntimeError('ROUND3_HARDENING_DRIFT')
if not getattr(original.BuilderAttestationVerifier.verify,
'__aios_round3_rollback_contract_patch__', False):
raise RuntimeError('ROUND3_VERIFIER_NOT_ACTIVE')
import c5_completion_renewal as renewal
renewal.configure_registered_bindings()
root = renewal.AUTHORITY_ROOT
sys.path.insert(0, str(root))
if Path(__file__) != root / 'c5_completion_api.py':
raise renewal.RenewalRejected('REGISTERED_API_WRAPPER_REQUIRED')
policy = renewal.RegisteredCompletionAuthority()()
parent = json.loads(renewal.secure_bytes(Path(policy['predecessor_snapshot'])))
keyring = json.loads(renewal.secure_bytes(root / 'approval-public-keyring.json'))
if keyring.get('algorithm') != 'Ed25519' or parent['key_id'] in keyring.get('revoked', []):
raise renewal.RenewalRejected('REGISTERED_APPROVAL_KEY_REQUIRED')
public_key = base64.b64decode(keyring['keys'][parent['key_id']], validate=True)
renewal.verify_signature(parent, public_key)
spec = importlib.util.spec_from_file_location('_registered_c5_completion_types',
root / 'silent_release_hardening.py')
types = importlib.util.module_from_spec(spec)
sys.modules[spec.name] = types
spec.loader.exec_module(types)
loaded = runpy.run_path(str(root / 'original_api.py'), run_name='_registered_original_api')
g = loaded['main'].__globals__
if g['SILENT_RELEASE_EXECUTION_ENABLED'] is not False or Path(g['SILENT_RELEASE_STATE_ROOT']) != renewal.STATE_ROOT:
raise renewal.RenewalRejected('EXISTING_SHADOW_PLANE_REQUIRED')
g['ReleasePlan'] = types.ReleasePlan
g['TrustedCandidateBundle'] = types.TrustedCandidateBundle
g['SilentReleaseActionService'] = service_type(types, renewal, public_key)
g['TRUSTED_RELEASE_POLICY'] = renewal.TRUSTED_RELEASE_POLICY_PATH
service = g['_build_silent_release_service']()
if service.execution_enabled or service.policy_sha256 != renewal.TRUSTED_RELEASE_POLICY_SHA256:
raise renewal.RenewalRejected('EXACT_API_POLICY_REQUIRED')
# Preserve the exact candidate boundary from the existing registered R2.
def reject_candidate_creation(payload):
raise original.CapabilityAuthorizationError('completion mode cannot create or replace candidates')
service.create_release_candidate = reject_candidate_creation
g['_SILENT_RELEASE_SERVICE'] = service
g['main']()
return 0
if __name__ == '__main__':
raise SystemExit(main())
+100
View File
@@ -0,0 +1,100 @@
"""Completion-only fixed tag and metadata adapter launcher, no runtime effects."""
import argparse
import base64
import io
import json
import os
from pathlib import Path
import runpy
import sys
import urllib.request
UNIT = 'aios-c5-exact-production-host-r1.service'
def main():
parser = argparse.ArgumentParser()
parser.add_argument('adapter')
parser.add_argument('mode', choices=('tag', 'registry', 'baseline'))
args = parser.parse_args()
import c5_completion_renewal as renewal
renewal.configure_registered_bindings()
root = renewal.AUTHORITY_ROOT
sys.path.insert(0, str(root))
from silent_release_hardening import SignedApprovalStore, SignedReleasePlanApproval
if Path(__file__) != root / 'c5_completion_phase.py' or os.geteuid() != 0:
raise renewal.RenewalRejected('REGISTERED_COMPLETION_PHASE_REQUIRED')
policy = renewal.RegisteredCompletionAuthority()()
credentials = Path(os.environ.get('CREDENTIALS_DIRECTORY', ''))
if credentials != Path('/run/credentials') / UNIT:
raise renewal.RenewalRejected('FIXED_COMPLETION_PURPOSE_UNIT_REQUIRED')
if not any(line.split(':', 2)[-1].rstrip('/').endswith('/' + UNIT)
for line in Path('/proc/self/cgroup').read_text().splitlines()):
raise renewal.RenewalRejected('FIXED_COMPLETION_CGROUP_REQUIRED')
module = 'governed_tag_adapter.py' if args.mode == 'tag' else 'release_metadata_adapter.py'
if Path(args.adapter) != ROOT / module:
raise renewal.RenewalRejected('FIXED_COMPLETION_ADAPTER_REQUIRED')
value = json.load(sys.stdin)
fixed = {'schema': 'aios.production-executor-request.v1', 'version': 'V2R2C5',
'target_oid': renewal.SOURCE, 'evidence_closure_oid': renewal.SOURCE,
'artifact_sha256': renewal.ARTIFACT,
'approval_plan_id': 'sha256:d8f64b8b92f9fdb17f5c6053b76a68ee5bcbc2f266d91438c4db7893463079f8',
'repository_identity': 'aios/aios', 'production_service': 'aios-v2r2c2-production.service',
'production_port': 18928}
if args.mode != 'tag':
fixed['tag_oid'] = renewal.TAG_OID
if (set(value) != set(fixed) | {'operation'} or value['operation'] not in {'probe', 'apply'}
or any(value[k] != v for k, v in fixed.items())):
raise renewal.RenewalRejected('EXACT_COMPLETION_REQUEST_REQUIRED')
parent = json.loads(renewal.secure_bytes(Path(policy['predecessor_snapshot'])))
ring = json.loads(renewal.secure_bytes(root / 'approval-public-keyring.json'))
if parent['key_id'] in ring.get('revoked', []):
raise renewal.RenewalRejected('APPROVAL_KEY_REVOKED')
public_key = base64.b64decode(ring['keys'][parent['key_id']], validate=True)
approval = renewal.RegisteredCompletionApprovalStore(
SignedApprovalStore(renewal.STATE_ROOT / 'approvals'), public_key, SignedReleasePlanApproval,
).load(renewal.CID)
if approval.fingerprint == renewal.OLD_FP:
raise renewal.RenewalRejected('FRESH_COMPLETION_SUCCESSOR_REQUIRED')
# Loading the store verifies both actual signatures, registered fixed nonce,
# append-only chain and current successor lifetime. The historical proof
# intentionally remains bound to the original approval, never rewritten.
runtime = json.load(urllib.request.urlopen('http://127.0.0.1:18928/health', timeout=10))
if any(runtime.get(k) != v for k, v in {
'version': 'V2R2C5', 'commit_oid': renewal.SOURCE, 'artifact_sha256': renewal.ARTIFACT,
'deployment_state': 'ACTIVE', 'production_deployed': True}.items()):
raise renewal.RenewalRejected('CURRENT_C5_RUNTIME_REQUIRED')
# Exact signed copies of these historical helpers are read-only here.
# Never call their activation, run(), or verification-phase effect methods.
import production_verification_phase_r1 as business
import public_business_acceptance_probe_r1 as public
business.RegisteredVerificationPhase.business_proof(None, True)
public.reconcile()
os.environ.clear()
os.environ.update(PATH='/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin',
PYTHONDONTWRITEBYTECODE='1', AIOS_GITOPS_WRITES_ENABLED='false',
AIOS_GITOPS_LEGACY_GLOBAL_WRITES_ENABLED='false',
AIOS_GITOPS_CONTROLLER_WORKSPACE=str(renewal.CONTROLLER_WORKSPACE),
AIOS_GITOPS_AUTHORITATIVE_GIT_DIR=str(renewal.AUTHORITATIVE_GIT_DIR),
AIOS_GITOPS_REPOSITORY_IDENTITY='aios/aios',
AIOS_RUNTIME_REGISTRY_ROOT=str(renewal.RUNTIME_REGISTRY_ROOT),
AIOS_RUNTIME_BASELINE_PATH=str(renewal.RUNTIME_BASELINE_PATH))
if args.mode == 'tag':
os.environ.update(GIT_COMMITTER_NAME='owner', GIT_COMMITTER_EMAIL='owner@cnnetmkt.top',
GIT_COMMITTER_DATE='1790826063 +0000', GIT_TERMINAL_PROMPT='0',
GIT_ASKPASS=str(root / 'publisher-askpass.py'))
sys.argv = [module, '--workspace', str(renewal.CONTROLLER_WORKSPACE),
'--repository', str(renewal.AUTHORITATIVE_GIT_DIR), '--remote', 'gitea-http',
'--gitea-api', 'http://127.0.0.1:3000/api/v1', '--gitea-repo', 'aios/aios',
'--gitea-token-file', str(credentials / 'gitea-release-publisher'),
'--state-root', str(renewal.HISTORICAL_ROOT / 'tag-publication')]
else:
sys.argv = [module, '--mode', args.mode, '--expected-previous-baseline', 'V2R2C4',
'--expected-previous-baseline-sha256', 'de0c7008facc44c5061239ee490135b58e521989952918f84a57cecb3358483c',
'--health-url', 'http://127.0.0.1:18928/health']
sys.stdin = io.StringIO(json.dumps(value))
runpy.run_path(str(root / module), run_name='__main__')
if __name__ == '__main__':
main()
+630
View File
@@ -0,0 +1,630 @@
"""Append-only C5 completion renewal boundary; activation requires registration.
The exact Owner-registered API, signer and completion host must call this
boundary. This module does not grant authority or perform deployment effects.
"""
import base64
import hashlib
import json
import math
import os
import stat
import time
from datetime import datetime, timezone
from pathlib import Path
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PublicKey
from cryptography.hazmat.primitives.serialization import Encoding, PublicFormat
CID = 'rc-v2r2c5-5f65e458957d'
# Immutable release content remains compiled into the validator. Renewable
# authority identities and filesystem locations are installed only from the
# digest-bound, root-owned registration file (see configure_registered_bindings).
OLD_FP = '0' * 64
SOURCE = '5f65e458957dd51c9973cfa956a624f3cbf54990'
TREE = 'ddd239d43d63a7fbd3cff46119c71466c9443737'
ARTIFACT = '7f9d8409f07a5e0d36f5db7c55eb82ea70935fa731e057998f29cd1a5ca0a9ad'
PLAN = '370521afcf9b2288f6ad3803ca3554a46ee05d2042d53b19b4f4cb80e37a9684'
MUTABLE = {'issued_at', 'not_before', 'expires_at', 'nonce', 'fingerprint', 'signature'}
CAPABILITIES = ['baseline_registry_finalize', 'runtime_switch', 'tag_publish']
AUTHORITY_ROOT = Path('/unconfigured/c5-completion-authority')
STATE_ROOT = Path('/unconfigured/c5-completion-state')
OWNER_KEY = 'UNCONFIGURED'
TAG_OID = '6d1745ff496103db7e721a2b2bc624fda52c5dc8'
SAGA_HEAD = '0' * 64
HISTORICAL_ROOT = Path('/unconfigured/c5-completion-history')
CONTROLLER_WORKSPACE = Path('/unconfigured/c5-controller-workspace')
AUTHORITATIVE_GIT_DIR = Path('/unconfigured/c5-authoritative-git')
RUNTIME_REGISTRY_ROOT = Path('/unconfigured/c5-runtime-registry')
RUNTIME_BASELINE_PATH = Path('/unconfigured/c5-runtime-baseline.json')
TRUSTED_RELEASE_POLICY_PATH = Path('/unconfigured/c5-trusted-release-policy.json')
TRUSTED_RELEASE_POLICY_SHA256 = '0' * 64
BINDINGS_PATH_ENV = 'AIOS_C5_COMPLETION_BINDINGS_PATH'
BINDINGS_SHA256_ENV = 'AIOS_C5_COMPLETION_BINDINGS_SHA256'
_BINDINGS_DIGEST = None
RENEWAL_SOURCE_FILES = ('c5_completion_renewal.py', 'c5_release_execution_host.py',
'silent_release_hardening.py', 'c5_completion_signer.py',
'c5_completion_api.py', 'c5_completion_phase.py') # gitleaks:allow -- filenames only
PHASE_DEPENDENCIES = ('governed_tag_adapter.py', 'release_metadata_adapter.py',
'production_verification_phase_r1.py', 'public_business_acceptance_probe_r1.py',
'publisher-askpass.py', 'c5_staging_installation.py',
'aios_single_host_authority.py', 'registered_owner_signer_adapter.py')
class RenewalRejected(ValueError):
pass
def canonical(value):
return json.dumps(value, sort_keys=True, separators=(',', ':'), allow_nan=False).encode()
def digest(raw):
return hashlib.sha256(raw).hexdigest()
def validate_completion_lineage(gate):
"""Bind a current completion-only journal suffix to the frozen Saga15 head.
The suffix is authenticated by the enclosing registered Owner contract;
these checks preserve its origin and exclude deployment/source transitions.
"""
revision = gate.get('saga_revision')
suffix = gate.get('completion_journal_suffix', [])
observed = gate.get('observed_at')
if (type(revision) is not int or revision < 15 or not isinstance(suffix, list)
or type(observed) not in {int, float} or not math.isfinite(observed)):
raise RenewalRejected('COMPLETION_LINEAGE_INVALID')
if revision == 15:
if gate.get('saga_head') != SAGA_HEAD or suffix:
raise RenewalRejected('COMPLETION_LINEAGE_INVALID')
return
if not 0 < len(suffix) <= 64 or len(suffix) != revision - 15:
raise RenewalRejected('COMPLETION_LINEAGE_INVALID')
allowed = {
'owner_registered_completion_successor_tag_reconciliation': ('TAG_PENDING', {'RECONCILIATION_REQUIRED'}),
'owner_registered_tag_capability_successor_verified': ('TAG_VERIFIED', {'RECONCILIATION_REQUIRED', 'TAG_PENDING'}),
'baseline_registry_finalization_pending': ('BASELINE_REGISTRY_PENDING', {'TAG_VERIFIED'}),
'owner_registered_completion_successor_metadata_reconciliation': ('BASELINE_REGISTRY_PENDING', {'RECONCILIATION_REQUIRED'}),
'release_reconciliation_required': ('RECONCILIATION_REQUIRED', {'TAG_PENDING', 'TAG_VERIFIED', 'BASELINE_REGISTRY_PENDING'}),
}
previous, state = SAGA_HEAD, 'RECONCILIATION_REQUIRED'
for offset, event in enumerate(suffix, 16):
if (not isinstance(event, dict)
or set(event) != {'schema_version', 'sequence', 'previous_hash', 'payload', 'event_hash'}
or type(event['schema_version']) is not int or event['schema_version'] != 1
or type(event['sequence']) is not int or event['sequence'] != offset + 1
or event['previous_hash'] != previous):
raise RenewalRejected('COMPLETION_LINEAGE_INVALID')
payload = event['payload']
if (not isinstance(payload, dict)
or set(payload) != {'candidate_id', 'release_plan_id', 'revision', 'state', 'event', 'timestamp', 'details'}
or payload['candidate_id'] != CID
or payload['release_plan_id'] != 'pending-' + CID
or type(payload['revision']) is not int or payload['revision'] != offset
or not isinstance(payload['details'], dict)
or not isinstance(payload['event'], str) or payload['event'] not in allowed):
raise RenewalRejected('COMPLETION_LINEAGE_INVALID')
target, origins = allowed[payload['event']]
if payload['state'] != target or state not in origins:
raise RenewalRejected('COMPLETION_LINEAGE_INVALID')
if ('release_plan_sha256' in payload['details']
and payload['details']['release_plan_sha256'] != PLAN):
raise RenewalRejected('COMPLETION_LINEAGE_INVALID')
try:
if timestamp(payload['timestamp']) > gate['observed_at']:
raise RenewalRejected('COMPLETION_LINEAGE_INVALID')
actual = digest(canonical({key: event[key] for key in ('sequence', 'previous_hash', 'payload')}))
except (TypeError, ValueError, AttributeError, KeyError) as exc:
raise RenewalRejected('COMPLETION_LINEAGE_INVALID') from exc
if event['event_hash'] != actual:
raise RenewalRejected('COMPLETION_LINEAGE_INVALID')
previous, state = actual, target
if gate.get('saga_head') != previous:
raise RenewalRejected('COMPLETION_LINEAGE_INVALID')
def timestamp(value):
parsed = datetime.fromisoformat(value.replace('Z', '+00:00'))
if parsed.tzinfo is None or parsed.utcoffset().total_seconds() != 0:
raise RenewalRejected('UTC_REQUIRED')
return parsed.timestamp()
def verify_signature(approval, public_key):
payload = {k: v for k, v in approval.items() if k not in {'signature', 'fingerprint'}}
raw = canonical(payload)
if approval.get('fingerprint') != digest(raw):
raise RenewalRejected('APPROVAL_DIGEST_INVALID')
Ed25519PublicKey.from_public_bytes(public_key).verify(
base64.b64decode(approval['signature'], validate=True), raw
)
def validate_successor(old, new, public_key, *, now):
"""Validate historical predecessor and fresh identical-binding successor.
Historical signature verification does not authorize an expired effect.
The caller must separately verify its fresh registered Owner capability.
"""
verify_signature(old, public_key)
verify_signature(new, public_key)
exact = {'candidate_id': CID, 'source_commit': SOURCE, 'tree_oid': TREE,
'artifact_sha256': ARTIFACT, 'release_plan_sha256': PLAN,
'fingerprint': OLD_FP, 'capabilities': CAPABILITIES}
if any(old.get(k) != v for k, v in exact.items()):
raise RenewalRejected('EXACT_PREDECESSOR_REQUIRED')
if set(old) != set(new) or any(new[k] != old[k] for k in set(old) - MUTABLE):
raise RenewalRejected('IMMUTABLE_APPROVAL_BINDING_DRIFT')
if timestamp(old['expires_at']) > now:
raise RenewalRejected('PREDECESSOR_NOT_EXPIRED')
issued, not_before, expires = (timestamp(new[k]) for k in ['issued_at', 'not_before', 'expires_at'])
if not issued <= not_before <= now < expires or not 0 < expires - issued <= 900:
raise RenewalRejected('FRESH_BOUNDED_APPROVAL_REQUIRED')
if new['nonce'] == old['nonce'] or new['fingerprint'] == old['fingerprint']:
raise RenewalRejected('FRESH_APPROVAL_IDENTITY_REQUIRED')
return new['fingerprint']
def secure_bytes(path, owner_uid=0):
path = Path(path)
descriptor = os.open(path, os.O_RDONLY | os.O_NOFOLLOW)
with os.fdopen(descriptor, 'rb') as stream:
metadata = os.fstat(stream.fileno())
if not stat.S_ISREG(metadata.st_mode) or metadata.st_uid != owner_uid or metadata.st_mode & 0o022:
raise RenewalRejected('UNSAFE_RENEWAL_FILE')
return stream.read()
def _safe_parents(path):
"""Reject writable or symlinked ancestors before opening authority files."""
path = Path(path)
if not path.is_absolute() or '..' in path.parts:
raise RenewalRejected('ABSOLUTE_CANONICAL_PATH_REQUIRED')
for parent in path.parents:
info = parent.lstat()
if not stat.S_ISDIR(info.st_mode) or info.st_uid != 0 or info.st_mode & 0o022:
raise RenewalRejected('UNSAFE_AUTHORITY_ANCESTOR')
def _sha(value):
return isinstance(value, str) and len(value) == 64 and all(c in '0123456789abcdef' for c in value)
def _oid(value):
return isinstance(value, str) and len(value) == 40 and all(c in '0123456789abcdef' for c in value)
def configure_registered_bindings(path=None, expected_sha256=None, *, owner_uid=0):
"""Install renewable completion bindings from one authenticated file.
No historical approval fingerprint, Saga head, Owner key, or authority/state
path is a runtime default. The service registration must provide both the
absolute file path and its SHA-256 digest.
"""
global _BINDINGS_DIGEST
path = Path(path or os.environ.get(BINDINGS_PATH_ENV, ''))
expected_sha256 = expected_sha256 or os.environ.get(BINDINGS_SHA256_ENV, '')
if not path.is_absolute() or not _sha(expected_sha256):
raise RenewalRejected('REGISTERED_COMPLETION_BINDINGS_REQUIRED')
_safe_parents(path)
raw = secure_bytes(path, owner_uid=owner_uid)
if digest(raw) != expected_sha256:
raise RenewalRejected('COMPLETION_BINDINGS_DIGEST_MISMATCH')
if _BINDINGS_DIGEST is not None and _BINDINGS_DIGEST != expected_sha256:
raise RenewalRejected('COMPLETION_BINDINGS_REBIND_FORBIDDEN')
try:
value = json.loads(raw)
except (UnicodeDecodeError, json.JSONDecodeError) as exc:
raise RenewalRejected('COMPLETION_BINDINGS_INVALID') from exc
fields = {
'schema', 'authority_root', 'state_root', 'owner_key_id',
'predecessor_fingerprint', 'saga_head', 'historical_root',
'controller_workspace', 'authoritative_git_dir',
'runtime_registry_root', 'runtime_baseline_path',
'trusted_release_policy_path', 'trusted_release_policy_sha256',
}
if not isinstance(value, dict) or set(value) != fields or value.get('schema') != 'aios.c5.completion-bindings.v1':
raise RenewalRejected('COMPLETION_BINDINGS_SCHEMA_INVALID')
authority_root = Path(value['authority_root'])
state_root = Path(value['state_root'])
governed_paths = {
name: Path(value[name]) for name in (
'historical_root', 'controller_workspace', 'authoritative_git_dir',
'runtime_registry_root', 'runtime_baseline_path', 'trusted_release_policy_path',
)
}
owner_key_id = value['owner_key_id']
try:
owner_key_valid = (isinstance(owner_key_id, str)
and owner_key_id.startswith('SHA256:')
and len(base64.b64decode(owner_key_id[7:], validate=True)) == 32)
except (ValueError, TypeError):
owner_key_valid = False
all_paths = (authority_root, state_root, *governed_paths.values())
if (any(not item.is_absolute() or '..' in item.parts for item in all_paths)
or not _sha(value['predecessor_fingerprint']) or not _sha(value['saga_head'])
or not _sha(value['trusted_release_policy_sha256'])
or not owner_key_valid):
raise RenewalRejected('COMPLETION_BINDINGS_VALUE_INVALID')
if path.parent != authority_root:
raise RenewalRejected('COMPLETION_BINDINGS_ROOT_MISMATCH')
global AUTHORITY_ROOT, STATE_ROOT, OWNER_KEY, OLD_FP, SAGA_HEAD
global HISTORICAL_ROOT, CONTROLLER_WORKSPACE, AUTHORITATIVE_GIT_DIR
global RUNTIME_REGISTRY_ROOT, RUNTIME_BASELINE_PATH
global TRUSTED_RELEASE_POLICY_PATH, TRUSTED_RELEASE_POLICY_SHA256
AUTHORITY_ROOT = authority_root
STATE_ROOT = state_root
OWNER_KEY = value['owner_key_id']
OLD_FP = value['predecessor_fingerprint']
SAGA_HEAD = value['saga_head']
HISTORICAL_ROOT = governed_paths['historical_root']
CONTROLLER_WORKSPACE = governed_paths['controller_workspace']
AUTHORITATIVE_GIT_DIR = governed_paths['authoritative_git_dir']
RUNTIME_REGISTRY_ROOT = governed_paths['runtime_registry_root']
RUNTIME_BASELINE_PATH = governed_paths['runtime_baseline_path']
TRUSTED_RELEASE_POLICY_PATH = governed_paths['trusted_release_policy_path']
TRUSTED_RELEASE_POLICY_SHA256 = value['trusted_release_policy_sha256']
_BINDINGS_DIGEST = expected_sha256
return dict(value)
class RegisteredCompletionAuthority:
"""Fixed root registration, Owner signature and immutable evidence verifier.
The completed production proofs are historical facts. This fresh authority
permits only approval renewal and remaining metadata effects; it cannot
authorize another runtime switch or public acceptance Task.
"""
@property
def root(self):
return AUTHORITY_ROOT
@property
def state_root(self):
return STATE_ROOT
def __call__(self):
from c5_staging_installation import RegistryOwnerVerifier
root = AUTHORITY_ROOT
_safe_parents(root / 'contract.json')
envelope = json.loads(secure_bytes(root / 'contract.json'))
if set(envelope) != {'payload', 'signature'}:
raise RenewalRejected('EXACT_AUTHORITY_ENVELOPE_REQUIRED')
p = envelope['payload']
fields = {'schema', 'host', 'repository', 'candidate_id', 'source_commit', 'tree_oid',
'artifact_sha256', 'release_plan_sha256', 'state_root', 'predecessor_fingerprint',
'predecessor_sha256', 'predecessor_uid', 'predecessor_snapshot', 'approval_nonce',
'issued_at', 'expires_at', 'owner_key_id', 'owner_namespace', 'allowed_effects',
'runtime_effects_allowed', 'files', 'gate_path', 'gate_observed_at',
'governed_source_commit', 'governed_source_tree'}
if not isinstance(p, dict) or set(p) != fields:
raise RenewalRejected('EXACT_AUTHORITY_SCHEMA_REQUIRED')
exact = {'schema': 'aios.c5.registered-completion-authority.v1', 'host': 'cn-network-mkt',
'repository': 'aios/aios', 'candidate_id': CID, 'source_commit': SOURCE,
'tree_oid': TREE, 'artifact_sha256': ARTIFACT, 'release_plan_sha256': PLAN,
'state_root': str(STATE_ROOT), 'predecessor_fingerprint': OLD_FP,
'owner_key_id': OWNER_KEY, 'owner_namespace': 'aios-owner-approval-v1',
'allowed_effects': ['approval_successor_append', 'baseline_finalize', 'registry_finalize', 'tag_publish'],
'runtime_effects_allowed': False}
if any(p[k] != v for k, v in exact.items()) or type(p['runtime_effects_allowed']) is not bool:
raise RenewalRejected('EXACT_COMPLETION_AUTHORITY_REQUIRED')
now = time.time()
if (type(p['issued_at']) is not int or type(p['expires_at']) is not int
or not p['issued_at'] <= now < p['expires_at']
or not 0 < p['expires_at'] - p['issued_at'] <= 1800
or type(p['gate_observed_at']) not in {int, float}
or not math.isfinite(p['gate_observed_at'])
or not p['issued_at'] - 60 <= p['gate_observed_at'] <= p['issued_at']):
raise RenewalRejected('FRESH_OWNER_AUTHORITY_REQUIRED')
if (not _sha(p['approval_nonce']) or not _sha(p['predecessor_sha256'])
or type(p['predecessor_uid']) is not int or p['predecessor_uid'] not in {0, 1000}
or any(not isinstance(p[k], str) or len(p[k]) != 40
or any(c not in '0123456789abcdef' for c in p[k])
for k in ('governed_source_commit', 'governed_source_tree'))):
raise RenewalRejected('AUTHORITY_IDENTITIES_INVALID')
verifier = RegistryOwnerVerifier(Path('/etc/aios/single-host-signer-registry.json'),
owner_signer_id='local-owner-authority')
if not verifier.verify(namespace=p['owner_namespace'], payload=canonical(p),
signature=envelope['signature'], key_id=p['owner_key_id']):
raise RenewalRejected('OWNER_SIGNATURE_REJECTED')
payload_sha = digest(canonical(p))
registered = json.loads(secure_bytes(root / 'registration.json'))
if (set(registered) != {'schema', 'status', 'payload_sha256', 'approval_nonce'}
or registered != {'schema': 'aios.c5.completion-registration.v1', 'status': 'REGISTERED',
'payload_sha256': payload_sha, 'approval_nonce': p['approval_nonce']}):
raise RenewalRejected('ROOT_REGISTRATION_REQUIRED')
if (root / 'rollback.json').exists():
raise RenewalRejected('REGISTRATION_ROLLED_BACK')
pins = p['files']
if not isinstance(pins, list) or not pins:
raise RenewalRejected('SIGNED_FILE_CLOSURE_REQUIRED')
checked = {}
for item in pins:
if (not isinstance(item, dict) or set(item) != {'path', 'sha256', 'uid'}
or not _sha(item['sha256']) or type(item['uid']) is not int or item['uid'] != 0):
raise RenewalRejected('SIGNED_FILE_PIN_INVALID')
path = Path(item['path'])
if path.parent != root or str(path) in checked:
raise RenewalRejected('SIGNED_FILE_PATH_INVALID')
_safe_parents(path)
raw = secure_bytes(path)
if digest(raw) != item['sha256']:
raise RenewalRejected('SIGNED_FILE_BYTES_DRIFT')
checked[str(path)] = raw
required = {str(root / name) for name in (*RENEWAL_SOURCE_FILES, *PHASE_DEPENDENCIES, 'source-material.json',
'historical-production-proof.json', 'predecessor-approval.json', 'current-gate.json',
'approval-public-keyring.json', 'original_api.py')}
if not required <= set(checked):
raise RenewalRejected('INCOMPLETE_REGISTERED_CLOSURE')
if Path(__file__) != root / 'c5_completion_renewal.py':
raise RenewalRejected('UNREGISTERED_AUTHORITY_MODULE')
if (p['predecessor_snapshot'] != str(root / 'predecessor-approval.json')
or p['gate_path'] != str(root / 'current-gate.json')
or digest(checked[p['predecessor_snapshot']]) != p['predecessor_sha256']):
raise RenewalRejected('REGISTERED_SNAPSHOT_DRIFT')
material = json.loads(checked[str(root / 'source-material.json')])
if (material.get('status') != 'MATERIALIZED' or material.get('task_status') != 'SUCCEEDED'
or material.get('remote_verified') is not True
or material.get('source_commit') != p['governed_source_commit']
or material.get('source_tree') != p['governed_source_tree']
or material.get('file_sha256') != {
name: digest(checked[str(root / name)])
for name in RENEWAL_SOURCE_FILES}):
raise RenewalRejected('GOVERNED_CODE_PROVENANCE_REQUIRED')
historical = json.loads(checked[str(root / 'historical-production-proof.json')])
if (historical.get('candidate_id') != CID or historical.get('approval_fingerprint') != OLD_FP
or historical.get('release_plan_sha256') != PLAN
or historical.get('status') != 'COMPLETED'
or any(historical.get(k) is not True for k in (
'public_business_verified', 'real_c4_rollback_verified', 'c5_restore_verified'))):
raise RenewalRejected('COMPLETED_PRODUCTION_PROOF_REQUIRED')
gate = json.loads(checked[p['gate_path']])
expected_gate = {'candidate_id': CID, 'source_commit': SOURCE, 'artifact_sha256': ARTIFACT,
'release_plan_sha256': PLAN,
'local_tag_oid': TAG_OID, 'runtime_verified': True, 'business_verified': True,
'global_writes_enabled': False, 'observed_at': p['gate_observed_at']}
if (any(gate.get(k) != v for k, v in expected_gate.items())
or any(type(gate.get(k)) is not bool for k in (
'runtime_verified', 'business_verified', 'global_writes_enabled'))
or gate.get('remote_tag_oid') not in {None, TAG_OID}):
raise RenewalRejected('EXACT_POSTVERIFICATION_GATE_REQUIRED')
validate_completion_lineage(gate)
return {**p, 'payload_sha256': payload_sha}
def validate_host(self, *, state_root, candidate_id, source_commit, tree_oid,
artifact_sha256, release_plan_sha256):
self()
if (Path(state_root), candidate_id, source_commit, tree_oid, artifact_sha256, release_plan_sha256) != (
STATE_ROOT, CID, SOURCE, TREE, ARTIFACT, PLAN):
raise RenewalRejected('COMPLETION_HOST_BINDING_DRIFT')
def deny_runtime_effect(self, **kwargs):
self()
raise RenewalRejected('COMPLETION_RUNTIME_EFFECT_FORBIDDEN')
class CompletionRuntimeGuard:
def __init__(self, authority):
if type(authority) is not RegisteredCompletionAuthority:
raise RenewalRejected('REGISTERED_COMPLETION_AUTHORITY_REQUIRED')
self.authority = authority
def execute(self, **kwargs):
return self.authority.deny_runtime_effect(**kwargs)
def append_file(path, raw):
"""An existing record must be identical; it is never replaced."""
path = Path(path)
try:
fd = os.open(path, os.O_WRONLY | os.O_CREAT | os.O_EXCL | os.O_NOFOLLOW, 0o600)
except FileExistsError:
if secure_bytes(path, owner_uid=os.geteuid()) != raw:
raise RenewalRejected('APPEND_ONLY_RECORD_CONFLICT')
return False
with os.fdopen(fd, 'wb') as stream:
stream.write(raw)
stream.flush()
os.fsync(stream.fileno())
fd = os.open(path.parent, os.O_DIRECTORY)
try:
os.fsync(fd)
finally:
os.close(fd)
return True
def publish_successor(directory, old, new, public_key, *, now, registration_digest, predecessor_bytes):
"""Append records inside the SAME plane under a verified caller lock.
Registration validation, current Saga CAS, production/business readbacks,
and permission to call this function remain mandatory caller gates.
"""
fp = validate_successor(old, new, public_key, now=now)
if json.loads(predecessor_bytes) != old:
raise RenewalRejected('PREDECESSOR_BYTES_DRIFT')
predecessor_bytes_digest = digest(predecessor_bytes)
if len(registration_digest) != 64 or any(c not in '0123456789abcdef' for c in registration_digest):
raise RenewalRejected('REGISTERED_DIGEST_REQUIRED')
directory = Path(directory)
metadata = directory.lstat()
if not stat.S_ISDIR(metadata.st_mode) or metadata.st_uid != os.geteuid() or metadata.st_mode & 0o022:
raise RenewalRejected('UNSAFE_APPEND_DIRECTORY')
receipt = {'schema': 'aios.c5.completion-approval-successor.v1', 'candidate_id': CID,
'predecessor_fingerprint': OLD_FP, 'predecessor_bytes_sha256': predecessor_bytes_digest,
'successor_fingerprint': fp, 'successor_sha256': digest(canonical(new)),
'owner_registration_payload_sha256': registration_digest,
'completion_only': True, 'runtime_effects_allowed': False}
# A crash after the first append is unresolved until its exact receipt is
# reconciled. A loader must require BOTH files; no mutable current pointer.
append_file(directory / (fp + '.approval.json'), canonical(new))
append_file(directory / (fp + '.receipt.json'), canonical(receipt))
return receipt
class CompletionApprovalStore:
"""Explicit store dependency for a registered completion host/API.
The original store and original candidate approval remain untouched.
No global class replacement or expired approval verification is used.
An authenticated registration fixes ONE successor nonce and immutable
receipt path before signing. The host must separately enforce TAG_PENDING
and forbid runtime effects; this store does not execute a Saga.
"""
def __init__(self, original, directory, public_key, authority, approval_type):
self.original = original
self.directory = Path(directory)
self.public_key = public_key
self.authority = authority
self.approval_type = approval_type
def _policy(self):
# authority must be the fixed registered verifier, not a caller-supplied
# HTTP callback. Wiring it is outside this unregistered draft.
policy = self.authority()
if policy.get('candidate_id') != CID or policy.get('predecessor_fingerprint') != OLD_FP:
raise RenewalRejected('REGISTERED_EXACT_POLICY_REQUIRED')
nonce = policy.get('approval_nonce', '')
if not isinstance(nonce, str) or len(nonce) != 64 or any(c not in '0123456789abcdef' for c in nonce):
raise RenewalRejected('REGISTERED_NONCE_REQUIRED')
return policy, self.directory / (nonce + '.activation.json')
def _parent(self):
old = self.original.load(CID)
policy, _ = self._policy()
owner = policy.get('predecessor_uid')
if type(owner) is not int or owner not in {0, 1000}:
raise RenewalRejected('REGISTERED_PREDECESSOR_OWNER_REQUIRED')
raw = secure_bytes(self.original.root / (CID + '.json'), owner_uid=owner)
if json.loads(raw) != old.to_dict() or digest(raw) != policy.get('predecessor_sha256'):
raise RenewalRejected('PREDECESSOR_FILE_DRIFT')
return old.to_dict(), raw
def load(self, candidate_id):
if candidate_id != CID:
return self.original.load(candidate_id)
policy, activation = self._policy()
if not activation.exists():
return self.original.load(candidate_id)
receipt = json.loads(secure_bytes(activation, owner_uid=policy['predecessor_uid']))
expected = {'schema', 'candidate_id', 'predecessor_fingerprint', 'predecessor_bytes_sha256',
'successor_fingerprint', 'successor_sha256', 'owner_registration_payload_sha256',
'completion_only', 'runtime_effects_allowed'}
if set(receipt) != expected or receipt.get('owner_registration_payload_sha256') != policy['payload_sha256']:
raise RenewalRejected('ACTIVATION_RECEIPT_INVALID')
fp = receipt.get('successor_fingerprint', '')
if len(fp) != 64 or any(c not in '0123456789abcdef' for c in fp):
raise RenewalRejected('SUCCESSOR_FINGERPRINT_INVALID')
raw = secure_bytes(self.directory / (fp + '.approval.json'), owner_uid=policy['predecessor_uid'])
new = json.loads(raw)
parent, parent_raw = self._parent()
valid = validate_successor(parent, new, self.public_key, now=datetime.now(timezone.utc).timestamp())
exact = {'candidate_id': CID, 'predecessor_fingerprint': OLD_FP,
'predecessor_bytes_sha256': digest(parent_raw), 'successor_fingerprint': valid,
'successor_sha256': digest(raw), 'completion_only': True, 'runtime_effects_allowed': False}
if any(receipt.get(k) != v for k, v in exact.items()) or new['nonce'] != policy['approval_nonce']:
raise RenewalRejected('SUCCESSOR_ACTIVATION_DRIFT')
durable = json.loads(secure_bytes(self.directory / (fp + '.receipt.json'), owner_uid=policy['predecessor_uid']))
if durable != receipt:
raise RenewalRejected('DURABLE_RECEIPT_DRIFT')
return self.approval_type.from_dict(new)
def save(self, candidate_id, approval):
if candidate_id != CID:
return self.original.save(candidate_id, approval)
policy, activation = self._policy()
parent, raw = self._parent()
if approval.fingerprint == parent['fingerprint']:
return self.original.save(candidate_id, approval)
new = approval.to_dict()
if new.get('nonce') != policy['approval_nonce']:
raise RenewalRejected('REGISTERED_NONCE_MISMATCH')
# The caller supplies its authenticated registered effect lock and
# invokes the existing ApprovalVerifier before this store boundary.
receipt = publish_successor(self.directory, parent, new, self.public_key,
now=datetime.now(timezone.utc).timestamp(),
registration_digest=policy['payload_sha256'], predecessor_bytes=raw)
created = append_file(activation, canonical(receipt))
if self.load(candidate_id).fingerprint != approval.fingerprint:
raise RenewalRejected('SUCCESSOR_READBACK_FAILED')
return activation, created
class RegisteredCompletionApprovalStore(CompletionApprovalStore):
"""Production dependency; no request-selected root or authority callback."""
def __init__(self, original, public_key, approval_type):
if Path(original.root) != STATE_ROOT / 'approvals':
raise RenewalRejected('SAME_APPROVAL_PLANE_REQUIRED')
super().__init__(original, STATE_ROOT / 'approvals' / 'c5-completion-successors',
public_key, RegisteredCompletionAuthority(), approval_type)
class CompletionSignerMixin:
"""Narrow additional method of the EXISTING credential-bound signer.
Activation must register this exact mixin, verifier and namespace snapshot
pins. The encrypted private key stays in the existing signer's unit. This
draft supplies no activation, credential access or HTTP route.
"""
def sign_exact_completion_successor(self, request):
expected = {'schema', 'operation', 'candidate_id', 'predecessor_fingerprint'}
if (set(request) != expected or request.get('operation') != 'renew_exact_completion_approval'
or request.get('candidate_id') != CID or request.get('predecessor_fingerprint') != OLD_FP
or request.get('schema') != 'aios.exact-release-signer.request.v1'):
raise RenewalRejected('EXACT_SIGNER_RENEWAL_REQUEST_REQUIRED')
policy = self.completion_authority()
now = datetime.now(timezone.utc).timestamp()
if (policy.get('candidate_id') != CID or policy.get('predecessor_fingerprint') != OLD_FP
or not policy['issued_at'] <= now < policy['expires_at']
or not 0 <= now - policy['gate_observed_at'] <= 60):
raise RenewalRejected('FRESH_REGISTERED_COMPLETION_GATE_REQUIRED')
old_raw = secure_bytes(policy['predecessor_snapshot'], owner_uid=0)
if digest(old_raw) != policy['predecessor_sha256']:
raise RenewalRejected('REGISTERED_SIGNER_PREIMAGE_DRIFT')
old = json.loads(old_raw)
public_key = self.private_key.public_key().public_bytes(Encoding.Raw, PublicFormat.Raw)
verify_signature(old, public_key)
exact = {'candidate_id': CID, 'source_commit': SOURCE, 'tree_oid': TREE,
'artifact_sha256': ARTIFACT, 'release_plan_sha256': PLAN,
'fingerprint': OLD_FP, 'capabilities': CAPABILITIES,
'key_id': self.policy.key_id, 'owner_identity': self.policy.owner_identity,
'trust_domain': self.policy.trust_domain,
'owner_approval_record_oid': self.authorization_oid}
if any(old.get(k) != v for k, v in exact.items()) or timestamp(old['expires_at']) > now:
raise RenewalRejected('EXACT_EXPIRED_SIGNER_PREDECESSOR_REQUIRED')
nonce = policy['approval_nonce']
if len(nonce) != 64 or any(c not in '0123456789abcdef' for c in nonce) or nonce == old['nonce']:
raise RenewalRejected('FRESH_REGISTERED_SIGNER_NONCE_REQUIRED')
output = self.state_root / 'completion-renewals' / (nonce + '.json')
if output.exists():
result = json.loads(secure_bytes(output, owner_uid=0))
if (result.get('registration_payload_sha256') != policy['payload_sha256']
or result['approval']['nonce'] != nonce):
raise RenewalRejected('SIGNER_REPLAY_CONFLICT')
validate_successor(old, result['approval'], public_key, now=now)
return {**result, 'status': 'SIGNED_IDEMPOTENT'}
expiry = min(now + 600, policy['expires_at'] - 5)
if expiry - now < 120:
raise RenewalRejected('INSUFFICIENT_SIGNER_WINDOW')
def utc(value):
return datetime.fromtimestamp(value, timezone.utc).replace(microsecond=0).isoformat().replace('+00:00', 'Z')
payload = {k:v for k,v in old.items() if k not in {'fingerprint', 'signature'}}
payload.update(issued_at=utc(now), not_before=utc(now), expires_at=utc(expiry), nonce=nonce)
raw = canonical(payload)
# All candidate, plan, historical signature and registered freshness
# checks precede this only signing operation.
approval = {**payload, 'fingerprint': digest(raw),
'signature': base64.b64encode(self.private_key.sign(raw)).decode()}
validate_successor(old, approval, public_key, now=now)
result = {'schema': 'aios.exact-release-signer.response.v1', 'status': 'SIGNED',
'approval': approval, 'registration_payload_sha256': policy['payload_sha256'],
'predecessor_fingerprint': OLD_FP, 'completion_only': True, 'arbitrary_signing': False}
# This registered directory must be created by the activation consumer
# inside the existing signer state root, never a request path.
append_file(output, canonical(result))
return result
@@ -0,0 +1,58 @@
"""Registered fixed extension of the existing credential-bound signer unit."""
import importlib.util
import os
from pathlib import Path
import sys
from c5_completion_renewal import (
CompletionSignerMixin, RegisteredCompletionAuthority,
RenewalRejected, secure_bytes, digest,
)
ORIGINAL = Path('/usr/local/libexec/aios_exact_release_signer.py')
ORIGINAL_SHA = '6e2a5a9df0edfe566175e5b556d15901b2c8fe4484388ad74af2ac64f810c930'
def signer_class(original):
class CompletionSigner(CompletionSignerMixin, original.ExactReleaseSigner):
def completion_authority(self):
return RegisteredCompletionAuthority()()
def handle(self, request):
if request.get('operation') == 'renew_exact_completion_approval':
return self.sign_exact_completion_successor(request)
return super().handle(request)
return CompletionSigner
def main():
import c5_completion_renewal as renewal
renewal.configure_registered_bindings()
if Path(__file__) != renewal.AUTHORITY_ROOT / 'c5_completion_signer.py':
raise RenewalRejected('REGISTERED_SIGNER_WRAPPER_REQUIRED')
RegisteredCompletionAuthority()()
if digest(secure_bytes(ORIGINAL)) != ORIGINAL_SHA:
raise RenewalRejected('ORIGINAL_SIGNER_PREIMAGE_DRIFT')
spec = importlib.util.spec_from_file_location('c5_original_exact_release_signer', ORIGINAL)
original = importlib.util.module_from_spec(spec)
sys.modules[spec.name] = original
spec.loader.exec_module(original)
credentials = Path(os.environ.get('CREDENTIALS_DIRECTORY', ''))
if not credentials.is_absolute() or credentials.parent != Path('/run/credentials'):
raise RenewalRejected('EXISTING_SIGNER_PURPOSE_CREDENTIAL_REQUIRED')
if credentials.name != 'aios-exact-release-signer.service':
raise RenewalRejected('EXISTING_SIGNER_UNIT_REQUIRED')
cgroup = Path('/proc/self/cgroup').read_text()
if not any(line.split(':', 2)[-1].rstrip('/').endswith('/aios-exact-release-signer.service')
for line in cgroup.splitlines()):
raise RenewalRejected('EXISTING_SIGNER_CGROUP_REQUIRED')
signer = signer_class(original)(
Path('/etc/aios-gitops/exact-release-signer-policy.json'),
credentials / 'owner_release_private_key',
)
original.serve(Path('/run/aios-exact-release-signer/signer.sock'), signer)
return 0
if __name__ == '__main__':
raise SystemExit(main())
@@ -0,0 +1,503 @@
"""Exact C5 runtime-switch adapter for the silent-release Saga.
The adapter owns only the production deployment phase. Tag publication and
Registry/Baseline finalization remain separate ``ReleaseOrchestrator`` Saga
steps. A host must register immutable executable bytes and provide the real
HMAC-authenticated source-admission store plus the signed Owner approval
verifier before this adapter can be constructed.
"""
from __future__ import annotations
from dataclasses import dataclass
import hashlib
import json
import os
from pathlib import Path
import re
import stat
import subprocess
from typing import Any, Mapping
from release_source_admission import ReleaseSourceAdmissionStore
from silent_release_controller import ReleaseCandidate
from silent_release_hardening import (
CapabilityAuthorizationError,
CapabilityLedger,
ClaimProbeResult,
ReleasePlan,
RuntimeSwitchController,
SignedApprovalVerifier,
SignedReleasePlanApproval,
_canonical_json,
)
C5_VERSION = "V2R2C5"
C5_PREPUBLICATION_SOURCE_COMMIT = "396ea9811124736aa140d2810ab53eaefb6b5c1c"
C4_ROLLBACK_TARGET = {
"version": "V2R2C4",
"target_oid": "e5f8209f57b43fc39e55c4555776e6362ce326eb",
"artifact_sha256": "0dfc0461f7f9aa25cfbfc37008dcbd2d48d5341383574163b7bba102852062c4",
}
C4_ROLLBACK_ARTIFACT_PATH = Path(
"/var/lib/aios-gitops/release-only/"
"v2r2c4-e5f8209f57b4/artifact.tar.gz"
)
C5_PRODUCTION_ENVIRONMENT_FILE = Path("/etc/aios/v2r2c2.env")
C5_PRODUCTION_SERVICE = "aios-v2r2c2-production.service"
C5_PRODUCTION_PORT = 18928
REQUEST_SCHEMA = "aios.c5.exact-production-deployment-request.v1"
HEX64 = re.compile(r"^[0-9a-f]{64}$")
class C5DeploymentBindingError(CapabilityAuthorizationError):
"""C5 source, plan, approval, executor, or receipt binding is invalid."""
class C5DeploymentOutcomeUnknown(RuntimeError):
"""The deployment effect must be reconciled before any retry."""
class C5DeploymentConflict(RuntimeError):
"""The observed runtime is bound to a different release."""
def _sha256_file(path: Path) -> str:
digest = hashlib.sha256()
with path.open("rb") as source:
for block in iter(lambda: source.read(1024 * 1024), b""):
digest.update(block)
return digest.hexdigest()
def _run_registered_adapter(
command: tuple[str, ...], request: Mapping[str, Any]
) -> dict[str, Any]:
"""Invoke only the code-pinned process and accept one JSON object."""
try:
completed = subprocess.run(
list(command),
input=_canonical_json(dict(request)),
check=False,
capture_output=True,
timeout=180,
env={"PATH": os.environ.get("PATH", "")},
)
except (OSError, subprocess.SubprocessError) as exc:
raise C5DeploymentOutcomeUnknown(
"C5_DEPLOYMENT_EXECUTOR_UNAVAILABLE"
) from exc
if completed.returncode != 0:
raise C5DeploymentOutcomeUnknown(
"C5_DEPLOYMENT_EXECUTOR_FAILED"
)
try:
lines = [
line
for line in completed.stdout.decode("utf-8").splitlines()
if line.strip()
]
value = json.loads(lines[-1]) if lines else None
except (UnicodeError, ValueError) as exc:
raise C5DeploymentOutcomeUnknown(
"C5_DEPLOYMENT_EXECUTOR_RESPONSE_INVALID"
) from exc
if not isinstance(value, dict):
raise C5DeploymentOutcomeUnknown(
"C5_DEPLOYMENT_EXECUTOR_RESPONSE_INVALID"
)
return value
def _secure_registered_file(
path: Path,
expected_sha256: str,
*,
require_root_owner: bool,
) -> None:
path = Path(path)
if (
not path.is_absolute()
or path.is_symlink()
or path.resolve(strict=False) != path
):
raise C5DeploymentBindingError("C5_EXECUTOR_PATH_INVALID")
try:
info = path.stat()
except OSError as exc:
raise C5DeploymentBindingError("C5_EXECUTOR_PATH_INVALID") from exc
if not stat.S_ISREG(info.st_mode):
raise C5DeploymentBindingError("C5_EXECUTOR_PATH_INVALID")
if os.name != "nt" and stat.S_IMODE(info.st_mode) & 0o022:
raise C5DeploymentBindingError("C5_EXECUTOR_PATH_WRITABLE")
if require_root_owner:
if not hasattr(os, "geteuid") or info.st_uid != 0:
raise C5DeploymentBindingError("C5_EXECUTOR_OWNER_INVALID")
parent = path.parent
while True:
parent_info = parent.stat()
if (
parent.is_symlink()
or parent_info.st_uid != 0
or stat.S_IMODE(parent_info.st_mode) & 0o022
):
raise C5DeploymentBindingError(
"C5_EXECUTOR_PARENT_SECURITY_INVALID"
)
if parent == parent.parent:
break
parent = parent.parent
if _sha256_file(path) != expected_sha256:
raise C5DeploymentBindingError("C5_EXECUTOR_CODE_MISMATCH")
@dataclass(frozen=True, slots=True)
class RegisteredC5DeploymentExecutor:
"""Pinned host configuration for one concrete process adapter.
``adapter_path`` is optional for a native executable. When present the
command is exactly ``executable adapter_path``; arbitrary argv and Python
callables are intentionally unsupported.
"""
executor_id: str
executable: Path
executable_sha256: str
artifact_path: Path
rollback_artifact_path: Path
production_environment_file: Path
adapter_path: Path | None = None
adapter_sha256: str | None = None
require_root_owner: bool = True
execution_scope: str = "PRODUCTION"
isolation_root: Path | None = None
rollback_target: Mapping[str, str] | None = None
def __post_init__(self) -> None:
if not self.executor_id or len(self.executor_id) > 255:
raise C5DeploymentBindingError("C5_EXECUTOR_ID_INVALID")
if HEX64.fullmatch(self.executable_sha256) is None:
raise C5DeploymentBindingError("C5_EXECUTOR_DIGEST_INVALID")
if (self.adapter_path is None) != (self.adapter_sha256 is None):
raise C5DeploymentBindingError("C5_EXECUTOR_ADAPTER_BINDING_INVALID")
if (
self.adapter_sha256 is not None
and HEX64.fullmatch(self.adapter_sha256) is None
):
raise C5DeploymentBindingError("C5_EXECUTOR_DIGEST_INVALID")
if not Path(self.artifact_path).is_absolute():
raise C5DeploymentBindingError("C5_ARTIFACT_PATH_INVALID")
if self.execution_scope not in {"PRODUCTION", "ISOLATED_REHEARSAL"}:
raise C5DeploymentBindingError("C5_EXECUTION_SCOPE_INVALID")
target = dict(self.rollback_target or C4_ROLLBACK_TARGET)
if target.keys() != C4_ROLLBACK_TARGET.keys() or target["version"] != C4_ROLLBACK_TARGET["version"] or target["target_oid"] != C4_ROLLBACK_TARGET["target_oid"] or HEX64.fullmatch(str(target["artifact_sha256"])) is None:
raise C5DeploymentBindingError("C5_ROLLBACK_TARGET_INVALID")
if self.execution_scope == "PRODUCTION":
if target != C4_ROLLBACK_TARGET:
raise C5DeploymentBindingError("C5_ROLLBACK_TARGET_INVALID")
if Path(self.rollback_artifact_path) != C4_ROLLBACK_ARTIFACT_PATH:
raise C5DeploymentBindingError("C5_ROLLBACK_ARTIFACT_PATH_INVALID")
if Path(self.production_environment_file) != C5_PRODUCTION_ENVIRONMENT_FILE:
raise C5DeploymentBindingError("C5_PRODUCTION_ENVIRONMENT_FILE_INVALID")
if self.isolation_root is not None:
raise C5DeploymentBindingError("C5_ISOLATION_ROOT_INVALID")
else:
if self.isolation_root is None or not Path(self.isolation_root).is_absolute():
raise C5DeploymentBindingError("C5_ISOLATION_ROOT_INVALID")
root = Path(self.isolation_root).resolve(strict=False)
for bound_path in (self.rollback_artifact_path, self.production_environment_file):
path = Path(bound_path)
if not path.is_absolute() or path.resolve(strict=False) == root:
raise C5DeploymentBindingError("C5_REHEARSAL_BINDING_INVALID")
try:
path.resolve(strict=False).relative_to(root)
except ValueError as exc:
raise C5DeploymentBindingError("C5_REHEARSAL_BINDING_INVALID") from exc
_secure_registered_file(
Path(self.rollback_artifact_path),
target["artifact_sha256"],
require_root_owner=self.require_root_owner,
)
object.__setattr__(self, "rollback_target", target)
self.verify_code()
def verify_code(self) -> None:
_secure_registered_file(
Path(self.executable),
self.executable_sha256,
require_root_owner=self.require_root_owner,
)
if self.adapter_path is not None:
_secure_registered_file(
Path(self.adapter_path),
str(self.adapter_sha256),
require_root_owner=self.require_root_owner,
)
@property
def command(self) -> tuple[str, ...]:
values = [str(Path(self.executable))]
if self.adapter_path is not None:
values.append(str(Path(self.adapter_path)))
return tuple(values)
class C5ExactDeploymentAdapter:
"""Callable runtime-switch phase with exact C5 admission and approval."""
def __init__(
self,
*,
candidate: ReleaseCandidate,
release_plan: ReleasePlan,
approval: SignedReleasePlanApproval,
approval_verifier: SignedApprovalVerifier,
source_admissions: ReleaseSourceAdmissionStore,
executor: RegisteredC5DeploymentExecutor,
) -> None:
if not isinstance(approval_verifier, SignedApprovalVerifier):
raise C5DeploymentBindingError("C5_APPROVAL_VERIFIER_REQUIRED")
if not isinstance(source_admissions, ReleaseSourceAdmissionStore):
raise C5DeploymentBindingError("C5_SOURCE_ADMISSION_STORE_REQUIRED")
self.candidate = candidate
self.release_plan = release_plan
self.approval = approval
self.approval_verifier = approval_verifier
self.source_admissions = source_admissions
self.executor = executor
self._binding = self._verify_binding(candidate, release_plan)
@staticmethod
def _global_writes_disabled() -> bool:
return os.environ.get("AIOS_GITOPS_WRITES_ENABLED", "false").lower() == "false"
def _verify_binding(
self,
candidate: ReleaseCandidate,
plan: ReleasePlan,
) -> dict[str, Any]:
if candidate.fingerprint != self.candidate.fingerprint or plan.sha256 != self.release_plan.sha256:
raise C5DeploymentBindingError("C5_RUNTIME_INPUT_BINDING_MISMATCH")
plan.validate()
if plan.tag.version != C5_VERSION or plan.finalization.version != C5_VERSION:
raise C5DeploymentBindingError("C5_VERSION_BINDING_INVALID")
if candidate.source_commit == C5_PREPUBLICATION_SOURCE_COMMIT:
raise C5DeploymentBindingError("C5_PREPUBLICATION_SOURCE_FORBIDDEN")
rollback_target = dict(self.executor.rollback_target or C4_ROLLBACK_TARGET)
if plan.deployment.rollback_target != rollback_target:
raise C5DeploymentBindingError("C5_ROLLBACK_TARGET_INVALID")
if plan.deployment.service != C5_PRODUCTION_SERVICE:
raise C5DeploymentBindingError("C5_PRODUCTION_SERVICE_INVALID")
if plan.deployment.writer_expected is not False:
raise C5DeploymentBindingError("C5_LEGACY_WRITES_MUST_REMAIN_DISABLED")
if not self._global_writes_disabled():
raise C5DeploymentBindingError("C5_LEGACY_WRITES_MUST_REMAIN_DISABLED")
admitted = self.source_admissions.resolve_recipe(
C5_VERSION,
candidate.candidate_id,
candidate.source_commit,
)
if (
admitted.get("source_commit") != candidate.source_commit
or admitted.get("tree_oid") != candidate.tree_oid
):
raise C5DeploymentBindingError("C5_ADMITTED_SOURCE_BINDING_INVALID")
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 C5DeploymentBindingError("C5_CANDIDATE_PLAN_BINDING_INVALID")
self.approval_verifier.verify_release_plan(
self.approval,
candidate,
plan,
plan.policy_sha256,
)
artifact = Path(self.executor.artifact_path)
_secure_registered_file(
artifact,
candidate.artifact_sha256,
require_root_owner=self.executor.require_root_owner,
)
artifact_size = artifact.stat().st_size
if artifact_size <= 0:
raise C5DeploymentBindingError("C5_ARTIFACT_BYTES_MISMATCH")
self.executor.verify_code()
return {
"version": C5_VERSION,
"candidate_id": candidate.candidate_id,
"source_commit": candidate.source_commit,
"target_oid": candidate.source_commit,
"tree_oid": candidate.tree_oid,
"artifact_sha256": candidate.artifact_sha256,
"artifact_path": str(artifact),
"artifact_size": artifact_size,
"manifest_sha256": candidate.manifest_sha256,
"deployment_plan_sha256": plan.deployment.sha256,
"release_plan_sha256": plan.sha256,
"policy_sha256": plan.policy_sha256,
"approval_plan_id": self.approval.approval_plan_id,
"approval_fingerprint": self.approval.fingerprint,
"owner_approval_record_oid": self.approval.owner_approval_record_oid,
"evidence_closure_oid": self.approval.evidence_closure_oid,
"repository_identity": plan.deployment.repository_identity,
"production_service": plan.deployment.service,
"production_port": C5_PRODUCTION_PORT,
"systemd_unit_sha256": plan.deployment.systemd_unit_sha256,
"dropin_sha256": plan.deployment.dropin_sha256,
"rollback_target": rollback_target,
"rollback_artifact_path": str(
self.executor.rollback_artifact_path
),
"production_environment_file": str(
self.executor.production_environment_file
),
"writer_expected": False,
"global_legacy_writes_enabled": False,
"registered_executor_id": self.executor.executor_id,
"executor_executable_sha256": self.executor.executable_sha256,
"executor_adapter_sha256": self.executor.adapter_sha256,
}
def _request(self, operation: str) -> dict[str, Any]:
return {
"schema": REQUEST_SCHEMA,
"operation": operation,
**self._binding,
}
def _invoke(self, operation: str) -> dict[str, Any]:
if not self._global_writes_disabled():
raise C5DeploymentBindingError("C5_LEGACY_WRITES_MUST_REMAIN_DISABLED")
self.executor.verify_code()
value = _run_registered_adapter(
self.executor.command, self._request(operation)
)
for key, expected in self._binding.items():
if value.get(key) != expected:
raise C5DeploymentBindingError(f"C5_DEPLOYMENT_RECEIPT_MISMATCH:{key}")
if value.get("writer_disabled") is not True:
raise C5DeploymentBindingError("C5_DEPLOYMENT_WRITER_STATE_INVALID")
receipt = value.get("deployment_receipt")
if not isinstance(receipt, Mapping) or not receipt:
raise C5DeploymentBindingError("C5_DEPLOYMENT_RECEIPT_REQUIRED")
return value
@staticmethod
def _classify(value: Mapping[str, Any]) -> ClaimProbeResult:
status = value.get("status")
if (
status == "ACTIVE_VERIFIED"
and value.get("verified") is True
and value.get("deployment_state") == "ACTIVE"
and value.get("production_deployed") is True
and value.get("writer_disabled") is True
and value.get("global_legacy_writes_enabled") is False
):
return ClaimProbeResult.APPLIED_VERIFIED
if status == "NOT_APPLIED":
return ClaimProbeResult.NOT_APPLIED
if status == "CONFLICT":
return ClaimProbeResult.CONFLICT
return ClaimProbeResult.INDETERMINATE
def recovery_probe(self) -> ClaimProbeResult:
"""Read-only exact probe used by ``CapabilityLedger`` after UNKNOWN."""
try:
self._verify_binding(self.candidate, self.release_plan)
return self._classify(self._invoke("probe"))
except C5DeploymentConflict:
return ClaimProbeResult.CONFLICT
except Exception:
return ClaimProbeResult.INDETERMINATE
def rollback(
self,
*,
candidate: ReleaseCandidate,
plan: ReleasePlan,
) -> dict[str, Any]:
"""Execute only the explicit Saga rollback phase and verify exact C4."""
self._verify_binding(candidate, plan)
value = self._invoke("rollback")
if (
value.get("status") != "ROLLED_BACK_VERIFIED"
or value.get("verified") is not True
or value.get("rollback_verified") is not True
or value.get("rollback_version") != self.executor.rollback_target["version"]
or value.get("rollback_target_oid")
!= self.executor.rollback_target["target_oid"]
or value.get("rollback_artifact_sha256")
!= self.executor.rollback_target["artifact_sha256"]
):
raise C5DeploymentOutcomeUnknown("C5_ROLLBACK_OUTCOME_UNKNOWN")
return {
"verified": True,
"rollback_verified": True,
"rollback_version": self.executor.rollback_target["version"],
"rollback_target_oid": self.executor.rollback_target["target_oid"],
"rollback_artifact_sha256": self.executor.rollback_target["artifact_sha256"],
"writer_disabled": True,
}
def __call__(
self,
*,
candidate: ReleaseCandidate,
plan: ReleasePlan,
) -> dict[str, Any]:
self._verify_binding(candidate, plan)
probe = self._invoke("probe")
classification = self._classify(probe)
if classification == ClaimProbeResult.CONFLICT:
raise C5DeploymentConflict("C5_RUNTIME_BINDING_CONFLICT")
if classification == ClaimProbeResult.INDETERMINATE:
raise C5DeploymentOutcomeUnknown("C5_DEPLOYMENT_OUTCOME_UNKNOWN")
if classification == ClaimProbeResult.NOT_APPLIED:
applied = self._invoke("apply")
if applied.get("status") == "CONFLICT":
raise C5DeploymentConflict("C5_RUNTIME_BINDING_CONFLICT")
if applied.get("status") not in {"APPLIED", "ACTIVE_VERIFIED"}:
raise C5DeploymentOutcomeUnknown("C5_DEPLOYMENT_OUTCOME_UNKNOWN")
probe = self._invoke("probe")
classification = self._classify(probe)
if classification == ClaimProbeResult.CONFLICT:
raise C5DeploymentConflict("C5_RUNTIME_BINDING_CONFLICT")
if classification != ClaimProbeResult.APPLIED_VERIFIED:
raise C5DeploymentOutcomeUnknown("C5_DEPLOYMENT_OUTCOME_UNKNOWN")
return {
"verified": True,
"version": C5_VERSION,
"candidate_id": candidate.candidate_id,
"source_commit": candidate.source_commit,
"tree_oid": candidate.tree_oid,
"artifact_sha256": candidate.artifact_sha256,
"release_plan_sha256": plan.sha256,
"approval_fingerprint": self.approval.fingerprint,
"rollback_target": dict(self.executor.rollback_target),
"writer_disabled": True,
"global_legacy_writes_enabled": False,
"registered_executor_id": self.executor.executor_id,
"deployment_receipt_sha256": hashlib.sha256(
_canonical_json(dict(probe))
).hexdigest(),
}
def controller(
self,
ledger: CapabilityLedger,
*,
lease_seconds: float = 300,
) -> RuntimeSwitchController:
"""Bind this exact adapter to the existing capability controller."""
return RuntimeSwitchController(
ledger,
self,
recovery_probe=self.recovery_probe,
executor_id=self.executor.executor_id,
lease_seconds=lease_seconds,
)
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,688 @@
"""Owner-registered dispatch boundary for the fixed-scope C5 installer.
The registration authorizes one exact signed installation contract and one
exact set of installed executor bytes. It is not an execution receipt; the
wrapped :class:`Installer` remains the component that durably records and
reconciles installation state.
"""
from __future__ import annotations
from dataclasses import dataclass
import argparse
import hashlib
import json
import os
from pathlib import Path
import re
import socket
import stat
import sys
import time
from typing import Any, Callable, Mapping
import c5_staging_installation as _installer_module
from c5_staging_installation import (
BRIDGE_EFFECTS,
BRIDGE_TARGET,
HOST,
InstallationError,
InstallationOutcomeUnknown,
ISSUER_EFFECTS,
ISSUER_TARGET,
OWNER_NAMESPACE,
R14_SOURCE_COMMIT,
REPOSITORY,
SUCCESSOR_SCHEMA,
Installer,
RegistryOwnerVerifier,
canonical_bytes,
verify_signed_contract,
)
from registered_owner_signer_adapter import RegisteredOwnerSignerAdapter
SCHEMA = "aios.c5.registered-installer-executor.v1"
PROVENANCE_SCHEMA = "aios.c5.executor-provenance.v1"
EXECUTION_DIGEST = "0779fa615e8e7971a8dcfccf0f295b1323cd9b64787e174aea4fd2affc414339"
BRIDGE_PORT = 18892
ISSUER_PORT = 18946
CAPABILITIES = frozenset({"install", "rollback", "reconcile"})
TARGET_EFFECTS = {BRIDGE_TARGET: BRIDGE_EFFECTS, ISSUER_TARGET: ISSUER_EFFECTS}
MAX_TTL_SECONDS = 900
MAX_CODE_BYTES = 16 * 1024 * 1024
DEFAULT_REGISTRY = Path("/etc/aios/single-host-signer-registry.json")
DEFAULT_PROVENANCE_MANIFEST = Path("/etc/aios/c5-executor-provenance.json")
HEX40 = re.compile(r"^[0-9a-f]{40}$")
HEX64 = re.compile(r"^[0-9a-f]{64}$")
NONCE = re.compile(r"^[A-Za-z0-9._:\-]{16,128}$")
SCOPE_ID = re.compile(r"^[A-Za-z0-9][A-Za-z0-9._:\-]{2,127}$")
class ExecutorRegistrationError(RuntimeError):
def __init__(self, code: str) -> None:
super().__init__(code)
self.code = code
def _fail(code: str) -> None:
raise ExecutorRegistrationError(code)
def _sha256_bytes(data: bytes) -> str:
return hashlib.sha256(data).hexdigest()
def _require_digest(value: Any, pattern: re.Pattern[str], code: str) -> str:
if not isinstance(value, str) or pattern.fullmatch(value) is None:
_fail(code)
return value
def _strict_object(pairs: list[tuple[str, Any]]) -> dict[str, Any]:
result: dict[str, Any] = {}
for key, value in pairs:
if key in result:
_fail("EXECUTOR_JSON_DUPLICATE_FIELD")
result[key] = value
return result
def _secure_bytes(
path: Path, *, require_root_owner: bool = True, maximum: int = MAX_CODE_BYTES,
) -> bytes:
path = Path(path)
if not path.is_absolute():
_fail("EXECUTOR_CODE_PATH_INVALID")
try:
if path.is_symlink() or path.resolve(strict=True) != path:
_fail("EXECUTOR_CODE_PATH_INVALID")
before = path.stat()
except OSError as exc:
raise ExecutorRegistrationError("EXECUTOR_CODE_PATH_INVALID") from exc
flags = os.O_RDONLY
if hasattr(os, "O_NOFOLLOW"):
flags |= os.O_NOFOLLOW
try:
fd = os.open(path, flags)
except OSError as exc:
raise ExecutorRegistrationError("EXECUTOR_CODE_PATH_INVALID") from exc
try:
current = os.fstat(fd)
if (
not stat.S_ISREG(current.st_mode)
or current.st_dev != before.st_dev
or current.st_ino != before.st_ino
or current.st_size < 1
or current.st_size > maximum
):
_fail("EXECUTOR_CODE_FILE_INVALID")
if require_root_owner:
if os.name != "posix" or current.st_uid != 0:
_fail("EXECUTOR_CODE_PATH_OWNER_INVALID")
if stat.S_IMODE(current.st_mode) & 0o022:
_fail("EXECUTOR_CODE_PATH_WRITABLE")
parent = path.parent
while True:
parent_stat = parent.stat()
if parent.is_symlink() or parent_stat.st_uid != 0:
_fail("EXECUTOR_CODE_PATH_OWNER_INVALID")
if stat.S_IMODE(parent_stat.st_mode) & 0o022:
_fail("EXECUTOR_CODE_PATH_WRITABLE")
if parent == parent.parent:
break
parent = parent.parent
chunks: list[bytes] = []
remaining = current.st_size
while remaining:
chunk = os.read(fd, min(remaining, 1024 * 1024))
if not chunk:
_fail("EXECUTOR_CODE_FILE_INVALID")
chunks.append(chunk)
remaining -= len(chunk)
if os.read(fd, 1):
_fail("EXECUTOR_CODE_FILE_INVALID")
return b"".join(chunks)
finally:
os.close(fd)
@dataclass(frozen=True, slots=True)
class CodeProvenance:
environment: str
instance_id: str
integration_commit: str
integration_tree: str
installer_module_path: Path
installer_script_path: Path
executor_module_path: Path
installer_module_sha256: str
installer_script_sha256: str
executor_module_sha256: str
authority_manifest_sha256: str
def _actual_installer_module_path() -> Path:
value = getattr(_installer_module, "__file__", None)
if not isinstance(value, str):
_fail("EXECUTOR_LOADED_INSTALLER_PATH_INVALID")
return Path(value).resolve(strict=True)
def _actual_executor_module_path() -> Path:
return Path(__file__).resolve(strict=True)
def load_code_provenance(
manifest: Path, *, require_root_owner: bool = True,
) -> CodeProvenance:
"""Load identity from an authority manifest and actual loaded paths."""
manifest = Path(manifest)
raw = _secure_bytes(manifest, require_root_owner=require_root_owner)
try:
value = json.loads(raw, object_pairs_hook=_strict_object)
except (UnicodeError, json.JSONDecodeError) as exc:
raise ExecutorRegistrationError("EXECUTOR_PROVENANCE_INVALID") from exc
if (
not isinstance(value, dict)
or set(value) != {
"schema", "environment", "instance_id", "integration_commit",
"integration_tree", "entries",
}
or value["schema"] != PROVENANCE_SCHEMA
or not isinstance(value["entries"], dict)
or set(value["entries"]) != {
"installer_module", "installer_script", "executor_module",
}
):
_fail("EXECUTOR_PROVENANCE_INVALID")
expected_paths = {
"installer_module": _actual_installer_module_path(),
"executor_module": _actual_executor_module_path(),
}
resolved: dict[str, Path] = {}
hashes: dict[str, str] = {}
for label, entry in value["entries"].items():
if not isinstance(entry, dict) or set(entry) != {"path", "sha256"}:
_fail("EXECUTOR_PROVENANCE_INVALID")
if not isinstance(entry["path"], str):
_fail("EXECUTOR_PROVENANCE_INVALID")
path = Path(entry["path"])
if not path.is_absolute():
_fail("EXECUTOR_CODE_PATH_INVALID")
try:
canonical = path.resolve(strict=True)
except OSError as exc:
raise ExecutorRegistrationError("EXECUTOR_CODE_PATH_INVALID") from exc
if path != canonical:
_fail("EXECUTOR_CODE_PATH_INVALID")
if label in expected_paths and canonical != expected_paths[label]:
_fail("EXECUTOR_PROVENANCE_LOADED_PATH_MISMATCH")
if label == "installer_script" and canonical.name != "c5_staging_installation.py":
_fail("EXECUTOR_INSTALLER_SCRIPT_PATH_INVALID")
digest = _sha256_bytes(
_secure_bytes(canonical, require_root_owner=require_root_owner)
)
if entry["sha256"] != digest:
_fail("EXECUTOR_PROVENANCE_CODE_MISMATCH")
resolved[label] = canonical
hashes[label] = digest
if (
not isinstance(value["environment"], str)
or SCOPE_ID.fullmatch(value["environment"]) is None
or not isinstance(value["instance_id"], str)
or SCOPE_ID.fullmatch(value["instance_id"]) is None
):
_fail("EXECUTOR_PROVENANCE_SCOPE_INVALID")
return CodeProvenance(
environment=value["environment"],
instance_id=value["instance_id"],
integration_commit=_require_digest(
value["integration_commit"], HEX40,
"EXECUTOR_INTEGRATION_COMMIT_INVALID",
),
integration_tree=_require_digest(
value["integration_tree"], HEX40,
"EXECUTOR_INTEGRATION_TREE_INVALID",
),
installer_module_path=resolved["installer_module"],
installer_script_path=resolved["installer_script"],
executor_module_path=resolved["executor_module"],
installer_module_sha256=hashes["installer_module"],
installer_script_sha256=hashes["installer_script"],
executor_module_sha256=hashes["executor_module"],
authority_manifest_sha256=_sha256_bytes(raw),
)
def _contract_binding(payload: Mapping[str, Any], digest: str) -> dict[str, Any]:
return {
"target": payload["target"],
"contract_digest": digest,
"old_snapshot_sha256": _sha256_bytes(canonical_bytes(payload["existing"])),
"new_manifest_sha256": _sha256_bytes(canonical_bytes({
"release_root": payload["release_root"],
"files": payload["files"],
"readiness": payload["readiness"],
})),
"rollback_sha256": _sha256_bytes(canonical_bytes(payload["rollback"])),
"contract_issued_at": payload["issued_at"],
"contract_expires_at": payload["expires_at"],
"contract_nonce": payload["nonce"],
}
def _issuer_dependency(payload: Mapping[str, Any], digest: str) -> dict[str, Any]:
return {
"target": payload["target"],
"contract_digest": digest,
"source_commit": payload["source_commit"],
"support_commit": payload["support_commit"],
"owner_signer_id": payload["owner_signer_id"],
"owner_key_id": payload["owner_key_id"],
"contract_nonce": payload["nonce"],
"contract_issued_at": payload["issued_at"],
"contract_expires_at": payload["expires_at"],
"rollback_sha256": _sha256_bytes(canonical_bytes(payload["rollback"])),
}
def _registration_payload(
*, executor_id: Any, nonce: Any, issued_at: Any, expires_at: Any,
contract_payload: Mapping[str, Any], contract_digest: str,
provenance: CodeProvenance,
issuer_dependency: Mapping[str, Any] | None,
) -> dict[str, Any]:
return {
"schema": SCHEMA,
"executor_id": executor_id,
"owner_signer_id": contract_payload["owner_signer_id"],
"owner_key_id": contract_payload["owner_key_id"],
"host": HOST,
"repository": REPOSITORY,
"environment": provenance.environment,
"instance_id": provenance.instance_id,
"source_commit": R14_SOURCE_COMMIT,
"support_commit": contract_payload["support_commit"],
"integration_commit": provenance.integration_commit,
"integration_tree": provenance.integration_tree,
"installer_module_sha256": provenance.installer_module_sha256,
"installer_script_sha256": provenance.installer_script_sha256,
"executor_module_sha256": provenance.executor_module_sha256,
"authority_manifest_sha256": provenance.authority_manifest_sha256,
"execution_digest": EXECUTION_DIGEST,
"bridge_port": BRIDGE_PORT,
"issuer_port": ISSUER_PORT,
"capabilities": sorted(CAPABILITIES),
"effects": sorted(contract_payload["effects"]),
"issuer_dependency": dict(issuer_dependency) if issuer_dependency is not None else None,
"issued_at": issued_at,
"expires_at": expires_at,
"nonce": nonce,
**_contract_binding(contract_payload, contract_digest),
}
def _validate_issuer_dependency(
dependency: Any, contract_payload: Mapping[str, Any],
) -> None:
if contract_payload["target"] == ISSUER_TARGET:
if dependency is not None:
_fail("EXECUTOR_ISSUER_DEPENDENCY_INVALID")
return
if (
not isinstance(dependency, Mapping)
or set(dependency) != {
"target", "contract_digest", "source_commit", "support_commit",
"owner_signer_id", "owner_key_id", "contract_nonce",
"contract_issued_at", "contract_expires_at", "rollback_sha256",
}
or dependency["target"] != ISSUER_TARGET
or dependency["source_commit"] != contract_payload["source_commit"]
or dependency["support_commit"] != contract_payload["support_commit"]
or dependency["owner_signer_id"] != contract_payload["owner_signer_id"]
or dependency["owner_key_id"] != contract_payload["owner_key_id"]
or type(dependency["contract_issued_at"]) is not int
or type(dependency["contract_expires_at"]) is not int
):
_fail("EXECUTOR_ISSUER_DEPENDENCY_INVALID")
_require_digest(
dependency["contract_digest"], HEX64,
"EXECUTOR_ISSUER_DEPENDENCY_INVALID",
)
_require_digest(
dependency["rollback_sha256"], HEX64,
"EXECUTOR_ISSUER_DEPENDENCY_INVALID",
)
def _validate_payload(
payload: Mapping[str, Any], *, now: int, provenance: CodeProvenance,
contract_payload: Mapping[str, Any], contract_digest: str,
issuer_dependency: Mapping[str, Any] | None,
) -> None:
expected = _registration_payload(
executor_id=payload.get("executor_id"), nonce=payload.get("nonce"),
issued_at=payload.get("issued_at"), expires_at=payload.get("expires_at"),
contract_payload=contract_payload, contract_digest=contract_digest,
provenance=provenance, issuer_dependency=issuer_dependency,
)
if set(payload) != set(expected) or any(
payload.get(key) != value for key, value in expected.items()
):
_fail("EXECUTOR_REGISTRATION_BINDING_INVALID")
if (
not isinstance(payload["executor_id"], str)
or not payload["executor_id"]
or not isinstance(payload["nonce"], str)
or NONCE.fullmatch(payload["nonce"]) is None
):
_fail("EXECUTOR_REGISTRATION_IDENTITY_INVALID")
if (
type(payload["issued_at"]) is not int
or type(payload["expires_at"]) is not int
or payload["issued_at"] > now
or now >= payload["expires_at"]
or not 1 <= payload["expires_at"] - payload["issued_at"] <= MAX_TTL_SECONDS
or payload["issued_at"] < contract_payload["issued_at"]
or payload["expires_at"] > contract_payload["expires_at"]
):
_fail("EXECUTOR_REGISTRATION_EXPIRED")
if payload["support_commit"] != payload["integration_commit"]:
_fail("EXECUTOR_COMMIT_BINDING_INVALID")
if (
issuer_dependency is not None
and payload["expires_at"] > issuer_dependency["contract_expires_at"]
):
_fail("EXECUTOR_REGISTRATION_EXPIRED")
if (
not isinstance(payload["environment"], str)
or SCOPE_ID.fullmatch(payload["environment"]) is None
or not isinstance(payload["instance_id"], str)
or SCOPE_ID.fullmatch(payload["instance_id"]) is None
):
_fail("EXECUTOR_ENVIRONMENT_BINDING_INVALID")
if payload["source_commit"] != R14_SOURCE_COMMIT:
_fail("EXECUTOR_SOURCE_COMMIT_INVALID")
if (
contract_payload["schema"] != SUCCESSOR_SCHEMA
or contract_payload["source_commit"] != R14_SOURCE_COMMIT
):
_fail("EXECUTOR_CONTRACT_GENERATION_INVALID")
effects = TARGET_EFFECTS.get(payload["target"])
if (
effects is None
or not isinstance(payload["effects"], list)
or frozenset(payload["effects"]) != effects
or len(payload["effects"]) != len(effects)
):
_fail("EXECUTOR_EFFECTS_INVALID")
if (
not isinstance(payload["capabilities"], list)
or frozenset(payload["capabilities"]) != CAPABILITIES
or len(payload["capabilities"]) != len(CAPABILITIES)
):
_fail("EXECUTOR_CAPABILITIES_INVALID")
_validate_issuer_dependency(issuer_dependency, contract_payload)
for name in (
"installer_module_sha256", "installer_script_sha256",
"executor_module_sha256", "authority_manifest_sha256",
"execution_digest", "contract_digest", "old_snapshot_sha256",
"new_manifest_sha256", "rollback_sha256",
):
_require_digest(payload[name], HEX64, f"EXECUTOR_{name.upper()}_INVALID")
def _verified_contract(
contract: Path, installer: Installer, *, now: int,
) -> tuple[dict[str, Any], str]:
return verify_signed_contract(
contract, installer.registry_path, now=now,
require_root_owner=installer.require_root_owner,
_layout=installer.layout,
)
def _verified_issuer_dependency(
issuer_contract: Path | None, installer: Installer, *, now: int,
contract_payload: Mapping[str, Any],
) -> dict[str, Any] | None:
if contract_payload["target"] == ISSUER_TARGET:
if issuer_contract is not None:
_fail("EXECUTOR_ISSUER_DEPENDENCY_INVALID")
return None
if issuer_contract is None:
_fail("EXECUTOR_ISSUER_DEPENDENCY_REQUIRED")
payload, digest = _verified_contract(issuer_contract, installer, now=now)
if payload["schema"] != SUCCESSOR_SCHEMA or payload["source_commit"] != R14_SOURCE_COMMIT:
_fail("EXECUTOR_ISSUER_DEPENDENCY_INVALID")
dependency = _issuer_dependency(payload, digest)
_validate_issuer_dependency(dependency, contract_payload)
return dependency
def issue_registration(
*, executor_id: str, nonce: str, ttl_seconds: int, contract: Path,
installer: Installer, provenance_manifest: Path,
registered_signer: RegisteredOwnerSignerAdapter,
clock: Callable[[], int], issuer_contract: Path | None = None,
require_root_owner: bool = True,
) -> dict[str, Any]:
"""Issue authorization after deriving every scope field from trusted input."""
if type(installer) is not Installer:
_fail("EXECUTOR_INSTALLER_TYPE_INVALID")
if not isinstance(registered_signer, RegisteredOwnerSignerAdapter):
_fail("REGISTERED_SIGNER_ADAPTER_REQUIRED")
if type(ttl_seconds) is not int or not 1 <= ttl_seconds <= MAX_TTL_SECONDS:
_fail("EXECUTOR_REGISTRATION_TTL_INVALID")
now = int(clock())
provenance = load_code_provenance(
provenance_manifest, require_root_owner=require_root_owner,
)
contract_payload, contract_digest = _verified_contract(contract, installer, now=now)
dependency = _verified_issuer_dependency(
issuer_contract, installer, now=now, contract_payload=contract_payload,
)
payload = _registration_payload(
executor_id=executor_id, nonce=nonce, issued_at=now,
expires_at=now + ttl_seconds, contract_payload=contract_payload,
contract_digest=contract_digest, provenance=provenance,
issuer_dependency=dependency,
)
_validate_payload(
payload, now=now, provenance=provenance,
contract_payload=contract_payload, contract_digest=contract_digest,
issuer_dependency=dependency,
)
if registered_signer.fingerprint != payload["owner_key_id"]:
_fail("EXECUTOR_SIGNER_BINDING_INVALID")
signature = registered_signer._sign_canonical_payload(
OWNER_NAMESPACE, canonical_bytes(payload),
)
return {"payload": payload, "signature": signature}
def load_registration(path: Path, *, require_root_owner: bool = True) -> dict[str, Any]:
raw = _secure_bytes(
Path(path), require_root_owner=require_root_owner, maximum=1024 * 1024,
)
try:
value = json.loads(raw, object_pairs_hook=_strict_object)
except (UnicodeError, json.JSONDecodeError) as exc:
raise ExecutorRegistrationError("EXECUTOR_REGISTRATION_INVALID") from exc
if not isinstance(value, dict) or set(value) != {"payload", "signature"}:
_fail("EXECUTOR_REGISTRATION_FIELDS_INVALID")
return value
class RegisteredInstallerExecutor:
def __init__(
self, *, owner_verifier: RegistryOwnerVerifier, installer: Installer,
provenance_manifest: Path, clock: Callable[[], int],
host: Callable[[], str] = socket.gethostname,
require_root_owner: bool = True,
) -> None:
if type(installer) is not Installer:
_fail("EXECUTOR_INSTALLER_TYPE_INVALID")
if not isinstance(owner_verifier, RegistryOwnerVerifier):
_fail("EXECUTOR_OWNER_VERIFIER_INVALID")
self.owner_verifier = owner_verifier
self.installer = installer
self.provenance_manifest = Path(provenance_manifest)
self.clock = clock
self.host = host
self.require_root_owner = require_root_owner
self.provenance = load_code_provenance(
self.provenance_manifest, require_root_owner=require_root_owner,
)
def _check_code_provenance(self) -> None:
current = load_code_provenance(
self.provenance_manifest, require_root_owner=self.require_root_owner,
)
if current != self.provenance:
_fail("EXECUTOR_CODE_PROVENANCE_CHANGED")
def _verify(
self, registration: Mapping[str, Any], contract: Path,
) -> tuple[dict[str, Any], str, dict[str, Any]]:
self._check_code_provenance()
if (
not isinstance(registration, Mapping)
or set(registration) != {"payload", "signature"}
or not isinstance(registration["payload"], Mapping)
):
_fail("EXECUTOR_REGISTRATION_FIELDS_INVALID")
if self.host() != HOST:
_fail("EXECUTOR_HOST_MISMATCH")
now = int(self.clock())
contract_payload, contract_digest = _verified_contract(
contract, self.installer, now=now,
)
payload = dict(registration["payload"])
dependency = payload.get("issuer_dependency")
_validate_payload(
payload, now=now, provenance=self.provenance,
contract_payload=contract_payload, contract_digest=contract_digest,
issuer_dependency=dependency,
)
if payload["owner_key_id"] != self.owner_verifier.record.fingerprint:
_fail("EXECUTOR_OWNER_KEY_BINDING_INVALID")
signature = registration["signature"]
if (
not isinstance(signature, str)
or not self.owner_verifier.verify(
namespace=OWNER_NAMESPACE, payload=canonical_bytes(payload),
signature=signature, key_id=payload["owner_key_id"],
)
):
_fail("EXECUTOR_OWNER_SIGNATURE_INVALID")
return payload, contract_digest, contract_payload
def dispatch(
self, registration: Mapping[str, Any], contract: Path,
artifact: Path, operation: str, *,
issuer_contract: Path | None = None,
issuer_artifact: Path | None = None,
) -> dict[str, Any]:
payload, contract_digest, contract_payload = self._verify(registration, contract)
capability = "install" if operation in {"preflight", "install"} else operation
if capability not in CAPABILITIES:
_fail("EXECUTOR_OPERATION_INVALID")
if capability not in payload["capabilities"]:
_fail("EXECUTOR_CAPABILITY_INVALID")
if operation == "rollback" and contract_payload["target"] == BRIDGE_TARGET:
if issuer_artifact is None:
_fail("EXECUTOR_ISSUER_DEPENDENCY_REQUIRED")
actual_dependency = _verified_issuer_dependency(
issuer_contract, self.installer, now=int(self.clock()),
contract_payload=contract_payload,
)
if actual_dependency != payload["issuer_dependency"]:
_fail("EXECUTOR_ISSUER_DEPENDENCY_MISMATCH")
elif issuer_contract is not None or issuer_artifact is not None:
_fail("EXECUTOR_ISSUER_DEPENDENCY_UNEXPECTED")
if operation == "preflight":
result = self.installer.preflight(contract, artifact)
elif operation == "install":
result = self.installer.apply(contract, artifact)
elif operation == "rollback":
result = self.installer.rollback_applied(
contract, artifact, issuer_contract=issuer_contract,
issuer_artifact=issuer_artifact,
)
else:
result = self.installer.reconcile_unknown(contract, artifact)
return {
**result,
"operation": operation,
"executor_id": payload["executor_id"],
"contract_digest": contract_digest,
"code_binding": {
"integration_commit": payload["integration_commit"],
"integration_tree": payload["integration_tree"],
"installer_module_sha256": payload["installer_module_sha256"],
"installer_script_sha256": payload["installer_script_sha256"],
"executor_module_sha256": payload["executor_module_sha256"],
},
"registration_policy_digest": _sha256_bytes(canonical_bytes(payload)),
}
def _parser() -> argparse.ArgumentParser:
parser = argparse.ArgumentParser(
description="Run one Owner-registered fixed-scope C5 installer operation.",
)
parser.add_argument(
"operation", choices=("preflight", "install", "rollback", "reconcile"),
)
parser.add_argument("--registration", type=Path, required=True)
parser.add_argument("--contract", type=Path, required=True)
parser.add_argument("--artifact", type=Path, required=True)
parser.add_argument("--issuer-contract", type=Path)
parser.add_argument("--issuer-artifact", type=Path)
return parser
def main(argv: list[str] | None = None) -> int:
"""Production CLI with fixed registry and provenance authority paths."""
args = _parser().parse_args(argv)
try:
registration = load_registration(args.registration)
payload = registration.get("payload")
if not isinstance(payload, Mapping) or not isinstance(payload.get("owner_signer_id"), str):
_fail("EXECUTOR_REGISTRATION_FIELDS_INVALID")
installer = Installer(DEFAULT_REGISTRY)
verifier = RegistryOwnerVerifier(
DEFAULT_REGISTRY, owner_signer_id=payload["owner_signer_id"],
)
executor = RegisteredInstallerExecutor(
owner_verifier=verifier, installer=installer,
provenance_manifest=DEFAULT_PROVENANCE_MANIFEST,
clock=lambda: int(time.time()),
)
result = executor.dispatch(
registration, args.contract, args.artifact, args.operation,
issuer_contract=args.issuer_contract,
issuer_artifact=args.issuer_artifact,
)
print(json.dumps(result, sort_keys=True))
return 0
except InstallationOutcomeUnknown as exc:
print(
json.dumps({"status": "UNKNOWN", "error": exc.code}, sort_keys=True),
file=sys.stderr,
)
return 3
except (ExecutorRegistrationError, InstallationError, OSError, ValueError, TypeError) as exc:
print(
json.dumps(
{"status": "REJECTED", "error": getattr(exc, "code", str(exc))},
sort_keys=True,
),
file=sys.stderr,
)
return 2
if __name__ == "__main__":
raise SystemExit(main())
@@ -0,0 +1,977 @@
#!/usr/bin/env python3
"""Root-owned one-shot assembly for the exact V2R2C5 release Saga.
This module only assembles existing hardened Saga phases. Each mutating phase
is a SHA-pinned process adapter; no caller-provided Python callable is accepted.
The staging HTTP service remains shadow-only and does not import this launcher.
"""
from __future__ import annotations
import argparse
from dataclasses import dataclass
import hashlib
import json
import os
from pathlib import Path
import stat
import subprocess
import time
from typing import Any, Mapping, Sequence
import approved_release_executor as approved_executor_module
from approved_release_executor import run_adapter
import c5_exact_deployment_adapter as exact_deployment_module
from c5_exact_deployment_adapter import (
C4_ROLLBACK_ARTIFACT_PATH,
C4_ROLLBACK_TARGET,
C5ExactDeploymentAdapter,
C5_PRODUCTION_ENVIRONMENT_FILE,
C5_PRODUCTION_PORT,
C5_PRODUCTION_SERVICE,
C5_VERSION,
RegisteredC5DeploymentExecutor,
)
from release_source_admission import open_source_admission_store
from silent_release_controller import ReleaseCandidate, ReleaseCandidateStore
from silent_release_hardening import (
ApprovalKeyRing,
ApprovalRevocationStore,
BaselineController,
CapabilityAuthorizationError,
CapabilityLedger,
ClaimProbeResult,
GovernedFinalizationCoordinator,
HardenedTagController,
PreSwitchValidator,
RegistryController,
ReleaseOrchestrator,
RecoverableReleaseError,
ReleasePlan,
ReleasePlanStore,
ReleaseSagaStore,
SignedApprovalStore,
SignedApprovalVerifier,
TagPlan,
TagPublicationConflict,
TagPublicationProbeEvidence,
TrustedReleasePolicy,
)
SCHEMA = "aios.c5.release-execution-host.v1"
PROVENANCE_SCHEMA = "aios.c5.release-execution-provenance.v1"
REQUEST_SCHEMA = "aios.production-executor-request.v1"
REPOSITORY = "aios/aios"
SCOPES = frozenset({"PRODUCTION", "ISOLATED_REHEARSAL"})
SHA256 = __import__("re").compile(r"^[0-9a-f]{64}$")
OID = __import__("re").compile(r"^[0-9a-f]{40}$")
class C5ExecutionHostBlocked(RuntimeError):
"""Exact host assembly or phase verification failed closed."""
def _sha256(path: Path) -> str:
digest = hashlib.sha256()
with path.open("rb") as source:
for chunk in iter(lambda: source.read(1024 * 1024), b""):
digest.update(chunk)
return digest.hexdigest()
def _secure_file(
path: Path,
expected_sha256: str,
*,
expected_owner_uid: int | None,
) -> Path:
path = Path(path)
if (
not path.is_absolute()
or path.is_symlink()
or path.resolve(strict=False) != path
):
raise C5ExecutionHostBlocked("HOST_FILE_PATH_INVALID")
try:
info = path.stat()
except OSError as exc:
raise C5ExecutionHostBlocked("HOST_FILE_UNAVAILABLE") from exc
if not stat.S_ISREG(info.st_mode) or (
os.name != "nt" and stat.S_IMODE(info.st_mode) & 0o022
):
raise C5ExecutionHostBlocked("HOST_FILE_SECURITY_INVALID")
if expected_owner_uid is not None and info.st_uid != expected_owner_uid:
raise C5ExecutionHostBlocked("HOST_FILE_OWNER_INVALID")
if expected_owner_uid is not None:
parent = path.parent
while True:
parent_info = parent.stat()
if (
parent.is_symlink()
or parent_info.st_uid != expected_owner_uid
or stat.S_IMODE(parent_info.st_mode) & 0o022
):
raise C5ExecutionHostBlocked("HOST_FILE_PARENT_SECURITY_INVALID")
if parent == parent.parent:
break
parent = parent.parent
if not SHA256.fullmatch(expected_sha256) or _sha256(path) != expected_sha256:
raise C5ExecutionHostBlocked("HOST_FILE_DIGEST_MISMATCH")
return path
def _absolute(value: Any, name: str) -> Path:
path = Path(str(value or ""))
if not path.is_absolute():
raise C5ExecutionHostBlocked(f"{name}_PATH_INVALID")
return path
@dataclass(frozen=True, slots=True)
class RegisteredPhaseCommand:
executable: Path
executable_sha256: str
adapter_path: Path
adapter_sha256: str
arguments: tuple[str, ...] = ()
@classmethod
def from_mapping(cls, raw: Any) -> "RegisteredPhaseCommand":
if not isinstance(raw, Mapping) or set(raw) != {
"executable",
"executable_sha256",
"adapter_path",
"adapter_sha256",
"arguments",
}:
raise C5ExecutionHostBlocked("PHASE_COMMAND_SCHEMA_INVALID")
arguments = raw["arguments"]
if not isinstance(arguments, list) or not all(
isinstance(item, str) and item and "\0" not in item
for item in arguments
):
raise C5ExecutionHostBlocked("PHASE_COMMAND_ARGUMENTS_INVALID")
return cls(
executable=_absolute(raw["executable"], "PHASE_EXECUTABLE"),
executable_sha256=str(raw["executable_sha256"]),
adapter_path=_absolute(raw["adapter_path"], "PHASE_ADAPTER"),
adapter_sha256=str(raw["adapter_sha256"]),
arguments=tuple(arguments),
)
def verify(self, *, expected_owner_uid: int | None) -> None:
_secure_file(
self.executable,
self.executable_sha256,
expected_owner_uid=expected_owner_uid,
)
_secure_file(
self.adapter_path,
self.adapter_sha256,
expected_owner_uid=expected_owner_uid,
)
@property
def command(self) -> tuple[str, ...]:
return (str(self.executable), str(self.adapter_path), *self.arguments)
@dataclass(frozen=True, slots=True)
class C5HostProvenance:
environment: str
instance_id: str
authority_commit: str
authority_tree: str
entries: Mapping[str, tuple[Path, str]]
manifest_sha256: str
@classmethod
def load(
cls,
path: Path,
*,
expected_sha256: str,
expected_owner_uid: int | None = 0,
) -> "C5HostProvenance":
path = _secure_file(
Path(path), expected_sha256,
expected_owner_uid=expected_owner_uid,
)
try:
raw = json.loads(path.read_text(encoding="utf-8"))
except (OSError, ValueError) as exc:
raise C5ExecutionHostBlocked("HOST_PROVENANCE_INVALID") from exc
labels = {
"host", "exact_deployment", "deployment", "approved_executor",
"tag", "registry", "baseline",
}
if (
not isinstance(raw, Mapping)
or set(raw) != {
"schema", "environment", "instance_id",
"authority_commit", "authority_tree", "entries",
}
or raw["schema"] != PROVENANCE_SCHEMA
or raw["environment"] not in SCOPES
or not isinstance(raw["entries"], Mapping)
or set(raw["entries"]) != labels
):
raise C5ExecutionHostBlocked("HOST_PROVENANCE_INVALID")
instance_id = str(raw["instance_id"] or "")
if not instance_id or len(instance_id) > 128:
raise C5ExecutionHostBlocked("HOST_PROVENANCE_INSTANCE_INVALID")
authority_commit = str(raw["authority_commit"])
authority_tree = str(raw["authority_tree"])
if not OID.fullmatch(authority_commit) or not OID.fullmatch(authority_tree):
raise C5ExecutionHostBlocked("HOST_PROVENANCE_AUTHORITY_INVALID")
actual_loaded = {
"host": Path(__file__).resolve(strict=True),
"exact_deployment": Path(exact_deployment_module.__file__).resolve(strict=True),
"approved_executor": Path(approved_executor_module.__file__).resolve(strict=True),
}
entries: dict[str, tuple[Path, str]] = {}
for label, item in raw["entries"].items():
if not isinstance(item, Mapping) or set(item) != {"path", "sha256"}:
raise C5ExecutionHostBlocked("HOST_PROVENANCE_ENTRY_INVALID")
entry_path = _absolute(item["path"], "HOST_PROVENANCE_ENTRY")
digest = str(item["sha256"])
_secure_file(
entry_path, digest, expected_owner_uid=expected_owner_uid
)
if label in actual_loaded and entry_path != actual_loaded[label]:
raise C5ExecutionHostBlocked("HOST_PROVENANCE_LOADED_PATH_MISMATCH")
entries[label] = (entry_path, digest)
return cls(
environment=str(raw["environment"]),
instance_id=instance_id,
authority_commit=authority_commit,
authority_tree=authority_tree,
entries=entries,
manifest_sha256=expected_sha256,
)
@dataclass(frozen=True, slots=True)
class C5ExecutionHostConfig:
host_provenance: C5HostProvenance
executor_authority_commit: str
executor_authority_tree: str
execution_scope: str
instance_id: str
provenance: Mapping[str, Any]
candidate_id: str
source_commit: str
tree_oid: str
artifact_sha256: str
release_plan_sha256: str
approval_fingerprint: str
state_root: Path
trusted_policy_path: Path
trusted_policy_sha256: str
approval_keyring_path: Path
approval_keyring_sha256: str
source_admission_root: Path
source_admission_policy_path: Path
source_admission_policy_sha256: str
source_admission_task_id: str
source_admission_hmac_path: Path
source_admission_hmac_sha256: str
deployment_executable: Path
deployment_executable_sha256: str
deployment_adapter_path: Path
deployment_adapter_sha256: str
artifact_path: Path
rollback_target: Mapping[str, str]
rollback_artifact_path: Path
production_environment_file: Path
authoritative_git_dir: Path
git_executable: Path
git_executable_sha256: str
tag_command: RegisteredPhaseCommand
registry_command: RegisteredPhaseCommand
baseline_command: RegisteredPhaseCommand
registry_record_path: Path
baseline_record_path: Path
@classmethod
def load(
cls,
path: Path,
*,
expected_sha256: str,
host_provenance: C5HostProvenance,
expected_owner_uid: int | None = 0,
) -> "C5ExecutionHostConfig":
path = _secure_file(
Path(path),
expected_sha256,
expected_owner_uid=expected_owner_uid,
)
try:
raw = json.loads(path.read_text(encoding="utf-8"))
except (OSError, ValueError) as exc:
raise C5ExecutionHostBlocked("HOST_CONFIG_INVALID") from exc
keys = {
"schema", "version", "execution_scope", "instance_id",
"executor_authority_commit", "executor_authority_tree",
"provenance", "candidate_id",
"source_commit", "tree_oid", "artifact_sha256",
"release_plan_sha256", "approval_fingerprint", "state_root",
"trusted_policy_path", "trusted_policy_sha256",
"approval_keyring_path", "approval_keyring_sha256",
"source_admission_root", "source_admission_policy_path",
"source_admission_policy_sha256", "source_admission_task_id", "source_admission_hmac_path",
"source_admission_hmac_sha256", "deployment_executable",
"deployment_executable_sha256", "deployment_adapter_path",
"deployment_adapter_sha256", "artifact_path",
"rollback_target", "rollback_artifact_path",
"production_environment_file", "production_service",
"production_port", "repository_identity", "global_legacy_writes_enabled",
"authoritative_git_dir", "git_executable", "git_executable_sha256",
"tag_command", "registry_command", "baseline_command",
"registry_record_path", "baseline_record_path",
}
if not isinstance(raw, Mapping) or set(raw) != keys:
raise C5ExecutionHostBlocked("HOST_CONFIG_SCHEMA_INVALID")
rollback_artifact_path = _absolute(raw["rollback_artifact_path"], "ROLLBACK_ARTIFACT")
production_environment_file = _absolute(raw["production_environment_file"], "PRODUCTION_ENVIRONMENT_FILE")
exact = (
raw["schema"] == SCHEMA
and raw["version"] == C5_VERSION
and raw["execution_scope"] in SCOPES
and isinstance(raw["rollback_target"], Mapping)
and raw["production_service"] == C5_PRODUCTION_SERVICE
and raw["production_port"] == C5_PRODUCTION_PORT
and raw["repository_identity"] == REPOSITORY
and raw["global_legacy_writes_enabled"] is False
)
if not exact:
raise C5ExecutionHostBlocked("HOST_CONFIG_BINDING_INVALID")
provenance = raw["provenance"]
expected_provenance = (
{
"environment": "ISOLATED_REHEARSAL",
"fixture": True,
"r14_source_receipt": False,
}
if raw["execution_scope"] == "ISOLATED_REHEARSAL"
else {
"environment": "PRODUCTION",
"fixture": False,
"r14_source_receipt": True,
}
)
if provenance != expected_provenance:
raise C5ExecutionHostBlocked("HOST_PROVENANCE_INVALID")
instance_id = str(raw["instance_id"] or "")
if not instance_id or len(instance_id) > 128 or any(
character not in "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_.-"
for character in instance_id
):
raise C5ExecutionHostBlocked("HOST_INSTANCE_ID_INVALID")
for name, pattern in (
("executor_authority_commit", OID),
("executor_authority_tree", OID),
("source_commit", OID), ("tree_oid", OID),
("artifact_sha256", SHA256),
("release_plan_sha256", SHA256),
("approval_fingerprint", SHA256),
):
if pattern.fullmatch(str(raw[name])) is None:
raise C5ExecutionHostBlocked(f"{name.upper()}_INVALID")
value = cls(
host_provenance=host_provenance,
executor_authority_commit=str(raw["executor_authority_commit"]),
executor_authority_tree=str(raw["executor_authority_tree"]),
execution_scope=str(raw["execution_scope"]),
instance_id=instance_id,
provenance=dict(provenance),
candidate_id=str(raw["candidate_id"]),
source_commit=str(raw["source_commit"]),
tree_oid=str(raw["tree_oid"]),
artifact_sha256=str(raw["artifact_sha256"]),
release_plan_sha256=str(raw["release_plan_sha256"]),
approval_fingerprint=str(raw["approval_fingerprint"]),
state_root=_absolute(raw["state_root"], "STATE_ROOT"),
trusted_policy_path=_absolute(raw["trusted_policy_path"], "TRUSTED_POLICY"),
trusted_policy_sha256=str(raw["trusted_policy_sha256"]),
approval_keyring_path=_absolute(raw["approval_keyring_path"], "APPROVAL_KEYRING"),
approval_keyring_sha256=str(raw["approval_keyring_sha256"]),
source_admission_root=_absolute(raw["source_admission_root"], "SOURCE_ADMISSION_ROOT"),
source_admission_policy_path=_absolute(raw["source_admission_policy_path"], "SOURCE_ADMISSION_POLICY"),
source_admission_policy_sha256=str(raw["source_admission_policy_sha256"]),
source_admission_task_id=str(raw["source_admission_task_id"]),
source_admission_hmac_path=_absolute(raw["source_admission_hmac_path"], "SOURCE_ADMISSION_HMAC"),
source_admission_hmac_sha256=str(raw["source_admission_hmac_sha256"]),
deployment_executable=_absolute(raw["deployment_executable"], "DEPLOYMENT_EXECUTABLE"),
deployment_executable_sha256=str(raw["deployment_executable_sha256"]),
deployment_adapter_path=_absolute(raw["deployment_adapter_path"], "DEPLOYMENT_ADAPTER"),
deployment_adapter_sha256=str(raw["deployment_adapter_sha256"]),
artifact_path=_absolute(raw["artifact_path"], "ARTIFACT"),
rollback_target=dict(raw["rollback_target"]),
rollback_artifact_path=rollback_artifact_path,
production_environment_file=production_environment_file,
authoritative_git_dir=_absolute(raw["authoritative_git_dir"], "AUTHORITATIVE_GIT_DIR"),
git_executable=_absolute(raw["git_executable"], "GIT_EXECUTABLE"),
git_executable_sha256=str(raw["git_executable_sha256"]),
tag_command=RegisteredPhaseCommand.from_mapping(raw["tag_command"]),
registry_command=RegisteredPhaseCommand.from_mapping(raw["registry_command"]),
baseline_command=RegisteredPhaseCommand.from_mapping(raw["baseline_command"]),
registry_record_path=_absolute(raw["registry_record_path"], "REGISTRY_RECORD"),
baseline_record_path=_absolute(raw["baseline_record_path"], "BASELINE_RECORD"),
)
if (not value.source_admission_task_id
or len(value.source_admission_task_id) > 160
or any(character not in "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_.:-"
for character in value.source_admission_task_id)):
raise C5ExecutionHostBlocked("SOURCE_ADMISSION_TASK_ID_INVALID")
if value.execution_scope == "ISOLATED_REHEARSAL":
rehearsal_root = value.state_root.resolve(strict=False)
lowered_parts = {part.lower() for part in rehearsal_root.parts}
if (
"isolated-rehearsal" not in lowered_parts
or value.instance_id not in rehearsal_root.parts
):
raise C5ExecutionHostBlocked("REHEARSAL_ROOT_NOT_ISOLATED")
for fixture_path in (
value.source_admission_root,
value.artifact_path,
value.rollback_artifact_path,
value.production_environment_file,
value.registry_record_path,
value.baseline_record_path,
):
try:
fixture_path.resolve(strict=False).relative_to(rehearsal_root)
except ValueError as exc:
raise C5ExecutionHostBlocked(
"REHEARSAL_FIXTURE_OUTSIDE_ISOLATION_ROOT"
) from exc
if value.execution_scope == "ISOLATED_REHEARSAL":
if set(value.rollback_target) != set(C4_ROLLBACK_TARGET) or value.rollback_target.get("version") != C4_ROLLBACK_TARGET["version"] or value.rollback_target.get("target_oid") != C4_ROLLBACK_TARGET["target_oid"]:
raise C5ExecutionHostBlocked("REHEARSAL_ROLLBACK_TARGET_INVALID")
if SHA256.fullmatch(str(value.rollback_target.get("artifact_sha256", ""))) is None:
raise C5ExecutionHostBlocked("REHEARSAL_ROLLBACK_TARGET_INVALID")
if (
host_provenance.environment != value.execution_scope
or host_provenance.instance_id != value.instance_id
or host_provenance.authority_commit
!= value.executor_authority_commit
or host_provenance.authority_tree != value.executor_authority_tree
):
raise C5ExecutionHostBlocked("HOST_PROVENANCE_BINDING_MISMATCH")
if value.execution_scope == "PRODUCTION" and (
value.rollback_target != C4_ROLLBACK_TARGET
or value.rollback_artifact_path != C4_ROLLBACK_ARTIFACT_PATH
or value.production_environment_file != C5_PRODUCTION_ENVIRONMENT_FILE
):
raise C5ExecutionHostBlocked("HOST_CONFIG_BINDING_INVALID")
expected_adapter_paths = {
"deployment": value.deployment_adapter_path,
"tag": value.tag_command.adapter_path,
"registry": value.registry_command.adapter_path,
"baseline": value.baseline_command.adapter_path,
}
if any(
host_provenance.entries[label][0] != path
for label, path in expected_adapter_paths.items()
):
raise C5ExecutionHostBlocked("HOST_PROVENANCE_ADAPTER_MISMATCH")
production_names = {
"deployment": "c5_production_deployment_adapter.py",
"tag": "governed_tag_adapter.py",
"registry": "release_metadata_adapter.py",
"baseline": "release_metadata_adapter.py",
}
observed_names = {
label: path.name for label, path in expected_adapter_paths.items()
}
if value.execution_scope == "PRODUCTION":
if observed_names != production_names:
raise C5ExecutionHostBlocked("PRODUCTION_ADAPTER_IDENTITY_INVALID")
elif any(
observed_names[label] == production_names[label]
for label in production_names
):
raise C5ExecutionHostBlocked("REHEARSAL_PRODUCTION_ADAPTER_FORBIDDEN")
value.verify_files(expected_owner_uid=expected_owner_uid)
return value
def verify_files(self, *, expected_owner_uid: int | None) -> None:
for path, digest in (
(self.trusted_policy_path, self.trusted_policy_sha256),
(self.approval_keyring_path, self.approval_keyring_sha256),
(self.source_admission_policy_path, self.source_admission_policy_sha256),
(self.source_admission_hmac_path, self.source_admission_hmac_sha256),
(self.git_executable, self.git_executable_sha256),
):
_secure_file(path, digest, expected_owner_uid=expected_owner_uid)
for command in (
self.tag_command, self.registry_command, self.baseline_command
):
command.verify(expected_owner_uid=expected_owner_uid)
def _load_policy(path: Path) -> TrustedReleasePolicy:
raw = json.loads(path.read_text(encoding="utf-8"))
required = {
"services", "release_roots", "cutover_links", "dropin_paths",
"authoritative_git_dirs", "repository_identities",
}
if not isinstance(raw, Mapping) or set(raw) != required:
raise C5ExecutionHostBlocked("TRUSTED_POLICY_SCHEMA_INVALID")
values: dict[str, frozenset[str]] = {}
for name in required:
items = raw[name]
if not isinstance(items, list) or not items or not all(
isinstance(item, str) and item for item in items
):
raise C5ExecutionHostBlocked("TRUSTED_POLICY_VALUES_INVALID")
values[name] = frozenset(items)
return TrustedReleasePolicy(**values)
def _phase_request(candidate: ReleaseCandidate, plan: ReleasePlan, approval, operation: str) -> dict[str, Any]:
return {
"schema": REQUEST_SCHEMA,
"operation": operation,
"version": C5_VERSION,
"target_oid": candidate.source_commit,
"evidence_closure_oid": approval.evidence_closure_oid,
"artifact_sha256": candidate.artifact_sha256,
"approval_plan_id": approval.approval_plan_id,
"repository_identity": plan.deployment.repository_identity,
"production_service": plan.deployment.service,
"production_port": C5_PRODUCTION_PORT,
}
def _checked_phase_response(
value: Mapping[str, Any], request: Mapping[str, Any]
) -> dict[str, Any]:
for name in (
"version", "target_oid", "evidence_closure_oid",
"artifact_sha256", "approval_plan_id",
):
if value.get(name) != request[name]:
raise C5ExecutionHostBlocked(f"PHASE_RECEIPT_MISMATCH:{name}")
return dict(value)
class _PreSwitchProbe:
def __init__(self, config: C5ExecutionHostConfig, source_admissions):
self.config = config
self.source_admissions = source_admissions
def _git(self, *args: str) -> str:
completed = subprocess.run(
[str(self.config.git_executable), f"--git-dir={self.config.authoritative_git_dir}", *args],
check=False,
capture_output=True,
text=True,
timeout=45,
env={"PATH": os.environ.get("PATH", "")},
)
if completed.returncode != 0:
raise C5ExecutionHostBlocked("PRE_SWITCH_GIT_UNAVAILABLE")
return completed.stdout.strip()
def collect(self, candidate: ReleaseCandidate, plan) -> Mapping[str, Any]:
admitted = self.source_admissions.resolve_recipe(
C5_VERSION, candidate.candidate_id, candidate.source_commit
)
tree = self._git("rev-parse", f"{candidate.source_commit}^{{tree}}")
artifact_ok = (
self.config.artifact_path.is_file()
and _sha256(self.config.artifact_path) == candidate.artifact_sha256
)
rollback_path = (
C4_ROLLBACK_ARTIFACT_PATH
if self.config.execution_scope == "PRODUCTION"
else self.config.rollback_artifact_path
)
rollback_target = (
C4_ROLLBACK_TARGET
if self.config.execution_scope == "PRODUCTION"
else self.config.rollback_target
)
rollback_ok = (
rollback_path.is_file()
and _sha256(rollback_path) == rollback_target["artifact_sha256"]
)
return {
"source_commit": candidate.source_commit,
"tree_oid": tree,
"artifact_sha256": candidate.artifact_sha256 if artifact_ok else "",
"manifest_sha256": candidate.manifest_sha256,
"deployment_plan_sha256": plan.sha256,
"policy_sha256": plan.policy_sha256,
"rollback_ready": rollback_ok,
"git_refs_consistent": (
admitted.get("source_commit") == candidate.source_commit
and admitted.get("tree_oid") == tree
),
"repository_identity": plan.repository_identity,
"writer_disabled": os.environ.get(
"AIOS_GITOPS_WRITES_ENABLED", "false"
).lower() == "false",
}
class _TagPhase:
def __init__(self, command: RegisteredPhaseCommand, candidate, plan, approval, owner_uid):
self.command_config = command
self.candidate = candidate
self.plan = plan
self.approval = approval
self.owner_uid = owner_uid
def _call(self, operation: str) -> dict[str, Any]:
self.command_config.verify(expected_owner_uid=self.owner_uid)
request = _phase_request(self.candidate, self.plan, self.approval, operation)
return _checked_phase_response(
run_adapter(self.command_config.command, request), request
)
def probe_evidence(self, tag_plan: TagPlan) -> TagPublicationProbeEvidence:
value = self._call("probe")
status = value.get("status")
listing = value.get("gitea_listing_visible")
if status == "NOT_APPLIED":
listing = False
return TagPublicationProbeEvidence(
repository_identity=tag_plan.repository_identity,
remote_reachable=True,
gitea_api_reachable=True,
gitea_listing_reachable=True,
remote_tag_oid=value.get("remote_tag_oid"),
annotated_tag_object_oid=value.get("tag_oid"),
remote_peeled_target_oid=value.get("remote_peeled_target_oid"),
gitea_api_tag_oid=value.get("gitea_tag_oid"),
gitea_api_peeled_target_oid=value.get("gitea_peeled_target_oid"),
gitea_listing_visible=listing,
gitea_listing_tag_oid=(value.get("gitea_tag_oid") if listing else None),
gitea_listing_peeled_target_oid=(value.get("gitea_peeled_target_oid") if listing else None),
observed_at=time.time(),
)
def __call__(self, *, tag_plan: TagPlan) -> Mapping[str, Any]:
value = self._call("probe")
if value.get("status") in {"NOT_APPLIED", "PARTIAL_LOCAL"}:
value = self._call("apply")
value = self._call("probe")
if value.get("status") == "CONFLICT":
raise TagPublicationConflict(
"tag publication conflicts with the exact C5 plan"
)
if value.get("status") != "APPLIED_VERIFIED":
raise RecoverableReleaseError(
"tag publication outcome is indeterminate"
)
proof = self.probe_evidence(tag_plan)
if proof.remote_tag_oid != tag_plan.expected_tag_oid:
raise C5ExecutionHostBlocked("TAG_OBJECT_BINDING_MISMATCH")
return {
"verified": True,
"evidence_closure_oid": self.approval.evidence_closure_oid,
"approval_plan_id": self.approval.approval_plan_id,
"repository_identity": tag_plan.repository_identity,
"remote_tag_oid": proof.remote_tag_oid,
"remote_peeled_target_oid": proof.remote_peeled_target_oid,
"gitea_api_tag_oid": proof.gitea_api_tag_oid,
"gitea_api_peeled_target_oid": proof.gitea_api_peeled_target_oid,
"gitea_listing_visible": proof.gitea_listing_visible,
}
class _MetadataPhase:
def __init__(self, *, mode: str, command: RegisteredPhaseCommand, record_path: Path,
candidate, plan, approval, tag_phase: _TagPhase, owner_uid):
self.mode = mode
self.command_config = command
self.record_path = record_path
self.candidate = candidate
self.plan = plan
self.approval = approval
self.tag_phase = tag_phase
self.owner_uid = owner_uid
def _call(self, operation: str) -> dict[str, Any]:
self.command_config.verify(expected_owner_uid=self.owner_uid)
request = _phase_request(self.candidate, self.plan, self.approval, operation)
request["tag_oid"] = self.plan.tag.expected_tag_oid
return _checked_phase_response(run_adapter(self.command_config.command, request), request)
def __call__(self, *, finalization_plan) -> Mapping[str, Any]:
value = self._call("probe")
if value.get("status") == "NOT_APPLIED":
self._call("apply")
value = self._call("probe")
if value.get("status") != "VERIFIED":
raise C5ExecutionHostBlocked(f"{self.mode.upper()}_FINALIZATION_NOT_VERIFIED")
try:
record = json.loads(self.record_path.read_text(encoding="utf-8"))
except (OSError, ValueError) as exc:
raise C5ExecutionHostBlocked(f"{self.mode.upper()}_RECORD_INVALID") from exc
expected_record = (
finalization_plan.registry_expected_record
if self.mode == "registry"
else finalization_plan.baseline_expected_record
)
if any(record.get(key) != item for key, item in expected_record.items()):
raise C5ExecutionHostBlocked(f"{self.mode.upper()}_RECORD_BINDING_MISMATCH")
tag = self.tag_phase.probe_evidence(self.plan.tag)
if (
tag.remote_tag_oid != self.plan.tag.expected_tag_oid
or tag.remote_peeled_target_oid != self.candidate.source_commit
or tag.gitea_api_peeled_target_oid != self.candidate.source_commit
or tag.gitea_listing_visible is not True
):
raise C5ExecutionHostBlocked("FINALIZATION_TAG_PROOF_INVALID")
proof = {
"verified": True,
"tag_verified": True,
"remote_tag_oid": tag.remote_tag_oid,
"remote_peeled_target_oid": tag.remote_peeled_target_oid,
"gitea_api_peeled_target_oid": tag.gitea_api_peeled_target_oid,
"gitea_listing_visible": True,
"evidence_closure_oid": self.approval.evidence_closure_oid,
"approval_plan_id": self.approval.approval_plan_id,
f"{self.mode}_target_oid": self.candidate.source_commit,
}
if self.mode == "baseline":
proof.update(
{
"registry_record": finalization_plan.registry_expected_record,
"baseline_record": finalization_plan.baseline_expected_record,
"baseline_written_last": True,
}
)
return proof
@dataclass(slots=True)
class C5ExecutionHost:
config: C5ExecutionHostConfig
orchestrator: ReleaseOrchestrator
def execute(self) -> dict[str, Any]:
result = dict(self.orchestrator.execute(self.config.candidate_id))
production_receipt = (
self.config.execution_scope == "PRODUCTION"
and result.get("state") == "COMPLETED"
)
return {
**result,
"execution_scope": self.config.execution_scope,
"instance_id": self.config.instance_id,
"provenance": dict(self.config.provenance),
"host_authority_commit": self.config.host_provenance.authority_commit,
"host_authority_tree": self.config.host_provenance.authority_tree,
"host_provenance_sha256": self.config.host_provenance.manifest_sha256,
"fixture_evidence": self.config.execution_scope == "ISOLATED_REHEARSAL",
"production_receipt": production_receipt,
"public_business_e2e_verified": False,
"production_acceptance": False,
}
def _validate_source_admission_task(config, candidate, admitted) -> None:
if admitted.get("task_id") != config.source_admission_task_id:
raise C5ExecutionHostBlocked("SOURCE_ADMISSION_TASK_BINDING_INVALID")
def build_execution_host(
config: C5ExecutionHostConfig,
*,
expected_owner_uid: int | None = 0,
completion_approvals: Any = None,
) -> C5ExecutionHost:
if os.environ.get("AIOS_GITOPS_WRITES_ENABLED", "false").lower() != "false":
raise C5ExecutionHostBlocked("LEGACY_GLOBAL_WRITES_ENABLED")
config.verify_files(expected_owner_uid=expected_owner_uid)
candidates = ReleaseCandidateStore(config.state_root / "candidates")
plans = ReleasePlanStore(config.state_root / "release-plans")
approvals = SignedApprovalStore(config.state_root / "approvals")
completion_authority = None
if completion_approvals is not None:
from c5_completion_renewal import (
RegisteredCompletionApprovalStore, RegisteredCompletionAuthority,
CompletionRuntimeGuard,
)
if (type(completion_approvals) is not RegisteredCompletionApprovalStore
or type(completion_approvals.authority) is not RegisteredCompletionAuthority
or expected_owner_uid != 0 or config.execution_scope != "PRODUCTION"
or Path(__file__) != completion_approvals.authority.root / "c5_release_execution_host.py"
or Path(completion_approvals.original.root) != approvals.root):
raise C5ExecutionHostBlocked("REGISTERED_COMPLETION_DEPENDENCY_REQUIRED")
completion_authority = completion_approvals.authority
completion_authority.validate_host(
state_root=config.state_root, candidate_id=config.candidate_id,
source_commit=config.source_commit, tree_oid=config.tree_oid,
artifact_sha256=config.artifact_sha256,
release_plan_sha256=config.release_plan_sha256,
)
approvals = completion_approvals
sagas = ReleaseSagaStore(config.state_root / "sagas")
candidate = candidates.load(config.candidate_id)
plan = plans.load(config.candidate_id).validate()
approval = approvals.load(config.candidate_id)
observed = (
candidate.source_commit, candidate.tree_oid, candidate.artifact_sha256,
plan.sha256, approval.fingerprint,
)
expected = (
config.source_commit, config.tree_oid, config.artifact_sha256,
config.release_plan_sha256, config.approval_fingerprint,
)
if observed != expected or plan.tag.version != C5_VERSION:
raise C5ExecutionHostBlocked("HOST_DURABLE_BINDING_MISMATCH")
if (
plan.policy_sha256 != config.trusted_policy_sha256
or Path(plan.deployment.authoritative_git_dir)
!= config.authoritative_git_dir
):
raise C5ExecutionHostBlocked("HOST_ACTUAL_PLAN_BINDING_MISMATCH")
policy = _load_policy(config.trusted_policy_path)
policy.validate_deployment(plan.deployment)
verifier = SignedApprovalVerifier(
ApprovalKeyRing.from_file(config.approval_keyring_path),
ApprovalRevocationStore(config.state_root / "revocations"),
)
verifier.verify_release_plan(approval, candidate, plan, plan.policy_sha256)
hmac_key = config.source_admission_hmac_path.read_bytes().strip()
source_admissions = open_source_admission_store(
config.source_admission_root,
repository=REPOSITORY,
policy=config.source_admission_policy_path,
hmac_key=hmac_key,
expected_owner_uid=expected_owner_uid,
writable=False,
)
admitted = source_admissions.resolve_recipe(
C5_VERSION, candidate.candidate_id, candidate.source_commit
)
_validate_source_admission_task(config, candidate, admitted)
deployment_executor = RegisteredC5DeploymentExecutor(
executor_id="c5-production-runtime-switch-v1",
executable=config.deployment_executable,
executable_sha256=config.deployment_executable_sha256,
adapter_path=config.deployment_adapter_path,
adapter_sha256=config.deployment_adapter_sha256,
artifact_path=config.artifact_path,
rollback_artifact_path=config.rollback_artifact_path,
production_environment_file=config.production_environment_file,
execution_scope=config.execution_scope,
isolation_root=(config.state_root if config.execution_scope == "ISOLATED_REHEARSAL" else None),
rollback_target=config.rollback_target,
require_root_owner=expected_owner_uid == 0,
)
runtime = C5ExactDeploymentAdapter(
candidate=candidate,
release_plan=plan,
approval=approval,
approval_verifier=verifier,
source_admissions=source_admissions,
executor=deployment_executor,
)
ledger = CapabilityLedger(config.state_root / "capabilities")
tag_phase = _TagPhase(
config.tag_command, candidate, plan, approval, expected_owner_uid
)
tag = HardenedTagController(
ledger,
tag_phase,
expected_repository=REPOSITORY,
publication_probe=tag_phase.probe_evidence,
executor_id="c5-production-tag-v1",
)
registry_adapter = _MetadataPhase(
mode="registry", command=config.registry_command,
record_path=config.registry_record_path, candidate=candidate,
plan=plan, approval=approval, tag_phase=tag_phase,
owner_uid=expected_owner_uid,
)
baseline_adapter = _MetadataPhase(
mode="baseline", command=config.baseline_command,
record_path=config.baseline_record_path, candidate=candidate,
plan=plan, approval=approval, tag_phase=tag_phase,
owner_uid=expected_owner_uid,
)
finalizer = GovernedFinalizationCoordinator(
RegistryController(ledger, registry_adapter, executor_id="c5-production-registry-v1"),
BaselineController(ledger, baseline_adapter, executor_id="c5-production-baseline-v1"),
)
def actual_plan_factory(actual_candidate: ReleaseCandidate) -> ReleasePlan:
actual = plans.load(actual_candidate.candidate_id).validate()
runtime._verify_binding(actual_candidate, actual)
return actual
def runtime_verify(*, candidate: ReleaseCandidate, plan: ReleasePlan) -> Mapping[str, Any]:
runtime._verify_binding(candidate, plan)
result = runtime._invoke("probe")
if runtime._classify(result) != ClaimProbeResult.APPLIED_VERIFIED:
raise C5ExecutionHostBlocked("RUNTIME_VERIFY_INDETERMINATE")
return {
"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,
}
orchestrator = ReleaseOrchestrator(
candidates=candidates,
plans=plans,
approvals=approvals,
sagas=sagas,
approval_verifier=verifier,
trusted_policy=policy,
actual_plan_factory=actual_plan_factory,
pre_switch_validator=PreSwitchValidator(
_PreSwitchProbe(config, source_admissions)
),
runtime_switch=(CompletionRuntimeGuard(completion_authority) if completion_authority
else runtime.controller(ledger)),
runtime_verify=runtime_verify,
runtime_rollback=(completion_authority.deny_runtime_effect if completion_authority
else runtime.rollback),
tag_publish=tag,
finalizer=finalizer,
)
return C5ExecutionHost(config=config, orchestrator=orchestrator)
def main(argv: Sequence[str] | None = None) -> int:
parser = argparse.ArgumentParser()
parser.add_argument("--config", type=Path, required=True)
parser.add_argument("--config-sha256", required=True)
parser.add_argument("--provenance", type=Path, required=True)
parser.add_argument("--provenance-sha256", required=True)
args = parser.parse_args(argv)
provenance = C5HostProvenance.load(
args.provenance,
expected_sha256=args.provenance_sha256,
expected_owner_uid=0,
)
config = C5ExecutionHostConfig.load(
args.config,
expected_sha256=args.config_sha256,
host_provenance=provenance,
expected_owner_uid=0,
)
result = build_execution_host(config, expected_owner_uid=0).execute()
print(json.dumps(result, sort_keys=True))
return 0
if __name__ == "__main__":
try:
raise SystemExit(main())
except (C5ExecutionHostBlocked, CapabilityAuthorizationError) as exc:
print(json.dumps({"status": "FAIL_CLOSED", "error": type(exc).__name__}))
raise SystemExit(2)
+163 -27
View File
@@ -35,6 +35,8 @@ from aios_single_host_authority import (
from registered_owner_signer_adapter import RegisteredOwnerSignerAdapter
SCHEMA = "aios.c5-staging-installation-authority.v3"
SUCCESSOR_SCHEMA = "aios.c5-staging-installation-authority.v4"
R14_SOURCE_COMMIT = "396ea9811124736aa140d2810ab53eaefb6b5c1c"
REQUEST_SCHEMA = "aios.c5-staging-installation-request.v2"
HOST = "cn-network-mkt"
REPOSITORY = "aios/aios"
@@ -47,13 +49,15 @@ ISSUER_UNIT = "aios-task-principal-issuer.service"
ISSUER_PORT = 18946
ISSUER_RUNTIME_URL = "http://127.0.0.1:18946/health"
RELEASE_BASE = Path("/var/lib/aios-gitops/bridge-v2-staging/releases")
BOOTSTRAP_RELEASE_NAME = re.compile(r"c5-bootstrap-r[1-9][0-9]*-releases\Z")
BOOTSTRAP_SOURCE_NAME = "c5-source-b915386ccbed46a7ae77220887da8ae666649a79"
DROPIN = (
Path("/etc/systemd/system") / f"{UNIT}.d"
/ "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-c5-source.conf"
/ ("~" * 128 + "-c5-r14-source.conf")
)
ISSUER_DROPIN = (
Path("/etc/systemd/system") / f"{ISSUER_UNIT}.d"
/ "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-c5-source.conf"
/ ("~" * 128 + "-c5-r14-source.conf")
)
# Replay state must not sit below a directory owned by the staging service.
STATE_ROOT = Path("/var/lib/aios-c5-staging-installation")
@@ -99,20 +103,6 @@ _PRINCIPAL_CONFIG_FIELDS = frozenset(
"require_root_plan_owner", "grant_db_allowed_group_gid",
}
)
_PRINCIPAL_FIXED = {
"schema": "aios.c5-task-principal-activation.v1",
"task_id": "TASK-V2R2C5-SOURCE-PUBLICATION-20260926-R1",
"agent_id": "agent/codex-c5-release",
"execution_digest": "1ba534d551991e8c99014285eeea36afcbc1500686035cb8bcd7f9f4efd6215e",
"store_path": "/var/lib/aios-gitops/c5-source-principal/enrollment.sqlite3",
"grant_db": "/var/lib/aios-gitops/bridge-v2-staging/task-admission.sqlite3",
"producer_state_root": "/var/lib/aios-gitops/bridge-v2-staging",
"owner_registry": "/etc/aios/single-host-signer-registry.json",
"owner_signer_id": "local-owner-authority",
"registered_signer_command": "/usr/local/libexec/aios-grant-signer-adapter",
}
class InstallationError(RuntimeError):
def __init__(self, code: str) -> None:
super().__init__(code)
@@ -281,6 +271,21 @@ def _absolute_safe_path(value: Any) -> bool:
return path.is_absolute() and ".." not in path.parts and "." not in path.parts
def _trusted_prior_config_root(path: Path, layout: _Layout) -> bool:
if layout.release_base in path.parents:
return True
release = path.parent
while release != release.parent:
if release.name == BOOTSTRAP_SOURCE_NAME:
bootstrap = release.parent
return (
bool(BOOTSTRAP_RELEASE_NAME.fullmatch(bootstrap.name))
and bootstrap.parent == layout.release_base.parent.parent
)
release = release.parent
return False
@dataclass(frozen=True, slots=True)
class _Layout:
release_base: Path = RELEASE_BASE
@@ -357,12 +362,31 @@ def _public_key(value: str) -> Ed25519PublicKey:
def _validate_principal_config(
raw: bytes, *, root: Path, release_files: set[str],
) -> None:
raw: bytes, *, root: Path, release_files: set[str], successor: bool,
) -> Mapping[str, Any]:
config = _json_bytes(raw)
if set(config) != _PRINCIPAL_CONFIG_FIELDS or canonical_bytes(config) != raw:
_fail("PRINCIPAL_CONFIG_CANONICAL_INVALID")
if any(config.get(key) != value for key, value in _PRINCIPAL_FIXED.items()):
if (
config.get("schema") != "aios.c5-task-principal-activation.v1"
or not isinstance(config.get("task_id"), str)
or not _IDENTIFIER.fullmatch(config["task_id"])
or not isinstance(config.get("agent_id"), str)
or not _IDENTIFIER.fullmatch(config["agent_id"])
or not isinstance(config.get("execution_digest"), str)
or not _HEX64.fullmatch(config["execution_digest"])
or not isinstance(config.get("owner_signer_id"), str)
or not _IDENTIFIER.fullmatch(config["owner_signer_id"])
):
_fail("PRINCIPAL_CONFIG_SCOPE_INVALID")
if successor and any(
config.get(name) != (root / relative).as_posix()
for name, relative in {
"plan_dir": "plans", "policy_artifact": "policy.json",
"policy_revocations": "policy-revocations.json",
"epoch_root": "epoch", "epoch_revocations": "epoch-revocations.json",
}.items()
):
_fail("PRINCIPAL_CONFIG_SCOPE_INVALID")
if config.get("require_root_plan_owner") is not True:
_fail("PRINCIPAL_CONFIG_SCOPE_INVALID")
@@ -391,6 +415,7 @@ def _validate_principal_config(
_fail("PRINCIPAL_CONFIG_REFERENCED_TREE_MISSING")
if root_text in {config[name] for name in path_fields}:
_fail("PRINCIPAL_CONFIG_PATH_INVALID")
return config
@dataclass(frozen=True, slots=True)
@@ -487,6 +512,19 @@ class SystemdBackend:
def hostname(self) -> str:
return socket.gethostname()
def verify_dropin_precedence(self, profile: _TargetProfile, dropin: Path) -> None:
active_paths = self._run(
["systemctl", "show", profile.unit, "--property=DropInPaths", "--value"]
).stdout.strip()
try:
paths = shlex.split(active_paths, posix=True)
except ValueError as exc:
raise InstallationError("SYSTEMD_DROPIN_PATHS_INVALID") from exc
if any(not PurePosixPath(path).is_absolute() or not path.endswith(".conf") for path in paths):
_fail("SYSTEMD_DROPIN_PATHS_INVALID")
if any(PurePosixPath(path).name > dropin.name for path in paths):
_fail("STAGING_DROPIN_PRECEDENCE_CONFLICT")
@staticmethod
def _run(command: list[str], *, check: bool = True) -> subprocess.CompletedProcess[str]:
try:
@@ -677,11 +715,12 @@ def _validate_payload(payload: Mapping[str, Any], now: int, *, layout: _Layout)
}
if not isinstance(payload, Mapping) or set(payload) != required:
_fail("INSTALL_CONTRACT_FIELDS_INVALID")
successor = payload.get("schema") == SUCCESSOR_SCHEMA
profile = _profile(payload.get("target"))
if not isinstance(payload.get("support_commit"), str):
_fail("INSTALL_IDENTITY_INVALID")
if (
payload["schema"] != SCHEMA or payload["owner_namespace"] != OWNER_NAMESPACE
payload["schema"] not in {SCHEMA, SUCCESSOR_SCHEMA} or payload["owner_namespace"] != OWNER_NAMESPACE
or payload["host"] != HOST or payload["repository"] != REPOSITORY
or payload["unit"] != profile.unit or payload["deployment_state"] != "STAGING"
or payload["release_root"] != _release_root(payload["support_commit"], layout).as_posix()
@@ -695,6 +734,8 @@ def _validate_payload(payload: Mapping[str, Any], now: int, *, layout: _Layout)
or not _HEX40.fullmatch(payload["support_commit"])
):
_fail("INSTALL_IDENTITY_INVALID")
if successor and payload["source_commit"] != R14_SOURCE_COMMIT:
_fail("INSTALL_SOURCE_COMMIT_MISMATCH")
if not isinstance(payload["listen"], Mapping) or dict(payload["listen"]) != {"host": "127.0.0.1", "port": profile.port}:
_fail("INSTALL_LISTEN_INVALID")
if (
@@ -719,16 +760,21 @@ def _validate_payload(payload: Mapping[str, Any], now: int, *, layout: _Layout)
new_module = (root / "app" / profile.module_name).as_posix()
principal_path = (root / "principal-activation.json").as_posix()
has_principal_config = principal_path in by_path
if payload["schema"] == SUCCESSOR_SCHEMA and not has_principal_config:
_fail("PRINCIPAL_CONFIG_REQUIRED")
new_activation = (
(root / "source-activation.json").as_posix()
if profile.name == BRIDGE_TARGET else None
)
new_principal_config = principal_path if has_principal_config else None
existing = payload["existing"]
if not isinstance(existing, Mapping) or set(existing) != {
existing_fields = {
"unit_active", "effective_config_sha256", "dropin", "pythonpath", "source_files",
"activation_file", "principal_scope_config",
}:
}
if not isinstance(existing, Mapping) or set(existing) != (
existing_fields | {"config_files"} if successor else existing_fields
):
_fail("EXISTING_SNAPSHOT_INVALID")
if (
existing["unit_active"] is not True
@@ -741,10 +787,37 @@ def _validate_payload(payload: Mapping[str, Any], now: int, *, layout: _Layout)
or (":" in item and not layout.test_only)
for item in existing["pythonpath"]
)
or existing["activation_file"] is not None
or existing["principal_scope_config"] is not None
):
_fail("EXISTING_SNAPSHOT_INVALID")
if successor:
if any(
value is not None and not isinstance(value, str)
for value in (existing["activation_file"], existing["principal_scope_config"])
):
_fail("EXISTING_CONFIG_SNAPSHOT_INVALID")
config_paths = {
value for value in (
existing["activation_file"], existing["principal_scope_config"]
) if value is not None
}
if not config_paths or not isinstance(existing["config_files"], list):
_fail("EXISTING_CONFIG_SNAPSHOT_INVALID")
records = tuple(FileRecord.from_mapping(item) for item in existing["config_files"])
if len(records) != len(config_paths) or {record.path for record in records} != config_paths:
_fail("EXISTING_CONFIG_SNAPSHOT_INVALID")
for record in records:
path = Path(record.path)
if (
record.kind != "existing_config"
or not _absolute_safe_path(record.path)
or not _trusted_prior_config_root(path, layout)
or record.mode not in _SAFE_EXISTING_SOURCE_MODES
or type(record.size) is not int or record.size < 0
or not isinstance(record.sha256, str) or not _HEX64.fullmatch(record.sha256)
):
_fail("EXISTING_CONFIG_SNAPSHOT_INVALID")
elif existing["activation_file"] is not None or existing["principal_scope_config"] is not None:
_fail("EXISTING_SNAPSHOT_INVALID")
old_paths = tuple(existing["pythonpath"])
if (root / "app").as_posix() in old_paths:
_fail("EXISTING_PYTHONPATH_ALREADY_ACTIVATED")
@@ -887,13 +960,39 @@ def _verified_artifact(
):
_fail("DROPIN_CONTENT_INVALID")
if principal_config:
_validate_principal_config(
principal = _validate_principal_config(
result["principal-activation.json"], root=root,
successor=payload["schema"] == SUCCESSOR_SCHEMA,
release_files={
item.path for item in records
if item.path != profile.dropin(layout).as_posix()
},
)
if payload["schema"] == SUCCESSOR_SCHEMA:
digest = principal["execution_digest"]
plan = result.get(f"plans/{digest}.json")
if plan is None or _sha(plan) != digest:
_fail("PRINCIPAL_PLAN_DIGEST_MISMATCH")
if payload["schema"] == SUCCESSOR_SCHEMA:
activation_raw = result.get("source-activation.json")
policy_raw = result.get("policy.json")
if activation_raw is None or policy_raw is None or "admission-activation.json" not in result:
_fail("SOURCE_ACTIVATION_REFERENCES_MISSING")
activation = _json_bytes(activation_raw)
if (
canonical_bytes(activation) != activation_raw
or set(activation) != {
"schema", "policy_artifact", "policy_sha256", "policy_revocations",
"admission_activation", "expected_agent_id",
}
or activation["schema"] != "aios.source-publication-host-activation.v1"
or activation["expected_agent_id"] != principal["agent_id"]
or activation["policy_artifact"] != (root / "policy.json").as_posix()
or activation["policy_sha256"] != _sha(policy_raw)
or activation["policy_revocations"] != (root / "policy-revocations.json").as_posix()
or activation["admission_activation"] != (root / "admission-activation.json").as_posix()
):
_fail("SOURCE_ACTIVATION_SCOPE_INVALID")
return result
@@ -921,7 +1020,8 @@ def _request_payload(
root = _release_root(request.support_commit, layout)
profile = _profile(request.target)
return {
"schema": SCHEMA, "target": request.target, "contract_id": request.contract_id,
"schema": SUCCESSOR_SCHEMA if "config_files" in request.existing else SCHEMA,
"target": request.target, "contract_id": request.contract_id,
"owner_signer_id": request.owner_signer_id, "owner_key_id": owner_key_id,
"owner_namespace": OWNER_NAMESPACE, "host": HOST, "repository": REPOSITORY,
"source_commit": request.source_commit, "support_commit": request.support_commit,
@@ -991,7 +1091,37 @@ class Installer:
self.layout = _test_layout if _test_layout is not None else _Layout()
def _state_path(self, payload: Mapping[str, Any]) -> Path:
return self.layout.state_root / f"{payload['target']}-support-{payload['support_commit']}.json"
legacy = self.layout.state_root / f"{payload['target']}-support-{payload['support_commit']}.json"
# Preserve the historical support-keyed record for its original
# contract. Once that contract is durably rolled back, a fresh
# contract receives its own immutable state record; APPLIED/UNKNOWN
# records remain a barrier and are never bypassed by this split.
if legacy.is_file():
prior = self._read_state(legacy)
if (
prior is not None
and prior.get("status") == "ROLLED_BACK"
and prior.get("contract_id") != payload.get("contract_id")
):
successor = self.layout.state_root / (
f"{payload['target']}-support-{payload['support_commit']}-contract-"
f"{_sha(str(payload['contract_id']).encode())}.json"
)
if successor.is_file():
return successor
prefix = f"{payload['target']}-support-{payload['support_commit']}-contract-"
for candidate in self.layout.state_root.glob(prefix + "*.json"):
if candidate.is_symlink() or not candidate.is_file():
_fail("INSTALL_STATE_INVALID")
observed = self._read_state(candidate)
if observed is None or observed.get("contract_id") == payload.get("contract_id"):
continue
if observed.get("status") in {"APPLIED", "UNKNOWN"}:
_fail("INSTALL_PRIOR_ATTEMPT_NOT_APPLIED")
if observed.get("status") not in {"ROLLED_BACK"}:
_fail("INSTALL_PRIOR_ATTEMPT_NOT_APPLIED")
return successor
return legacy
def _nonce_path(self, payload: Mapping[str, Any]) -> Path:
return self.layout.state_root / "nonces" / f"{_sha(payload['nonce'].encode())}.json"
@@ -1024,6 +1154,8 @@ class Installer:
existing = payload["existing"]
profile = _profile(payload["target"])
dropin = profile.dropin(self.layout)
if isinstance(self.backend, SystemdBackend):
self.backend.verify_dropin_precedence(profile, dropin)
snapshot = self.backend.unit_snapshot(profile)
if (
snapshot.active is not True
@@ -1047,6 +1179,8 @@ class Installer:
_fail("EXISTING_DROPIN_MISMATCH")
for item in existing["source_files"]:
self._verify_file(FileRecord.from_mapping(item))
for item in existing.get("config_files", ()):
self._verify_file(FileRecord.from_mapping(item))
return backup
def _verify_readiness(
@@ -1262,6 +1396,8 @@ class Installer:
_fail("ROLLBACK_EFFECTIVE_CONFIG_MISMATCH")
for item in existing["source_files"]:
self._verify_file(FileRecord.from_mapping(item))
for item in existing.get("config_files", ()):
self._verify_file(FileRecord.from_mapping(item))
self._verify_readiness(prepared.payload["rollback"]["readiness"], profile)
if not profile.installs_release:
self._verify_release_tree(prepared.payload, profile)
@@ -83,6 +83,8 @@ class CommittedTreeSourceReader:
_OID = re.compile(r"^[0-9a-f]{40}$")
_SHA = re.compile(r"^[0-9a-f]{64}$")
_SAFE_CODE = re.compile(r"^(?:TASK_PRINCIPAL|EXECUTION_PLAN|GOVERNED)_[A-Z0-9_]{3,120}$")
_SAFE_OPERATION = re.compile(r"^[A-Za-z][A-Za-z0-9]{1,63}$")
def _oid(value: Any) -> str:
@@ -133,6 +135,7 @@ class GovernedPublicationClient:
runtime_fingerprint: str,
process_start_identity: str,
commit_message: str = "governed source publication",
diagnostic_sink: Callable[[Mapping[str, Any]], None] | None = None,
) -> None:
if (
writer_executor is None
@@ -166,6 +169,36 @@ class GovernedPublicationClient:
self.runtime_fingerprint = runtime_fingerprint
self.process_start_identity = process_start_identity
self.commit_message = commit_message
if diagnostic_sink is not None and not callable(diagnostic_sink):
raise ValueError("diagnostic_sink_invalid")
self.diagnostic_sink = diagnostic_sink
def _record_uncertain_effect(self, *, exc: Exception, task_id: str,
stage: str) -> None:
"""Emit only allowlisted facts; never serialize an exception or body."""
if self.diagnostic_sink is None:
return
raw_status = getattr(exc, "status", None)
status = raw_status if type(raw_status) is int and 100 <= raw_status <= 599 else None
raw_code = getattr(exc, "error_code", None)
code = raw_code if isinstance(raw_code, str) and _SAFE_CODE.fullmatch(raw_code) else None
raw_operation = getattr(exc, "operation", None)
operation = (
raw_operation if isinstance(raw_operation, str)
and _SAFE_OPERATION.fullmatch(raw_operation) else stage
)
diagnostic = {
"task_id": task_id, "operation": operation,
"http_status": status,
"error_code": (
"transport_unknown" if status is None else (code or "issuer_error_unknown")
),
}
try:
self.diagnostic_sink(diagnostic)
except Exception:
# Diagnostic observation must never change the uncertainty policy.
pass
def _validate_admission(self, intent: SourcePublicationIntent, receipt: Mapping[str, Any]) -> Mapping[str, Any]:
if not isinstance(receipt, Mapping):
@@ -233,8 +266,10 @@ class GovernedPublicationClient:
manifest_id=self.manifest_id, runtime_fingerprint=self.runtime_fingerprint,
process_start_identity=self.process_start_identity,
)
stage = "acquireWriterLease"
try:
session = self.writer_executor.acquire(request=request, job_id=job_id)
stage = "commitFiles"
commit_result = self.writer_executor.commit_and_readback(
session=session, request=request, files=files,
expected_sha256=hashes, message=self.commit_message,
@@ -251,6 +286,7 @@ class GovernedPublicationClient:
# A transport exception after a mutation is indeterminate. Never
# resubmit it here; the caller must reconcile through the existing
# Job/recovery service.
self._record_uncertain_effect(exc=exc, task_id=intent.task_id, stage=stage)
raise GovernedPublicationError("PUBLICATION_EFFECT_UNKNOWN") from exc
if not isinstance(readback, Mapping) or readback.get("authenticated") is not True:
raise GovernedPublicationError("PUBLICATION_AUTHENTICATED_READBACK_REQUIRED")
@@ -326,6 +362,9 @@ class GovernedPublicationClient:
except Exception as exc:
# A lost result response may have changed the durable Task. Never
# re-register, reclaim or retry here.
self._record_uncertain_effect(
exc=exc, task_id=intent.task_id, stage="submitTaskResult",
)
raise GovernedPublicationError("PUBLICATION_EFFECT_UNKNOWN") from exc
publication.update(
task_result=task_result,
@@ -553,7 +553,7 @@ class ReleaseSourceAdmissionStore:
supplied_candidate = str(candidate_id or "")
if supplied_candidate and supplied_candidate != record["candidate_id"]:
raise ReleaseSourceAdmissionError("SOURCE_ADMISSION_CANDIDATE_MISMATCH")
return {
result = {
"mode": RECIPE_MODE,
"task_id": record["task_id"],
"candidate_id": record["candidate_id"],
@@ -562,6 +562,9 @@ class ReleaseSourceAdmissionStore:
"version": record["version"],
"purpose": record["purpose"],
}
if version == "V2R2C5":
result["tree_oid"] = record["tree_oid"]
return result
@dataclass(slots=True)
+25 -1
View File
@@ -434,6 +434,7 @@ class DeploymentPlan:
health_contract_version: str = ""
timeout_policy: dict[str, Any] = field(default_factory=dict)
retry_policy: dict[str, Any] = field(default_factory=dict)
rollback_target: dict[str, str] = field(default_factory=dict)
def __post_init__(self) -> None:
_validate_identifier("service", self.service)
@@ -463,12 +464,20 @@ class DeploymentPlan:
raise ValueError("timeout_policy must be an object")
if not isinstance(self.retry_policy, dict):
raise ValueError("retry_policy must be an object")
if not isinstance(self.rollback_target, dict):
raise ValueError("rollback_target must be an object")
if self.rollback_target:
if set(self.rollback_target) != {"version", "target_oid", "artifact_sha256"}:
raise ValueError("rollback_target fields invalid")
_validate_identifier("rollback_target.version", self.rollback_target["version"])
_validate_oid("rollback_target.target_oid", self.rollback_target["target_oid"])
_validate_sha("rollback_target.artifact_sha256", self.rollback_target["artifact_sha256"])
if self.writer_expected is not False:
raise ValueError("production writer must remain disabled")
@property
def canonical_payload(self) -> dict[str, Any]:
return {
payload = {
"artifact_sha256": self.artifact_sha256,
"authoritative_git_dir": self.authoritative_git_dir,
"baseline_before_sha256": self.baseline_before_sha256,
@@ -497,6 +506,11 @@ class DeploymentPlan:
"writer_expected": self.writer_expected,
"registry_before_sha256": self.registry_before_sha256,
}
# Preserve historical deployment digests when the extension is absent.
# An explicit target becomes part of both deployment and approval hashes.
if self.rollback_target:
payload["rollback_target"] = dict(self.rollback_target)
return payload
@property
def sha256(self) -> str:
@@ -4031,6 +4045,7 @@ class SilentReleaseActionService:
policy_sha256: str = "",
max_builder_attempts: int = 3,
builder_attempt_timeout_seconds: int = 180,
completion_approvals: Any = None,
current_authority_guard: Callable[
[SignedReleasePlanApproval, str, str], None
]
@@ -4078,6 +4093,15 @@ class SilentReleaseActionService:
self.sagas = ReleaseSagaStore(self.root / "sagas")
self.approvals_root = self.root / "approvals"
self.approvals = SignedApprovalStore(self.approvals_root)
if completion_approvals is not None:
from c5_completion_renewal import RegisteredCompletionApprovalStore
if (type(completion_approvals) is not RegisteredCompletionApprovalStore
or self.root != completion_approvals.authority.state_root
or completion_approvals.original.root != self.approvals_root
or approval_verifier is None or execution_enabled):
raise CapabilityAuthorizationError("registered shadow completion approval dependency required")
completion_approvals.authority()
self.approvals = completion_approvals
self.intents_root = self.root / "intents"
self.acceptance_root = self.root / "acceptance"
self.notifications_root = self.root / "notifications"
@@ -29,6 +29,7 @@ import base64
import hashlib
import json
import os
import re
import secrets
import subprocess
import time
@@ -63,6 +64,17 @@ class LiveProtocolUnavailable(LiveProtocolError):
pass
class IssuerResponseError(LiveProtocolError):
"""Safe, structured issuer failure metadata for isolated diagnostics."""
def __init__(self, message: str, *, status: int, error_code: str | None,
operation: str) -> None:
super().__init__(message)
self.status = status
self.error_code = error_code
self.operation = operation
@dataclass(frozen=True)
class HttpResult:
status: int
@@ -568,8 +580,14 @@ class LiveTaskPrincipalClient:
break
time.sleep(1)
status = 0 if last is None else last.status
raise LiveProtocolError(
f"TaskPrincipal assertion issue failed operation={operation} HTTP {status}"
raw_code = None if last is None else last.body.get("error")
error_code = (
raw_code if isinstance(raw_code, str)
and re.fullmatch(r"(?:TASK_PRINCIPAL|EXECUTION_PLAN|GOVERNED)_[A-Z0-9_]{3,120}", raw_code) else None
)
raise IssuerResponseError(
f"TaskPrincipal assertion issue failed operation={operation} HTTP {status}",
status=status, error_code=error_code, operation=operation,
)
@@ -26,6 +26,8 @@ import re
import stat
import subprocess
import sys
import tarfile
import tempfile
import time
from datetime import datetime, timezone
from pathlib import Path
@@ -405,51 +407,85 @@ def run_secret_scan_provider(
tree_oid: str,
store_id: str = "secret_scan",
provider_identity: str = "aios.secret-scanner",
artifact_path: Path | None = None,
) -> tuple[Mapping[str, Any], str]:
"""Execute the real secret scanner (gitleaks) and produce a root receipt."""
if not Path(gitleaks_bin).is_file():
raise TrustedProviderExecutionError("SECRET_SCANNER_UNAVAILABLE")
artifact_sha = None
if artifact_path is not None:
artifact_sha = _validate_artifact_scan_target(artifact_path, target)
# --no-git scans the working tree as a filesystem (does not require a .git
# dir), so an exported release checkout can be scanned faithfully.
scanned = _run(
[
gitleaks_bin, "detect",
"--source", str(target),
"--no-git",
"--no-banner",
"--exit-code", "0",
],
cwd=target,
timeout=1800,
)
warnings = 0
# gitleaks exits 1 when findings are present; with --exit-code 0 it returns 0
# but prints findings. Count lines that indicate a finding.
finding_lines = [
ln for ln in scanned.stdout.splitlines()
if ("Finding:" in ln and ":" in ln and "RuleID" not in ln)
] + [
ln for ln in scanned.stderr.splitlines()
if ("Finding:" in ln and ":" in ln and "RuleID" not in ln)
]
artifact_sha = None
if target.is_file():
artifact_sha = hashlib.sha256(target.read_bytes()).hexdigest()
with tempfile.TemporaryDirectory(prefix="aios-secret-report-") as report_root:
report = Path(report_root) / "findings.json"
scanned = _run(
[
gitleaks_bin, "detect",
"--source", str(target),
"--no-git",
"--no-banner",
"--exit-code", "1",
"--report-format", "json", "--report-path", str(report),
],
cwd=target,
timeout=1800,
)
try:
findings = json.loads(report.read_text())
except (OSError, ValueError) as exc:
raise TrustedProviderExecutionError("SECRET_SCANNER_REPORT_INVALID") from exc
if not isinstance(findings, list) or any(not isinstance(v, dict) for v in findings):
raise TrustedProviderExecutionError("SECRET_SCANNER_REPORT_INVALID")
report_sha = hashlib.sha256(report.read_bytes()).hexdigest()
if artifact_path is not None and _validate_artifact_scan_target(artifact_path, target) != artifact_sha:
raise TrustedProviderExecutionError("SECRET_SCAN_ARTIFACT_CHANGED")
# Passed only when the scanner actually ran cleanly (exit 0) AND found zero
# leaks. A scanner crash (non-zero exit) is never a pass.
clean_run = scanned.returncode == 0
result = {
"passed": clean_run and len(finding_lines) == 0,
"passed": clean_run and len(findings) == 0,
"scanner": "gitleaks",
"exit_code": scanned.returncode,
"target": str(target),
"findings": len(finding_lines),
"findings": len(findings),
"report_sha256": report_sha,
"artifact_sha256": artifact_sha,
"clean_run": clean_run,
}
return result, _receipt_ref_for(result, "secret_scan")
def _validate_artifact_scan_target(artifact: Path, target: Path) -> str:
"""Bind a real filesystem scan to every byte of its exact archive payload."""
if artifact.is_symlink() or not artifact.is_file() or target.is_symlink() or not target.is_dir():
raise TrustedProviderExecutionError("SECRET_SCAN_ARTIFACT_TARGET_INVALID")
raw = artifact.read_bytes()
import io
expected: set[str] = set()
try:
with tarfile.open(fileobj=io.BytesIO(raw), mode="r:*") as archive:
members = archive.getmembers()
if not members or len(members) > 20_000 or sum(m.size for m in members) > 2 * 1024**3:
raise TrustedProviderExecutionError("SECRET_SCAN_ARCHIVE_LIMIT")
for member in members:
parts = Path(member.name).parts
if not member.isfile() or member.name.startswith("/") or "\\" in member.name or not parts or any(p in {".", ".."} for p in parts) or member.name in expected:
raise TrustedProviderExecutionError("SECRET_SCAN_ARCHIVE_INVALID")
path = target.joinpath(*parts)
if path.is_symlink() or not path.is_file() or not path.resolve().is_relative_to(target.resolve()):
raise TrustedProviderExecutionError("SECRET_SCAN_PAYLOAD_MISMATCH")
if archive.extractfile(member).read() != path.read_bytes():
raise TrustedProviderExecutionError("SECRET_SCAN_PAYLOAD_MISMATCH")
expected.add(member.name)
except (tarfile.TarError, OSError) as exc:
raise TrustedProviderExecutionError("SECRET_SCAN_ARCHIVE_INVALID") from exc
actual = {p.relative_to(target).as_posix() for p in target.rglob("*") if p.is_file()}
if actual != expected or any(p.is_symlink() for p in target.rglob("*")):
raise TrustedProviderExecutionError("SECRET_SCAN_PAYLOAD_MISMATCH")
return hashlib.sha256(raw).hexdigest()
def _receipt_ref_for(result: Mapping[str, Any], kind: str) -> str:
run_id = hashlib.sha256(canonical_json_bytes(result)).hexdigest()[:16]
return f"run-{run_id}/receipt.json"
@@ -531,6 +567,7 @@ def _run_provider_executor(
target=repo_root,
source_oid=source_oid,
tree_oid=tree_oid,
artifact_path=(Path(os.environ["AIOS_RELEASE_ARTIFACT"]) if os.environ.get("AIOS_RELEASE_ARTIFACT") else None),
)
if kind == "gitea":
gitea_api_url = os.environ.get("AIOS_GITEA_API_URL", "").strip()
@@ -601,9 +638,14 @@ if __name__ == "__main__":
parser.add_argument("--store-root", required=True)
parser.add_argument("--receipt-ref", required=True)
parser.add_argument("--repo-root", default=None, help="Real source checkout to execute against")
parser.add_argument("--artifact-path", type=Path, default=None)
args = parser.parse_args()
if args.repo_root:
os.environ["AIOS_REPO_ROOT"] = args.repo_root
if args.artifact_path:
if args.kind != "secret_scan":
raise SystemExit("ARTIFACT_BINDING_REQUIRES_SECRET_SCAN")
os.environ["AIOS_RELEASE_ARTIFACT"] = str(args.artifact_path)
try:
digest = produce(
args.kind,
@@ -0,0 +1,80 @@
# V2R2C5 C5 mainline consolidation source record
## Scope and authority boundary
This maintenance candidate is reconciled onto current main
`e33664f03302fd9de7635afdd8aa9339a50e5f0a` (tree
`92ece1e9646ac2d12b4b467c82b1279ac1e11608`) and preserves its V2R2C6 source
and security changes. The original reviewed selection was made against
`b915386ccbed46a7ae77220887da8ae666649a79`. Current main already contains the
product source commit; the remaining five reviewed source commits and isolated
fixtures are carried by this candidate. This is a source and test consolidation
only. It does not create a production baseline, update the runtime registry,
activate a Task or Agent, publish a tag, or authorize a protected merge. The
production source, tag, artifact, and existing baseline records remain immutable
inputs to their validators.
| Layer | Commit | Current-main relationship |
| --- | --- | --- |
| installer / G1 | `7e14abeb6b91978ce16f3d903b6c0714af302991` | integrated by this candidate |
| builder | `8fc61bd11213bf22cc795ba933a5f367f8831f82` | integrated by this candidate |
| completion | `66530975f52a4d6f1449f18d23af808e0c9339bb` | integrated by this candidate |
| deployment | `5443c93f00ce879e1025a53258557788da4dbcec` | integrated by this candidate |
| boundary / provider | `74c0eb75348b752c82f45e53759edfff5a47e3ef` | integrated by this candidate |
| product | `5f65e458957dd51c9973cfa956a624f3cbf54990` | already an ancestor of current main |
The reviewed selection manifest contained 41 paths and eight non-identical
overlap records. No receipt, journal, live baseline, runtime registry record,
or other generated production state was used as source.
## Per-file decisions
| Classification | Paths | Decision |
| --- | --- | --- |
| source | `active-runtime/app/aios_baseline/service.py`; `aios_capability_contract.py`; `aios_task_principal_v3_receipt_provider.py`; `approved_release_executor.py`; `c5_exact_deployment_adapter.py`; `c5_registered_installer_executor.py`; `c5_staging_installation.py`; `governed_publication_client.py`; `release_source_admission.py`; `task_plane/live_governed_writer_adapters.py` | Integrated G1/product behavior and fail-closed validators. |
| builder source | `active-runtime/app/aios_trusted_candidate_builder.py` | Kept the later builder contract and integration validation. Replaced historical live policy identity constants with values read from the governed policy. |
| completion source | `active-runtime/app/c5_completion_api.py`; `c5_completion_phase.py`; `c5_completion_renewal.py`; `c5_completion_signer.py`; `silent_release_hardening.py` | Retained completion-only successor validation. Authority root, state root, Owner key, predecessor fingerprint, and historical Saga head now require a root-owned digest-bound bindings file. |
| shared completion/boundary source | `active-runtime/app/c5_release_execution_host.py` | Retained the completion host and added the boundary integration-policy checks. Source-admission Task identity is now supplied by the digest-bound host configuration. |
| deployment source | `active-runtime/app/c5_production_deployment_adapter.py` | Applied the later strict symlink-resolution check to the integrated installer version. |
| provider source | `active-runtime/app/trusted_evidence_providers.py` | Retained artifact-to-extracted-tree binding and structured gitleaks JSON handling. |
| helper | `scripts/build_c5_live_principal_overlay.py`; `scripts/build_c5_staging_artifact.py`; `scripts/probe_issuer_v3_r2_readonly.py` | Kept deterministic/read-only helper behavior. Overlay creation now requires explicit Task and Agent bindings; the checked-in example uses synthetic fixture identities. The staging artifact producer emits the final consumer's fixed 128-tilde `c5-r14-source.conf` drop-in path. |
| fixture | `tests/fixtures/c5-r14-execution-plan.json`; `tests/fixtures/issuer-v3-base/*`; `tests/fixtures/trusted-candidate-builder-policy-c5.json` | Kept only isolated synthetic Task/Agent identities. Moved the C5 builder policy from runtime config to test fixtures. |
| tests | `tests/baseline_v1_1/test_claim_heartbeat.py`; `tests/baseline_v1_1/test_loop.py`; `tests/task_plane/test_live_governed_writer_adapters.py`; `tests/test_c5_builder_contract.py`; `tests/test_c5_completion_renewal.py`; `tests/test_c5_exact_deployment_adapter.py`; `tests/test_c5_production_deployment_adapter.py`; `tests/test_c5_registered_installer_executor.py`; `tests/test_c5_release_execution_host.py`; `tests/test_c5_staging_artifact.py`; `tests/test_c5_staging_installation.py`; `tests/test_governed_publication_client.py`; `tests/test_issuer_v3_overlay_builder.py`; `tests/test_issuer_v3_receipt_provider.py`; `tests/test_signer_contract_compatibility.py`; `tests/test_trusted_candidate_builder.py`; `tests/test_trusted_evidence_providers_harness.py`; `tests/test_v2r2c5_capability_contract.py` | Integrated the selected regression coverage and resolved overlap tests in favor of the combined security contract. Execution-enabled heartbeat fixtures explicitly install trusted identity and evidence verifiers. The staging-artifact fixture builds a disposable Git source repository and never reads a live release tree. |
## Overlap resolution
1. Builder source and tests use the later builder contract while preserving G1
admission and rollback validation.
2. Deployment source and tests retain the integrated adapter and add the later
strict symlink target checks.
3. The release host retains completion-only support and the boundary/provider
integration checks; the boundary host was not accepted wholesale because it
would remove completion behavior.
4. `silent_release_hardening.py` retains the completion dependency and derives
its state-root check from the registered completion authority while keeping
the C6 standing-authorization guard and credential fencing.
5. The six current-main overlaps were reconciled without whole-file selection:
the baseline evidence changes were identical, the capability vocabulary kept
the C6 digest additions, staging tests kept C6 file-mode fixtures, and the C5
completion dependency was combined with C6 authorization guards.
6. The unsigned staging producer and the final consumer now share the governed
fixed drop-in identity. The consumer's path, mode, archive, hash, and signature
validators remain unchanged and fail closed on any other producer path.
## Excluded ephemeral material
The candidate contains no live one-time Task or Agent identity, TTL, nonce,
approval fingerprint, activation file, or default historical Saga-head binding.
It also contains no copied receipt, live journal, baseline, registry projection,
signer credential, or production service state. Immutable source/tree/artifact,
lineage, hash, expiry, authentication, and compare-and-swap checks remain in the
code. Test-only identities are explicitly named `fixture-*` or `FIXTURE-*`.
## Verification record
Verification is recorded against the committed candidate and its exported
snapshot. Windows results are treated as development evidence only where POSIX
ownership, mode, or symlink semantics are unavailable. The authoritative full
regression is run from the exported tree in a disposable Linux unit with empty
capabilities, private network and temporary directories, a read-only system,
and no signer, live configuration, or Task database access.
+177 -8
View File
@@ -6,9 +6,12 @@ from __future__ import annotations
import argparse
import hashlib
import json
import io
from pathlib import Path
import shutil
import stat
import subprocess
import tarfile
from typing import Any
@@ -23,6 +26,74 @@ PINNED = {
"governed_runtime_scope.py": "c56bd709c1e0c8956a5be7a4a8dc2a12591bdec2b84dcfba5c53f6bced746bcb",
}
# The currently loaded generic issuer adapter/scope, verified independently
# before preparing a V3-only overlay. They remain the V1/V2 implementation.
ISSUER_V3_BASE = (
"/var/lib/aios-gitops/bridge-v2-staging/releases/"
"p0-task-principal-governed-runtime-20260910-02/app"
)
ISSUER_V3_BASE_HASHES = {
"aios_task_principal_admission.py": "d56fb0078cfcb907f2afb35f0163051a962a89520b63f6c53149d478fc7777c8",
"aios_task_principal_governed_adapter.py": "1f8fae5c4462b333f55b471acedd1bbc680b29f0c51fd0318ddb6f5626671933",
"governed_runtime_scope.py": "04680f00111893411456ac6e5c3c557feedb824f9e568629e7d2904cee81a371",
}
ISSUER_V3_SOURCE_FILES = (
"active-runtime/app/aios_task_principal_v3_receipt_provider.py",
"active-runtime/app/aios_task_admission_live_adapters.py",
"active-runtime/app/aios_task_admission_bootstrap.py",
"active-runtime/app/aios_governed_task_bootstrap.py",
"active-runtime/app/aios_gitops_job_controller.py",
"active-runtime/app/aios_gitops_job_identity.py",
"active-runtime/app/aios_online_agent_execution.py",
)
ISSUER_V3_ADAPTER_OVERLAY = '''"""V3 receipt-bound overlay; legacy V1/V2 adapter is hash-pinned."""
from __future__ import annotations
import hashlib
import importlib.util
import os
from pathlib import Path
import stat
import sys
BASE_ROOT = Path({base!r})
BASE_HASHES = {hashes!r}
for name, expected in BASE_HASHES.items():
source = BASE_ROOT / name
metadata = source.lstat()
if (stat.S_ISLNK(metadata.st_mode) or not stat.S_ISREG(metadata.st_mode)
or stat.S_IMODE(metadata.st_mode) & 0o022
or hashlib.sha256(source.read_bytes()).hexdigest() != expected):
raise RuntimeError("ISSUER_V3_BASE_MODULE_DRIFT:" + name)
BASE = BASE_ROOT / "aios_task_principal_governed_adapter.py"
spec = importlib.util.spec_from_file_location("_issuer_v3_pinned_base_adapter", BASE)
if spec is None or spec.loader is None:
raise RuntimeError("ISSUER_V3_BASE_ADAPTER_UNLOADABLE")
module = importlib.util.module_from_spec(spec)
sys.modules[spec.name] = module
spec.loader.exec_module(module)
import governed_runtime_scope as _issuer_scope
if Path(_issuer_scope.__file__).resolve() != (BASE_ROOT / "governed_runtime_scope.py").resolve():
raise RuntimeError("ISSUER_V3_BASE_SCOPE_PATH_DRIFT")
for name, value in module.__dict__.items():
if not name.startswith("__"):
globals()[name] = value
from aios_task_principal_v3_receipt_provider import IssuerV3ReceiptScopeProvider
class GovernedScopeRuntimeAdapter(module.GovernedScopeRuntimeAdapter):
def __init__(self, config, *, audience_builder, provider=None, operations=None):
super().__init__(config, audience_builder=audience_builder,
provider=provider, operations=operations)
root = os.environ.get("AIOS_GOVERNED_PRODUCER_STATE_ROOT", "").strip()
if root:
self.provider = IssuerV3ReceiptScopeProvider(
legacy=self.provider, producer_state_root=Path(root),
plan_root=self.plans.root,
grant_reader=self.grants.require_current_grant,
)
'''
EFFECTIVE_ADMISSION_SOURCE = (
"/var/lib/aios-gitops/bridge-v2-staging/releases/"
"p0-task-principal-governed-runtime-20260910-02/app/"
@@ -44,7 +115,7 @@ BASE_SOURCE = Path(
"aios_task_principal_admission.py"
)
BASE_SOURCE_SHA256 = "d56fb0078cfcb907f2afb35f0163051a962a89520b63f6c53149d478fc7777c8"
C5_TASK_ID = "TASK-V2R2C5-SOURCE-PUBLICATION-20260926-R1"
C5_TASK_ID = "__AIOS_GOVERNED_TASK_ID__"
def _load_base():
@@ -182,8 +253,8 @@ AIOS_C5_PRINCIPAL_SCOPE_CONFIG=/var/lib/aios-gitops/bridge-v2-staging/releases/R
ACTIVATION_EXAMPLE = {
"schema": "aios.c5-task-principal-activation.v1",
"task_id": "TASK-V2R2C5-SOURCE-PUBLICATION-20260926-R1",
"agent_id": "agent/codex-c5-release",
"task_id": "fixture-governed-task-id",
"agent_id": "agent/fixture-c5-release",
"execution_digest": "1ba534d551991e8c99014285eeea36afcbc1500686035cb8bcd7f9f4efd6215e",
"store_path": "/var/lib/aios-gitops/c5-source-principal/enrollment.sqlite3",
"grant_db": "/var/lib/aios-gitops/bridge-v2-staging/task-admission.sqlite3",
@@ -249,16 +320,105 @@ def build_issuer_overlay(source: Path, target: Path) -> dict[str, Any]:
}
def build_issuer_v3_overlay(
*, adapter_source_root: Path, output_root: Path,
source_commit: str, repository_root: Path,
) -> dict[str, Any]:
"""Build from one immutable source commit and pinned live base inputs."""
source_commit = source_commit.strip().lower()
if len(source_commit) != 40 or any(c not in "0123456789abcdef" for c in source_commit):
raise SystemExit("ISSUER_V3_SOURCE_COMMIT_INVALID")
repository_root = repository_root.resolve()
commit = subprocess.run(
["git", "-C", str(repository_root), "rev-parse", "--verify", source_commit + "^{commit}"],
check=True, capture_output=True, text=True,
).stdout.strip()
if commit != source_commit:
raise SystemExit("ISSUER_V3_SOURCE_COMMIT_MISMATCH")
adapter_source_root = adapter_source_root.resolve()
for name, expected in ISSUER_V3_BASE_HASHES.items():
source = adapter_source_root / name
if source.is_symlink() or not source.is_file() or sha256(source) != expected:
raise SystemExit("ISSUER_V3_BASE_HASH_MISMATCH:" + name)
output_root = output_root.resolve()
if output_root.exists() and any(output_root.iterdir()):
raise SystemExit("OUTPUT_ROOT_NOT_EMPTY")
app = output_root / "app"
app.mkdir(parents=True, exist_ok=True)
files: dict[str, str] = {}
for path in ISSUER_V3_SOURCE_FILES:
raw = subprocess.run(
["git", "-C", str(repository_root), "show", source_commit + ":" + path],
check=True, capture_output=True,
).stdout
target = app / Path(path).name
target.write_bytes(raw)
files["app/" + target.name] = hashlib.sha256(raw).hexdigest()
overlay = ISSUER_V3_ADAPTER_OVERLAY.format(
base=ISSUER_V3_BASE,
hashes=ISSUER_V3_BASE_HASHES,
).encode("utf-8")
target = app / "aios_task_principal_governed_adapter.py"
target.write_bytes(overlay)
files["app/" + target.name] = hashlib.sha256(overlay).hexdigest()
file_manifest_digest = hashlib.sha256(json.dumps(
files, sort_keys=True, separators=(",", ":"),
).encode()).hexdigest()
archive = output_root / "issuer-v3-overlay.tar"
with tarfile.open(archive, mode="w", format=tarfile.USTAR_FORMAT) as bundle:
for name in sorted(files):
raw = (output_root / name).read_bytes()
entry = tarfile.TarInfo(name)
entry.size = len(raw)
entry.mode = 0o444
entry.uid = entry.gid = entry.mtime = 0
entry.uname = entry.gname = ""
bundle.addfile(entry, io.BytesIO(raw))
artifact_digest = sha256(archive)
manifest = {
"schema": "aios.issuer-v3-receipt-overlay.v1",
"source_commit": source_commit,
"artifact_sha256": artifact_digest,
"artifact_filename": archive.name,
"file_manifest_sha256": file_manifest_digest,
"files": files,
"base_module_root": ISSUER_V3_BASE,
"base_module_sha256": ISSUER_V3_BASE_HASHES,
"provider": "aios_task_principal_v3_receipt_provider.IssuerV3ReceiptScopeProvider",
"activation_effect_performed": False,
}
(output_root / "overlay-manifest.json").write_text(
json.dumps(manifest, sort_keys=True, indent=2) + "\n", encoding="utf-8",
)
return manifest
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--principal-source-root", type=Path, required=True)
parser.add_argument("--principal-source-root", type=Path)
parser.add_argument(
"--issuer-source", type=Path,
help="Exact issuer server source; defaults to principal-source-root/aios_task_principal_issuer_server.py",
)
parser.add_argument("--adapter-source-root", type=Path, required=True)
parser.add_argument("--adapter-source-root", type=Path)
parser.add_argument("--output-root", type=Path, required=True)
parser.add_argument("--issuer-v3-only", action="store_true")
parser.add_argument("--source-commit")
parser.add_argument("--task-id")
parser.add_argument("--agent-id")
args = parser.parse_args()
if args.issuer_v3_only:
if args.adapter_source_root is None or not args.source_commit:
parser.error("--issuer-v3-only requires --adapter-source-root and --source-commit")
build_issuer_v3_overlay(
adapter_source_root=args.adapter_source_root,
output_root=args.output_root,
source_commit=args.source_commit,
repository_root=Path(__file__).resolve().parents[1],
)
return 0
if args.principal_source_root is None or args.adapter_source_root is None:
parser.error("--principal-source-root and --adapter-source-root are required")
principal = args.principal_source_root.resolve()
adapter = args.adapter_source_root.resolve()
output = args.output_root.resolve()
@@ -274,7 +434,15 @@ def main() -> int:
"BASE_HASH_MISMATCH:aios_task_principal_admission.py:" + observed
)
admission_target = app / "aios_task_principal_admission.py"
admission_target.write_text(ADMISSION_OVERLAY, encoding="utf-8", newline="")
if not args.task_id or not args.agent_id:
raise SystemExit("TASK_AND_AGENT_BINDINGS_REQUIRED")
if any(character not in "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_.:-/"
for value in (args.task_id, args.agent_id) for character in value):
raise SystemExit("TASK_OR_AGENT_BINDING_INVALID")
admission_target.write_text(
ADMISSION_OVERLAY.replace("__AIOS_GOVERNED_TASK_ID__", args.task_id),
encoding="utf-8", newline="",
)
outputs: dict[str, Any] = {
"aios_task_principal_admission.py": {
@@ -317,14 +485,15 @@ def main() -> int:
env_path = config_dir / "c5-principal-scope.env.example"
env_path.write_text(ENV_EXAMPLE, encoding="utf-8", newline="")
activation_path = config_dir / "principal-activation.example.json"
activation = {**ACTIVATION_EXAMPLE, "task_id": args.task_id, "agent_id": args.agent_id}
activation_path.write_text(
json.dumps(ACTIVATION_EXAMPLE, sort_keys=True, separators=(",", ":")),
json.dumps(activation, sort_keys=True, separators=(",", ":")),
encoding="utf-8", newline="",
)
manifest = {
"schema": "aios.c5-task-principal-overlay.v1",
"task_id": "TASK-V2R2C5-SOURCE-PUBLICATION-20260926-R1",
"task_id": args.task_id,
"execution_digest": "1ba534d551991e8c99014285eeea36afcbc1500686035cb8bcd7f9f4efd6215e",
"files": outputs,
"entrypoints": {
+1 -1
View File
@@ -29,7 +29,7 @@ UNIT = "aios-chatgpt-bridge-v2-staging.service"
BRIDGE_TARGET = "bridge-18892"
ISSUER_TARGET = "principal-issuer-18946"
ISSUER_UNIT = "aios-task-principal-issuer.service"
DROPIN_NAME = "~" * 32 + "-c5-source.conf"
DROPIN_NAME = "~" * 128 + "-c5-r14-source.conf"
DROPIN = f"/etc/systemd/system/{UNIT}.d/{DROPIN_NAME}"
ISSUER_DROPIN = f"/etc/systemd/system/{ISSUER_UNIT}.d/{DROPIN_NAME}"
RUNTIME_URL = "http://127.0.0.1:18892/v2/runtime"
+84
View File
@@ -0,0 +1,84 @@
"""Read the expired R2 V3 plan and producer binding without a write path."""
from __future__ import annotations
import argparse
import hashlib
import json
from pathlib import Path
import sqlite3
import sys
ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(ROOT / "tests" / "fixtures" / "issuer-v3-base"))
sys.path.insert(0, str(ROOT / "active-runtime" / "app"))
from governed_runtime_scope import ( # noqa: E402
ExecutionSnapshot, GrantSnapshot, GovernedScopeError, TaskSnapshot, TrustSnapshot,
)
from aios_task_principal_v3_receipt_provider import IssuerV3ReceiptScopeProvider # noqa: E402
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--grant-db", type=Path, required=True)
parser.add_argument("--plan-dir", type=Path, required=True)
parser.add_argument("--producer-root", type=Path, required=True)
parser.add_argument("--task-id", required=True)
parser.add_argument("--execution-digest", required=True)
parser.add_argument("--job-id", required=True)
args = parser.parse_args()
if len(args.execution_digest) != 64 or any(c not in "0123456789abcdef" for c in args.execution_digest):
raise SystemExit("EXECUTION_DIGEST_INVALID")
# mode=ro does not create the database; no producer or issuer service call.
with sqlite3.connect(f"file:{args.grant_db.as_posix()}?mode=ro", uri=True) as db:
rows = db.execute("SELECT grant_json FROM task_admission_grants").fetchall()
matches = [
json.loads(row[0]) for row in rows
if json.loads(row[0]).get("task_id") == args.task_id
and json.loads(row[0]).get("execution_digest") == args.execution_digest
]
if len(matches) != 1:
raise SystemExit("R2_EXACT_GRANT_NOT_UNIQUE")
grant = matches[0]
raw_plan = (args.plan_dir / (args.execution_digest + ".json")).read_bytes()
plan = json.loads(raw_plan)
if hashlib.sha256(raw_plan).hexdigest() != args.execution_digest:
raise SystemExit("R2_ORIGINAL_PLAN_DIGEST_MISMATCH")
provider = IssuerV3ReceiptScopeProvider(
legacy=None, producer_state_root=args.producer_root,
plan_root=args.plan_dir, grant_reader=lambda **_kwargs: grant,
)
job_id, proof = provider.inspect_v3_binding(grant_raw=grant, plan=plan)
if job_id != args.job_id or proof.get("job_id") != args.job_id:
raise SystemExit("R2_PRODUCER_JOB_MISMATCH")
task = TaskSnapshot(args.task_id, "EXPIRED")
execution = ExecutionSnapshot(
args.task_id, args.job_id, grant["repository"], grant["target_branch"],
grant["generation"], args.execution_digest,
)
snapshot = GrantSnapshot(
grant["grant_id"], "ADMITTED", args.task_id, grant["repository"],
grant["authority_base_oid"], grant["target_branch"],
grant["generation"], args.execution_digest, tuple(grant["capabilities"]),
tuple(grant["file_allowlist"]), int(grant["expires_at"]),
)
stale_code = None
try:
provider.resolve(
task=task, execution=execution, grant=snapshot, plan=plan,
trust=TrustSnapshot("fixture", "fixture", "fixture", "TASK_PRINCIPAL", "fixture"),
now=int(grant["expires_at"]) + 1,
)
except GovernedScopeError as exc:
stale_code = str(exc)
if stale_code != "TASK_NOT_CLAIMED":
raise SystemExit("R2_EXPIRED_STATE_NOT_REJECTED")
print(json.dumps({
"schema": plan.get("schema"), "execution_digest": args.execution_digest,
"producer_job_id": job_id, "receipt_binding": "EXACT_READBACK",
"expired_state_rejection": stale_code, "mutation_performed": False,
}, sort_keys=True))
return 0
if __name__ == "__main__":
raise SystemExit(main())
+3 -1
View File
@@ -6,7 +6,7 @@ import pytest
from app.task_plane.api import TaskPlaneService
from app.task_plane.errors import ConflictError, LockedError
from app.task_plane.policy import ALL_ACTION_CAPABILITIES, PolicyEngine
from app.task_plane.policy import ALL_ACTION_CAPABILITIES, IdentityVerifier, PolicyEngine
from app.task_plane.store import TaskPlaneStore
@@ -19,7 +19,9 @@ def claimed_task():
service = TaskPlaneService(
store,
policy=PolicyEngine(
identity_verifier=IdentityVerifier({"anon", IDENTITY}),
execution_enabled=True,
evidence_attestation_verifier=lambda _envelope: True,
identity_capabilities={"*": set(ALL_ACTION_CAPABILITIES)},
),
)
+1
View File
@@ -0,0 +1 @@
{"authority_base_oid":"b915386ccbed46a7ae77220887da8ae666649a79","capabilities":["agent-task:control","governed.task.create","governed.write.files","task.principal.issue","writer.lease"],"file_allowlist":["active-runtime/app/aios_capability_contract.py","tests/test_v2r2c5_capability_contract.py"],"file_sha256":{"active-runtime/app/aios_capability_contract.py":"67d8a3a296110e20a4ea59863cd7982b6bdada71abd457c0f7cec480b06d278a","tests/test_v2r2c5_capability_contract.py":"f91af641528ea9a625ca45a46b6c26f5dba568e6b69cba819ca33ab5cba28de3"},"generation":1,"repository":"aios/aios","risk":"engineering","schema":"aios.task-admission-execution-plan.v3","target_branch":"fixture/c5-r14-source-publication","task_id":"fixture-c5-r14-source-publication"}
@@ -0,0 +1,180 @@
from __future__ import annotations
"""Versioned TaskPrincipal admission overlay for governed runtime scopes.
This module DOES NOT fork or modify FINAL_SOURCE. It loads the existing exact9
admission chain from its immutable versioned path, then applies two surgical
runtime patches to the underlying principal_legacy module:
1. static-first `_scope_and_trust` fallback, only for exact task-binding mismatch;
2. assertion TTL pre-clamp for runtime grant-backed scopes.
The original exact9/r10/legacy chain remains the source of every other behavior.
"""
from pathlib import Path
from typing import Any, Mapping
import importlib.util
import os
from aios_task_principal_governed_adapter import (
GovernedScopeRuntimeAdapter,
config_from_environment,
)
from governed_scope_hook import (
clamp_assertion_expiry,
is_exact_scope_binding_mismatch,
static_first_scope,
)
DEFAULT_EXACT9_ADMISSION = (
"/var/lib/aios-gitops/bridge-v2-staging/releases/"
"formal-lineage-exact9-principal-20260907-01/app/"
"aios_task_principal_admission.py"
)
BASE_PATH = Path(
os.environ.get("AIOS_GOVERNED_EXACT9_ADMISSION_BASE", DEFAULT_EXACT9_ADMISSION)
)
if not BASE_PATH.is_file():
raise RuntimeError(f"GOVERNED_EXACT9_BASE_MISSING:{BASE_PATH}")
_spec = importlib.util.spec_from_file_location(
"_aios_task_principal_exact9_base_for_governed", BASE_PATH
)
if _spec is None or _spec.loader is None:
raise RuntimeError("GOVERNED_EXACT9_BASE_SPEC_INVALID")
_base = importlib.util.module_from_spec(_spec)
_spec.loader.exec_module(_base)
# Preserve the complete existing admission API surface.
for _name, _value in _base.__dict__.items():
if not _name.startswith("__"):
globals()[_name] = _value
_legacy = _base._legacy
_ORIGINAL_SCOPE_AND_TRUST = _legacy._scope_and_trust
_ORIGINAL_ISSUE = _legacy.TaskPrincipalIssuer.issue
_GOVERNED_RUNTIME_ADAPTER: GovernedScopeRuntimeAdapter | None = None
def _audience(trust: Mapping[str, Any], operation: str) -> str:
bridge_id = str(trust.get("bridge_id", "")).strip()
operation = str(operation).strip()
if not bridge_id or not operation:
raise RuntimeError("GOVERNED_AUDIENCE_INPUT_INVALID")
return f"aios.gitops.bridge.v2/{bridge_id}/{operation}"
def set_governed_runtime_adapter(
adapter: GovernedScopeRuntimeAdapter | None,
) -> None:
global _GOVERNED_RUNTIME_ADAPTER
_GOVERNED_RUNTIME_ADAPTER = adapter
def governed_runtime_adapter() -> GovernedScopeRuntimeAdapter | None:
return _GOVERNED_RUNTIME_ADAPTER
def _governed_scope_and_trust(
con, trust, task, now, *, lease_token=None, pepper=None
):
def _static():
return _legacy._static_scope_and_trust(con, trust, task)
def _governed():
if _GOVERNED_RUNTIME_ADAPTER is None:
# Preserve the original denial when governed runtime mode is off.
_legacy._fail("SCOPE_TASK_BINDING_MISMATCH")
execution = _legacy._execution(task)
return _GOVERNED_RUNTIME_ADAPTER.resolve_row(
task=task, execution=execution, trust=trust, now=now
)
scope = static_first_scope(
static_resolver=_static,
governed_resolver=_governed,
)
claim_digest = _legacy._claim(task, lease_token, pepper, now)
_legacy._check_revocations(con, task, scope, trust, None)
return scope, claim_digest
def _maybe_clamp_request_ttl(self, request):
if _GOVERNED_RUNTIME_ADAPTER is None:
return request
x = _legacy._json_object(request, "ISSUE_REQUEST_INVALID")
task_id = _legacy._text(x.get("task_id"), "task_id")
ttl = _legacy._integer(
x.get("requested_ttl_seconds"), "requested_ttl_seconds", 1, 300
)
now = _legacy._now_epoch(self.clock)
with _legacy._db(self.db_path) as con:
_legacy._ready(con)
task = con.execute(
"SELECT * FROM tasks WHERE task_id=?", (task_id,)
).fetchone()
if task is None:
return request
try:
_legacy._static_scope_and_trust(con, self.trust, task)
# Historical exact9/static path: retain original TTL semantics.
return request
except Exception as exc:
if not is_exact_scope_binding_mismatch(exc):
raise
execution = _legacy._execution(task)
scope = _GOVERNED_RUNTIME_ADAPTER.resolve_row(
task=task, execution=execution, trust=self.trust, now=now
)
lease_expires_at = _legacy._lease_expiry(task["lease_expires_at"])
expires_at = clamp_assertion_expiry(
normal_expires_at=now + ttl,
scope=scope,
task_lease_expires_at=lease_expires_at,
now=now,
)
effective_ttl = int(expires_at) - int(now)
if effective_ttl < 1 or effective_ttl > ttl:
_legacy._fail("TTL_INVALID")
# Preserve all caller fields; change only the TTL downward.
adjusted = dict(x)
adjusted["requested_ttl_seconds"] = effective_ttl
return adjusted
def _governed_issue(self, request, task_lease):
return _ORIGINAL_ISSUE(
self, _maybe_clamp_request_ttl(self, request), task_lease
)
# Patch ONLY the legacy entrypoints used by issue() and verify().
_legacy._scope_and_trust = _governed_scope_and_trust
_legacy.TaskPrincipalIssuer.issue = _governed_issue
# Also expose the patched symbols through this admission module for diagnostics.
globals()["_scope_and_trust"] = _governed_scope_and_trust
globals()["TaskPrincipalIssuer"] = _legacy.TaskPrincipalIssuer
_cfg = config_from_environment()
if _cfg is not None:
set_governed_runtime_adapter(
GovernedScopeRuntimeAdapter(
_cfg,
audience_builder=_audience,
operations=dict(_legacy.OPERATIONS),
)
)
GOVERNED_RUNTIME_SCOPE_OVERLAY = {
"schema": "aios.governed-runtime-scope-overlay.v1",
"base_admission": str(BASE_PATH),
"enabled": _cfg is not None,
}
@@ -0,0 +1,289 @@
from __future__ import annotations
"""Read-only live adapters for governed runtime TaskPrincipal scopes."""
from dataclasses import dataclass
from pathlib import Path
from typing import Any, Callable, Mapping
import json
import os
import sqlite3
import stat
from governed_runtime_scope import (
ExecutionSnapshot, GovernedRuntimeScopeProvider, GrantSnapshot,
TaskSnapshot, TrustSnapshot, sha256_json,
)
class GovernedAdapterError(RuntimeError):
pass
def _value(obj: Any, name: str, default: Any = None) -> Any:
if isinstance(obj, Mapping):
return obj.get(name, default)
try:
return obj[name]
except (KeyError, IndexError, TypeError):
return getattr(obj, name, default)
def _safe_regular(
path: Path, *, allow_group_write_gid: int | None = None
) -> os.stat_result:
st = os.lstat(path)
if stat.S_ISLNK(st.st_mode) or not stat.S_ISREG(st.st_mode):
raise GovernedAdapterError(f"UNSAFE_FILE:{path}")
mode = stat.S_IMODE(st.st_mode)
if mode & 0o002:
raise GovernedAdapterError(f"WORLD_WRITABLE_FILE:{path}")
if mode & 0o020 and (
allow_group_write_gid is None or st.st_gid != int(allow_group_write_gid)
):
raise GovernedAdapterError(f"UNEXPECTED_GROUP_WRITABLE_FILE:{path}")
return st
def _safe_plan_dir(path: Path, *, require_root_owner: bool = True) -> os.stat_result:
st = os.lstat(path)
if stat.S_ISLNK(st.st_mode) or not stat.S_ISDIR(st.st_mode):
raise GovernedAdapterError(f"UNSAFE_PLAN_DIR:{path}")
if require_root_owner and st.st_uid != 0:
raise GovernedAdapterError(f"PLAN_DIR_NOT_ROOT_OWNED:{path}")
if stat.S_IMODE(st.st_mode) & 0o022:
raise GovernedAdapterError(f"PLAN_DIR_WRITABLE_BY_GROUP_OR_WORLD:{path}")
return st
class ReadOnlyTaskAdmissionGrantStore:
def __init__(
self, db_path: str | Path, *,
allowed_group_write_gid: int | None = None
):
self.db_path = Path(db_path)
self.allowed_group_write_gid = allowed_group_write_gid
def _connect(self) -> sqlite3.Connection:
_safe_regular(
self.db_path, allow_group_write_gid=self.allowed_group_write_gid
)
con = sqlite3.connect(
f"file:{self.db_path.as_posix()}?mode=ro",
uri=True, timeout=3.0
)
con.row_factory = sqlite3.Row
return con
def require_current_grant(
self, *, task_id: str, execution_digest: str, repository: str,
target_branch: str, generation: int, now: int
) -> Mapping[str, Any]:
candidates: list[tuple[tuple[int, int, str], dict[str, Any]]] = []
with self._connect() as con:
rows = con.execute(
"SELECT grant_id, grant_json FROM task_admission_grants"
).fetchall()
for row in rows:
try:
grant = json.loads(str(row["grant_json"]))
except Exception as exc:
raise GovernedAdapterError("GRANT_STORE_JSON_INVALID") from exc
if grant.get("schema") != "aios.task-admission-grant.v1":
continue
if (
str(grant.get("task_id")) != str(task_id)
or str(grant.get("execution_digest")) != str(execution_digest)
or str(grant.get("repository")) != str(repository)
or str(grant.get("target_branch")) != str(target_branch)
or int(grant.get("generation", 0)) != int(generation)
):
continue
if str(grant.get("grant_id")) != str(row["grant_id"]):
raise GovernedAdapterError("GRANT_ID_STORAGE_MISMATCH")
expires = int(grant.get("expires_at", 0))
issued = int(grant.get("issued_at", 0))
if expires <= int(now):
continue
candidates.append(((issued, expires, str(grant["grant_id"])), grant))
if not candidates:
raise GovernedAdapterError("CURRENT_ADMITTED_GRANT_NOT_FOUND")
candidates.sort(key=lambda item: item[0], reverse=True)
best_key, best = candidates[0]
if len(candidates) > 1 and candidates[1][0][:2] == best_key[:2]:
raise GovernedAdapterError("CURRENT_ADMITTED_GRANT_AMBIGUOUS")
return best
class ReadOnlyExecutionPlanStore:
def __init__(self, root: str | Path, *, require_root_owner: bool = True):
self.root = Path(root)
self.require_root_owner = require_root_owner
def require_plan(self, execution_digest: str) -> Mapping[str, Any]:
_safe_plan_dir(self.root, require_root_owner=self.require_root_owner)
digest = str(execution_digest).strip().lower()
if len(digest) != 64 or any(c not in "0123456789abcdef" for c in digest):
raise GovernedAdapterError("EXECUTION_DIGEST_INVALID")
path = self.root / f"{digest}.json"
st = _safe_regular(path)
if self.require_root_owner and st.st_uid != 0:
raise GovernedAdapterError("EXECUTION_PLAN_NOT_ROOT_OWNED")
if stat.S_IMODE(st.st_mode) & 0o022:
raise GovernedAdapterError("EXECUTION_PLAN_WRITABLE_BY_GROUP_OR_WORLD")
try:
value = json.loads(path.read_text(encoding="utf-8"))
except Exception as exc:
raise GovernedAdapterError("EXECUTION_PLAN_JSON_INVALID") from exc
if not isinstance(value, Mapping):
raise GovernedAdapterError("EXECUTION_PLAN_NOT_OBJECT")
if sha256_json(value) != digest:
raise GovernedAdapterError("EXECUTION_PLAN_DIGEST_MISMATCH")
return value
@dataclass(frozen=True)
class GovernedScopeRuntimeConfig:
grant_db: Path
plan_dir: Path
grant_db_allowed_group_gid: int | None = None
require_root_plan_owner: bool = True
class GovernedScopeRuntimeAdapter:
def __init__(
self, config: GovernedScopeRuntimeConfig, *,
audience_builder: Callable[[Mapping[str, Any], str], str],
provider: GovernedRuntimeScopeProvider | None = None,
operations: Mapping[str, str] | None = None,
):
self.grants = ReadOnlyTaskAdmissionGrantStore(
config.grant_db,
allowed_group_write_gid=config.grant_db_allowed_group_gid
)
self.plans = ReadOnlyExecutionPlanStore(
config.plan_dir, require_root_owner=config.require_root_plan_owner
)
self.provider = provider or GovernedRuntimeScopeProvider()
self.audience_builder = audience_builder
self.operations = dict(operations or {})
def resolve_row(
self, *, task: Any, execution: Any,
trust: Mapping[str, Any], now: int
) -> dict[str, Any]:
task_id = str(_value(task, "task_id", "")).strip()
status = str(_value(task, "status", "")).strip()
exec_snap = ExecutionSnapshot(
task_id=task_id,
external_job_id=str(_value(execution, "external_job_id", "")).strip(),
repository=str(_value(execution, "repository", "")).strip(),
target_branch=str(_value(execution, "target_branch", "")).strip(),
generation=int(_value(execution, "generation", 0)),
execution_digest=str(_value(execution, "execution_digest", "")).strip(),
)
grant_raw = self.grants.require_current_grant(
task_id=task_id,
execution_digest=exec_snap.execution_digest,
repository=exec_snap.repository,
target_branch=exec_snap.target_branch,
generation=exec_snap.generation,
now=now,
)
caps = (
grant_raw.get("capabilities")
or grant_raw.get("requested_capabilities")
or []
)
files = grant_raw.get("file_allowlist") or []
grant = GrantSnapshot(
grant_id=str(grant_raw["grant_id"]),
status="ADMITTED",
task_id=str(grant_raw["task_id"]),
repository=str(grant_raw["repository"]),
authority_base_oid=str(grant_raw["authority_base_oid"]),
target_branch=str(grant_raw["target_branch"]),
generation=int(grant_raw["generation"]),
execution_digest=str(grant_raw["execution_digest"]),
requested_capabilities=tuple(map(str, caps)),
file_allowlist=tuple(map(str, files)),
expires_at=int(grant_raw["expires_at"]),
)
plan = self.plans.require_plan(exec_snap.execution_digest)
trust_manifest = dict(trust)
trust_snap = TrustSnapshot(
issuer_id=str(trust.get("issuer_id", "")),
key_id=str(trust.get("key_id", "")),
public_key=str(trust.get("public_key", "")),
purpose=str(trust.get("purpose", "")),
bridge_id=str(trust.get("bridge_id", "")),
scope_manifest_sha256=sha256_json(trust_manifest),
manifest=trust_manifest,
)
derived = self.provider.resolve(
task=TaskSnapshot(task_id, status),
execution=exec_snap,
grant=grant,
plan=plan,
trust=trust_snap,
now=int(now),
)
if self.operations:
allowed = set(derived.capabilities)
audiences = sorted({
str(self.audience_builder(trust, operation))
for operation, capability in self.operations.items()
if capability in allowed
})
else:
# Backward-compatible generic mode. Live issuer integration should
# supply OPERATIONS so audience identities are operation-level.
audiences = sorted({
str(self.audience_builder(trust, capability))
for capability in derived.capabilities
})
if not audiences or any(not a for a in audiences):
raise GovernedAdapterError("AUDIENCE_BUILD_FAILED")
return {
"scope_id": derived.scope_id,
"task_id": derived.task_id,
"external_job_id": derived.external_job_id,
"repository": derived.repository,
"target_branch": derived.target_branch,
"generation": derived.generation,
"execution_digest": derived.execution_digest,
"capabilities_json": json.dumps(
list(derived.capabilities), ensure_ascii=False,
sort_keys=True, separators=(",", ":")
),
"audiences_json": json.dumps(
audiences, ensure_ascii=False,
sort_keys=True, separators=(",", ":")
),
"trust_manifest_sha256": derived.trust_manifest_sha256,
"grant_id": derived.grant_id,
"grant_expires_at": derived.grant_expires_at,
"scope_origin": derived.origin,
}
def config_from_environment() -> GovernedScopeRuntimeConfig | None:
if os.environ.get("AIOS_GOVERNED_RUNTIME_SCOPE_ENABLED", "").strip() != "1":
return None
db = os.environ.get("AIOS_GOVERNED_TASK_ADMISSION_DB", "").strip()
plan = os.environ.get("AIOS_GOVERNED_EXECUTION_PLAN_DIR", "").strip()
if not db or not plan:
raise GovernedAdapterError("GOVERNED_SCOPE_ENV_INCOMPLETE")
gid_raw = os.environ.get(
"AIOS_GOVERNED_GRANT_DB_ALLOWED_GROUP_GID", ""
).strip()
return GovernedScopeRuntimeConfig(
grant_db=Path(db),
plan_dir=Path(plan),
grant_db_allowed_group_gid=int(gid_raw) if gid_raw else None,
)
+315
View File
@@ -0,0 +1,315 @@
from __future__ import annotations
"""Pure fail-closed runtime scope derivation for newly authorized governed tasks.
Supports:
- aios.task-admission.execution-plan.v1
- aios.task-admission-execution-plan.v2 (live Round-3)
The ORIGINAL materialized plan mapping is always hashed for execution identity.
Schema adaptation is read-only parsing and never rewrites canonical plan bytes.
"""
from dataclasses import dataclass
from typing import Any, Mapping, Sequence
import hashlib
import json
import re
HEX64 = re.compile(r"^[0-9a-f]{64}$")
OID40 = re.compile(r"^[0-9a-f]{40}$")
TASK_RE = re.compile(r"^[A-Za-z0-9][A-Za-z0-9._:@/-]{2,255}$")
BRANCH_RE = re.compile(r"^(?:agent|task)/[A-Za-z0-9._/-]+$")
JOB_RE = re.compile(r"^[A-Za-z0-9][A-Za-z0-9._:@/-]{2,255}$")
GRANT_RE = re.compile(r"^[A-Za-z0-9][A-Za-z0-9._:@/-]{2,255}$")
PLAN_V1 = "aios.task-admission.execution-plan.v1"
PLAN_V2 = "aios.task-admission-execution-plan.v2"
class GovernedScopeError(RuntimeError):
pass
@dataclass(frozen=True)
class TaskSnapshot:
task_id: str
status: str
@dataclass(frozen=True)
class ExecutionSnapshot:
task_id: str
external_job_id: str
repository: str
target_branch: str
generation: int
execution_digest: str
@dataclass(frozen=True)
class GrantSnapshot:
grant_id: str
status: str
task_id: str
repository: str
authority_base_oid: str
target_branch: str
generation: int
execution_digest: str
requested_capabilities: tuple[str, ...]
file_allowlist: tuple[str, ...]
expires_at: int
@dataclass(frozen=True)
class TrustSnapshot:
issuer_id: str
key_id: str
public_key: str
purpose: str
bridge_id: str
scope_manifest_sha256: str | None = None
manifest: Mapping[str, Any] | None = None
@dataclass(frozen=True)
class RuntimeScope:
scope_id: str
task_id: str
external_job_id: str
repository: str
target_branch: str
generation: int
execution_digest: str
capabilities: tuple[str, ...]
trust_manifest_sha256: str
grant_id: str
grant_expires_at: int
origin: str = "TASK_ADMISSION_GRANT"
def canonical_json_bytes(value: Any) -> bytes:
return json.dumps(
value, ensure_ascii=False, sort_keys=True, separators=(",", ":")
).encode("utf-8")
def sha256_json(value: Any) -> str:
return hashlib.sha256(canonical_json_bytes(value)).hexdigest()
def _task_id(value: Any) -> str:
s = str(value).strip()
if not TASK_RE.fullmatch(s):
raise GovernedScopeError("TASK_ID_INVALID")
return s
def _job_id(value: Any) -> str:
s = str(value).strip()
if not JOB_RE.fullmatch(s) or s.lower() in {
"tbd", "todo", "unknown", "placeholder", "none", "null"
}:
raise GovernedScopeError("EXTERNAL_JOB_ID_INVALID")
return s
def _branch(value: Any) -> str:
s = str(value).strip()
if not BRANCH_RE.fullmatch(s) or ".." in s or "//" in s:
raise GovernedScopeError("TARGET_BRANCH_INVALID")
return s
def _hex64(value: Any, name: str) -> str:
s = str(value).strip().lower()
if not HEX64.fullmatch(s):
raise GovernedScopeError(f"{name.upper()}_INVALID")
return s
def _oid40(value: Any, name: str) -> str:
s = str(value).strip().lower()
if not OID40.fullmatch(s):
raise GovernedScopeError(f"{name.upper()}_INVALID")
return s
def _generation(value: Any) -> int:
try:
n = int(value)
except (TypeError, ValueError):
raise GovernedScopeError("GENERATION_INVALID")
if n < 1:
raise GovernedScopeError("GENERATION_INVALID")
return n
def _normalize_caps(values: Sequence[Any]) -> tuple[str, ...]:
caps = tuple(sorted({str(v).strip() for v in values if str(v).strip()}))
if not caps:
raise GovernedScopeError("CAPABILITIES_EMPTY")
return caps
def _normalize_files(values: Sequence[Any]) -> tuple[str, ...]:
files = []
for raw in values:
p = str(raw).strip().replace("\\", "/")
if not p or p == "." or p.startswith("/") or p.endswith("/"):
raise GovernedScopeError(f"FILE_ALLOWLIST_PATH_INVALID:{p}")
parts = p.split("/")
if any(part in ("", ".", "..") for part in parts) or ".git" in parts:
raise GovernedScopeError(f"FILE_ALLOWLIST_PATH_INVALID:{p}")
files.append(p)
if not files or len(set(files)) != len(files):
raise GovernedScopeError("FILE_ALLOWLIST_INVALID")
return tuple(sorted(files))
def _trust_digest(trust: TrustSnapshot) -> str:
if trust.manifest is not None:
return sha256_json(dict(trust.manifest))
return sha256_json({
"issuer_id": trust.issuer_id,
"key_id": trust.key_id,
"public_key": trust.public_key,
"purpose": trust.purpose,
"bridge_id": trust.bridge_id,
})
def _parse_plan(plan: Mapping[str, Any]) -> dict[str, Any]:
schema = plan.get("schema")
if schema not in {PLAN_V1, PLAN_V2}:
raise GovernedScopeError("EXECUTION_PLAN_SCHEMA_INVALID")
raw_files = plan.get("files") if schema == PLAN_V1 else plan.get("file_allowlist")
files = _normalize_files(raw_files or [])
if schema == PLAN_V1:
purpose = str(plan.get("purpose", "")).strip()
if not purpose:
raise GovernedScopeError("EXECUTION_PLAN_PURPOSE_REQUIRED")
plan_capabilities = None
else:
purpose = PLAN_V2
plan_capabilities = _normalize_caps(plan.get("capabilities") or [])
file_sha256 = plan.get("file_sha256")
if not isinstance(file_sha256, Mapping):
raise GovernedScopeError("EXECUTION_PLAN_FILE_SHA256_REQUIRED")
if set(map(str, file_sha256.keys())) != set(files):
raise GovernedScopeError("EXECUTION_PLAN_FILE_SHA256_COVERAGE_MISMATCH")
for path, digest in file_sha256.items():
_hex64(digest, f"file_sha256:{path}")
return {
"task_id": _task_id(plan.get("task_id")),
"repository": str(plan.get("repository", "")).strip(),
"target_branch": _branch(plan.get("target_branch")),
"generation": _generation(plan.get("generation")),
"external_job_id": _job_id(plan.get("external_job_id")),
"files": files,
"authority_base_oid": _oid40(
plan.get("authority_base_oid"), "authority_base_oid"
),
"purpose": purpose,
"capabilities": plan_capabilities,
}
class GovernedRuntimeScopeProvider:
def resolve(
self, *, task: TaskSnapshot, execution: ExecutionSnapshot,
grant: GrantSnapshot, plan: Mapping[str, Any],
trust: TrustSnapshot, now: int,
) -> RuntimeScope:
task_id = _task_id(task.task_id)
if str(task.status).upper() not in {"CLAIMED", "RUNNING"}:
raise GovernedScopeError("TASK_NOT_CLAIMED")
exec_task = _task_id(execution.task_id)
exec_job = _job_id(execution.external_job_id)
exec_branch = _branch(execution.target_branch)
exec_generation = _generation(execution.generation)
exec_digest = _hex64(execution.execution_digest, "execution_digest")
if execution.repository != "aios/aios":
raise GovernedScopeError("EXECUTION_REPOSITORY_INVALID")
grant_id = str(grant.grant_id).strip()
if not GRANT_RE.fullmatch(grant_id):
raise GovernedScopeError("GRANT_ID_INVALID")
if str(grant.status).upper() != "ADMITTED":
raise GovernedScopeError("GRANT_NOT_ADMITTED")
if int(now) >= int(grant.expires_at):
raise GovernedScopeError("GRANT_EXPIRED")
grant_task = _task_id(grant.task_id)
grant_branch = _branch(grant.target_branch)
grant_generation = _generation(grant.generation)
grant_digest = _hex64(grant.execution_digest, "grant_execution_digest")
grant_base = _oid40(grant.authority_base_oid, "grant_authority_base_oid")
grant_caps = _normalize_caps(grant.requested_capabilities)
grant_files = _normalize_files(grant.file_allowlist)
if grant.repository != "aios/aios":
raise GovernedScopeError("GRANT_REPOSITORY_INVALID")
p = _parse_plan(plan)
# Hash the ORIGINAL plan object. v2 field adaptation is parsing only.
if sha256_json(plan) != exec_digest:
raise GovernedScopeError("EXECUTION_PLAN_DIGEST_MISMATCH")
expected = (
task_id, execution.repository, exec_branch, exec_generation, exec_digest
)
if (
exec_task, grant.repository, grant_branch, grant_generation, grant_digest
) != expected:
raise GovernedScopeError("GRANT_EXECUTION_BINDING_MISMATCH")
if grant_task != task_id:
raise GovernedScopeError("GRANT_TASK_BINDING_MISMATCH")
if (
p["task_id"], p["repository"], p["target_branch"], p["generation"]
) != (task_id, execution.repository, exec_branch, exec_generation):
raise GovernedScopeError("PLAN_TASK_BINDING_MISMATCH")
if p["external_job_id"] != exec_job:
raise GovernedScopeError("PLAN_EXTERNAL_JOB_BINDING_MISMATCH")
if p["files"] != grant_files:
raise GovernedScopeError("PLAN_FILE_ALLOWLIST_MISMATCH")
if p["authority_base_oid"] != grant_base:
raise GovernedScopeError("PLAN_AUTHORITY_BASE_MISMATCH")
if p["capabilities"] is not None and p["capabilities"] != grant_caps:
raise GovernedScopeError("PLAN_CAPABILITIES_MISMATCH")
if not trust.issuer_id or not trust.key_id or not trust.public_key:
raise GovernedScopeError("TRUST_IDENTITY_INVALID")
if "TASK_PRINCIPAL" not in str(trust.purpose).upper().replace("-", "_"):
raise GovernedScopeError("TRUST_PURPOSE_INVALID")
if not str(trust.bridge_id).strip():
raise GovernedScopeError("TRUST_BRIDGE_ID_INVALID")
trust_digest = _trust_digest(trust)
if trust.scope_manifest_sha256 is not None:
expected_trust_digest = _hex64(
trust.scope_manifest_sha256, "scope_manifest_sha256"
)
if expected_trust_digest != trust_digest:
raise GovernedScopeError("TRUST_MANIFEST_DIGEST_MISMATCH")
return RuntimeScope(
scope_id=(
f"governed-runtime:{task_id}:g{exec_generation}:"
f"{exec_digest[:16]}"
),
task_id=task_id,
external_job_id=exec_job,
repository=execution.repository,
target_branch=exec_branch,
generation=exec_generation,
execution_digest=exec_digest,
capabilities=grant_caps,
trust_manifest_sha256=trust_digest,
grant_id=grant_id,
grant_expires_at=int(grant.expires_at),
)
+70
View File
@@ -0,0 +1,70 @@
{
"allowed_peer_cgroups": ["/system.slice/fixture-c5-builder.service"],
"allowed_peer_executable_prefix": "/usr/bin/python3",
"allowed_peer_runtime_prefix": "/opt/aios/gitops-model-api/",
"allowed_peer_runtime_suffix": "/runtime/app/aios_model_api_staging.py",
"allowed_peer_uid": 1000,
"c5_contract": {
"accepted_base_oid": "e5f8209f57b43fc39e55c4555776e6362ce326eb",
"authority_base_oid": "b915386ccbed46a7ae77220887da8ae666649a79",
"rollback_artifact_sha256": "0dfc0461f7f9aa25cfbfc37008dcbd2d48d5341383574163b7bba102852062c4",
"rollback_target_oid": "e5f8209f57b43fc39e55c4555776e6362ce326eb",
"rollback_version": "V2R2C4",
"task_id": "fixture-c5-source-admission"
},
"evidence_layout": {
"artifact": "{version}/artifact.tar.gz",
"canary": "{version}/canary.json",
"gitea": "{version}/gitea.json",
"manifest": "{version}/manifest.json",
"package_closure": "{version}/checksums.sha256",
"policy": "{version}/trusted-release-policy.json",
"rollback": "{version}/rollback.json",
"secret_scan": "{version}/secret-scan.json",
"tests": "{version}/tests.json"
},
"governed_recipes": {
"V2R2C3": {
"base_oid": "5cf20128d8be2d10aef87b09f8f7a2aecb2000f3",
"builder_engineering_oid": "49d30a2510038975ddddc5e56c1b9fdc44fc85fe",
"candidate_id": "fixture-v2r2c3-candidate",
"derived_from_v2r2c2_attestation": "81874204c86a3f4d8578a78d31328e15ebed8b69f8696a26038e1caa91db1b35",
"mode": "governed-derived",
"purpose": "AIOS_TRUSTED_RELEASE_BUILDER",
"source_commit": "4420b2afcb2a1eaf821ed2c4acc86331e063eb5d",
"task_id": "fixture-v2r2c3-task",
"version": "V2R2C3"
}
},
"maximum_request_age_seconds": 60,
"operation": "BuildTrustedCandidateV1",
"readiness_operation": "ProbeTrustedCandidateBuilderV1",
"recipes": {
"V2R2C2": {
"base_oid": "0136a5342618d8d6e31424e81c83cb9235d2115b",
"candidate_id": "fixture-v2r2c2-candidate",
"mode": "fixed",
"purpose": "AIOS_TRUSTED_RELEASE_BUILDER",
"source_commit": "4642b74af62c89a505ae49241e805a5a88ca897f",
"task_id": "fixture-v2r2c2-task",
"version": "V2R2C2"
},
"V2R2C3": {
"derives_from": "V2R2C2",
"mode": "governed-derived-only",
"purpose": "AIOS_TRUSTED_RELEASE_BUILDER",
"version": "V2R2C3"
},
"V2R2C5": {
"mode": "governed-source-admission-only",
"purpose": "AIOS_TRUSTED_RELEASE_BUILDER",
"version": "V2R2C5"
}
},
"repository": "aios/aios",
"request_schema": "aios.trusted-candidate-builder.request.v1",
"request_timeout_seconds": 30,
"required_checks": ["authoritative_git", "artifact", "canary", "gitea", "manifest", "package_closure", "rollback", "secret_scan", "tests"],
"response_schema": "aios.trusted-candidate-builder.response.v1",
"schema": "aios.trusted-candidate-builder-policy.c5.v1"
}
@@ -16,6 +16,7 @@ from app.task_plane.live_governed_writer_adapters import (
LiveProtocolConfig,
LiveProtocolConflict,
LiveProtocolError,
IssuerResponseError,
LiveTaskPrincipalClient,
SecretHandleStore,
)
@@ -212,6 +213,30 @@ class LiveAgentTaskContractTests(unittest.TestCase):
"TaskLease lease-token-secret-12345",
)
def test_issuer_rejection_exposes_only_stable_response_facts(self):
class Rejected(RecordingTransport):
def request(self, method, url, **kwargs):
if url.endswith("/v1/task-principal/assertions"):
return HttpResult(403, {
"error": "EXECUTION_PLAN_SCHEMA_INVALID",
"detail": "token=secret-value",
})
return super().request(method, url, **kwargs)
handle = self.secrets.put("task-lease", "lease-token-secret-12345")
principal = LiveTaskPrincipalClient(
self.cfg, transport=Rejected(), secrets_store=self.secrets,
)
with self.assertRaises(IssuerResponseError) as caught:
principal.issue_assertion(
task_id="task-live-1", lease_handle=handle,
operation="acquireWriterLease", bridge_request={}, request_id="req-1",
)
self.assertEqual(caught.exception.status, 403)
self.assertEqual(caught.exception.error_code, "EXECUTION_PLAN_SCHEMA_INVALID")
self.assertEqual(caught.exception.operation, "acquireWriterLease")
self.assertNotIn("secret-value", str(caught.exception))
class LiveBridgeContinuationTests(unittest.TestCase):
def setUp(self):
+251
View File
@@ -0,0 +1,251 @@
"""Isolated C5 build contract tests; these are not deployment readiness receipts."""
import dataclasses
import hashlib
import json
from pathlib import Path
from types import SimpleNamespace
import pytest
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
from aios_trusted_candidate_builder import (
BuilderError, BuilderKeyring, DeterministicEvidenceCollector, PeerIdentity,
ServerOwnedEvidenceSource, TrustedCandidateBuilder, TrustedCandidateBuilderConfig,
UnixTrustedCandidateBuilderClient, load_builder_policy,
)
from release_source_admission import ReleaseSourceAdmitter
from silent_release_hardening import (
BuilderAttestation, BuilderAttestationKeyRecord, BuilderAttestationKeyRing,
BuilderAttestationVerifier, ReleasePlan, TrustedCandidateBundle,
PreSwitchValidator, StaleApprovalError,
)
from silent_release_controller import ReleaseCandidate
from test_release_source_admission import FakeBackend, REF, make_store
from test_trusted_candidate_builder import NOW, passing_bundle
ROOT = Path(__file__).resolve().parents[1]
SOURCE = "2" * 40 # A test publication, never the prepublication candidate OID.
AUTHORITY_BASE = "b915386ccbed46a7ae77220887da8ae666649a79"
ACCEPTED_BASE = "e5f8209f57b43fc39e55c4555776e6362ce326eb"
C5_POLICY = ROOT / "tests/fixtures/trusted-candidate-builder-policy-c5.json"
def canonical(value):
return json.dumps(value, sort_keys=True, separators=(",", ":")).encode()
@pytest.fixture
def c5(tmp_path):
config = TrustedCandidateBuilderConfig.from_policy(load_builder_policy(
C5_POLICY
))
raw = passing_bundle("V2R2C5", {
"base_oid": SOURCE, "candidate_id": "rc-v2r2c5-" + SOURCE[:12]
})
candidate = ReleaseCandidate.from_dict(raw["candidate"])
plan = ReleasePlan.from_dict(raw["release_plan"])
store = make_store(tmp_path)
record = ReleaseSourceAdmitter(store, FakeBackend(
main=AUTHORITY_BASE, tree=candidate.tree_oid,
)).admit(version="V2R2C5", task_id=config.c5_contract["task_id"],
source_ref=REF, source_commit=SOURCE)
config = dataclasses.replace(config, source_admission_resolver=store.resolve_recipe)
recipe = config.resolve_recipe("V2R2C5", None, SOURCE)
evidence = tmp_path / "evidence"
(tmp_path / "repo.git").mkdir()
version = evidence / "V2R2C5"
version.mkdir(parents=True)
evidence.chmod(0o700)
def write(name, content):
data = content if isinstance(content, bytes) else canonical(content)
path = version / name
path.write_bytes(data)
path.chmod(0o600)
return hashlib.sha256(data).hexdigest()
tests = {"passed": True, "receipt": "isolated-tests"}
canary = {"passed": True, "receipt": "isolated-canary"}
git = {"consistent": True, "repository": "aios/aios",
"source_commit": SOURCE, "tree_oid": candidate.tree_oid}
gitea = {**git, "remote_ref_oid": SOURCE}
rollback = {
"ready": True, "source_commit": SOURCE,
"rollback_sha256": plan.deployment.rollback_sha256,
}
artifact = write("artifact.tar.gz", b"isolated C5 artifact")
manifest = write("manifest.json", {"artifact_sha256": artifact})
closure = write("checksums.sha256", b"isolated closure")
policy = write("trusted-release-policy.json", b"isolated release policy")
for name, value in (("tests", tests), ("canary", canary), ("gitea", gitea),
("rollback", rollback),
("secret-scan", {"passed": True, "artifact_sha256": artifact})):
write(name + ".json", value)
candidate.artifact_sha256 = artifact
candidate.manifest_sha256 = manifest
candidate.tests, candidate.canary = tests, canary
candidate.git_consistency, candidate.gitea_consistency = git, gitea
plan = dataclasses.replace(
plan,
deployment=dataclasses.replace(plan.deployment, artifact_sha256=artifact,
manifest_sha256=manifest, package_closure_sha256=closure, policy_sha256=policy,
rollback_target={"version": "V2R2C4", "target_oid": ACCEPTED_BASE,
"artifact_sha256": config.c5_contract["rollback_artifact_sha256"]}),
tag=dataclasses.replace(plan.tag, version="V2R2C5"),
finalization=dataclasses.replace(plan.finalization, version="V2R2C5"),
policy_sha256=policy,
)
write("receipt.json", {"schema": "aios.trusted-candidate-builder.evidence.v1",
"version": "V2R2C5", "candidate": candidate.to_dict(), "release_plan": plan.to_dict()})
git_state = {"tree": candidate.tree_oid, "rejected_base": None, "queries": []}
def git_runner(argv, **_kwargs):
git_state["queries"].append(argv)
if "rev-parse" in argv:
return SimpleNamespace(stdout=git_state["tree"] + "\n", returncode=0)
return SimpleNamespace(stdout="", returncode=int(argv[-2] == git_state["rejected_base"]))
source = ServerOwnedEvidenceSource(config, tmp_path / "repo.git", evidence,
expected_owner_uid=None, git_runner=git_runner)
return SimpleNamespace(config=config, recipe=recipe, source=source, record=record,
store=store, git=git_state, write=write, rollback=rollback, version=version)
def test_c5_authenticated_admission_to_signed_build(c5):
key = Ed25519PrivateKey.generate()
class Signer:
key_id = "isolated-c5-builder"
key_generation = 1
def sign_for_purpose(self, purpose, payload):
assert purpose == "AIOS_TRUSTED_RELEASE_BUILDER"
return key.sign(payload)
config = c5.config
keyring = BuilderKeyring(builder_hmac=b"h" * 32)
server = TrustedCandidateBuilder(config, keyring,
collector=DeterministicEvidenceCollector(config, evidence_source=c5.source),
signer=Signer(), peer_identity_resolver=lambda _: PeerIdentity(pid=77, uid=1000, gid=1000),
cgroup_resolver=lambda _: config.allowed_peer_cgroups[0],
process_identity_resolver=lambda _: ("/usr/bin/python3.12", (
"/usr/bin/python3", "/opt/aios/gitops-model-api/test/runtime/app/aios_model_api_staging.py")),
clock=lambda: NOW)
client = UnixTrustedCandidateBuilderClient(config, keyring, clock=lambda: NOW)
response = server.handle_request(client.create_request("V2R2C5", None, SOURCE,
nonce="c5-isolated-contract-000000000001"), connection=object())
public = key.public_key().public_bytes(serialization.Encoding.Raw, serialization.PublicFormat.Raw)
verifier = BuilderAttestationVerifier(BuilderAttestationKeyRing([
BuilderAttestationKeyRecord(key_id=Signer.key_id, public_key=public, algorithm="Ed25519",
purpose="AIOS_TRUSTED_RELEASE_BUILDER", generation=1,
not_before="2020-01-01T00:00:00Z", expires_at="2099-01-01T00:00:00Z", status="ACTIVE")
], require_attestation_metadata=True))
raw = response["silent_release_bundle"]
bundle = TrustedCandidateBundle(ReleaseCandidate.from_dict(raw["candidate"]),
ReleasePlan.from_dict(raw["release_plan"]), raw["checks"],
BuilderAttestation.from_dict(raw["attestation"], require_fingerprint=True))
assert bundle.validate(verifier).candidate.source_commit == SOURCE
assert response["task_id"] == c5.config.c5_contract["task_id"]
queried_bases = {q[-2] for q in c5.git["queries"] if "merge-base" in q}
assert queried_bases == {AUTHORITY_BASE, ACCEPTED_BASE}
@pytest.mark.parametrize("base", [AUTHORITY_BASE, ACCEPTED_BASE])
def test_c5_rejects_either_broken_lineage(c5, base):
c5.git["rejected_base"] = base
with pytest.raises(BuilderError, match="COLLECTOR_GIT_MISMATCH"):
c5.source("V2R2C5", c5.recipe)
def test_c5_rejects_tree_disagreement_with_authenticated_admission(c5):
c5.git["tree"] = "f" * 40
with pytest.raises(BuilderError, match="COLLECTOR_GIT_MISMATCH"):
c5.source("V2R2C5", c5.recipe)
@pytest.mark.parametrize("field,value", [
("target_oid", "f" * 40),
("version", "V2R2C3"),
("artifact_sha256", "f" * 64),
])
def test_c5_rejects_wrong_rollback_target(c5, field, value):
receipt = json.loads((c5.version / "receipt.json").read_text())
receipt["release_plan"]["deployment"]["rollback_target"][field] = value
c5.write("receipt.json", receipt)
with pytest.raises(BuilderError, match="COLLECTOR_ROLLBACK_MISMATCH"):
c5.source("V2R2C5", c5.recipe)
def test_c5_rejects_changed_artifact(c5):
c5.write("artifact.tar.gz", b"substituted artifact")
with pytest.raises(BuilderError, match="COLLECTOR_DIGEST_MISMATCH"):
c5.source("V2R2C5", c5.recipe)
@pytest.mark.parametrize("version", ["V2R2C4", "V2R2C6", "unknown"])
def test_c5_policy_rejects_unknown_recipe_before_resolver(c5, version):
def unexpected(*args):
pytest.fail("Unknown recipe reached source admission resolver")
config = dataclasses.replace(c5.config, source_admission_resolver=unexpected)
with pytest.raises(BuilderError, match="RECIPE_UNKNOWN"):
config.resolve_recipe(version, None, SOURCE)
def test_old_policy_can_consume_new_authenticated_c5_tree_projection(c5):
config = TrustedCandidateBuilderConfig.from_policy(load_builder_policy(
ROOT / "config/gitops/trusted-candidate-builder-policy.json"
))
config = dataclasses.replace(config, source_admission_resolver=c5.store.resolve_recipe)
assert config.resolve_recipe("V2R2C5", None, SOURCE)["tree_oid"] == c5.record["tree_oid"]
@pytest.mark.parametrize("field,value", [
("source_commit", "f" * 40), ("version", "V2R2C4"),
("tree_oid", "invalid"), ("candidate_id", ""),
("purpose", "unapproved-purpose"),
])
def test_c5_rejects_admission_coordinate_substitution(c5, field, value):
admitted = c5.store.resolve_recipe("V2R2C5", None, SOURCE)
admitted[field] = value
config = dataclasses.replace(c5.config, source_admission_resolver=lambda *_: admitted)
expected = "SOURCE_ADMISSION_BINDING_INVALID" if field == "candidate_id" else "SOURCE_ADMISSION_INVALID"
with pytest.raises(BuilderError, match=expected):
config.resolve_recipe("V2R2C5", None, SOURCE)
@pytest.mark.parametrize("field,value", [
("task_id", ""), ("authority_base_oid", "substituted"),
("accepted_base_oid", "f" * 40), ("rollback_version", "other"),
("rollback_target_oid", "f" * 40), ("rollback_artifact_sha256", "substituted"),
])
def test_c5_policy_rejects_malformed_or_inconsistent_governed_contract(field, value):
policy = dict(load_builder_policy(C5_POLICY))
policy["c5_contract"][field] = value
with pytest.raises(BuilderError, match="POLICY_V2R2C5_CONTRACT_INVALID"):
TrustedCandidateBuilderConfig.from_policy(policy)
def test_rollback_target_is_hashed_and_invalidates_old_approval(c5):
receipt = c5.source("V2R2C5", c5.recipe)
plan = ReleasePlan.from_dict(receipt["release_plan"])
candidate = ReleaseCandidate.from_dict(receipt["candidate"])
changed = dataclasses.replace(plan.deployment,
rollback_target={**plan.deployment.rollback_target, "target_oid": "f" * 40})
assert changed.sha256 != plan.deployment.sha256
assert dataclasses.replace(plan, deployment=changed).sha256 != plan.sha256
approval = SimpleNamespace(candidate_id=candidate.candidate_id, source_commit=SOURCE,
tree_oid=candidate.tree_oid, artifact_sha256=candidate.artifact_sha256,
manifest_sha256=candidate.manifest_sha256,
deployment_plan_sha256=plan.deployment.sha256, policy_sha256=plan.policy_sha256)
with pytest.raises(StaleApprovalError, match="APPROVAL_STALE_REAPPROVAL_REQUIRED"):
PreSwitchValidator(None).validate(candidate, approval, changed, expected_repository="aios/aios")
def test_legacy_deployment_digest_does_not_gain_empty_extension(c5):
plan = ReleasePlan.from_dict(c5.source("V2R2C5", c5.recipe)["release_plan"])
legacy = dataclasses.replace(plan.deployment, rollback_target={})
payload = legacy.canonical_payload
assert "rollback_target" not in payload
assert legacy.sha256 == hashlib.sha256(canonical(payload)).hexdigest()
assert legacy.from_dict(legacy.to_dict()).sha256 == legacy.sha256
+584
View File
@@ -0,0 +1,584 @@
import base64
import copy
import os
from pathlib import Path
from datetime import datetime, timezone
import pytest
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
from cryptography.hazmat.primitives.serialization import Encoding, PublicFormat
import c5_completion_renewal as renewal
def test_registered_bindings_require_digest_and_install_no_live_defaults(monkeypatch):
root = Path.cwd() / 'registered'
value = {
'schema': 'aios.c5.completion-bindings.v1',
'authority_root': str(root / 'c5'),
'state_root': str(root / 'state'),
'owner_key_id': 'SHA256:' + base64.b64encode(b'k' * 32).decode(),
'predecessor_fingerprint': '1' * 64,
'saga_head': '2' * 64,
'historical_root': str(root / 'history'),
'controller_workspace': str(root / 'workspace'),
'authoritative_git_dir': str(root / 'repo.git'),
'runtime_registry_root': str(root / 'registry'),
'runtime_baseline_path': str(root / 'baseline.json'),
'trusted_release_policy_path': str(root / 'policy.json'),
'trusted_release_policy_sha256': '3' * 64,
}
raw = renewal.canonical(value)
monkeypatch.setattr(renewal, '_safe_parents', lambda _path: None)
monkeypatch.setattr(renewal, 'secure_bytes', lambda _path, owner_uid=0: raw)
monkeypatch.setattr(renewal, '_BINDINGS_DIGEST', None)
result = renewal.configure_registered_bindings(
root / 'c5' / 'completion-bindings.json', renewal.digest(raw),
)
assert result == value
assert renewal.AUTHORITY_ROOT == root / 'c5'
assert renewal.STATE_ROOT == root / 'state'
with pytest.raises(renewal.RenewalRejected, match='DIGEST_MISMATCH'):
renewal.configure_registered_bindings(
root / 'c5' / 'completion-bindings.json', 'f' * 64,
)
def signed(payload, key):
raw = renewal.canonical(payload)
return {**payload, 'fingerprint': renewal.digest(raw),
'signature': base64.b64encode(key.sign(raw)).decode()}
@pytest.fixture
def materials(monkeypatch):
key = Ed25519PrivateKey.generate()
public = key.public_key().public_bytes(Encoding.Raw, PublicFormat.Raw)
payload = {'candidate_id': renewal.CID, 'source_commit': renewal.SOURCE,
'tree_oid': renewal.TREE, 'artifact_sha256': renewal.ARTIFACT,
'release_plan_sha256': renewal.PLAN, 'capabilities': renewal.CAPABILITIES,
'issued_at': '2026-10-01T07:47:47Z', 'not_before': '2026-10-01T07:47:47Z',
'expires_at': '2026-10-01T08:47:47Z', 'nonce': 'historical', 'key_id': 'fixture',
'owner_identity': 'fixture-owner', 'trust_domain': 'fixture-domain',
'owner_approval_record_oid': 'a'*40}
old = signed(payload, key)
monkeypatch.setattr(renewal, 'OLD_FP', old['fingerprint'])
successor = {**payload, 'issued_at': '2026-10-01T08:50:00Z',
'not_before': '2026-10-01T08:50:00Z', 'expires_at': '2026-10-01T09:00:00Z', 'nonce': 'fresh'}
return key, public, old, signed(successor, key), renewal.timestamp('2026-10-01T08:51:00Z')
def test_valid_chain_and_append_preserve_original(tmp_path, materials):
key, public, old, new, now = materials
historical = renewal.canonical(old)
path = tmp_path / 'original.json';path.write_bytes(historical)
receipt = renewal.publish_successor(tmp_path, old, new, public, now=now,
registration_digest='1'*64, predecessor_bytes=historical)
assert path.read_bytes() == historical
assert receipt['runtime_effects_allowed'] is False
assert receipt['completion_only'] is True
assert renewal.publish_successor(tmp_path, old, new, public, now=now,
registration_digest='1'*64, predecessor_bytes=historical) == receipt
@pytest.mark.parametrize('field,value', [
('candidate_id','other'), ('source_commit','f'*40), ('tree_oid','f'*40),
('artifact_sha256','f'*64), ('release_plan_sha256','f'*64), ('key_id','other'),
('capabilities',['tag_publish']), ('nonce','historical'),
('expires_at','2026-10-01T08:51:00Z'), ('expires_at','2026-10-01T09:50:00Z'),
('issued_at','2026-10-01T08:52:00Z'), ('not_before','2026-10-01T08:52:00Z'),
])
def test_signed_drift_or_stale_rejected(materials, field, value):
key, public, old, new, now = materials
payload = {k:v for k,v in new.items() if k not in {'fingerprint','signature'}}
payload[field] = value
with pytest.raises(renewal.RenewalRejected):
renewal.validate_successor(old, signed(payload,key), public, now=now)
def test_live_predecessor_cannot_be_superseded(materials):
key, public, old, new, now = materials
with pytest.raises(renewal.RenewalRejected, match='PREDECESSOR_NOT_EXPIRED'):
renewal.validate_successor(old, new, public, now=renewal.timestamp('2026-10-01T08:47:00Z'))
def test_tampered_signature_and_unexpected_field_rejected(materials):
key, public, old, new, now = materials
altered = copy.deepcopy(new);altered['source_commit'] = 'f'*40
with pytest.raises(renewal.RenewalRejected, match='DIGEST_INVALID'):
renewal.validate_successor(old, altered, public, now=now)
altered = {k:v for k,v in new.items() if k not in {'fingerprint','signature'}}
altered['new_authority'] = True
with pytest.raises(renewal.RenewalRejected, match='BINDING_DRIFT'):
renewal.validate_successor(old, signed(altered,key), public, now=now)
def test_conflicting_receipt_is_not_replaced(tmp_path, materials):
_, public, old, new, now = materials
receipt = tmp_path/(new['fingerprint']+'.receipt.json')
receipt.write_bytes(b'old audit');receipt.chmod(0o600)
with pytest.raises(renewal.RenewalRejected, match='APPEND_ONLY_RECORD_CONFLICT'):
renewal.publish_successor(tmp_path, old, new, public, now=now,
registration_digest='1'*64, predecessor_bytes=renewal.canonical(old))
assert receipt.read_bytes() == b'old audit'
def test_symlink_and_world_writable_directory_rejected(tmp_path, materials):
_, public, old, new, now = materials
original = tmp_path/'preserved';original.write_bytes(b'unchanged');original.chmod(0o600)
alias = tmp_path/(new['fingerprint']+'.approval.json');alias.symlink_to(original)
with pytest.raises(OSError):
renewal.publish_successor(tmp_path, old, new, public, now=now,
registration_digest='1'*64, predecessor_bytes=renewal.canonical(old))
assert original.read_bytes() == b'unchanged'
alias.unlink();tmp_path.chmod(0o777)
with pytest.raises(renewal.RenewalRejected, match='UNSAFE_APPEND_DIRECTORY'):
renewal.publish_successor(tmp_path, old, new, public, now=now,
registration_digest='1'*64, predecessor_bytes=renewal.canonical(old))
def test_predecessor_raw_bytes_must_match_signed_parent(tmp_path, materials):
_, public, old, new, now = materials
with pytest.raises(renewal.RenewalRejected, match='PREDECESSOR_BYTES_DRIFT'):
renewal.publish_successor(tmp_path, old, new, public, now=now,
registration_digest='1'*64, predecessor_bytes=b'{}')
assert list(tmp_path.iterdir()) == []
@pytest.fixture
def store_fixture(tmp_path, materials, monkeypatch):
key, public, old, new, now = materials
new_payload = {k:v for k,v in new.items() if k not in {'fingerprint','signature'}}
new_payload['nonce'] = 'a'*64
new = signed(new_payload,key)
class Frozen(datetime):
@classmethod
def now(cls, tz=None):
return datetime.fromtimestamp(now, timezone.utc)
monkeypatch.setattr(renewal,'datetime',Frozen)
class Approval:
def __init__(self,value):self.value=value;self.fingerprint=value['fingerprint']
def to_dict(self):return self.value
@classmethod
def from_dict(cls,value):return cls(value)
class Original:
def __init__(self,root):self.root=root
def load(self,candidate_id):return Approval(old)
def save(self,candidate_id,approval):
assert approval.fingerprint == old['fingerprint']
return self.root/(candidate_id+'.json'),False
origin=tmp_path/'original';origin.mkdir(mode=0o700)
raw=renewal.canonical(old)
(origin/(renewal.CID+'.json')).write_bytes(raw)
(origin/(renewal.CID+'.json')).chmod(0o600)
directory=tmp_path/'renewals';directory.mkdir(mode=0o700)
policy={'candidate_id':renewal.CID,'predecessor_fingerprint':old['fingerprint'],
'approval_nonce':'a'*64,'payload_sha256':'1'*64,
'predecessor_uid':os.geteuid(),'predecessor_sha256':renewal.digest(raw)}
store=renewal.CompletionApprovalStore(Original(origin),directory,public,lambda:policy,Approval)
return store,Approval(new),Approval(old),policy
def test_explicit_store_activation_and_replay(store_fixture):
store,new,old,policy=store_fixture
original=(store.original.root/(renewal.CID+'.json')).read_bytes()
assert store.load(renewal.CID).fingerprint==old.fingerprint
path,created=store.save(renewal.CID,new)
assert created and path.is_file()
assert store.load(renewal.CID).fingerprint==new.fingerprint
assert store.save(renewal.CID,new)[1] is False
assert (store.original.root/(renewal.CID+'.json')).read_bytes()==original
def test_store_rejects_wrong_registered_nonce(store_fixture):
store,new,old,policy=store_fixture
policy['approval_nonce']='b'*64
with pytest.raises(renewal.RenewalRejected,match='REGISTERED_NONCE_MISMATCH'):
store.save(renewal.CID,new)
assert list(store.directory.iterdir())==[]
def test_store_detects_parent_file_change(store_fixture):
store,new,old,policy=store_fixture
path=store.original.root/(renewal.CID+'.json')
path.write_bytes(renewal.canonical({**old.to_dict(),'nonce':'altered'}))
with pytest.raises(renewal.RenewalRejected,match='PREDECESSOR_FILE_DRIFT'):
store.save(renewal.CID,new)
def test_partial_append_is_not_active(store_fixture):
store,new,old,policy=store_fixture
renewal.append_file(store.directory/(new.fingerprint+'.approval.json'),renewal.canonical(new.to_dict()))
assert store.load(renewal.CID).fingerprint==old.fingerprint
def test_active_receipt_tamper_fails_closed(store_fixture):
store,new,old,policy=store_fixture
path,_=store.save(renewal.CID,new)
receipt=__import__('json').loads(path.read_bytes());receipt['runtime_effects_allowed']=True
path.write_bytes(renewal.canonical(receipt))
with pytest.raises(renewal.RenewalRejected,match='SUCCESSOR_ACTIVATION_DRIFT'):
store.load(renewal.CID)
@pytest.fixture
def signer_fixture(tmp_path, materials, monkeypatch):
key,public,old,new,now=materials
class Frozen(datetime):
@classmethod
def now(cls,tz=None):return datetime.fromtimestamp(now,timezone.utc)
monkeypatch.setattr(renewal,'datetime',Frozen)
snapshot=tmp_path/'parent.json';snapshot.write_bytes(renewal.canonical(old));snapshot.chmod(0o600)
policy={'candidate_id':renewal.CID,'predecessor_fingerprint':old['fingerprint'],
'issued_at':now-30,'expires_at':now+900,'gate_observed_at':now-1,
'predecessor_snapshot':str(snapshot),'predecessor_sha256':renewal.digest(snapshot.read_bytes()),
'approval_nonce':'b'*64,'payload_sha256':'1'*64}
class SigningKey:
count=0
def public_key(self):return key.public_key()
def sign(self,raw):self.count+=1;return key.sign(raw)
class Signer(renewal.CompletionSignerMixin):pass
signer=Signer();signer.private_key=SigningKey();signer.authorization_oid='a'*40
signer.policy=type('Policy',(),{'key_id':'fixture','owner_identity':'fixture-owner','trust_domain':'fixture-domain'})()
signer.state_root=tmp_path;(tmp_path/'completion-renewals').mkdir(mode=0o700)
signer.completion_authority=lambda:policy
request={'schema':'aios.exact-release-signer.request.v1','operation':'renew_exact_completion_approval',
'candidate_id':renewal.CID,'predecessor_fingerprint':old['fingerprint']}
return signer,policy,request,old
def test_purpose_signer_signs_once_and_replays(signer_fixture):
signer,policy,request,old=signer_fixture
result=signer.sign_exact_completion_successor(request)
assert result['status']=='SIGNED' and result['completion_only']
assert signer.private_key.count==1
assert result['approval']['nonce']==policy['approval_nonce']
assert signer.sign_exact_completion_successor(request)['status']=='SIGNED_IDEMPOTENT'
assert signer.private_key.count==1
@pytest.mark.parametrize('change',[{'candidate_id':'other'},{'extra':'arbitrary'},{'operation':'sign_anything'}])
def test_purpose_signer_rejects_arbitrary_request_before_sign(signer_fixture,change):
signer,policy,request,old=signer_fixture
with pytest.raises(renewal.RenewalRejected,match='EXACT_SIGNER_RENEWAL_REQUEST_REQUIRED'):
signer.sign_exact_completion_successor({**request,**change})
assert signer.private_key.count==0
@pytest.mark.parametrize('field,value',[('gate_observed_at',0),('expires_at',0),('predecessor_sha256','f'*64),('approval_nonce','historical')])
def test_purpose_signer_rejects_stale_gate_and_preimage_before_sign(signer_fixture,field,value):
signer,policy,request,old=signer_fixture;policy[field]=value
with pytest.raises(renewal.RenewalRejected):signer.sign_exact_completion_successor(request)
assert signer.private_key.count==0
@pytest.fixture
def registered_authority(tmp_path, monkeypatch):
import sys
import types
root = tmp_path / 'registered'; root.mkdir(mode=0o700)
monkeypatch.setattr(renewal, 'AUTHORITY_ROOT', root)
monkeypatch.setattr(renewal, '__file__', str(root / 'c5_completion_renewal.py'))
# pytest scratch parents are not production paths; preserve the real file
# checks but substitute this ancestry fixture only in the isolated test.
monkeypatch.setattr(renewal, '_safe_parents', lambda path: None)
monkeypatch.setattr(renewal.time, 'time', lambda: 1100)
verifier = types.SimpleNamespace(verify=lambda **kw: kw['signature'] == 'fixture-valid')
monkeypatch.setitem(sys.modules, 'c5_staging_installation', types.SimpleNamespace(
RegistryOwnerVerifier=lambda *a, **kw: verifier))
files = {
'c5_completion_renewal.py': b'fixture registered authority module',
'c5_release_execution_host.py': b'fixture typed host',
'silent_release_hardening.py': b'fixture typed API',
'c5_completion_signer.py': b'fixture exact signer wrapper',
'c5_completion_api.py': b'fixture typed composition',
'c5_completion_phase.py': b'fixture fixed completion phase',
'approval-public-keyring.json': b'fixture public keyring',
'original_api.py': b'fixture preserved original API',
'predecessor-approval.json': b'fixture historical bytes',
'historical-production-proof.json': renewal.canonical({
'candidate_id': renewal.CID, 'approval_fingerprint': renewal.OLD_FP,
'release_plan_sha256': renewal.PLAN, 'status': 'COMPLETED',
'public_business_verified': True, 'real_c4_rollback_verified': True, 'c5_restore_verified': True}),
'current-gate.json': renewal.canonical({
'candidate_id': renewal.CID, 'source_commit': renewal.SOURCE,
'artifact_sha256': renewal.ARTIFACT, 'release_plan_sha256': renewal.PLAN,
'saga_revision': 15, 'saga_head': renewal.SAGA_HEAD, 'local_tag_oid': renewal.TAG_OID,
'remote_tag_oid': None, 'runtime_verified': True, 'business_verified': True,
'global_writes_enabled': False, 'observed_at': 1000}),
}
for name in renewal.PHASE_DEPENDENCIES: files[name] = b'fixture signed dependency'
files['source-material.json'] = renewal.canonical({
'status': 'MATERIALIZED', 'task_status': 'SUCCEEDED', 'remote_verified': True,
'source_commit': '1'*40, 'source_tree': '2'*40,
'file_sha256': {n: renewal.digest(files[n]) for n in renewal.RENEWAL_SOURCE_FILES}})
for name, raw in files.items():
(root/name).write_bytes(raw); (root/name).chmod(0o600)
payload = {'schema': 'aios.c5.registered-completion-authority.v1', 'host': 'cn-network-mkt',
'repository': 'aios/aios', 'candidate_id': renewal.CID, 'source_commit': renewal.SOURCE,
'tree_oid': renewal.TREE, 'artifact_sha256': renewal.ARTIFACT, 'release_plan_sha256': renewal.PLAN,
'state_root': str(renewal.STATE_ROOT), 'predecessor_fingerprint': renewal.OLD_FP,
'predecessor_sha256': renewal.digest(files['predecessor-approval.json']), 'predecessor_uid': 1000,
'predecessor_snapshot': str(root/'predecessor-approval.json'), 'approval_nonce': '3'*64,
'issued_at': 1000, 'expires_at': 1600, 'owner_key_id': renewal.OWNER_KEY,
'owner_namespace': 'aios-owner-approval-v1',
'allowed_effects': ['approval_successor_append','baseline_finalize','registry_finalize','tag_publish'],
'runtime_effects_allowed': False,
'files': [{'path': str(root/n), 'sha256': renewal.digest(raw), 'uid': 0} for n, raw in files.items()],
'gate_path': str(root/'current-gate.json'), 'gate_observed_at': 1000,
'governed_source_commit': '1'*40, 'governed_source_tree': '2'*40}
def seal(signature='fixture-valid'):
(root/'contract.json').write_bytes(renewal.canonical({'payload': payload, 'signature': signature}))
(root/'registration.json').write_bytes(renewal.canonical({
'schema': 'aios.c5.completion-registration.v1', 'status': 'REGISTERED',
'payload_sha256': renewal.digest(renewal.canonical(payload)), 'approval_nonce': payload['approval_nonce']}))
(root/'contract.json').chmod(0o600); (root/'registration.json').chmod(0o600)
seal()
return root, payload, seal, renewal.RegisteredCompletionAuthority()
def completion_gate():
gate = {'saga_revision': 20, 'observed_at': 1000, 'completion_journal_suffix': []}
previous = renewal.SAGA_HEAD
pairs = [
('owner_registered_completion_successor_tag_reconciliation', 'TAG_PENDING'),
('release_reconciliation_required', 'RECONCILIATION_REQUIRED'),
('owner_registered_tag_capability_successor_verified', 'TAG_VERIFIED'),
('baseline_registry_finalization_pending', 'BASELINE_REGISTRY_PENDING'),
('release_reconciliation_required', 'RECONCILIATION_REQUIRED'),
]
for revision, (name, state) in enumerate(pairs, 16):
event = {'schema_version': 1, 'sequence': revision + 1, 'previous_hash': previous,
'payload': {'candidate_id': renewal.CID, 'release_plan_id': 'pending-' + renewal.CID,
'revision': revision, 'state': state, 'event': name,
'timestamp': '1970-01-01T00:10:00Z', 'details': {}}}
event['event_hash'] = renewal.digest(renewal.canonical({
key: event[key] for key in ('sequence', 'previous_hash', 'payload')}))
gate['completion_journal_suffix'].append(event)
previous = event['event_hash']
gate['saga_head'] = previous
return gate
def test_registered_authority_accepts_authenticated_post_tag_lineage(registered_authority):
import json
root, payload, seal, authority = registered_authority
path = root / 'current-gate.json'
gate = json.loads(path.read_bytes())
gate.update(completion_gate())
path.write_bytes(renewal.canonical(gate))
next(item for item in payload['files'] if item['path'] == str(path))['sha256'] = renewal.digest(path.read_bytes())
seal()
assert authority()['payload_sha256'] == renewal.digest(renewal.canonical(payload))
@pytest.mark.parametrize('fault', [
'head', 'origin', 'gap', 'truncated', 'revision_bool', 'sequence_bool', 'candidate',
'plan', 'payload_hash', 'runtime_event', 'state', 'future', 'observation_bool', 'observation_nan',
])
def test_completion_lineage_rejects_corruption_or_noncompletion_effect(fault):
gate = completion_gate()
first = gate['completion_journal_suffix'][0]
if fault == 'head': gate['saga_head'] = '0' * 64
elif fault == 'origin': first['previous_hash'] = '0' * 64
elif fault == 'gap': first['sequence'] = 18
elif fault == 'truncated': gate['completion_journal_suffix'].pop()
elif fault == 'revision_bool': gate['saga_revision'] = True
elif fault == 'sequence_bool': first['sequence'] = True
elif fault == 'candidate': first['payload']['candidate_id'] = 'other'
elif fault == 'plan': first['payload']['details']['release_plan_sha256'] = '0' * 64
elif fault == 'payload_hash': first['payload']['details']['tampered'] = True
elif fault == 'runtime_event': first['payload']['event'] = 'runtime_switch_pending'
elif fault == 'state': first['payload']['state'] = 'SWITCH_PENDING'
elif fault == 'future': first['payload']['timestamp'] = '2026-10-01T12:00:00Z'
elif fault == 'observation_bool': gate['observed_at'] = True
elif fault == 'observation_nan': gate['observed_at'] = float('nan')
with pytest.raises(renewal.RenewalRejected): renewal.validate_completion_lineage(gate)
def test_registered_authority_validates_closure_and_denies_runtime(registered_authority):
root, payload, _, authority = registered_authority
assert authority()['payload_sha256'] == renewal.digest(renewal.canonical(payload))
authority.validate_host(state_root=renewal.STATE_ROOT, candidate_id=renewal.CID,
source_commit=renewal.SOURCE, tree_oid=renewal.TREE, artifact_sha256=renewal.ARTIFACT,
release_plan_sha256=renewal.PLAN)
with pytest.raises(renewal.RenewalRejected, match='RUNTIME_EFFECT_FORBIDDEN'):
authority.deny_runtime_effect()
@pytest.mark.parametrize('field,value', [
('candidate_id', 'other'), ('artifact_sha256','f'*64), ('release_plan_sha256','f'*64),
('state_root','/tmp/second-plane'), ('runtime_effects_allowed',True),
('owner_key_id','other-key'), ('owner_namespace','other-namespace'),
('expires_at',1100), ('issued_at',1101), ('expires_at',3000),
('approval_nonce','bad'), ('predecessor_uid',1), ('gate_observed_at',939),
('governed_source_commit','bad')])
def test_registered_authority_signed_drift_rejected(registered_authority, field, value):
_, payload, seal, authority = registered_authority
payload[field] = value; seal()
with pytest.raises(renewal.RenewalRejected): authority()
def test_registered_authority_rejects_signature_registration_and_rollback(registered_authority):
root, _, seal, authority = registered_authority
seal('invalid')
with pytest.raises(renewal.RenewalRejected, match='SIGNATURE_REJECTED'): authority()
seal(); (root/'registration.json').write_bytes(b'{}')
with pytest.raises(renewal.RenewalRejected, match='REGISTRATION_REQUIRED'): authority()
seal(); (root/'rollback.json').write_bytes(b'{}')
with pytest.raises(renewal.RenewalRejected, match='ROLLED_BACK'): authority()
def test_registered_authority_rejects_code_drift_and_symlink(registered_authority):
root, _, _, authority = registered_authority
path = root/'c5_completion_renewal.py'; original = path.read_bytes()
path.write_bytes(b'altered')
with pytest.raises(renewal.RenewalRejected, match='BYTES_DRIFT'): authority()
path.unlink(); target=root/'target'; target.write_bytes(original); path.symlink_to(target)
with pytest.raises(OSError): authority()
@pytest.mark.parametrize('file,field,value', [
('source-material.json','task_status','FAILED'),
('source-material.json','remote_verified',False),
('historical-production-proof.json','real_c4_rollback_verified',False),
('historical-production-proof.json','approval_fingerprint','f'*64),
('current-gate.json','business_verified',False),
('current-gate.json','saga_revision',14),
('current-gate.json','remote_tag_oid','f'*40)])
def test_registered_authority_rejects_signed_false_evidence(registered_authority, file, field, value):
import json
root, payload, seal, authority=registered_authority
path=root/file; data=json.loads(path.read_bytes()); data[field]=value
raw=renewal.canonical(data); path.write_bytes(raw)
for pin in payload['files']:
if pin['path']==str(path): pin['sha256']=renewal.digest(raw)
seal()
with pytest.raises(renewal.RenewalRejected): authority()
def test_registered_store_cannot_select_another_plane(tmp_path):
from types import SimpleNamespace
with pytest.raises(renewal.RenewalRejected, match='SAME_APPROVAL_PLANE_REQUIRED'):
renewal.RegisteredCompletionApprovalStore(SimpleNamespace(root=tmp_path), b'', object)
def test_completion_runtime_guard_uses_execute_contract(registered_authority):
_, _, _, authority = registered_authority
with pytest.raises(renewal.RenewalRejected, match='RUNTIME_EFFECT_FORBIDDEN'):
renewal.CompletionRuntimeGuard(authority).execute(candidate=object(), plan=object())
with pytest.raises(renewal.RenewalRejected, match='AUTHORITY_REQUIRED'):
renewal.CompletionRuntimeGuard(lambda: {})
def test_typed_host_rejects_callback_before_approval_load(tmp_path, monkeypatch):
import c5_release_execution_host as host
from types import SimpleNamespace
monkeypatch.setenv('AIOS_GITOPS_WRITES_ENABLED','false')
config=SimpleNamespace(verify_files=lambda **kw: None, state_root=tmp_path)
with pytest.raises(host.C5ExecutionHostBlocked, match='REGISTERED_COMPLETION_DEPENDENCY_REQUIRED'):
host.build_execution_host(config, completion_approvals=lambda: {})
def test_api_rejects_untyped_completion_store(tmp_path):
from silent_release_hardening import SilentReleaseActionService, CapabilityAuthorizationError
with pytest.raises(CapabilityAuthorizationError, match='completion approval dependency'):
SilentReleaseActionService(tmp_path, completion_approvals=object())
def test_signer_wrapper_preserves_existing_dispatch():
from types import SimpleNamespace
from c5_completion_signer import signer_class
class Original:
def handle(self, request): return {'original_operation': request['operation']}
signer=signer_class(SimpleNamespace(ExactReleaseSigner=Original))()
assert signer.handle({'operation':'status'}) == {'original_operation':'status'}
with pytest.raises(renewal.RenewalRejected, match='EXACT_SIGNER_RENEWAL_REQUEST_REQUIRED'):
signer.handle({'operation':'renew_exact_completion_approval', 'arbitrary':True})
@pytest.fixture
def real_api_roundtrip(tmp_path, monkeypatch):
import dataclasses
from datetime import timedelta
from test_c5_release_execution_host import _plan, _approval
import c5_exact_deployment_adapter as exact
import silent_release_hardening as core
from silent_release_controller import ReleaseCandidate, ReleaseCandidateStore
private = Ed25519PrivateKey.generate()
public = private.public_key().public_bytes(Encoding.Raw, PublicFormat.Raw)
policy_sha = '1'*64
plan = _plan(renewal.SOURCE, renewal.TREE, renewal.ARTIFACT,
tmp_path/'repo.git', policy_sha, exact.C4_ROLLBACK_TARGET)
monkeypatch.setattr(renewal,'PLAN',plan.sha256)
candidate=ReleaseCandidate(candidate_id=renewal.CID, source_commit=renewal.SOURCE,
tree_oid=renewal.TREE, artifact_sha256=renewal.ARTIFACT,
manifest_sha256=plan.deployment.manifest_sha256)
now=datetime.now(timezone.utc)
def sign(approval):
return dataclasses.replace(approval,signature=base64.b64encode(
private.sign(approval.canonical_bytes())).decode())
old=sign(dataclasses.replace(_approval(candidate,plan,private),
capabilities=tuple(renewal.CAPABILITIES),
issued_at=(now-timedelta(minutes=70)).isoformat(),
not_before=(now-timedelta(minutes=70)).isoformat(),
expires_at=(now-timedelta(minutes=10)).isoformat()))
new=sign(dataclasses.replace(old,issued_at=(now-timedelta(seconds=5)).isoformat(),
not_before=(now-timedelta(seconds=5)).isoformat(),
expires_at=(now+timedelta(seconds=595)).isoformat(),nonce='f'*64))
monkeypatch.setattr(renewal,'OLD_FP',old.fingerprint)
monkeypatch.setattr(renewal,'STATE_ROOT',tmp_path)
original=core.SignedApprovalStore(tmp_path/'approvals')
original.save(renewal.CID,old)
raw=(original.root/(renewal.CID+'.json')).read_bytes()
policy={'candidate_id':renewal.CID,'predecessor_fingerprint':old.fingerprint,
'approval_nonce':'f'*64,'payload_sha256':'1'*64,'predecessor_uid':os.geteuid(),
'predecessor_sha256':renewal.digest(raw)}
class FixtureAuthority:
state_root = tmp_path
def __call__(self):return policy
monkeypatch.setattr(renewal,'RegisteredCompletionAuthority',FixtureAuthority)
directory=original.root/'c5-completion-successors';directory.mkdir(mode=0o700)
store=renewal.RegisteredCompletionApprovalStore(original,public,core.SignedReleasePlanApproval)
ReleaseCandidateStore(tmp_path/'candidates').save(candidate)
core.ReleasePlanStore(tmp_path/'release-plans').save(renewal.CID,plan)
sagas=core.ReleaseSagaStore(tmp_path/'sagas')
saga=sagas.create(renewal.CID,old.release_plan_id)
sagas.compare_and_set(renewal.CID,expected_revision=saga.revision,
expected_states={core.SagaState.PREPARING},target=core.SagaState.RECONCILIATION_REQUIRED,
event='isolated_existing_partial_tag')
verifier=core.SignedApprovalVerifier(core.ApprovalKeyRing({'c5-owner-key-001':public}),
core.ApprovalRevocationStore(tmp_path/'revocations'))
service=core.SilentReleaseActionService(tmp_path,approval_verifier=verifier,
policy_sha256=policy_sha,completion_approvals=store)
return service,store,old,new,raw,private
def test_actual_api_verifier_append_successor_roundtrip(real_api_roundtrip):
service,store,old,new,raw,_=real_api_roundtrip
response=service.submit_owner_approval(new.to_dict())
assert response['status']=='VERIFIED_AND_STORED'
assert response['saga_state']=='RECONCILIATION_REQUIRED'
assert response['side_effects_executed'] is False
assert response['shadow_mode'] is True
assert store.load(renewal.CID).fingerprint==new.fingerprint
assert service.submit_owner_approval(new.to_dict())['status']=='IDEMPOTENT'
assert (store.original.root/(renewal.CID+'.json')).read_bytes()==raw
@pytest.mark.parametrize('failure',['expired','tampered','wrong_binding','revoked'])
def test_actual_api_verifier_rejects_before_successor_append(real_api_roundtrip,failure):
import dataclasses
import silent_release_hardening as core
service,store,old,new,raw,key=real_api_roundtrip
value=new.to_dict()
if failure=='expired':value=old.to_dict()
if failure=='tampered':value['signature']=base64.b64encode(b'0'*64).decode()
if failure=='wrong_binding':
bad=dataclasses.replace(new,artifact_sha256='0'*64)
bad=dataclasses.replace(bad,signature=base64.b64encode(key.sign(bad.canonical_bytes())).decode())
value=bad.to_dict()
if failure=='revoked':service.approval_verifier.keyring.revoke(new.key_id)
with pytest.raises(core.CapabilityAuthorizationError):service.submit_owner_approval(value)
assert list(store.directory.iterdir())==[]
assert (store.original.root/(renewal.CID+'.json')).read_bytes()==raw
+427
View File
@@ -0,0 +1,427 @@
from __future__ import annotations
import base64
import dataclasses
from datetime import datetime, timedelta, timezone
import hashlib
import hmac
import json
import os
from pathlib import Path
import sys
import pytest
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
import c5_exact_deployment_adapter as c5
import release_source_admission
from release_source_admission import ReleaseSourceAdmissionStore
from silent_release_controller import ReleaseCandidate
from silent_release_hardening import (
ApprovalKeyRing,
ApprovalRevocationStore,
CapabilityLedger,
DeploymentPlan,
FinalizationPlan,
ReleasePlan,
SignedApprovalVerifier,
SignedReleasePlanApproval,
TagPlan,
)
SOURCE = "1" * 40
TREE = "2" * 40
MANIFEST = "3" * 64
POLICY = "4" * 64
TAG_OID = "5" * 40
EVIDENCE = "6" * 40
OWNER_RECORD = "7" * 40
BASELINE = "8" * 64
REGISTRY = "9" * 64
TAG_REFSET = "a" * 64
PACKAGE = "b" * 64
SYSTEMD = "c" * 64
ABSENT = "d" * 64
CANDIDATE_ID = "c5-candidate-final-publication-001"
APPROVAL_PLAN_ID = "c5-owner-plan-final-publication-001"
@pytest.fixture(autouse=True)
def _windows_has_no_posix_mode_model(monkeypatch):
if os.name == "nt":
monkeypatch.setattr(
release_source_admission,
"_secure_stat",
lambda *args, **kwargs: None,
)
def _sha(path: Path) -> str:
return hashlib.sha256(path.read_bytes()).hexdigest()
def _plan(source: str, tree: str, artifact_sha256: str) -> ReleasePlan:
deployment = DeploymentPlan(
service=c5.C5_PRODUCTION_SERVICE,
release_path=f"/data/aios-gitops/runtime/releases/{source}",
release_root="/data/aios-gitops/runtime/releases",
cutover_link=(
"/etc/systemd/system/aios-gitops-model-api.service.d/production.conf"
),
dropin_path=(
f"/data/aios-gitops/runtime/releases/{source}/deployment/production.conf"
),
dropin_sha256=SYSTEMD,
systemd_unit_sha256=SYSTEMD,
rollback_path="/usr/libexec/aios-gitops-runtime-anchor",
rollback_sha256="e" * 64,
source_commit=source,
tree_oid=tree,
artifact_sha256=artifact_sha256,
manifest_sha256=MANIFEST,
package_closure_sha256=PACKAGE,
authoritative_git_dir="/data/aios-gitops/repo.git",
repository_identity="aios/aios",
writer_expected=False,
baseline_before_sha256=BASELINE,
registry_before_sha256=REGISTRY,
production_tag_refset_before_sha256=TAG_REFSET,
health_contract_version="silent-release-health/v1",
timeout_policy={"health_seconds": 30},
retry_policy={"health_attempts": 3},
policy_sha256=POLICY,
health_contract={
"runtime_commit": source,
"writer": False,
"repository_identity": "aios/aios",
},
rollback_target=dict(c5.C4_ROLLBACK_TARGET),
)
return ReleasePlan(
deployment=deployment,
tag=TagPlan(
version=c5.C5_VERSION,
tag_type="annotated",
expected_tag_oid=TAG_OID,
expected_target_oid=source,
evidence_closure_oid=EVIDENCE,
approval_plan_id=APPROVAL_PLAN_ID,
repository_identity="aios/aios",
gitea_owner="aios",
gitea_repository="aios",
current_remote_tag_oid=None,
remote_absent_evidence_sha256=ABSENT,
verification_contract="git+api+listing/v1",
),
finalization=FinalizationPlan(
version=c5.C5_VERSION,
expected_tag_oid=TAG_OID,
expected_target_oid=source,
expected_previous_baseline_sha256=BASELINE,
registry_expected_record={"version": c5.C5_VERSION, "target_oid": source},
baseline_expected_record={"version": c5.C5_VERSION, "target_oid": source},
final_consistency_contract="registry=baseline=tag=v1",
),
policy_sha256=POLICY,
)
def _approval(
candidate: ReleaseCandidate,
plan: ReleasePlan,
private: Ed25519PrivateKey,
) -> SignedReleasePlanApproval:
now = datetime.now(timezone.utc)
value = SignedReleasePlanApproval(
release_plan_id="c5-release-plan-final-publication-001",
candidate_id=candidate.candidate_id,
source_commit=candidate.source_commit,
tree_oid=candidate.tree_oid,
artifact_sha256=candidate.artifact_sha256,
manifest_sha256=candidate.manifest_sha256,
deployment_plan_sha256=plan.deployment.sha256,
policy_sha256=plan.policy_sha256,
approval_plan_id=APPROVAL_PLAN_ID,
evidence_closure_oid=EVIDENCE,
owner_approval_record_oid=OWNER_RECORD,
capabilities=("runtime_switch",),
issued_at=(now - timedelta(minutes=1)).isoformat().replace("+00:00", "Z"),
not_before=(now - timedelta(minutes=1)).isoformat().replace("+00:00", "Z"),
expires_at=(now + timedelta(minutes=30)).isoformat().replace("+00:00", "Z"),
nonce="c5-exact-deployment-nonce-0001",
key_id="c5-owner-key-001",
signature="",
owner_identity="owner:imruyi",
trust_domain="aios.production",
release_plan_sha256=plan.sha256,
)
return dataclasses.replace(
value,
signature=base64.b64encode(private.sign(value.canonical_bytes())).decode(),
)
def _admission_store(
root: Path,
candidate: ReleaseCandidate,
) -> ReleaseSourceAdmissionStore:
root.mkdir(mode=0o700)
root.chmod(0o700)
key = b"c5-source-admission-test-key-000000000000"
record = {
"schema": "aios.release-source-admission.v1",
"admission_id": "c5-final-source-admission-001",
"repository": "aios/aios",
"task_id": "TASK-V2R2C5-FINAL-PUBLICATION-001",
"version": c5.C5_VERSION,
"candidate_id": candidate.candidate_id,
"base_oid": c5.C4_ROLLBACK_TARGET["target_oid"],
"source_ref": "refs/heads/agent/v2r2c5-final-publication",
"source_commit": candidate.source_commit,
"remote_ref_oid": candidate.source_commit,
"tree_oid": candidate.tree_oid,
"purpose": "AIOS_TRUSTED_RELEASE_BUILDER",
"policy_sha256": POLICY,
"admitted_at": "2026-09-30T00:00:00Z",
}
record["mac"] = hmac.new(
key,
release_source_admission._canonical(record),
hashlib.sha256,
).hexdigest()
path = root / f"{candidate.source_commit}.json"
path.write_text(json.dumps(record, sort_keys=True) + "\n", encoding="utf-8")
path.chmod(0o600)
return ReleaseSourceAdmissionStore(
root,
repository="aios/aios",
policy_sha256=POLICY,
hmac_key=key,
writable=False,
)
def _adapter_script(path: Path, state: Path, calls: Path, mode: str) -> None:
path.write_text(
"import json, pathlib, sys\n"
f"state=pathlib.Path({str(state)!r})\n"
f"calls=pathlib.Path({str(calls)!r})\n"
f"mode={mode!r}\n"
"request=json.loads(sys.stdin.buffer.read())\n"
"with calls.open('a', encoding='utf-8') as out: out.write(request['operation']+'\\n')\n"
"binding={k:v for k,v in request.items() if k not in {'schema','operation'}}\n"
"result={**binding,'writer_disabled':True,'deployment_receipt':{'adapter':'registered-c5','operation':request['operation']}}\n"
"if mode == 'bad-receipt': result.pop('tree_oid')\n"
"if request['operation']=='probe':\n"
" if mode == 'unknown':\n"
" result['status']='NOT_APPLIED' if len(calls.read_text(encoding='utf-8').splitlines()) == 1 else 'INDETERMINATE'\n"
" elif state.exists(): result.update(status='ACTIVE_VERIFIED',verified=True,deployment_state='ACTIVE',production_deployed=True)\n"
" else: result['status']='NOT_APPLIED'\n"
"elif request['operation']=='apply':\n"
" if mode == 'unknown': result['status']='EXECUTION_OUTCOME_UNKNOWN'\n"
" else:\n"
" state.write_text(request['release_plan_sha256'], encoding='utf-8')\n"
" result['status']='APPLIED'\n"
"elif request['operation']=='rollback':\n"
" result.update(status='ROLLED_BACK_VERIFIED',verified=True,rollback_verified=True,rollback_version='V2R2C4',rollback_target_oid='e5f8209f57b43fc39e55c4555776e6362ce326eb',rollback_artifact_sha256='0dfc0461f7f9aa25cfbfc37008dcbd2d48d5341383574163b7bba102852062c4')\n"
"else: raise SystemExit(2)\n"
"print(json.dumps(result, sort_keys=True))\n",
encoding="utf-8",
)
path.chmod(0o600)
def _fixture(tmp_path: Path, *, mode: str = "success", source: str = SOURCE):
artifact = tmp_path / "c5-release.tar.zst"
artifact.write_bytes(b"exact-c5-final-governed-artifact")
artifact.chmod(0o600)
artifact_sha256 = _sha(artifact)
candidate = ReleaseCandidate(
candidate_id=CANDIDATE_ID,
source_commit=source,
tree_oid=TREE,
artifact_sha256=artifact_sha256,
manifest_sha256=MANIFEST,
)
plan = _plan(source, TREE, artifact_sha256)
private = Ed25519PrivateKey.generate()
public = private.public_key().public_bytes(
serialization.Encoding.Raw,
serialization.PublicFormat.Raw,
)
approval = _approval(candidate, plan, private)
verifier = SignedApprovalVerifier(
ApprovalKeyRing({"c5-owner-key-001": public}),
ApprovalRevocationStore(tmp_path / "revocations"),
allowed_trust_domains={"aios.production"},
)
admissions = _admission_store(tmp_path / "admissions", candidate)
script = tmp_path / "registered_c5_adapter.py"
state = tmp_path / "runtime-state"
calls = tmp_path / "calls.log"
_adapter_script(script, state, calls, mode)
executable = Path(sys.executable).resolve()
executor = c5.RegisteredC5DeploymentExecutor(
executor_id="registered-c5-production-adapter-001",
executable=executable,
executable_sha256=_sha(executable),
adapter_path=script.resolve(),
adapter_sha256=_sha(script),
artifact_path=artifact.resolve(),
rollback_artifact_path=c5.C4_ROLLBACK_ARTIFACT_PATH,
production_environment_file=c5.C5_PRODUCTION_ENVIRONMENT_FILE,
require_root_owner=False,
)
return candidate, plan, approval, verifier, admissions, executor, calls
def _build_adapter(parts) -> c5.C5ExactDeploymentAdapter:
candidate, plan, approval, verifier, admissions, executor, _calls = parts
return c5.C5ExactDeploymentAdapter(
candidate=candidate,
release_plan=plan,
approval=approval,
approval_verifier=verifier,
source_admissions=admissions,
executor=executor,
)
def test_runtime_switch_executes_only_registered_exact_deployment_and_is_idempotent(
tmp_path,
):
parts = _fixture(tmp_path)
candidate, plan, *_rest, calls = parts
adapter = _build_adapter(parts)
controller = adapter.controller(CapabilityLedger(tmp_path / "ledger"))
first = controller.execute(
adapter.approval,
operation=f"runtime:{plan.sha256}",
candidate=candidate,
plan=plan,
)
second = controller.execute(
adapter.approval,
operation=f"runtime:{plan.sha256}",
candidate=candidate,
plan=plan,
)
assert first["status"] == "EXECUTED"
assert first["result"]["verified"] is True
assert first["result"]["rollback_target"] == c5.C4_ROLLBACK_TARGET
assert first["result"]["global_legacy_writes_enabled"] is False
assert second["status"] == "IDEMPOTENT"
assert calls.read_text(encoding="utf-8").splitlines() == ["probe", "apply", "probe"]
def test_unknown_effect_reconciles_and_never_reapplies(tmp_path):
parts = _fixture(tmp_path, mode="unknown")
candidate, plan, *_rest, calls = parts
adapter = _build_adapter(parts)
controller = adapter.controller(CapabilityLedger(tmp_path / "ledger"))
operation = f"runtime:{plan.sha256}"
with pytest.raises(c5.C5DeploymentOutcomeUnknown, match="OUTCOME_UNKNOWN"):
controller.execute(
adapter.approval,
operation=operation,
candidate=candidate,
plan=plan,
)
reconciled = controller.execute(
adapter.approval,
operation=operation,
candidate=candidate,
plan=plan,
)
assert reconciled["status"] == "RECONCILIATION_REQUIRED"
assert calls.read_text(encoding="utf-8").splitlines() == ["probe", "apply", "probe"]
def test_wrong_rollback_target_fails_before_registered_executor(tmp_path):
parts = list(_fixture(tmp_path))
plan = parts[1]
parts[1] = dataclasses.replace(
plan,
deployment=dataclasses.replace(
plan.deployment,
rollback_target={
**c5.C4_ROLLBACK_TARGET,
"target_oid": "f" * 40,
},
),
)
with pytest.raises(c5.C5DeploymentBindingError, match="ROLLBACK_TARGET_INVALID"):
_build_adapter(tuple(parts))
assert not parts[-1].exists()
def test_rehearsal_executor_rejects_bindings_outside_exact_isolation_root(tmp_path):
parts = _fixture(tmp_path)
_candidate, _plan, _approval, _verifier, _admissions, executor, _calls = parts
rehearsal_root = (tmp_path / "isolated-rehearsal" / "instance-001").resolve()
rehearsal_root.mkdir(parents=True)
with pytest.raises(c5.C5DeploymentBindingError, match="C5_REHEARSAL_BINDING_INVALID"):
dataclasses.replace(
executor,
execution_scope="ISOLATED_REHEARSAL",
isolation_root=rehearsal_root,
rollback_artifact_path=tmp_path / "outside-c4.tar.gz",
production_environment_file=rehearsal_root / "v2r2c2.env",
rollback_target={**c5.C4_ROLLBACK_TARGET, "artifact_sha256": "f" * 64},
)
def test_prepublication_installer_source_cannot_become_final_c5_binding(tmp_path):
parts = _fixture(tmp_path, source=c5.C5_PREPUBLICATION_SOURCE_COMMIT)
with pytest.raises(c5.C5DeploymentBindingError, match="PREPUBLICATION_SOURCE_FORBIDDEN"):
_build_adapter(parts)
assert not parts[-1].exists()
def test_receipt_must_echo_tree_plan_approval_and_rollback_binding(tmp_path):
parts = _fixture(tmp_path, mode="bad-receipt")
candidate, plan, *_rest, calls = parts
adapter = _build_adapter(parts)
controller = adapter.controller(CapabilityLedger(tmp_path / "ledger"))
with pytest.raises(c5.C5DeploymentBindingError, match="RECEIPT_MISMATCH:tree_oid"):
controller.execute(
adapter.approval,
operation=f"runtime:{plan.sha256}",
candidate=candidate,
plan=plan,
)
assert calls.read_text(encoding="utf-8").splitlines() == ["probe"]
def test_global_legacy_writes_true_blocks_before_executor(tmp_path, monkeypatch):
parts = _fixture(tmp_path)
monkeypatch.setenv("AIOS_GITOPS_WRITES_ENABLED", "true")
with pytest.raises(c5.C5DeploymentBindingError, match="LEGACY_WRITES"):
_build_adapter(parts)
assert not parts[-1].exists()
def test_explicit_saga_rollback_requires_exact_c4_verified_receipt(tmp_path):
parts = _fixture(tmp_path)
candidate, plan, *_rest, calls = parts
adapter = _build_adapter(parts)
result = adapter.rollback(candidate=candidate, plan=plan)
assert result == {
"verified": True,
"rollback_verified": True,
"rollback_version": "V2R2C4",
"rollback_target_oid": c5.C4_ROLLBACK_TARGET["target_oid"],
"rollback_artifact_sha256": c5.C4_ROLLBACK_TARGET[
"artifact_sha256"
],
"writer_disabled": True,
}
assert calls.read_text(encoding="utf-8").splitlines() == ["rollback"]
@@ -525,6 +525,7 @@ def test_overlay_builder_patches_one_shared_admission_module(tmp_path, monkeypat
monkeypatch.setattr(sys, "argv", [
str(builder_path), "--principal-source-root", str(principal),
"--adapter-source-root", str(adapter), "--output-root", str(output),
"--task-id", "fixture-governed-task", "--agent-id", "agent/fixture-c5",
])
assert builder.main() == 0
manifest = json.loads((output / "overlay-manifest.json").read_text())
@@ -0,0 +1,408 @@
from __future__ import annotations
import hashlib
import io
import json
import os
from pathlib import Path
import tarfile
from types import SimpleNamespace
import pytest
import c5_production_deployment_adapter as production
def _sha(data: bytes) -> str:
return hashlib.sha256(data).hexdigest()
def _request(tmp_path: Path) -> dict:
artifact = tmp_path / "artifact.tar.gz"
artifact.write_bytes(b"placeholder")
return {
"schema": production.SCHEMA,
"operation": "probe",
"version": production.VERSION,
"candidate_id": "rc-v2r2c5-final-001",
"source_commit": "1" * 40,
"target_oid": "1" * 40,
"tree_oid": "2" * 40,
"artifact_sha256": _sha(b"placeholder"),
"artifact_path": str(artifact.resolve()).replace("\\", "/"),
"artifact_size": len(b"placeholder"),
"manifest_sha256": "3" * 64,
"deployment_plan_sha256": "4" * 64,
"release_plan_sha256": "5" * 64,
"policy_sha256": "6" * 64,
"approval_plan_id": "c5-owner-plan-final-001",
"approval_fingerprint": "7" * 64,
"owner_approval_record_oid": "8" * 40,
"evidence_closure_oid": "9" * 40,
"repository_identity": production.REPOSITORY,
"production_service": production.SERVICE,
"production_port": production.PORT,
"systemd_unit_sha256": "a" * 64,
"dropin_sha256": "a" * 64,
"rollback_target": dict(production.ROLLBACK),
"rollback_artifact_path": str(production.ROLLBACK_ARTIFACT),
"production_environment_file": str(production.ENV_FILE),
"writer_expected": False,
"global_legacy_writes_enabled": False,
"registered_executor_id": "c5-production-adapter-001",
"executor_executable_sha256": "c" * 64,
"executor_adapter_sha256": "d" * 64,
}
def _layout(tmp_path: Path) -> production.HostLayout:
return production.HostLayout(
release_root=tmp_path / "release-only",
current_link=tmp_path / "opt" / "v2r2c2",
environment_file=tmp_path / "etc" / "v2r2c2.env",
state_file=tmp_path / "state" / "deployment-state.json",
unit_file=tmp_path / "systemd" / production.SERVICE,
rollback_runtime_root=tmp_path / "c4" / "runtime-root",
rollback_root=tmp_path / "rollback-record",
)
def test_link_target_resolves_real_predecessor_symlink(tmp_path):
layout = _layout(tmp_path)
layout.rollback_runtime_root.mkdir(parents=True)
layout.current_link.parent.mkdir(parents=True)
layout.current_link.symlink_to(layout.rollback_runtime_root, target_is_directory=True)
assert production._link_target(layout) == layout.rollback_runtime_root.resolve(strict=True)
def test_link_target_resolves_relative_symlink(tmp_path):
from dataclasses import replace
target = tmp_path / "runtime-root"
target.mkdir()
link = tmp_path / "current"
link.symlink_to("runtime-root", target_is_directory=True)
assert production._link_target(replace(_layout(tmp_path), current_link=link)) == target
@pytest.mark.parametrize("kind", ["missing", "directory", "dangling", "cycle"])
def test_link_target_rejects_unresolved_or_non_symlink_identity(tmp_path, kind):
from dataclasses import replace
link = tmp_path / "current"
if kind == "directory":
link.mkdir()
elif kind == "dangling":
link.symlink_to(tmp_path / "absent", target_is_directory=True)
elif kind == "cycle":
link.symlink_to(link, target_is_directory=True)
assert production._link_target(replace(_layout(tmp_path), current_link=link)) is None
def test_request_rejects_wrong_production_env_and_global_write_state(tmp_path):
request = _request(tmp_path)
request["production_environment_file"] = "/etc/aios/aios-v2r2c2.env"
with pytest.raises(
production.C5ProductionAdapterBlocked,
match="ENVIRONMENT_FILE_BINDING_INVALID",
):
production._validate_request(request)
request = _request(tmp_path)
request["global_legacy_writes_enabled"] = True
with pytest.raises(
production.C5ProductionAdapterBlocked,
match="LEGACY_WRITES_BINDING_INVALID",
):
production._validate_request(request)
def test_probe_accepts_only_exact_c4_predecessor_as_not_applied(
tmp_path, monkeypatch
):
binding = production._validate_request(_request(tmp_path))
layout = _layout(tmp_path)
layout.rollback_runtime_root.mkdir(parents=True)
monkeypatch.setattr(
production,
"_link_target",
lambda _layout: layout.rollback_runtime_root.resolve(),
)
monkeypatch.setattr(
production,
"_validate_rollback_material",
lambda _layout: "[Service]",
)
monkeypatch.setattr(
production,
"_systemctl",
lambda *args, **kwargs: SimpleNamespace(returncode=0),
)
monkeypatch.setattr(
production,
"_health",
lambda _port: {
"version": production.ROLLBACK["version"],
"commit_oid": production.ROLLBACK["target_oid"],
"artifact_sha256": production.ROLLBACK["artifact_sha256"],
"deployment_state": "ACTIVE",
"production_deployed": True,
},
)
result = production.probe(binding, layout)
assert result["status"] == "NOT_APPLIED"
assert result["writer_disabled"] is True
assert result["deployment_receipt"]["operation"] == "probe"
def test_probe_maps_active_but_unobservable_runtime_to_indeterminate(
tmp_path, monkeypatch
):
binding = production._validate_request(_request(tmp_path))
monkeypatch.setattr(
production,
"_systemctl",
lambda *args, **kwargs: SimpleNamespace(returncode=0),
)
monkeypatch.setattr(production, "_health", lambda _port: None)
result = production.probe(binding, _layout(tmp_path))
assert result["status"] == "INDETERMINATE"
def test_probe_requires_health_link_state_and_writer_disabled_env(
tmp_path, monkeypatch
):
binding = production._validate_request(_request(tmp_path))
layout = _layout(tmp_path)
desired = production._target(binding, rollback=False)
release = production._release_dir(layout, desired)
release.mkdir(parents=True)
monkeypatch.setattr(
production, "_link_target", lambda _layout: release.resolve()
)
layout.environment_file.parent.mkdir(parents=True)
layout.environment_file.write_text(
"AIOS_GITOPS_WRITES_ENABLED=false\n", encoding="utf-8"
)
layout.state_file.parent.mkdir(parents=True)
layout.state_file.write_text(
json.dumps(
{
"release_plan_sha256": binding["release_plan_sha256"],
"candidate_id": binding["candidate_id"],
"tree_oid": binding["tree_oid"],
}
),
encoding="utf-8",
)
monkeypatch.setattr(
production,
"_systemctl",
lambda *args, **kwargs: SimpleNamespace(returncode=0),
)
monkeypatch.setattr(
production,
"_health",
lambda _port: {
"version": production.VERSION,
"commit_oid": binding["target_oid"],
"artifact_sha256": binding["artifact_sha256"],
"deployment_state": "ACTIVE",
"production_deployed": True,
},
)
assert production.probe(binding, layout)["status"] == "ACTIVE_VERIFIED"
layout.environment_file.write_text(
"AIOS_GITOPS_WRITES_ENABLED=true\n", encoding="utf-8"
)
assert production.probe(binding, layout)["status"] == "INDETERMINATE"
def test_install_extracts_bound_runtime_and_rejects_marker_conflict(
tmp_path, monkeypatch
):
chown_calls = []
monkeypatch.setattr(
production.os,
"chown",
lambda path, uid, gid: chown_calls.append((Path(path), uid, gid)),
raising=False,
)
request = _request(tmp_path)
unit = (
"[Service]\n"
f"EnvironmentFile={production.ENV_FILE}\n"
"ExecStart=/usr/bin/python3 app.py\n"
).encode()
package = json.dumps(
{
"source_commit": request["source_commit"],
"tree_oid": request["tree_oid"],
},
sort_keys=True,
).encode()
members = {
"payload/VERSION": (production.VERSION + "\n").encode(),
"payload/governed-runtime-package.json": package,
f"payload/deploy/aios-v2/systemd/{production.SERVICE}": unit,
}
artifact = Path(request["artifact_path"])
with tarfile.open(artifact, "w:gz") as archive:
for name, data in members.items():
info = tarfile.TarInfo(name)
info.size = len(data)
archive.addfile(info, io.BytesIO(data))
request["artifact_sha256"] = production._sha256(artifact)
request["artifact_size"] = artifact.stat().st_size
request["systemd_unit_sha256"] = _sha(unit)
request["dropin_sha256"] = _sha(unit)
binding = production._validate_request(request)
layout = _layout(tmp_path)
release = production._install_release(
binding, production._target(binding, rollback=False), layout
)
if production.os.name == "posix":
assert len(chown_calls) == 1
assert chown_calls[0][1:] == (0, 0)
assert (release / "VERSION").read_text().strip() == production.VERSION
assert release.parent.name == "v2r2c5-" + request["target_oid"][:12]
marker = release.parent / "DEPLOYMENT-BINDING.json"
value = json.loads(marker.read_text(encoding="utf-8"))
value["target_oid"] = "f" * 40
marker.write_text(json.dumps(value), encoding="utf-8")
with pytest.raises(
production.C5ProductionAdapterBlocked,
match="EXISTING_RELEASE_BINDING_CONFLICT",
):
production._install_release(
binding, production._target(binding, rollback=False), layout
)
def test_apply_verifies_before_switch_and_restores_exact_predecessor_on_failure(
tmp_path, monkeypatch
):
request = _request(tmp_path)
layout = _layout(tmp_path)
predecessor = layout.rollback_runtime_root
predecessor.mkdir(parents=True)
desired = production._release_dir(
layout, production._target(request, rollback=False)
)
desired.mkdir(parents=True)
new_unit = (
"[Service]\nUser=aios-runtime\n"
f"EnvironmentFile={production.ENV_FILE}\n"
)
unit_source = desired / "deploy" / "aios-v2" / "systemd" / production.SERVICE
unit_source.parent.mkdir(parents=True)
unit_source.write_bytes(new_unit.encode())
request["systemd_unit_sha256"] = production._sha256(unit_source)
request["dropin_sha256"] = request["systemd_unit_sha256"]
binding = production._validate_request(request)
old = {
layout.environment_file: "old-env\n",
layout.state_file: "old-state\n",
layout.unit_file: "old-unit\n",
}
for path, value in old.items():
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text(value, encoding="utf-8")
monkeypatch.setattr(
production,
"ROLLBACK_UNIT_SHA256",
production._sha256(layout.unit_file),
)
monkeypatch.setattr(
production, "_install_release", lambda *_args: desired
)
monkeypatch.setattr(
production, "_validate_rollback_material", lambda _layout: "unit"
)
monkeypatch.setattr(production, "_runtime_gid", lambda _text: 123)
chown_calls = []
monkeypatch.setattr(
production.os,
"chown",
lambda path, uid, gid: chown_calls.append((Path(path), uid, gid)),
raising=False,
)
monkeypatch.setattr(os, "readlink", lambda _path: str(predecessor))
link = {"target": predecessor.resolve()}
monkeypatch.setattr(
production, "_link_target", lambda _layout: link["target"]
)
monkeypatch.setattr(
production,
"_atomic_symlink",
lambda _link, target: link.__setitem__("target", Path(target).resolve()),
)
monkeypatch.setattr(
production,
"_health",
lambda _port: {
"version": production.ROLLBACK["version"],
"commit_oid": production.ROLLBACK["target_oid"],
"artifact_sha256": production.ROLLBACK["artifact_sha256"],
"deployment_state": "ACTIVE",
"production_deployed": True,
},
)
events = []
def verify_before_switch(path):
assert path == unit_source
assert link["target"] == predecessor.resolve()
assert all(path.read_text(encoding="utf-8") == value for path, value in old.items())
events.append("verified")
monkeypatch.setattr(production, "_verify_unit", verify_before_switch)
restart_calls = {"count": 0}
def systemctl(*args, **_kwargs):
events.append("systemctl:" + ":".join(args))
if args and args[0] == "restart":
restart_calls["count"] += 1
if restart_calls["count"] == 1:
raise production.C5ProductionAdapterBlocked(
"INJECTED_RESTART_FAILURE"
)
return SimpleNamespace(returncode=0)
monkeypatch.setattr(production, "_systemctl", systemctl)
with pytest.raises(
production.C5ProductionAdapterBlocked,
match="INJECTED_RESTART_FAILURE",
):
production._activate(
binding, production._target(binding, rollback=False), layout
)
assert events[0] == "verified"
assert link["target"] == predecessor.resolve()
assert all(path.read_text(encoding="utf-8") == value for path, value in old.items())
record = json.loads(
(layout.rollback_root / "record.json").read_text(encoding="utf-8")
)
assert record["candidate_id"] == binding["candidate_id"]
for value in record["files"].values():
backup = Path(value["backup"])
assert production._sha256(backup) == value["sha256"]
if os.name == "posix":
runtime_owned = [
(path, uid, gid)
for path, uid, gid in chown_calls
if uid == 0 and gid == 123
]
assert len(runtime_owned) == 2
@@ -0,0 +1,449 @@
from __future__ import annotations
import copy
import hashlib
import json
from pathlib import Path
import shutil
import pytest
import c5_registered_installer_executor as ex
import c5_staging_installation as c5
from registered_owner_signer_adapter import RegisteredOwnerSignerAdapter
import test_c5_staging_installation as fixtures
def _sha(path: Path) -> str:
return hashlib.sha256(path.read_bytes()).hexdigest()
def _write_provenance(
tmp_path: Path, *, integration_commit: str = fixtures.SUPPORT,
installer_module: Path | None = None,
environment: str = "ENGINEERING_TEST", name: str = "executor-provenance.json",
) -> Path:
module = Path(c5.__file__).resolve() if installer_module is None else installer_module.resolve()
script = (Path(__file__).parents[1] / "scripts" / "c5_staging_installation.py").resolve()
wrapper = Path(ex.__file__).resolve()
value = {
"schema": ex.PROVENANCE_SCHEMA,
"environment": environment,
"instance_id": "pytest-c5-registered-installer",
"integration_commit": integration_commit,
"integration_tree": "b" * 40,
"entries": {
"installer_module": {"path": str(module), "sha256": _sha(module)},
"installer_script": {"path": str(script), "sha256": _sha(script)},
"executor_module": {"path": str(wrapper), "sha256": _sha(wrapper)},
},
}
path = tmp_path / name
path.write_text(json.dumps(value), encoding="utf-8")
return path.resolve()
def _authority(case):
verifier = c5.RegistryOwnerVerifier(
case.registry,
owner_signer_id="local-owner-authority",
require_root_owner=False,
)
signer = RegisteredOwnerSignerAdapter(
"test-registered-owner-signer",
key_id=verifier.record.fingerprint,
owner_verifier=verifier,
require_root=False,
test_mode=True,
runner=fixtures.SigningRunner(case.key),
)
return verifier, signer
def _registration(
case, manifest: Path, *, issuer_contract: Path | None = None,
nonce: str = "registered-executor-nonce-0001",
):
verifier, signer = _authority(case)
registration = ex.issue_registration(
executor_id="c5-registered-installer-r14",
nonce=nonce,
ttl_seconds=40,
contract=case.contract,
installer=case.installer,
provenance_manifest=manifest,
registered_signer=signer,
clock=lambda: fixtures.NOW,
issuer_contract=issuer_contract,
require_root_owner=False,
)
executor = ex.RegisteredInstallerExecutor(
owner_verifier=verifier,
installer=case.installer,
provenance_manifest=manifest,
clock=lambda: fixtures.NOW,
host=lambda: c5.HOST,
require_root_owner=False,
)
return registration, executor
class PairBackend:
"""Engineering backend for the two-target installer state machine.
It models both target readbacks but does not claim to be a durable host or
systemd receipt. The separate rehearsal plan covers that boundary.
"""
def __init__(self, bridge, issuer) -> None:
self.cases = {c5.BRIDGE_TARGET: bridge, c5.ISSUER_TARGET: issuer}
self.host = c5.HOST
self.reloads = 0
self.restarts = []
def hostname(self) -> str:
return self.host
def _case(self, profile):
return self.cases[profile.name]
def _is_new(self, profile) -> bool:
case = self._case(profile)
dropin = profile.dropin(case.layout)
return dropin.exists() and dropin.read_bytes() == case.new_dropin
def unit_snapshot(self, profile) -> c5.UnitSnapshot:
case = self._case(profile)
if self._is_new(profile):
return c5.UnitSnapshot(
True, fixtures.sha(b"new-effective-config"),
case.new_pythonpath, case.activation_path,
case.principal_config_path,
)
return c5.UnitSnapshot(
True, case.old_config_hash, case.old_pythonpath,
case.payload["existing"]["activation_file"],
case.payload["existing"]["principal_scope_config"],
)
def daemon_reload(self) -> None:
self.reloads += 1
def restart(self, profile) -> None:
self.restarts.append(profile.name)
def runtime(self, *, url: str, timeout_seconds: int):
assert timeout_seconds == 3
profile = next(
item for item in (c5._BRIDGE_PROFILE, c5._ISSUER_PROFILE)
if item.runtime_url == url
)
case = self._case(profile)
if self._is_new(profile):
return {"status": "bad"} if case.backend_fail_new else dict(case.new_runtime)
return {"status": "bad"} if case.backend_fail_old else dict(case.old_runtime)
def _successor_pair(tmp_path: Path):
"""Create contracts for the exact shared tree used by both R14 targets."""
issuer = fixtures.make_successor_issuer_case(tmp_path)
issuer.layout.issuer_dropin.unlink()
issuer.payload["existing"]["dropin"] = {"exists": False, "sha256": None}
issuer.contract = tmp_path / "issuer-contract.json"
issuer.artifact = tmp_path / "issuer-artifact.tar"
fixtures.write_tar(issuer.artifact, [
(path.relative_to(issuer.root).as_posix(), path.read_bytes(), 0o644, "file")
for path in sorted(issuer.root.rglob("*")) if path.is_file()
] + [("dropin.conf", issuer.new_dropin, 0o644, "file")])
fixtures.resign(issuer)
bridge = fixtures.Case()
bridge.profile = c5._BRIDGE_PROFILE
bridge.layout = issuer.layout
bridge.root = issuer.root
bridge.key = issuer.key
bridge.registry = issuer.registry
bridge.old_dropin = issuer.old_dropin
bridge.layout.dropin.unlink()
bridge.old_pythonpath = issuer.old_pythonpath
bridge.old_config_hash = issuer.old_config_hash
bridge.old_module = tmp_path / "old-runtime" / "app" / "aios_chatgpt_bridge_v2_server.py"
bridge.old_module_bytes = bridge.old_module.read_bytes()
bridge.new_module = bridge.root / "app" / "aios_chatgpt_bridge_v2_server.py"
bridge.new_module_bytes = bridge.new_module.read_bytes()
bridge.activation = bridge.root / "source-activation.json"
bridge.activation_path = bridge.activation.as_posix()
bridge.principal_config_path = (bridge.root / "principal-activation.json").as_posix()
bridge.new_pythonpath = ((bridge.root / "app").as_posix(), *bridge.old_pythonpath)
bridge.new_dropin = c5._dropin_bytes(
bridge.root, bridge.old_pythonpath,
target=c5.BRIDGE_TARGET, principal_config=True,
)
bridge.old_runtime = {
"status": "active", "health": 200, "deployment_state": "STAGING",
"repository": c5.REPOSITORY, "control_plane_target_oid": "1" * 40,
}
bridge.new_runtime = {
"status": "active", "health": 200, "deployment_state": "STAGING",
"repository": c5.REPOSITORY, "governed_writes_enabled": True,
"source_module_path": bridge.new_module.as_posix(),
"source_module_sha256": fixtures.sha(bridge.new_module_bytes),
}
previous = bridge.layout.release_base / "previous-bridge"
previous.mkdir()
old_activation = previous / "source-activation.json"
old_activation.write_bytes(b'{"schema":"previous-source-activation"}\n')
bridge.payload = copy.deepcopy(issuer.payload)
bridge.payload.update(
target=c5.BRIDGE_TARGET,
contract_id="c5-successor-bridge-pair-01",
unit=c5.UNIT,
listen={"host": "127.0.0.1", "port": c5.PORT},
effects=sorted(c5.BRIDGE_EFFECTS),
nonce="nonce-c5-successor-bridge-pair-0001",
)
bridge.payload["existing"] = {
"unit_active": True,
"effective_config_sha256": bridge.old_config_hash,
"dropin": {"exists": False, "sha256": None},
"pythonpath": list(bridge.old_pythonpath),
"source_files": [fixtures.file_record(
bridge.old_module.as_posix(), bridge.old_module_bytes, "existing_source",
)],
"activation_file": old_activation.as_posix(),
"principal_scope_config": None,
"config_files": [fixtures.file_record(
old_activation.as_posix(), old_activation.read_bytes(), "existing_config",
)],
}
bridge.payload["readiness"] = {
"url": c5.RUNTIME_URL, "timeout_seconds": 3, "unit_active": True,
"pythonpath": list(bridge.new_pythonpath),
"activation_file": bridge.activation_path,
"principal_scope_config": bridge.principal_config_path,
"module_path": bridge.new_module.as_posix(),
"module_sha256": fixtures.sha(bridge.new_module_bytes),
"runtime_fields": bridge.new_runtime,
}
bridge.payload["rollback"] = {
"strategy": "restore-exact-dropin-and-restart",
"dropin_restore": "exact",
"readiness": {
"url": c5.RUNTIME_URL, "timeout_seconds": 3, "unit_active": True,
"pythonpath": list(bridge.old_pythonpath),
"activation_file": old_activation.as_posix(),
"principal_scope_config": None,
"module_path": bridge.old_module.as_posix(),
"module_sha256": fixtures.sha(bridge.old_module_bytes),
"runtime_fields": bridge.old_runtime,
},
}
root_records = [
fixtures.file_record(path.as_posix(), path.read_bytes(), "release_file")
for path in sorted(bridge.root.rglob("*")) if path.is_file()
]
bridge.files = root_records + [fixtures.file_record(
bridge.layout.dropin.as_posix(), bridge.new_dropin, "systemd_dropin",
)]
bridge.payload["files"] = bridge.files
bridge.contract = tmp_path / "bridge-contract.json"
bridge.artifact = tmp_path / "bridge-artifact.tar"
fixtures.write_tar(bridge.artifact, [
(path.relative_to(bridge.root).as_posix(), path.read_bytes(), 0o644, "file")
for path in sorted(bridge.root.rglob("*")) if path.is_file()
] + [("dropin.conf", bridge.new_dropin, 0o644, "file")])
fixtures.resign(bridge)
bridge.backend_fail_new = bridge.backend_fail_old = False
issuer.backend_fail_new = issuer.backend_fail_old = False
backend = PairBackend(bridge, issuer)
for case in (bridge, issuer):
case.backend = backend
case.installer = c5.Installer(
case.registry, backend=backend, clock=lambda: fixtures.NOW,
sleeper=lambda _seconds: None, require_root_owner=False,
_test_layout=case.layout,
)
shutil.rmtree(bridge.root)
return bridge, issuer
def test_provenance_rejects_caller_substituted_installer_module(tmp_path: Path) -> None:
fake = tmp_path / "c5_staging_installation.py"
fake.write_text("# not the loaded installer\n", encoding="utf-8")
manifest = _write_provenance(tmp_path, installer_module=fake)
with pytest.raises(ex.ExecutorRegistrationError, match="LOADED_PATH_MISMATCH"):
ex.load_code_provenance(manifest, require_root_owner=False)
def test_real_signature_preflight_binds_contract_policy_and_issuer_effects(tmp_path: Path) -> None:
case = fixtures.make_successor_issuer_case(tmp_path)
manifest = _write_provenance(tmp_path)
registration, executor = _registration(case, manifest)
result = executor.dispatch(
registration, case.contract, case.artifact, "preflight",
)
assert result["status"] == "PREFLIGHT_PASS"
assert registration["payload"]["capabilities"] == ["install", "reconcile", "rollback"]
assert set(registration["payload"]["effects"]) == c5.ISSUER_EFFECTS
assert registration["payload"]["support_commit"] == registration["payload"]["integration_commit"]
assert registration["payload"]["issuer_dependency"] is None
assert registration["payload"]["contract_nonce"] == case.payload["nonce"]
assert registration["payload"]["contract_expires_at"] == case.payload["expires_at"]
assert "receipt" not in result
def test_fixture_registration_is_rejected_by_a_production_manifest(tmp_path: Path) -> None:
case = fixtures.make_successor_issuer_case(tmp_path)
fixture_manifest = _write_provenance(tmp_path)
production_manifest = _write_provenance(
tmp_path, environment="PRODUCTION", name="production-provenance.json",
)
registration, _executor = _registration(case, fixture_manifest)
verifier, _signer = _authority(case)
production_executor = ex.RegisteredInstallerExecutor(
owner_verifier=verifier,
installer=case.installer,
provenance_manifest=production_manifest,
clock=lambda: fixtures.NOW,
host=lambda: c5.HOST,
require_root_owner=False,
)
with pytest.raises(ex.ExecutorRegistrationError, match="REGISTRATION_BINDING_INVALID"):
production_executor.dispatch(
registration, case.contract, case.artifact, "preflight",
)
def test_wrapped_two_target_apply_replay_and_exact_paired_rollback(tmp_path: Path) -> None:
bridge, issuer = _successor_pair(tmp_path)
manifest = _write_provenance(tmp_path)
bridge_registration, bridge_executor = _registration(
bridge, manifest, issuer_contract=issuer.contract,
nonce="registered-bridge-pair-0001",
)
issuer_registration, issuer_executor = _registration(
issuer, manifest, nonce="registered-issuer-pair-0001",
)
assert bridge_executor.dispatch(
bridge_registration, bridge.contract, bridge.artifact, "preflight",
)["status"] == "PREFLIGHT_PASS"
assert bridge_executor.dispatch(
bridge_registration, bridge.contract, bridge.artifact, "install",
)["status"] == "APPLIED"
assert bridge_executor.dispatch(
bridge_registration, bridge.contract, bridge.artifact, "install",
)["status"] == "IDEMPOTENT_REPLAY"
assert issuer_executor.dispatch(
issuer_registration, issuer.contract, issuer.artifact, "preflight",
)["status"] == "PREFLIGHT_PASS"
assert issuer_executor.dispatch(
issuer_registration, issuer.contract, issuer.artifact, "install",
)["status"] == "APPLIED"
assert issuer_executor.dispatch(
issuer_registration, issuer.contract, issuer.artifact, "install",
)["status"] == "IDEMPOTENT_REPLAY"
assert issuer_executor.dispatch(
issuer_registration, issuer.contract, issuer.artifact, "rollback",
)["status"] == "ROLLED_BACK"
assert issuer_executor.dispatch(
issuer_registration, issuer.contract, issuer.artifact, "rollback",
)["status"] == "IDEMPOTENT_ROLLBACK"
assert bridge_executor.dispatch(
bridge_registration, bridge.contract, bridge.artifact, "rollback",
issuer_contract=issuer.contract, issuer_artifact=issuer.artifact,
)["status"] == "ROLLED_BACK"
assert bridge_executor.dispatch(
bridge_registration, bridge.contract, bridge.artifact, "rollback",
issuer_contract=issuer.contract, issuer_artifact=issuer.artifact,
)["status"] == "IDEMPOTENT_ROLLBACK"
assert not bridge.root.exists()
def test_wrapped_unknown_and_idempotent_reconciliation(tmp_path: Path) -> None:
case = fixtures.make_successor_issuer_case(tmp_path)
manifest = _write_provenance(tmp_path)
registration, executor = _registration(case, manifest)
case.backend.fail_new = True
case.backend.fail_old = True
with pytest.raises(c5.InstallationOutcomeUnknown, match="INSTALLATION_OUTCOME_UNKNOWN"):
executor.dispatch(registration, case.contract, case.artifact, "install")
state = json.loads(case.installer._state_path(case.payload).read_text())
assert state["status"] == "UNKNOWN"
case.backend.fail_old = False
assert executor.dispatch(
registration, case.contract, case.artifact, "reconcile",
)["status"] == "ROLLED_BACK"
assert executor.dispatch(
registration, case.contract, case.artifact, "reconcile",
)["status"] == "IDEMPOTENT_RECONCILIATION"
@pytest.mark.parametrize("field", ["old", "new", "rollback", "ttl", "nonce"])
def test_registration_rejects_resigned_contract_scope_drift_before_installer(
tmp_path: Path, field: str,
) -> None:
case = fixtures.make_successor_issuer_case(tmp_path)
manifest = _write_provenance(tmp_path)
registration, executor = _registration(case, manifest)
if field == "old":
case.payload["existing"]["effective_config_sha256"] = "f" * 64
elif field == "new":
case.payload["readiness"]["runtime_fields"]["manifest_revision"] = 2
elif field == "rollback":
case.payload["rollback"]["readiness"]["runtime_fields"]["rollback_revision"] = 2
elif field == "ttl":
case.payload["expires_at"] += 1
else:
case.payload["nonce"] = "nonce-c5-issuer-installation-0002"
fixtures.resign(case)
with pytest.raises(ex.ExecutorRegistrationError, match="REGISTRATION_BINDING_INVALID"):
executor.dispatch(registration, case.contract, case.artifact, "preflight")
assert case.backend.reloads == case.backend.restarts == 0
def test_issue_rejects_support_commit_different_from_manifest_commit(tmp_path: Path) -> None:
case = fixtures.make_successor_issuer_case(tmp_path)
manifest = _write_provenance(tmp_path, integration_commit="a" * 40)
verifier, signer = _authority(case)
del verifier
with pytest.raises(ex.ExecutorRegistrationError, match="COMMIT_BINDING_INVALID"):
ex.issue_registration(
executor_id="c5-registered-installer-r14",
nonce="registered-executor-nonce-0001",
ttl_seconds=40,
contract=case.contract,
installer=case.installer,
provenance_manifest=manifest,
registered_signer=signer,
clock=lambda: fixtures.NOW,
require_root_owner=False,
)
def test_bridge_rollback_rejects_unregistered_issuer_dependency(tmp_path: Path) -> None:
bridge, issuer = _successor_pair(tmp_path)
manifest = _write_provenance(tmp_path)
registration, executor = _registration(
bridge, manifest, issuer_contract=issuer.contract,
)
executor.dispatch(registration, bridge.contract, bridge.artifact, "install")
other = copy.deepcopy(issuer.payload)
other["nonce"] = "nonce-c5-other-issuer-contract-0001"
issuer.payload = other
fixtures.resign(issuer)
with pytest.raises(ex.ExecutorRegistrationError, match="DEPENDENCY_MISMATCH"):
executor.dispatch(
registration, bridge.contract, bridge.artifact, "rollback",
issuer_contract=issuer.contract, issuer_artifact=issuer.artifact,
)
state = json.loads(bridge.installer._state_path(bridge.payload).read_text())
assert state["status"] == "APPLIED"
+425
View File
@@ -0,0 +1,425 @@
from __future__ import annotations
import base64
import dataclasses
from datetime import datetime, timedelta, timezone
import hashlib
import hmac
import json
import os
from pathlib import Path
import shutil
import subprocess
import sys
from types import SimpleNamespace
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
import pytest
import c5_exact_deployment_adapter as exact
import c5_release_execution_host as host
import release_source_admission
from silent_release_controller import ReleaseCandidate, ReleaseCandidateStore
from silent_release_hardening import (
ApprovalKeyRing,
DeploymentPlan,
FinalizationPlan,
ReleasePlan,
ReleasePlanStore,
ReleaseSagaStore,
SagaState,
SignedApprovalStore,
SignedReleasePlanApproval,
TagPlan,
)
@pytest.mark.parametrize("task", ["fixture-governed-task", "other-task"])
def test_source_admission_requires_configured_governed_task(task):
config = SimpleNamespace(source_admission_task_id="fixture-governed-task")
candidate = SimpleNamespace(source_commit="1" * 40, tree_oid="2" * 40)
if task == config.source_admission_task_id:
host._validate_source_admission_task(config, candidate, {"task_id": task})
else:
with pytest.raises(host.C5ExecutionHostBlocked, match="SOURCE_ADMISSION_TASK_BINDING_INVALID"):
host._validate_source_admission_task(config, candidate, {"task_id": task})
def test_configured_task_cannot_be_replaced_by_historical_task():
config = SimpleNamespace(source_admission_task_id="fixture-current-task")
candidate = SimpleNamespace(source_commit="1" * 40, tree_oid="2" * 40)
with pytest.raises(host.C5ExecutionHostBlocked, match="SOURCE_ADMISSION_TASK_BINDING_INVALID"):
host._validate_source_admission_task(config, candidate, {"task_id": "fixture-expired-task"})
def _sha(path: Path) -> str:
return hashlib.sha256(path.read_bytes()).hexdigest()
def _write(path: Path, data: bytes) -> Path:
path.parent.mkdir(parents=True, exist_ok=True)
path.write_bytes(data)
path.chmod(0o600)
return path.resolve()
def _git_fixture(tmp_path: Path) -> tuple[Path, Path, str, str]:
raw = shutil.which("git")
if raw is None:
pytest.skip("git is required")
git = Path(raw).resolve()
repository = tmp_path / "authority.git"
repository.mkdir()
subprocess.run([str(git), "init"], cwd=repository, check=True, capture_output=True)
subprocess.run([str(git), "config", "user.email", "fixture@example.invalid"], cwd=repository, check=True)
subprocess.run([str(git), "config", "user.name", "fixture"], cwd=repository, check=True)
(repository / "fixture.txt").write_text("isolated rehearsal\n", encoding="utf-8")
subprocess.run([str(git), "add", "fixture.txt"], cwd=repository, check=True)
subprocess.run([str(git), "commit", "-m", "fixture"], cwd=repository, check=True, capture_output=True)
source = subprocess.run([str(git), "rev-parse", "HEAD"], cwd=repository, check=True, capture_output=True, text=True).stdout.strip()
tree = subprocess.run([str(git), "rev-parse", "HEAD^{tree}"], cwd=repository, check=True, capture_output=True, text=True).stdout.strip()
return git, (repository / ".git").resolve(), source, tree
def _plan(source: str, tree: str, artifact_sha: str, git_dir: Path, policy_sha: str, rollback: dict) -> ReleasePlan:
deployment = DeploymentPlan(
service=exact.C5_PRODUCTION_SERVICE,
release_path=f"/data/aios-gitops/runtime/releases/{source}",
release_root="/data/aios-gitops/runtime/releases",
cutover_link="/etc/systemd/system/aios-gitops-model-api.service.d/production.conf",
dropin_path=f"/data/aios-gitops/runtime/releases/{source}/deployment/production.conf",
dropin_sha256="1" * 64,
systemd_unit_sha256="1" * 64,
rollback_path="/usr/libexec/aios-gitops-runtime-anchor",
rollback_sha256="2" * 64,
source_commit=source,
tree_oid=tree,
artifact_sha256=artifact_sha,
manifest_sha256="3" * 64,
package_closure_sha256="4" * 64,
authoritative_git_dir=str(git_dir),
repository_identity=host.REPOSITORY,
writer_expected=False,
baseline_before_sha256="5" * 64,
registry_before_sha256="6" * 64,
production_tag_refset_before_sha256="7" * 64,
health_contract_version="silent-release-health/v1",
timeout_policy={"health_seconds": 30},
retry_policy={"health_attempts": 3},
policy_sha256=policy_sha,
health_contract={"runtime_commit": source, "writer": False, "repository_identity": host.REPOSITORY},
rollback_target=dict(rollback),
)
tag_oid = "8" * 40
approval_plan = "sha256:" + "9" * 64
return ReleasePlan(
deployment=deployment,
tag=TagPlan(
version=exact.C5_VERSION,
tag_type="annotated",
expected_tag_oid=tag_oid,
expected_target_oid=source,
evidence_closure_oid="a" * 40,
approval_plan_id=approval_plan,
repository_identity=host.REPOSITORY,
gitea_owner="aios",
gitea_repository="aios",
current_remote_tag_oid=None,
remote_absent_evidence_sha256="b" * 64,
verification_contract="git+api+listing/v1",
),
finalization=FinalizationPlan(
version=exact.C5_VERSION,
expected_tag_oid=tag_oid,
expected_target_oid=source,
expected_previous_baseline_sha256="5" * 64,
registry_expected_record={"version": exact.C5_VERSION, "target_oid": source},
baseline_expected_record={"version": exact.C5_VERSION, "target_oid": source},
final_consistency_contract="registry=baseline=tag=v1",
),
policy_sha256=policy_sha,
)
def _approval(candidate, plan, private) -> SignedReleasePlanApproval:
now = datetime.now(timezone.utc)
value = SignedReleasePlanApproval(
release_plan_id="c5-isolated-rehearsal-plan-001",
candidate_id=candidate.candidate_id,
source_commit=candidate.source_commit,
tree_oid=candidate.tree_oid,
artifact_sha256=candidate.artifact_sha256,
manifest_sha256=candidate.manifest_sha256,
deployment_plan_sha256=plan.deployment.sha256,
policy_sha256=plan.policy_sha256,
approval_plan_id=plan.tag.approval_plan_id,
evidence_closure_oid=plan.tag.evidence_closure_oid,
owner_approval_record_oid="c" * 40,
capabilities=("runtime_switch", "tag_publish", "baseline_registry_finalize"),
expires_at=(now + timedelta(minutes=30)).isoformat().replace("+00:00", "Z"),
nonce="c5-isolated-rehearsal-nonce-0001",
key_id="c5-owner-key-001",
signature="",
owner_identity="owner:fixture",
trust_domain="aios.production",
issued_at=(now - timedelta(minutes=1)).isoformat().replace("+00:00", "Z"),
not_before=(now - timedelta(minutes=1)).isoformat().replace("+00:00", "Z"),
release_plan_sha256=plan.sha256,
)
return dataclasses.replace(value, signature=base64.b64encode(private.sign(value.canonical_bytes())).decode())
def _phase_script(path: Path) -> Path:
return _write(
path,
(
"import json,sys\n"
"mode=sys.argv[1] if len(sys.argv)>1 else 'deployment'\n"
"v=json.load(sys.stdin)\n"
"if v['schema']=='aios.c5.exact-production-deployment-request.v1':\n"
" r={k:x for k,x in v.items() if k not in {'schema','operation'}}\n"
" r.update(status='ACTIVE_VERIFIED',verified=True,deployment_state='ACTIVE',production_deployed=True,writer_disabled=True,deployment_receipt={'scope':'fixture'})\n"
"elif mode=='tag':\n"
" r={k:v[k] for k in ('version','target_oid','evidence_closure_oid','artifact_sha256','approval_plan_id')}\n"
" r.update(status='APPLIED_VERIFIED',tag_oid='8'*40,peeled_target_oid=v['target_oid'],remote_tag_oid='8'*40,remote_peeled_target_oid=v['target_oid'],gitea_tag_oid='8'*40,gitea_peeled_target_oid=v['target_oid'],gitea_listing_visible=True)\n"
"else:\n"
" r={k:v[k] for k in ('version','target_oid','evidence_closure_oid','artifact_sha256','approval_plan_id')}\n"
" r.update(status='VERIFIED',tag_oid='8'*40)\n"
"print(json.dumps(r,sort_keys=True))\n"
).encode(),
)
def _command(executable: Path, script: Path, mode: str) -> dict:
return {
"executable": str(executable),
"executable_sha256": _sha(executable),
"adapter_path": str(script),
"adapter_sha256": _sha(script),
"arguments": [mode],
}
def test_isolated_rehearsal_uses_real_saga_and_cannot_emit_production_receipt(tmp_path, monkeypatch):
monkeypatch.setattr(release_source_admission, "_secure_stat", lambda *args, **kwargs: None)
instance_id = "c5-g2-fixture-001"
root = tmp_path / "isolated-rehearsal" / instance_id
root.mkdir(parents=True)
git, repository, source, tree = _git_fixture(root)
artifact = _write(root / "artifact.tar.gz", b"isolated-c5-artifact")
rollback_artifact = _write(root / "rollback-c4.tar.gz", b"isolated-c4-artifact")
rollback = {**exact.C4_ROLLBACK_TARGET, "artifact_sha256": _sha(rollback_artifact)}
environment = root / "v2r2c2.env"
trusted_policy = _write(
root / "trusted-policy.json",
json.dumps(
{
"services": [exact.C5_PRODUCTION_SERVICE],
"release_roots": ["/data/aios-gitops/runtime/releases"],
"cutover_links": ["/etc/systemd/system/aios-gitops-model-api.service.d/production.conf"],
"dropin_paths": [f"/data/aios-gitops/runtime/releases/{source}/deployment/production.conf"],
"authoritative_git_dirs": [str(repository)],
"repository_identities": [host.REPOSITORY],
}, sort_keys=True
).encode(),
)
policy_sha = _sha(trusted_policy)
plan = _plan(source, tree, _sha(artifact), repository, policy_sha, rollback)
candidate = ReleaseCandidate(
candidate_id="c5-isolated-rehearsal-candidate-001",
source_commit=source,
tree_oid=tree,
artifact_sha256=_sha(artifact),
manifest_sha256=plan.deployment.manifest_sha256,
)
private = Ed25519PrivateKey.generate()
approval = _approval(candidate, plan, private)
keyring = ApprovalKeyRing(
{"c5-owner-key-001": private.public_key().public_bytes(serialization.Encoding.Raw, serialization.PublicFormat.Raw)}
)
keyring_path = root / "approval-keyring.json"
keyring.save_public(keyring_path)
keyring_path.chmod(0o600)
ReleaseCandidateStore(root / "candidates").save(candidate)
ReleasePlanStore(root / "release-plans").save(candidate.candidate_id, plan)
SignedApprovalStore(root / "approvals").save(candidate.candidate_id, approval)
sagas = ReleaseSagaStore(root / "sagas")
saga = sagas.create(candidate.candidate_id, approval.release_plan_id)
sagas.compare_and_set(candidate.candidate_id, expected_revision=saga.revision, expected_states={SagaState.PREPARING}, target=SagaState.APPROVED, event="fixture_approval_bound")
admission_policy = _write(root / "source-policy.json", b"fixture-source-policy\n")
hmac_path = _write(root / "source-admission-hmac", b"fixture-hmac-key-000000000000000000000")
admission_root = root / "source-admissions"
admission_root.mkdir()
record = {
"schema": "aios.release-source-admission.v1", "admission_id": "c5-fixture-admission-001",
"repository": host.REPOSITORY, "task_id": "FIXTURE-NOT-R14", "version": exact.C5_VERSION,
"candidate_id": candidate.candidate_id, "base_oid": rollback["target_oid"],
"source_ref": "refs/heads/fixture/c5", "source_commit": source,
"remote_ref_oid": source, "tree_oid": tree, "purpose": "AIOS_TRUSTED_RELEASE_BUILDER",
"policy_sha256": _sha(admission_policy), "admitted_at": "2026-09-30T00:00:00Z",
}
record["mac"] = hmac.new(hmac_path.read_bytes().strip(), release_source_admission._canonical(record), hashlib.sha256).hexdigest()
_write(admission_root / f"{source}.json", (json.dumps(record, sort_keys=True) + "\n").encode())
script = _phase_script(root / "registered-phase-adapter.py")
executable = Path(sys.executable).resolve()
registry_record = _write(root / "registry.json", json.dumps(plan.finalization.registry_expected_record).encode())
baseline_record = _write(root / "baseline.json", json.dumps(plan.finalization.baseline_expected_record).encode())
config_value = {
"schema": host.SCHEMA, "version": exact.C5_VERSION,
"execution_scope": "ISOLATED_REHEARSAL", "instance_id": instance_id,
"executor_authority_commit": "a" * 40,
"executor_authority_tree": "b" * 40,
"provenance": {"environment": "ISOLATED_REHEARSAL", "fixture": True, "r14_source_receipt": False},
"candidate_id": candidate.candidate_id, "source_commit": source, "tree_oid": tree,
"artifact_sha256": candidate.artifact_sha256, "release_plan_sha256": plan.sha256,
"approval_fingerprint": approval.fingerprint, "state_root": str(root.resolve()),
"trusted_policy_path": str(trusted_policy), "trusted_policy_sha256": _sha(trusted_policy),
"approval_keyring_path": str(keyring_path.resolve()), "approval_keyring_sha256": _sha(keyring_path),
"source_admission_root": str(admission_root.resolve()),
"source_admission_policy_path": str(admission_policy), "source_admission_policy_sha256": _sha(admission_policy),
"source_admission_task_id": "FIXTURE-NOT-R14",
"source_admission_hmac_path": str(hmac_path), "source_admission_hmac_sha256": _sha(hmac_path),
"deployment_executable": str(executable), "deployment_executable_sha256": _sha(executable),
"deployment_adapter_path": str(script), "deployment_adapter_sha256": _sha(script),
"artifact_path": str(artifact), "rollback_target": rollback,
"rollback_artifact_path": str(rollback_artifact), "production_environment_file": str(environment),
"production_service": exact.C5_PRODUCTION_SERVICE, "production_port": exact.C5_PRODUCTION_PORT,
"repository_identity": host.REPOSITORY, "global_legacy_writes_enabled": False,
"authoritative_git_dir": str(repository), "git_executable": str(git), "git_executable_sha256": _sha(git),
"tag_command": _command(executable, script, "tag"),
"registry_command": _command(executable, script, "registry"),
"baseline_command": _command(executable, script, "baseline"),
"registry_record_path": str(registry_record), "baseline_record_path": str(baseline_record),
}
config_path = _write(root / "execution-config.json", (json.dumps(config_value, sort_keys=True) + "\n").encode())
provenance_value = {
"schema": host.PROVENANCE_SCHEMA,
"environment": "ISOLATED_REHEARSAL",
"instance_id": instance_id,
"authority_commit": config_value["executor_authority_commit"],
"authority_tree": config_value["executor_authority_tree"],
"entries": {
"host": {"path": str(Path(host.__file__).resolve()), "sha256": _sha(Path(host.__file__).resolve())},
"exact_deployment": {"path": str(Path(exact.__file__).resolve()), "sha256": _sha(Path(exact.__file__).resolve())},
"deployment": {"path": str(script), "sha256": _sha(script)},
"approved_executor": {"path": str(Path(host.approved_executor_module.__file__).resolve()), "sha256": _sha(Path(host.approved_executor_module.__file__).resolve())},
"tag": {"path": str(script), "sha256": _sha(script)},
"registry": {"path": str(script), "sha256": _sha(script)},
"baseline": {"path": str(script), "sha256": _sha(script)},
},
}
provenance_path = _write(root / "host-provenance.json", (json.dumps(provenance_value, sort_keys=True) + "\n").encode())
provenance = host.C5HostProvenance.load(provenance_path, expected_sha256=_sha(provenance_path), expected_owner_uid=None)
config = host.C5ExecutionHostConfig.load(
config_path,
expected_sha256=_sha(config_path),
host_provenance=provenance,
expected_owner_uid=None,
)
assert config.executor_authority_commit != config.source_commit
assert config.executor_authority_tree != config.tree_oid
wrong_executor_authority = {
**config_value,
"executor_authority_commit": "c" * 40,
}
wrong_executor_config = _write(
root / "wrong-executor-authority.json",
(json.dumps(wrong_executor_authority, sort_keys=True) + "\n").encode(),
)
with pytest.raises(
host.C5ExecutionHostBlocked,
match="HOST_PROVENANCE_BINDING_MISMATCH",
):
host.C5ExecutionHostConfig.load(
wrong_executor_config,
expected_sha256=_sha(wrong_executor_config),
host_provenance=provenance,
expected_owner_uid=None,
)
production_claim = {
**config_value,
"execution_scope": "PRODUCTION",
"provenance": {
"environment": "PRODUCTION",
"fixture": False,
"r14_source_receipt": True,
},
}
production_config = _write(
root / "production-claim.json",
(json.dumps(production_claim, sort_keys=True) + "\n").encode(),
)
with pytest.raises(
host.C5ExecutionHostBlocked,
match="HOST_PROVENANCE_BINDING_MISMATCH",
):
host.C5ExecutionHostConfig.load(
production_config,
expected_sha256=_sha(production_config),
host_provenance=provenance,
expected_owner_uid=None,
)
escaped_rehearsal = {**config_value, "rollback_artifact_path": str(tmp_path / "outside-c4.tar.gz")}
escaped_path = _write(
root / "escaped-rehearsal.json",
(json.dumps(escaped_rehearsal, sort_keys=True) + "\n").encode(),
)
with pytest.raises(host.C5ExecutionHostBlocked, match="REHEARSAL_FIXTURE_OUTSIDE_ISOLATION_ROOT"):
host.C5ExecutionHostConfig.load(
escaped_path,
expected_sha256=_sha(escaped_path),
host_provenance=provenance,
expected_owner_uid=None,
)
production_with_rehearsal_bindings = {
**config_value,
"execution_scope": "PRODUCTION",
"provenance": {"environment": "PRODUCTION", "fixture": False, "r14_source_receipt": True},
}
production_provenance = dataclasses.replace(provenance, environment="PRODUCTION")
production_with_rehearsal_path = _write(
root / "production-with-rehearsal-bindings.json",
(json.dumps(production_with_rehearsal_bindings, sort_keys=True) + "\n").encode(),
)
with pytest.raises(host.C5ExecutionHostBlocked, match="HOST_CONFIG_BINDING_INVALID"):
host.C5ExecutionHostConfig.load(
production_with_rehearsal_path,
expected_sha256=_sha(production_with_rehearsal_path),
host_provenance=production_provenance,
expected_owner_uid=None,
)
result = host.build_execution_host(config, expected_owner_uid=None).execute()
assert result["state"] == "COMPLETED"
assert result["execution_scope"] == "ISOLATED_REHEARSAL"
assert result["fixture_evidence"] is True
assert result["production_receipt"] is False
assert result["public_business_e2e_verified"] is False
assert result["production_acceptance"] is False
assert result["provenance"]["r14_source_receipt"] is False
def test_rehearsal_result_label_can_never_be_a_production_receipt():
fake_provenance = type(
"HostProvenance",
(),
{
"authority_commit": "a" * 40,
"authority_tree": "b" * 40,
"manifest_sha256": "c" * 64,
},
)()
fake = type("Config", (), {"candidate_id": "fixture", "execution_scope": "ISOLATED_REHEARSAL", "instance_id": "x", "provenance": {"fixture": True}, "host_provenance": fake_provenance})()
execution = host.C5ExecutionHost(fake, type("Orchestrator", (), {"execute": lambda self, _candidate: {"state": "COMPLETED"}})())
result = execution.execute()
assert result["production_receipt"] is False
+80 -16
View File
@@ -4,7 +4,7 @@ import hashlib
import io
import json
from pathlib import Path
import shutil
import re
import subprocess
import sys
import tarfile
@@ -19,25 +19,84 @@ def _sha(data: bytes) -> str:
def _fixture(tmp_path: Path) -> tuple[Path, str, Path, Path, Path]:
repo = Path(__file__).parents[1]
source_repo = Path(__file__).parents[1]
repo = tmp_path / "source"
repo.mkdir()
subprocess.run(["git", "init", "--quiet", str(repo)], check=True)
subprocess.run(
["git", "-C", str(repo), "config", "user.name", "C5 fixture"], check=True
)
subprocess.run(
["git", "-C", str(repo), "config", "user.email", "c5-fixture.invalid"],
check=True,
)
source_paths = (
"active-runtime/app/source_publication_runtime.py",
"active-runtime/app/governed_source_publication_policy.py",
"active-runtime/app/registered_owner_signer_adapter.py",
"active-runtime/app/aios_single_host_authority.py",
"active-runtime/app/c5_staging_installation.py",
)
for relative in source_paths:
destination = repo / relative
destination.parent.mkdir(parents=True, exist_ok=True)
destination.write_bytes((source_repo / relative).read_bytes())
bridge_base = b'"""Deterministic C5 bridge base fixture."""\n'
bridge_builder_source = (
source_repo / "scripts" / "build_c5_live_bridge_overlay.py"
).read_text(encoding="utf-8")
bridge_builder_source, replacements = re.subn(
r'(?m)^BASE_SHA256 = "[0-9a-f]{64}"$',
f'BASE_SHA256 = "{_sha(bridge_base)}"',
bridge_builder_source,
count=1,
)
assert replacements == 1
bridge_builder = repo / "scripts" / "build_c5_live_bridge_overlay.py"
bridge_builder.parent.mkdir(parents=True)
bridge_builder.write_text(bridge_builder_source, encoding="utf-8")
subprocess.run(["git", "-C", str(repo), "add", "--all"], check=True)
subprocess.run(
["git", "-C", str(repo), "commit", "--quiet", "-m", "C5 test source"],
check=True,
)
support = subprocess.check_output(
["git", "-C", str(repo), "rev-parse", "HEAD"], text=True
).strip()
live = repo.parent / "live-release"
overlay = tmp_path / "staging-overlay" / "app"
snapshot = overlay.parent.parent / "staging-source-snapshot"
source_snapshot = overlay.parent.parent / "staging-source-snapshot"
overlay.mkdir(parents=True)
snapshot.mkdir(parents=True)
for name in (
"aios_chatgpt_bridge_v2_server.py",
"aios_task_admission_bootstrap.py",
"aios_task_admission_live_adapters.py",
):
shutil.copy2(live / "staging-overlay" / "app" / name, overlay / name)
shutil.copy2(live / "staging-source-snapshot" / name, snapshot / name)
shutil.copy2(
live / "staging-overlay" / "c5-epoch-v2-overlay.result.json",
overlay.parent / "c5-epoch-v2-overlay.result.json",
source_snapshot.mkdir(parents=True)
fixture_modules = {
"aios_chatgpt_bridge_v2_server.py": (
bridge_base,
b'"""Deterministic C5 bridge overlay fixture."""\n',
),
"aios_task_admission_bootstrap.py": (
b'"""Deterministic C5 admission bootstrap base fixture."""\n',
b'"""Deterministic C5 admission bootstrap overlay fixture."""\n',
),
"aios_task_admission_live_adapters.py": (
b'"""Deterministic C5 admission adapters base fixture."""\n',
b'"""Deterministic C5 admission adapters overlay fixture."""\n',
),
}
overlay_result: dict[str, dict[str, int | str]] = {}
for name, (base, result) in fixture_modules.items():
(source_snapshot / name).write_bytes(base)
(overlay / name).write_bytes(result)
if name != "aios_chatgpt_bridge_v2_server.py":
overlay_result[name] = {
"base_sha256": _sha(base),
"base_bytes": len(base),
"result_sha256": _sha(result),
"result_bytes": len(result),
}
(overlay.parent / "c5-epoch-v2-overlay.result.json").write_text(
json.dumps({"files": overlay_result}, sort_keys=True), encoding="utf-8"
)
authority = tmp_path / "authority"
authority.mkdir()
@@ -51,7 +110,7 @@ def _fixture(tmp_path: Path) -> tuple[Path, str, Path, Path, Path]:
"policy_sha256": _sha(policy_bytes),
"policy_revocations": f"{root}/policy-revocations.json",
"admission_activation": f"{root}/admission-activation.json",
"expected_agent_id": "agent/codex-c5-release",
"expected_agent_id": "agent/fixture-c5-release",
}
(authority / "source-activation.json").write_bytes(json.dumps(source_activation).encode())
(authority / "policy-revocations.json").write_bytes(b"{}")
@@ -92,6 +151,11 @@ def test_builds_regular_only_archive_and_typed_request(tmp_path: Path) -> None:
paths = {record["path"] for record in request["files"]}
assert any(path.endswith("/app/c5_source_authority.py") for path in paths)
assert any(path.endswith("/app/aios_task_admission_live_adapters.py") for path in paths)
assert (
"/etc/systemd/system/aios-chatgpt-bridge-v2-staging.service.d/"
+ "~" * 128
+ "-c5-r14-source.conf"
) in paths
with tarfile.open(artifact, "r:") as bundle:
members = bundle.getmembers()
assert members
+398 -16
View File
@@ -28,13 +28,36 @@ from registered_owner_signer_adapter import RegisteredOwnerSignerAdapter
NOW = 1050
SOURCE = "536a2af0f07962495c88526061ce43fb27994c15"
SUPPORT = "04a5e3cbfecf8156074f3a68cd72b678c06ace11"
R14_PRINCIPAL_FIXTURE = {
"task_id": "fixture-c5-r14-source-publication",
"agent_id": "fixture-c5-r14-agent",
"execution_digest": "81045739c0adf64aab8d0ee13f211d60ab2ca710478140df56a78c96df8e3a62",
}
def sha(data: bytes) -> str:
return hashlib.sha256(data).hexdigest()
@pytest.mark.parametrize("profile", [c5._BRIDGE_PROFILE, c5._ISSUER_PROFILE])
def test_systemd_dropin_precedence_rejects_bootstrap_override(monkeypatch, profile):
backend = c5.SystemdBackend()
parent = Path("/etc/systemd/system") / (profile.unit + ".d")
active = parent / ("~" * 67 + "-c5-bootstrap-admission-20261001-r10.conf")
monkeypatch.setattr(backend, "_run", lambda command: SimpleNamespace(stdout=active.as_posix()))
with pytest.raises(c5.InstallationError, match="STAGING_DROPIN_PRECEDENCE_CONFLICT"):
backend.verify_dropin_precedence(profile, parent / ("~" * 64 + "-c5-r14-source.conf"))
backend.verify_dropin_precedence(profile, profile.dropin(c5._Layout()))
later = parent / ("~" * 129 + "-unexpected-override.conf")
monkeypatch.setattr(backend, "_run", lambda command: SimpleNamespace(stdout=later.as_posix()))
with pytest.raises(c5.InstallationError, match="STAGING_DROPIN_PRECEDENCE_CONFLICT"):
backend.verify_dropin_precedence(profile, profile.dropin(c5._Layout()))
def file_record(path: str, data: bytes, kind: str, mode: int = 0o644) -> dict:
fixture = Path(path)
if fixture.is_file() and fixture.read_bytes() == data:
fixture.chmod(mode)
return {
"path": path,
"sha256": sha(data),
@@ -54,9 +77,9 @@ def principal_material(root: Path) -> tuple[bytes, dict[str, bytes]]:
}
config = {
"schema": "aios.c5-task-principal-activation.v1",
"task_id": "TASK-V2R2C5-SOURCE-PUBLICATION-20260926-R1",
"agent_id": "agent/codex-c5-release",
"execution_digest": "1ba534d551991e8c99014285eeea36afcbc1500686035cb8bcd7f9f4efd6215e",
"task_id": "fixture-c5-source-publication",
"agent_id": "fixture-c5-agent",
"execution_digest": "1" * 64,
"store_path": "/var/lib/aios-gitops/c5-source-principal/enrollment.sqlite3",
"grant_db": "/var/lib/aios-gitops/bridge-v2-staging/task-admission.sqlite3",
"plan_dir": (root / "plans").as_posix(),
@@ -155,8 +178,8 @@ class FakeBackend:
True,
self.case.old_config_hash,
self.case.old_pythonpath,
None,
None,
self.case.payload["existing"]["activation_file"],
self.case.payload["existing"]["principal_scope_config"],
)
def daemon_reload(self) -> None:
@@ -463,6 +486,131 @@ def make_issuer_case(tmp_path: Path) -> Case:
return case
def make_successor_bridge_case(tmp_path: Path) -> Case:
case = make_case(tmp_path)
digest = R14_PRINCIPAL_FIXTURE["execution_digest"]
plan = (Path(__file__).parent / "fixtures" / "c5-r14-execution-plan.json").read_bytes()
assert sha(plan) == digest
previous = case.layout.release_base / "previous-release"
previous.mkdir()
old_activation = previous / "source-activation.json"
old_activation.write_bytes(b'{"schema":"previous-source-activation"}\n')
case.payload["schema"] = c5.SUCCESSOR_SCHEMA
case.payload["source_commit"] = c5.R14_SOURCE_COMMIT
case.payload["existing"]["activation_file"] = old_activation.as_posix()
case.payload["existing"]["config_files"] = [
file_record(old_activation.as_posix(), old_activation.read_bytes(), "existing_config")
]
case.payload["rollback"]["readiness"]["activation_file"] = old_activation.as_posix()
config_raw, supporting = principal_material(case.root)
config = json.loads(config_raw)
config.update(
task_id=R14_PRINCIPAL_FIXTURE["task_id"],
agent_id=R14_PRINCIPAL_FIXTURE["agent_id"],
execution_digest=digest,
)
supporting.pop("plans/task.json")
supporting[f"plans/{digest}.json"] = plan
supporting["admission-activation.json"] = c5.canonical_bytes(
{"schema": "aios.task-admission-host-activation.v1"}
)
config_raw = c5.canonical_bytes(config)
activation_raw = c5.canonical_bytes({
"schema": "aios.source-publication-host-activation.v1",
"policy_artifact": (case.root / "policy.json").as_posix(),
"policy_sha256": sha(supporting["policy.json"]),
"policy_revocations": (case.root / "policy-revocations.json").as_posix(),
"admission_activation": (case.root / "admission-activation.json").as_posix(),
"expected_agent_id": R14_PRINCIPAL_FIXTURE["agent_id"],
})
case.activation_bytes = activation_raw
case.principal_config_path = (case.root / "principal-activation.json").as_posix()
case.new_dropin = c5._dropin_bytes(
case.root, case.old_pythonpath,
target=c5.BRIDGE_TARGET, principal_config=True,
)
case.payload["readiness"]["principal_scope_config"] = case.principal_config_path
members = [
("app/aios_chatgpt_bridge_v2_server.py", case.new_module_bytes),
("source-activation.json", activation_raw),
("principal-activation.json", config_raw),
*sorted(supporting.items()),
]
case.files = [
file_record((case.root / name).as_posix(), data, "release_file")
for name, data in members
] + [file_record(case.layout.dropin.as_posix(), case.new_dropin, "systemd_dropin")]
case.payload["files"] = case.files
write_tar(case.artifact, [
(name, data, 0o644, "file") for name, data in members
] + [("dropin.conf", case.new_dropin, 0o644, "file")])
resign(case)
return case
def make_successor_issuer_case(tmp_path: Path) -> Case:
"""Build the R14 successor issuer case used by installer and seam tests."""
case = make_issuer_case(tmp_path)
digest = R14_PRINCIPAL_FIXTURE["execution_digest"]
plan = (Path(__file__).parent / "fixtures" / "c5-r14-execution-plan.json").read_bytes()
previous = case.layout.release_base / "previous-issuer"
previous.mkdir()
old_config = previous / "principal-activation.json"
old_config.write_bytes(b'{"schema":"previous-principal-activation"}\n')
case.payload["schema"] = c5.SUCCESSOR_SCHEMA
case.payload["source_commit"] = c5.R14_SOURCE_COMMIT
case.payload["existing"]["principal_scope_config"] = old_config.as_posix()
case.payload["existing"]["config_files"] = [
file_record(old_config.as_posix(), old_config.read_bytes(), "existing_config")
]
case.payload["rollback"]["readiness"]["principal_scope_config"] = old_config.as_posix()
case.old_runtime.update(
c5_scope_enabled=True, c5_scope_role="ISSUER", c5_scope_enrolled=False,
)
case.payload["rollback"]["readiness"]["runtime_fields"] = dict(case.old_runtime)
config_path = Path(case.principal_config_path)
config = json.loads(config_path.read_bytes())
config.update(
task_id=R14_PRINCIPAL_FIXTURE["task_id"],
agent_id=R14_PRINCIPAL_FIXTURE["agent_id"],
execution_digest=digest,
)
config_path.write_bytes(c5.canonical_bytes(config))
(case.root / "plans" / "task.json").unlink()
(case.root / "plans" / f"{digest}.json").write_bytes(plan)
admission = case.root / "admission-activation.json"
admission.write_bytes(
c5.canonical_bytes({"schema": "aios.task-admission-host-activation.v1"})
)
activation = {
"schema": "aios.source-publication-host-activation.v1",
"policy_artifact": (case.root / "policy.json").as_posix(),
"policy_sha256": sha((case.root / "policy.json").read_bytes()),
"policy_revocations": (case.root / "policy-revocations.json").as_posix(),
"admission_activation": admission.as_posix(),
"expected_agent_id": R14_PRINCIPAL_FIXTURE["agent_id"],
}
case.activation.write_bytes(c5.canonical_bytes(activation))
case.files = [
file_record(path.as_posix(), path.read_bytes(), "release_file")
for path in sorted(case.root.rglob("*")) if path.is_file()
] + [
file_record(
case.layout.issuer_dropin.as_posix(), case.new_dropin,
"systemd_dropin",
)
]
case.payload["files"] = case.files
write_tar(case.artifact, [
(path.relative_to(case.root).as_posix(), path.read_bytes(), 0o644, "file")
for path in sorted(case.root.rglob("*")) if path.is_file()
] + [("dropin.conf", case.new_dropin, 0o644, "file")])
resign(case)
return case
def resign(case: Case) -> None:
signature = sign_sshsig(case.key, c5.canonical_bytes(case.payload), namespace=OWNER_NAMESPACE)
case.contract.write_text(
@@ -663,6 +811,126 @@ def test_apply_and_same_contract_replay_verify_bytes_and_runtime(tmp_path: Path)
case.installer.apply(case.contract, case.artifact)
def test_successor_install_pins_old_activation_and_rolls_back(tmp_path: Path) -> None:
case = make_successor_bridge_case(tmp_path)
old_activation = Path(case.payload["existing"]["activation_file"])
old_bytes = old_activation.read_bytes()
assert case.installer.preflight(case.contract, case.artifact)["status"] == "PREFLIGHT_PASS"
case.backend.fail_new = True
with pytest.raises(c5.InstallationError, match="ROLLBACK_VERIFIED"):
case.installer.apply(case.contract, case.artifact)
assert old_activation.read_bytes() == old_bytes
assert case.layout.dropin.read_bytes() == case.old_dropin
assert not case.root.exists()
def test_successor_rejects_old_config_drift_before_side_effect(tmp_path: Path) -> None:
case = make_successor_bridge_case(tmp_path)
old_activation = Path(case.payload["existing"]["activation_file"])
old_activation.write_bytes(b"changed\n")
with pytest.raises(c5.InstallationError, match="INSTALLED_FILE_MISMATCH"):
case.installer.preflight(case.contract, case.artifact)
assert case.backend.reloads == case.backend.restarts == 0
assert case.layout.dropin.read_bytes() == case.old_dropin
@pytest.mark.parametrize("generation", ("r8", "r9", "r10"))
def test_successor_accepts_registered_bootstrap_config_root(
tmp_path: Path, generation: str,
) -> None:
case = make_successor_bridge_case(tmp_path)
old = Path(case.payload["existing"]["activation_file"])
legacy_root = (
case.layout.release_base.parent.parent
/ f"c5-bootstrap-{generation}-releases"
/ c5.BOOTSTRAP_SOURCE_NAME
)
legacy_root.mkdir(parents=True)
moved = legacy_root / old.name
old.replace(moved)
case.payload["existing"]["activation_file"] = moved.as_posix()
case.payload["existing"]["config_files"][0]["path"] = moved.as_posix()
case.payload["rollback"]["readiness"]["activation_file"] = moved.as_posix()
resign(case)
assert case.installer.preflight(case.contract, case.artifact)["status"] == "PREFLIGHT_PASS"
def test_successor_rejects_other_external_config_root(tmp_path: Path) -> None:
case = make_successor_bridge_case(tmp_path)
old = Path(case.payload["existing"]["activation_file"])
other = case.layout.release_base.parent.parent / "unregistered-release" / old.name
other.parent.mkdir(parents=True)
old.replace(other)
case.payload["existing"]["activation_file"] = other.as_posix()
case.payload["existing"]["config_files"][0]["path"] = other.as_posix()
case.payload["rollback"]["readiness"]["activation_file"] = other.as_posix()
resign(case)
with pytest.raises(c5.InstallationError, match="EXISTING_CONFIG_SNAPSHOT_INVALID"):
case.installer.preflight(case.contract, case.artifact)
@pytest.mark.parametrize("directory", (
"c5-bootstrap-r9-releases/other-source",
"c5-bootstrap-test-releases/c5-source-b915386ccbed46a7ae77220887da8ae666649a79",
"unregistered/c5-bootstrap-r9-releases/c5-source-b915386ccbed46a7ae77220887da8ae666649a79",
))
def test_successor_rejects_lookalike_bootstrap_config_root(
tmp_path: Path, directory: str,
) -> None:
case = make_successor_bridge_case(tmp_path)
old = Path(case.payload["existing"]["activation_file"])
other = case.layout.release_base.parent.parent / directory / old.name
other.parent.mkdir(parents=True)
old.replace(other)
case.payload["existing"]["activation_file"] = other.as_posix()
case.payload["existing"]["config_files"][0]["path"] = other.as_posix()
case.payload["rollback"]["readiness"]["activation_file"] = other.as_posix()
resign(case)
with pytest.raises(c5.InstallationError, match="EXISTING_CONFIG_SNAPSHOT_INVALID"):
case.installer.preflight(case.contract, case.artifact)
def test_successor_dropin_takes_precedence_over_current_r12_and_promotion() -> None:
assert c5.DROPIN.name > ("~" * 39 + "-c5-r12-source.conf")
assert c5.ISSUER_DROPIN.name > ("~" * 50 + "-c5-promotion-issuer-20260928.conf")
def test_successor_rejects_a_different_plan_even_with_matching_manifest(tmp_path: Path) -> None:
case = make_successor_bridge_case(tmp_path)
digest = R14_PRINCIPAL_FIXTURE["execution_digest"]
plan_name = f"plans/{digest}.json"
replacement = b'{"other":"plan"}'
with tarfile.open(case.artifact, "r") as source:
members = [
(item.name, source.extractfile(item).read(), item.mode, "file")
for item in source.getmembers()
]
members = [
(name, replacement if name == plan_name else data, mode, kind)
for name, data, mode, kind in members
]
for record in case.payload["files"]:
if record["path"] == (case.root / plan_name).as_posix():
record.update(sha256=sha(replacement), size=len(replacement))
write_tar(case.artifact, members)
resign(case)
with pytest.raises(c5.InstallationError, match="PRINCIPAL_PLAN_DIGEST_MISMATCH"):
case.installer.preflight(case.contract, case.artifact)
def test_successor_issuer_preserves_promotion_config_on_failed_cutover(tmp_path: Path) -> None:
case = make_successor_issuer_case(tmp_path)
old_config = Path(case.payload["existing"]["principal_scope_config"])
assert case.installer.preflight(case.contract, case.artifact)["status"] == "PREFLIGHT_PASS"
case.backend.fail_new = True
with pytest.raises(c5.InstallationError, match="ROLLBACK_VERIFIED"):
case.installer.apply(case.contract, case.artifact)
assert old_config.read_bytes() == b'{"schema":"previous-principal-activation"}\n'
assert case.layout.issuer_dropin.read_bytes() == case.old_dropin
def test_future_bridge_contract_requires_governed_writes_readiness(
tmp_path: Path,
) -> None:
@@ -726,6 +994,110 @@ def test_different_nonce_or_digest_conflicts_after_install(tmp_path: Path) -> No
case.installer.apply(case.contract, case.artifact)
def test_fresh_contract_after_rolled_back_support_reuses_no_terminal_state(
tmp_path: Path,
) -> None:
case = make_case(tmp_path)
case.backend.fail_new = True
with pytest.raises(c5.InstallationError, match="ROLLBACK_VERIFIED"):
case.installer.apply(case.contract, case.artifact)
old_contract = case.payload["contract_id"]
case.payload["contract_id"] = "c5-installation-fresh-after-rollback"
case.payload["nonce"] = "nonce-c5-installation-fresh-0001"
resign(case)
case.backend.fail_new = False
assert case.installer.apply(case.contract, case.artifact)["status"] == "APPLIED"
assert case.payload["contract_id"] != old_contract
assert case.installer._state_path(case.payload) != (
case.layout.state_root / f"{case.payload['target']}-support-{case.payload['support_commit']}.json"
)
def test_rolled_back_original_contract_remains_terminal_barrier(
tmp_path: Path,
) -> None:
case = make_case(tmp_path)
case.backend.fail_new = True
with pytest.raises(c5.InstallationError, match="ROLLBACK_VERIFIED"):
case.installer.apply(case.contract, case.artifact)
case.backend.fail_new = False
with pytest.raises(c5.InstallationError, match="INSTALL_PRIOR_ATTEMPT_NOT_APPLIED"):
case.installer.apply(case.contract, case.artifact)
def test_applied_support_rejects_different_contract_identity(
tmp_path: Path,
) -> None:
case = make_case(tmp_path)
assert case.installer.apply(case.contract, case.artifact)["status"] == "APPLIED"
case.payload["contract_id"] = "c5-installation-different-contract"
case.payload["nonce"] = "nonce-c5-installation-different-0001"
resign(case)
with pytest.raises(c5.InstallationError, match="INSTALL_REPLAY_CONFLICT"):
case.installer.apply(case.contract, case.artifact)
def test_successive_rolled_back_contract_generations_can_progress(
tmp_path: Path,
) -> None:
case = make_case(tmp_path)
case.backend.fail_new = True
with pytest.raises(c5.InstallationError, match="ROLLBACK_VERIFIED"):
case.installer.apply(case.contract, case.artifact)
case.backend.fail_new = False
for generation in ("a", "b"):
case.payload["contract_id"] = f"c5-installation-generation-{generation}"
case.payload["nonce"] = f"nonce-c5-installation-generation-{generation}-0001"
resign(case)
case.backend.fail_new = True
with pytest.raises(c5.InstallationError, match="ROLLBACK_VERIFIED"):
case.installer.apply(case.contract, case.artifact)
case.backend.fail_new = False
def test_successor_contract_is_blocked_by_unknown_generation(
tmp_path: Path,
) -> None:
case = make_case(tmp_path)
case.backend.fail_new = True
with pytest.raises(c5.InstallationError, match="ROLLBACK_VERIFIED"):
case.installer.apply(case.contract, case.artifact)
case.backend.fail_old = False
case.payload["contract_id"] = "c5-installation-generation-unknown"
case.payload["nonce"] = "nonce-c5-installation-generation-unknown-0001"
resign(case)
case.backend.fail_new = True
case.backend.fail_old = True
with pytest.raises(c5.InstallationOutcomeUnknown):
case.installer.apply(case.contract, case.artifact)
case.backend.fail_new = False
case.backend.fail_old = False
case.payload["contract_id"] = "c5-installation-successor-after-unknown"
case.payload["nonce"] = "nonce-c5-installation-successor-unknown-0001"
resign(case)
with pytest.raises(c5.InstallationError, match="INSTALL_PRIOR_ATTEMPT_NOT_APPLIED"):
case.installer.apply(case.contract, case.artifact)
def test_successor_contract_is_blocked_by_applied_generation(
tmp_path: Path,
) -> None:
case = make_case(tmp_path)
case.backend.fail_new = True
with pytest.raises(c5.InstallationError, match="ROLLBACK_VERIFIED"):
case.installer.apply(case.contract, case.artifact)
case.backend.fail_new = False
case.payload["contract_id"] = "c5-installation-generation-applied"
case.payload["nonce"] = "nonce-c5-installation-generation-applied-0001"
resign(case)
assert case.installer.apply(case.contract, case.artifact)["status"] == "APPLIED"
case.payload["contract_id"] = "c5-installation-successor-after-applied"
case.payload["nonce"] = "nonce-c5-installation-successor-applied-0001"
resign(case)
with pytest.raises(c5.InstallationError, match="INSTALL_PRIOR_ATTEMPT_NOT_APPLIED"):
case.installer.apply(case.contract, case.artifact)
def test_existing_release_directory_is_never_overwritten(tmp_path: Path) -> None:
case = make_case(tmp_path)
case.root.mkdir()
@@ -867,7 +1239,7 @@ class SigningRunner:
return SimpleNamespace(returncode=0)
def producer_case(tmp_path: Path, ttl: int = 100):
def producer_case(tmp_path: Path, monkeypatch, ttl: int = 100):
key = Ed25519PrivateKey.generate()
runner = SigningRunner(key)
registry = tmp_path / "producer-registry.json"
@@ -885,14 +1257,23 @@ def producer_case(tmp_path: Path, ttl: int = 100):
test_mode=True,
runner=runner,
)
root = c5.RELEASE_BASE / f"c5-source-{SUPPORT}"
layout = c5._Layout(
release_base=tmp_path / "var" / "releases",
dropin=tmp_path / "etc" / f"{c5.UNIT}.d" / c5.DROPIN.name,
issuer_dropin=tmp_path / "etc" / f"{c5.ISSUER_UNIT}.d" / c5.ISSUER_DROPIN.name,
state_root=tmp_path / "var" / "state",
test_only=True,
)
monkeypatch.setattr(c5, "_Layout", lambda: layout)
root = layout.release_base / f"c5-source-{SUPPORT}"
module = root / "app" / "aios_chatgpt_bridge_v2_server.py"
activation = root / "source-activation.json"
old_module = Path("/opt/aios/current/app/aios_chatgpt_bridge_v2_server.py")
old_root = tmp_path / "opt" / "aios" / "current"
old_module = old_root / "app" / "aios_chatgpt_bridge_v2_server.py"
module_bytes = b"new\n"
activation_bytes = b"{}\n"
old_bytes = b"old\n"
old_paths = ("/opt/aios/current", "/opt/aios/current/app")
old_paths = (old_root.as_posix(), (old_root / "app").as_posix())
dropin = c5._dropin_bytes(root, old_paths)
new_runtime = {
"status": "active", "health": 200, "deployment_state": "STAGING",
@@ -912,7 +1293,7 @@ def producer_case(tmp_path: Path, ttl: int = 100):
files=(
c5.FileRecord(**file_record(module.as_posix(), module_bytes, "release_file")),
c5.FileRecord(**file_record(activation.as_posix(), activation_bytes, "release_file")),
c5.FileRecord(**file_record(c5.DROPIN.as_posix(), dropin, "systemd_dropin")),
c5.FileRecord(**file_record(layout.dropin.as_posix(), dropin, "systemd_dropin")),
),
existing={
"unit_active": True,
@@ -950,11 +1331,11 @@ def producer_case(tmp_path: Path, ttl: int = 100):
("dropin.conf", dropin, 0o644, "file"),
],
)
return request, artifact, registry, signer, runner
return request, artifact, registry, signer, runner, layout
def test_typed_producer_validates_then_signs_and_consumer_verifies(tmp_path: Path) -> None:
request, artifact, registry, signer, runner = producer_case(tmp_path)
def test_typed_producer_validates_then_signs_and_consumer_verifies(tmp_path: Path, monkeypatch) -> None:
request, artifact, registry, signer, runner, layout = producer_case(tmp_path, monkeypatch)
outer = c5.issue_signed_contract(
request,
artifact,
@@ -971,12 +1352,13 @@ def test_typed_producer_validates_then_signs_and_consumer_verifies(tmp_path: Pat
registry,
now=NOW,
require_root_owner=False,
_layout=layout,
)
assert payload["owner_key_id"] == signer.fingerprint
def test_typed_producer_rejects_before_private_signer_call(tmp_path: Path) -> None:
request, artifact, registry, signer, runner = producer_case(tmp_path, ttl=901)
def test_typed_producer_rejects_before_private_signer_call(tmp_path: Path, monkeypatch) -> None:
request, artifact, registry, signer, runner, _layout = producer_case(tmp_path, monkeypatch, ttl=901)
with pytest.raises(c5.InstallationError, match="INSTALL_TTL_OR_NONCE_INVALID"):
c5.issue_signed_contract(
request,
@@ -996,5 +1378,5 @@ def test_production_cli_has_no_test_backend_or_arbitrary_root() -> None:
assert "--test-backend" not in source
assert "--release-root" not in source
assert c5.DROPIN == Path("/etc/systemd/system") / f"{c5.UNIT}.d" / (
"~" * 32 + "-c5-source.conf"
"~" * 128 + "-c5-r14-source.conf"
)
+50 -1
View File
@@ -70,7 +70,8 @@ class Writer:
}
def client(writer: Writer, files=None, readback=None, *, resolver=None, git=False) -> GovernedPublicationClient:
def client(writer: Writer, files=None, readback=None, *, resolver=None, git=False,
diagnostic_sink=None) -> GovernedPublicationClient:
i = intent()
source = files or {p: p.encode() for p in i.file_allowlist}
evidence = readback or {
@@ -90,6 +91,7 @@ def client(writer: Writer, files=None, readback=None, *, resolver=None, git=Fals
authenticated_readback=lambda **_kwargs: evidence,
subject="aios-autonomous-admin", manifest_id="manifest:c5",
runtime_fingerprint="runtime:c5", process_start_identity="proc:c5",
diagnostic_sink=diagnostic_sink,
)
@@ -162,6 +164,53 @@ def test_transport_failure_is_unknown_and_never_retried():
assert [call[0] for call in writer.calls] == ["acquire"]
def test_isolated_diagnostic_records_only_safe_issuer_fields():
class IssuerFailure(RuntimeError):
status = 403
error_code = "EXECUTION_PLAN_SCHEMA_INVALID"
operation = "acquireWriterLease"
evidence = []
writer = Writer(fail=IssuerFailure("token=secret-value signature=secret-value"))
i = intent()
with pytest.raises(GovernedPublicationError, match="PUBLICATION_EFFECT_UNKNOWN"):
client(writer, diagnostic_sink=evidence.append).publish(i, admission(i))
assert evidence == [{
"task_id": i.task_id, "operation": "acquireWriterLease",
"http_status": 403, "error_code": "EXECUTION_PLAN_SCHEMA_INVALID",
}]
assert "secret-value" not in str(evidence)
assert [call[0] for call in writer.calls] == ["acquire"]
def test_isolated_diagnostic_marks_transport_unknown_without_inventing_http():
evidence = []
i = intent()
with pytest.raises(GovernedPublicationError, match="PUBLICATION_EFFECT_UNKNOWN"):
client(Writer(fail=TimeoutError("token=secret-value")),
diagnostic_sink=evidence.append).publish(i, admission(i))
assert evidence == [{
"task_id": i.task_id, "operation": "acquireWriterLease",
"http_status": None, "error_code": "transport_unknown",
}]
assert "secret-value" not in str(evidence)
def test_isolated_diagnostic_rejects_unrecognized_response_code():
class UntrustedCode(RuntimeError):
status = 403
error_code = "TOKEN_SECRET_VALUE"
evidence = []
i = intent()
with pytest.raises(GovernedPublicationError, match="PUBLICATION_EFFECT_UNKNOWN"):
client(Writer(fail=UntrustedCode("secret")),
diagnostic_sink=evidence.append).publish(i, admission(i))
assert evidence[0]["http_status"] == 403
assert evidence[0]["error_code"] == "issuer_error_unknown"
assert "TOKEN_SECRET_VALUE" not in str(evidence)
def test_missing_authenticated_readback_fails_closed():
writer = Writer()
i = intent()
+58
View File
@@ -0,0 +1,58 @@
"""Immutable V3 issuer overlay assembly from a committed source tree."""
from __future__ import annotations
import importlib.util
from pathlib import Path
import subprocess
import tarfile
import pytest
ROOT = Path(__file__).resolve().parents[1]
spec = importlib.util.spec_from_file_location(
"build_c5_live_principal_overlay", ROOT / "scripts" / "build_c5_live_principal_overlay.py",
)
assert spec is not None and spec.loader is not None
builder = importlib.util.module_from_spec(spec)
spec.loader.exec_module(builder)
def _commit() -> str:
return subprocess.run(
["git", "rev-parse", "HEAD"], cwd=ROOT, check=True,
capture_output=True, text=True,
).stdout.strip()
def test_overlay_archive_is_deterministic_and_hash_bound(tmp_path):
fixture = ROOT / "tests" / "fixtures" / "issuer-v3-base"
manifests = [builder.build_issuer_v3_overlay(
adapter_source_root=fixture, output_root=tmp_path / str(i),
source_commit=_commit(), repository_root=ROOT,
) for i in (1, 2)]
assert manifests[0]["artifact_sha256"] == manifests[1]["artifact_sha256"]
assert manifests[0]["file_manifest_sha256"] == manifests[1]["file_manifest_sha256"]
archive = tmp_path / "1" / manifests[0]["artifact_filename"]
assert builder.sha256(archive) == manifests[0]["artifact_sha256"]
with tarfile.open(archive, "r") as bundle:
assert set(bundle.getnames()) == set(manifests[0]["files"])
for member in bundle:
assert member.uid == member.gid == member.mtime == 0
assert member.mode == 0o444
assert builder.hashlib.sha256(bundle.extractfile(member).read()).hexdigest() == (
manifests[0]["files"][member.name]
)
def test_base_snapshot_drift_is_rejected_before_assembly(tmp_path):
source = ROOT / "tests" / "fixtures" / "issuer-v3-base"
copied = tmp_path / "base"
copied.mkdir()
for item in source.glob("*.py"):
(copied / item.name).write_bytes(item.read_bytes())
(copied / "governed_runtime_scope.py").write_bytes(b"changed")
with pytest.raises(SystemExit, match="ISSUER_V3_BASE_HASH_MISMATCH"):
builder.build_issuer_v3_overlay(
adapter_source_root=copied, output_root=tmp_path / "out",
source_commit=_commit(), repository_root=ROOT,
)
+227
View File
@@ -0,0 +1,227 @@
"""Isolated issuer V3 readback against the real V1/V2 base snapshots."""
from dataclasses import asdict, replace
import hashlib
import json
import os
from pathlib import Path
import sqlite3
import sys
import pytest
HERE = Path(__file__).resolve().parent
ROOT = HERE.parent
sys.path.insert(0, str(HERE / "fixtures" / "issuer-v3-base"))
sys.path.insert(0, str(ROOT / "active-runtime" / "app"))
import governed_runtime_scope as scope # noqa: E402
import aios_task_admission_live_adapters as admission_adapter # noqa: E402
from aios_task_principal_v3_receipt_provider import ( # noqa: E402
IssuerV3ReceiptScopeProvider, _read_only_bootstrap,
)
from test_generic_reserve_before_admit import ( # noqa: E402
plan_for, write_plan, runtime_for, bound_provider, RecordingTransport,
)
pytestmark = pytest.mark.skipif(os.name != "posix", reason="producer store requires POSIX")
class Legacy:
def __init__(self):
self.calls = 0
def resolve(self, **_kwargs):
self.calls += 1
return "LEGACY"
def fixture(tmp_path, monkeypatch):
plan = plan_for()
grant = write_plan(tmp_path / "plans", plan)
events = []
runtime = runtime_for(tmp_path, plan, monkeypatch, events)
transport = RecordingTransport([])
port = admission_adapter.AgentTaskAdmissionPort(
transport, execution_plan_provider=bound_provider(runtime, tmp_path / "plans")
)
assert port.admit(grant)["state"] == "QUEUED"
job_id = runtime.bootstrap.get_binding(grant.task_id).job_id
raw = asdict(grant)
legacy = Legacy()
provider = IssuerV3ReceiptScopeProvider(
legacy=legacy, producer_state_root=tmp_path / "runtime",
plan_root=tmp_path / "plans", grant_reader=lambda **_kwargs: raw,
plan_loader=lambda root, g: admission_adapter.validate_v3_plan(
grant=g, raw=(root / (g["execution_digest"] + ".json")).read_bytes(),
),
)
task = scope.TaskSnapshot(grant.task_id, "RUNNING")
execution = scope.ExecutionSnapshot(
grant.task_id, job_id, grant.repository, grant.target_branch,
grant.generation, grant.execution_digest,
)
grant_snapshot = scope.GrantSnapshot(
grant.grant_id, "ADMITTED", grant.task_id, grant.repository,
grant.authority_base_oid, grant.target_branch, grant.generation,
grant.execution_digest, tuple(grant.capabilities),
tuple(grant.file_allowlist), grant.expires_at,
)
trust = scope.TrustSnapshot(
issuer_id="issuer", key_id="key", public_key="public",
purpose="TASK_PRINCIPAL", bridge_id="bridge",
)
return locals()
def resolve(f, **changes):
values = {key: f[key] for key in ("task", "execution", "grant_snapshot", "plan", "trust")}
values.update(changes)
return f["provider"].resolve(
task=values["task"], execution=values["execution"],
grant=values["grant_snapshot"], plan=values["plan"],
trust=values["trust"], now=f["grant"].issued_at + 1,
)
def test_v3_receipt_scope_preserves_original_digest_and_job(tmp_path, monkeypatch):
f = fixture(tmp_path, monkeypatch)
result = resolve(f)
assert result.external_job_id == f["job_id"]
assert result.execution_digest == f["grant"].execution_digest
assert result.execution_digest == hashlib.sha256(
admission_adapter.canonical_json_bytes(f["plan"])
).hexdigest()
assert f["legacy"].calls == 0
job, proof = f["provider"].inspect_v3_binding(
grant_raw=f["raw"], plan=f["plan"],
)
assert job == proof["job_id"] == f["job_id"]
@pytest.mark.parametrize("schema", [scope.PLAN_V1, scope.PLAN_V2])
def test_v1_v2_remain_exact_legacy_dispatch(tmp_path, monkeypatch, schema):
f = fixture(tmp_path, monkeypatch)
assert resolve(f, plan={"schema": schema}) == "LEGACY"
assert f["legacy"].calls == 1
@pytest.mark.parametrize("schema", [scope.PLAN_V1, scope.PLAN_V2])
def test_v1_v2_actual_scope_equals_hash_pinned_legacy_provider(tmp_path, monkeypatch, schema):
f = fixture(tmp_path, monkeypatch)
plan = dict(f["plan"])
plan["schema"] = schema
plan["external_job_id"] = f["job_id"]
if schema == scope.PLAN_V1:
plan["files"] = list(plan["file_allowlist"])
plan["purpose"] = "governed source publication"
digest = scope.sha256_json(plan)
execution = replace(f["execution"], execution_digest=digest)
grant_snapshot = replace(f["grant_snapshot"], execution_digest=digest)
legacy = scope.GovernedRuntimeScopeProvider()
provider = IssuerV3ReceiptScopeProvider(
legacy=legacy, producer_state_root=tmp_path / "runtime",
plan_root=tmp_path / "plans", grant_reader=lambda **_kwargs: None,
)
args = dict(
task=f["task"], execution=execution, grant=grant_snapshot,
plan=plan, trust=f["trust"], now=f["grant"].issued_at + 1,
)
assert provider.resolve(**args) == legacy.resolve(**args)
@pytest.mark.parametrize("field,value", [
("task_id", "task-wrong"),
("repository", "other/repo"),
("target_branch", "agent/other-branch"),
("generation", 2),
("authority_base_oid", "a" * 40),
("file_allowlist", ["active-runtime/app/other.py"]),
("file_sha256", {"active-runtime/app/task_plane/api.py": "a" * 64}),
("capabilities", ["other.capability"]),
("risk", "high"),
])
def test_v3_wrong_canonical_plan_is_rejected(tmp_path, monkeypatch, field, value):
f = fixture(tmp_path, monkeypatch)
bad = dict(f["plan"])
bad[field] = value
with pytest.raises((scope.GovernedScopeError, admission_adapter.ProducerReceiptError)):
resolve(f, plan=bad)
@pytest.mark.parametrize("field,value", [
("task_id", "task-wrong"),
("repository", "other/repo"),
("target_branch", "agent/wrong"),
("generation", 99),
("execution_digest", "a" * 64),
])
def test_execution_identity_mismatch_is_rejected(tmp_path, monkeypatch, field, value):
f = fixture(tmp_path, monkeypatch)
with pytest.raises(scope.GovernedScopeError):
resolve(f, execution=replace(f["execution"], **{field: value}))
@pytest.mark.parametrize("field,value", [
("authority_base_oid", "a" * 40),
("requested_capabilities", ("other.capability",)),
("file_allowlist", ("active-runtime/app/other.py",)),
])
def test_grant_snapshot_scope_mismatch_is_rejected(tmp_path, monkeypatch, field, value):
f = fixture(tmp_path, monkeypatch)
with pytest.raises(scope.GovernedScopeError):
resolve(f, grant_snapshot=replace(f["grant_snapshot"], **{field: value}))
def test_missing_producer_receipt_and_forged_job_fail_closed(tmp_path, monkeypatch):
f = fixture(tmp_path, monkeypatch)
with pytest.raises(scope.GovernedScopeError, match="EXECUTION_PRODUCER_JOB_MISMATCH"):
resolve(f, execution=replace(f["execution"], external_job_id="job-forged"))
# An absent binding is corruption, not a prompt to call producer.create().
f["runtime"].bootstrap._metadata(f["grant"].task_id).unlink()
with pytest.raises(scope.GovernedScopeError, match="PRODUCER_RECEIPT_MISSING"):
resolve(f)
def test_producer_binding_conflict_is_rejected(tmp_path, monkeypatch):
f = fixture(tmp_path, monkeypatch)
metadata = f["runtime"].bootstrap._metadata(f["grant"].task_id)
value = json.loads(metadata.read_text())
value["target_branch"] = "agent/forged"
metadata.write_text(json.dumps(value) + "\n")
with pytest.raises(scope.GovernedScopeError, match="DURABLE_BINDING_CONFLICT"):
resolve(f)
def test_exact_replay_reads_without_new_producer_effect(tmp_path, monkeypatch):
f = fixture(tmp_path, monkeypatch)
db_path, = (tmp_path / "runtime" / "jobs").rglob("state.sqlite3")
def rows():
with sqlite3.connect(db_path.as_uri() + "?mode=ro", uri=True) as db:
return db.execute("SELECT job_id, state, side_effect_state FROM jobs").fetchall()
before = rows()
creates_before = f["events"].count("bootstrap.create")
assert resolve(f) == resolve(f)
assert rows() == before
assert f["events"].count("bootstrap.create") == creates_before
def test_expired_task_and_grant_fail_closed_after_readonly_binding(tmp_path, monkeypatch):
f = fixture(tmp_path, monkeypatch)
assert f["provider"].inspect_v3_binding(
grant_raw=f["raw"], plan=f["plan"],
)[0] == f["job_id"]
with pytest.raises(scope.GovernedScopeError, match="TASK_NOT_CLAIMED"):
resolve(f, task=replace(f["task"], status="EXPIRED"))
f["grant_snapshot"] = replace(f["grant_snapshot"], expires_at=f["grant"].issued_at)
with pytest.raises(scope.GovernedScopeError, match="GRANT_EXPIRED_OR_NOT_ADMITTED"):
resolve(f)
def test_readonly_view_denies_all_mutating_producer_entries(tmp_path, monkeypatch):
f = fixture(tmp_path, monkeypatch)
view = _read_only_bootstrap(tmp_path / "runtime")
for operation in (view.create, view._store, view._write):
with pytest.raises(scope.GovernedScopeError, match="MUTATION_FORBIDDEN"):
operation({})
assert resolve(f).external_job_id == f["job_id"]
@@ -108,6 +108,8 @@ class SignerContractCompatibilityTests(unittest.TestCase):
def test_real_signer_help_and_self_test_are_available(self) -> None:
signer = "/usr/local/libexec/aios-grant-signer-adapter"
if os.environ.get("AIOS_TEST_LIVE_SIGNER") != "1":
self.skipTest("live signer compatibility requires AIOS_TEST_LIVE_SIGNER=1")
if not Path(signer).is_file():
self.skipTest("deployed signer is not present")
if os.geteuid() != 0:
+112
View File
@@ -722,6 +722,118 @@ def test_unknown_release_source_requires_explicit_admission() -> None:
config.resolve_recipe("V2R2C4", None, "2" * 40)
def test_c5_successor_recipe_binds_authenticated_admission_and_contract() -> None:
policy_path = ROOT / "tests" / "fixtures" / "trusted-candidate-builder-policy-c5.json"
config = TrustedCandidateBuilderConfig.from_policy(load_builder_policy(policy_path))
source = "2" * 40
admitted = {
"mode": "governed-source-admission", "task_id": config.c5_contract["task_id"],
"candidate_id": "rc-v2r2c5-222222222222", "base_oid": config.c5_contract["authority_base_oid"],
"source_commit": source, "version": "V2R2C5", "purpose": "AIOS_TRUSTED_RELEASE_BUILDER",
"tree_oid": "3" * 40,
}
config = dataclasses.replace(config, source_admission_resolver=lambda *_: dict(admitted))
recipe = config.resolve_recipe("V2R2C5", admitted["candidate_id"], source)
assert recipe["tree_oid"] == "3" * 40
assert recipe["accepted_base_oid"] == "e5f8209f57b43fc39e55c4555776e6362ce326eb"
assert recipe["rollback_artifact_sha256"] == "0dfc0461f7f9aa25cfbfc37008dcbd2d48d5341383574163b7bba102852062c4"
def integration_policy():
policy = load_builder_policy(ROOT / "tests/fixtures/trusted-candidate-builder-policy-c5.json")
policy["schema"] = "aios.trusted-candidate-builder-policy.c5-integration.v1"
policy["recipes"]["V2R2C5"]["mode"] = "governed-source-integration-only"
policy["c5_contract"].update({
"integration_source_commit": "5f65e458957dd51c9973cfa956a624f3cbf54990",
"integration_tree_oid": "ddd239d43d63a7fbd3cff46119c71466c9443737",
"publication_source_commit": "d9597cba91abff90288186bb20b882c4d706a55d",
"publication_tree_oid": "0597372a2b60991433b9920e50dcc45eac7f2c2e",
"publication_plan_sha256": "0779fa615e8e7971a8dcfccf0f295b1323cd9b64787e174aea4fd2affc414339",
"repair_source_commit": "c74db7a426eb74732ed476687314bd52cf140f3b",
"repair_tree_oid": "9042109dc12158defa59031e9aea0b08777bd492",
})
return policy
def integration_admission():
contract = integration_policy()["c5_contract"]
return {
"mode": "governed-source-admission", "task_id": contract["task_id"],
"candidate_id": "rc-v2r2c5-5f65e458957d", "base_oid": contract["authority_base_oid"],
"source_commit": contract["integration_source_commit"], "version": "V2R2C5",
"purpose": "AIOS_TRUSTED_RELEASE_BUILDER", "tree_oid": contract["integration_tree_oid"],
}
def test_c5_integration_recipe_retains_frozen_publication_and_independent_repair():
config = TrustedCandidateBuilderConfig.from_policy(integration_policy())
admission = integration_admission()
config = dataclasses.replace(config, source_admission_resolver=lambda *_: dict(admission))
recipe = config.resolve_recipe("V2R2C5", admission["candidate_id"], admission["source_commit"])
assert recipe["publication_source_commit"] == "d9597cba91abff90288186bb20b882c4d706a55d"
assert recipe["repair_source_commit"] == "c74db7a426eb74732ed476687314bd52cf140f3b"
assert recipe["source_commit"] != recipe["publication_source_commit"]
assert recipe["rollback_version"] == "V2R2C4"
@pytest.mark.parametrize("field", sorted(builder_runtime.C5_INTEGRATION_CONTRACT_FIELDS))
def test_c5_integration_policy_rejects_component_identity_drift(field):
policy = integration_policy()
policy["c5_contract"][field] = "wrong"
with pytest.raises(BuilderError, match="POLICY_V2R2C5_CONTRACT_INVALID"):
TrustedCandidateBuilderConfig.from_policy(policy)
@pytest.mark.parametrize("field", ["task_id", "base_oid", "source_commit", "tree_oid", "candidate_id"])
def test_c5_integration_rejects_wrong_admission(field):
config = TrustedCandidateBuilderConfig.from_policy(integration_policy())
admission = integration_admission()
source, candidate = admission["source_commit"], admission["candidate_id"]
admission[field] = "f" * 40 if field.endswith("oid") or field == "source_commit" else "wrong"
config = dataclasses.replace(config, source_admission_resolver=lambda *_: dict(admission))
with pytest.raises(BuilderError):
config.resolve_recipe("V2R2C5", candidate, source)
def test_c5_integration_collector_rejects_undeclared_source_changes(tmp_path):
config = TrustedCandidateBuilderConfig.from_policy(integration_policy())
admission = integration_admission()
config = dataclasses.replace(config, source_admission_resolver=lambda *_: dict(admission))
recipe = config.resolve_recipe("V2R2C5", admission["candidate_id"], admission["source_commit"])
trees = {recipe[c]: recipe[t] for c, t in [
("integration_source_commit", "integration_tree_oid"),
("publication_source_commit", "publication_tree_oid"),
("repair_source_commit", "repair_tree_oid"),
]}
def run(argv, **kwargs):
operation = argv[2]
if operation == "rev-parse":
return SimpleNamespace(stdout=trees[argv[3].split("^")[0]] + "\n")
if operation == "show":
return SimpleNamespace(stdout=recipe["authority_base_oid"] + "\n")
assert operation == "diff"
return SimpleNamespace(stdout="\n".join([*builder_runtime.C5_INTEGRATION_FILES, "unexpected.py"]))
source = ServerOwnedEvidenceSource(config, authority_git_dir=tmp_path, evidence_root=tmp_path, git_runner=run)
with pytest.raises(BuilderError, match="COLLECTOR_INTEGRATION_FILE_SET_MISMATCH"):
source._validate_integration_source(recipe)
@pytest.mark.parametrize("field", ["task_id", "base_oid"])
def test_c5_successor_recipe_rejects_wrong_authenticated_binding(field: str) -> None:
policy_path = ROOT / "tests" / "fixtures" / "trusted-candidate-builder-policy-c5.json"
config = TrustedCandidateBuilderConfig.from_policy(load_builder_policy(policy_path))
source = "2" * 40
admitted = {
"mode": "governed-source-admission", "task_id": config.c5_contract["task_id"],
"candidate_id": "rc-v2r2c5-222222222222", "base_oid": config.c5_contract["authority_base_oid"],
"source_commit": source, "version": "V2R2C5", "purpose": "AIOS_TRUSTED_RELEASE_BUILDER", "tree_oid": "3" * 40,
}
admitted[field] = "wrong" if field == "task_id" else "f" * 40
config = dataclasses.replace(config, source_admission_resolver=lambda *_: dict(admitted))
with pytest.raises(BuilderError):
config.resolve_recipe("V2R2C5", admitted["candidate_id"], source)
def test_governed_source_admission_rejects_caller_candidate_rebinding() -> None:
policy = load_builder_policy(POLICY_PATH)
@@ -2,9 +2,16 @@ from __future__ import annotations
import os
import sys
import hashlib
import io
import json
import subprocess
import tarfile
import pytest
from pathlib import Path
from trusted_evidence_providers import _run, _tests_env
import trusted_evidence_providers as providers
REPO_ROOT = Path(__file__).resolve().parents[1]
@@ -31,3 +38,59 @@ def test_run_extra_env_reaches_pytest_child_process():
result = _run(probe, extra_env={"PROBE_EXTRA": "v2r2c2-gatew-provider-harness"})
assert result.returncode == 0
assert result.stdout.strip() == "v2r2c2-gatew-provider-harness"
def artifact_target(tmp_path):
target = tmp_path / "extracted"
path = target / "payload" / "module.py"
path.parent.mkdir(parents=True)
path.write_bytes(b"print('source')\n")
artifact = tmp_path / "artifact.tar"
with tarfile.open(artifact, "w") as archive:
archive.add(path, arcname="payload/module.py")
return artifact, target, path
def test_archive_scan_binding_rejects_changed_and_extra_payload(tmp_path):
artifact, target, path = artifact_target(tmp_path)
assert providers._validate_artifact_scan_target(artifact, target) == hashlib.sha256(artifact.read_bytes()).hexdigest()
path.write_bytes(b"different\n")
with pytest.raises(providers.TrustedProviderExecutionError, match="PAYLOAD_MISMATCH"):
providers._validate_artifact_scan_target(artifact, target)
path.write_bytes(b"print('source')\n")
(target / "extra").write_bytes(b"extra")
with pytest.raises(providers.TrustedProviderExecutionError, match="PAYLOAD_MISMATCH"):
providers._validate_artifact_scan_target(artifact, target)
def test_archive_scan_binding_rejects_traversal(tmp_path):
artifact, target, path = artifact_target(tmp_path)
with tarfile.open(artifact, "w") as archive:
item = tarfile.TarInfo("../outside")
item.size = 1
archive.addfile(item, io.BytesIO(b"x"))
with pytest.raises(providers.TrustedProviderExecutionError, match="ARCHIVE_INVALID"):
providers._validate_artifact_scan_target(artifact, target)
@pytest.mark.parametrize("findings,exit_code,expected", [([], 0, True), ([{"RuleID": "leak"}], 1, False), ([{"RuleID": "leak"}], 0, False), ([], 2, False)])
def test_scanner_uses_structured_findings_and_real_exit(monkeypatch, tmp_path, findings, exit_code, expected):
artifact, target, path = artifact_target(tmp_path)
scanner = tmp_path / "scanner"
scanner.write_bytes(b"fixture")
def run(argv, **kwargs):
Path(argv[argv.index("--report-path") + 1]).write_text(json.dumps(findings))
return subprocess.CompletedProcess(argv, exit_code, "", "")
monkeypatch.setattr(providers, "_run", run)
result, _ = providers.run_secret_scan_provider(gitleaks_bin=str(scanner), target=target, source_oid="1" * 40, tree_oid="2" * 40, artifact_path=artifact)
assert result["passed"] is expected
assert result["findings"] == len(findings)
assert result["artifact_sha256"] == hashlib.sha256(artifact.read_bytes()).hexdigest()
def test_scanner_missing_report_cannot_pass(monkeypatch, tmp_path):
scanner = tmp_path / "scanner"
scanner.write_bytes(b"fixture")
monkeypatch.setattr(providers, "_run", lambda argv, **kwargs: subprocess.CompletedProcess(argv, 0, "", ""))
with pytest.raises(providers.TrustedProviderExecutionError, match="REPORT_INVALID"):
providers.run_secret_scan_provider(gitleaks_bin=str(scanner), target=tmp_path, source_oid="1" * 40, tree_oid="2" * 40)