Compare commits

...
11 changed files with 2307 additions and 5 deletions
@@ -0,0 +1,20 @@
[Unit]
Description=AIOS single-owner governance activation verifier
ConditionPathExists=/etc/aios-gitops/single-owner-activation-bindings.json
[Service]
Type=oneshot
User=root
Group=gitops
Environment=AIOS_GITOPS_WRITES_ENABLED=false
ExecStart=/usr/bin/aios-single-owner-governance --verify --bindings /etc/aios-gitops/single-owner-activation-bindings.json --state-root /var/lib/aios-gitops/governance-activation --lock-path /run/aios-gitops/governance-activation.lock
NoNewPrivileges=true
PrivateTmp=true
ProtectHome=true
ProtectSystem=strict
ReadWritePaths=/var/lib/aios-gitops/governance-activation /run/aios-gitops
RestrictSUIDSGID=true
UMask=0027
# Deliberately not installable. Deployment remains disabled/inactive;
# activation is an explicit approved CLI transaction, never an always-on unit.
@@ -0,0 +1,4 @@
d /var/lib/aios-gitops/governance-activation 0750 root gitops -
d /var/lib/aios-gitops/governance-activation/wal 0750 root gitops -
d /var/lib/aios-gitops/governance-activation/receipts 0750 root gitops -
d /run/aios-gitops 0750 root gitops -
@@ -0,0 +1,25 @@
{
"artifact_path": "/var/lib/aios-gitops/runtime/tasks/AIOS-SINGLE-OWNER-GOVERNANCE-ACTIVATION-20260729/artifacts/hardening-71def340e7cc34f4f19918dcf8d3c847abbe4fbf/live-activation-71def340e7cc34f4f19918dcf8d3c847abbe4fbf.tar.gz",
"artifact_sha256": "32bc49c207ec454b6fd81b0484f5b257bb789481b37175995029a2ec78cc62fa",
"copied_live_db_acceptance": "PASS",
"implementation_commit_oid": "71def340e7cc34f4f19918dcf8d3c847abbe4fbf",
"implementation_tree_oid": "3cc61bfbf69cbe0a24d54a0e53a6c50bcc130066",
"independent_validator": "ALLOW_CANDIDATE",
"production_mutation": false,
"result": "PASS",
"rollback_artifact_path": "/var/lib/aios-gitops/runtime/tasks/AIOS-SINGLE-OWNER-GOVERNANCE-ACTIVATION-20260729/artifacts/hardening-71def340e7cc34f4f19918dcf8d3c847abbe4fbf/live-activation-rollback-71def340e7cc34f4f19918dcf8d3c847abbe4fbf.tar.gz",
"rollback_artifact_sha256": "4efe1ea73d4fd72e5364fc91a16ad5e9d1e6ebe78d8aa466f7b11c4915b08403",
"runtime_source_sha256": {
"services/aios-single-owner-governance/aios_single_owner_governance/activation.py": "48846a58fa3afb2b7c8ffe6e88504a9333863932df867f33ba0321ad3bac6003",
"services/aios-single-owner-governance/aios_single_owner_governance/gitea_protected_branch.py": "00775efe3f3d631d88379eff4ed2dcfaf386a2fa43111abe961e75109e80d768",
"services/aios-single-owner-governance/aios_single_owner_governance/journal.py": "a8f121245e04487d99c99fb03ada83f0c907723f8eb18552872ff46dcf5a7873",
"services/aios-single-owner-governance/aios_single_owner_governance/state_machine.py": "0b08a2c378d143cb9031a25132afc7b7d825bdc03d11efe10610341d4b70167e",
"services/aios-single-owner-governance/aios_single_owner_governance/validator.py": "754639846cb15cb997d612a1fe0ceb0224ddef5bbc3ccac9802a3747f122dac1"
},
"subtests_passed": 17,
"task_id": "AIOS-SINGLE-OWNER-GOVERNANCE-ACTIVATION-20260729",
"test_evidence_path": "/var/lib/aios-gitops/runtime/tasks/AIOS-SINGLE-OWNER-GOVERNANCE-ACTIVATION-20260729/artifacts/hardening-71def340e7cc34f4f19918dcf8d3c847abbe4fbf/test-evidence-71def340e7cc34f4f19918dcf8d3c847abbe4fbf.json",
"test_evidence_sha256": "c1633732767f3f051ffb01404d12d05a6168a4b01b91efdb34a64988d6dee684",
"tests_failed": 0,
"tests_passed": 90
}
@@ -5,7 +5,47 @@ authorization. It validates exact-head Owner approvals and machine gates,
models protection-policy compare-and-swap with compensation, builds evidence,
and exercises every mutation path only against disposable fixtures.
It intentionally has no live activation entry point. Both systemd services are
static, refuse manual start, carry no credential, and keep all write and
activation flags false. A separate `SINGLE_OWNER_GOVERNANCE_ATOMIC_ACTIVATION`
approval is required before any live status publication or protection change.
The prerequisite shadow services remain static and refuse live mutation. This
branch adds a separate, one-shot activation executor with `--plan`,
`--activate`, `--verify`, `--rollback`, and `--status` commands. It is not an
always-on writer and never enables global GitOps writes.
The executor is fail-closed: every consequential command is bound to an exact
approval merge, governance OID, evidence closure, Owner review, component
digests, full branch-protection snapshot, operation ID, and plan ID. It writes
a fsynced WAL before the single-row SQLite CAS, verifies the full applied row,
and only rolls back with a target-to-previous CAS. Audit records are
hash-chained and credentials are redacted.
## Hardening Contract
Fresh activation is authorized only before the approved `expires_at` instant.
An already-recorded `PREPARED` transaction may recover after expiry only to
reach a safe terminal state using the same exact bindings; `--verify` and
`--rollback` remain available for that recorded transaction after expiry.
`ROLLED_BACK` and `FAILED` are terminal states and must never be replayed as a
new activation.
The approved execution binding must pin the implementation commit and its
exact tree, the installed hashes of the five runtime source files
(`activation.py`, `gitea_protected_branch.py`, `journal.py`, `validator.py`,
and `state_machine.py`), and independent SHA-256 values for the deployment
artifact, rollback artifact, and test evidence. It must also bind an evidence
closure that is a commit with the exact direct parent and exactly one approved
closure file, including that file's Git blob OID and SHA-256.
Every required machine status is accepted only when its exact status record,
creator identity, producer provenance, target URL, description digest, head
OID, state, and observation time match the approved binding. A fixture status
or a dormant self-check is not machine-status producer provenance.
This contract hardens validation; it does not authorize deployment. The
existing governed approval rules still apply, and installation remains
disabled and inactive until the required merged approval authorizes one
explicit execution.
Installation must remain disabled and inactive until a merged
`SINGLE_OWNER_GOVERNANCE_LIVE_ACTIVATION_IMPLEMENTATION_AND_EXECUTION`
approval authorizes one explicit execution. The approved runtime paths are
`/var/lib/aios-gitops/governance-activation/` and
`/run/aios-gitops/governance-activation.lock`.
@@ -0,0 +1,115 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"additionalProperties": false,
"properties": {
"activation_approval_merge_oid": {"pattern": "^[0-9a-f]{40}$", "type": "string"},
"activation_approval_pr_number": {"minimum": 1, "type": "integer"},
"activation_manifest_blob_oid": {"pattern": "^[0-9a-f]{40,64}$", "type": "string"},
"activation_manifest_path": {"pattern": "^approvals/.+\\.json$", "type": "string"},
"activation_manifest_sha256": {"pattern": "^[0-9a-f]{64}$", "type": "string"},
"artifact_path": {"pattern": "^/", "type": "string"},
"artifact_sha256": {"pattern": "^[0-9a-f]{64}$", "type": "string"},
"approval_db_path": {"minLength": 1, "type": "string"},
"approval_request_head_oid": {"pattern": "^[0-9a-f]{40}$", "type": "string"},
"backend": {"enum": ["file", "sqlite"]},
"backend_path": {"minLength": 1, "type": "string"},
"branch": {"const": "governance/approvals"},
"evidence_closure_oid": {"pattern": "^[0-9a-f]{40}$", "type": "string"},
"evidence_closure_path": {"pattern": "^(?!/)(?!.*\\.\\.).+", "type": "string"},
"evidence_closure_sha256": {"pattern": "^[0-9a-f]{64}$", "type": "string"},
"evidence_git_dir": {"minLength": 1, "type": "string"},
"executor": {"not": {"const": "owner"}, "type": "string"},
"expected_branch_protection_sha256": {"pattern": "^[0-9a-f]{64}$", "type": "string"},
"expected_gitea_schema_sha256": {"pattern": "^[0-9a-f]{64}$", "type": "string"},
"expected_governance_oid": {"pattern": "^[0-9a-f]{40}$", "type": "string"},
"expected_state_machine_sha256": {"pattern": "^[0-9a-f]{64}$", "type": "string"},
"expected_validator_sha256": {"pattern": "^[0-9a-f]{64}$", "type": "string"},
"expires_at": {"format": "date-time", "type": "string"},
"forbidden_operations": {"additionalProperties": {"const": false}, "type": "object"},
"governance_git_dir": {"minLength": 1, "type": "string"},
"governance_ref": {"const": "refs/heads/governance/approvals"},
"implementation_commit_oid": {"pattern": "^[0-9a-f]{40}$", "type": "string"},
"implementation_tree_oid": {"pattern": "^[0-9a-f]{40}$", "type": "string"},
"machine_status_producer_id": {"minimum": 1, "type": "integer"},
"machine_status_producer_name": {"minLength": 1, "type": "string"},
"machine_checks": {
"additionalProperties": false,
"properties": {
"aios/branch-protection": {"$ref": "#/$defs/machineCheck"},
"aios/evidence-closure": {"$ref": "#/$defs/machineCheck"},
"aios/secret-scan": {"$ref": "#/$defs/machineCheck"},
"aios/state-machine": {"$ref": "#/$defs/machineCheck"},
"aios/validator": {"$ref": "#/$defs/machineCheck"}
},
"required": ["aios/branch-protection", "aios/evidence-closure", "aios/secret-scan", "aios/state-machine", "aios/validator"],
"type": "object"
},
"observed_evidence_closure_oid": {"pattern": "^[0-9a-f]{40}$", "type": "string"},
"observed_governance_oid": {"pattern": "^[0-9a-f]{40}$", "type": "string"},
"observed_owner_review_oid": {"type": ["integer", "string"]},
"observed_state_machine_sha256": {"pattern": "^[0-9a-f]{64}$", "type": "string"},
"observed_validator_sha256": {"pattern": "^[0-9a-f]{64}$", "type": "string"},
"operation_id": {"minLength": 1, "type": "string"},
"owner_review_oid": {"type": ["integer", "string"]},
"repo_id": {"minimum": 1, "type": "integer"},
"requester": {"not": {"const": "owner"}, "type": "string"},
"rollback_artifact_path": {"pattern": "^/", "type": "string"},
"rollback_artifact_sha256": {"pattern": "^[0-9a-f]{64}$", "type": "string"},
"runtime_sources": {
"items": {
"additionalProperties": false,
"properties": {
"git_path": {"minLength": 1, "type": "string"},
"runtime_path": {"pattern": "^/", "type": "string"},
"sha256": {"pattern": "^[0-9a-f]{64}$", "type": "string"}
},
"required": ["git_path", "runtime_path", "sha256"],
"type": "object"
},
"maxItems": 5,
"minItems": 5,
"type": "array"
},
"state_machine_path": {"minLength": 1, "type": "string"},
"target_branch_protection": {"type": "object"},
"task_id": {"minLength": 1, "type": "string"},
"test_evidence_path": {"pattern": "^/", "type": "string"},
"test_evidence_sha256": {"pattern": "^[0-9a-f]{64}$", "type": "string"},
"validator_path": {"minLength": 1, "type": "string"},
"wip": {"const": false}
},
"$defs": {
"machineCheck": {
"additionalProperties": false,
"properties": {
"head_oid": {"pattern": "^[0-9a-f]{40}$", "type": "string"},
"status_id": {"minimum": 1, "type": "integer"},
"creator_id": {"minimum": 1, "type": "integer"},
"creator_login": {"minLength": 1, "type": "string"},
"state": {"const": "success"}
},
"required": ["state", "head_oid", "status_id", "creator_id", "creator_login"],
"type": "object"
}
},
"required": [
"task_id", "operation_id", "expected_governance_oid", "observed_governance_oid",
"expected_validator_sha256", "observed_validator_sha256", "validator_path",
"expected_state_machine_sha256", "observed_state_machine_sha256", "state_machine_path",
"expected_branch_protection_sha256", "expected_gitea_schema_sha256",
"activation_approval_merge_oid", "activation_approval_pr_number", "approval_request_head_oid",
"activation_manifest_path", "activation_manifest_blob_oid", "activation_manifest_sha256",
"evidence_closure_oid", "observed_evidence_closure_oid", "evidence_git_dir",
"evidence_closure_path", "evidence_closure_sha256",
"implementation_commit_oid", "implementation_tree_oid", "runtime_sources",
"artifact_path", "artifact_sha256", "rollback_artifact_path", "rollback_artifact_sha256",
"test_evidence_path", "test_evidence_sha256",
"machine_status_producer_id", "machine_status_producer_name",
"owner_review_oid", "observed_owner_review_oid", "approval_db_path", "expires_at",
"governance_git_dir", "governance_ref", "machine_checks", "forbidden_operations",
"target_branch_protection", "backend", "backend_path", "repo_id", "branch",
"requester", "executor", "wip"
],
"title": "AIOS Single Owner Live Activation Bindings",
"type": "object"
}
@@ -0,0 +1,860 @@
from __future__ import annotations
import argparse
from contextlib import contextmanager
from datetime import datetime, timezone
import hashlib
import json
import os
from pathlib import Path
import re
import sqlite3
import subprocess
from typing import Any, Iterator, Mapping, Protocol
try:
import fcntl
except ImportError: # pragma: no cover
fcntl = None
from .gitea_protected_branch import (
CASConflict,
FileProtectedBranchAdapter,
SQLiteProtectedBranchAdapter,
calculate_snapshot_sha256,
)
from .journal import JournalStore, atomic_write_json, read_json, redact, sha256_json
from . import state_machine as state_machine_module
from . import validator as validator_module
DEFAULT_STATE_ROOT = Path("/var/lib/aios-gitops/governance-activation")
DEFAULT_LOCK_PATH = Path("/run/aios-gitops/governance-activation.lock")
LIVE_GITEA_DB = Path("/data/aios-gitops/runtime/gitea-bin/data/gitea.db")
LIVE_AUTHORITATIVE_GIT = Path("/data/aios-gitops/repo.git")
HEX40 = re.compile(r"^[0-9a-f]{40}$")
HEX64 = re.compile(r"^[0-9a-f]{64}$")
REQUIRED_BINDINGS = {
"task_id",
"operation_id",
"expected_governance_oid",
"expected_validator_sha256",
"expected_state_machine_sha256",
"expected_branch_protection_sha256",
"activation_approval_merge_oid",
"evidence_closure_oid",
"owner_review_oid",
"observed_governance_oid",
"observed_validator_sha256",
"observed_state_machine_sha256",
"observed_evidence_closure_oid",
"observed_owner_review_oid",
"approval_request_head_oid",
"activation_approval_pr_number",
"expires_at",
"validator_path",
"state_machine_path",
"governance_git_dir",
"governance_ref",
"evidence_git_dir",
"approval_db_path",
"expected_gitea_schema_sha256",
"backend",
"backend_path",
"repo_id",
"branch",
"activation_manifest_path",
"activation_manifest_blob_oid",
"activation_manifest_sha256",
"implementation_commit_oid",
"implementation_tree_oid",
"runtime_sources",
"artifact_path",
"artifact_sha256",
"rollback_artifact_path",
"rollback_artifact_sha256",
"test_evidence_path",
"test_evidence_sha256",
"evidence_closure_path",
"evidence_closure_sha256",
"machine_status_producer_id",
"machine_status_producer_name",
}
REQUIRED_MACHINE_CONTEXTS = {
"aios/validator",
"aios/state-machine",
"aios/branch-protection",
"aios/secret-scan",
"aios/evidence-closure",
}
REQUIRED_RUNTIME_GIT_PATHS = {
"services/aios-single-owner-governance/aios_single_owner_governance/activation.py",
"services/aios-single-owner-governance/aios_single_owner_governance/gitea_protected_branch.py",
"services/aios-single-owner-governance/aios_single_owner_governance/journal.py",
"services/aios-single-owner-governance/aios_single_owner_governance/state_machine.py",
"services/aios-single-owner-governance/aios_single_owner_governance/validator.py",
}
class InjectedCrash(BaseException):
pass
class Adapter(Protocol):
def read_current(self) -> dict[str, Any]: ...
def calculate_snapshot_sha256(self, snapshot: Mapping[str, Any]) -> str: ...
def apply_atomic(self, expected_sha256: str, target: Mapping[str, Any]) -> dict[str, Any]: ...
def verify_applied(self, expected_sha256: str) -> bool: ...
def rollback_atomic(self, expected_target_sha256: str, previous: Mapping[str, Any]) -> dict[str, Any]: ...
def verify_rollback(self, expected_sha256: str) -> bool: ...
@contextmanager
def exclusive_lock(path: Path) -> Iterator[None]:
path.parent.mkdir(parents=True, exist_ok=True, mode=0o750)
flags = os.O_RDWR | os.O_CREAT
if hasattr(os, "O_NOFOLLOW"):
flags |= os.O_NOFOLLOW
fd = os.open(path, flags, 0o640)
try:
os.fchmod(fd, 0o640)
if fcntl is not None:
fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
yield
finally:
if fcntl is not None:
fcntl.flock(fd, fcntl.LOCK_UN)
os.close(fd)
def _require_hex(name: str, value: object, lengths: tuple[int, ...]) -> None:
if not isinstance(value, str) or len(value) not in lengths or not re.fullmatch(r"[0-9a-f]+", value):
raise RuntimeError(f"{name.upper()}_INVALID")
def _sha256_file(path: Path) -> str:
if not path.is_absolute() or not path.is_file() or path.is_symlink():
raise RuntimeError("VERIFIED_COMPONENT_PATH_INVALID")
digest = hashlib.sha256()
with path.open("rb") as handle:
for block in iter(lambda: handle.read(1024 * 1024), b""):
digest.update(block)
return digest.hexdigest()
def _git(git_dir: Path, *args: str) -> str:
if not git_dir.is_absolute() or not git_dir.exists() or git_dir.is_symlink():
raise RuntimeError("GIT_OBJECT_STORE_INVALID")
result = subprocess.run(
["git", f"--git-dir={git_dir}", *args],
text=True,
capture_output=True,
check=False,
)
if result.returncode != 0:
raise RuntimeError("GIT_EVIDENCE_VERIFICATION_FAILED")
return result.stdout.strip()
def _git_raw(git_dir: Path, *args: str) -> bytes:
if not git_dir.is_absolute() or not git_dir.exists() or git_dir.is_symlink():
raise RuntimeError("GIT_OBJECT_STORE_INVALID")
result = subprocess.run(
["git", f"--git-dir={git_dir}", *args], capture_output=True, check=False
)
if result.returncode != 0:
raise RuntimeError("GIT_EVIDENCE_VERIFICATION_FAILED")
return result.stdout
def _parse_utc(value: object) -> datetime:
if not isinstance(value, str):
raise RuntimeError("EXPIRY_INVALID")
try:
parsed = datetime.fromisoformat(value.replace("Z", "+00:00"))
except ValueError as exc:
raise RuntimeError("EXPIRY_INVALID") from exc
if parsed.tzinfo is None:
raise RuntimeError("EXPIRY_INVALID")
return parsed.astimezone(timezone.utc)
def _utc_now() -> datetime:
return datetime.now(timezone.utc)
def _approved_execution_binding(bindings: Mapping[str, Any]) -> dict[str, Any]:
return {
"task_id": bindings["task_id"],
"operation_id": bindings["operation_id"],
"expected_validator_sha256": bindings["expected_validator_sha256"],
"expected_state_machine_sha256": bindings["expected_state_machine_sha256"],
"expected_branch_protection_sha256": bindings["expected_branch_protection_sha256"],
"expected_gitea_schema_sha256": bindings["expected_gitea_schema_sha256"],
"target_branch_protection_sha256": calculate_snapshot_sha256(
bindings["target_branch_protection"]
),
"evidence_closure_oid": bindings["evidence_closure_oid"],
"evidence_closure_path": bindings["evidence_closure_path"],
"evidence_closure_sha256": bindings["evidence_closure_sha256"],
"implementation_commit_oid": bindings["implementation_commit_oid"],
"implementation_tree_oid": bindings["implementation_tree_oid"],
"runtime_sources": bindings["runtime_sources"],
"artifact_path": bindings["artifact_path"],
"artifact_sha256": bindings["artifact_sha256"],
"rollback_artifact_path": bindings["rollback_artifact_path"],
"rollback_artifact_sha256": bindings["rollback_artifact_sha256"],
"test_evidence_path": bindings["test_evidence_path"],
"test_evidence_sha256": bindings["test_evidence_sha256"],
"machine_status_producer_id": bindings["machine_status_producer_id"],
"machine_status_producer_name": bindings["machine_status_producer_name"],
"backend": bindings["backend"],
"repo_id": bindings["repo_id"],
"branch": bindings["branch"],
"requester": bindings["requester"],
"executor": bindings["executor"],
"forbidden_operations": bindings["forbidden_operations"],
}
def _same_path(left: Path, right: Path) -> bool:
try:
return left.resolve(strict=True) == right.resolve(strict=True) and os.path.samefile(left, right)
except (FileNotFoundError, OSError):
return False
def _verify_authority_paths(bindings: Mapping[str, Any]) -> None:
if bindings["backend"] != "sqlite":
return
approval_db = Path(str(bindings["approval_db_path"]))
backend_db = Path(str(bindings["backend_path"]))
if not _same_path(approval_db, backend_db):
raise RuntimeError("APPROVAL_DB_BACKEND_DB_IDENTITY_MISMATCH")
live = _same_path(backend_db, LIVE_GITEA_DB)
if live:
if not _same_path(Path(str(bindings["governance_git_dir"])), LIVE_AUTHORITATIVE_GIT):
raise RuntimeError("AUTHORITATIVE_GOVERNANCE_GIT_REQUIRED")
if not _same_path(Path(str(bindings["evidence_git_dir"])), LIVE_AUTHORITATIVE_GIT):
raise RuntimeError("AUTHORITATIVE_EVIDENCE_GIT_REQUIRED")
return
acceptance_root = os.environ.get("AIOS_GOVERNANCE_ACTIVATION_ACCEPTANCE_ROOT")
if not acceptance_root:
raise RuntimeError("NON_LIVE_SQLITE_BACKEND_DENIED")
root = Path(acceptance_root).resolve(strict=True)
if not str(root).startswith("/run/aios-live-activation-acceptance-"):
raise RuntimeError("ACCEPTANCE_ROOT_INVALID")
for path in (approval_db, backend_db, Path(str(bindings["governance_git_dir"])), Path(str(bindings["evidence_git_dir"]))):
try:
path.resolve(strict=True).relative_to(root)
except (FileNotFoundError, ValueError) as exc:
raise RuntimeError("ACCEPTANCE_AUTHORITY_ESCAPE_DENIED") from exc
def _load_approved_manifest(bindings: Mapping[str, Any], git_dir: Path) -> dict[str, Any]:
path = str(bindings["activation_manifest_path"])
if not path.startswith("approvals/") or not path.endswith(".json") or ".." in Path(path).parts:
raise RuntimeError("ACTIVATION_MANIFEST_PATH_INVALID")
merge_oid = str(bindings["activation_approval_merge_oid"])
blob_oid = _git(git_dir, "rev-parse", f"{merge_oid}:{path}")
if blob_oid != bindings["activation_manifest_blob_oid"]:
raise RuntimeError("ACTIVATION_MANIFEST_BLOB_MISMATCH")
raw_result = subprocess.run(
["git", f"--git-dir={git_dir}", "show", f"{merge_oid}:{path}"],
capture_output=True,
check=False,
)
if raw_result.returncode != 0:
raise RuntimeError("ACTIVATION_MANIFEST_READ_FAILED")
if hashlib.sha256(raw_result.stdout).hexdigest() != bindings["activation_manifest_sha256"]:
raise RuntimeError("ACTIVATION_MANIFEST_SHA256_MISMATCH")
try:
manifest = json.loads(raw_result.stdout)
except (UnicodeDecodeError, json.JSONDecodeError) as exc:
raise RuntimeError("ACTIVATION_MANIFEST_INVALID") from exc
if not isinstance(manifest, dict):
raise RuntimeError("ACTIVATION_MANIFEST_INVALID")
return manifest
def _require_approval_unexpired(bindings: Mapping[str, Any]) -> None:
if _utc_now() >= _parse_utc(bindings["expires_at"]):
raise RuntimeError("APPROVAL_EXPIRED")
def _safe_git_path(value: object) -> str:
path = str(value)
if not path or Path(path).is_absolute() or ".." in Path(path).parts:
raise RuntimeError("APPROVED_GIT_PATH_INVALID")
return path
def _verify_approved_entities(bindings: Mapping[str, Any], git_dir: Path) -> None:
implementation_oid = str(bindings["implementation_commit_oid"])
_git(git_dir, "cat-file", "-e", f"{implementation_oid}^{{commit}}")
if _git(git_dir, "rev-parse", f"{implementation_oid}^{{tree}}") != bindings["implementation_tree_oid"]:
raise RuntimeError("IMPLEMENTATION_TREE_MISMATCH")
sources = bindings["runtime_sources"]
if not isinstance(sources, list) or not sources:
raise RuntimeError("RUNTIME_SOURCES_INVALID")
seen_git: set[str] = set()
seen_runtime: set[str] = set()
loaded_runtime_paths = {
"services/aios-single-owner-governance/aios_single_owner_governance/activation.py": Path(__file__).resolve(),
"services/aios-single-owner-governance/aios_single_owner_governance/gitea_protected_branch.py": Path(__import__(SQLiteProtectedBranchAdapter.__module__, fromlist=["x"]).__file__).resolve(),
"services/aios-single-owner-governance/aios_single_owner_governance/journal.py": Path(__import__(JournalStore.__module__, fromlist=["x"]).__file__).resolve(),
"services/aios-single-owner-governance/aios_single_owner_governance/state_machine.py": Path(state_machine_module.__file__).resolve(),
"services/aios-single-owner-governance/aios_single_owner_governance/validator.py": Path(validator_module.__file__).resolve(),
}
for source in sources:
if not isinstance(source, dict) or set(source) != {"git_path", "runtime_path", "sha256"}:
raise RuntimeError("RUNTIME_SOURCE_BINDING_INVALID")
git_path = _safe_git_path(source["git_path"])
runtime_path = str(source["runtime_path"])
_require_hex("runtime_source_sha256", source["sha256"], (64,))
if git_path in seen_git or runtime_path in seen_runtime:
raise RuntimeError("RUNTIME_SOURCE_BINDING_DUPLICATE")
seen_git.add(git_path)
seen_runtime.add(runtime_path)
if Path(runtime_path).resolve() != loaded_runtime_paths.get(git_path):
raise RuntimeError("RUNTIME_SOURCE_PATH_MISMATCH")
if hashlib.sha256(_git_raw(git_dir, "show", f"{implementation_oid}:{git_path}")).hexdigest() != source["sha256"]:
raise RuntimeError("IMPLEMENTATION_SOURCE_DIGEST_MISMATCH")
if _sha256_file(Path(runtime_path)) != source["sha256"]:
raise RuntimeError("RUNTIME_SOURCE_DIGEST_MISMATCH")
if seen_git != REQUIRED_RUNTIME_GIT_PATHS:
raise RuntimeError("RUNTIME_SOURCE_SET_MISMATCH")
for prefix in ("artifact", "rollback_artifact", "test_evidence"):
if _sha256_file(Path(str(bindings[f"{prefix}_path"]))) != bindings[f"{prefix}_sha256"]:
raise RuntimeError("APPROVED_ENTITY_DIGEST_MISMATCH")
closure_oid = str(bindings["evidence_closure_oid"])
if _git(git_dir, "cat-file", "-t", closure_oid) != "commit":
raise RuntimeError("EVIDENCE_CLOSURE_NOT_COMMIT")
parents = _git(git_dir, "rev-list", "--parents", "-n", "1", closure_oid).split()
if parents != [closure_oid, implementation_oid]:
raise RuntimeError("EVIDENCE_CLOSURE_PARENT_MISMATCH")
closure_path = _safe_git_path(bindings["evidence_closure_path"])
if hashlib.sha256(_git_raw(git_dir, "show", f"{closure_oid}:{closure_path}")).hexdigest() != bindings["evidence_closure_sha256"]:
raise RuntimeError("EVIDENCE_CLOSURE_SHA256_MISMATCH")
if _git(git_dir, "diff", "--name-status", implementation_oid, closure_oid) != f"A\t{closure_path}":
raise RuntimeError("EVIDENCE_CLOSURE_DIFF_INVALID")
def _verify_external_evidence(
bindings: Mapping[str, Any], *, require_unexpired: bool = True
) -> None:
_verify_authority_paths(bindings)
validator_sha = _sha256_file(Path(str(bindings["validator_path"])))
state_machine_sha = _sha256_file(Path(str(bindings["state_machine_path"])))
if validator_sha != bindings["expected_validator_sha256"] or validator_sha != bindings["observed_validator_sha256"]:
raise RuntimeError("VALIDATOR_DIGEST_MISMATCH")
if state_machine_sha != bindings["expected_state_machine_sha256"] or state_machine_sha != bindings["observed_state_machine_sha256"]:
raise RuntimeError("STATE_MACHINE_DIGEST_MISMATCH")
governance_git_dir = Path(str(bindings["governance_git_dir"]))
governance_oid = _git(governance_git_dir, "rev-parse", "--verify", str(bindings["governance_ref"]))
if governance_oid != bindings["expected_governance_oid"] or governance_oid != bindings["observed_governance_oid"]:
raise RuntimeError("WRONG_GOVERNANCE_OID_DENIED")
merge_oid = str(bindings["activation_approval_merge_oid"])
_git(governance_git_dir, "cat-file", "-e", f"{merge_oid}^{{commit}}")
if subprocess.run(
["git", f"--git-dir={governance_git_dir}", "merge-base", "--is-ancestor", merge_oid, governance_oid],
capture_output=True,
check=False,
).returncode != 0:
raise RuntimeError("ACTIVATION_APPROVAL_NOT_IN_GOVERNANCE_REF")
manifest = _load_approved_manifest(bindings, governance_git_dir)
if manifest.get("approval_type") != "SINGLE_OWNER_GOVERNANCE_LIVE_ACTIVATION_IMPLEMENTATION_AND_EXECUTION":
raise RuntimeError("ACTIVATION_APPROVAL_TYPE_MISMATCH")
if manifest.get("execution_binding") != _approved_execution_binding(bindings):
raise RuntimeError("APPROVED_EXECUTION_BINDING_MISMATCH")
if manifest.get("execution_binding_sha256") != sha256_json(manifest["execution_binding"]):
raise RuntimeError("APPROVED_EXECUTION_BINDING_SHA256_MISMATCH")
if manifest.get("expires_at") != bindings["expires_at"]:
raise RuntimeError("APPROVAL_EXPIRY_BINDING_MISMATCH")
manifest_bound_fields = (
"implementation_commit_oid", "implementation_tree_oid", "runtime_sources",
"artifact_path", "artifact_sha256", "rollback_artifact_path",
"rollback_artifact_sha256", "test_evidence_path", "test_evidence_sha256",
"evidence_closure_oid", "evidence_closure_path", "evidence_closure_sha256",
"machine_status_producer_id", "machine_status_producer_name",
)
if any(manifest.get(field) != bindings[field] for field in manifest_bound_fields):
raise RuntimeError("APPROVED_ARTIFACT_BINDING_MISMATCH")
evidence_git_dir = Path(str(bindings["evidence_git_dir"]))
evidence_oid = str(bindings["evidence_closure_oid"])
if evidence_oid != bindings["observed_evidence_closure_oid"]:
raise RuntimeError("WRONG_EVIDENCE_CLOSURE_DENIED")
_verify_approved_entities(bindings, evidence_git_dir)
db_path = Path(str(bindings["approval_db_path"]))
if not db_path.is_absolute() or not db_path.is_file() or db_path.is_symlink():
raise RuntimeError("APPROVAL_DATABASE_INVALID")
expiry = _parse_utc(bindings["expires_at"])
if require_unexpired:
_require_approval_unexpired(bindings)
with sqlite3.connect(f"file:{db_path}?mode=ro", uri=True) as connection:
connection.row_factory = sqlite3.Row
pr = connection.execute(
"SELECT pr.issue_id,pr.has_merged,pr.merged_commit_id,pr.merged_unix,i.is_closed "
"FROM pull_request pr JOIN issue i ON i.id=pr.issue_id "
"WHERE i.repo_id=? AND pr.\"index\"=? AND pr.base_branch=?",
(int(bindings["repo_id"]), int(bindings["activation_approval_pr_number"]), str(bindings["branch"])),
).fetchone()
if pr is None or int(pr["has_merged"]) != 1 or int(pr["is_closed"]) != 1:
raise RuntimeError("ACTIVATION_APPROVAL_PR_NOT_MERGED")
if pr["merged_commit_id"] != bindings["activation_approval_merge_oid"]:
raise RuntimeError("ACTIVATION_APPROVAL_MERGE_MISMATCH")
if int(pr["merged_unix"] or 0) <= 0 or datetime.fromtimestamp(int(pr["merged_unix"]), timezone.utc) > expiry:
raise RuntimeError("ACTIVATION_APPROVAL_MERGED_AFTER_EXPIRY")
review = connection.execute(
"SELECT r.id,r.type,r.reviewer_id,r.commit_id,r.stale,r.dismissed,r.created_unix,u.lower_name "
"FROM review r JOIN user u ON u.id=r.reviewer_id WHERE r.id=? AND r.issue_id=?",
(int(bindings["owner_review_oid"]), int(pr["issue_id"])),
).fetchone()
if review is None or int(review["id"]) != int(bindings["observed_owner_review_oid"]):
raise RuntimeError("WRONG_APPROVAL_OID_DENIED")
if (
int(review["type"]) != 1
or int(review["reviewer_id"]) != 2
or review["lower_name"] != "owner"
or review["commit_id"] != bindings["approval_request_head_oid"]
or int(review["stale"]) != 0
or int(review["dismissed"]) != 0
or datetime.fromtimestamp(int(review["created_unix"]), timezone.utc) >= expiry
):
raise RuntimeError("OWNER_APPROVAL_INVALID")
statuses = connection.execute(
"SELECT cs.id,cs.context,cs.state,cs.sha,cs.creator_id,u.lower_name AS creator_login "
"FROM commit_status cs LEFT JOIN user u ON u.id=cs.creator_id "
"WHERE cs.repo_id=? AND cs.sha=? ORDER BY cs.id DESC",
(int(bindings["repo_id"]), str(bindings["approval_request_head_oid"])),
).fetchall()
observed: dict[str, sqlite3.Row] = {}
for row in statuses:
if row["context"] in REQUIRED_MACHINE_CONTEXTS:
observed.setdefault(row["context"], row)
if set(observed) != REQUIRED_MACHINE_CONTEXTS:
raise RuntimeError("MACHINE_GATE_SET_MISMATCH")
for context, row in observed.items():
if row["state"] != "success" or row["sha"] != bindings["approval_request_head_oid"]:
raise RuntimeError("MACHINE_GATE_FAILED")
if (
int(row["creator_id"] or 0) != int(bindings["machine_status_producer_id"])
or row["creator_login"] != bindings["machine_status_producer_name"]
):
raise RuntimeError("MACHINE_GATE_PRODUCER_MISMATCH")
expected = bindings["machine_checks"][context]
actual = {
"state": row["state"], "head_oid": row["sha"], "status_id": int(row["id"]),
"creator_id": int(row["creator_id"]), "creator_login": row["creator_login"],
}
if expected != actual:
raise RuntimeError("MACHINE_GATE_BINDING_MISMATCH")
def validate_bindings(
bindings: Mapping[str, Any], *, require_unexpired: bool = True
) -> None:
missing = sorted(REQUIRED_BINDINGS - set(bindings))
if missing:
raise RuntimeError("MISSING_BINDINGS:" + ",".join(missing))
for name in (
"expected_governance_oid",
"activation_approval_merge_oid",
"evidence_closure_oid",
"implementation_commit_oid",
"implementation_tree_oid",
):
_require_hex(name, bindings[name], (40,))
for name in (
"expected_validator_sha256",
"expected_state_machine_sha256",
"expected_branch_protection_sha256",
"observed_validator_sha256",
"observed_state_machine_sha256",
"expected_gitea_schema_sha256",
"activation_manifest_sha256",
"artifact_sha256",
"rollback_artifact_sha256",
"test_evidence_sha256",
"evidence_closure_sha256",
):
_require_hex(name, bindings[name], (64,))
_require_hex("activation_manifest_blob_oid", bindings["activation_manifest_blob_oid"], (40, 64))
if not isinstance(bindings["owner_review_oid"], (int, str)) or str(bindings["owner_review_oid"]).strip() == "":
raise RuntimeError("OWNER_REVIEW_OID_INVALID")
if bindings["governance_ref"] != "refs/heads/governance/approvals":
raise RuntimeError("GOVERNANCE_REF_INVALID")
if bindings["branch"] != "governance/approvals":
raise RuntimeError("BRANCH_BINDING_DENIED")
if bindings["backend"] not in {"file", "sqlite"}:
raise RuntimeError("UNKNOWN_PROTECTION_BACKEND")
if bindings["observed_governance_oid"] != bindings["expected_governance_oid"]:
raise RuntimeError("WRONG_GOVERNANCE_OID_DENIED")
if bindings["observed_evidence_closure_oid"] != bindings["evidence_closure_oid"]:
raise RuntimeError("WRONG_EVIDENCE_CLOSURE_DENIED")
if str(bindings["observed_owner_review_oid"]) != str(bindings["owner_review_oid"]):
raise RuntimeError("WRONG_APPROVAL_OID_DENIED")
if bindings.get("requester") == "owner":
raise RuntimeError("REQUESTER_SELF_APPROVAL_DENIED")
if bindings.get("executor") == "owner":
raise RuntimeError("EXECUTOR_SELF_APPROVAL_DENIED")
if bindings.get("wip") is not False:
raise RuntimeError("WIP_BLOCKED")
checks = bindings.get("machine_checks")
if not isinstance(checks, dict) or set(checks) != REQUIRED_MACHINE_CONTEXTS:
raise RuntimeError("MACHINE_GATE_SET_MISMATCH")
if not isinstance(bindings["machine_status_producer_id"], int) or bindings["machine_status_producer_id"] < 1:
raise RuntimeError("MACHINE_STATUS_PRODUCER_INVALID")
if not isinstance(bindings["machine_status_producer_name"], str) or not bindings["machine_status_producer_name"]:
raise RuntimeError("MACHINE_STATUS_PRODUCER_INVALID")
for context, check in checks.items():
if (
not isinstance(check, dict)
or set(check) != {"state", "head_oid", "status_id", "creator_id", "creator_login"}
or check.get("state") != "success"
or check.get("head_oid") != bindings["approval_request_head_oid"]
or not isinstance(check.get("status_id"), int)
or check.get("status_id") < 1
or check.get("creator_id") != bindings["machine_status_producer_id"]
or check.get("creator_login") != bindings["machine_status_producer_name"]
):
raise RuntimeError(f"MACHINE_GATE_FAILED:{context}")
forbidden = bindings.get("forbidden_operations", {})
if any(bool(value) for value in forbidden.values()):
raise RuntimeError("FORBIDDEN_OPERATION_REQUESTED")
if os.environ.get("AIOS_GITOPS_WRITES_ENABLED", "false").lower() != "false":
raise RuntimeError("GLOBAL_GITOPS_WRITES_MUST_REMAIN_FALSE")
if not isinstance(bindings.get("target_branch_protection"), dict):
raise RuntimeError("TARGET_BRANCH_PROTECTION_REQUIRED")
_verify_external_evidence(bindings, require_unexpired=require_unexpired)
def binding_payload_hash(bindings: Mapping[str, Any]) -> str:
stable = dict(bindings)
stable.pop("crash_at", None)
return sha256_json(redact(stable))
def plan_id(bindings: Mapping[str, Any], previous: Mapping[str, Any], target: Mapping[str, Any]) -> str:
stable = dict(bindings)
stable.pop("crash_at", None)
return sha256_json(
{
"bindings": redact(stable),
"previous": dict(previous),
"target": dict(target),
}
)
class ActivationEngine:
def __init__(self, adapter: Adapter, state_root: Path, lock_path: Path) -> None:
self.adapter = adapter
self.journal = JournalStore(state_root)
self.lock_path = lock_path
def plan(self, bindings: Mapping[str, Any]) -> dict[str, Any]:
validate_bindings(bindings)
previous = self.adapter.read_current()
previous_sha = calculate_snapshot_sha256(previous)
if previous_sha != bindings["expected_branch_protection_sha256"]:
raise CASConflict("EXPECTED_BRANCH_PROTECTION_SHA256_MISMATCH")
target = dict(bindings["target_branch_protection"])
# Validate without writing by using the public target validator through a
# disposable comparison: adapters enforce the same checks again in CAS.
from .gitea_protected_branch import validate_target
validate_target(previous, target)
result = {
"status": "PLAN_READY",
"task_id": bindings["task_id"],
"operation_id": bindings["operation_id"],
"plan_id": plan_id(bindings, previous, target),
"payload_hash": binding_payload_hash(bindings),
"previous_sha256": previous_sha,
"target_sha256": calculate_snapshot_sha256(target),
}
return result
def _recover(self, record: dict[str, Any]) -> dict[str, Any]:
current = self.adapter.read_current()
current_sha = calculate_snapshot_sha256(current)
previous_sha = record["previous_sha256"]
target_sha = record["target_sha256"]
if record["phase"] == "PREPARED":
if current_sha == previous_sha:
return {**record, "recovery": "RESUME_BEFORE_APPLY"}
if current_sha == target_sha:
record = {**record, "phase": "APPLIED", "recovery": "RESUME_AFTER_APPLY"}
self.journal.write(record["operation_id"], record)
return record
raise RuntimeError("MANUAL_INTERVENTION_REQUIRED")
if record["phase"] in {"APPLIED", "COMMITTED"} and current_sha not in {target_sha, previous_sha}:
raise RuntimeError("MANUAL_INTERVENTION_REQUIRED")
return record
def _ensure_closure(self, record: dict[str, Any], event: str) -> None:
expected_sha = (
record["target_sha256"] if record["phase"] == "COMMITTED" else record["previous_sha256"]
)
if calculate_snapshot_sha256(self.adapter.read_current()) != expected_sha:
raise RuntimeError("MANUAL_INTERVENTION_REQUIRED")
atomic_write_json(
self.journal.root / "current-snapshot.json",
record["target"] if record["phase"] == "COMMITTED" else record["previous"],
)
atomic_write_json(self.journal.root / "rollback-snapshot.json", record["previous"])
self.journal.write_receipt(record["operation_id"], record)
if not self.journal.audit.has_event(record["operation_id"], event):
payload = {"event": event}
if event == "SINGLE_OWNER_ACTIVATION_COMMITTED":
payload.update(
payload_hash=record["payload_hash"],
target_sha256=record["target_sha256"],
)
self.journal.audit.append(record["operation_id"], payload)
atomic_write_json(self.journal.root / "state.json", record)
def activate(self, bindings: Mapping[str, Any], supplied_plan_id: str) -> dict[str, Any]:
with exclusive_lock(self.lock_path):
self.journal.audit.verify()
operation_id = str(bindings["operation_id"])
payload_hash = binding_payload_hash(bindings)
record = self.journal.load(operation_id)
if record:
if record.get("payload_hash") != payload_hash:
raise RuntimeError("OPERATION_ID_PAYLOAD_MISMATCH_DENIED")
if record.get("plan_id") != supplied_plan_id:
raise RuntimeError("PLAN_ID_MISMATCH")
if record.get("phase") == "ROLLED_BACK":
raise RuntimeError("OPERATION_TERMINAL_ROLLED_BACK")
if record.get("phase") == "FAILED":
raise RuntimeError("OPERATION_TERMINAL_FAILED")
prepared_at = _parse_utc(record.get("prepared_at"))
if prepared_at >= _parse_utc(bindings["expires_at"]):
raise RuntimeError("RECOVERY_AUTHORIZATION_INVALID")
record = self._recover(record)
if record["phase"] == "COMMITTED":
self._ensure_closure(record, "SINGLE_OWNER_ACTIVATION_COMMITTED")
return {"status": "ALREADY_COMMITTED", **record}
else:
validate_bindings(bindings)
planned = self.plan(bindings)
if supplied_plan_id != planned["plan_id"]:
raise RuntimeError("PLAN_ID_MISMATCH")
previous = self.adapter.read_current()
target = dict(bindings["target_branch_protection"])
if record is None:
if calculate_snapshot_sha256(previous) != planned["previous_sha256"]:
raise CASConflict("PREPARED_BOUNDARY_SNAPSHOT_CHANGED")
prepared_at = _utc_now()
if prepared_at >= _parse_utc(bindings["expires_at"]):
raise RuntimeError("APPROVAL_EXPIRED")
record = {
"task_id": bindings["task_id"],
"operation_id": operation_id,
"transaction_id": operation_id,
"payload_hash": payload_hash,
"plan_id": supplied_plan_id,
"phase": "PREPARED",
"prepared_at": prepared_at.isoformat().replace("+00:00", "Z"),
"previous": previous,
"target": target,
"previous_sha256": calculate_snapshot_sha256(previous),
"target_sha256": calculate_snapshot_sha256(target),
"activation_approval_merge_oid": bindings["activation_approval_merge_oid"],
"evidence_closure_oid": bindings["evidence_closure_oid"],
"owner_review_oid": bindings["owner_review_oid"],
}
self.journal.write(operation_id, record)
atomic_write_json(self.journal.root / "current-snapshot.json", previous)
atomic_write_json(self.journal.root / "rollback-snapshot.json", previous)
crash_at = bindings.get("crash_at")
if crash_at == "before_apply":
raise InjectedCrash("before_apply")
try:
current_sha = calculate_snapshot_sha256(self.adapter.read_current())
if current_sha == record["previous_sha256"]:
applied = self.adapter.apply_atomic(record["previous_sha256"], record["target"])
record["target"] = applied
record["target_sha256"] = calculate_snapshot_sha256(applied)
elif current_sha != record["target_sha256"]:
raise CASConflict("CAS_CONFLICT")
if crash_at == "during_apply":
raise InjectedCrash("during_apply")
record["phase"] = "APPLIED"
self.journal.write(operation_id, record)
if crash_at == "after_apply_before_receipt":
raise InjectedCrash("after_apply_before_receipt")
if not self.adapter.verify_applied(record["target_sha256"]):
raise RuntimeError("POST_ACTIVATION_VERIFY_FAILED")
record["phase"] = "COMMITTED"
record["single_owner_policy_active"] = True
record["break_glass_active"] = False
self.journal.write(operation_id, record)
if crash_at == "after_commit_wal":
raise InjectedCrash("after_commit_wal")
self._ensure_closure(record, "SINGLE_OWNER_ACTIVATION_COMMITTED")
return {"status": "ACTIVATED", **record}
except InjectedCrash:
raise
except Exception as exc:
failure = {**record, "phase": "FAILED", "error": type(exc).__name__}
current_sha = calculate_snapshot_sha256(self.adapter.read_current())
if current_sha == record["target_sha256"]:
self.adapter.rollback_atomic(record["target_sha256"], record["previous"])
if not self.adapter.verify_rollback(record["previous_sha256"]):
raise RuntimeError("ROLLBACK_VERIFY_FAILED") from exc
failure["phase"] = "ROLLED_BACK"
self.journal.write(operation_id, failure)
self.journal.write_receipt(operation_id, failure)
self.journal.audit.append(operation_id, {"event": failure["phase"], "error": type(exc).__name__})
raise
def verify(self, bindings: Mapping[str, Any], operation_id: str) -> dict[str, Any]:
record = self.journal.load(operation_id)
if not record:
raise RuntimeError("TRANSACTION_NOT_FOUND")
if record.get("payload_hash") != binding_payload_hash(bindings):
raise RuntimeError("OPERATION_ID_PAYLOAD_MISMATCH_DENIED")
self.journal.audit.verify()
current_sha = calculate_snapshot_sha256(self.adapter.read_current())
passed = record.get("phase") == "COMMITTED" and current_sha == record.get("target_sha256")
if not passed:
raise RuntimeError("ACTIVATION_VERIFY_FAILED")
return {"status": "VERIFIED", "operation_id": operation_id, "target_sha256": current_sha}
def rollback(
self, bindings: Mapping[str, Any], operation_id: str, supplied_plan_id: str
) -> dict[str, Any]:
with exclusive_lock(self.lock_path):
self.journal.audit.verify()
record = self.journal.load(operation_id)
if not record:
raise RuntimeError("TRANSACTION_NOT_FOUND")
if record.get("payload_hash") != binding_payload_hash(bindings):
raise RuntimeError("OPERATION_ID_PAYLOAD_MISMATCH_DENIED")
if record.get("plan_id") != supplied_plan_id:
raise RuntimeError("PLAN_ID_MISMATCH")
for field in (
"activation_approval_merge_oid",
"evidence_closure_oid",
"owner_review_oid",
):
if str(record.get(field)) != str(bindings[field]):
raise RuntimeError("ROLLBACK_AUTHORIZATION_MISMATCH_DENIED")
current_sha = calculate_snapshot_sha256(self.adapter.read_current())
if current_sha == record["previous_sha256"]:
record["phase"] = "ROLLED_BACK"
record["single_owner_policy_active"] = False
self.journal.write(operation_id, record)
self._ensure_closure(record, "ACTIVATION_ROLLED_BACK")
return {"status": "ALREADY_ROLLED_BACK", "operation_id": operation_id}
if current_sha != record["target_sha256"]:
raise RuntimeError("MANUAL_INTERVENTION_REQUIRED")
self.adapter.rollback_atomic(record["target_sha256"], record["previous"])
if not self.adapter.verify_rollback(record["previous_sha256"]):
raise RuntimeError("ROLLBACK_VERIFY_FAILED")
if bindings.get("crash_at") == "after_rollback_cas":
raise InjectedCrash("after_rollback_cas")
record["phase"] = "ROLLED_BACK"
record["single_owner_policy_active"] = False
self.journal.write(operation_id, record)
if bindings.get("crash_at") == "after_rollback_wal":
raise InjectedCrash("after_rollback_wal")
self._ensure_closure(record, "ACTIVATION_ROLLED_BACK")
return {"status": "ROLLED_BACK", "operation_id": operation_id}
def status(self, operation_id: str | None = None) -> dict[str, Any]:
audit_head = self.journal.audit.verify()
if operation_id:
record = self.journal.load(operation_id)
if not record:
raise RuntimeError("TRANSACTION_NOT_FOUND")
return {"status": "OK", "audit_head": audit_head, "transaction": record}
state_path = self.journal.root / "state.json"
return {
"status": "OK",
"audit_head": audit_head,
"state": read_json(state_path) if state_path.exists() else None,
"transactions": list(self.journal.iter_transactions()),
}
def load_bindings(path: Path) -> dict[str, Any]:
return read_json(path)
def adapter_from_bindings(bindings: Mapping[str, Any]) -> Adapter:
backend = bindings.get("backend", "file")
backend_path = Path(str(bindings.get("backend_path", "")))
if not backend_path.is_absolute():
raise RuntimeError("BACKEND_PATH_MUST_BE_ABSOLUTE")
if backend == "file":
return FileProtectedBranchAdapter(backend_path)
if backend == "sqlite":
return SQLiteProtectedBranchAdapter(
backend_path,
repo_id=int(bindings.get("repo_id", 1)),
branch=str(bindings.get("branch", "governance/approvals")),
expected_schema_sha256=str(bindings["expected_gitea_schema_sha256"]),
)
raise RuntimeError("UNKNOWN_PROTECTION_BACKEND")
def build_parser() -> argparse.ArgumentParser:
parser = argparse.ArgumentParser()
action = parser.add_mutually_exclusive_group(required=True)
for name in ("plan", "activate", "verify", "rollback", "status"):
action.add_argument(f"--{name}", action="store_true")
parser.add_argument("--bindings", type=Path)
parser.add_argument("--plan-id")
parser.add_argument("--transaction")
parser.add_argument("--state-root", type=Path, default=DEFAULT_STATE_ROOT)
parser.add_argument("--lock-path", type=Path, default=DEFAULT_LOCK_PATH)
return parser
def main(argv: list[str] | None = None) -> int:
args = build_parser().parse_args(argv)
if args.status and args.bindings is None:
# Status without bindings is intentionally limited to durable local state.
journal = JournalStore(args.state_root)
result = {
"status": "OK",
"audit_head": journal.audit.verify(),
"transactions": list(journal.iter_transactions()),
}
print(json.dumps(result, sort_keys=True))
return 0
if args.bindings is None:
raise SystemExit("--bindings is required")
bindings = load_bindings(args.bindings)
engine = ActivationEngine(adapter_from_bindings(bindings), args.state_root, args.lock_path)
if args.plan:
result = engine.plan(bindings)
elif args.activate:
if not args.plan_id:
raise SystemExit("--plan-id is required")
result = engine.activate(bindings, args.plan_id)
elif args.verify:
result = engine.verify(bindings, args.transaction or str(bindings["operation_id"]))
elif args.rollback:
if not args.plan_id:
raise SystemExit("--plan-id is required")
result = engine.rollback(
bindings, args.transaction or str(bindings["operation_id"]), args.plan_id
)
else:
result = engine.status(args.transaction)
print(json.dumps(redact(result), sort_keys=True))
return 0
if __name__ == "__main__":
raise SystemExit(main())
@@ -0,0 +1,260 @@
from __future__ import annotations
import json
from pathlib import Path
import sqlite3
import time
from typing import Any, Mapping
from .journal import atomic_write_json, read_json, sha256_json
class CASConflict(RuntimeError):
pass
class UnsafePolicy(RuntimeError):
pass
def calculate_snapshot_sha256(snapshot: Mapping[str, Any]) -> str:
return sha256_json(dict(snapshot))
def validate_target(previous: Mapping[str, Any], target: Mapping[str, Any]) -> None:
if set(previous) != set(target):
raise UnsafePolicy("PROTECTION_ROW_SHAPE_CHANGED")
for field in ("id", "repo_id", "branch_name"):
if previous.get(field) != target.get(field):
raise UnsafePolicy(f"PROTECTION_IDENTITY_CHANGED:{field}")
if target.get("branch_name") != "governance/approvals":
raise UnsafePolicy("BRANCH_BINDING_DENIED")
if int(target.get("can_push", 1)) != 0:
raise UnsafePolicy("DIRECT_PUSH_MUST_REMAIN_DISABLED")
if int(target.get("required_approvals", 0)) != 1:
raise UnsafePolicy("EXACTLY_ONE_APPROVAL_REQUIRED")
if int(target.get("enable_approvals_whitelist", 0)) != 1:
raise UnsafePolicy("APPROVAL_WHITELIST_MUST_REMAIN_ENABLED")
if json.loads(target.get("approvals_whitelist_user_i_ds", "[]")) != [2]:
raise UnsafePolicy("OWNER_APPROVAL_WHITELIST_REQUIRED")
if int(target.get("enable_merge_whitelist", 0)) != 1:
raise UnsafePolicy("MERGE_WHITELIST_MUST_REMAIN_ENABLED")
if target.get("merge_whitelist_user_i_ds") != previous.get("merge_whitelist_user_i_ds"):
raise UnsafePolicy("MERGE_EXECUTOR_WHITELIST_CHANGE_DENIED")
if int(target.get("dismiss_stale_approvals", 0)) != 1:
raise UnsafePolicy("DISMISS_STALE_APPROVALS_REQUIRED")
if int(target.get("enable_status_check", 0)) != 1:
raise UnsafePolicy("STATUS_CHECKS_REQUIRED")
required_contexts = {
"aios/validator",
"aios/state-machine",
"aios/branch-protection",
"aios/secret-scan",
"aios/evidence-closure",
}
try:
contexts = set(json.loads(target.get("status_check_contexts", "[]")))
except (TypeError, ValueError) as exc:
raise UnsafePolicy("STATUS_CHECK_CONTEXTS_INVALID") from exc
if contexts != required_contexts:
raise UnsafePolicy("STATUS_CHECK_CONTEXTS_MISMATCH")
if int(target.get("block_on_rejected_reviews", 0)) != 1:
raise UnsafePolicy("REJECTED_REVIEWS_MUST_BLOCK")
if int(target.get("block_on_outdated_branch", 0)) != 1:
raise UnsafePolicy("OUTDATED_BRANCH_MUST_BLOCK")
if target.get("require_signed_commits") != previous.get("require_signed_commits"):
raise UnsafePolicy("SIGNATURE_POLICY_CHANGE_DENIED")
class FileProtectedBranchAdapter:
def __init__(self, path: Path) -> None:
self.path = path
@staticmethod
def calculate_snapshot_sha256(snapshot: Mapping[str, Any]) -> str:
return calculate_snapshot_sha256(snapshot)
def read_current(self) -> dict[str, Any]:
return read_json(self.path)
def compare_expected(self, expected_sha256: str) -> bool:
return calculate_snapshot_sha256(self.read_current()) == expected_sha256
def apply_atomic(
self, expected_sha256: str, target: Mapping[str, Any]
) -> dict[str, Any]:
previous = self.read_current()
if calculate_snapshot_sha256(previous) != expected_sha256:
raise CASConflict("CAS_CONFLICT")
validate_target(previous, target)
atomic_write_json(self.path, dict(target))
applied = self.read_current()
if calculate_snapshot_sha256(applied) != calculate_snapshot_sha256(target):
raise CASConflict("CAS_VERIFY_FAILED")
return applied
def verify_applied(self, expected_sha256: str) -> bool:
return self.compare_expected(expected_sha256)
def rollback_atomic(
self, expected_target_sha256: str, previous: Mapping[str, Any]
) -> dict[str, Any]:
current = self.read_current()
if calculate_snapshot_sha256(current) != expected_target_sha256:
raise CASConflict("ROLLBACK_CONFLICT")
atomic_write_json(self.path, dict(previous))
restored = self.read_current()
if calculate_snapshot_sha256(restored) != calculate_snapshot_sha256(previous):
raise CASConflict("ROLLBACK_VERIFY_FAILED")
return restored
def verify_rollback(self, expected_sha256: str) -> bool:
return self.compare_expected(expected_sha256)
class SQLiteProtectedBranchAdapter:
ALLOWED_UPDATE_FIELDS = (
"can_push",
"enable_merge_whitelist",
"merge_whitelist_user_i_ds",
"enable_status_check",
"status_check_contexts",
"enable_approvals_whitelist",
"approvals_whitelist_user_i_ds",
"required_approvals",
"block_on_rejected_reviews",
"block_on_official_review_requests",
"block_on_outdated_branch",
"dismiss_stale_approvals",
"ignore_stale_approvals",
"updated_unix",
)
def __init__(
self,
db_path: Path,
*,
repo_id: int = 1,
branch: str = "governance/approvals",
expected_schema_sha256: str,
) -> None:
self.db_path = db_path
self.repo_id = repo_id
self.branch = branch
self.expected_schema_sha256 = expected_schema_sha256
def _connect(self) -> sqlite3.Connection:
connection = sqlite3.connect(self.db_path, timeout=10)
connection.row_factory = sqlite3.Row
return connection
def schema_sha256(self, connection: sqlite3.Connection | None = None) -> str:
own = connection is None
connection = connection or self._connect()
try:
rows = [dict(row) for row in connection.execute("PRAGMA table_info(protected_branch)")]
return sha256_json(rows)
finally:
if own:
connection.close()
def _check_schema(self, connection: sqlite3.Connection) -> None:
if not isinstance(self.expected_schema_sha256, str) or len(self.expected_schema_sha256) != 64:
raise RuntimeError("GITEA_SCHEMA_FINGERPRINT_REQUIRED")
if self.schema_sha256(connection) != self.expected_schema_sha256:
raise RuntimeError("GITEA_SCHEMA_FINGERPRINT_MISMATCH")
def _read(self, connection: sqlite3.Connection) -> dict[str, Any]:
row = connection.execute(
"SELECT * FROM protected_branch WHERE repo_id=? AND branch_name=?",
(self.repo_id, self.branch),
).fetchone()
if row is None:
raise RuntimeError("PROTECTED_BRANCH_ROW_NOT_FOUND")
return dict(row)
def read_current(self) -> dict[str, Any]:
with self._connect() as connection:
self._check_schema(connection)
return self._read(connection)
@staticmethod
def calculate_snapshot_sha256(snapshot: Mapping[str, Any]) -> str:
return calculate_snapshot_sha256(snapshot)
def compare_expected(self, expected_sha256: str) -> bool:
return calculate_snapshot_sha256(self.read_current()) == expected_sha256
def apply_atomic(
self, expected_sha256: str, target: Mapping[str, Any]
) -> dict[str, Any]:
connection = self._connect()
try:
connection.execute("BEGIN IMMEDIATE")
self._check_schema(connection)
previous = self._read(connection)
if calculate_snapshot_sha256(previous) != expected_sha256:
raise CASConflict("CAS_CONFLICT")
validate_target(previous, target)
values = dict(target)
values["updated_unix"] = int(values.get("updated_unix") or time.time())
assignments = ",".join(f"{field}=?" for field in self.ALLOWED_UPDATE_FIELDS)
parameters = [values[field] for field in self.ALLOWED_UPDATE_FIELDS]
parameters.extend((self.repo_id, self.branch))
cursor = connection.execute(
f"UPDATE protected_branch SET {assignments} WHERE repo_id=? AND branch_name=?",
parameters,
)
if cursor.rowcount != 1:
raise CASConflict("CAS_ROWCOUNT_INVALID")
applied = self._read(connection)
expected_target = dict(target)
expected_target["updated_unix"] = values["updated_unix"]
if calculate_snapshot_sha256(applied) != calculate_snapshot_sha256(expected_target):
raise CASConflict("CAS_VERIFY_FAILED")
connection.commit()
return applied
except Exception:
connection.rollback()
raise
finally:
connection.close()
def verify_applied(self, expected_sha256: str) -> bool:
return self.compare_expected(expected_sha256)
def rollback_atomic(
self, expected_target_sha256: str, previous: Mapping[str, Any]
) -> dict[str, Any]:
connection = self._connect()
try:
connection.execute("BEGIN IMMEDIATE")
self._check_schema(connection)
current = self._read(connection)
if calculate_snapshot_sha256(current) != expected_target_sha256:
raise CASConflict("ROLLBACK_CONFLICT")
for field in ("id", "repo_id", "branch_name"):
if current.get(field) != previous.get(field):
raise CASConflict("ROLLBACK_IDENTITY_MISMATCH")
assignments = ",".join(f"{field}=?" for field in self.ALLOWED_UPDATE_FIELDS)
parameters = [previous[field] for field in self.ALLOWED_UPDATE_FIELDS]
parameters.extend((self.repo_id, self.branch))
cursor = connection.execute(
f"UPDATE protected_branch SET {assignments} WHERE repo_id=? AND branch_name=?",
parameters,
)
if cursor.rowcount != 1:
raise CASConflict("ROLLBACK_ROWCOUNT_INVALID")
restored = self._read(connection)
if calculate_snapshot_sha256(restored) != calculate_snapshot_sha256(previous):
raise CASConflict("ROLLBACK_VERIFY_FAILED")
connection.commit()
return restored
except Exception:
connection.rollback()
raise
finally:
connection.close()
def verify_rollback(self, expected_sha256: str) -> bool:
return self.compare_expected(expected_sha256)
@@ -0,0 +1,189 @@
from __future__ import annotations
import hashlib
import json
import os
from pathlib import Path
import tempfile
from typing import Any, Iterable
SENSITIVE_FRAGMENTS = (
"authorization",
"cookie",
"credential",
"password",
"private_key",
"secret",
"token",
)
def canonical_json(value: object) -> bytes:
return json.dumps(value, sort_keys=True, separators=(",", ":")).encode("utf-8")
def sha256_json(value: object) -> str:
return hashlib.sha256(canonical_json(value)).hexdigest()
def redact(value: Any) -> Any:
if isinstance(value, dict):
return {
str(key): (
"[REDACTED]"
if any(fragment in str(key).lower() for fragment in SENSITIVE_FRAGMENTS)
else redact(item)
)
for key, item in value.items()
}
if isinstance(value, list):
return [redact(item) for item in value]
if isinstance(value, tuple):
return [redact(item) for item in value]
return value
def ensure_state_directory(path: Path) -> None:
if path.is_symlink():
raise RuntimeError("STATE_DIRECTORY_SYMLINK_DENIED")
path.mkdir(parents=True, exist_ok=True, mode=0o750)
path.chmod(0o750)
mode = path.stat().st_mode & 0o777
if mode != 0o750:
raise RuntimeError("STATE_DIRECTORY_MODE_INVALID")
def atomic_write_json(path: Path, value: object) -> None:
ensure_state_directory(path.parent)
raw = json.dumps(redact(value), sort_keys=True, indent=2).encode("utf-8") + b"\n"
with tempfile.NamedTemporaryFile(
dir=path.parent, prefix=f".{path.name}.", delete=False
) as handle:
temporary = Path(handle.name)
handle.write(raw)
handle.flush()
os.fsync(handle.fileno())
temporary.chmod(0o640)
os.replace(temporary, path)
path.chmod(0o640)
directory_fd = os.open(path.parent, os.O_RDONLY)
try:
os.fsync(directory_fd)
finally:
os.close(directory_fd)
def read_json(path: Path) -> dict[str, Any]:
raw = json.loads(path.read_text(encoding="utf-8"))
if not isinstance(raw, dict):
raise RuntimeError("STATE_DOCUMENT_NOT_OBJECT")
return raw
class AuditChain:
def __init__(self, path: Path) -> None:
self.path = path
def _records(self) -> list[dict[str, Any]]:
if not self.path.exists():
return []
records: list[dict[str, Any]] = []
for line in self.path.read_text(encoding="utf-8").splitlines():
if line.strip():
record = json.loads(line)
if not isinstance(record, dict):
raise RuntimeError("AUDIT_RECORD_INVALID")
records.append(record)
return records
def verify(self) -> str:
previous = "0" * 64
for index, record in enumerate(self._records(), 1):
payload = record.get("payload")
if record.get("sequence") != index:
raise RuntimeError("AUDIT_SEQUENCE_INVALID")
if record.get("previous_event_hash") != previous:
raise RuntimeError("AUDIT_PREVIOUS_HASH_INVALID")
payload_hash = sha256_json(payload)
if record.get("payload_hash") != payload_hash:
raise RuntimeError("AUDIT_PAYLOAD_HASH_INVALID")
material = {
"sequence": index,
"previous_event_hash": previous,
"payload_hash": payload_hash,
"transaction_id": record.get("transaction_id"),
}
event_hash = sha256_json(material)
if record.get("event_hash") != event_hash:
raise RuntimeError("AUDIT_EVENT_HASH_INVALID")
previous = event_hash
return previous
def append(self, transaction_id: str, payload: dict[str, Any]) -> dict[str, Any]:
ensure_state_directory(self.path.parent)
previous = self.verify()
sequence = len(self._records()) + 1
clean_payload = redact(payload)
payload_hash = sha256_json(clean_payload)
material = {
"sequence": sequence,
"previous_event_hash": previous,
"payload_hash": payload_hash,
"transaction_id": transaction_id,
}
record = {
**material,
"payload": clean_payload,
"event_hash": sha256_json(material),
}
fd = os.open(self.path, os.O_WRONLY | os.O_APPEND | os.O_CREAT, 0o640)
try:
os.write(fd, canonical_json(record) + b"\n")
os.fsync(fd)
finally:
os.close(fd)
self.path.chmod(0o640)
return record
def has_event(self, transaction_id: str, event: str) -> bool:
self.verify()
return any(
record.get("transaction_id") == transaction_id
and isinstance(record.get("payload"), dict)
and record["payload"].get("event") == event
for record in self._records()
)
class JournalStore:
def __init__(self, root: Path) -> None:
self.root = root
self.wal = root / "wal"
self.receipts = root / "receipts"
ensure_state_directory(root)
ensure_state_directory(self.wal)
ensure_state_directory(self.receipts)
self.audit = AuditChain(root / "audit.jsonl")
def transaction_path(self, operation_id: str) -> Path:
if not operation_id or any(char not in "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789._-" for char in operation_id):
raise RuntimeError("OPERATION_ID_INVALID")
return self.wal / f"{operation_id}.json"
def receipt_path(self, operation_id: str) -> Path:
return self.receipts / f"{operation_id}.json"
def load(self, operation_id: str) -> dict[str, Any] | None:
path = self.transaction_path(operation_id)
return read_json(path) if path.exists() else None
def write(self, operation_id: str, value: dict[str, Any]) -> None:
atomic_write_json(self.transaction_path(operation_id), value)
def write_receipt(self, operation_id: str, value: dict[str, Any]) -> None:
atomic_write_json(self.receipt_path(operation_id), value)
def iter_transactions(self) -> Iterable[dict[str, Any]]:
for path in sorted(self.wal.glob("*.json")):
yield read_json(path)
@@ -8,6 +8,9 @@ version = "0.1.0"
requires-python = ">=3.11"
dependencies = []
[project.scripts]
aios-single-owner-governance = "aios_single_owner_governance.activation:main"
[tool.setuptools.packages.find]
where = ["."]
include = ["aios_single_owner_governance*"]
@@ -0,0 +1,771 @@
from __future__ import annotations
import json
from pathlib import Path
import sqlite3
import subprocess
import hashlib
import os
from datetime import datetime, timedelta, timezone
import pytest
from jsonschema import Draft202012Validator, ValidationError
from aios_single_owner_governance.activation import (
ActivationEngine,
InjectedCrash,
_verify_approved_entities,
validate_bindings,
)
import aios_single_owner_governance.validator as validator_module
import aios_single_owner_governance.state_machine as state_machine_module
import aios_single_owner_governance.activation as activation_module
from aios_single_owner_governance.gitea_protected_branch import (
CASConflict,
FileProtectedBranchAdapter,
SQLiteProtectedBranchAdapter,
UnsafePolicy,
calculate_snapshot_sha256,
validate_target,
)
from aios_single_owner_governance.journal import sha256_json
MACHINE_CONTEXTS = {
"aios/validator",
"aios/state-machine",
"aios/branch-protection",
"aios/secret-scan",
"aios/evidence-closure",
}
def write_json(path: Path, value: object) -> None:
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text(json.dumps(value, sort_keys=True) + "\n", encoding="utf-8")
def legacy_row() -> dict:
return {
"id": 7,
"repo_id": 1,
"branch_name": "governance/approvals",
"can_push": 0,
"enable_whitelist": 0,
"whitelist_user_i_ds": "null",
"whitelist_team_i_ds": "null",
"enable_merge_whitelist": 1,
"whitelist_deploy_keys": 0,
"merge_whitelist_user_i_ds": "[42,43]",
"merge_whitelist_team_i_ds": "null",
"enable_status_check": 0,
"status_check_contexts": "[]",
"enable_approvals_whitelist": 1,
"approvals_whitelist_user_i_ds": "[42,43]",
"approvals_whitelist_team_i_ds": "null",
"required_approvals": 2,
"block_on_rejected_reviews": 1,
"block_on_official_review_requests": 1,
"block_on_outdated_branch": 1,
"dismiss_stale_approvals": 1,
"ignore_stale_approvals": 0,
"require_signed_commits": 0,
"protected_file_patterns": "",
"unprotected_file_patterns": "",
"created_unix": 1,
"updated_unix": 10,
}
def owner_row() -> dict:
row = legacy_row()
row.update(
{
"approvals_whitelist_user_i_ds": "[2]",
"required_approvals": 1,
"enable_status_check": 1,
"status_check_contexts": json.dumps(sorted(MACHINE_CONTEXTS), separators=(",", ":")),
"updated_unix": 11,
}
)
return row
def git_output(git_dir: Path, *args: str, stdin: str | None = None) -> str:
return subprocess.run(
["git", f"--git-dir={git_dir}", *args], input=stdin, text=True,
capture_output=True, check=True,
env={
**__import__("os").environ,
"GIT_AUTHOR_NAME": "Test", "GIT_AUTHOR_EMAIL": "test@example.invalid",
"GIT_COMMITTER_NAME": "Test", "GIT_COMMITTER_EMAIL": "test@example.invalid",
},
).stdout.strip()
def commit_files(
git_dir: Path, files: dict[str, str], message: str, *, parent: str | None = None
) -> tuple[str, str]:
root: dict[str, object] = {}
for path, raw in files.items():
node = root
parts = path.split("/")
for directory in parts[:-1]:
node = node.setdefault(directory, {}) # type: ignore[assignment]
node[parts[-1]] = git_output(git_dir, "hash-object", "-w", "--stdin", stdin=raw)
def build_tree(node: dict[str, object]) -> str:
lines = []
for name in sorted(node):
value = node[name]
if isinstance(value, dict):
lines.append(f"040000 tree {build_tree(value)}\t{name}")
else:
lines.append(f"100644 blob {value}\t{name}")
return git_output(git_dir, "mktree", stdin="\n".join(lines) + "\n")
tree = build_tree(root)
args = ["commit-tree", tree]
if parent is not None:
args.extend(("-p", parent))
return git_output(git_dir, *args, stdin=message + "\n"), tree
def evidence_fixture(tmp_path: Path, execution_binding: dict) -> dict:
git_dir = tmp_path / "evidence.git"
subprocess.run(["git", "init", "--bare", str(git_dir)], capture_output=True, check=True)
package_root = Path(activation_module.__file__).resolve().parent
source_files: dict[str, str] = {}
runtime_sources = []
for name in (
"activation.py", "gitea_protected_branch.py", "journal.py",
"state_machine.py", "validator.py",
):
source = package_root / name
raw = source.read_text(encoding="utf-8")
git_path = (
"services/aios-single-owner-governance/"
f"aios_single_owner_governance/{name}"
)
source_files[git_path] = raw
runtime_sources.append({
"git_path": git_path,
"runtime_path": str(source.resolve()),
"sha256": hashlib.sha256(source.read_bytes()).hexdigest(),
})
implementation_oid, implementation_tree = commit_files(
git_dir, source_files, "implementation"
)
closure_path = "evidence/live-activation-closure.json"
closure_raw = '{"result":"PASS"}\n'
closure_oid, _ = commit_files(
git_dir, {**source_files, closure_path: closure_raw},
"closure", parent=implementation_oid
)
closure_sha = hashlib.sha256(closure_raw.encode()).hexdigest()
entities = {}
for name, raw in {
"artifact": b"approved-artifact\n",
"rollback_artifact": b"approved-rollback\n",
"test_evidence": b"approved-tests\n",
}.items():
path = tmp_path / "artifacts" / f"{name}.bin"
path.parent.mkdir(exist_ok=True)
path.write_bytes(raw)
entities[f"{name}_path"] = str(path.resolve())
entities[f"{name}_sha256"] = hashlib.sha256(raw).hexdigest()
head_oid = git_output(git_dir, "hash-object", "-w", "--stdin", stdin="head\n")
expiry = datetime.now(timezone.utc) + timedelta(hours=1)
producer_id = 99
producer_name = "trusted-machine-producer"
execution_binding = {
**execution_binding,
"evidence_closure_oid": closure_oid,
"evidence_closure_path": closure_path,
"evidence_closure_sha256": closure_sha,
"implementation_commit_oid": implementation_oid,
"implementation_tree_oid": implementation_tree,
"runtime_sources": runtime_sources,
"machine_status_producer_id": producer_id,
"machine_status_producer_name": producer_name,
**entities,
}
manifest = {
"approval_type": "SINGLE_OWNER_GOVERNANCE_LIVE_ACTIVATION_IMPLEMENTATION_AND_EXECUTION",
"expires_at": expiry.isoformat().replace("+00:00", "Z"),
"execution_binding": execution_binding,
"execution_binding_sha256": sha256_json(execution_binding),
**{key: execution_binding[key] for key in (
"implementation_commit_oid", "implementation_tree_oid", "runtime_sources",
"artifact_path", "artifact_sha256", "rollback_artifact_path",
"rollback_artifact_sha256", "test_evidence_path", "test_evidence_sha256",
"evidence_closure_oid", "evidence_closure_path", "evidence_closure_sha256",
"machine_status_producer_id", "machine_status_producer_name",
)},
}
manifest_raw = json.dumps(manifest, sort_keys=True, separators=(",", ":")) + "\n"
manifest_blob = git_output(git_dir, "hash-object", "-w", "--stdin", stdin=manifest_raw)
leaf = git_output(git_dir, "mktree", stdin=f"100644 blob {manifest_blob}\tactivation.json\n")
approvals = git_output(git_dir, "mktree", stdin=f"040000 tree {leaf}\tsingle-owner\n")
tree = git_output(git_dir, "mktree", stdin=f"040000 tree {approvals}\tapprovals\n")
merge_oid = git_output(git_dir, "commit-tree", tree, stdin="approved\n")
git_output(git_dir, "update-ref", "refs/heads/governance/approvals", merge_oid)
db = tmp_path / "approval.db"
with sqlite3.connect(db) as connection:
connection.executescript(
"CREATE TABLE issue(id INTEGER,repo_id INTEGER,is_closed INTEGER);"
"CREATE TABLE pull_request(issue_id INTEGER,\"index\" INTEGER,base_branch TEXT,has_merged INTEGER,merged_commit_id TEXT,merged_unix INTEGER);"
"CREATE TABLE user(id INTEGER,lower_name TEXT);"
"CREATE TABLE review(id INTEGER,type INTEGER,reviewer_id INTEGER,issue_id INTEGER,commit_id TEXT,stale INTEGER,dismissed INTEGER,created_unix INTEGER);"
"CREATE TABLE commit_status(id INTEGER,repo_id INTEGER,state TEXT,sha TEXT,context TEXT,creator_id INTEGER);"
)
connection.execute("INSERT INTO issue VALUES(10,1,1)")
merged_unix = int(datetime.now(timezone.utc).timestamp())
connection.execute("INSERT INTO pull_request VALUES(10,31,'governance/approvals',1,?,?)", (merge_oid, merged_unix))
connection.execute("INSERT INTO user VALUES(2,'owner')")
connection.execute("INSERT INTO user VALUES(?,?)", (producer_id, producer_name))
connection.execute(
"INSERT INTO review VALUES(52,1,2,10,?,0,0,?)",
(head_oid, int(datetime.now(timezone.utc).timestamp())),
)
machine_checks = {}
for index, context in enumerate(sorted(MACHINE_CONTEXTS), 101):
connection.execute(
"INSERT INTO commit_status VALUES(?,1,'success',?,?,?)",
(index, head_oid, context, producer_id),
)
machine_checks[context] = {
"state": "success", "head_oid": head_oid, "status_id": index,
"creator_id": producer_id, "creator_login": producer_name,
}
return {
"git_dir": git_dir, "merge_oid": merge_oid, "closure_oid": closure_oid,
"head_oid": head_oid, "db": db, "expiry": expiry.isoformat().replace("+00:00", "Z"),
"manifest_path": "approvals/single-owner/activation.json",
"manifest_blob": manifest_blob,
"manifest_sha256": hashlib.sha256(manifest_raw.encode()).hexdigest(),
"execution_binding": execution_binding,
"machine_checks": machine_checks,
**execution_binding,
}
def bindings(tmp_path: Path, adapter: FileProtectedBranchAdapter, **overrides) -> dict:
validator_path = Path(validator_module.__file__).resolve()
state_machine_path = Path(state_machine_module.__file__).resolve()
validator_sha = hashlib.sha256(validator_path.read_bytes()).hexdigest()
state_machine_sha = hashlib.sha256(state_machine_path.read_bytes()).hexdigest()
execution_binding = {
"task_id": "AIOS-LIVE-ACTIVATION-TEST",
"operation_id": "op-live-001",
"expected_validator_sha256": validator_sha,
"expected_state_machine_sha256": state_machine_sha,
"expected_branch_protection_sha256": adapter.calculate_snapshot_sha256(legacy_row()),
"expected_gitea_schema_sha256": "0" * 64,
"target_branch_protection_sha256": calculate_snapshot_sha256(owner_row()),
"backend": "file",
"repo_id": 1,
"branch": "governance/approvals",
"requester": "agent",
"executor": "activation-executor",
"forbidden_operations": {
"main": False, "governance_direct": False, "integration": False,
"tags": False, "force_push": False,
},
}
evidence = evidence_fixture(tmp_path, execution_binding)
value = {
"task_id": "AIOS-LIVE-ACTIVATION-TEST",
"operation_id": "op-live-001",
"expected_governance_oid": evidence["merge_oid"],
"observed_governance_oid": evidence["merge_oid"],
"expected_validator_sha256": validator_sha,
"observed_validator_sha256": validator_sha,
"expected_state_machine_sha256": state_machine_sha,
"observed_state_machine_sha256": state_machine_sha,
"validator_path": str(validator_path),
"state_machine_path": str(state_machine_path),
"governance_git_dir": str(evidence["git_dir"].resolve()),
"governance_ref": "refs/heads/governance/approvals",
"evidence_git_dir": str(evidence["git_dir"].resolve()),
"approval_db_path": str(evidence["db"].resolve()),
"expected_branch_protection_sha256": adapter.calculate_snapshot_sha256(legacy_row()),
"activation_approval_merge_oid": evidence["merge_oid"],
"approval_request_head_oid": evidence["head_oid"],
"activation_approval_pr_number": 31,
"activation_manifest_path": evidence["manifest_path"],
"activation_manifest_blob_oid": evidence["manifest_blob"],
"activation_manifest_sha256": evidence["manifest_sha256"],
"expires_at": evidence["expiry"],
"evidence_closure_oid": evidence["closure_oid"],
"observed_evidence_closure_oid": evidence["closure_oid"],
**{key: evidence[key] for key in (
"implementation_commit_oid", "implementation_tree_oid", "runtime_sources",
"artifact_path", "artifact_sha256", "rollback_artifact_path",
"rollback_artifact_sha256", "test_evidence_path", "test_evidence_sha256",
"evidence_closure_path", "evidence_closure_sha256",
"machine_status_producer_id", "machine_status_producer_name",
)},
"owner_review_oid": 52,
"observed_owner_review_oid": 52,
"requester": "agent",
"executor": "activation-executor",
"wip": False,
"machine_checks": evidence["machine_checks"],
"forbidden_operations": {
"main": False,
"governance_direct": False,
"integration": False,
"tags": False,
"force_push": False,
},
"target_branch_protection": owner_row(),
"backend": "file",
"backend_path": str(adapter.path.resolve()),
"repo_id": 1,
"branch": "governance/approvals",
"expected_gitea_schema_sha256": "0" * 64,
}
value.update(overrides)
return value
def harness(tmp_path: Path):
backend = tmp_path / "protected.json"
write_json(backend, legacy_row())
adapter = FileProtectedBranchAdapter(backend)
engine = ActivationEngine(adapter, tmp_path / "state", tmp_path / "run" / "activation.lock")
request = bindings(tmp_path, adapter)
return backend, adapter, engine, request
def test_live_plan_success(tmp_path: Path) -> None:
_, _, engine, request = harness(tmp_path)
result = engine.plan(request)
assert result["status"] == "PLAN_READY"
assert len(result["plan_id"]) == 64
def test_activation_bindings_schema_matches_runtime_contract(tmp_path: Path) -> None:
_, _, _, request = harness(tmp_path)
schema_path = Path(activation_module.__file__).resolve().parents[1] / "activation-bindings.schema.json"
schema = json.loads(schema_path.read_text(encoding="utf-8"))
Draft202012Validator.check_schema(schema)
validator = Draft202012Validator(schema)
validator.validate(request)
invalid = json.loads(json.dumps(request))
invalid["machine_checks"][sorted(MACHINE_CONTEXTS)[0]]["status_id"] = 0
with pytest.raises(ValidationError):
validator.validate(invalid)
def test_runtime_source_must_be_actual_loaded_module_not_decoy(tmp_path: Path) -> None:
_, _, engine, request = harness(tmp_path)
source = Path(request["runtime_sources"][0]["runtime_path"])
decoy = tmp_path / "decoy.py"
decoy.write_bytes(source.read_bytes())
request["runtime_sources"][0]["runtime_path"] = str(decoy.resolve())
with pytest.raises(RuntimeError, match="RUNTIME_SOURCE_PATH_MISMATCH"):
_verify_approved_entities(request, Path(request["evidence_git_dir"]))
@pytest.mark.parametrize("path_field", ["artifact_path", "rollback_artifact_path", "test_evidence_path"])
def test_approved_entity_digest_is_enforced(tmp_path: Path, path_field: str) -> None:
_, _, engine, request = harness(tmp_path)
Path(request[path_field]).write_bytes(b"tampered\n")
with pytest.raises(RuntimeError, match="APPROVED_ENTITY_DIGEST_MISMATCH"):
engine.plan(request)
def test_evidence_closure_must_be_direct_child_commit(tmp_path: Path) -> None:
_, _, engine, request = harness(tmp_path)
git_dir = Path(request["evidence_git_dir"])
unrelated, _ = commit_files(
git_dir,
{request["evidence_closure_path"]: '{"result":"PASS"}\n'},
"unrelated",
)
request["evidence_closure_oid"] = unrelated
request["observed_evidence_closure_oid"] = unrelated
with pytest.raises(RuntimeError, match="APPROVED_EXECUTION_BINDING_MISMATCH"):
engine.plan(request)
def test_pr_merge_after_expiry_is_denied(tmp_path: Path) -> None:
_, _, engine, request = harness(tmp_path)
with sqlite3.connect(request["approval_db_path"]) as connection:
connection.execute(
"UPDATE pull_request SET merged_unix=?",
(int(_parse_test_utc(request["expires_at"]).timestamp()) + 1,),
)
with pytest.raises(RuntimeError, match="ACTIVATION_APPROVAL_MERGED_AFTER_EXPIRY"):
engine.plan(request)
def _parse_test_utc(value: str) -> datetime:
return datetime.fromisoformat(value.replace("Z", "+00:00"))
def test_machine_status_must_match_bound_producer_and_observed_row(tmp_path: Path) -> None:
_, _, engine, request = harness(tmp_path)
context = sorted(MACHINE_CONTEXTS)[0]
request["machine_checks"][context]["status_id"] += 1000
with pytest.raises(RuntimeError, match="MACHINE_GATE_BINDING_MISMATCH"):
engine.plan(request)
def test_latest_machine_status_from_unapproved_producer_is_denied(tmp_path: Path) -> None:
_, _, engine, request = harness(tmp_path)
context = sorted(MACHINE_CONTEXTS)[0]
with sqlite3.connect(request["approval_db_path"]) as connection:
connection.execute("INSERT INTO user VALUES(100,'untrusted-producer')")
connection.execute(
"INSERT INTO commit_status VALUES(999,1,'success',?,?,100)",
(request["approval_request_head_oid"], context),
)
with pytest.raises(RuntimeError, match="MACHINE_GATE_PRODUCER_MISMATCH"):
engine.plan(request)
def test_live_activate_verify_status_success(tmp_path: Path) -> None:
_, adapter, engine, request = harness(tmp_path)
planned = engine.plan(request)
activated = engine.activate(request, planned["plan_id"])
assert activated["status"] == "ACTIVATED"
assert adapter.read_current() == owner_row()
assert engine.verify(request, request["operation_id"])["status"] == "VERIFIED"
assert engine.status(request["operation_id"])["transaction"]["phase"] == "COMMITTED"
assert (engine.journal.root / "current-snapshot.json").exists()
assert (engine.journal.root / "rollback-snapshot.json").exists()
def test_live_rollback_success(tmp_path: Path) -> None:
_, adapter, engine, request = harness(tmp_path)
planned = engine.plan(request)
engine.activate(request, planned["plan_id"])
assert engine.rollback(request, request["operation_id"], planned["plan_id"])["status"] == "ROLLED_BACK"
assert adapter.read_current() == legacy_row()
assert engine.rollback(request, request["operation_id"], planned["plan_id"])["status"] == "ALREADY_ROLLED_BACK"
def test_terminal_rollback_cannot_be_reactivated(tmp_path: Path) -> None:
_, _, engine, request = harness(tmp_path)
planned = engine.plan(request)
engine.activate(request, planned["plan_id"])
engine.rollback(request, request["operation_id"], planned["plan_id"])
with pytest.raises(RuntimeError, match="OPERATION_TERMINAL_ROLLED_BACK"):
engine.activate(request, planned["plan_id"])
def test_verify_and_rollback_remain_available_after_approval_expiry(
tmp_path: Path, monkeypatch
) -> None:
_, adapter, engine, request = harness(tmp_path)
planned = engine.plan(request)
engine.activate(request, planned["plan_id"])
future = datetime.now(timezone.utc) + timedelta(hours=2)
class FutureDateTime(datetime):
@classmethod
def now(cls, tz=None):
return future if tz is not None else future.replace(tzinfo=None)
monkeypatch.setattr(activation_module, "datetime", FutureDateTime)
assert engine.verify(request, request["operation_id"])["status"] == "VERIFIED"
assert engine.rollback(request, request["operation_id"], planned["plan_id"])["status"] == "ROLLED_BACK"
assert adapter.read_current() == legacy_row()
def test_prepared_crash_can_recover_after_approval_expiry(tmp_path: Path, monkeypatch) -> None:
_, adapter, engine, request = harness(tmp_path)
request["crash_at"] = "before_apply"
planned = engine.plan(request)
with pytest.raises(InjectedCrash):
engine.activate(request, planned["plan_id"])
request.pop("crash_at")
future = datetime.now(timezone.utc) + timedelta(hours=2)
class FutureDateTime(datetime):
@classmethod
def now(cls, tz=None):
return future if tz is not None else future.replace(tzinfo=None)
monkeypatch.setattr(activation_module, "datetime", FutureDateTime)
assert engine.activate(request, planned["plan_id"])["status"] == "ACTIVATED"
assert adapter.read_current() == owner_row()
def test_fresh_activation_rechecks_expiry_at_prepared_boundary(
tmp_path: Path, monkeypatch
) -> None:
_, adapter, engine, request = harness(tmp_path)
planned = engine.plan(request)
expiry = _parse_test_utc(request["expires_at"])
moments = iter((
expiry - timedelta(seconds=2),
expiry - timedelta(seconds=1),
expiry + timedelta(seconds=1),
))
monkeypatch.setattr(activation_module, "_utc_now", lambda: next(moments))
with pytest.raises(RuntimeError, match="APPROVAL_EXPIRED"):
engine.activate(request, planned["plan_id"])
assert adapter.read_current() == legacy_row()
assert engine.journal.load(request["operation_id"]) is None
def test_fresh_activation_rejects_snapshot_drift_at_prepared_boundary(
tmp_path: Path, monkeypatch
) -> None:
_, adapter, engine, request = harness(tmp_path)
planned = engine.plan(request)
original_read = adapter.read_current
reads = 0
def drifting_read() -> dict:
nonlocal reads
reads += 1
if reads == 2:
drifted = original_read()
drifted["can_push"] = 1
drifted["updated_unix"] = 777
return drifted
return original_read()
monkeypatch.setattr(adapter, "read_current", drifting_read)
with pytest.raises(CASConflict, match="PREPARED_BOUNDARY_SNAPSHOT_CHANGED"):
engine.activate(request, planned["plan_id"])
assert original_read() == legacy_row()
assert engine.journal.load(request["operation_id"]) is None
def test_rollback_uses_durable_authorization_after_live_status_rerun(tmp_path: Path) -> None:
_, adapter, engine, request = harness(tmp_path)
planned = engine.plan(request)
engine.activate(request, planned["plan_id"])
context = sorted(MACHINE_CONTEXTS)[0]
with sqlite3.connect(request["approval_db_path"]) as connection:
connection.execute(
"INSERT INTO commit_status VALUES(999,1,'success',?,?,?)",
(
request["approval_request_head_oid"], context,
request["machine_status_producer_id"],
),
)
result = engine.rollback(request, request["operation_id"], planned["plan_id"])
assert result["status"] == "ROLLED_BACK"
assert adapter.read_current() == legacy_row()
def test_missing_approval_denied(tmp_path: Path) -> None:
_, _, engine, request = harness(tmp_path)
request.pop("owner_review_oid")
with pytest.raises(RuntimeError, match="MISSING_BINDINGS"):
engine.plan(request)
@pytest.mark.parametrize(
("field", "value", "message"),
[
("observed_owner_review_oid", 99, "WRONG_APPROVAL_OID_DENIED"),
("observed_governance_oid", "f" * 40, "WRONG_GOVERNANCE_OID_DENIED"),
("observed_evidence_closure_oid", "f" * 40, "WRONG_EVIDENCE_CLOSURE_DENIED"),
],
)
def test_wrong_binding_denied(tmp_path: Path, field: str, value: object, message: str) -> None:
_, _, engine, request = harness(tmp_path)
request[field] = value
with pytest.raises(RuntimeError, match=message):
engine.plan(request)
def test_cas_conflict_denied(tmp_path: Path) -> None:
backend, _, engine, request = harness(tmp_path)
planned = engine.plan(request)
changed = legacy_row()
changed["updated_unix"] = 12
write_json(backend, changed)
with pytest.raises(CASConflict):
engine.activate(request, planned["plan_id"])
@pytest.mark.parametrize("crash_at", ["before_apply", "during_apply", "after_apply_before_receipt", "after_commit_wal"])
def test_process_crash_restart_recovery(tmp_path: Path, crash_at: str) -> None:
_, adapter, engine, request = harness(tmp_path)
request["crash_at"] = crash_at
planned = engine.plan(request)
with pytest.raises(InjectedCrash):
engine.activate(request, planned["plan_id"])
request.pop("crash_at")
result = engine.activate(request, planned["plan_id"])
expected_status = "ALREADY_COMMITTED" if crash_at == "after_commit_wal" else "ACTIVATED"
assert result["status"] == expected_status
assert adapter.read_current() == owner_row()
assert engine.journal.receipt_path(request["operation_id"]).exists()
assert engine.journal.audit.has_event(request["operation_id"], "SINGLE_OWNER_ACTIVATION_COMMITTED")
def test_post_activation_verify_failure_rolls_back(tmp_path: Path, monkeypatch) -> None:
_, adapter, engine, request = harness(tmp_path)
planned = engine.plan(request)
monkeypatch.setattr(adapter, "verify_applied", lambda _: False)
with pytest.raises(RuntimeError, match="POST_ACTIVATION_VERIFY_FAILED"):
engine.activate(request, planned["plan_id"])
assert adapter.read_current() == legacy_row()
assert engine.journal.load(request["operation_id"])["phase"] == "ROLLED_BACK"
def test_operation_id_idempotency(tmp_path: Path) -> None:
_, _, engine, request = harness(tmp_path)
planned = engine.plan(request)
engine.activate(request, planned["plan_id"])
assert engine.activate(request, planned["plan_id"])["status"] == "ALREADY_COMMITTED"
@pytest.mark.parametrize("crash_at", ["after_rollback_cas", "after_rollback_wal"])
def test_rollback_crash_restart_repairs_closure(tmp_path: Path, crash_at: str) -> None:
_, adapter, engine, request = harness(tmp_path)
planned = engine.plan(request)
engine.activate(request, planned["plan_id"])
request["crash_at"] = crash_at
with pytest.raises(InjectedCrash):
engine.rollback(request, request["operation_id"], planned["plan_id"])
request.pop("crash_at")
assert engine.rollback(request, request["operation_id"], planned["plan_id"])["status"] == "ALREADY_ROLLED_BACK"
assert adapter.read_current() == legacy_row()
assert engine.journal.receipt_path(request["operation_id"]).exists()
assert engine.journal.audit.has_event(request["operation_id"], "ACTIVATION_ROLLED_BACK")
def test_operation_id_payload_mismatch_denied(tmp_path: Path) -> None:
_, _, engine, request = harness(tmp_path)
planned = engine.plan(request)
engine.activate(request, planned["plan_id"])
changed = dict(request)
approval_alias = tmp_path / "approval-alias.db"
os.link(request["approval_db_path"], approval_alias)
changed["approval_db_path"] = str(approval_alias)
with pytest.raises(RuntimeError, match="PAYLOAD_MISMATCH"):
engine.activate(changed, planned["plan_id"])
@pytest.mark.parametrize(
("field", "value"),
[("operation_id", "op-live-REPLAY"), ("task_id", "REPLAY-TASK")],
)
def test_approved_execution_binding_replay_denied(tmp_path: Path, field: str, value: str) -> None:
_, _, engine, request = harness(tmp_path)
request[field] = value
with pytest.raises(RuntimeError, match="APPROVED_EXECUTION_BINDING_MISMATCH"):
engine.plan(request)
def test_expiry_must_come_from_approved_manifest(tmp_path: Path) -> None:
_, _, engine, request = harness(tmp_path)
request["expires_at"] = (datetime.now(timezone.utc) + timedelta(hours=12)).isoformat().replace("+00:00", "Z")
with pytest.raises(RuntimeError, match="APPROVAL_EXPIRY_BINDING_MISMATCH"):
engine.plan(request)
def test_target_snapshot_must_match_approved_manifest(tmp_path: Path) -> None:
_, _, engine, request = harness(tmp_path)
request["target_branch_protection"] = {**owner_row(), "updated_unix": 99}
with pytest.raises(RuntimeError, match="APPROVED_EXECUTION_BINDING_MISMATCH"):
engine.plan(request)
def test_approval_database_must_be_backend_database(tmp_path: Path) -> None:
_, _, engine, request = harness(tmp_path)
other_db = tmp_path / "other.db"
other_db.write_bytes(Path(request["approval_db_path"]).read_bytes())
request.update(backend="sqlite", backend_path=str(other_db.resolve()))
with pytest.raises(RuntimeError, match="APPROVAL_DB_BACKEND_DB_IDENTITY_MISMATCH"):
engine.plan(request)
def test_audit_chain_continuity_and_credential_redaction(tmp_path: Path) -> None:
_, _, engine, request = harness(tmp_path)
request["api_token"] = "must-not-appear"
planned = engine.plan(request)
engine.activate(request, planned["plan_id"])
assert len(engine.journal.audit.verify()) == 64
raw = (engine.journal.root / "audit.jsonl").read_text(encoding="utf-8")
assert "must-not-appear" not in raw
assert "[REDACTED]" not in raw # only hashes/events are persisted to audit
@pytest.mark.parametrize(
"mutation",
[
lambda request: request.update(requester="owner"),
lambda request: request.update(executor="owner"),
lambda request: request.update(wip=True),
lambda request: request.update(machine_checks={"anything": {"state": "success", "head_oid": request["approval_request_head_oid"]}}),
lambda request: request.update(forbidden_operations={"main": True}),
lambda request: request.update(forbidden_operations={"tags": True}),
lambda request: request.update(forbidden_operations={"force_push": True}),
],
)
def test_negative_governance_acceptance(tmp_path: Path, mutation) -> None:
_, _, engine, request = harness(tmp_path)
mutation(request)
with pytest.raises(RuntimeError):
engine.plan(request)
@pytest.mark.parametrize(
"mutation",
[
lambda target: target.update(enable_approvals_whitelist=0),
lambda target: target.update(enable_merge_whitelist=0),
lambda target: target.update(status_check_contexts="[]"),
],
)
def test_unsafe_policy_enable_fields_denied(tmp_path: Path, mutation) -> None:
_, _, _, request = harness(tmp_path)
mutation(request["target_branch_protection"])
with pytest.raises(UnsafePolicy):
validate_target(legacy_row(), request["target_branch_protection"])
@pytest.mark.parametrize("invalid_owner", ["codex", "gpt"])
def test_owner_review_must_come_from_gitea_database(tmp_path: Path, invalid_owner: str) -> None:
_, _, engine, request = harness(tmp_path)
with sqlite3.connect(request["approval_db_path"]) as connection:
connection.execute("UPDATE user SET lower_name=? WHERE id=2", (invalid_owner,))
with pytest.raises(RuntimeError, match="OWNER_APPROVAL_INVALID"):
engine.plan(request)
def test_stale_owner_review_denied_from_gitea_database(tmp_path: Path) -> None:
_, _, engine, request = harness(tmp_path)
with sqlite3.connect(request["approval_db_path"]) as connection:
connection.execute("UPDATE review SET stale=1 WHERE id=52")
with pytest.raises(RuntimeError, match="OWNER_APPROVAL_INVALID"):
engine.plan(request)
def create_sqlite_fixture(path: Path) -> None:
row = legacy_row()
columns = []
for key, value in row.items():
kind = "INTEGER" if isinstance(value, int) else "TEXT"
columns.append(f"{key} {kind}")
with sqlite3.connect(path) as connection:
connection.execute(f"CREATE TABLE protected_branch ({','.join(columns)})")
connection.execute(
f"INSERT INTO protected_branch ({','.join(row)}) VALUES ({','.join('?' for _ in row)})",
list(row.values()),
)
def test_sqlite_full_row_cas_and_rollback(tmp_path: Path) -> None:
db = tmp_path / "gitea.db"
create_sqlite_fixture(db)
probe = SQLiteProtectedBranchAdapter(db, expected_schema_sha256="0" * 64)
adapter = SQLiteProtectedBranchAdapter(db, expected_schema_sha256=probe.schema_sha256())
previous = adapter.read_current()
applied = adapter.apply_atomic(calculate_snapshot_sha256(previous), owner_row())
assert applied == owner_row()
restored = adapter.rollback_atomic(calculate_snapshot_sha256(applied), previous)
assert restored == previous
@@ -9,7 +9,10 @@ ROOT = Path(__file__).resolve().parents[2]
class SystemdContractTests(unittest.TestCase):
def test_units_are_static_dormant_and_credential_free(self):
units = sorted((ROOT / "deploy" / "systemd").glob("aios-governance-*.service"))
units = sorted(
unit for unit in (ROOT / "deploy" / "systemd").glob("aios-governance-*.service")
if unit.name != "aios-governance-single-owner-activation.service"
)
self.assertEqual(len(units), 2)
for unit in units:
text = unit.read_text(encoding="utf-8")
@@ -25,6 +28,18 @@ class SystemdContractTests(unittest.TestCase):
self.assertIn("--checkpoint-dir /var/lib/aios-gitops/runtime/single-owner-governance", text)
self.assertNotIn("/data/aios-gitops/production", text)
def test_activation_verifier_is_static_and_has_no_credential_or_install_hook(self):
unit = ROOT / "deploy" / "systemd" / "aios-governance-single-owner-activation.service"
text = unit.read_text(encoding="utf-8")
self.assertIn("AIOS_GITOPS_WRITES_ENABLED=false", text)
self.assertIn("--verify", text)
self.assertIn("ProtectSystem=strict", text)
self.assertNotIn("EnvironmentFile=", text)
self.assertNotIn("LoadCredential=", text)
self.assertNotIn("WantedBy=", text)
self.assertNotIn("[Install]", text)
self.assertNotIn("--activate", text)
if __name__ == "__main__":
unittest.main()