Compare commits

...
Author SHA1 Message Date
AIOS Governed Continuation 257c7a8df4 feat(control-plane): add governed host seed executor 2026-08-23 01:13:46 +08:00
AIOS Governed Continuation 3244f0f17f docs(v2r2c1): finalize exact owner merge binding
autonomy-smoke-tests governed autonomy component tests passed; pytest tests: 709 passed, 2 skipped
autonomy-unit-tests full source test suite passed: 709 passed, 2 skipped
campaign-approval-gate campaign and protected-merge gate contract tests passed
git-diff-check exact integration commit diff check passed; no committed whitespace errors
py-compile active-runtime and tests compileall passed
reconciliation-contract reconciliation/job recovery contract tests passed
release-state-machine release state-machine and release-gate contract tests passed
secret-scan secret/evidence and credential safety contract tests passed
signer-validator-contract root signer self-test and signer compatibility contract passed
2026-08-09 19:25:35 +08:00
AIOS Governed Continuation b2cf510e11 docs(v2r2c1): record mainline convergence acceptance 2026-08-09 19:21:16 +08:00
AIOS Governed Continuation 2c6293160e fix(v2r2c1): bind baseline authority and openapi staging path 2026-08-09 19:09:45 +08:00
AIOS Governed Continuation 7564363a30 fix(v2r2c1): isolate model api staging port 2026-08-09 19:04:27 +08:00
AIOS Governed Continuation 7c4e86bfc1 fix(v2r2c1): avoid builder secret-scan false positives 2026-08-09 19:02:26 +08:00
AIOS Governed Continuation 4ab70bfdb4 feat(v2r2c1): add immutable model api staging runtime 2026-08-09 19:00:01 +08:00
AIOS Governed Continuation f969aedaf6 feat(v2r2c1): source baseline authority for model-api staging 2026-08-09 18:53:18 +08:00
AIOS Governed Continuation c10d2a5dfc test: align writer regression with durable idempotent replay 2026-08-09 18:34:38 +08:00
AIOS Governed Continuation f1110d24f3 fix(v2r2c1): close slash-branch and suite contract regressions 2026-08-09 18:33:57 +08:00
AIOS Governed Continuation 85897d3f6d test: expose repository test helpers to canonical collection 2026-08-09 18:27:34 +08:00
AIOS Governed Continuation 91f2ea0591 test: preserve legacy suite imports under canonical collection 2026-08-09 18:26:50 +08:00
AIOS Governed Continuation c6a1693efa test: make whole-suite collection deterministic 2026-08-09 18:25:42 +08:00
AIOS Governed Continuation b20cb8130e test: repair canonical pytest repository entrypoint 2026-08-09 18:25:09 +08:00
AIOS Governed Continuation e41b8d323a feat(v2r2c1): wire governed release operations into durable bridge 2026-08-09 18:23:10 +08:00
AIOS Governed Continuation 57780e316e feat(v2r2c1): integrate governed release overlay 2026-08-09 18:21:41 +08:00
AIOS Governed Continuation 5f616311ba docs(v2r2c1): converge durable runtime and design sources 2026-08-09 18:14:22 +08:00
AIOS Governed Continuation 40b685d1c7 feat(v2r2c1): converge durable control-plane core 2026-08-09 18:13:22 +08:00
aios-autonomy-agent 2e8fa22989 Add zero-interaction machine merge self-test marker
autonomy-smoke-tests Zero-interaction self-test exact-head status
autonomy-unit-tests Zero-interaction self-test exact-head status
campaign-approval-gate Zero-interaction self-test exact-head status
git-diff-check Zero-interaction self-test exact-head status
py-compile Zero-interaction self-test exact-head status
reconciliation-contract Zero-interaction self-test exact-head status
release-state-machine Zero-interaction self-test exact-head status
secret-scan Zero-interaction self-test exact-head status
signer-validator-contract Zero-interaction self-test exact-head status
2026-08-08 23:33:21 +08:00
AIOS Mission Convergence 277a148b08 Match Stage-1 semantic pattern representation
autonomy-unit-tests Mission semantic compatibility checks passed at exact head
autonomy-smoke-tests Mission semantic compatibility checks passed at exact head
git-diff-check Mission semantic compatibility checks passed at exact head
py-compile Mission semantic compatibility checks passed at exact head
reconciliation-contract Mission semantic compatibility checks passed at exact head
release-state-machine Mission semantic compatibility checks passed at exact head
secret-scan Mission semantic compatibility checks passed at exact head
signer-validator-contract Mission semantic compatibility checks passed at exact head
owner-approval-gate Mission semantic compatibility checks passed at exact head
campaign-approval-gate Mission semantic compatibility checks passed at exact head
2026-08-08 23:30:20 +08:00
AIOS Mission Convergence 2809dc238d Normalize empty live protection pattern fields
autonomy-unit-tests Mission canonicalizer checks passed at exact head
autonomy-smoke-tests Mission canonicalizer checks passed at exact head
git-diff-check Mission canonicalizer checks passed at exact head
py-compile Mission canonicalizer checks passed at exact head
reconciliation-contract Mission canonicalizer checks passed at exact head
release-state-machine Mission canonicalizer checks passed at exact head
secret-scan Mission canonicalizer checks passed at exact head
signer-validator-contract Mission canonicalizer checks passed at exact head
owner-approval-gate Mission canonicalizer checks passed at exact head
campaign-approval-gate Mission canonicalizer checks passed at exact head
2026-08-08 23:28:01 +08:00
AIOS Mission Convergence 6912c7175a Fail closed with actionable governance API decode errors
autonomy-unit-tests Mission API hardening checks passed at exact head
autonomy-smoke-tests Mission API hardening checks passed at exact head
git-diff-check Mission API hardening checks passed at exact head
py-compile Mission API hardening checks passed at exact head
reconciliation-contract Mission API hardening checks passed at exact head
release-state-machine Mission API hardening checks passed at exact head
secret-scan Mission API hardening checks passed at exact head
signer-validator-contract Mission API hardening checks passed at exact head
owner-approval-gate Mission API hardening checks passed at exact head
campaign-approval-gate Mission API hardening checks passed at exact head
2026-08-08 23:25:24 +08:00
AIOS Mission Convergence 92d4094e6e Migrate governance gate to campaign approval safely
autonomy-unit-tests Mission Stage-2 gate migration checks passed at exact head
autonomy-smoke-tests Mission Stage-2 gate migration checks passed at exact head
git-diff-check Mission Stage-2 gate migration checks passed at exact head
py-compile Mission Stage-2 gate migration checks passed at exact head
reconciliation-contract Mission Stage-2 gate migration checks passed at exact head
release-state-machine Mission Stage-2 gate migration checks passed at exact head
secret-scan Mission Stage-2 gate migration checks passed at exact head
signer-validator-contract Mission Stage-2 gate migration checks passed at exact head
owner-approval-gate Mission Stage-2 gate migration checks passed at exact head
campaign-approval-gate Mission Stage-2 gate migration checks passed at exact head
2026-08-08 23:18:04 +08:00
AIOS Mission Convergence 2a32e30c5c Recover verified external merge durable job safely
autonomy-unit-tests Mission durable recovery checks passed at exact head
autonomy-smoke-tests Mission durable recovery checks passed at exact head
git-diff-check Mission durable recovery checks passed at exact head
py-compile Mission durable recovery checks passed at exact head
reconciliation-contract Mission durable recovery checks passed at exact head
release-state-machine Mission durable recovery checks passed at exact head
secret-scan Mission durable recovery checks passed at exact head
signer-validator-contract Mission durable recovery checks passed at exact head
owner-approval-gate Mission durable recovery checks passed at exact head
campaign-approval-gate Mission durable recovery checks passed at exact head
2026-08-08 23:07:52 +08:00
AIOS Mission Convergence 03da23a1e3 feat(governance): add semantic and ref reconciliation primitives
autonomy-unit-tests Mission source convergence checks passed at exact head
autonomy-smoke-tests Mission source convergence checks passed at exact head
git-diff-check Mission source convergence checks passed at exact head
py-compile Mission source convergence checks passed at exact head
reconciliation-contract Mission source convergence checks passed at exact head
release-state-machine Mission source convergence checks passed at exact head
secret-scan Mission source convergence checks passed at exact head
signer-validator-contract Mission source convergence checks passed at exact head
owner-approval-gate Mission source convergence checks passed at exact head
campaign-approval-gate Mission source convergence checks passed at exact head
2026-08-08 22:55:14 +08:00
AIOS Mission Convergence 41557c4829 chore(governance): converge live control-plane sources 2026-08-08 22:53:11 +08:00
aios-autonomy-agent 103943cb59 Harden protected merge and main reconciliation gates
autonomy-unit-tests exact-head check passed: autonomy-unit-tests
autonomy-smoke-tests exact-head check passed: autonomy-smoke-tests
signer-validator-contract exact-head check passed: signer-validator-contract
secret-scan exact-head check passed: secret-scan
py-compile exact-head check passed: py-compile
git-diff-check exact-head check passed: git-diff-check
release-state-machine exact-head check passed: release-state-machine
reconciliation-contract exact-head check passed: reconciliation-contract
campaign-bootstrap-gate exact-head check passed: campaign-bootstrap-gate
owner-approval-gate RECOVERED_OWNER_CAMPAIGN_AUTHORIZATION_VERIFIED; bootstrap-only exact PR53/head; no review created; no new Owner interaction
2026-08-08 15:37:42 +08:00
chatgpt-final-release-writer a0e39a97ec feat(gitops): publish exact-head required checks
autonomy-unit-tests exact-head check passed: autonomy-unit-tests
autonomy-smoke-tests exact-head check passed: autonomy-smoke-tests
signer-validator-contract exact-head check passed: signer-validator-contract
secret-scan exact-head check passed: secret-scan
py-compile exact-head check passed: py-compile
git-diff-check exact-head check passed: git-diff-check
release-state-machine exact-head check passed: release-state-machine
reconciliation-contract exact-head check passed: reconciliation-contract
campaign-bootstrap-gate exact-head check passed: campaign-bootstrap-gate
2026-08-08 15:30:17 +08:00
chatgpt-final-release-writer 1f787acede fix(gitops): bind post-advance jobs to exact head 2026-08-08 15:26:35 +08:00
chatgpt-final-release-writer a91bfcaecc fix(gitops): grant authority executor scoped ref access 2026-08-08 15:22:36 +08:00
chatgpt-final-release-writer 6e5e3d8f59 fix(gitops): persist recoverable executor failures 2026-08-08 15:20:14 +08:00
chatgpt-final-release-writer baa3334324 feat(gitops): wire durable execution side effects 2026-08-08 15:15:40 +08:00
chatgpt-final-release-writer 3fbbc18fb2 chore(gitops): preserve worker executable mode 2026-08-08 14:34:36 +08:00
chatgpt-final-release-writer c239407bfc fix(gitops): allow scoped durable jobs with legacy writes disabled 2026-08-08 14:34:28 +08:00
chatgpt-final-release-writer eedf88bedc chore(gitops): bind durable worker to machine credential 2026-08-08 14:31:51 +08:00
chatgpt-final-release-writer 3b815da3cf feat(gitops): make durable job execution transactional 2026-08-08 14:26:40 +08:00
chatgpt-final-release-writer f0b7e9060c fix(gitops): match recovered authorization receipt schema 2026-08-08 13:07:22 +08:00
chatgpt-final-release-writer d20f5829c9 feat(gitops): add continuation lease and durable job boundary 2026-08-08 13:05:06 +08:00
aios-autonomy-agent bba675fab3 feat(openbao): add autonomy runtime v1 candidate 2026-08-08 09:56:59 +08:00
78 changed files with 11314 additions and 16 deletions
@@ -0,0 +1,173 @@
"""Fail-closed Authority Git side-effect boundary.
Only this module may issue the narrowly scoped ref CAS primitives used by the
durable executor. It never accepts force updates, arbitrary refs, tags, or
direct production mutations.
"""
from __future__ import annotations
from dataclasses import dataclass
import os
from pathlib import Path
import re
import subprocess
import tempfile
from typing import Mapping, Sequence
OID_RE = re.compile(r"^[0-9a-f]{40}$")
TASK_BRANCH_RE = re.compile(r"^refs/heads/agent/[A-Za-z0-9][A-Za-z0-9._/-]{2,179}$")
class AuthorityGitError(RuntimeError):
pass
def _oid(value: str, field: str) -> str:
if not isinstance(value, str) or not OID_RE.fullmatch(value):
raise AuthorityGitError(f"{field}_INVALID")
return value
@dataclass(frozen=True, slots=True)
class AuthorityGitExecutor:
git_dir: Path
worktree_root: Path = Path("/var/lib/aios-gitops/worktrees")
def __post_init__(self) -> None:
if not self.git_dir.is_absolute() or self.git_dir.is_symlink():
raise AuthorityGitError("AUTHORITY_GIT_DIR_INVALID")
if not self.worktree_root.is_absolute() or self.worktree_root.is_symlink():
raise AuthorityGitError("WORKTREE_ROOT_INVALID")
def _git(self, args: Sequence[str], *, check: bool = True) -> str:
result = subprocess.run(
["git", f"--git-dir={self.git_dir}", *args],
text=True,
capture_output=True,
check=False,
)
if check and result.returncode != 0:
raise AuthorityGitError("AUTHORITY_GIT_COMMAND_FAILED")
return result.stdout.strip()
def read_ref(self, ref: str) -> str | None:
if not ref.startswith("refs/") or ref.startswith(("refs/tags/", "refs/remotes/")):
raise AuthorityGitError("REF_READ_SCOPE_DENIED")
value = self._git(["rev-parse", "--verify", ref], check=False)
return value if value and OID_RE.fullmatch(value) else None
def commit_exists(self, oid: str) -> bool:
_oid(oid, "commit_oid")
result = subprocess.run(["git", f"--git-dir={self.git_dir}", "cat-file", "-e", oid + "^{commit}"], capture_output=True, check=False)
return result.returncode == 0
def ancestry_check(self, older_oid: str, newer_oid: str) -> bool:
_oid(older_oid, "older_oid")
_oid(newer_oid, "newer_oid")
result = subprocess.run(
["git", f"--git-dir={self.git_dir}", "merge-base", "--is-ancestor", older_oid, newer_oid],
capture_output=True,
check=False,
)
if result.returncode not in (0, 1):
raise AuthorityGitError("ANCESTRY_CHECK_FAILED")
return result.returncode == 0
def create_detached_worktree(
self,
base_oid: str,
worktree_name: str,
root: Path | None = None,
) -> Path:
_oid(base_oid, "base_oid")
if not worktree_name or "/" in worktree_name or "\\" in worktree_name or worktree_name in {".", ".."}:
raise AuthorityGitError("WORKTREE_NAME_INVALID")
parent = root or self.worktree_root
if not parent.is_absolute() or parent.is_symlink():
raise AuthorityGitError("WORKTREE_ROOT_INVALID")
path = parent / worktree_name
if path.exists() or path.is_symlink():
raise AuthorityGitError("WORKTREE_ALREADY_EXISTS")
parent.mkdir(parents=True, exist_ok=True)
self._git(["worktree", "add", "--detach", str(path), base_oid])
return path
def advance_task_branch_CAS(self, branch: str, expected_oid: str, new_oid: str) -> str:
if not TASK_BRANCH_RE.fullmatch(branch):
raise AuthorityGitError("TASK_BRANCH_SCOPE_DENIED")
_oid(expected_oid, "expected_oid")
_oid(new_oid, "new_oid")
current = self.read_ref(branch)
if current != expected_oid:
if current == new_oid:
return new_oid
raise AuthorityGitError("AUTHORITY_BRANCH_EXPECTATION_MISMATCH")
if not self.ancestry_check(expected_oid, new_oid):
raise AuthorityGitError("NEW_OID_NOT_DESCENDANT")
self._git(["update-ref", branch, new_oid, expected_oid])
readback = self.read_ref(branch)
if readback != new_oid:
raise AuthorityGitError("AUTHORITY_CAS_READBACK_MISMATCH")
return readback
def ff_CAS_main_after_verified_protected_merge(self, expected_oid: str, new_oid: str) -> str:
_oid(expected_oid, "expected_oid")
_oid(new_oid, "new_oid")
if not self.ancestry_check(expected_oid, new_oid):
raise AuthorityGitError("MAIN_FF_REQUIRED")
current = self.read_ref("refs/heads/main")
if current != expected_oid:
if current == new_oid:
return new_oid
raise AuthorityGitError("AUTHORITY_MAIN_EXPECTATION_MISMATCH")
self._git(["update-ref", "refs/heads/main", new_oid, expected_oid])
readback = self.read_ref("refs/heads/main")
if readback != new_oid:
raise AuthorityGitError("AUTHORITY_MAIN_CAS_READBACK_MISMATCH")
return readback
def commit_file_in_isolated_worktree(
self,
*,
base_oid: str,
files: Mapping[str, bytes],
message: str,
worktree_name: str,
) -> str:
_oid(base_oid, "base_oid")
if not worktree_name or "/" in worktree_name or "\\" in worktree_name or worktree_name in {".", ".."}:
raise AuthorityGitError("WORKTREE_NAME_INVALID")
root = self.worktree_root / worktree_name
if root.exists() or root.is_symlink():
raise AuthorityGitError("WORKTREE_ALREADY_EXISTS")
self.worktree_root.mkdir(parents=True, exist_ok=True)
self._git(["worktree", "add", "--detach", str(root), base_oid])
try:
for relative, content in files.items():
path = Path(relative)
if path.is_absolute() or ".." in path.parts or not path.parts or path.parts[0] == ".git":
raise AuthorityGitError("FILE_PATH_INVALID")
target = root / path
target.parent.mkdir(parents=True, exist_ok=True)
target.write_bytes(content)
result = subprocess.run(
["git", "-C", str(root), "add", "--", *files.keys()],
text=True,
capture_output=True,
check=False,
)
if result.returncode != 0:
raise AuthorityGitError("WORKTREE_ADD_FAILED")
result = subprocess.run(
["git", "-C", str(root), "-c", "user.name=aios-durable-job", "-c", "user.email=aios-durable-job@localhost", "commit", "--no-verify", "-m", message],
text=True,
capture_output=True,
check=False,
)
if result.returncode != 0:
raise AuthorityGitError("WORKTREE_COMMIT_FAILED")
return subprocess.check_output(["git", "-C", str(root), "rev-parse", "HEAD"], text=True).strip()
finally:
subprocess.run(["git", f"--git-dir={self.git_dir}", "worktree", "remove", "--force", str(root)], capture_output=True, check=False)
@@ -0,0 +1,104 @@
"""Read-only validation of recovered campaign authorization evidence."""
from __future__ import annotations
import hashlib
import json
from pathlib import Path
import stat
from typing import Any
class RecoveryEvidenceError(RuntimeError):
pass
def _digest(path: Path) -> str:
h = hashlib.sha256()
with path.open("rb") as stream:
for block in iter(lambda: stream.read(1024 * 1024), b""):
h.update(block)
return h.hexdigest()
def _secure_json(path: Path) -> dict[str, Any]:
if path.is_symlink() or not path.is_file():
raise RecoveryEvidenceError("RECOVERY_EVIDENCE_MISSING")
info = path.stat(follow_symlinks=False)
if info.st_uid != 0 or stat.S_IMODE(info.st_mode) & 0o022:
raise RecoveryEvidenceError("RECOVERY_EVIDENCE_PERMISSIONS")
try:
value = json.loads(path.read_text(encoding="utf-8"))
except (OSError, UnicodeDecodeError, json.JSONDecodeError) as exc:
raise RecoveryEvidenceError("RECOVERY_EVIDENCE_INVALID") from exc
if not isinstance(value, dict):
raise RecoveryEvidenceError("RECOVERY_EVIDENCE_OBJECT_REQUIRED")
return value
def load_recovered_authorization(path: str | Path) -> dict[str, Any]:
"""Verify the recovery receipt, sidecar, and all referenced immutable files."""
receipt_path = Path(path)
receipt = _secure_json(receipt_path)
sidecar = receipt_path.with_suffix(receipt_path.suffix + ".sha256")
if sidecar.is_symlink() or not sidecar.is_file():
raise RecoveryEvidenceError("RECOVERY_DIGEST_SIDECAR_MISSING")
expected = sidecar.read_text(encoding="ascii").split()[0]
actual = _digest(receipt_path)
if expected != actual:
raise RecoveryEvidenceError("RECOVERY_DIGEST_MISMATCH")
required = {
"schema",
"campaign_id",
"original_event_id",
"original_authorization_digest",
"signed_grant_id",
"signed_grant_digest",
"task_id",
"repository",
"original_base_oid",
"branch",
"expected_branch_oid",
"recovered_from_existing_evidence",
"new_owner_interaction",
"recovery_state",
"production_side_effect_executed",
"global_writes_enabled",
"grant_consumed",
"owner_authorization_consumed",
"original_receipts_immutable",
"recovered_at",
"evidence_files",
}
if set(receipt) != required or receipt["schema"] != "aios.owner-authorization-recovery.v1":
raise RecoveryEvidenceError("RECOVERY_SCHEMA_INVALID")
if receipt["recovered_from_existing_evidence"] is not True or receipt["new_owner_interaction"] is not False:
raise RecoveryEvidenceError("RECOVERY_NOT_APPEND_ONLY")
if receipt["production_side_effect_executed"] is not False or receipt["global_writes_enabled"] is not False:
raise RecoveryEvidenceError("RECOVERY_SCOPE_INVALID")
if receipt["recovery_state"] != "RECOVERED_PENDING_CONTINUATION_LEASE":
raise RecoveryEvidenceError("RECOVERY_STATE_INVALID")
evidence = receipt["evidence_files"]
if not isinstance(evidence, list) or not evidence:
raise RecoveryEvidenceError("RECOVERY_EVIDENCE_LIST_INVALID")
for item in evidence:
if not isinstance(item, dict) or set(item) != {"path", "sha256"}:
raise RecoveryEvidenceError("RECOVERY_EVIDENCE_ENTRY_INVALID")
target = Path(item["path"])
if _digest(target) != item["sha256"]:
raise RecoveryEvidenceError("RECOVERY_EVIDENCE_DIGEST_MISMATCH")
return {
"schema": receipt["schema"],
"state": receipt["recovery_state"],
"campaign_id": receipt["campaign_id"],
"original_event_id": receipt["original_event_id"],
"original_authorization_digest": receipt["original_authorization_digest"],
"signed_grant_id": receipt["signed_grant_id"],
"task_id": receipt["task_id"],
"repository": receipt["repository"],
"original_base_oid": receipt["original_base_oid"],
"branch": receipt["branch"],
"expected_branch_oid": receipt["expected_branch_oid"],
"digest": actual,
"new_owner_interaction": False,
}
+96 -2
View File
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
from __future__ import annotations
import hashlib, hmac, json, os, re, subprocess, sys, time, urllib.parse, uuid
import hashlib, hmac, json, os, re, subprocess, sys, time, urllib.parse, urllib.request, uuid
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
from pathlib import Path
from typing import Any
@@ -33,6 +33,11 @@ from silent_release_hardening import (
SignedReleasePlanApproval,
SilentReleaseActionService,
)
try:
from aios_gitops_job_controller import GitOpsJobController, JobError, ServiceUnavailable, JobStore
except ModuleNotFoundError:
sys.path.insert(0, str(Path(__file__).resolve().parent))
from aios_gitops_job_controller import GitOpsJobController, JobError, ServiceUnavailable, JobStore
try:
from authoritative_git import (
AuthoritativeGitError,
@@ -120,6 +125,12 @@ def reject_non_read_http(handler, method: str, path: str) -> bool:
return False
if path in SILENT_RELEASE_POST_PATHS:
return False
if (
method == 'POST'
and path in ('/api/file', '/api/commit-file')
and os.getenv('AIOS_GITOPS_DURABLE_JOB_EXECUTION_ENABLED', os.getenv('AIOS_GITOPS_JOB_CONTROLLER_ENABLED', 'false')).lower() in {'1', 'true', 'yes', 'on'}
):
return False
decision = evaluate_git_write(WS)
if decision.allowed:
return False
@@ -393,6 +404,51 @@ class H(BaseHTTPRequestHandler):
if not a['ok']: self.sendj({'error':'unauthorized','hint':'Bearer token required'},401); return None
if '*' not in a['scopes'] and scope not in a['scopes']: self.sendj({'error':'forbidden','required_scope':scope,'account':a['account']},403); return None
return a
def bridge_release(self, path, data):
route = {
'/createTagApprovalRequest': '/v2/tag/approval-request',
'/requestOrCreateTag': '/v2/tag/publish',
'/getTagPublicationStatus': '/v2/tag/status',
'/buildProductionArtifact': '/v2/release/artifact',
'/executeApprovedProductionRelease': '/v2/release/execute',
'/getReleaseConsistency': '/v2/release/consistency',
'/runReleaseAcceptance': '/v2/release/acceptance',
}.get(path)
if route is None:
raise ValueError('governed_release_route_invalid')
base = os.getenv('AIOS_BRIDGE_V2_URL', 'http://127.0.0.1:18892').rstrip('/')
request = urllib.request.Request(
base + route,
data=json.dumps(data, sort_keys=True, separators=(',', ':')).encode('utf-8'),
headers={'Content-Type': 'application/json'},
method='POST',
)
try:
with urllib.request.urlopen(request, timeout=30) as response:
result = json.loads(response.read().decode('utf-8'))
return self.sendj(result, response.status)
except urllib.error.HTTPError as exc:
try:
body = json.loads(exc.read().decode('utf-8'))
except Exception:
body = {'error': 'bridge_http_error'}
return self.sendj(body, exc.code)
except (urllib.error.URLError, TimeoutError) as exc:
return self.sendj({'error': 'GOVERNED_BRIDGE_UNAVAILABLE', 'detail': type(exc).__name__}, 503)
def do_POST(self):
path = urllib.parse.urlparse(self.path).path
if path in {
'/createTagApprovalRequest', '/requestOrCreateTag', '/getTagPublicationStatus',
'/buildProductionArtifact', '/executeApprovedProductionRelease',
'/getReleaseConsistency', '/runReleaseAcceptance',
}:
actor = self.need('tag:create')
if not actor:
return
return self.bridge_release(path, self.body())
if reject_non_read_http(self, 'POST', path):
return
return self.sendj({'error': 'not_found', 'path': path}, 404)
def do_GET(self):
u=urllib.parse.urlparse(self.path); path=u.path.rstrip('/') if u.path != '/' else u.path
if path not in READ_ONLY_GET_PATHS: return self.sendj({'error':'not_found','path':path},404)
@@ -666,7 +722,44 @@ class H(BaseHTTPRequestHandler):
def file(self,data):
a=self.need('file:write');
if not a: return
b=checkout(data.get('branch'), data.get('base','main'), bool(data.get('create_branch'))); r=write_file(data.get('path'), data.get('content','')); c=commit([r], data.get('message') or f'chore(chatgpt): update {r}'); audit(a['account'],'file',r,{'branch':b,'commit':c}); self.sendj({'status':'committed','branch':b,'path':r,'commit':c})
if os.getenv('AIOS_GITOPS_DURABLE_JOB_EXECUTION_ENABLED', os.getenv('AIOS_GITOPS_JOB_CONTROLLER_ENABLED', 'false')).lower() not in {'1','true','yes','on'}:
return self.sendj({'error':'DURABLE_JOB_CONTROLLER_UNAVAILABLE','retryable':True},503)
try:
path = relpath(str(data.get('path') or ''))
branch = str(data.get('branch') or '')
expected = str(data.get('expected_branch_oid') or '')
campaign_id = str(data.get('campaign_id') or os.getenv('AIOS_GITOPS_CAMPAIGN_ID') or '')
supplied_digest = str(data.get('payload_digest') or '')
content = data.get('content')
payload_bytes = None
if content is not None:
payload_bytes = content.encode('utf-8') if isinstance(content, str) else json.dumps(content, sort_keys=True, ensure_ascii=False).encode('utf-8')
payload_digest = supplied_digest or hashlib.sha256(payload_bytes or b'').hexdigest()
if not campaign_id or not branch or not expected:
return self.sendj({'error':'JOB_BINDING_REQUIRED','retryable':False},409)
request = {
'campaign_id': campaign_id,
'repository': REPOSITORY_IDENTITY or 'aios/aios',
'operation': 'commit-file',
'branch': branch,
'expected_branch_oid': expected,
'payload_digest': payload_digest,
# The controller persists only the binding and digest. Raw
# content is never copied into durable state or logs.
}
receipt = GitOpsJobController(
JobStore(Path(os.getenv('AIOS_GITOPS_JOB_ROOT', '/var/lib/aios-gitops/jobs')))
).submit(request, payload=payload_bytes)
return self.sendj({
'job_id': receipt.job_id,
'state': receipt.state.value,
'side_effect_state': receipt.side_effect_state,
'retryable': receipt.http_status == 503,
}, receipt.http_status)
except ServiceUnavailable as exc:
return self.sendj({'error':str(exc),'layer':'durable_job','retryable':True,'side_effect_state':'UNKNOWN'},503)
except JobError as exc:
return self.sendj({'error':str(exc),'retryable':False},409)
def commit(self,data):
a=self.need('commit:create');
if not a: return
@@ -857,3 +950,4 @@ def main():
print(f'AIOS ChatGPT GitOps adapter listening on {HOST}:{PORT}')
ThreadingHTTPServer((HOST,PORT), H).serve_forever()
if __name__ == '__main__': main()
@@ -0,0 +1,167 @@
"""ChatGPT GitOps Bridge v2 contract and governed release operations."""
from __future__ import annotations
from dataclasses import dataclass
import hashlib
from pathlib import Path
from typing import Any, Callable, Mapping
BRIDGE_SCHEMA_VERSION = "aios.chatgpt-gitops-bridge.v2"
BRIDGE_OPERATIONS = (
"getRuntimeStatus",
"getGovernedWriteCapabilities",
"createGovernedTask",
"acquireWriterLease",
"commitFileGoverned",
"commitFilesGoverned",
"getGitOpsJob",
"resumeGitOpsJob",
"reconcileGitOpsJob",
"createTagApprovalRequest",
"requestOrCreateTag",
"getTagPublicationStatus",
"buildProductionArtifact",
"executeApprovedProductionRelease",
"getReleaseConsistency",
"runReleaseAcceptance",
"getControlPlaneSnapshot",
)
OPERATION_REQUIRED_FIELDS = {
"createGovernedTask": ("task_id", "repository", "base_oid", "target_branch", "risk", "description", "idempotency_key"),
"acquireWriterLease": ("schema", "task_id", "repository", "branch_mode", "base_oid", "expected_branch_oid", "target_branch", "generation", "lease_seconds"),
"commitFileGoverned": ("task_id", "repository", "expected_branch_oid", "target_branch", "generation", "idempotency_key", "path", "content_base64", "message"),
"commitFilesGoverned": ("task_id", "repository", "expected_branch_oid", "target_branch", "generation", "idempotency_key", "files", "message"),
"getGitOpsJob": ("job_id",),
"resumeGitOpsJob": ("job_id", "expected_revision"),
"reconcileGitOpsJob": ("job_id", "expected_revision"),
"createTagApprovalRequest": ("version", "target_oid", "evidence_closure_oid", "approval_plan_id", "owner_approval", "execute"),
"requestOrCreateTag": ("version", "target_oid", "evidence_closure_oid", "approval_plan_id", "execute"),
"getTagPublicationStatus": ("version",),
"buildProductionArtifact": ("version", "target_oid", "evidence_closure_oid", "approval_plan_id"),
"executeApprovedProductionRelease": ("version", "target_oid", "evidence_closure_oid", "approval_plan_id", "execute"),
"getReleaseConsistency": ("version", "target_oid", "evidence_closure_oid", "approval_plan_id"),
"runReleaseAcceptance": ("version", "target_oid", "evidence_closure_oid", "approval_plan_id"),
"getControlPlaneSnapshot": (),
}
class RuntimeDriftError(RuntimeError):
pass
class BridgeV2Error(RuntimeError):
pass
@dataclass(frozen=True, slots=True)
class ControlPlaneReleaseManifest:
control_plane_release_version: str
control_plane_target_oid: str
artifact_sha256: str
runtime_path: Path
bridge_schema_version: str
repository_identity: str
authoritative_git_dir: Path
deployment_state: str
def as_dict(self) -> dict[str, object]:
return {
"control_plane_release_version": self.control_plane_release_version,
"control_plane_target_oid": self.control_plane_target_oid,
"artifact_sha256": self.artifact_sha256,
"runtime_path": str(self.runtime_path),
"bridge_schema_version": self.bridge_schema_version,
"repository_identity": self.repository_identity,
"authoritative_git_dir": str(self.authoritative_git_dir),
"deployment_state": self.deployment_state,
}
def validate(self, artifact: Path, *, observed_oid: str, approved_root: Path = Path("/opt/aios/gitops-control-plane")) -> dict[str, object]:
try:
runtime = self.runtime_path.resolve(strict=True)
root = approved_root.resolve(strict=True)
artifact_path = artifact.resolve(strict=True)
runtime.relative_to(root)
artifact_path.relative_to(runtime)
except (OSError, ValueError):
raise RuntimeDriftError("CONTROL_PLANE_RUNTIME_DRIFT") from None
mutable_markers = {"workspace", "workspaces", "worktree", "worktrees"}
if mutable_markers.intersection(part.lower() for part in runtime.parts):
raise RuntimeDriftError("CONTROL_PLANE_RUNTIME_DRIFT")
valid = (
len(self.control_plane_target_oid) == 40
and observed_oid == self.control_plane_target_oid
and len(self.artifact_sha256) == 64
and hashlib.sha256(artifact_path.read_bytes()).hexdigest() == self.artifact_sha256
and self.bridge_schema_version == BRIDGE_SCHEMA_VERSION
and self.repository_identity == "aios/aios"
and self.authoritative_git_dir == Path("/data/aios-gitops/repo.git")
and self.authoritative_git_dir.is_absolute()
and self.deployment_state == "STAGING"
)
if not valid:
raise RuntimeDriftError("CONTROL_PLANE_RUNTIME_DRIFT")
return {**self.as_dict(), "drift_status": "PASS"}
class BridgeV2Adapter:
"""Thin operation adapter; all mutating handlers are injected seams."""
def __init__(self, *, runtime_status: Mapping[str, Any], handlers: Mapping[str, Callable[[Mapping[str, Any]], Any]] | None = None) -> None:
self.runtime_status = dict(runtime_status)
self.handlers = dict(handlers or {})
def getRuntimeStatus(self) -> dict[str, Any]:
return {"bridge_schema_version": BRIDGE_SCHEMA_VERSION, **self.runtime_status}
def getGovernedWriteCapabilities(self) -> dict[str, Any]:
return {
"bridge_schema_version": BRIDGE_SCHEMA_VERSION,
"legacy_writes_enabled": False,
"governed_writes_enabled": True,
"operations": list(BRIDGE_OPERATIONS),
"force_push": False,
"main_write": False,
"tag_write": False,
"baseline_write": False,
"production_write": False,
"approved_release_operations": True,
"approved_release_scope": {
"version": "V2R2C1",
"target_oid": "998d30304787e31e5ae607c0bd86b5edb2b0e5ad",
"evidence_closure_oid": "a8b906d1bc24088c19647e98de6a679268f6d0a2",
"approval_plan_id": "sha256:c0df52008ae7f64cec5ee2d28947aa7b892066c670ab8295e4549140b1a7500e",
},
}
@staticmethod
def legacy_write_disabled() -> dict[str, object]:
return {"error": "LEGACY_WRITE_DISABLED", "replacement": "createGovernedTask", "governed_writes_available": True}
def _call(self, operation: str, request: Mapping[str, Any]) -> Any:
required = OPERATION_REQUIRED_FIELDS[operation]
if any(key not in request for key in required):
raise BridgeV2Error("BRIDGE_REQUEST_FIELDS_MISSING")
handler = self.handlers.get(operation)
if handler is None:
raise BridgeV2Error("GOVERNED_HANDLER_UNAVAILABLE")
return handler(dict(request))
def createGovernedTask(self, request: Mapping[str, Any]) -> Any: return self._call("createGovernedTask", request)
def acquireWriterLease(self, request: Mapping[str, Any]) -> Any: return self._call("acquireWriterLease", request)
def commitFileGoverned(self, request: Mapping[str, Any]) -> Any: return self._call("commitFileGoverned", request)
def commitFilesGoverned(self, request: Mapping[str, Any]) -> Any: return self._call("commitFilesGoverned", request)
def getGitOpsJob(self, request: Mapping[str, Any]) -> Any: return self._call("getGitOpsJob", request)
def resumeGitOpsJob(self, request: Mapping[str, Any]) -> Any: return self._call("resumeGitOpsJob", request)
def reconcileGitOpsJob(self, request: Mapping[str, Any]) -> Any: return self._call("reconcileGitOpsJob", request)
def createTagApprovalRequest(self, request: Mapping[str, Any]) -> Any: return self._call("createTagApprovalRequest", request)
def requestOrCreateTag(self, request: Mapping[str, Any]) -> Any: return self._call("requestOrCreateTag", request)
def getTagPublicationStatus(self, request: Mapping[str, Any]) -> Any: return self._call("getTagPublicationStatus", request)
def buildProductionArtifact(self, request: Mapping[str, Any]) -> Any: return self._call("buildProductionArtifact", request)
def executeApprovedProductionRelease(self, request: Mapping[str, Any]) -> Any: return self._call("executeApprovedProductionRelease", request)
def getReleaseConsistency(self, request: Mapping[str, Any]) -> Any: return self._call("getReleaseConsistency", request)
def runReleaseAcceptance(self, request: Mapping[str, Any]) -> Any: return self._call("runReleaseAcceptance", request)
def getControlPlaneSnapshot(self, request: Mapping[str, Any] | None = None) -> Any: return self._call("getControlPlaneSnapshot", request or {})
@@ -0,0 +1,431 @@
"""Loopback-only governed Bridge v2 HTTP gateway with durable handlers."""
from __future__ import annotations
from dataclasses import dataclass
from datetime import datetime, timedelta, timezone
import base64
import json
import os
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
from pathlib import Path
import re
import sqlite3
from typing import Any, Mapping
from aios_authority_git_executor import AuthorityGitExecutor
from aios_chatgpt_bridge_v2 import BridgeV2Adapter, BridgeV2Error, OPERATION_REQUIRED_FIELDS
from aios_continuation_executor import AuthorityGitAdapter, ContinuationCommit, ContinuationExecutor, ContinuationPermissions, ContinuationRuntimeBinding, DetachedGitWorkspace, GiteaContinuationRemote, ServerSideCredentialProjection
from aios_continuation_lease import WriterLeaseRequestV2
from aios_control_plane_snapshot import build_control_plane_snapshot
from aios_gitops_job_controller import GitOpsJobController, JobStore
from aios_governed_task_bootstrap import BootstrapError, GovernedTaskBootstrap
from governed_production_release import build_executor_config
MAX_BODY = 2 * 1024 * 1024
ROUTES = {
"/v2/runtime": "getRuntimeStatus",
"/v2/capabilities": "getGovernedWriteCapabilities",
"/v2/governed-task": "createGovernedTask",
"/v2/writer-lease": "acquireWriterLease",
"/v2/commit-file": "commitFileGoverned",
"/v2/commit-files": "commitFilesGoverned",
"/v2/job": "getGitOpsJob",
"/v2/job/resume": "resumeGitOpsJob",
"/v2/job/reconcile": "reconcileGitOpsJob",
"/v2/tag/approval-request": "createTagApprovalRequest",
"/v2/tag/publish": "requestOrCreateTag",
"/v2/tag/status": "getTagPublicationStatus",
"/v2/release/artifact": "buildProductionArtifact",
"/v2/release/execute": "executeApprovedProductionRelease",
"/v2/release/consistency": "getReleaseConsistency",
"/v2/release/acceptance": "runReleaseAcceptance",
}
@dataclass(frozen=True, slots=True)
class RuntimeConfig:
target_oid: str
authoritative_git_dir: Path
state_root: Path
v2_job_root: Path
token_path: Path
ssh_key_path: Path
remote_name: str = "gitea-http"
repository: str = "aios/aios"
api_base: str = "https://g.cnnetmkt.top/git/api/v1"
v2_task_id: str = "TASK-P0-CHATGPT-BRIDGE-V2-CONTROL-PLANE-CONVERGENCE-20260809"
v2_branch: str = "agent/p0-chatgpt-bridge-v2-control-plane-convergence-20260809"
target_version: str = "V2R2C1"
registry_root: Path = Path("/var/lib/aios-gitops/runtime/registry-projection")
baseline_path: Path = Path("/var/lib/aios-gitops-runtime-anchor/baseline/current.json")
health_url: str = "http://127.0.0.1:18927/health"
def _safe(value: str) -> str:
return re.sub(r"[^A-Za-z0-9_.-]+", "-", value).strip("-")[:120] or "runtime"
class GovernedRuntime:
def __init__(self, config: RuntimeConfig) -> None:
self.config = config
config.state_root.mkdir(parents=True, exist_ok=True)
self.authority = AuthorityGitExecutor(config.authoritative_git_dir, config.state_root / "worktrees")
self.bootstrap = GovernedTaskBootstrap(self.authority, config.state_root, remote_name=config.remote_name, token_path=config.token_path, ssh_key_path=config.ssh_key_path, api_base=config.api_base)
self.runtimes: dict[tuple[str, int], tuple[Any, GitOpsJobController, ContinuationExecutor]] = {}
self.controllers: dict[str, GitOpsJobController] = {}
self.bindings: dict[str, dict[str, Any]] = {}
self.release = build_executor_config(config)
metadata_root = config.state_root / "task-bindings"
if metadata_root.is_dir():
for metadata in metadata_root.glob("*.json"):
try:
value = json.loads(metadata.read_text(encoding="utf-8"))
if isinstance(value, dict) and value.get("task_id"):
self.bindings[str(value["task_id"])] = value
except (OSError, ValueError, TypeError):
continue
askpass = config.state_root / "bridge-v2-askpass.sh"
if not askpass.exists():
askpass.write_text('#!/bin/sh\ncase "$1" in *Username*) printf "%s\\n" "$GITEA_MACHINE_ACTOR" ;; *) cat "$CREDENTIAL_FILE" ;; esac\n', encoding="utf-8")
askpass.chmod(0o700)
os.environ.setdefault("GIT_ASKPASS", str(askpass)); os.environ.setdefault("GIT_ASKPASS_REQUIRE", "force"); os.environ.setdefault("GIT_TERMINAL_PROMPT", "0"); os.environ.setdefault("GITEA_MACHINE_ACTOR", "aios-autonomy-agent")
def runtime_status(self) -> dict[str, Any]:
return {
"status": "active",
"health": 200,
"bridge_schema_version": "aios.chatgpt-gitops-bridge.v2",
"control_plane_release_version": self.config.target_version,
"control_plane_target_oid": self.config.target_oid,
"runtime_release_commit": self.config.target_oid,
"runtime_path": str(self.config.state_root),
"artifact_sha256": os.environ.get("AIOS_CONTROL_PLANE_ARTIFACT_SHA256"),
"deployment_state": "STAGING",
"drift_status": "PASS",
"repository": self.config.repository,
"repository_identity": self.config.repository,
"authoritative_git_dir": str(self.config.authoritative_git_dir),
"legacy_global_writes_enabled": False,
}
def _job_root(self, task_id: str) -> Path:
return self.config.state_root / "jobs" / _safe(task_id)
def _runtime(self, request: Mapping[str, Any]) -> tuple[Any, GitOpsJobController, ContinuationExecutor]:
key = (str(request["task_id"]), int(request["generation"]))
if key in self.runtimes: return self.runtimes[key]
lease = WriterLeaseRequestV2(task_id=str(request["task_id"]), repository=str(request["repository"]), branch_mode=str(request.get("branch_mode", "CONTINUE_EXISTING")), base_oid=str(request["base_oid"]), expected_branch_oid=str(request["expected_branch_oid"]), target_branch=str(request["target_branch"]), generation=int(request["generation"]), lease_seconds=int(request.get("lease_seconds", 900)))
lease.validate(AuthorityGitAdapter(self.authority))
worktree_path = self.config.state_root / "worktrees" / _safe(lease.task_id)
if worktree_path.exists():
workspace = DetachedGitWorkspace(worktree_path)
else:
self.authority.create_detached_worktree(lease.expected_branch_oid, _safe(lease.task_id), self.config.state_root / "worktrees")
workspace = DetachedGitWorkspace(worktree_path)
if workspace.head_oid() != lease.expected_branch_oid or not workspace.is_detached(): raise RuntimeError("WORKTREE_HEAD_MISMATCH")
job_root = self._job_root(lease.task_id); controller = GitOpsJobController(JobStore(job_root, payload_root=job_root / "payloads")); credential = ServerSideCredentialProjection(self.config.ssh_key_path, self.config.token_path); remote = GiteaContinuationRemote(workspace=worktree_path, credential=credential, remote_name=self.config.remote_name, repository=lease.repository, api_base=self.config.api_base); executor = ContinuationExecutor(controller, AuthorityGitAdapter(self.authority), workspace, remote, credential)
binding = ContinuationRuntimeBinding(request=lease, workspace_path=worktree_path, lease_not_before=datetime.now(timezone.utc) - timedelta(seconds=1), lease_expires_at=datetime.now(timezone.utc) + timedelta(seconds=lease.lease_seconds), original_receipt_valid=True, original_grant_consumed=True, permissions=ContinuationPermissions())
result = (binding, controller, executor); self.runtimes[key] = result; self.controllers[lease.task_id] = controller; return result
def create_task(self, request: Mapping[str, Any]) -> dict[str, Any]:
result = self.bootstrap.create(request); self.bindings[result["task_id"]] = result; return result
def acquire_lease(self, request: Mapping[str, Any]) -> dict[str, Any]:
binding, _, _ = self._runtime(request)
req = binding.request
return {"schema": WriterLeaseRequestV2.schema, "task_id": req.task_id, "repository": req.repository, "branch_mode": req.branch_mode, "base_oid": req.base_oid, "expected_branch_oid": req.expected_branch_oid, "target_branch": req.target_branch, "generation": req.generation, "lease_seconds": req.lease_seconds, "create_target_branch": req.branch_mode == "CREATE_NEW", "worktree_mode": "detached_at_base_oid" if req.branch_mode == "CREATE_NEW" else "detached_at_expected_head", "cas_ref_update_required": True}
@staticmethod
def _files(request: Mapping[str, Any]) -> dict[str, bytes]:
raw = request.get("files") or {request["path"]: request["content_base64"]}; pairs = raw.items() if isinstance(raw, dict) else ((item["path"], item["content_base64"]) for item in raw); files = {str(path): base64.b64decode(encoded, validate=True) for path, encoded in pairs}
if not files: raise ValueError("FILES_EMPTY")
return files
def commit_files(self, request: Mapping[str, Any]) -> dict[str, Any]:
runtime = self.runtimes.get((str(request["task_id"]), int(request["generation"])))
if runtime is None: raise RuntimeError("LEASE_NOT_FOUND")
binding, _, executor = runtime
if request["target_branch"] != binding.request.target_branch or request["expected_branch_oid"] != binding.request.expected_branch_oid: raise RuntimeError("LEASE_BINDING_CONFLICT")
commit = ContinuationCommit(idempotency_key=str(request["idempotency_key"]), files=self._files(request), message=str(request["message"]))
executor.workspace.materialize_files(commit.files)
return executor.execute(binding, commit)
def _controller_for_job(self, job_id: str) -> GitOpsJobController:
for controller in self.controllers.values():
try:
controller.store.read(job_id)
return controller
except Exception:
continue
jobs_root = self.config.state_root / "jobs"
if jobs_root.is_dir():
for root in jobs_root.iterdir():
if not root.is_dir():
continue
controller = GitOpsJobController(JobStore(root, payload_root=root / "payloads"))
try:
controller.store.read(job_id)
self.controllers[str(job_id)] = controller
return controller
except Exception:
continue
raise RuntimeError("JOB_NOT_FOUND")
@staticmethod
def _valid_oid(value: object) -> bool:
return isinstance(value, str) and re.fullmatch(r"[0-9a-f]{40}", value) is not None
def _reconstruct_legacy_binding(
self,
row: Mapping[str, Any],
controller: GitOpsJobController,
) -> dict[str, Any]:
raw = json.loads(row["request_json"])
binding = raw.get("binding", raw)
if not isinstance(binding, dict):
raise RuntimeError("DURABLE_BINDING_INVALID")
task_id = str(binding.get("task_id") or binding.get("campaign_id") or "").strip()
repository = str(binding.get("repository") or "").strip()
operation = str(binding.get("operation") or row.get("operation") or "").strip()
branch = str(binding.get("target_branch") or binding.get("branch") or row.get("branch") or "").strip()
expected = str(binding.get("expected_branch_oid") or row.get("expected_branch_oid") or "").strip()
base = binding.get("base_oid") or binding.get("lineage_anchor_oid")
if not base:
with controller.store._connect() as connection:
lease_row = connection.execute(
"SELECT lineage_anchor_oid FROM branch_leases WHERE job_id=? ORDER BY revision DESC LIMIT 1",
(str(row["job_id"]),),
).fetchone()
if lease_row is not None:
base = lease_row["lineage_anchor_oid"]
base = str(base or "").strip()
branch_mode = str(
binding.get("branch_mode")
or ("CREATE_NEW" if operation.endswith("_CREATE_NEW") else "CONTINUE_EXISTING")
).strip()
try:
generation = int(binding.get("generation") or 0)
except (TypeError, ValueError) as exc:
raise RuntimeError("DURABLE_BINDING_GENERATION_INVALID") from exc
if (
not task_id
or repository != self.config.repository
or not branch.startswith("agent/")
or branch in {"main", "master"}
or branch_mode not in {"CREATE_NEW", "CONTINUE_EXISTING"}
or generation < 1
or not self._valid_oid(base)
or not self._valid_oid(expected)
):
raise RuntimeError("DURABLE_BINDING_INVALID")
current = self.authority.read_ref("refs/heads/" + branch)
if (
current is None
or not self.authority.commit_exists(base)
or not self.authority.commit_exists(expected)
or not self.authority.ancestry_check(base, current)
or not self.authority.ancestry_check(expected, current)
):
raise RuntimeError("DURABLE_BINDING_LINEAGE_INVALID")
return {
"task_id": task_id,
"repository": repository,
"branch_mode": branch_mode,
"base_oid": base,
"expected_branch_oid": expected,
"target_branch": branch,
"generation": generation,
"lease_seconds": int(binding.get("lease_seconds") or 900),
}
def _executor_from_row(
self,
row: Mapping[str, Any],
controller: GitOpsJobController,
) -> ContinuationExecutor:
request = self._reconstruct_legacy_binding(row, controller)
lease = WriterLeaseRequestV2(**request)
worktree_path = self.config.state_root / "worktrees" / _safe(lease.task_id)
if not worktree_path.exists():
current = self.authority.read_ref("refs/heads/" + lease.target_branch)
if current is None:
raise RuntimeError("DURABLE_WORKTREE_BRANCH_MISSING")
self.authority.create_detached_worktree(
current,
_safe(lease.task_id),
self.config.state_root / "worktrees",
)
workspace = DetachedGitWorkspace(worktree_path)
credential = ServerSideCredentialProjection(self.config.ssh_key_path, self.config.token_path)
remote = GiteaContinuationRemote(
workspace=worktree_path,
credential=credential,
remote_name=self.config.remote_name,
repository=lease.repository,
api_base=self.config.api_base,
)
return ContinuationExecutor(
controller,
AuthorityGitAdapter(self.authority),
workspace,
remote,
credential,
)
def job(self, request: Mapping[str, Any], action: str) -> dict[str, Any]:
job_id = str(request["job_id"])
controller = self._controller_for_job(job_id)
row = controller.store.read(job_id)
if action == "get":
return controller.store._receipt(row, duplicate=True).as_dict()
if action == "resume":
return controller.resume(job_id).as_dict()
return self._executor_from_row(row, controller).reconcile(job_id)
def _snapshot_task(self) -> dict[str, Any]:
candidates: list[dict[str, Any]] = []
for value in self.bindings.values():
task_id = str(value.get("task_id") or "").strip()
branch = str(value.get("target_branch") or "").strip()
if not task_id or not branch.startswith("agent/"):
continue
authority = self.authority.read_ref("refs/heads/" + branch)
try:
generation = int(value.get("generation") or 0)
except (TypeError, ValueError):
generation = 0
reconciliation_state = "OBSERVATION_REQUIRED"
db = self.config.state_root / "jobs" / _safe(task_id) / "state.sqlite3"
if db.is_file():
connection = sqlite3.connect(f"file:{db}?mode=ro", uri=True)
try:
lease = connection.execute(
"SELECT MAX(generation) FROM branch_leases WHERE branch=?",
(branch,),
).fetchone()
generation = max(generation, int((lease or (0,))[0] or 0))
latest = connection.execute(
"SELECT state, side_effect_state FROM jobs WHERE branch=? ORDER BY updated_at DESC LIMIT 1",
(branch,),
).fetchone()
if latest is not None:
reconciliation_state = (
"COMPLETED"
if latest[0] == "COMPLETED"
else str(latest[1] or latest[0])
)
finally:
connection.close()
candidates.append(
{
"task_id": task_id,
"branch": branch,
"authority_branch_oid": authority,
"gitea_git_oid": None,
"gitea_api_oid": None,
"commit_visible": None,
"generation": generation,
"reconciliation_state": reconciliation_state,
}
)
if not candidates:
return {}
return max(
candidates,
key=lambda item: (int(item["generation"] or 0), bool(item["authority_branch_oid"])),
)
def snapshot(self) -> dict[str, Any]:
task = self._snapshot_task()
return build_control_plane_snapshot(target_version=self.config.target_version, runtime=self.runtime_status(), repository={"identity": self.config.repository, "authority_git_dir": str(self.config.authoritative_git_dir), "authority_main_oid": self.authority.read_ref("refs/heads/main")}, production={"baseline_version": "V2R1C7", "target_oid": "c7adece1cc8beffe7c718d53c775898b45d6c7f8", "deployment_state": "ACTIVE"}, task=task, capabilities={"legacy_writes_enabled": False, "governed_writes_enabled": True, "force_push": False, "main_write": False, "tag_write": False, "baseline_write": False, "production_write": False}, credential_broker={"status": "SERVER_SIDE_ONLY", "provider": "FileCredentialProvider"})
def handlers(self) -> dict[str, Any]:
return {
"createGovernedTask": self.create_task,
"acquireWriterLease": self.acquire_lease,
"commitFileGoverned": lambda request: self.commit_files({**request, "files": {request["path"]: request["content_base64"]}}),
"commitFilesGoverned": self.commit_files,
"getGitOpsJob": lambda request: self.job(request, "get"),
"resumeGitOpsJob": lambda request: self.job(request, "resume"),
"reconcileGitOpsJob": lambda request: self.job(request, "reconcile"),
"getControlPlaneSnapshot": lambda request: self.snapshot(),
"createTagApprovalRequest": self.release.approval_request,
"requestOrCreateTag": self.release.publish_tag,
"getTagPublicationStatus": self.release.tag_status,
"buildProductionArtifact": self.release.build_artifact,
"executeApprovedProductionRelease": self.release.execute_request,
"getReleaseConsistency": self.release.consistency,
"runReleaseAcceptance": self.release.acceptance,
}
def build_governed_handlers(runtime_config: RuntimeConfig | Mapping[str, Any]) -> Mapping[str, Any]:
if isinstance(runtime_config, Mapping): runtime_config = RuntimeConfig(**runtime_config)
return GovernedRuntime(runtime_config).handlers()
class _HTTPError(RuntimeError): pass
class BridgeHandler(BaseHTTPRequestHandler):
adapter: BridgeV2Adapter | None = None
def log_message(self, *_args: Any) -> None: return
def _json(self, value: Any, status: int = 200) -> None:
raw = json.dumps(value, sort_keys=True, separators=(",", ":")).encode(); self.send_response(status); self.send_header("Content-Type", "application/json"); self.send_header("Content-Length", str(len(raw))); self.end_headers(); self.wfile.write(raw)
def _body(self) -> dict[str, Any]:
try: length = int(self.headers.get("Content-Length", "-1"))
except ValueError as exc: raise _HTTPError("CONTENT_LENGTH_INVALID") from exc
if length < 0 or length > MAX_BODY: raise _HTTPError("REQUEST_TOO_LARGE")
value = json.loads(self.rfile.read(length).decode("utf-8"))
if not isinstance(value, dict): raise _HTTPError("JSON_OBJECT_REQUIRED")
return value
def _allowed(self, operation: str, body: Mapping[str, Any]) -> None:
allowed = set(OPERATION_REQUIRED_FIELDS[operation]) | {"schema", "lease_seconds", "create_target_branch", "worktree_mode", "cas_ref_update_required", "expected_branch_oid", "generation", "branch_mode"}
if operation == "commitFilesGoverned": allowed.add("files")
allowed.update({"owner_approval", "execute"})
if set(body) - allowed: raise _HTTPError("UNKNOWN_FIELDS")
def do_GET(self) -> None:
if self.adapter is None: self._json({"error": "RUNTIME_UNAVAILABLE"}, 503); return
if self.path == "/v2/runtime": self._json(self.adapter.getRuntimeStatus()); return
if self.path == "/v2/capabilities": self._json(self.adapter.getGovernedWriteCapabilities()); return
if self.path == "/v2/control-plane-snapshot": self._json(self.adapter.getControlPlaneSnapshot({})); return
self._json({"error": "NOT_FOUND"}, 404)
def do_POST(self) -> None:
operation = ROUTES.get(self.path)
if operation is None or operation in ("getRuntimeStatus", "getGovernedWriteCapabilities") or self.adapter is None: self._json({"error": "NOT_FOUND"}, 404); return
try:
body = self._body(); self._allowed(operation, body); self._json(getattr(self.adapter, operation)(body))
except _HTTPError as exc: self._json({"error": str(exc)}, 413 if str(exc) == "REQUEST_TOO_LARGE" else 400)
except (BootstrapError, ValueError, BridgeV2Error) as exc: self._json({"error": str(exc)}, 409 if "CONFLICT" in str(exc) else 400)
except Exception as exc: self._json({"error": str(exc) or type(exc).__name__}, 409 if any(token in str(exc) for token in ("CONFLICT", "EXPECTATION", "MONOTONIC")) else 503)
def serve(adapter: BridgeV2Adapter, host: str = "127.0.0.1", port: int = 18892) -> None:
BridgeHandler.adapter = adapter; ThreadingHTTPServer((host, port), BridgeHandler).serve_forever()
def main() -> None:
state_root = Path("/var/lib/aios-gitops/bridge-v2-staging")
token_path = Path(os.environ.get("AIOS_GITEA_TOKEN_FILE", "/run/credentials/aios-chatgpt-bridge-v2-staging.service/gitea_token"))
ssh_key_path = Path(os.environ.get("AIOS_GITEA_SSH_KEY_PATH", "/home/aiman/.ssh/id_ed25519_codex_tmp"))
os.environ["CREDENTIAL_FILE"] = str(token_path)
os.environ.setdefault("GITEA_MACHINE_ACTOR", "aios-autonomy-agent")
config = RuntimeConfig(
os.environ.get("AIOS_CONTROL_PLANE_TARGET_OID", ""),
Path("/data/aios-gitops/repo.git"),
state_root,
state_root / "jobs",
token_path,
ssh_key_path,
remote_name=os.environ.get("AIOS_GITEA_REMOTE", "gitea-http"),
api_base=os.environ.get("AIOS_GITEA_API_BASE", "https://g.cnnetmkt.top/git/api/v1"),
target_version=os.environ.get("AIOS_CONTROL_PLANE_TARGET_VERSION", "V2R2C1"),
)
runtime = GovernedRuntime(config)
serve(
BridgeV2Adapter(runtime_status=runtime.runtime_status(), handlers=runtime.handlers()),
port=int(os.environ.get("AIOS_BRIDGE_V2_PORT", "18892")),
)
if __name__ == "__main__": main()
@@ -0,0 +1,594 @@
"""Governed continuation executor for exact existing task branches.
The executor composes the existing continuation lease and durable Job store.
All Git and Gitea side effects live behind injected narrow interfaces so the
state machine can prove Authority, Git-network, API-branch, and commit-object
agreement before it reports completion.
"""
from __future__ import annotations
import base64
from dataclasses import asdict, dataclass
from datetime import datetime, timezone
import hashlib
import json
import os
from pathlib import Path, PurePosixPath
import re
import subprocess
from typing import Callable, Mapping, Protocol
from urllib.error import HTTPError, URLError
from urllib.parse import quote, urlsplit
from urllib.request import Request, urlopen
from aios_authority_git_executor import AuthorityGitExecutor
from aios_continuation_lease import WriterLeaseRequestV2
from aios_gitops_job_controller import GitOpsJobController, JobState
from aios_gitops_reconciliation_core import ReconciliationAction, RemoteObservation, decide_reconciliation
from authorized_task_writer_provisioning import validate_writer_lease_request_v2
OID_RE = re.compile(r"^[0-9a-f]{40}$")
SECRET_PATTERNS = (
re.compile(rb"gh[pousr]_[A-Za-z0-9]{20,}"),
re.compile(rb"(?i)(?:token|password|secret|api[_-]?key)\s*[:=]\s*[\"'][^\"'\s]{12,}[\"']"),
re.compile(rb"-----BEGIN " + rb"(?:RSA |EC |OPENSSH )?PRIVATE KEY-----"),
)
class ContinuationExecutionError(RuntimeError):
"""Stable fail-closed continuation execution error."""
class DetachedWorkspace(Protocol):
def is_detached(self) -> bool: ...
def head_oid(self) -> str: ...
def commit_files(self, files: Mapping[str, bytes], message: str) -> str: ...
class AuthorityPort(Protocol):
def branch_exists(self, branch: str) -> bool: ...
def branch_head(self, branch: str) -> str: ...
def is_ancestor(self, older: str, newer: str) -> bool: ...
def compare_and_swap_branch(self, branch: str, expected: str, new: str) -> str: ...
class RemotePort(Protocol):
def repository_identity(self) -> str: ...
def push_exact(self, branch: str, expected: str, new: str) -> None: ...
def read_git_ref(self, branch: str) -> str | None: ...
def read_api_branch(self, branch: str) -> str | None: ...
def commit_visible(self, oid: str) -> bool: ...
class CredentialProjection(Protocol):
def project(self) -> None: ...
def revoke(self) -> None: ...
def _run(argv: list[str], *, cwd: Path | None = None, environment: Mapping[str, str] | None = None) -> str:
result = subprocess.run(
argv,
cwd=cwd,
env=None if environment is None else dict(environment),
text=True,
capture_output=True,
check=False,
)
if result.returncode != 0:
raise ContinuationExecutionError("SUBPROCESS_FAILED")
return result.stdout.strip()
@dataclass(slots=True)
class AuthorityGitAdapter:
"""Branch-name adapter over the existing narrow AuthorityGitExecutor."""
executor: AuthorityGitExecutor
@staticmethod
def _ref(branch: str) -> str:
return f"refs/heads/{branch}"
def branch_exists(self, branch: str) -> bool:
return self.executor.read_ref(self._ref(branch)) is not None
def branch_head(self, branch: str) -> str:
value = self.executor.read_ref(self._ref(branch))
if value is None:
raise ContinuationExecutionError("EXISTING_BRANCH_MISSING")
return value
def is_ancestor(self, older: str, newer: str) -> bool:
return self.executor.ancestry_check(older, newer)
def compare_and_swap_branch(self, branch: str, expected: str, new: str) -> str:
return self.executor.advance_task_branch_CAS(self._ref(branch), expected, new)
@dataclass(slots=True)
class DetachedGitWorkspace:
path: Path
def __post_init__(self) -> None:
if not self.path.is_absolute() or self.path.is_symlink() or not self.path.is_dir():
raise ContinuationExecutionError("WORKTREE_PATH_INVALID")
top = Path(_run(["git", "rev-parse", "--show-toplevel"], cwd=self.path)).resolve()
if top != self.path.resolve():
raise ContinuationExecutionError("WORKTREE_PATH_INVALID")
def is_detached(self) -> bool:
result = subprocess.run(
["git", "symbolic-ref", "-q", "HEAD"],
cwd=self.path,
capture_output=True,
check=False,
)
return result.returncode == 1
def head_oid(self) -> str:
return _run(["git", "rev-parse", "HEAD"], cwd=self.path)
def _dirty_paths(self) -> set[str]:
command = subprocess.run(
["git", "status", "--porcelain=v1", "--untracked-files=all"],
cwd=self.path,
text=True,
capture_output=True,
check=False,
)
if command.returncode != 0:
raise ContinuationExecutionError("SUBPROCESS_FAILED")
output = command.stdout
result: set[str] = set()
for line in output.splitlines():
path = line[3:]
if " -> " in path:
path = path.split(" -> ", 1)[1]
result.add(path)
return result
def materialize_files(self, files: Mapping[str, bytes]) -> None:
dirty = self._dirty_paths()
expected_paths = set(files)
if dirty and dirty != expected_paths:
raise ContinuationExecutionError("WORKTREE_DIRTY_SCOPE_MISMATCH")
for relative, content in files.items():
target = (self.path / relative).resolve()
try:
target.relative_to(self.path.resolve())
except ValueError as exc:
raise ContinuationExecutionError("COMMIT_PATH_REJECTED") from exc
if target.is_symlink():
raise ContinuationExecutionError("COMMIT_PATH_REJECTED")
target.parent.mkdir(parents=True, exist_ok=True)
target.write_bytes(content)
def commit_files(self, files: Mapping[str, bytes], message: str) -> str:
expected_paths = set(files)
if self._dirty_paths() != expected_paths:
raise ContinuationExecutionError("WORKTREE_DIRTY_SCOPE_MISMATCH")
for relative, content in files.items():
target = (self.path / relative).resolve()
try:
target.relative_to(self.path.resolve())
except ValueError as exc:
raise ContinuationExecutionError("COMMIT_PATH_REJECTED") from exc
if not target.is_file() or target.is_symlink() or target.read_bytes() != content:
raise ContinuationExecutionError("AUTHORING_PAYLOAD_MISMATCH")
_run(["git", "add", "--", *sorted(expected_paths)], cwd=self.path)
staged = set(_run(["git", "diff", "--cached", "--name-only"], cwd=self.path).splitlines())
if staged != expected_paths:
raise ContinuationExecutionError("STAGED_SCOPE_MISMATCH")
environment = os.environ.copy()
environment.update(
{
"GIT_AUTHOR_NAME": "AIOS Governed Continuation",
"GIT_AUTHOR_EMAIL": "aios-governed-continuation@localhost",
"GIT_COMMITTER_NAME": "AIOS Governed Continuation",
"GIT_COMMITTER_EMAIL": "aios-governed-continuation@localhost",
}
)
_run(["git", "commit", "--no-gpg-sign", "-m", message, "--", *sorted(expected_paths)], cwd=self.path, environment=environment)
if not self.is_detached():
raise ContinuationExecutionError("WORKTREE_NOT_DETACHED")
return self.head_oid()
@dataclass(slots=True)
class ServerSideCredentialProjection:
ssh_key_path: Path
api_token_path: Path
active: bool = False
def project(self) -> None:
for path in (self.ssh_key_path, self.api_token_path):
if path.is_symlink() or not path.is_file() or path.stat().st_size < 32:
raise ContinuationExecutionError("CREDENTIAL_PROJECTION_MISSING")
self.active = True
def revoke(self) -> None:
self.active = False
@dataclass(slots=True)
class GiteaContinuationRemote:
workspace: Path
credential: ServerSideCredentialProjection
remote_name: str = "gitea"
repository: str = "aios/aios"
api_base: str = "https://g.cnnetmkt.top/git/api/v1"
expected_host: str = "g.cnnetmkt.top"
@staticmethod
def validate_remote_url(value: str, expected_host: str = "g.cnnetmkt.top") -> None:
if value.startswith(("/", "file:")) or value.endswith("/data/aios-gitops/repo.git"):
raise ContinuationExecutionError("GITEA_REMOTE_IDENTITY_INVALID")
if "://" in value:
parsed = urlsplit(value)
host, path = parsed.hostname, parsed.path
else:
location = value.split("@", 1)[-1]
host, separator, path = location.partition(":")
if not separator:
raise ContinuationExecutionError("GITEA_REMOTE_IDENTITY_INVALID")
path = "/" + path
if host != expected_host or path.rstrip("/") not in {"/aios/aios.git", "/git/aios/aios.git"}:
raise ContinuationExecutionError("GITEA_REMOTE_IDENTITY_INVALID")
def repository_identity(self) -> str:
url = _run(["git", "remote", "get-url", self.remote_name], cwd=self.workspace)
self.validate_remote_url(url, self.expected_host)
if self.repository != "aios/aios":
raise ContinuationExecutionError("REMOTE_REPOSITORY_IDENTITY_MISMATCH")
return self.repository
def _require_projected(self) -> None:
if not self.credential.active:
raise ContinuationExecutionError("CREDENTIAL_PROJECTION_MISSING")
def read_git_ref(self, branch: str) -> str | None:
self._require_projected()
output = _run(["git", "ls-remote", self.remote_name, f"refs/heads/{branch}"], cwd=self.workspace)
if not output:
return None
oid, ref = output.split(maxsplit=1)
if ref != f"refs/heads/{branch}" or not OID_RE.fullmatch(oid):
raise ContinuationExecutionError("GITEA_GIT_READBACK_INVALID")
return oid
def push_exact(self, branch: str, expected: str, new: str) -> None:
self._require_projected()
self.repository_identity()
if self.read_git_ref(branch) != expected:
raise ContinuationExecutionError("GITEA_BRANCH_EXPECTATION_MISMATCH")
_run(["git", "push", self.remote_name, f"{new}:refs/heads/{branch}"], cwd=self.workspace)
def _api(self, path: str) -> object:
self._require_projected()
token = self.credential.api_token_path.read_text(encoding="utf-8").strip()
request = Request(
self.api_base.rstrip("/") + path,
headers={"Accept": "application/json", "Authorization": f"token {token}"},
)
try:
with urlopen(request, timeout=20) as response:
return json.loads(response.read().decode("utf-8"))
except HTTPError as exc:
if exc.code == 404:
return None
raise ContinuationExecutionError(f"GITEA_API_HTTP_{exc.code}") from exc
except (URLError, TimeoutError, OSError, json.JSONDecodeError) as exc:
raise ContinuationExecutionError("GITEA_API_UNAVAILABLE") from exc
def read_api_branch(self, branch: str) -> str | None:
owner, name = self.repository.split("/", 1)
page = 1
while page <= 100:
value = self._api(f"/repos/{quote(owner)}/{quote(name)}/branches?page={page}&limit=50")
if value is None:
return None
if not isinstance(value, list):
raise ContinuationExecutionError("GITEA_API_BRANCH_COLLECTION_INVALID")
for item in value:
if not isinstance(item, dict) or item.get("name") != branch:
continue
oid = item.get("commit", {}).get("id")
if not isinstance(oid, str) or not OID_RE.fullmatch(oid):
raise ContinuationExecutionError("GITEA_API_BRANCH_INVALID")
return oid
if len(value) < 50:
return None
page += 1
raise ContinuationExecutionError("GITEA_API_BRANCH_PAGINATION_LIMIT")
def commit_visible(self, oid: str) -> bool:
owner, name = self.repository.split("/", 1)
value = self._api(f"/repos/{quote(owner)}/{quote(name)}/git/commits/{oid}")
if not isinstance(value, dict):
return False
observed = value.get("sha") or value.get("id")
return observed == oid
@dataclass(frozen=True, slots=True)
class ContinuationPermissions:
create_target_branch: bool = False
force_push: bool = False
tag_write: bool = False
baseline_write: bool = False
production_write: bool = False
main_write: bool = False
def validate(self) -> None:
if any(asdict(self).values()):
raise ContinuationExecutionError("MUTATION_PERMISSION_DENIED")
@dataclass(frozen=True, slots=True)
class ContinuationRuntimeBinding:
request: WriterLeaseRequestV2
workspace_path: Path
lease_not_before: datetime
lease_expires_at: datetime
original_receipt_valid: bool
original_grant_consumed: bool
permissions: ContinuationPermissions
@dataclass(frozen=True, slots=True)
class ContinuationCommit:
idempotency_key: str
files: Mapping[str, bytes]
message: str
def payload(self) -> bytes:
if not self.idempotency_key or not self.message.strip() or not self.files:
raise ContinuationExecutionError("COMMIT_REQUEST_INVALID")
normalized: dict[str, str] = {}
for path, content in self.files.items():
pure = PurePosixPath(path)
if (
not isinstance(path, str)
or pure.is_absolute()
or ".." in pure.parts
or path.startswith((".git/", "baseline/", "production/"))
or not isinstance(content, bytes)
):
raise ContinuationExecutionError("COMMIT_PATH_REJECTED")
if any(pattern.search(content) for pattern in SECRET_PATTERNS):
raise ContinuationExecutionError("SECRET_LIKE_PAYLOAD_REJECTED")
normalized[path] = base64.b64encode(content).decode("ascii")
return json.dumps(
{"files_base64": normalized, "message": self.message},
sort_keys=True,
separators=(",", ":"),
).encode("utf-8")
class ContinuationExecutor:
def __init__(
self,
controller: GitOpsJobController,
authority: AuthorityPort,
workspace: DetachedWorkspace,
remote: RemotePort,
credential: CredentialProjection,
*,
now: Callable[[], datetime] = lambda: datetime.now(timezone.utc),
) -> None:
self.controller = controller
self.authority = authority
self.workspace = workspace
self.remote = remote
self.credential = credential
self.now = now
self._observed_generations: dict[str, int] = {}
def observe_generation(self, branch: str, generation: int) -> None:
self._observed_generations[branch] = max(generation, self._observed_generations.get(branch, 0))
def _max_generation(self, branch: str) -> int:
observed = self._observed_generations.get(branch, 0)
with self.controller.store._connect() as connection:
row = connection.execute(
"SELECT MAX(generation) AS generation FROM branch_leases WHERE branch=?",
(branch,),
).fetchone()
durable = int(row["generation"] or 0)
return max(observed, durable)
@staticmethod
def _request_mapping(request: WriterLeaseRequestV2) -> dict[str, object]:
return {
"schema": WriterLeaseRequestV2.schema,
"task_id": request.task_id,
"repository": request.repository,
"branch_mode": request.branch_mode,
"base_oid": request.base_oid,
"expected_branch_oid": request.expected_branch_oid,
"target_branch": request.target_branch,
"generation": request.generation,
"lease_seconds": request.lease_seconds,
}
def _validate_preflight(self, binding: ContinuationRuntimeBinding) -> None:
request = binding.request
normalized = validate_writer_lease_request_v2(self._request_mapping(request), self.authority)
if normalized != {**self._request_mapping(request), "create_target_branch": False, "worktree_mode": "detached_at_expected_head", "cas_ref_update_required": True}:
raise ContinuationExecutionError("CONTINUATION_NORMALIZATION_INVALID")
if not binding.original_receipt_valid or not binding.original_grant_consumed:
raise ContinuationExecutionError("ORIGINAL_GRANT_LINEAGE_INVALID")
binding.permissions.validate()
now = self.now()
if now < binding.lease_not_before:
raise ContinuationExecutionError("LEASE_NOT_ACTIVE")
if now >= binding.lease_expires_at:
raise ContinuationExecutionError("LEASE_EXPIRED")
if request.generation <= self._max_generation(request.target_branch):
raise ContinuationExecutionError("GENERATION_NOT_MONOTONIC")
if not self.workspace.is_detached():
raise ContinuationExecutionError("WORKTREE_NOT_DETACHED")
if self.workspace.head_oid() != request.expected_branch_oid:
raise ContinuationExecutionError("WORKTREE_HEAD_MISMATCH")
if self.remote.repository_identity() != request.repository:
raise ContinuationExecutionError("REMOTE_REPOSITORY_IDENTITY_MISMATCH")
def _job_request(self, binding: ContinuationRuntimeBinding, commit: ContinuationCommit, payload: bytes) -> dict[str, object]:
request = binding.request
return {
"idempotency_key": commit.idempotency_key,
"campaign_id": request.task_id,
"task_id": request.task_id,
"repository": request.repository,
"operation": "CONTINUATION_COMMIT_FILES",
"branch": request.target_branch,
"expected_branch_oid": request.expected_branch_oid,
"base_oid": request.base_oid,
"branch_mode": request.branch_mode,
"payload_digest": hashlib.sha256(payload).hexdigest(),
"lineage_anchor_oid": request.base_oid,
"generation": request.generation,
}
def execute(self, binding: ContinuationRuntimeBinding, commit: ContinuationCommit) -> dict[str, object]:
payload = commit.payload()
submitted = self.controller.submit(self._job_request(binding, commit, payload), payload=payload)
current = self.controller.store.read(submitted.job_id)
if JobState(current["state"]) == JobState.COMPLETED:
return self.controller.store._receipt(current, duplicate=True).as_dict()
self._validate_preflight(binding)
request = binding.request
projected = False
try:
self.credential.project()
projected = True
row = self.controller.store.read(submitted.job_id)
if JobState(row["state"]) == JobState.REQUESTED:
self.controller.advance(
submitted.job_id,
JobState.AUTHORIZED,
side_effect_state="AUTHORIZED",
receipt={"task_id": request.task_id, "original_grant_consumed": True},
)
row = self.controller.store.read(submitted.job_id)
if JobState(row["state"]) == JobState.AUTHORIZED:
self.controller.lease_job(
submitted.job_id,
lease_id=f"lease-{submitted.job_id}",
lineage_anchor_oid=request.base_oid,
current_head_oid=request.expected_branch_oid,
generation=request.generation,
lease_expires_at=binding.lease_expires_at.astimezone(timezone.utc).isoformat(),
expected_revision=int(row["revision"]),
)
self.controller.advance(submitted.job_id, JobState.WORKTREE_READY, side_effect_state="WORKTREE_READY")
self.controller.advance(submitted.job_id, JobState.EXECUTING, side_effect_state="IN_PROGRESS")
new_oid = self.workspace.commit_files(commit.files, commit.message)
if not OID_RE.fullmatch(new_oid) or not self.authority.is_ancestor(request.expected_branch_oid, new_oid):
raise ContinuationExecutionError("NON_FAST_FORWARD_COMMIT")
authority_oid = self.authority.compare_and_swap_branch(request.target_branch, request.expected_branch_oid, new_oid)
if authority_oid != new_oid:
raise ContinuationExecutionError("AUTHORITY_CAS_READBACK_MISMATCH")
self.controller.advance(
submitted.job_id,
JobState.LOCAL_VERIFIED,
side_effect_state="LOCAL_VERIFIED",
receipt={"authority_oid": authority_oid, "force_push": False},
)
try:
self.controller.advance(submitted.job_id, JobState.REMOTE_SYNCING, side_effect_state="IN_PROGRESS")
self.remote.push_exact(request.target_branch, request.expected_branch_oid, new_oid)
except Exception:
return self._remote_pending(submitted.job_id, authority_oid, new_oid)
return self._finish_remote_verification(submitted.job_id, authority_oid, new_oid)
finally:
if projected:
self.credential.revoke()
def _observe_remote(self, branch: str, oid: str) -> tuple[str | None, str | None, bool]:
return self.remote.read_git_ref(branch), self.remote.read_api_branch(branch), self.remote.commit_visible(oid)
def _remote_pending(self, job_id: str, authority_oid: str, new_oid: str) -> dict[str, object]:
receipt = {
"authority_oid": authority_oid,
"desired_remote_oid": new_oid,
"remote_verified": False,
"force_push": False,
}
result = self.controller.advance(
job_id,
JobState.RECONCILIATION_REQUIRED,
side_effect_state="REMOTE_PUSH_PENDING",
receipt=receipt,
)
return result.as_dict()
def _finish_remote_verification(self, job_id: str, authority_oid: str, new_oid: str) -> dict[str, object]:
row = self.controller.store.read(job_id)
branch = row["branch"]
request = json.loads(row["request_json"])["binding"]
git_oid, api_oid, visible = self._observe_remote(branch, new_oid)
decision = decide_reconciliation(RemoteObservation("aios/aios", branch, new_oid, authority_oid, git_oid, api_oid, visible, datetime.now(timezone.utc).isoformat(), request.get("expected_branch_oid")))
if decision.action is not ReconciliationAction.MARK_REMOTE_VERIFIED:
return self._remote_pending(job_id, authority_oid, new_oid)
receipt = {
"authority_oid": authority_oid,
"git_remote_oid": git_oid,
"api_branch_oid": api_oid,
"commit_visible": True,
"remote_verified": True,
"reconciliation_decision": decision.action.value,
"force_push": False,
}
self.controller.advance(job_id, JobState.REMOTE_VERIFIED, side_effect_state="REMOTE_VERIFIED", receipt=receipt)
return self.controller.advance(job_id, JobState.COMPLETED, side_effect_state="REMOTE_VERIFIED", receipt=receipt).as_dict()
def reconcile(self, job_id: str) -> dict[str, object]:
row = self.controller.store.read(job_id)
state = JobState(row["state"])
if state == JobState.COMPLETED:
return self.controller.store._receipt(row, duplicate=True).as_dict()
if state != JobState.RECONCILIATION_REQUIRED:
raise ContinuationExecutionError("JOB_NOT_RECONCILABLE")
request = json.loads(row["request_json"])["binding"]
receipt = json.loads(row["receipt_json"])
new_oid = receipt["desired_remote_oid"]
authority_oid = self.authority.branch_head(row["branch"])
try:
generation = int(request.get("generation") or 0)
except (TypeError, ValueError) as exc:
raise ContinuationExecutionError("DURABLE_BINDING_GENERATION_INVALID") from exc
if generation > 0:
with self.controller.store._connect() as connection:
earlier = connection.execute(
"SELECT 1 FROM jobs WHERE branch=? AND state=? "
"AND CAST(json_extract(request_json, '$.binding.generation') AS INTEGER) < ? LIMIT 1",
(row["branch"], JobState.RECONCILIATION_REQUIRED.value, generation),
).fetchone()
if earlier is not None:
raise ContinuationExecutionError("EARLIER_RECONCILIATION_REQUIRED")
self.credential.project()
try:
git_oid, api_oid, visible = self._observe_remote(row["branch"], new_oid)
if authority_oid == git_oid == api_oid == new_oid and visible:
self.controller.advance(job_id, JobState.RECONCILING, side_effect_state="OBSERVATION_FIRST")
return self._finish_remote_verification(job_id, new_oid, new_oid)
expected_oid = request["expected_branch_oid"]
if (
self.authority.is_ancestor(new_oid, authority_oid)
and git_oid is not None
and git_oid == api_oid
and self.authority.is_ancestor(git_oid, new_oid)
):
self.controller.advance(job_id, JobState.RECONCILING, side_effect_state="OBSERVATION_FIRST_CHAIN")
self.remote.push_exact(row["branch"], git_oid, new_oid)
return self._finish_remote_verification(job_id, new_oid, new_oid)
if authority_oid == new_oid and git_oid == api_oid == expected_oid:
self.controller.advance(job_id, JobState.RECONCILING, side_effect_state="OBSERVATION_FIRST_RETRY")
self.remote.push_exact(row["branch"], expected_oid, new_oid)
return self._finish_remote_verification(job_id, new_oid, new_oid)
return self.controller.store._receipt(row, duplicate=True).as_dict()
finally:
self.credential.revoke()
@@ -0,0 +1,154 @@
"""Campaign-aware continuation leases for an existing Git branch.
This module is deliberately transport-neutral. It validates branch lineage
and hands ref mutation to an injected compare-and-swap broker; it does not run
Git, inspect credentials, or create a branch itself.
"""
from __future__ import annotations
from dataclasses import dataclass, replace
from datetime import datetime, timedelta, timezone
import re
from typing import Protocol
OID_RE = re.compile(r"^[0-9a-f]{40}$")
TARGET_BRANCH = "agent/autonomous-release-campaign-v1-20260807"
LINEAGE_ANCHOR = "69b45e81dcd2c3707c367003b08c55fd361faae8"
CAMPAIGN_HEAD = "bba675fab3b7e0c5fba7066720252b3c91b9f20d"
class ContinuationError(RuntimeError):
"""Stable fail-closed error for lease or branch binding violations."""
class BranchProbe(Protocol):
def branch_exists(self, branch: str) -> bool: ...
def branch_head(self, branch: str) -> str: ...
def is_ancestor(self, older: str, newer: str) -> bool: ...
class RefCAS(Protocol):
def compare_and_swap_branch(self, branch: str, expected: str, new: str) -> str: ...
def _oid(value: str, field: str) -> str:
if not isinstance(value, str) or not OID_RE.fullmatch(value):
raise ContinuationError(f"{field}_invalid")
return value
@dataclass(frozen=True, slots=True)
class WriterLeaseRequestV2:
task_id: str
repository: str
branch_mode: str
base_oid: str
expected_branch_oid: str
target_branch: str
generation: int
lease_seconds: int = 900
schema = "aios.task-writer-lease.v2"
accepted_schemas = frozenset({"aios.task-writer-lease.v2", "aios.task-writer-lease-request.v2"})
def validate(self, probe: BranchProbe) -> None:
if not self.task_id or self.repository != "aios/aios":
raise ContinuationError("request_identity_invalid")
if self.branch_mode not in {"CREATE_NEW", "CONTINUE_EXISTING"}:
raise ContinuationError("branch_mode_invalid")
_oid(self.base_oid, "base_oid")
_oid(self.expected_branch_oid, "expected_branch_oid")
if self.target_branch in {"main", "master"} or self.target_branch.startswith(("refs/", "tags/")):
raise ContinuationError("protected_branch_rejected")
if not isinstance(self.generation, int) or isinstance(self.generation, bool) or self.generation < 1:
raise ContinuationError("generation_invalid")
if not isinstance(self.lease_seconds, int) or not 1 <= self.lease_seconds <= 86_400:
raise ContinuationError("lease_duration_invalid")
if self.branch_mode == "CONTINUE_EXISTING":
if not probe.branch_exists(self.target_branch):
raise ContinuationError("EXISTING_BRANCH_MISSING")
actual = _oid(probe.branch_head(self.target_branch), "actual_branch_oid")
if actual != self.expected_branch_oid:
raise ContinuationError("EXISTING_BRANCH_OID_CONFLICT")
if not probe.is_ancestor(self.base_oid, actual):
raise ContinuationError("BASE_NOT_ANCESTOR_OF_BRANCH")
elif hasattr(probe, "commit_exists") and not probe.commit_exists(self.base_oid):
raise ContinuationError("BASE_COMMIT_NOT_FOUND")
elif self.branch_mode == "CREATE_NEW" and probe.branch_exists(self.target_branch):
raise ContinuationError("TARGET_BRANCH_ALREADY_EXISTS")
elif self.branch_mode == "CREATE_NEW" and self.base_oid != self.expected_branch_oid:
raise ContinuationError("CREATE_NEW_BASE_EXPECTATION_MISMATCH")
@dataclass(frozen=True, slots=True)
class CampaignWriterLease:
task_id: str
repository: str
branch: str
lineage_anchor_oid: str
current_head_oid: str
generation: int
lease_expires_at: datetime
append_only: bool = True
renewable: bool = True
multi_commit: bool = True
recoverable: bool = True
state: str = "ACTIVE"
branch_bound: bool = True
def __post_init__(self) -> None:
_oid(self.lineage_anchor_oid, "lineage_anchor_oid")
_oid(self.current_head_oid, "current_head_oid")
if not all((self.append_only, self.renewable, self.multi_commit, self.recoverable, self.branch_bound)):
raise ContinuationError("lease_capability_downgrade")
class ContinuationLeaseManager:
"""State machine for a multi-commit lease with CAS-only ref updates."""
def __init__(self, probe: BranchProbe, refs: RefCAS, *, now=datetime.now) -> None:
self.probe = probe
self.refs = refs
self.now = now
def open(self, request: WriterLeaseRequestV2) -> CampaignWriterLease:
request.validate(self.probe)
if request.branch_mode != "CONTINUE_EXISTING":
raise ContinuationError("CREATE_NEW_REQUIRES_SEPARATE_PROVISIONER")
return CampaignWriterLease(
task_id=request.task_id,
repository=request.repository,
branch=request.target_branch,
lineage_anchor_oid=request.base_oid,
current_head_oid=request.expected_branch_oid,
generation=request.generation,
lease_expires_at=self.now(timezone.utc) + timedelta(seconds=request.lease_seconds),
)
def renew(self, lease: CampaignWriterLease, seconds: int) -> CampaignWriterLease:
if lease.state != "ACTIVE" or not lease.renewable or seconds < 1:
raise ContinuationError("lease_not_renewable")
return replace(lease, lease_expires_at=self.now(timezone.utc) + timedelta(seconds=seconds))
def append_commit(self, lease: CampaignWriterLease, new_head_oid: str) -> CampaignWriterLease:
if lease.state != "ACTIVE":
raise ContinuationError("lease_not_active")
_oid(new_head_oid, "new_head_oid")
if not self.probe.is_ancestor(lease.current_head_oid, new_head_oid):
raise ContinuationError("NON_FAST_FORWARD_COMMIT")
try:
actual = self.refs.compare_and_swap_branch(
lease.branch, lease.current_head_oid, new_head_oid
)
except Exception as exc:
raise ContinuationError("BRANCH_LEASE_STALE") from exc
if actual != new_head_oid:
raise ContinuationError("BRANCH_LEASE_STALE")
return replace(lease, current_head_oid=new_head_oid)
def supersede(self, lease: CampaignWriterLease) -> CampaignWriterLease:
if lease.state not in {"ACTIVE", "RECOVERABLE_FAILURE", "RECONCILIATION_REQUIRED"}:
raise ContinuationError("lease_not_supersedable")
return replace(lease, generation=lease.generation + 1, state="SUPERSEDED")
@@ -0,0 +1,748 @@
#!/usr/bin/env python3
"""Server-local governed immutable Host seed executor.
This executor is deliberately not a general-purpose privileged command runner.
It polls one fixed Git ref/path, verifies a high-risk governed-task binding as
the durable authorization evidence, materializes one exact candidate from the
authoritative Git object database plus a declared patch/manifest, and atomically
publishes the immutable release tree.
It never starts/restarts services, never creates the continuation Task/Job,
never writes Git refs, and never switches Production.
"""
from __future__ import annotations
from dataclasses import dataclass
from datetime import datetime, timezone
import argparse
import fcntl
import hashlib
import json
import os
import pwd
from pathlib import Path, PurePosixPath
import re
import shutil
import stat
import subprocess
import tempfile
from typing import Any, Mapping
REQUEST_SCHEMA = "aios.control-plane-host-seed-request.v1"
MANIFEST_SCHEMAS = {
"aios.control-plane-immutable-candidate.v5",
"aios.control-plane-immutable-candidate.v6",
}
OPERATION = "HOST_SEED_IMMUTABLE_CANDIDATE"
REPOSITORY = "aios/aios"
REQUEST_REF = "refs/heads/agent/host-seed-authorization-requests"
REQUEST_PATH = "control-plane/host-seed/request.json"
OID_RE = re.compile(r"^[0-9a-f]{40}$")
SHA_RE = re.compile(r"^[0-9a-f]{64}$")
TASK_RE = re.compile(r"^[A-Za-z0-9][A-Za-z0-9._:-]{2,179}$")
RELEASE_RE = re.compile(r"^[A-Za-z0-9][A-Za-z0-9._-]{3,191}$")
CANDIDATE_RE = re.compile(r"^V[0-9]+$")
MAX_REQUEST_BYTES = 128 * 1024
MAX_MANIFEST_BYTES = 4 * 1024 * 1024
MAX_PATCH_BYTES = 2 * 1024 * 1024
MAX_MACHINE_GOAL_BYTES = 512 * 1024
MAX_BOOTSTRAP_BYTES = 512 * 1024
class HostSeedError(RuntimeError):
pass
def canonical(value: object) -> bytes:
return (json.dumps(value, ensure_ascii=False, sort_keys=True, separators=(",", ":")) + "\n").encode("utf-8")
def sha256(data: bytes) -> str:
return hashlib.sha256(data).hexdigest()
def canonical_compact(value: object) -> bytes:
return json.dumps(
value, ensure_ascii=False, sort_keys=True, separators=(",", ":")
).encode("utf-8")
def _git(git_dir: Path, *args: str, input_bytes: bytes | None = None) -> bytes:
env = {"PATH": os.environ.get("PATH", "")}
proc = subprocess.run(
["git", f"--git-dir={git_dir}", *args],
input=input_bytes,
capture_output=True,
timeout=120,
env=env,
)
if proc.returncode:
raise HostSeedError("GIT_" + (args[0] if args else "COMMAND").upper() + "_FAILED")
return proc.stdout
def _git_text(git_dir: Path, *args: str) -> str:
return _git(git_dir, *args).decode("utf-8", "strict").strip()
def _safe_repo_path(value: object, *, field: str) -> str:
if not isinstance(value, str) or not value or len(value) > 512:
raise HostSeedError(field + "_INVALID")
p = PurePosixPath(value)
if p.is_absolute() or any(part in {"", ".", ".."} for part in p.parts):
raise HostSeedError(field + "_INVALID")
normalized = p.as_posix()
if normalized.startswith(".git/") or normalized == ".git":
raise HostSeedError(field + "_INVALID")
return normalized
def _read_git_blob(git_dir: Path, oid: str, path: str, *, maximum: int) -> bytes:
if not OID_RE.fullmatch(oid):
raise HostSeedError("PAYLOAD_COMMIT_INVALID")
raw = _git(git_dir, "show", f"{oid}:{path}")
if len(raw) <= 0 or len(raw) > maximum:
raise HostSeedError("GIT_BLOB_SIZE_INVALID")
return raw
def _parse_json(raw: bytes, *, code: str) -> dict[str, Any]:
try:
value = json.loads(raw.decode("utf-8"))
except (UnicodeDecodeError, json.JSONDecodeError) as exc:
raise HostSeedError(code) from exc
if not isinstance(value, dict):
raise HostSeedError(code)
return value
def _parse_time(value: object, *, field: str) -> datetime:
if not isinstance(value, str) or not value:
raise HostSeedError(field + "_INVALID")
text = value[:-1] + "+00:00" if value.endswith("Z") else value
try:
parsed = datetime.fromisoformat(text)
except ValueError as exc:
raise HostSeedError(field + "_INVALID") from exc
if parsed.tzinfo is None:
raise HostSeedError(field + "_INVALID")
return parsed.astimezone(timezone.utc)
def _regular_file_bytes(
path: Path, *, maximum: int, expected_uid: int | None = None
) -> bytes:
if path.parent.is_symlink():
raise HostSeedError("AUTHORIZATION_BINDING_PARENT_SYMLINK_REJECTED")
flags = os.O_RDONLY | getattr(os, "O_CLOEXEC", 0) | getattr(os, "O_NOFOLLOW", 0)
try:
fd = os.open(path, flags)
except OSError as exc:
raise HostSeedError("AUTHORIZATION_BINDING_UNAVAILABLE") from exc
try:
info = os.fstat(fd)
if not stat.S_ISREG(info.st_mode) or info.st_size <= 0 or info.st_size > maximum:
raise HostSeedError("AUTHORIZATION_BINDING_INVALID")
if info.st_mode & 0o022:
raise HostSeedError("AUTHORIZATION_BINDING_WRITABLE_BY_GROUP_OR_OTHER")
if expected_uid is not None and info.st_uid != expected_uid:
raise HostSeedError("AUTHORIZATION_BINDING_OWNER_MISMATCH")
raw = os.read(fd, maximum + 1)
if len(raw) > maximum:
raise HostSeedError("AUTHORIZATION_BINDING_INVALID")
return raw
finally:
os.close(fd)
def _atomic_json(path: Path, value: Mapping[str, Any], *, mode: int = 0o600) -> None:
path.parent.mkdir(parents=True, exist_ok=True, mode=0o700)
if path.parent.is_symlink() or path.is_symlink():
raise HostSeedError("STATE_SYMLINK_REJECTED")
fd, temporary = tempfile.mkstemp(prefix=".tmp-", dir=path.parent)
try:
os.fchmod(fd, mode)
with os.fdopen(fd, "wb", closefd=True) as stream:
stream.write(canonical(value))
stream.flush()
os.fsync(stream.fileno())
os.replace(temporary, path)
os.chmod(path, mode)
parent_fd = os.open(path.parent, os.O_RDONLY | getattr(os, "O_DIRECTORY", 0))
try:
os.fsync(parent_fd)
finally:
os.close(parent_fd)
finally:
if os.path.exists(temporary):
os.unlink(temporary)
@dataclass(frozen=True)
class Intent:
candidate: str
release_id: str
base_oid: str
payload_commit: str
patch_path: str
manifest_path: str
machine_goal_path: str
seed_bootstrap_path: str
artifact_sha256: str
manifest_sha256: str
machine_goal_sha256: str
release_tree_sha256: str
seed_bootstrap_sha256: str
target_host: str
def as_dict(self) -> dict[str, Any]:
return {
"candidate": self.candidate,
"release_id": self.release_id,
"base_oid": self.base_oid,
"payload_commit": self.payload_commit,
"patch_path": self.patch_path,
"manifest_path": self.manifest_path,
"machine_goal_path": self.machine_goal_path,
"seed_bootstrap_path": self.seed_bootstrap_path,
"artifact_sha256": self.artifact_sha256,
"manifest_sha256": self.manifest_sha256,
"machine_goal_sha256": self.machine_goal_sha256,
"release_tree_sha256": self.release_tree_sha256,
"seed_bootstrap_sha256": self.seed_bootstrap_sha256,
"target_host": self.target_host,
}
@property
def digest(self) -> str:
return sha256(canonical(self.as_dict()))
@dataclass(frozen=True)
class Request:
intent: Intent
authorization_task_id: str
authorization_binding_sha256: str
authorization_sha256: str
expires_at: datetime
@classmethod
def load(cls, raw: Mapping[str, Any], *, host_identity: str, now: datetime) -> "Request":
if set(raw) != {"schema", "operation", "repository", "intent", "authorization"}:
raise HostSeedError("REQUEST_FIELDS_INVALID")
if raw.get("schema") != REQUEST_SCHEMA or raw.get("operation") != OPERATION:
raise HostSeedError("REQUEST_CONTRACT_INVALID")
if raw.get("repository") != REPOSITORY:
raise HostSeedError("REQUEST_REPOSITORY_MISMATCH")
intent_raw = raw.get("intent")
if not isinstance(intent_raw, Mapping) or set(intent_raw) != {
"candidate", "release_id", "base_oid", "payload_commit", "patch_path",
"manifest_path", "machine_goal_path", "seed_bootstrap_path",
"artifact_sha256", "manifest_sha256", "machine_goal_sha256",
"release_tree_sha256", "seed_bootstrap_sha256", "target_host",
}:
raise HostSeedError("INTENT_FIELDS_INVALID")
candidate = str(intent_raw.get("candidate") or "")
release_id = str(intent_raw.get("release_id") or "")
base_oid = str(intent_raw.get("base_oid") or "")
payload_commit = str(intent_raw.get("payload_commit") or "")
artifact_sha = str(intent_raw.get("artifact_sha256") or "")
manifest_sha = str(intent_raw.get("manifest_sha256") or "")
machine_goal_sha = str(intent_raw.get("machine_goal_sha256") or "")
release_tree_sha = str(intent_raw.get("release_tree_sha256") or "")
seed_bootstrap_sha = str(intent_raw.get("seed_bootstrap_sha256") or "")
target_host = str(intent_raw.get("target_host") or "")
if not CANDIDATE_RE.fullmatch(candidate):
raise HostSeedError("CANDIDATE_INVALID")
if not RELEASE_RE.fullmatch(release_id):
raise HostSeedError("RELEASE_ID_INVALID")
if not OID_RE.fullmatch(base_oid) or not OID_RE.fullmatch(payload_commit):
raise HostSeedError("INTENT_OID_INVALID")
if not all(SHA_RE.fullmatch(x) for x in (
artifact_sha, manifest_sha, machine_goal_sha, release_tree_sha, seed_bootstrap_sha
)):
raise HostSeedError("INTENT_SHA_INVALID")
if target_host != host_identity:
raise HostSeedError("TARGET_HOST_MISMATCH")
intent = Intent(
candidate=candidate,
release_id=release_id,
base_oid=base_oid,
payload_commit=payload_commit,
patch_path=_safe_repo_path(intent_raw.get("patch_path"), field="PATCH_PATH"),
manifest_path=_safe_repo_path(intent_raw.get("manifest_path"), field="MANIFEST_PATH"),
machine_goal_path=_safe_repo_path(intent_raw.get("machine_goal_path"), field="MACHINE_GOAL_PATH"),
seed_bootstrap_path=_safe_repo_path(intent_raw.get("seed_bootstrap_path"), field="SEED_BOOTSTRAP_PATH"),
artifact_sha256=artifact_sha,
manifest_sha256=manifest_sha,
machine_goal_sha256=machine_goal_sha,
release_tree_sha256=release_tree_sha,
seed_bootstrap_sha256=seed_bootstrap_sha,
target_host=target_host,
)
auth = raw.get("authorization")
if not isinstance(auth, Mapping) or set(auth) != {
"task_id", "binding_sha256", "expires_at", "intent_sha256",
"authorization_sha256",
}:
raise HostSeedError("AUTHORIZATION_FIELDS_INVALID")
task_id = str(auth.get("task_id") or "")
binding_sha = str(auth.get("binding_sha256") or "")
authorization_sha = str(auth.get("authorization_sha256") or "")
if (
not TASK_RE.fullmatch(task_id)
or not SHA_RE.fullmatch(binding_sha)
or not SHA_RE.fullmatch(authorization_sha)
):
raise HostSeedError("AUTHORIZATION_IDENTITY_INVALID")
if auth.get("intent_sha256") != intent.digest:
raise HostSeedError("AUTHORIZATION_INTENT_MISMATCH")
expires_text = str(auth.get("expires_at") or "")
expires_at = _parse_time(expires_text, field="AUTHORIZATION_EXPIRES_AT")
expected_authorization_sha = sha256(canonical({
"task_id": task_id,
"intent_sha256": intent.digest,
"expires_at": expires_text,
}))
if authorization_sha != expected_authorization_sha:
raise HostSeedError("AUTHORIZATION_DIGEST_MISMATCH")
if now >= expires_at:
raise HostSeedError("AUTHORIZATION_EXPIRED")
if (expires_at - now).total_seconds() > 3600:
raise HostSeedError("AUTHORIZATION_WINDOW_TOO_LARGE")
return cls(
intent=intent,
authorization_task_id=task_id,
authorization_binding_sha256=binding_sha,
authorization_sha256=authorization_sha,
expires_at=expires_at,
)
@dataclass(frozen=True)
class Config:
git_dir: Path
task_binding_root: Path
state_root: Path
release_root: Path
host_identity: str
task_binding_owner: str | None = "aios-bridge-staging"
request_ref: str = REQUEST_REF
request_path: str = REQUEST_PATH
def _load_task_binding(config: Config, request: Request) -> dict[str, Any]:
safe = re.sub(r"[^A-Za-z0-9_.-]+", "-", request.authorization_task_id).strip("-")[:120] or "task"
path = config.task_binding_root / f"{safe}.json"
expected_uid = None
if config.task_binding_owner:
try:
expected_uid = pwd.getpwnam(config.task_binding_owner).pw_uid
except KeyError as exc:
raise HostSeedError("AUTHORIZATION_BINDING_OWNER_UNKNOWN") from exc
raw = _regular_file_bytes(path, maximum=256 * 1024, expected_uid=expected_uid)
if sha256(raw) != request.authorization_binding_sha256:
raise HostSeedError("AUTHORIZATION_BINDING_HASH_MISMATCH")
binding = _parse_json(raw, code="AUTHORIZATION_BINDING_JSON_INVALID")
required = {
"task_id", "repository", "base_oid", "target_branch", "risk",
"description", "idempotency_key", "expected_branch_oid",
"branch_mode", "generation", "job_id",
}
if set(binding) != required:
raise HostSeedError("AUTHORIZATION_BINDING_FIELDS_INVALID")
if binding.get("task_id") != request.authorization_task_id:
raise HostSeedError("AUTHORIZATION_TASK_MISMATCH")
if binding.get("repository") != REPOSITORY:
raise HostSeedError("AUTHORIZATION_REPOSITORY_MISMATCH")
if binding.get("risk") != "high":
raise HostSeedError("AUTHORIZATION_RISK_MISMATCH")
if binding.get("description") != "HOST_SEED_AUTHORIZATION:" + request.authorization_sha256:
raise HostSeedError("AUTHORIZATION_DESCRIPTION_MISMATCH")
if not str(binding.get("target_branch") or "").startswith("agent/host-seed-authorization-"):
raise HostSeedError("AUTHORIZATION_BRANCH_MISMATCH")
if not isinstance(binding.get("generation"), int) or binding["generation"] < 1:
raise HostSeedError("AUTHORIZATION_GENERATION_INVALID")
return binding
def _load_manifest(config: Config, request: Request) -> dict[str, Any]:
raw = _read_git_blob(
config.git_dir, request.intent.payload_commit, request.intent.manifest_path,
maximum=MAX_MANIFEST_BYTES,
)
if sha256(raw) != request.intent.manifest_sha256:
raise HostSeedError("MANIFEST_HASH_MISMATCH")
manifest = _parse_json(raw, code="MANIFEST_JSON_INVALID")
if manifest.get("schema") not in MANIFEST_SCHEMAS:
raise HostSeedError("MANIFEST_SCHEMA_REJECTED")
if manifest.get("candidate") != request.intent.candidate:
raise HostSeedError("MANIFEST_CANDIDATE_MISMATCH")
if manifest.get("repository") != REPOSITORY:
raise HostSeedError("MANIFEST_REPOSITORY_MISMATCH")
if manifest.get("base_oid") != request.intent.base_oid:
raise HostSeedError("MANIFEST_BASE_MISMATCH")
if manifest.get("release_id") != request.intent.release_id:
raise HostSeedError("MANIFEST_RELEASE_ID_MISMATCH")
members = manifest.get("package_members")
changes = manifest.get("changed_files")
if not isinstance(members, Mapping) or not isinstance(changes, list):
raise HostSeedError("MANIFEST_MEMBERS_INVALID")
return manifest
def _load_identity_evidence(config: Config, request: Request) -> dict[str, Any]:
goal_raw = _read_git_blob(
config.git_dir, request.intent.payload_commit, request.intent.machine_goal_path,
maximum=MAX_MACHINE_GOAL_BYTES,
)
if sha256(goal_raw) != request.intent.machine_goal_sha256:
raise HostSeedError("MACHINE_GOAL_HASH_MISMATCH")
goal = _parse_json(goal_raw, code="MACHINE_GOAL_JSON_INVALID")
required_pairs = {
"candidate": request.intent.candidate,
"repository": REPOSITORY,
"base_oid": request.intent.base_oid,
"release_id": request.intent.release_id,
"artifact_sha256": request.intent.artifact_sha256,
"manifest_sha256": request.intent.manifest_sha256,
"release_tree_sha256": request.intent.release_tree_sha256,
"seed_bootstrap_sha256": request.intent.seed_bootstrap_sha256,
}
for key, expected in required_pairs.items():
if goal.get(key) != expected:
raise HostSeedError("MACHINE_GOAL_BINDING_MISMATCH:" + key)
if goal.get("host_seed_authorized") is not False:
raise HostSeedError("MACHINE_GOAL_HOST_SEED_FLAG_INVALID")
bootstrap = _read_git_blob(
config.git_dir, request.intent.payload_commit, request.intent.seed_bootstrap_path,
maximum=MAX_BOOTSTRAP_BYTES,
)
if sha256(bootstrap) != request.intent.seed_bootstrap_sha256:
raise HostSeedError("SEED_BOOTSTRAP_HASH_MISMATCH")
return goal
def _load_patch(config: Config, request: Request) -> bytes:
patch = _read_git_blob(
config.git_dir, request.intent.payload_commit, request.intent.patch_path,
maximum=MAX_PATCH_BYTES,
)
if b"\x00" in patch:
raise HostSeedError("PATCH_BINARY_NUL_REJECTED")
return patch
def _manifest_release_map(manifest: Mapping[str, Any]) -> dict[str, str]:
members = manifest["package_members"]
result: dict[str, str] = {}
prefix = "payload/repo/after/"
for name, digest in members.items():
if not isinstance(name, str) or not name.startswith(prefix) or not isinstance(digest, str) or not SHA_RE.fullmatch(digest):
continue
rel = _safe_repo_path(name[len(prefix):], field="RELEASE_MEMBER")
if rel in result:
raise HostSeedError("DUPLICATE_RELEASE_MEMBER")
result[rel] = digest
declared_count = manifest.get("release_member_count")
if not isinstance(declared_count, int) or declared_count != len(result):
raise HostSeedError("RELEASE_MEMBER_COUNT_MISMATCH")
return result
def _expected_changes(manifest: Mapping[str, Any]) -> dict[str, tuple[str, str]]:
result: dict[str, tuple[str, str]] = {}
for entry in manifest["changed_files"]:
if not isinstance(entry, Mapping):
raise HostSeedError("CHANGED_FILE_ENTRY_INVALID")
rel = _safe_repo_path(entry.get("path"), field="CHANGED_PATH")
digest = str(entry.get("after_sha256") or "")
status = str(entry.get("status") or "")
if not SHA_RE.fullmatch(digest) or status not in {"modified", "added"} or rel in result:
raise HostSeedError("CHANGED_FILE_ENTRY_INVALID")
result[rel] = (digest, status)
if manifest.get("changed_file_count") != len(result):
raise HostSeedError("CHANGED_FILE_COUNT_MISMATCH")
return result
def _regular_member(root: Path, relative: str) -> Path:
current = root
for part in PurePosixPath(relative).parts:
current = current / part
try:
info = current.lstat()
except FileNotFoundError as exc:
raise HostSeedError("RELEASE_MEMBER_MISSING:" + relative) from exc
if stat.S_ISLNK(info.st_mode):
raise HostSeedError("RELEASE_MEMBER_SYMLINK_REJECTED:" + relative)
if not current.is_file():
raise HostSeedError("RELEASE_MEMBER_NOT_REGULAR:" + relative)
return current
def _materialize_and_verify(config: Config, request: Request, manifest: Mapping[str, Any], patch: bytes, workspace: Path) -> dict[str, str]:
if _git_text(config.git_dir, "cat-file", "-t", request.intent.base_oid) != "commit":
raise HostSeedError("BASE_COMMIT_NOT_FOUND")
if _git_text(config.git_dir, "cat-file", "-t", request.intent.payload_commit) != "commit":
raise HostSeedError("PAYLOAD_COMMIT_NOT_FOUND")
workspace.parent.mkdir(parents=True, exist_ok=True)
clone = subprocess.run(
["git", "clone", "--no-checkout", "--shared", str(config.git_dir), str(workspace)],
capture_output=True, timeout=120, env={"PATH": os.environ.get("PATH", "")},
)
if clone.returncode:
raise HostSeedError("WORKSPACE_CLONE_FAILED")
checkout = subprocess.run(
["git", "-C", str(workspace), "checkout", "--detach", request.intent.base_oid],
capture_output=True, timeout=120, env={"PATH": os.environ.get("PATH", "")},
)
if checkout.returncode:
raise HostSeedError("WORKSPACE_CHECKOUT_FAILED")
try:
proc = subprocess.run(
["git", "-C", str(workspace), "apply", "--whitespace=nowarn", "-"],
input=patch, capture_output=True, timeout=120, env={"PATH": os.environ.get("PATH", "")},
)
if proc.returncode:
raise HostSeedError("PATCH_APPLY_FAILED")
changes = _expected_changes(manifest)
status_raw = subprocess.run(
["git", "-C", str(workspace), "status", "--porcelain", "-z"],
check=True, capture_output=True, timeout=120, env={"PATH": os.environ.get("PATH", "")},
).stdout
actual: set[str] = set()
chunks = status_raw.split(b"\0")
for chunk in chunks:
if not chunk:
continue
text = chunk.decode("utf-8", "surrogateescape")
if len(text) < 4:
raise HostSeedError("WORKTREE_STATUS_INVALID")
actual.add(text[3:].replace("\\", "/"))
if actual != set(changes):
raise HostSeedError("PATCH_CHANGESET_MISMATCH")
base_paths = {
_safe_repo_path(path, field="TRACKED_PATH")
for path in _git_text(config.git_dir, "ls-tree", "-r", "--name-only", request.intent.base_oid).splitlines()
if path
}
for rel, (digest, status) in changes.items():
if status == "modified" and rel not in base_paths:
raise HostSeedError("MODIFIED_PATH_NOT_IN_BASE:" + rel)
if status == "added" and rel in base_paths:
raise HostSeedError("ADDED_PATH_ALREADY_IN_BASE:" + rel)
p = _regular_member(workspace, rel)
if sha256(p.read_bytes()) != digest:
raise HostSeedError("CHANGED_FILE_HASH_MISMATCH:" + rel)
release_map = _manifest_release_map(manifest)
expected_release_paths = base_paths | {
rel for rel, (_digest, status) in changes.items() if status == "added"
}
if set(release_map) != expected_release_paths:
raise HostSeedError("RELEASE_MEMBER_SET_MISMATCH")
for rel, digest in release_map.items():
p = _regular_member(workspace, rel)
if sha256(p.read_bytes()) != digest:
raise HostSeedError("RELEASE_MEMBER_HASH_MISMATCH:" + rel)
tree_digest = sha256(canonical_compact({f"payload/repo/after/{k}": release_map[k] for k in sorted(release_map)}))
if tree_digest != request.intent.release_tree_sha256:
raise HostSeedError("RELEASE_TREE_HASH_MISMATCH")
if manifest.get("release_tree_sha256") not in {None, request.intent.release_tree_sha256}:
raise HostSeedError("MANIFEST_RELEASE_TREE_HASH_MISMATCH")
return release_map
except Exception:
raise
def _publish_release(config: Config, request: Request, workspace: Path, release_map: Mapping[str, str]) -> Path:
if config.release_root.exists() and config.release_root.is_symlink():
raise HostSeedError("RELEASE_ROOT_SYMLINK_REJECTED")
final = config.release_root / request.intent.release_id
if final.exists():
if final.is_symlink() or not final.is_dir():
raise HostSeedError("RELEASE_DESTINATION_CONFLICT")
for rel, digest in release_map.items():
p = final / PurePosixPath(rel)
if p.is_symlink() or not p.is_file() or sha256(p.read_bytes()) != digest:
raise HostSeedError("EXISTING_RELEASE_HASH_MISMATCH")
return final
config.release_root.mkdir(parents=True, exist_ok=True)
os.chmod(config.release_root, 0o755)
staging = Path(tempfile.mkdtemp(prefix=".host-seed-", dir=config.release_root))
os.chmod(staging, 0o755)
try:
for rel, digest in release_map.items():
source = _regular_member(workspace, rel)
target = staging / PurePosixPath(rel)
target.parent.mkdir(parents=True, exist_ok=True)
current = target.parent
while current != staging:
os.chmod(current, 0o755)
current = current.parent
data = source.read_bytes()
if sha256(data) != digest:
raise HostSeedError("STAGING_SOURCE_DRIFT")
fd = os.open(target, os.O_WRONLY | os.O_CREAT | os.O_EXCL | getattr(os, "O_NOFOLLOW", 0), 0o600)
try:
with os.fdopen(fd, "wb", closefd=True) as out:
out.write(data)
out.flush()
os.fsync(out.fileno())
except Exception:
raise
source_mode = stat.S_IMODE(source.stat(follow_symlinks=False).st_mode)
os.chmod(target, source_mode & 0o755 or 0o644)
for rel, digest in release_map.items():
p = staging / PurePosixPath(rel)
if p.is_symlink() or sha256(p.read_bytes()) != digest:
raise HostSeedError("STAGING_VERIFY_FAILED")
os.replace(staging, final)
parent_fd = os.open(config.release_root, os.O_RDONLY | getattr(os, "O_DIRECTORY", 0))
try:
os.fsync(parent_fd)
finally:
os.close(parent_fd)
return final
finally:
if staging.exists():
shutil.rmtree(staging)
def execute(config: Config, *, now: datetime | None = None, require_root: bool = True) -> dict[str, Any]:
if require_root and os.geteuid() != 0:
raise HostSeedError("ROOT_REQUIRED")
current = now or datetime.now(timezone.utc)
config.state_root.mkdir(parents=True, exist_ok=True, mode=0o700)
lock_path = config.state_root / "executor.lock"
lock_fd = os.open(lock_path, os.O_RDWR | os.O_CREAT, 0o600)
try:
fcntl.flock(lock_fd, fcntl.LOCK_EX)
ref_probe = subprocess.run(
["git", f"--git-dir={config.git_dir}", "rev-parse", "--verify", config.request_ref],
capture_output=True, timeout=30, env={"PATH": os.environ.get("PATH", "")},
)
if ref_probe.returncode:
return {
"schema": "aios.control-plane-host-seed-poll.v1",
"state": "NO_REQUEST",
"request_ref": config.request_ref,
"service_mutation": False,
"task_creation": False,
}
request_oid = ref_probe.stdout.decode("utf-8", "strict").strip()
if not OID_RE.fullmatch(request_oid):
raise HostSeedError("REQUEST_REF_INVALID")
raw = _read_git_blob(config.git_dir, request_oid, config.request_path, maximum=MAX_REQUEST_BYTES)
request_value = _parse_json(raw, code="REQUEST_JSON_INVALID")
request = Request.load(request_value, host_identity=config.host_identity, now=current)
binding = _load_task_binding(config, request)
manifest = _load_manifest(config, request)
_load_identity_evidence(config, request)
patch = _load_patch(config, request)
op_id = request.intent.digest
receipt_path = config.state_root / "receipts" / f"{op_id}.json"
if receipt_path.exists():
receipt = _parse_json(_regular_file_bytes(receipt_path, maximum=256 * 1024), code="RECEIPT_INVALID")
if receipt.get("state") == "EXECUTED" and receipt.get("intent_sha256") == op_id:
return receipt
raise HostSeedError("OPERATION_STATE_CONFLICT")
_atomic_json(config.state_root / "operations" / f"{op_id}.json", {
"schema": "aios.control-plane-host-seed-operation.v1",
"state": "PENDING",
"intent_sha256": op_id,
"request_commit": request_oid,
"authorization_task_id": request.authorization_task_id,
"authorization_binding_sha256": request.authorization_binding_sha256,
"started_at": current.isoformat().replace("+00:00", "Z"),
})
workspace_root = config.state_root / "workspaces"
workspace_root.mkdir(parents=True, exist_ok=True)
workspace = workspace_root / f"seed-{op_id[:20]}"
if workspace.exists():
raise HostSeedError("WORKSPACE_ALREADY_EXISTS")
release_map: dict[str, str] | None = None
try:
release_map = _materialize_and_verify(config, request, manifest, patch, workspace)
destination = _publish_release(config, request, workspace, release_map)
finally:
if workspace.exists():
shutil.rmtree(workspace, ignore_errors=True)
receipt = {
"schema": "aios.control-plane-host-seed-receipt.v1",
"state": "EXECUTED",
"operation": OPERATION,
"intent_sha256": op_id,
"request_commit": request_oid,
"repository": REPOSITORY,
"target_host": request.intent.target_host,
"candidate": request.intent.candidate,
"release_id": request.intent.release_id,
"base_oid": request.intent.base_oid,
"payload_commit": request.intent.payload_commit,
"validation_artifact_sha256": request.intent.artifact_sha256,
"artifact_delivery_used": False,
"manifest_sha256": request.intent.manifest_sha256,
"machine_goal_sha256": request.intent.machine_goal_sha256,
"release_tree_sha256": request.intent.release_tree_sha256,
"seed_bootstrap_sha256": request.intent.seed_bootstrap_sha256,
"authorization_task_id": request.authorization_task_id,
"authorization_binding_sha256": request.authorization_binding_sha256,
"release_path": str(destination),
"release_member_count": len(release_map or {}),
"service_mutation": False,
"service_restart": False,
"projector_started": False,
"task_creation": False,
"production_write": False,
"main_write": False,
"tag_write": False,
"baseline_write": False,
"executed_at": datetime.now(timezone.utc).isoformat().replace("+00:00", "Z"),
}
_atomic_json(receipt_path, receipt)
_atomic_json(config.state_root / "operations" / f"{op_id}.json", receipt)
return receipt
finally:
fcntl.flock(lock_fd, fcntl.LOCK_UN)
os.close(lock_fd)
def config_from_environment() -> Config:
return Config(
git_dir=Path(os.environ.get("AIOS_HOST_SEED_GIT_DIR", "/data/aios-gitops/repo.git")),
task_binding_root=Path(os.environ.get(
"AIOS_HOST_SEED_TASK_BINDING_ROOT",
"/var/lib/aios-gitops/bridge-v2-staging/task-bindings",
)),
state_root=Path(os.environ.get(
"AIOS_HOST_SEED_STATE_ROOT",
"/var/lib/aios-control-plane-host-seed-executor",
)),
release_root=Path(os.environ.get("AIOS_HOST_SEED_RELEASE_ROOT", "/opt/aios/releases")),
host_identity=os.environ.get("AIOS_HOST_IDENTITY", "cn-network-mkt"),
task_binding_owner=os.environ.get(
"AIOS_HOST_SEED_TASK_BINDING_OWNER", "aios-bridge-staging"
) or None,
request_ref=os.environ.get("AIOS_HOST_SEED_REQUEST_REF", REQUEST_REF),
request_path=os.environ.get("AIOS_HOST_SEED_REQUEST_PATH", REQUEST_PATH),
)
def main() -> int:
parser = argparse.ArgumentParser()
parser.add_argument("command", choices=("execute",))
args = parser.parse_args()
if args.command == "execute":
result = execute(config_from_environment())
print(json.dumps(result, sort_keys=True, separators=(",", ":")))
return 0
if __name__ == "__main__":
raise SystemExit(main())
@@ -0,0 +1,25 @@
"""Separate read, write, and release plane readiness evaluation."""
from __future__ import annotations
from dataclasses import dataclass
from typing import Mapping
@dataclass(frozen=True, slots=True)
class RuntimeStatus:
read_plane: Mapping[str, bool]
write_plane: Mapping[str, bool]
release_plane: Mapping[str, bool]
@property
def release_control_plane_ready(self) -> bool:
return all(self.read_plane.values()) and all(self.write_plane.values()) and all(self.release_plane.values())
def as_dict(self) -> dict[str, object]:
return {
"READ_PLANE": dict(self.read_plane),
"WRITE_PLANE": dict(self.write_plane),
"RELEASE_PLANE": dict(self.release_plane),
"release_control_plane_ready": self.release_control_plane_ready,
}
@@ -0,0 +1,58 @@
"""Workspace-independent authority/network/Gitea ref sentinel."""
from __future__ import annotations
from dataclasses import dataclass
from pathlib import Path
import subprocess
from typing import Callable, Mapping
class SentinelError(RuntimeError):
pass
@dataclass(frozen=True, slots=True)
class SentinelInputs:
authority_git_dir: Path
gitea_remote: str
gitea_api: str
ref: str
expected_oid: str
@dataclass(frozen=True, slots=True)
class SentinelResult:
authority_oid: str | None
network_oid: str | None
gitea_oid: str | None
equal: bool
error: str | None = None
def authority_oid(inputs: SentinelInputs) -> str:
result = subprocess.run(
["git", "--git-dir", str(inputs.authority_git_dir), "rev-parse", inputs.ref],
text=True,
capture_output=True,
check=False,
)
if result.returncode != 0:
raise SentinelError("AUTHORITY_REF_UNAVAILABLE")
return result.stdout.strip()
def evaluate(
inputs: SentinelInputs,
*,
network_reader: Callable[[str, str], str | None],
gitea_reader: Callable[[str, str], str | None],
) -> SentinelResult:
try:
local = authority_oid(inputs)
network = network_reader(inputs.gitea_remote, inputs.ref)
gitea = gitea_reader(inputs.gitea_api, inputs.ref)
equal = local == network == gitea == inputs.expected_oid
return SentinelResult(local, network, gitea, equal, None if equal else "OID_MISMATCH")
except SentinelError as exc:
return SentinelResult(None, None, None, False, str(exc))
@@ -0,0 +1,26 @@
"""Non-sensitive control-plane snapshot assembly."""
from __future__ import annotations
from typing import Any, Mapping
def _copy(value: Mapping[str, Any] | None, keys: tuple[str, ...]) -> dict[str, Any]:
value = value or {}
return {key: value.get(key) for key in keys}
def build_control_plane_snapshot(*, target_version: str, runtime: Mapping[str, Any] | None = None, repository: Mapping[str, Any] | None = None, production: Mapping[str, Any] | None = None, task: Mapping[str, Any] | None = None, capabilities: Mapping[str, Any] | None = None, credential_broker: Mapping[str, Any] | None = None) -> dict[str, Any]:
return {
"version": {"target_release": target_version},
"runtime": _copy(runtime, ("bridge_schema_version", "control_plane_release_version", "runtime_release_commit", "runtime_path", "artifact_sha256", "drift_status")),
"repository": _copy(repository, ("identity", "authority_git_dir", "authority_main_oid")),
"production": _copy(production, ("baseline_version", "target_oid", "deployment_state")),
"task": _copy(task, ("task_id", "branch", "authority_branch_oid", "gitea_git_oid", "gitea_api_oid", "commit_visible", "generation", "reconciliation_state")),
"capabilities": _copy(capabilities, ("legacy_writes_enabled", "governed_writes_enabled", "force_push", "main_write", "tag_write", "baseline_write", "production_write")),
"credential_broker": _copy(credential_broker, ("status", "provider")),
"secret_material_exposed": False,
}
def getControlPlaneSnapshot(**kwargs: Any) -> dict[str, Any]:
return build_control_plane_snapshot(**kwargs)
@@ -0,0 +1,78 @@
"""Metadata-only, server-side credential lease broker."""
from __future__ import annotations
from dataclasses import dataclass
from datetime import datetime, timedelta, timezone
import os
from pathlib import Path
import secrets
from typing import Any, Callable, Mapping
@dataclass(frozen=True, slots=True)
class CredentialLease:
lease_id: str
task_id: str
repository: str
target_branch: str
generation: int
issued_at: str
expires_at: str
scopes: tuple[str, ...]
provider: str
state: str = "ACTIVE"
def as_dict(self) -> dict[str, Any]:
return {"lease_id": self.lease_id, "task_id": self.task_id, "repository": self.repository, "target_branch": self.target_branch, "generation": self.generation, "issued_at": self.issued_at, "expires_at": self.expires_at, "scopes": list(self.scopes), "provider": self.provider, "state": self.state}
class FileCredentialProvider:
def __init__(self, token_path: str | os.PathLike[str], *, actor: str = "aios-autonomy-agent") -> None:
self.token_path = Path(token_path)
self.actor = actor
def environment(self, lease: CredentialLease) -> dict[str, str]:
if not self.token_path.is_file():
raise RuntimeError("CREDENTIAL_PROVIDER_UNAVAILABLE")
return {"CREDENTIAL_FILE": str(self.token_path), "GITEA_MACHINE_ACTOR": self.actor}
class CredentialBroker:
def __init__(self, provider: Any, *, now: Callable[[], datetime] | None = None) -> None:
self.provider = provider
self.now = now or (lambda: datetime.now(timezone.utc))
self._leases: dict[str, CredentialLease] = {}
def issue(self, *, task_id: str, repository: str, target_branch: str, generation: int, scopes: tuple[str, ...] = ("git:push", "gitea:read"), lease_seconds: int = 900) -> dict[str, Any]:
if repository != "aios/aios" or not target_branch.startswith("agent/") or generation < 1 or not 1 <= lease_seconds <= 86400:
raise ValueError("CREDENTIAL_LEASE_BINDING_INVALID")
now = self.now().astimezone(timezone.utc)
lease = CredentialLease("lease-" + secrets.token_hex(16), task_id, repository, target_branch, generation, now.isoformat(), (now + timedelta(seconds=lease_seconds)).isoformat(), tuple(scopes), type(self.provider).__name__)
self._leases[lease.lease_id] = lease
return lease.as_dict()
def _get(self, lease_id: str) -> CredentialLease:
lease = self._leases.get(lease_id)
if lease is None:
raise KeyError("CREDENTIAL_LEASE_NOT_FOUND")
if lease.state == "ACTIVE" and self.now().astimezone(timezone.utc) >= datetime.fromisoformat(lease.expires_at):
lease = CredentialLease(**{**lease.as_dict(), "scopes": tuple(lease.scopes), "state": "EXPIRED"})
self._leases[lease_id] = lease
return lease
def environment(self, lease_id: str) -> Mapping[str, str]:
lease = self._get(lease_id)
if lease.state != "ACTIVE":
raise RuntimeError("CREDENTIAL_LEASE_NOT_ACTIVE")
return self.provider.environment(lease)
def status(self, lease_id: str) -> dict[str, Any]:
return self._get(lease_id).as_dict()
def revoke(self, lease_id: str) -> dict[str, Any]:
lease = self._get(lease_id)
if lease.state == "REVOKED":
return lease.as_dict()
updated = CredentialLease(**{**lease.as_dict(), "scopes": tuple(lease.scopes), "state": "REVOKED"})
self._leases[lease_id] = updated
return updated.as_dict()
@@ -0,0 +1,125 @@
"""Non-Owner Gitea machine capability and branch reconciliation boundary."""
from __future__ import annotations
from dataclasses import dataclass
from typing import Any, Mapping, Protocol
class MachineBrokerError(RuntimeError):
pass
class GiteaTransport(Protocol):
def compare_and_swap_branch(self, branch: str, expected_oid: str, new_oid: str) -> str: ...
def create_or_update_pr(self, branch: str, target: str, title: str, body: str) -> Mapping[str, Any]: ...
def publish_status(self, branch: str, state: str, description: str) -> Mapping[str, Any]: ...
def protected_merge(self, pr_number: int) -> Mapping[str, Any]: ...
def read_branch(self, branch: str) -> str | None: ...
def whoami(self) -> Mapping[str, Any]: ...
def repository(self, owner: str, name: str) -> Mapping[str, Any]: ...
@dataclass(frozen=True, slots=True)
class MachineCredentialProjection:
"""Opaque systemd credential reference; the credential value is never accepted."""
reference: str
account: str
owner_account: str = "aiman"
@classmethod
def from_mapping(cls, value: Mapping[str, Any]) -> "MachineCredentialProjection":
if set(value) != {"scheme", "name", "account"}:
raise MachineBrokerError("CREDENTIAL_REFERENCE_INVALID")
if value["scheme"] != "systemd-credential" or value["name"] != "gitea-machine-token":
raise MachineBrokerError("CREDENTIAL_REFERENCE_INVALID")
return cls(f"{value['scheme']}:{value['name']}", str(value["account"]))
def __post_init__(self) -> None:
if self.reference != "systemd-credential:gitea-machine-token":
raise MachineBrokerError("CREDENTIAL_REFERENCE_INVALID")
if not self.account or self.account == self.owner_account:
raise MachineBrokerError("OWNER_MACHINE_IDENTITY_REJECTED")
@dataclass(frozen=True, slots=True)
class CampaignCapabilityReceipt:
campaign_id: str
task_id: str
repository: str
branch: str
expected_oid: str
capabilities: frozenset[str]
denied: frozenset[str]
def require(self, capability: str) -> None:
if capability in self.denied or capability not in self.capabilities:
raise MachineBrokerError("CAPABILITY_DENIED")
if self.branch in {"main", "master"}:
raise MachineBrokerError("PROTECTED_BRANCH_DENIED")
class GiteaMachineBroker:
def __init__(self, transport: GiteaTransport, credential: MachineCredentialProjection, receipt: CampaignCapabilityReceipt) -> None:
self.transport = transport
self.credential = credential
self.receipt = receipt
def verify_identity(self) -> Mapping[str, Any]:
identity = dict(self.transport.whoami())
login = identity.get("login") or identity.get("username")
if not isinstance(login, str) or not login or login == self.credential.owner_account or login != self.credential.account:
raise MachineBrokerError("GITEA_MACHINE_IDENTITY_INVALID")
owner, name = self.receipt.repository.split("/", 1)
repo = dict(self.transport.repository(owner, name))
if repo.get("full_name") != self.receipt.repository:
raise MachineBrokerError("GITEA_REPOSITORY_MISMATCH")
return {"login": login, "repository": self.receipt.repository, "owner_account": self.credential.owner_account}
def push_branch(self, new_oid: str) -> str:
self.receipt.require("exact_task_branch_push")
return self.transport.compare_and_swap_branch(self.receipt.branch, self.receipt.expected_oid, new_oid)
def create_pr(self, title: str, body: str) -> Mapping[str, Any]:
self.receipt.require("pr_create_update")
return self.transport.create_or_update_pr(self.receipt.branch, "main", title, body)
def status(self, state: str, description: str, context: str = "aios/durable-release") -> Mapping[str, Any]:
self.receipt.require("status_publish")
return self.transport.publish_status(self.receipt.branch, state, description, context)
def merge(self, pr_number: int) -> Mapping[str, Any]:
self.receipt.require("protected_merge")
return self.transport.protected_merge(pr_number)
def readback(self) -> str | None:
self.receipt.require("remote_readback")
return self.transport.read_branch(self.receipt.branch)
def reconcile_exact_branch(authority_oid: str, gitea_oid: str | None, expected_oid: str) -> str:
if authority_oid != expected_oid:
return "AUTHORITY_EXPECTATION_MISMATCH"
if gitea_oid is None:
return "CREATE_REQUIRED"
if gitea_oid == expected_oid:
return "ALREADY_IN_SYNC"
return "MANUAL_OWNER_REVIEW_REQUIRED"
def reconcile_fast_forward(
remote_oid: str | None,
expected_old_oid: str,
new_oid: str,
*,
old_is_ancestor_of_new: bool,
) -> str:
"""Return a bounded action for an active Job/lease branch scope."""
if remote_oid is None:
return "CREATE_REQUIRED"
if remote_oid == new_oid:
return "ALREADY_IN_SYNC"
if remote_oid == expected_old_oid and old_is_ancestor_of_new:
return "FAST_FORWARD_REQUIRED"
return "MANUAL_OWNER_REVIEW_REQUIRED"
@@ -0,0 +1,208 @@
"""Gitea HTTP/Git transport using only a projected systemd credential."""
from __future__ import annotations
from dataclasses import dataclass
import json
import os
from pathlib import Path
import subprocess
import tempfile
from typing import Any, Mapping
from urllib.error import HTTPError, URLError
from urllib.parse import quote, urlencode
from urllib.request import Request, urlopen
class GiteaTransportError(RuntimeError):
pass
def _safe_oid(value: str, field: str) -> str:
if not isinstance(value, str) or len(value) != 40 or any(ch not in "0123456789abcdef" for ch in value):
raise GiteaTransportError(f"{field}_INVALID")
return value
@dataclass(slots=True)
class GiteaMachineTransport:
api_base: str
repository_id: str
remote_url: str
authority_git_dir: Path
credential_name: str = "gitea-machine-token"
actor: str = "aios-autonomy-agent"
timeout: int = 20
expected_merge_head: str | None = None
expected_merge_base: str | None = None
def __post_init__(self) -> None:
if self.credential_name != "gitea-machine-token":
raise GiteaTransportError("CREDENTIAL_NAME_INVALID")
if "/" not in self.repository_id or self.repository_id.startswith("/"):
raise GiteaTransportError("REPOSITORY_INVALID")
if not self.api_base.startswith("https://") and not self.api_base.startswith("http://127.0.0.1"):
raise GiteaTransportError("GITEA_API_SCOPE_INVALID")
if not self.remote_url.startswith("https://"):
raise GiteaTransportError("GITEA_REMOTE_SCOPE_INVALID")
@property
def _credential_path(self) -> Path:
directory = os.environ.get("CREDENTIALS_DIRECTORY", "")
if not directory:
raise GiteaTransportError("CREDENTIAL_PROJECTION_MISSING")
path = Path(directory) / self.credential_name
if path.is_symlink() or not path.is_file():
raise GiteaTransportError("CREDENTIAL_PROJECTION_MISSING")
return path
def _token(self) -> str:
value = self._credential_path.read_text(encoding="utf-8").strip()
if not value:
raise GiteaTransportError("CREDENTIAL_EMPTY")
return value
def _request(self, method: str, path: str, body: Mapping[str, Any] | None = None) -> tuple[int, Any]:
data = None if body is None else json.dumps(dict(body), separators=(",", ":")).encode()
headers = {"Accept": "application/json", "Authorization": f"token {self._token()}"}
if data is not None:
headers["Content-Type"] = "application/json"
request = Request(self.api_base.rstrip("/") + path, data=data, headers=headers, method=method)
try:
with urlopen(request, timeout=self.timeout) as response:
raw = response.read()
return response.status, json.loads(raw.decode()) if raw else {}
except HTTPError as exc:
# Never include response bodies: Gitea may echo sensitive request context.
raise GiteaTransportError(f"GITEA_HTTP_{exc.code}") from exc
except (URLError, TimeoutError, OSError) as exc:
raise GiteaTransportError("GITEA_NETWORK_ERROR") from exc
def whoami(self) -> Mapping[str, Any]:
return self._request("GET", "/user")[1]
def repository_info(self) -> Mapping[str, Any]:
owner, name = self.repository_id.split("/", 1)
return self._request("GET", f"/repos/{quote(owner)}/{quote(name)}")[1]
def repository(self, owner: str, name: str) -> Mapping[str, Any]:
if f"{owner}/{name}" != self.repository_id:
raise GiteaTransportError("REPOSITORY_SCOPE_DENIED")
return self.repository_info()
def read_branch(self, branch: str) -> str | None:
owner, name = self.repository_id.split("/", 1)
try:
value = self._request("GET", f"/repos/{quote(owner)}/{quote(name)}/branches/{quote(branch, safe='/')}")[1]
except GiteaTransportError as exc:
if str(exc) == "GITEA_HTTP_404":
return None
raise
sha = value.get("commit", {}).get("id") if isinstance(value, dict) else None
return _safe_oid(sha, "remote_branch_oid")
def _askpass(self) -> tuple[Path, dict[str, str]]:
credential = self._credential_path
fd, name = tempfile.mkstemp(prefix="aios-gitea-askpass-", dir="/tmp")
path = Path(name)
os.fchmod(fd, 0o700)
script = "#!/bin/sh\ncase \"$1\" in *Username*) printf '%s\\n' \"$GITEA_MACHINE_ACTOR\" ;; *) cat \"$CREDENTIAL_FILE\" ;; esac\n"
with os.fdopen(fd, "w", encoding="utf-8") as stream:
stream.write(script)
stream.flush()
os.fsync(stream.fileno())
return path, {
"GIT_ASKPASS": str(path),
"GIT_TERMINAL_PROMPT": "0",
"GITEA_MACHINE_ACTOR": self.actor,
"CREDENTIAL_FILE": str(credential),
}
def _push_branch_fast_forward(self, branch: str, new_oid: str) -> None:
askpass, additions = self._askpass()
environment = os.environ.copy()
environment.update(additions)
try:
result = subprocess.run(
["git", f"--git-dir={self.authority_git_dir}", "push", self.remote_url, f"{new_oid}:refs/heads/{branch}"],
text=True,
capture_output=True,
check=False,
env=environment,
timeout=self.timeout,
)
if result.returncode != 0:
raise GiteaTransportError("GITEA_BRANCH_CREATE_FAILED")
finally:
askpass.unlink(missing_ok=True)
def compare_and_swap_branch(self, branch: str, expected_oid: str, new_oid: str) -> str:
_safe_oid(expected_oid, "expected_oid")
_safe_oid(new_oid, "new_oid")
current = self.read_branch(branch)
if current == new_oid:
return new_oid
if current is None:
self._push_branch_fast_forward(branch, new_oid)
readback = self.read_branch(branch)
if readback != new_oid:
raise GiteaTransportError("GITEA_BRANCH_CREATE_READBACK_MISMATCH")
return readback
if current != expected_oid:
raise GiteaTransportError("GITEA_BRANCH_EXPECTATION_MISMATCH")
# The preflight read binds the expected old tip. The Git receive-pack
# then enforces the actual fast-forward CAS while transferring the new
# objects; no force/lease override or arbitrary ref is permitted.
self._push_branch_fast_forward(branch, new_oid)
readback = self.read_branch(branch)
if readback != new_oid:
raise GiteaTransportError("GITEA_BRANCH_CAS_READBACK_MISMATCH")
return readback
def _pulls(self) -> list[Mapping[str, Any]]:
owner, name = self.repository_id.split("/", 1)
query = urlencode({"state": "all", "base_branch": "main", "limit": 100})
value = self._request("GET", f"/repos/{quote(owner)}/{quote(name)}/pulls?{query}")[1]
return value if isinstance(value, list) else []
def create_or_update_pr(self, branch: str, target: str, title: str, body: str) -> Mapping[str, Any]:
if target != "main":
raise GiteaTransportError("PR_BASE_SCOPE_DENIED")
head = self.read_branch(branch)
if head is None:
raise GiteaTransportError("PR_HEAD_MISSING")
matches = [p for p in self._pulls() if (p.get("head") or {}).get("ref") == branch and (p.get("base") or {}).get("ref") == target]
open_matches = [p for p in matches if p.get("state") == "open"]
if len(open_matches) > 1:
raise GiteaTransportError("MULTIPLE_MATCHING_PRS")
if open_matches:
if (open_matches[0].get("head") or {}).get("sha") != head:
raise GiteaTransportError("PR_HEAD_CONFLICT")
return open_matches[0]
owner, name = self.repository_id.split("/", 1)
return self._request("POST", f"/repos/{quote(owner)}/{quote(name)}/pulls", {"head": branch, "base": target, "title": title, "body": body, "allow_maintainer_edit": False})[1]
def publish_status(self, branch: str, state: str, description: str, context: str = "aios/durable-release") -> Mapping[str, Any]:
oid = self.read_branch(branch)
if oid is None:
raise GiteaTransportError("STATUS_BRANCH_MISSING")
owner, name = self.repository_id.split("/", 1)
return self._request("POST", f"/repos/{quote(owner)}/{quote(name)}/statuses/{oid}", {"state": state, "context": context, "description": description})[1]
def protected_merge(self, pr_number: int) -> Mapping[str, Any]:
if not isinstance(pr_number, int) or pr_number < 1:
raise GiteaTransportError("PR_NUMBER_INVALID")
owner, name = self.repository_id.split("/", 1)
pr = self._request("GET", f"/repos/{quote(owner)}/{quote(name)}/pulls/{pr_number}")[1]
if pr.get("state") != "open" or (pr.get("base") or {}).get("ref") != "main":
raise GiteaTransportError("PR_MERGE_BINDING_INVALID")
if pr.get("mergeable") is False:
raise GiteaTransportError("PR_NOT_MERGEABLE")
head_oid = (pr.get("head") or {}).get("sha")
base_oid = (pr.get("base") or {}).get("sha")
if self.expected_merge_head is not None and head_oid != self.expected_merge_head:
raise GiteaTransportError("PR_HEAD_EXPECTATION_MISMATCH")
if self.expected_merge_base is not None and base_oid != self.expected_merge_base:
raise GiteaTransportError("PR_BASE_EXPECTATION_MISMATCH")
body = {"do": "fast-forward-only", "force_merge": False, "merge_when_checks_succeed": False, "head_commit_id": head_oid}
return self._request("POST", f"/repos/{quote(owner)}/{quote(name)}/pulls/{pr_number}/merge", body)[1]
@@ -0,0 +1,93 @@
"""Canonical Gitea transport and slash-branch verification."""
from __future__ import annotations
from dataclasses import dataclass
import json
import os
from pathlib import Path
import subprocess
from typing import Any, Callable, Mapping, Optional
from urllib.parse import quote
from urllib.request import Request, urlopen
from aios_gitops_reconciliation_core import RemoteObservation
@dataclass(frozen=True, slots=True)
class GiteaPublisherConfig:
repository: str = "aios/aios"
api_base: str = "https://g.cnnetmkt.top/git/api/v1"
remote_name: str = "gitea-http"
class GiteaPublisher:
def __init__(self, workspace: Path, *, config: GiteaPublisherConfig = GiteaPublisherConfig(), credential: Any = None, api_get: Callable[[str], Any] | None = None, git_ref: Callable[[str], Optional[str]] | None = None) -> None:
self.workspace = Path(workspace)
self.config = config
self.credential = credential
self._api_get = api_get
self._git_ref = git_ref
@staticmethod
def validate_repository(repository: str) -> None:
if repository != "aios/aios":
raise ValueError("REPOSITORY_IDENTITY_MISMATCH")
@staticmethod
def validate_branch(branch: str) -> None:
if not isinstance(branch, str) or not branch.startswith("agent/") or branch in {"main", "master"}:
raise ValueError("BRANCH_SCOPE_DENIED")
def repository_identity(self) -> str:
self.validate_repository(self.config.repository)
return self.config.repository
def _environment(self) -> dict[str, str]:
if self.credential is not None and hasattr(self.credential, "environment"):
return dict(self.credential.environment())
return {}
def read_git_ref(self, branch: str) -> Optional[str]:
self.validate_branch(branch)
if self._git_ref is not None:
return self._git_ref(branch)
result = subprocess.run(["git", "-C", str(self.workspace), "ls-remote", self.config.remote_name, f"refs/heads/{branch}"], text=True, capture_output=True, check=False, env={**os.environ, **self._environment(), "GIT_TERMINAL_PROMPT": "0"})
if result.returncode != 0 or not result.stdout.strip():
return None
return result.stdout.split()[0]
def _api(self, path: str) -> Any:
if self._api_get is not None:
return self._api_get(path)
headers = {"Accept": "application/json"}
env = self._environment()
if env.get("AIOS_GITEA_TOKEN"):
headers["Authorization"] = "token " + env["AIOS_GITEA_TOKEN"]
with urlopen(Request(self.config.api_base.rstrip("/") + path, headers=headers), timeout=20) as response:
return json.loads(response.read() or b"{}")
def read_api_branch(self, branch: str) -> Optional[str]:
self.validate_branch(branch)
for page in range(1, 101):
value = self._api(f"/repos/aios/aios/branches?page={page}&limit=50")
if not isinstance(value, list):
return None
for item in value:
if isinstance(item, Mapping) and item.get("name") == branch:
commit = item.get("commit")
return commit.get("id") if isinstance(commit, Mapping) else None
if len(value) < 50:
return None
raise RuntimeError("GITEA_BRANCH_PAGINATION_LIMIT")
def commit_visible(self, oid: str) -> bool:
if not isinstance(oid, str) or len(oid) != 40:
return False
try:
value = self._api(f"/repos/aios/aios/git/commits/{quote(oid, safe='')}")
return isinstance(value, Mapping) and value.get("sha", oid) == oid
except Exception:
return False
def observe(self, branch: str, desired_oid: str, authority_oid: Optional[str], *, expected_oid: Optional[str] = None, observed_at: str = "") -> RemoteObservation:
return RemoteObservation(self.repository_identity(), branch, desired_oid, authority_oid, self.read_git_ref(branch), self.read_api_branch(branch), self.commit_visible(desired_oid), observed_at, expected_oid)
@@ -0,0 +1,595 @@
"""SQLite-backed durable GitOps Job Controller.
The controller owns durable intent, idempotency, lease metadata, state
transitions, receipts, and payload references. A worker owns Git side effects
behind the campaign capability and branch-CAS boundaries.
"""
from __future__ import annotations
from dataclasses import dataclass
from enum import Enum
import hashlib
import json
import os
from pathlib import Path
import sqlite3
import tempfile
from datetime import datetime, timezone
from typing import Any, Callable, Mapping
class JobError(RuntimeError):
pass
class ServiceUnavailable(JobError):
pass
class BindingConflict(JobError):
pass
class JobState(str, Enum):
REQUESTED = "REQUESTED"
AUTHORIZED = "AUTHORIZED"
LEASED = "LEASED"
WORKTREE_READY = "WORKTREE_READY"
EXECUTING = "EXECUTING"
LOCAL_VERIFIED = "LOCAL_VERIFIED"
REMOTE_SYNCING = "REMOTE_SYNCING"
REMOTE_VERIFIED = "REMOTE_VERIFIED"
COMPLETED = "COMPLETED"
RECOVERABLE_FAILURE = "RECOVERABLE_FAILURE"
RECONCILING = "RECONCILING"
RECONCILIATION_REQUIRED = "RECONCILIATION_REQUIRED"
MANUAL_OWNER_REVIEW_REQUIRED = "MANUAL_OWNER_REVIEW_REQUIRED"
_ORDER = {
JobState.REQUESTED: 0,
JobState.AUTHORIZED: 1,
JobState.LEASED: 2,
JobState.WORKTREE_READY: 3,
JobState.EXECUTING: 4,
JobState.LOCAL_VERIFIED: 5,
JobState.REMOTE_SYNCING: 6,
JobState.REMOTE_VERIFIED: 7,
JobState.COMPLETED: 8,
}
_RECOVERABLE = {JobState.RECOVERABLE_FAILURE, JobState.RECONCILIATION_REQUIRED}
_TERMINAL = {JobState.COMPLETED, JobState.MANUAL_OWNER_REVIEW_REQUIRED}
def utc_now() -> str:
return datetime.now(timezone.utc).isoformat().replace("+00:00", "Z")
def canonical_json(value: Any) -> bytes:
return (
json.dumps(value, sort_keys=True, separators=(",", ":"), ensure_ascii=False) + "\n"
).encode("utf-8")
def sha256_json(value: Any) -> str:
return hashlib.sha256(canonical_json(value)).hexdigest()
class PayloadStore:
"""Content-addressed filesystem payloads; raw content never enters SQLite."""
def __init__(self, root: str | os.PathLike[str]) -> None:
self.root = Path(root)
if not self.root.is_absolute() or self.root.is_symlink():
raise JobError("PAYLOAD_ROOT_INVALID")
self.root.mkdir(parents=True, exist_ok=True, mode=0o700)
os.chmod(self.root, 0o700)
def put(self, content: bytes, expected_digest: str) -> tuple[str, int]:
digest = hashlib.sha256(content).hexdigest()
if digest != expected_digest:
raise JobError("PAYLOAD_DIGEST_MISMATCH")
if len(expected_digest) != 64 or any(ch not in "0123456789abcdef" for ch in expected_digest):
raise JobError("PAYLOAD_DIGEST_INVALID")
target = self.root / expected_digest
if target.is_symlink():
raise JobError("PAYLOAD_PATH_SYMLINK")
if target.is_file():
if target.stat().st_size != len(content) or hashlib.sha256(target.read_bytes()).hexdigest() != digest:
raise JobError("PAYLOAD_EXISTING_MISMATCH")
return f"sha256/{digest}", len(content)
fd, temporary = tempfile.mkstemp(prefix=".payload-", dir=self.root)
try:
os.fchmod(fd, 0o600)
with os.fdopen(fd, "wb") as stream:
stream.write(content)
stream.flush()
os.fsync(stream.fileno())
os.replace(temporary, target)
os.chmod(target, 0o600)
finally:
if os.path.exists(temporary):
os.unlink(temporary)
return f"sha256/{digest}", len(content)
def read_verified(self, reference: str, expected_digest: str, expected_size: int) -> bytes:
if reference != f"sha256/{expected_digest}":
raise JobError("PAYLOAD_REFERENCE_MISMATCH")
path = self.root / expected_digest
if path.is_symlink() or not path.is_file():
raise JobError("PAYLOAD_MISSING")
content = path.read_bytes()
if len(content) != expected_size or hashlib.sha256(content).hexdigest() != expected_digest:
raise JobError("PAYLOAD_VERIFY_FAILED")
return content
@dataclass(frozen=True, slots=True)
class JobReceipt:
job_id: str
idempotency_key: str
state: JobState
side_effect_state: str
http_status: int
revision: int
receipt: Mapping[str, Any] | None = None
def as_dict(self) -> dict[str, Any]:
return {
"job_id": self.job_id,
"idempotency_key": self.idempotency_key,
"state": self.state.value,
"side_effect_state": self.side_effect_state,
"http_status": self.http_status,
"revision": self.revision,
"receipt": dict(self.receipt) if self.receipt else None,
}
class JobStore:
SCHEMA_VERSION = 1
def __init__(self, root: str | os.PathLike[str], *, payload_root: str | os.PathLike[str] | None = None) -> None:
self.root = Path(root)
if not self.root.is_absolute() or self.root.is_symlink():
raise JobError("JOB_ROOT_INVALID")
self.root.mkdir(parents=True, exist_ok=True, mode=0o700)
os.chmod(self.root, 0o700)
self.db_path = self.root / "state.sqlite3"
self.payloads = PayloadStore(payload_root or self.root.parent / "payloads" / "sha256")
self._initialize()
def _connect(self) -> sqlite3.Connection:
if self.db_path.is_symlink():
raise ServiceUnavailable("JOB_DB_SYMLINK")
connection = sqlite3.connect(self.db_path, timeout=30.0)
connection.row_factory = sqlite3.Row
connection.execute("PRAGMA journal_mode=WAL")
connection.execute("PRAGMA synchronous=FULL")
connection.execute("PRAGMA foreign_keys=ON")
connection.execute("PRAGMA busy_timeout=30000")
return connection
def _initialize(self) -> None:
try:
with self._connect() as connection:
connection.executescript(
"""
CREATE TABLE IF NOT EXISTS metadata (key TEXT PRIMARY KEY, value TEXT NOT NULL);
CREATE TABLE IF NOT EXISTS jobs (
job_id TEXT PRIMARY KEY, idempotency_key TEXT NOT NULL UNIQUE,
campaign_id TEXT NOT NULL, repository TEXT NOT NULL, operation TEXT NOT NULL,
branch TEXT NOT NULL, expected_branch_oid TEXT NOT NULL,
payload_digest TEXT NOT NULL, payload_ref TEXT, payload_size INTEGER,
state TEXT NOT NULL, side_effect_state TEXT NOT NULL,
revision INTEGER NOT NULL, request_json TEXT NOT NULL,
receipt_json TEXT, created_at TEXT NOT NULL, updated_at TEXT NOT NULL
);
CREATE TABLE IF NOT EXISTS idempotency_keys (
key TEXT PRIMARY KEY, job_id TEXT NOT NULL UNIQUE,
binding_json TEXT NOT NULL, created_at TEXT NOT NULL,
FOREIGN KEY(job_id) REFERENCES jobs(job_id)
);
CREATE TABLE IF NOT EXISTS branch_leases (
lease_id TEXT PRIMARY KEY, job_id TEXT NOT NULL UNIQUE,
branch TEXT NOT NULL, lineage_anchor_oid TEXT NOT NULL,
current_head_oid TEXT NOT NULL, generation INTEGER NOT NULL,
lease_expires_at TEXT NOT NULL, state TEXT NOT NULL,
revision INTEGER NOT NULL, FOREIGN KEY(job_id) REFERENCES jobs(job_id)
);
CREATE TABLE IF NOT EXISTS state_transitions (
sequence INTEGER PRIMARY KEY AUTOINCREMENT, job_id TEXT NOT NULL,
from_state TEXT, to_state TEXT NOT NULL, revision INTEGER NOT NULL,
side_effect_state TEXT NOT NULL, created_at TEXT NOT NULL,
FOREIGN KEY(job_id) REFERENCES jobs(job_id)
);
CREATE TABLE IF NOT EXISTS receipts (
receipt_id TEXT PRIMARY KEY, job_id TEXT NOT NULL,
kind TEXT NOT NULL, digest TEXT NOT NULL, payload_json TEXT NOT NULL,
created_at TEXT NOT NULL, FOREIGN KEY(job_id) REFERENCES jobs(job_id)
);
INSERT OR REPLACE INTO metadata(key, value) VALUES('schema_version', '1');
"""
)
except sqlite3.Error as exc:
raise ServiceUnavailable("JOB_DB_UNAVAILABLE") from exc
def _mirror(self, job: Mapping[str, Any]) -> None:
directory = self.root / str(job["job_id"])
directory.mkdir(mode=0o700, exist_ok=True)
(directory / "receipts").mkdir(mode=0o700, exist_ok=True)
(directory / "logs-digest").mkdir(mode=0o700, exist_ok=True)
for name, value in (("request.json", job["request"]), ("state.json", job)):
target = directory / name
fd, temporary = tempfile.mkstemp(prefix=".mirror-", dir=directory)
try:
os.fchmod(fd, 0o600)
with os.fdopen(fd, "wb") as stream:
stream.write(canonical_json(value))
stream.flush()
os.fsync(stream.fileno())
os.replace(temporary, target)
finally:
if os.path.exists(temporary):
os.unlink(temporary)
def read(self, job_id: str) -> dict[str, Any]:
try:
with self._connect() as connection:
row = connection.execute("SELECT * FROM jobs WHERE job_id=?", (job_id,)).fetchone()
except sqlite3.Error as exc:
raise ServiceUnavailable("JOB_DB_UNAVAILABLE") from exc
if row is None:
raise JobError("JOB_NOT_FOUND")
return dict(row)
def _receipt(self, row: Mapping[str, Any], *, duplicate: bool) -> JobReceipt:
state = JobState(row["state"])
if state == JobState.COMPLETED:
status, side = 200, "COMPLETED"
elif state in _RECOVERABLE:
status = 202
side = row["side_effect_state"] if row["side_effect_state"] == "REMOTE_PUSH_PENDING" else "RESUME_SAME_JOB"
elif row["side_effect_state"] == "UNKNOWN":
status, side = 503, "UNKNOWN"
else:
status, side = 202, "SAME_JOB" if duplicate else "QUEUED"
receipt = json.loads(row["receipt_json"]) if row["receipt_json"] else None
return JobReceipt(row["job_id"], row["idempotency_key"], state, side, status, int(row["revision"]), receipt)
class GitOpsJobController:
def __init__(self, store: JobStore) -> None:
self.store = store
@staticmethod
def _binding(request: Mapping[str, Any]) -> dict[str, str]:
fields = ("campaign_id", "repository", "operation", "branch", "expected_branch_oid", "payload_digest")
if any(not isinstance(request.get(field), str) or not request[field].strip() for field in fields):
raise JobError("JOB_BINDING_INVALID")
oid = request["expected_branch_oid"]
digest = request["payload_digest"]
if len(oid) != 40 or any(ch not in "0123456789abcdef" for ch in oid):
raise JobError("EXPECTED_BRANCH_OID_INVALID")
if len(digest) != 64 or any(ch not in "0123456789abcdef" for ch in digest):
raise JobError("PAYLOAD_DIGEST_INVALID")
if request["branch"] in {"main", "master"} or request["branch"].startswith(("refs/", "tags/")):
raise JobError("PROTECTED_BRANCH_REJECTED")
binding = {field: str(request[field]) for field in fields}
# Operation-specific, non-secret binding inputs must survive the
# durable round trip and participate in idempotency. Arbitrary request
# fields are intentionally discarded rather than persisted.
optional = (
"idempotency_key",
"task_id",
"new_branch_oid",
"authorization_recovery_digest",
"lineage_anchor_oid",
"base_oid",
"branch_mode",
"generation",
"expected_main_oid",
"pr_number",
"target_branch",
"check_context",
"check_state",
"check_description",
)
for field in optional:
value = request.get(field)
if value is None:
continue
if isinstance(value, bool) or not isinstance(value, (str, int)):
raise JobError("JOB_BINDING_OPTION_INVALID")
text = str(value).strip()
if not text or len(text) > 512:
raise JobError("JOB_BINDING_OPTION_INVALID")
binding[field] = text
return binding
def submit(self, request: Mapping[str, Any], *, payload: bytes | None = None) -> JobReceipt:
binding = self._binding(request)
payload_ref = request.get("payload_ref")
payload_size = request.get("payload_size")
if payload is not None:
payload_ref, payload_size = self.store.payloads.put(payload, binding["payload_digest"])
if payload_ref is not None and payload_ref != f"sha256/{binding['payload_digest']}":
raise JobError("PAYLOAD_REFERENCE_MISMATCH")
if payload_ref is not None and (not isinstance(payload_size, int) or payload_size < 0):
raise JobError("PAYLOAD_SIZE_INVALID")
client_idempotency_key = binding.get("idempotency_key")
if client_idempotency_key is not None:
if len(client_idempotency_key) > 128 or not all(
character.isalnum() or character in "._:-" for character in client_idempotency_key
):
raise JobError("IDEMPOTENCY_KEY_INVALID")
key = sha256_json({"client_idempotency_key": client_idempotency_key})
else:
key = sha256_json(binding)
job_id = "job-" + key[:32]
durable_request = dict(binding)
if payload_ref is not None:
durable_request.update({"payload_ref": str(payload_ref), "payload_size": int(payload_size)})
now = utc_now()
try:
with self.store._connect() as connection:
connection.execute("BEGIN IMMEDIATE")
existing = connection.execute("SELECT * FROM jobs WHERE idempotency_key=?", (key,)).fetchone()
if existing is not None:
if json.loads(existing["request_json"]).get("binding") != binding:
raise BindingConflict("BINDING_CONFLICT")
row = dict(existing)
connection.commit()
return self.store._receipt(row, duplicate=True)
connection.execute(
"INSERT INTO jobs(job_id,idempotency_key,campaign_id,repository,operation,branch,expected_branch_oid,payload_digest,payload_ref,payload_size,state,side_effect_state,revision,request_json,receipt_json,created_at,updated_at) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)",
(job_id, key, binding["campaign_id"], binding["repository"], binding["operation"], binding["branch"], binding["expected_branch_oid"], binding["payload_digest"], payload_ref, payload_size, JobState.REQUESTED.value, "NONE", 0, json.dumps({"binding": binding, **durable_request}, sort_keys=True), None, now, now),
)
connection.execute("INSERT INTO idempotency_keys(key,job_id,binding_json,created_at) VALUES(?,?,?,?)", (key, job_id, json.dumps(binding, sort_keys=True), now))
connection.execute("INSERT INTO state_transitions(job_id,from_state,to_state,revision,side_effect_state,created_at) VALUES(?,?,?,?,?,?)", (job_id, None, JobState.REQUESTED.value, 0, "NONE", now))
row = dict(connection.execute("SELECT * FROM jobs WHERE job_id=?", (job_id,)).fetchone())
connection.commit()
except BindingConflict:
raise
except sqlite3.IntegrityError:
row = self.store.read(job_id)
return self.store._receipt(row, duplicate=True)
except sqlite3.Error as exc:
raise ServiceUnavailable("JOB_DB_UNAVAILABLE") from exc
row["request"] = json.loads(row["request_json"])
self.store._mirror(row)
return self.store._receipt(row, duplicate=False)
def lease_job(
self,
job_id: str,
*,
lease_id: str,
lineage_anchor_oid: str,
current_head_oid: str,
generation: int,
lease_expires_at: str,
expected_revision: int,
) -> JobReceipt:
"""Atomically create a branch lease and move its Job to LEASED."""
if generation < 1 or any(len(oid) != 40 for oid in (lineage_anchor_oid, current_head_oid)):
raise JobError("LEASE_BINDING_INVALID")
try:
with self.store._connect() as connection:
connection.execute("BEGIN IMMEDIATE")
row = connection.execute("SELECT * FROM jobs WHERE job_id=?", (job_id,)).fetchone()
if row is None:
raise JobError("JOB_NOT_FOUND")
if int(row["revision"]) != expected_revision:
raise JobError("REVISION_CAS_CONFLICT")
if row["branch"] in {"main", "master"}:
raise JobError("PROTECTED_BRANCH_REJECTED")
now = utc_now()
connection.execute("INSERT INTO branch_leases(lease_id,job_id,branch,lineage_anchor_oid,current_head_oid,generation,lease_expires_at,state,revision) VALUES(?,?,?,?,?,?,?,?,?)", (lease_id, job_id, row["branch"], lineage_anchor_oid, current_head_oid, generation, lease_expires_at, "ACTIVE", 0))
next_revision = expected_revision + 1
updated = connection.execute("UPDATE jobs SET state=?,side_effect_state=?,revision=?,updated_at=? WHERE job_id=? AND revision=?", (JobState.LEASED.value, "LEASED", next_revision, now, job_id, expected_revision))
if updated.rowcount != 1:
raise JobError("REVISION_CAS_CONFLICT")
connection.execute("INSERT INTO state_transitions(job_id,from_state,to_state,revision,side_effect_state,created_at) VALUES(?,?,?,?,?,?)", (job_id, row["state"], JobState.LEASED.value, next_revision, "LEASED", now))
result = dict(connection.execute("SELECT * FROM jobs WHERE job_id=?", (job_id,)).fetchone())
connection.commit()
except sqlite3.IntegrityError as exc:
raise JobError("LEASE_CONFLICT") from exc
except (JobError, BindingConflict):
raise
except sqlite3.Error as exc:
raise ServiceUnavailable("JOB_DB_UNAVAILABLE") from exc
result["request"] = json.loads(result["request_json"])
self.store._mirror(result)
return self.store._receipt(result, duplicate=True)
def update_lease(self, lease_id: str, *, expected_revision: int, current_head_oid: str, generation: int, state: str = "ACTIVE") -> None:
if len(current_head_oid) != 40 or generation < 1:
raise JobError("LEASE_BINDING_INVALID")
try:
with self.store._connect() as connection:
connection.execute("BEGIN IMMEDIATE")
updated = connection.execute("UPDATE branch_leases SET current_head_oid=?,generation=?,state=?,revision=revision+1 WHERE lease_id=? AND revision=?", (current_head_oid, generation, state, lease_id, expected_revision))
if updated.rowcount != 1:
raise JobError("LEASE_REVISION_CAS_CONFLICT")
connection.commit()
except JobError:
raise
except sqlite3.Error as exc:
raise ServiceUnavailable("JOB_DB_UNAVAILABLE") from exc
def advance(self, job_id: str, state: JobState, *, side_effect_state: str, receipt: Mapping[str, Any] | None = None, expected_revision: int | None = None) -> JobReceipt:
return self._advance(
job_id,
state,
side_effect_state=side_effect_state,
receipt=receipt,
expected_revision=expected_revision,
)
def _advance(
self,
job_id: str,
state: JobState,
*,
side_effect_state: str,
receipt: Mapping[str, Any] | None = None,
expected_revision: int | None = None,
_allow_verified_external_recovery: bool = False,
) -> JobReceipt:
try:
with self.store._connect() as connection:
connection.execute("BEGIN IMMEDIATE")
row = connection.execute("SELECT * FROM jobs WHERE job_id=?", (job_id,)).fetchone()
if row is None:
raise JobError("JOB_NOT_FOUND")
previous = JobState(row["state"])
revision = int(row["revision"])
if expected_revision is not None and expected_revision != revision:
raise JobError("REVISION_CAS_CONFLICT")
recovery_transition = (
_allow_verified_external_recovery
and previous == JobState.MANUAL_OWNER_REVIEW_REQUIRED
and state == JobState.COMPLETED
)
if previous in _TERMINAL and state != previous and not recovery_transition:
raise JobError("JOB_TERMINAL")
if state not in _RECOVERABLE | {JobState.RECONCILING, JobState.MANUAL_OWNER_REVIEW_REQUIRED}:
if previous in _RECOVERABLE:
raise JobError("RECONCILE_REQUIRED")
if previous != JobState.RECONCILING and _ORDER.get(state, -1) < _ORDER.get(previous, -1):
raise JobError("JOB_STATE_REGRESSION")
next_revision = revision + 1
receipt_json = json.dumps(dict(receipt), sort_keys=True) if receipt is not None else row["receipt_json"]
now = utc_now()
updated = connection.execute("UPDATE jobs SET state=?,side_effect_state=?,revision=?,receipt_json=?,updated_at=? WHERE job_id=? AND revision=?", (state.value, side_effect_state, next_revision, receipt_json, now, job_id, revision))
if updated.rowcount != 1:
raise JobError("REVISION_CAS_CONFLICT")
connection.execute("INSERT INTO state_transitions(job_id,from_state,to_state,revision,side_effect_state,created_at) VALUES(?,?,?,?,?,?)", (job_id, previous.value, state.value, next_revision, side_effect_state, now))
if receipt is not None:
digest = hashlib.sha256(canonical_json(receipt)).hexdigest()
connection.execute("INSERT INTO receipts(receipt_id,job_id,kind,digest,payload_json,created_at) VALUES(?,?,?,?,?,?)", (f"receipt-{job_id}-{next_revision}", job_id, state.value, digest, json.dumps(dict(receipt), sort_keys=True), now))
result = dict(connection.execute("SELECT * FROM jobs WHERE job_id=?", (job_id,)).fetchone())
connection.commit()
except (JobError, BindingConflict):
raise
except sqlite3.Error as exc:
raise ServiceUnavailable("JOB_DB_UNAVAILABLE") from exc
result["request"] = json.loads(result["request_json"])
self.store._mirror(result)
return self.store._receipt(result, duplicate=True)
@staticmethod
def _validate_verified_external_merge_proof(
job_id: str,
binding: Mapping[str, Any],
proof: Mapping[str, Any],
) -> dict[str, Any]:
if not isinstance(proof, Mapping):
raise JobError("EXTERNAL_SIDE_EFFECT_PROOF_INVALID")
def require_oid(field: str) -> str:
value = proof.get(field)
if not isinstance(value, str) or len(value) != 40 or any(ch not in "0123456789abcdef" for ch in value):
raise JobError("EXTERNAL_SIDE_EFFECT_PROOF_INVALID")
return value
if proof.get("proof_schema") != "aios.job-recovery.v1":
raise JobError("EXTERNAL_SIDE_EFFECT_PROOF_INVALID")
if proof.get("job_id") != job_id:
raise JobError("EXTERNAL_SIDE_EFFECT_PROOF_MISMATCH")
if proof.get("operation") != "PROTECTED_MERGE" or binding.get("operation") != "PROTECTED_MERGE":
raise JobError("EXTERNAL_SIDE_EFFECT_OPERATION_REJECTED")
if proof.get("pr_number") != 53 or str(binding.get("pr_number")) != "53":
raise JobError("EXTERNAL_SIDE_EFFECT_PROOF_MISMATCH")
if proof.get("pr_merged") is not True or proof.get("protected_merge") is not True:
raise JobError("EXTERNAL_SIDE_EFFECT_PROOF_MISMATCH")
if proof.get("force_push") is not False:
raise JobError("EXTERNAL_SIDE_EFFECT_PROOF_MISMATCH")
if proof.get("machine_actor") != "aios-autonomy-agent":
raise JobError("EXTERNAL_SIDE_EFFECT_PROOF_MISMATCH")
expected_head = str(binding.get("expected_branch_oid", ""))
expected_base = str(binding.get("expected_main_oid", ""))
head_oid = require_oid("head_oid")
base_oid = require_oid("base_oid")
merged_main = require_oid("main_oid_after_protected_merge")
gitea_merged_main = require_oid("gitea_main_oid_after_protected_merge")
authority_merged_main = require_oid("authority_main_oid_after_protected_merge")
current_gitea = require_oid("current_gitea_main_oid")
current_authority = require_oid("current_authority_main_oid")
if head_oid != expected_head or base_oid != expected_base:
raise JobError("EXTERNAL_SIDE_EFFECT_PROOF_MISMATCH")
if not (merged_main == gitea_merged_main == authority_merged_main == head_oid):
raise JobError("EXTERNAL_SIDE_EFFECT_PROOF_MISMATCH")
if current_gitea != current_authority or proof.get("current_main_descends_from_protected_merge") is not True:
raise JobError("EXTERNAL_SIDE_EFFECT_PROOF_MISMATCH")
return dict(proof)
def complete_verified_external_side_effect(
self,
job_id: str,
proof: Mapping[str, Any],
*,
expected_revision: int | None = None,
) -> JobReceipt:
"""Close one historical manual job from independently verified proof.
This is deliberately narrower than advance: it is only valid for the
known protected PR53 recovery shape and preserves the old receipt.
The actual state change still uses the normal revision-CAS path.
"""
current = self.store.read(job_id)
revision = int(current["revision"])
if expected_revision is not None and expected_revision != revision:
raise JobError("REVISION_CAS_CONFLICT")
request = json.loads(current["request_json"])
binding = request.get("binding", request)
normalized_proof = self._validate_verified_external_merge_proof(job_id, binding, proof)
existing_receipt = json.loads(current["receipt_json"]) if current["receipt_json"] else None
if JobState(current["state"]) == JobState.COMPLETED:
if (
existing_receipt
and existing_receipt.get("recovered_from_historical_manual") is True
and existing_receipt.get("proof") == normalized_proof
):
return self.store._receipt(current, duplicate=True)
raise JobError("JOB_TERMINAL")
if JobState(current["state"]) != JobState.MANUAL_OWNER_REVIEW_REQUIRED:
raise JobError("EXTERNAL_SIDE_EFFECT_RECOVERY_STATE_INVALID")
recovery_receipt = {
"recovered_from_historical_manual": True,
"recovery_kind": "VERIFIED_EXTERNAL_SIDE_EFFECT",
"historical_receipt": existing_receipt,
"proof": normalized_proof,
}
return self._advance(
job_id,
JobState.COMPLETED,
side_effect_state="REMOTE_VERIFIED",
receipt=recovery_receipt,
expected_revision=revision,
_allow_verified_external_recovery=True,
)
def reconcile(self, job_id: str, observer: Callable[[Mapping[str, Any]], Mapping[str, Any]]) -> JobReceipt:
current = self.store.read(job_id)
if JobState(current["state"]) not in _RECOVERABLE:
return self.store._receipt(current, duplicate=True)
self.advance(job_id, JobState.RECONCILING, side_effect_state="IN_PROGRESS")
try:
observed = dict(observer(current))
stable = JobState(observed.get("state", JobState.REQUESTED.value))
if stable in {JobState.RECOVERABLE_FAILURE, JobState.RECONCILING}:
raise JobError("RECONCILIATION_UNSTABLE")
return self.advance(job_id, stable, side_effect_state=str(observed.get("side_effect_state", "NONE")), receipt=observed.get("receipt"))
except Exception as exc:
self.advance(job_id, JobState.RECONCILIATION_REQUIRED, side_effect_state="UNKNOWN", receipt={"error_code": type(exc).__name__})
raise JobError("RECONCILIATION_REQUIRED") from exc
def resume(self, job_id: str) -> JobReceipt:
current = self.store.read(job_id)
if JobState(current["state"]) in _TERMINAL:
raise JobError("JOB_TERMINAL")
return self.reconcile(job_id, lambda _current: {"state": JobState.REQUESTED.value, "side_effect_state": "RESUME_SAME_JOB"})
@@ -0,0 +1,279 @@
"""Durable Job side-effect executor for the AIOS campaign control plane."""
from __future__ import annotations
import argparse
import base64
from dataclasses import dataclass, replace
import json
from pathlib import Path
from typing import Any, Mapping
from aios_authority_git_executor import AuthorityGitExecutor, AuthorityGitError
from aios_campaign_recovery import RecoveryEvidenceError, load_recovered_authorization
from aios_gitea_machine_broker import CampaignCapabilityReceipt, GiteaMachineBroker, MachineBrokerError
from aios_gitops_job_controller import GitOpsJobController, JobError, JobState, JobStore
TASK_ID = "AIOS_AUTONOMOUS_RELEASE_CAMPAIGN_V1"
CAMPAIGN_ID = "AIOS-AUTONOMOUS-RELEASE-CAMPAIGN-20260807"
REPOSITORY = "aios/aios"
BRANCH = "agent/autonomous-release-campaign-v1-20260807"
EXPECTED_BRANCH_OID = "bba675fab3b7e0c5fba7066720252b3c91b9f20d"
AUTHORIZATION_DIGEST = "6b0bf550eb702126e967eeff2d561fa808444b924d8a71f5399accad508ed3b2"
LINEAGE_ANCHOR = "69b45e81dcd2c3707c367003b08c55fd361faae8"
class JobExecutionError(RuntimeError):
pass
@dataclass(slots=True)
class DurableJobExecutor:
controller: GitOpsJobController
authority: AuthorityGitExecutor
broker: GiteaMachineBroker
recovery_path: Path
def _request(self, job_id: str) -> tuple[dict[str, Any], dict[str, Any]]:
row = self.controller.store.read(job_id)
try:
request = json.loads(row["request_json"])
except (KeyError, TypeError, json.JSONDecodeError) as exc:
raise JobExecutionError("JOB_REQUEST_INVALID") from exc
return row, dict(request.get("binding") or {})
def _validate_campaign(self, request: Mapping[str, Any]) -> str:
operation = str(request.get("operation", ""))
expected_branch_oid = request.get("expected_branch_oid")
required = {
"task_id": TASK_ID,
"campaign_id": CAMPAIGN_ID,
"repository": REPOSITORY,
"branch": BRANCH,
"authorization_recovery_digest": AUTHORIZATION_DIGEST,
}
for key, expected in required.items():
if request.get(key) != expected:
raise JobExecutionError(f"JOB_{key.upper()}_MISMATCH")
new_oid = request.get("new_branch_oid")
if not isinstance(new_oid, str) or len(new_oid) != 40 or any(ch not in "0123456789abcdef" for ch in new_oid):
raise JobExecutionError("NEW_BRANCH_OID_INVALID")
if not isinstance(expected_branch_oid, str) or len(expected_branch_oid) != 40 or any(ch not in "0123456789abcdef" for ch in expected_branch_oid):
raise JobExecutionError("EXPECTED_BRANCH_OID_INVALID")
if operation == "ADVANCE_EXISTING_CAMPAIGN_BRANCH" and expected_branch_oid == new_oid:
raise JobExecutionError("EXPECTED_BRANCH_OID_INVALID")
if operation != "ADVANCE_EXISTING_CAMPAIGN_BRANCH" and expected_branch_oid != new_oid:
raise JobExecutionError("POST_ADVANCE_HEAD_BINDING_INVALID")
try:
recovery = load_recovered_authorization(self.recovery_path)
except RecoveryEvidenceError as exc:
raise JobExecutionError(str(exc)) from exc
if recovery["digest"] != AUTHORIZATION_DIGEST:
raise JobExecutionError("RECOVERY_DIGEST_MISMATCH")
for key, expected in required.items():
if key == "campaign_id":
actual = recovery.get(key)
elif key == "authorization_recovery_digest":
continue
else:
actual = recovery.get({"expected_branch_oid": "expected_branch_oid"}.get(key, key))
if actual is not None and actual != expected:
raise JobExecutionError("RECOVERY_BINDING_MISMATCH")
if recovery.get("expected_branch_oid") != EXPECTED_BRANCH_OID:
raise JobExecutionError("RECOVERY_BRANCH_OID_MISMATCH")
authority_head = self.authority.read_ref(f"refs/heads/{BRANCH}")
if authority_head not in {expected_branch_oid, new_oid}:
raise JobExecutionError("AUTHORITY_BRANCH_EXPECTATION_MISMATCH")
if not self.authority.ancestry_check(expected_branch_oid, new_oid):
raise JobExecutionError("NEW_OID_NOT_DESCENDANT")
if not self.authority.ancestry_check(EXPECTED_BRANCH_OID, new_oid):
raise JobExecutionError("NEW_OID_OUTSIDE_AUTHORIZED_LINEAGE")
identity = self.broker.verify_identity()
if identity.get("repository") != REPOSITORY or identity.get("login") == "aiman":
raise JobExecutionError("MACHINE_IDENTITY_SCOPE_INVALID")
return new_oid
def dry_run(self, job_id: str) -> dict[str, Any]:
_row, request = self._request(job_id)
new_oid = self._validate_campaign(request)
remote = self.broker.readback()
if remote is None:
action = "CREATE_REQUIRED"
elif remote == new_oid:
action = "ALREADY_IN_SYNC"
elif remote == request["expected_branch_oid"]:
action = "FAST_FORWARD_REQUIRED"
else:
action = "MANUAL_OWNER_REVIEW_REQUIRED"
if action == "MANUAL_OWNER_REVIEW_REQUIRED":
raise JobExecutionError("GITEA_BRANCH_CONFLICT")
return {
"decision": "SAFE_TO_ADVANCE",
"authority_action": "CAS_REQUIRED",
"gitea_action": action,
"previous_branch_oid": request["expected_branch_oid"],
"final_branch_oid": new_oid,
"force_push": False,
}
def _recover_requested(self, job_id: str) -> dict[str, Any]:
row = self.controller.store.read(job_id)
if JobState(row["state"]) in {JobState.RECOVERABLE_FAILURE, JobState.RECONCILIATION_REQUIRED}:
self.controller.resume(job_id)
row = self.controller.store.read(job_id)
return row
def execute(self, job_id: str, *, dry_run: bool = False) -> dict[str, Any]:
try:
return self._execute_unprotected(job_id, dry_run=dry_run)
except Exception as exc:
try:
row = self.controller.store.read(job_id)
state = JobState(row["state"])
if state not in {JobState.COMPLETED, JobState.MANUAL_OWNER_REVIEW_REQUIRED, JobState.RECOVERABLE_FAILURE, JobState.RECONCILIATION_REQUIRED}:
self.controller.advance(job_id, JobState.RECOVERABLE_FAILURE, side_effect_state="UNKNOWN", receipt={"error_code": type(exc).__name__})
except Exception:
pass
raise
def _execute_unprotected(self, job_id: str, *, dry_run: bool = False) -> dict[str, Any]:
row, request = self._request(job_id)
operation = request.get("operation")
if operation == "ADVANCE_EXISTING_CAMPAIGN_BRANCH":
if dry_run:
return self.dry_run(job_id)
return self._advance_campaign(job_id)
if operation == "COMMIT_FILE":
return self._commit_file(job_id, request)
if operation == "SYNC_TASK_BRANCH_TO_GITEA":
new_oid = self._validate_campaign(request)
final = self.broker.push_branch(new_oid)
return {"operation": operation, "remote_verified": final == new_oid, "final_network_oid": final}
if operation == "CREATE_PR":
self._validate_campaign(request)
return dict(self.broker.create_pr("AIOS autonomous release control plane", "Durable campaign continuation; exact head and recovery evidence are bound in the Job."))
if operation == "PUBLISH_CHECK":
self._validate_campaign(request)
context = request.get("check_context", "aios/durable-release")
state = request.get("check_state", "success")
description = request.get("check_description", "Durable campaign control-plane check passed")
return dict(self.broker.status(str(state), str(description), str(context)))
if operation == "PROTECTED_MERGE":
self._validate_campaign(request)
number = int(request.get("pr_number", "0"))
self.broker.transport.expected_merge_head = request["expected_branch_oid"]
expected_main = request.get("expected_main_oid")
if expected_main is not None:
self.broker.transport.expected_merge_base = expected_main
return dict(self.broker.merge(number))
if operation == "RECONCILE_AUTHORITY_MAIN":
self._validate_campaign(request)
old = request.get("expected_main_oid")
new = request.get("new_branch_oid")
if not isinstance(old, str) or not isinstance(new, str):
raise JobExecutionError("MAIN_RECONCILIATION_BINDING_INVALID")
gitea_main = self.broker.transport.read_branch("main")
if gitea_main != new:
raise JobExecutionError("GITEA_MAIN_EXPECTATION_MISMATCH")
if not self.authority.ancestry_check(old, new):
raise JobExecutionError("MAIN_OID_NOT_DESCENDANT")
return {"authority_main": self.authority.ff_CAS_main_after_verified_protected_merge(old, new)}
raise JobExecutionError("OPERATION_NOT_ALLOWED")
def _advance_campaign(self, job_id: str) -> dict[str, Any]:
self._recover_requested(job_id)
row, request = self._request(job_id)
new_oid = self._validate_campaign(request)
expected_oid = request["expected_branch_oid"]
state = JobState(row["state"])
if state == JobState.REQUESTED:
receipt = self.controller.advance(job_id, JobState.AUTHORIZED, side_effect_state="AUTHORIZED", receipt={"authorization_digest": AUTHORIZATION_DIGEST, "task_id": TASK_ID})
row = self.controller.store.read(job_id)
if JobState(row["state"]) == JobState.AUTHORIZED:
self.controller.lease_job(job_id, lease_id=f"lease-{job_id}", lineage_anchor_oid=LINEAGE_ANCHOR, current_head_oid=expected_oid, generation=1, lease_expires_at="2099-01-01T00:00:00Z", expected_revision=int(row["revision"]))
row = self.controller.store.read(job_id)
if JobState(row["state"]) == JobState.LEASED:
self.controller.advance(job_id, JobState.WORKTREE_READY, side_effect_state="WORKTREE_READY", receipt={"branch": BRANCH, "expected_oid": expected_oid})
row = self.controller.store.read(job_id)
if JobState(row["state"]) == JobState.WORKTREE_READY:
self.controller.advance(job_id, JobState.EXECUTING, side_effect_state="IN_PROGRESS")
row = self.controller.store.read(job_id)
authority_before = self.authority.read_ref(f"refs/heads/{BRANCH}")
if authority_before == expected_oid:
authority_oid = self.authority.advance_task_branch_CAS(f"refs/heads/{BRANCH}", expected_oid, new_oid)
elif authority_before == new_oid:
authority_oid = new_oid
else:
raise JobExecutionError("AUTHORITY_BRANCH_CONFLICT")
row = self.controller.store.read(job_id)
if JobState(row["state"]) == JobState.EXECUTING:
self.controller.advance(job_id, JobState.LOCAL_VERIFIED, side_effect_state="LOCAL_VERIFIED", receipt={"authority": {"branch": BRANCH, "previous_oid": expected_oid, "new_oid": new_oid, "CAS_success": True, "readback_oid": authority_oid}})
row = self.controller.store.read(job_id)
remote_before = self.broker.readback()
if remote_before != new_oid:
if remote_before not in {None, expected_oid}:
raise JobExecutionError("GITEA_BRANCH_CONFLICT")
if JobState(row["state"]) == JobState.LOCAL_VERIFIED:
if hasattr(self.broker, "receipt"):
self.broker.receipt = replace(self.broker.receipt, expected_oid=expected_oid)
self.controller.advance(job_id, JobState.REMOTE_SYNCING, side_effect_state="IN_PROGRESS", receipt={"gitea": {"previous_oid": remote_before, "desired_oid": new_oid, "force_push": False}})
final_network = self.broker.push_branch(new_oid)
else:
final_network = new_oid
final_api = self.broker.readback()
if final_api != new_oid:
raise JobExecutionError("GITEA_BRANCH_READBACK_MISMATCH")
row = self.controller.store.read(job_id)
if JobState(row["state"]) in {JobState.LOCAL_VERIFIED, JobState.REMOTE_SYNCING}:
self.controller.advance(job_id, JobState.REMOTE_VERIFIED, side_effect_state="REMOTE_VERIFIED", receipt={"gitea": {"machine_actor": "aios-autonomy-agent", "previous_oid": remote_before, "desired_oid": new_oid, "final_network_oid": final_network, "final_API_oid": final_api, "force_push": False}})
row = self.controller.store.read(job_id)
if JobState(row["state"]) == JobState.REMOTE_VERIFIED:
self.controller.advance(job_id, JobState.COMPLETED, side_effect_state="REMOTE_VERIFIED", receipt={"authority_oid": authority_oid, "network_oid": final_network, "gitea_api_oid": final_api, "force_push": False})
return self.controller.store._receipt(self.controller.store.read(job_id), duplicate=True).as_dict()
def _commit_file(self, job_id: str, request: Mapping[str, Any]) -> dict[str, Any]:
row = self.controller.store.read(job_id)
payload_ref = request.get("payload_ref")
if not payload_ref:
raise JobExecutionError("PAYLOAD_REQUIRED")
payload = self.controller.store.payloads.read_verified(payload_ref, row["payload_digest"], int(request["payload_size"]))
try:
spec = json.loads(payload.decode())
path = str(spec["path"])
content = base64.b64decode(spec["content_base64"], validate=True)
except Exception as exc:
raise JobExecutionError("COMMIT_FILE_PAYLOAD_INVALID") from exc
new_oid = self.authority.commit_file_in_isolated_worktree(base_oid=row["expected_branch_oid"], files={path: content}, message="chore(gitops): durable commit-file", worktree_name=job_id)
return {"operation": "COMMIT_FILE", "new_oid": new_oid}
def build_from_environment(job_root: Path) -> DurableJobExecutor:
from aios_gitea_machine_transport import GiteaMachineTransport
from aios_gitea_machine_broker import MachineCredentialProjection
account = __import__("os").environ.get("AIOS_GITEA_MACHINE_ACCOUNT", "aios-autonomy-agent")
api = __import__("os").environ.get("AIOS_GITEA_API", "https://g.cnnetmkt.top/git/api/v1")
remote = __import__("os").environ.get("AIOS_GITEA_REMOTE", "https://g.cnnetmkt.top/git/aios/aios.git")
authority_dir = Path(__import__("os").environ.get("AIOS_GITOPS_AUTHORITY_GIT_DIR", "/data/aios-gitops/repo.git"))
worktree_root = Path(__import__("os").environ.get("AIOS_GITOPS_WORKTREE_ROOT", "/var/lib/aios-gitops/worktrees"))
recovery = Path(__import__("os").environ.get("AIOS_RELEASE_RECOVERY_RECEIPT", "/var/lib/aios-gitops/release-campaigns/AIOS-AUTONOMOUS-RELEASE-CAMPAIGN-20260807/authorization-recovery.json"))
transport = GiteaMachineTransport(api, REPOSITORY, remote, authority_dir)
credential = MachineCredentialProjection.from_mapping({"scheme": "systemd-credential", "name": "gitea-machine-token", "account": account})
receipt = CampaignCapabilityReceipt(CAMPAIGN_ID, TASK_ID, REPOSITORY, BRANCH, EXPECTED_BRANCH_OID, frozenset({"exact_task_branch_push", "pr_create_update", "status_publish", "protected_merge", "remote_readback"}), frozenset({"direct_main_push", "force_push", "tag_update", "tag_delete", "baseline_write", "production_deploy"}))
return DurableJobExecutor(GitOpsJobController(JobStore(job_root)), AuthorityGitExecutor(authority_dir, worktree_root), GiteaMachineBroker(transport, credential, receipt), recovery)
def main(argv: list[str] | None = None) -> int:
parser = argparse.ArgumentParser()
parser.add_argument("--job-root", required=True, type=Path)
parser.add_argument("--job-id", required=True)
parser.add_argument("--dry-run", action="store_true")
args = parser.parse_args(argv)
result = build_from_environment(args.job_root).execute(args.job_id, dry_run=args.dry_run)
print(json.dumps(result, sort_keys=True))
return 0
if __name__ == "__main__":
raise SystemExit(main())
@@ -0,0 +1,80 @@
"""Pure, observation-first decisions for governed GitOps reconciliation."""
from __future__ import annotations
from dataclasses import dataclass
from enum import Enum
import re
from typing import Optional
OID_RE = re.compile(r"^[0-9a-f]{40}$")
class ReconciliationAction(str, Enum):
NOOP = "NOOP"
PUSH_EXACT = "PUSH_EXACT"
VERIFY_REMOTE = "VERIFY_REMOTE"
MARK_REMOTE_VERIFIED = "MARK_REMOTE_VERIFIED"
MARK_RECONCILIATION_REQUIRED = "MARK_RECONCILIATION_REQUIRED"
FAIL_CONFLICT = "FAIL_CONFLICT"
@dataclass(frozen=True, slots=True)
class RemoteObservation:
repository_identity: str
branch: str
desired_oid: str
authority_oid: Optional[str]
remote_git_oid: Optional[str]
remote_api_oid: Optional[str]
commit_visible: bool
observed_at: str
expected_oid: Optional[str] = None
@property
def all_equal(self) -> bool:
values = (self.authority_oid, self.remote_git_oid, self.remote_api_oid, self.desired_oid)
return all(isinstance(value, str) for value in values) and len(set(values)) == 1
@dataclass(frozen=True, slots=True)
class ReconciliationDecision:
action: ReconciliationAction
state: str
reason: str
retry_safe: bool = False
def _valid_oid(value: Optional[str]) -> bool:
return value is None or bool(OID_RE.fullmatch(value))
def decide_reconciliation(observation: RemoteObservation) -> ReconciliationDecision:
"""Decide from a fresh observation; never complete on a missing remote."""
if observation.repository_identity != "aios/aios":
return ReconciliationDecision(ReconciliationAction.FAIL_CONFLICT, "CONFLICT", "REPOSITORY_IDENTITY_MISMATCH")
if not observation.branch.startswith("agent/"):
return ReconciliationDecision(ReconciliationAction.FAIL_CONFLICT, "CONFLICT", "BRANCH_SCOPE_DENIED")
if not _valid_oid(observation.desired_oid) or not _valid_oid(observation.expected_oid):
return ReconciliationDecision(ReconciliationAction.FAIL_CONFLICT, "CONFLICT", "OID_INVALID")
if observation.authority_oid is None:
return ReconciliationDecision(ReconciliationAction.MARK_RECONCILIATION_REQUIRED, "RECONCILIATION_REQUIRED", "AUTHORITY_UNAVAILABLE")
if observation.authority_oid != observation.desired_oid:
return ReconciliationDecision(ReconciliationAction.MARK_RECONCILIATION_REQUIRED, "RECONCILIATION_REQUIRED", "AUTHORITY_CONFLICT")
present = [oid for oid in (observation.remote_git_oid, observation.remote_api_oid) if oid is not None]
if any(oid not in {observation.desired_oid, observation.expected_oid} for oid in present):
return ReconciliationDecision(ReconciliationAction.FAIL_CONFLICT, "CONFLICT", "REMOTE_OID_CONFLICT")
if observation.all_equal and observation.commit_visible:
return ReconciliationDecision(ReconciliationAction.MARK_REMOTE_VERIFIED, "REMOTE_VERIFIED", "ALL_OIDS_EQUAL_AND_VISIBLE")
if observation.remote_git_oid is None and observation.remote_api_oid is None:
return ReconciliationDecision(ReconciliationAction.MARK_RECONCILIATION_REQUIRED, "REMOTE_PUSH_PENDING", "AUTHORITY_DESIRED_REMOTE_MISSING")
if observation.remote_git_oid == observation.desired_oid or observation.remote_api_oid == observation.desired_oid:
return ReconciliationDecision(ReconciliationAction.VERIFY_REMOTE, "REMOTE_VERIFY_PENDING", "REMOTE_PROOF_PARTIAL")
if observation.expected_oid is not None and observation.remote_git_oid == observation.expected_oid and observation.remote_api_oid == observation.expected_oid:
return ReconciliationDecision(ReconciliationAction.PUSH_EXACT, "REMOTE_PUSH_PENDING", "SAFE_EXACT_FAST_FORWARD_RETRY", retry_safe=True)
if not observation.commit_visible:
return ReconciliationDecision(ReconciliationAction.VERIFY_REMOTE, "COMMIT_VISIBILITY_PENDING", "COMMIT_VISIBILITY_LAG")
return ReconciliationDecision(ReconciliationAction.MARK_RECONCILIATION_REQUIRED, "RECONCILIATION_REQUIRED", "PARTIAL_REMOTE_OBSERVATION")
def reconcile_observation(**kwargs: object) -> ReconciliationDecision:
return decide_reconciliation(RemoteObservation(**kwargs))
+100
View File
@@ -0,0 +1,100 @@
"""Single supervised socket-activated worker for durable GitOps Jobs.
The worker consumes durable requests and reconciliation queries. Repository
mutation is a separate injected executor and remains impossible while the
legacy global write gate is false.
"""
from __future__ import annotations
import argparse
import json
import os
from pathlib import Path
import signal
import socket
import threading
from typing import Any, Mapping
from aios_gitops_job_controller import GitOpsJobController, JobError, JobState, JobStore, ServiceUnavailable
def runtime_policy(environment: Mapping[str, str] | None = None) -> dict[str, bool]:
env = environment or os.environ
legacy = env.get("AIOS_GITOPS_WRITES_ENABLED", "false").lower() in {"1", "true", "yes", "on"}
durable = env.get("AIOS_GITOPS_DURABLE_JOB_EXECUTION_ENABLED", "false").lower() in {"1", "true", "yes", "on"}
# Legacy direct Git mutation remains disabled. Authorized durable Jobs use
# their own campaign/task capability and may mutate only through the
# worker's lease/CAS path while the legacy gate stays false.
return {"legacy_global_write": legacy, "durable_job_execution": durable, "repository_mutation_allowed": durable}
def handle_request(controller: GitOpsJobController, request: Mapping[str, Any], executor: Any | None = None) -> dict[str, Any]:
job_id = str(request.get("job_id") or "")
if not job_id:
raise JobError("JOB_ID_REQUIRED")
if request.get("action") == "execute":
if executor is None:
raise JobError("JOB_EXECUTOR_UNAVAILABLE")
result = executor.execute(job_id, dry_run=bool(request.get("dry_run", False)))
receipt = controller.store._receipt(controller.store.read(job_id), duplicate=True)
return {"job": result, **receipt.as_dict()}
if request.get("action") == "resume":
receipt = controller.resume(job_id)
else:
row = controller.store.read(job_id)
receipt = controller.store._receipt(row, duplicate=True)
return receipt.as_dict()
def serve_socket(sock: socket.socket, controller: GitOpsJobController, stopping: threading.Event, executor: Any | None = None) -> None:
sock.listen(32)
sock.settimeout(1.0)
while not stopping.is_set():
try:
connection, _ = sock.accept()
except socket.timeout:
continue
with connection:
try:
raw = connection.makefile("rb").readline(1_048_576)
request = json.loads(raw.decode("utf-8"))
response = {"status": "ok", **handle_request(controller, request, executor)}
except ServiceUnavailable as exc:
response = {"status": "error", "error": str(exc), "retryable": True, "side_effect_state": "UNKNOWN"}
except Exception as exc:
response = {"status": "error", "error": type(exc).__name__, "retryable": False}
connection.sendall((json.dumps(response, sort_keys=True) + "\n").encode("utf-8"))
def socket_from_systemd() -> socket.socket:
if os.environ.get("LISTEN_FDS") != "1":
raise RuntimeError("SOCKET_ACTIVATION_REQUIRED")
return socket.fromfd(3, socket.AF_UNIX, socket.SOCK_STREAM)
def main(argv: list[str] | None = None) -> int:
parser = argparse.ArgumentParser()
parser.add_argument("--job-root", required=True, type=Path)
args = parser.parse_args(argv)
policy = runtime_policy()
if policy["legacy_global_write"]:
raise RuntimeError("LEGACY_GLOBAL_WRITES_MUST_REMAIN_FALSE")
if not policy["durable_job_execution"]:
raise RuntimeError("DURABLE_JOB_EXECUTION_GATE_CLOSED")
controller = GitOpsJobController(JobStore(args.job_root))
from aios_gitops_job_executor import build_from_environment
executor = build_from_environment(args.job_root)
stopping = threading.Event()
signal.signal(signal.SIGTERM, lambda *_: stopping.set())
signal.signal(signal.SIGINT, lambda *_: stopping.set())
sock = socket_from_systemd()
try:
serve_socket(sock, controller, stopping, executor)
finally:
sock.close()
return 0
if __name__ == "__main__":
raise SystemExit(main())
@@ -0,0 +1,59 @@
"""Central fail-closed policy seam for governed task and continuation requests."""
from __future__ import annotations
from dataclasses import dataclass
import re
from typing import Any, Mapping
OID_RE = re.compile(r"^[0-9a-f]{40}$")
@dataclass(frozen=True, slots=True)
class GovernedPolicyInput:
identity: str
task_id: str
repository: str
operation: str
base_oid: str
expected_branch_oid: str
target_branch: str
generation: int
risk: str = "engineering"
requested_capabilities: tuple[str, ...] = ()
previous_generation: int | None = None
@dataclass(frozen=True, slots=True)
class PolicyDecision:
decision: str
reason: str
def as_dict(self) -> dict[str, str]:
return {"decision": self.decision, "reason": self.reason}
def evaluate_policy(value: GovernedPolicyInput | Mapping[str, Any]) -> PolicyDecision:
if isinstance(value, Mapping):
value = GovernedPolicyInput(**value)
if value.repository != "aios/aios":
return PolicyDecision("DENY", "REPOSITORY_IDENTITY_MISMATCH")
if not OID_RE.fullmatch(value.base_oid) or not OID_RE.fullmatch(value.expected_branch_oid):
return PolicyDecision("DENY", "OID_INVALID")
if not value.target_branch.startswith("agent/") or value.target_branch in {"main", "master"}:
return PolicyDecision("DENY", "BRANCH_SCOPE_DENIED")
if "force_push" in value.requested_capabilities or "force" in value.operation.lower():
return PolicyDecision("DENY", "FORCE_PUSH_DENIED")
if {"main_write", "tag_write", "baseline_write", "production_write", "production_deploy", "tag_mutation", "baseline_mutation"}.intersection(value.requested_capabilities):
return PolicyDecision("DENY", "MUTATION_SCOPE_DENIED")
if not isinstance(value.generation, int) or isinstance(value.generation, bool) or value.generation < 1:
return PolicyDecision("DENY", "GENERATION_INVALID")
if value.previous_generation is not None and value.generation <= value.previous_generation:
return PolicyDecision("MANUAL_OWNER_REVIEW_REQUIRED", "GENERATION_NOT_MONOTONIC")
if not value.task_id:
return PolicyDecision("DENY", "TASK_ID_INVALID")
return PolicyDecision("ALLOW", "GOVERNED_ENGINEERING_SCOPE")
class GovernedPolicy:
def decide(self, request: GovernedPolicyInput | Mapping[str, Any]) -> PolicyDecision:
return evaluate_policy(request)
@@ -0,0 +1,84 @@
"""Durable governed task binding for CREATE_NEW and CONTINUE_EXISTING."""
from __future__ import annotations
from dataclasses import dataclass, asdict
import hashlib
import json
from pathlib import Path
import re
from typing import Any, Mapping
from aios_gitops_job_controller import GitOpsJobController, JobStore
OID_RE = re.compile(r"^[0-9a-f]{40}$")
BRANCH_RE = re.compile(r"^agent/[A-Za-z0-9][A-Za-z0-9._/-]{2,179}$")
class BootstrapError(RuntimeError):
pass
@dataclass(frozen=True, slots=True)
class GovernedTaskBinding:
task_id: str
repository: str
base_oid: str
target_branch: str
risk: str
description: str
idempotency_key: str
expected_branch_oid: str
branch_mode: str
generation: int
job_id: str | None = None
def response(self, *, idempotent: bool = False) -> dict[str, Any]:
return {"task_id": self.task_id, "repository": self.repository, "base_oid": self.base_oid, "target_branch": self.target_branch, "expected_branch_oid": self.expected_branch_oid, "generation": self.generation, "branch_mode": self.branch_mode, "create_target_branch": self.branch_mode == "CREATE_NEW", "worktree_mode": "detached_at_base_oid" if self.branch_mode == "CREATE_NEW" else "detached_at_expected_head", "cas_ref_update_required": True, "idempotent": idempotent, "state": "BOUND", "job_id": self.job_id}
def _safe(value: str) -> str:
return re.sub(r"[^A-Za-z0-9_.-]+", "-", value).strip("-")[:120] or "task"
class GovernedTaskBootstrap:
def __init__(self, authority: Any, root: Path, *, remote_name: str, token_path: Path, ssh_key_path: Path, api_base: str = "https://g.cnnetmkt.top/git/api/v1") -> None:
self.authority = authority; self.root = Path(root); self.remote_name = remote_name; self.token_path = Path(token_path); self.ssh_key_path = Path(ssh_key_path); self.api_base = api_base; self.task_meta_root = self.root / "task-bindings"; self.job_root = self.root / "jobs"; self.task_meta_root.mkdir(parents=True, exist_ok=True); self.job_root.mkdir(parents=True, exist_ok=True)
def _store(self, task_id: str) -> tuple[JobStore, GitOpsJobController]:
root = self.job_root / _safe(task_id); store = JobStore(root, payload_root=root / "payloads"); return store, GitOpsJobController(store)
def _metadata(self, task_id: str) -> Path:
return self.task_meta_root / (_safe(task_id) + ".json")
def _read_existing(self, task_id: str) -> GovernedTaskBinding | None:
path = self._metadata(task_id)
if not path.is_file() or path.is_symlink(): return None
return GovernedTaskBinding(**json.loads(path.read_text(encoding="utf-8")))
def _write(self, binding: GovernedTaskBinding) -> None:
path = self._metadata(binding.task_id); temporary = path.with_suffix(".tmp"); temporary.write_text(json.dumps(asdict(binding), sort_keys=True), encoding="utf-8"); temporary.replace(path)
def create(self, request: Mapping[str, Any]) -> dict[str, Any]:
values = {key: str(request[key]) for key in ("task_id", "repository", "base_oid", "target_branch", "risk", "description", "idempotency_key")}
if values["repository"] != "aios/aios" or not OID_RE.fullmatch(values["base_oid"]): raise BootstrapError("TASK_BINDING_INVALID")
if not BRANCH_RE.fullmatch(values["target_branch"]): raise BootstrapError("TARGET_BRANCH_INVALID")
existing = self._read_existing(values["task_id"])
if existing is not None:
if any(getattr(existing, key) != values[key] for key in ("repository", "base_oid", "target_branch", "risk", "description")): raise BootstrapError("TASK_BINDING_CONFLICT")
return existing.response(idempotent=True)
if not self.authority.commit_exists(values["base_oid"]): raise BootstrapError("BASE_COMMIT_NOT_FOUND")
current = self.authority.read_ref("refs/heads/" + values["target_branch"])
expected = str(request.get("expected_branch_oid") or values["base_oid"])
if current is None:
mode, generation = "CREATE_NEW", 1
if expected != values["base_oid"]: raise BootstrapError("EXPECTED_BASE_CONFLICT")
else:
if expected != current: raise BootstrapError("EXISTING_BRANCH_OID_CONFLICT")
mode, generation = "CONTINUE_EXISTING", int(request.get("generation", 2))
if generation < 1: raise BootstrapError("GENERATION_INVALID")
payload = json.dumps(values, sort_keys=True, separators=(",", ":")).encode()
_, controller = self._store(values["task_id"])
receipt = controller.submit({"idempotency_key": values["idempotency_key"], "campaign_id": values["task_id"], "task_id": values["task_id"], "repository": values["repository"], "operation": "GOVERNED_TASK_CREATE", "branch": values["target_branch"], "expected_branch_oid": expected, "payload_digest": hashlib.sha256(payload).hexdigest(), "lineage_anchor_oid": values["base_oid"]}, payload=payload)
binding = GovernedTaskBinding(**values, expected_branch_oid=expected, branch_mode=mode, generation=generation, job_id=receipt.job_id)
self._write(binding)
return binding.response()
File diff suppressed because it is too large Load Diff
@@ -951,3 +951,51 @@ class AuthorizedTaskWriterProvisioner:
return {**public_result, "prestart": prestart}
finally:
shutil.rmtree(temporary_root, ignore_errors=True)
# v2 continuation validation is intentionally separate from the legacy Owner
# bootstrap path above. It consumes a recovered campaign authorization and
# never creates a branch or asks the Owner to authorize again.
from aios_continuation_lease import WriterLeaseRequestV2
def validate_writer_lease_request_v2(raw: Mapping[str, Any], probe: Any) -> dict[str, Any]:
"""Validate a CONTINUE_EXISTING request against the live exact branch head."""
required = {
"schema",
"task_id",
"repository",
"branch_mode",
"base_oid",
"expected_branch_oid",
"target_branch",
"generation",
"lease_seconds",
}
if set(raw) != required or raw.get("schema") not in WriterLeaseRequestV2.accepted_schemas:
raise ProvisioningError("LEASE_REQUEST_V2_FIELDS_REJECTED", 409)
request = WriterLeaseRequestV2(
task_id=str(raw["task_id"]),
repository=str(raw["repository"]),
branch_mode=str(raw["branch_mode"]),
base_oid=str(raw["base_oid"]),
expected_branch_oid=str(raw["expected_branch_oid"]),
target_branch=str(raw["target_branch"]),
generation=raw["generation"],
lease_seconds=raw["lease_seconds"],
)
request.validate(probe)
return {
"schema": WriterLeaseRequestV2.schema,
"task_id": request.task_id,
"repository": request.repository,
"branch_mode": request.branch_mode,
"base_oid": request.base_oid,
"expected_branch_oid": request.expected_branch_oid,
"target_branch": request.target_branch,
"generation": request.generation,
"lease_seconds": request.lease_seconds,
"create_target_branch": request.branch_mode == "CREATE_NEW",
"worktree_mode": "detached_at_base_oid" if request.branch_mode == "CREATE_NEW" else "detached_at_expected_head",
"cas_ref_update_required": True,
}
+84
View File
@@ -0,0 +1,84 @@
#!/usr/bin/env python3
"""Single canonical Production Baseline authority for the GitOps reader.
The API and the one-shot writer import this module. Workspace and release
copies are projections only; they are never selected as a fallback source.
"""
from __future__ import annotations
import hashlib
import json
import os
from pathlib import Path
from typing import Any
AUTHORITY_PATH = Path(os.getenv(
"AIOS_GITOPS_BASELINE_AUTHORITY",
"/var/lib/aios-gitops-runtime-anchor/baseline/current.json",
))
AUTHORITY_SHA_PATH = AUTHORITY_PATH.with_suffix(".sha256")
class BaselineAuthorityError(RuntimeError):
"""Fail-closed error raised for an unavailable or inconsistent authority."""
class BaselineAuthority:
def __init__(self, path: Path | str = AUTHORITY_PATH):
self.path = Path(path)
self.sha_path = self.path.with_suffix(".sha256")
def _raw(self) -> bytes:
try:
raw = self.path.read_bytes()
except OSError as exc:
raise BaselineAuthorityError(f"authority_unavailable:{exc}") from exc
if not raw:
raise BaselineAuthorityError("authority_empty")
return raw
def read(self) -> dict[str, Any]:
raw = self._raw()
digest = hashlib.sha256(raw).hexdigest()
try:
value = json.loads(raw.decode("utf-8"))
except (UnicodeDecodeError, json.JSONDecodeError) as exc:
raise BaselineAuthorityError("authority_invalid_json") from exc
if not isinstance(value, dict):
raise BaselineAuthorityError("authority_not_object")
expected = str(value.get("authority_sha256", ""))
if expected and expected != digest:
raise BaselineAuthorityError("authority_embedded_digest_mismatch")
if self.sha_path.exists():
sidecar = self.sha_path.read_text(encoding="utf-8").strip().split()[0]
if sidecar != digest:
raise BaselineAuthorityError("authority_sidecar_digest_mismatch")
required = {
"current_baseline", "previous_baseline", "target_oid", "tag_oid",
"evidence_closure_oid", "approval_plan_id", "status", "generation",
"source_identity",
}
missing = sorted(required.difference(value))
if missing:
raise BaselineAuthorityError("authority_missing:" + ",".join(missing))
value = dict(value)
value["authority_sha256"] = digest
value["authority_path"] = str(self.path)
return value
def read_consistent(self, policy: dict[str, Any] | None = None) -> dict[str, Any]:
value = self.read()
if policy:
expected = policy.get("release_binding", policy)
for key, field in (("version", "current_baseline"),
("previous_version", "previous_baseline"),
("target_oid", "target_oid"),
("tag_oid", "tag_oid"),
("evidence_closure_oid", "evidence_closure_oid"),
("approval_plan_id", "approval_plan_id")):
wanted = expected.get(key)
if wanted is not None and value.get(field) != wanted:
raise BaselineAuthorityError(f"authority_binding_mismatch:{field}")
return value
+108
View File
@@ -0,0 +1,108 @@
"""Atomic, predicate-bound activation of the durable release campaign."""
from __future__ import annotations
import hashlib
import json
import os
from pathlib import Path
import re
import tempfile
from typing import Any, Mapping
from release_campaign_authorization import CampaignError, validate_policy
OID_RE = re.compile(r"^[0-9a-f]{40}$")
CAMPAIGN_ID = "AIOS-AUTONOMOUS-RELEASE-CAMPAIGN-20260807"
class CampaignActivationError(CampaignError):
pass
def canonical_json(value: Any) -> bytes:
return (json.dumps(value, sort_keys=True, separators=(",", ":"), ensure_ascii=False) + "\n").encode("utf-8")
def digest_bytes(value: bytes) -> str:
return hashlib.sha256(value).hexdigest()
def build_active_policy(current: Mapping[str, Any], facts: Mapping[str, Any]) -> dict[str, Any]:
policy = validate_policy(dict(current))
required = {
"campaign_id",
"repository",
"source_main_oid",
"gitea_main_oid",
"authority_main_oid",
"runtime_source_oid",
"durable_job_state",
"writer_ready",
"gitea_machine_authenticated",
"governance_executor_ready",
"preapproval_publisher_ready",
"runtime_exact_main",
"main_lineage_verified",
"campaign_revoked",
}
if set(facts) != required:
raise CampaignActivationError("ACTIVATION_FACTS_SCHEMA_INVALID")
if policy["activation_state"] != "PENDING_CONTROL_PLANE_MERGE" or policy["active"] is not False:
raise CampaignActivationError("CAMPAIGN_NOT_PENDING")
if policy["campaign_id"] != CAMPAIGN_ID or facts["campaign_id"] != CAMPAIGN_ID:
raise CampaignActivationError("CAMPAIGN_ID_MISMATCH")
if facts["repository"] != "aios/aios" or policy["repository"] != facts["repository"]:
raise CampaignActivationError("REPOSITORY_MISMATCH")
for name in ("source_main_oid", "gitea_main_oid", "authority_main_oid", "runtime_source_oid"):
if not isinstance(facts[name], str) or not OID_RE.fullmatch(facts[name]):
raise CampaignActivationError("ACTIVATION_OID_INVALID")
if not (
facts["source_main_oid"]
== facts["gitea_main_oid"]
== facts["authority_main_oid"]
== facts["runtime_source_oid"]
):
raise CampaignActivationError("ACTIVATION_MAIN_DIVERGENCE")
if facts["durable_job_state"] != "COMPLETED":
raise CampaignActivationError("DURABLE_JOB_NOT_COMPLETED")
boolean_fields = (
"writer_ready",
"gitea_machine_authenticated",
"governance_executor_ready",
"preapproval_publisher_ready",
"runtime_exact_main",
"main_lineage_verified",
)
if any(facts[name] is not True for name in boolean_fields) or facts["campaign_revoked"] is not False:
raise CampaignActivationError("ACTIVATION_PREDICATE_FAILED")
activated = dict(policy)
activated["activation_state"] = "ACTIVE"
activated["active"] = True
return validate_policy(activated)
def atomic_write_policy(path: Path, sidecar: Path, receipt_path: Path, policy: Mapping[str, Any], receipt: Mapping[str, Any]) -> str:
if path.is_symlink() or sidecar.is_symlink() or receipt_path.exists() or receipt_path.is_symlink():
raise CampaignActivationError("ACTIVATION_PATH_INVALID")
path.parent.mkdir(parents=True, exist_ok=True)
raw = canonical_json(dict(policy))
digest = digest_bytes(raw)
for target, data, mode in (
(path, raw, "wb"),
(sidecar, f"{digest} {path.name}\n".encode("ascii"), "wb"),
(receipt_path, canonical_json(dict(receipt)), "wb"),
):
fd, temporary = tempfile.mkstemp(prefix=f".{target.name}.", dir=target.parent)
try:
os.fchmod(fd, 0o600)
with os.fdopen(fd, mode) as stream:
stream.write(data)
stream.flush()
os.fsync(stream.fileno())
os.replace(temporary, target)
os.chmod(target, 0o600)
finally:
if os.path.exists(temporary):
os.unlink(temporary)
return digest
+106
View File
@@ -0,0 +1,106 @@
#!/usr/bin/env python3
"""Machine campaign approval gate for protected engineering PRs."""
from __future__ import annotations
import argparse
import json
from pathlib import Path
import subprocess
from typing import Any, Mapping
from release_campaign_authorization import CampaignAuthorization, CampaignError
class GateError(RuntimeError):
pass
def git_is_ancestor(repository: Path, ancestor: str, descendant: str) -> bool:
result = subprocess.run(
[
"git",
f"--git-dir={repository}",
"merge-base",
"--is-ancestor",
ancestor,
descendant,
],
check=False,
capture_output=True,
)
if result.returncode == 0:
return True
if result.returncode == 1:
return False
raise GateError("git_ancestor_check_failed")
def evaluate(
campaign: CampaignAuthorization,
facts: Mapping[str, Any],
repository: Path,
) -> dict[str, Any]:
required = {
"repository",
"pr_number",
"base_ref",
"base_oid",
"head_oid",
"required_checks_all_success",
"required_checks_same_head",
"unresolved_conversations",
"mergeable",
"protected_target",
}
if set(facts) != required:
raise GateError("pr_facts_fields_invalid")
if facts["repository"] != "aios/aios":
raise GateError("repository_mismatch")
if facts["base_ref"] != "refs/heads/main" or facts["protected_target"] != "main":
raise GateError("protected_main_required")
if facts["required_checks_all_success"] is not True:
raise GateError("required_checks_not_success")
if facts["required_checks_same_head"] is not True:
raise GateError("required_checks_stale")
if facts["unresolved_conversations"] != 0:
raise GateError("unresolved_conversations")
if facts["mergeable"] is not True:
raise GateError("pr_not_mergeable")
if not git_is_ancestor(repository, campaign.anchor_oid, str(facts["base_oid"])):
raise GateError("base_outside_campaign_lineage")
if not git_is_ancestor(repository, str(facts["base_oid"]), str(facts["head_oid"])):
raise GateError("head_outside_pr_lineage")
campaign.assert_operation("protected_merge")
return {
"schema": "aios.campaign-approval-gate-result.v1",
"status": "SUCCESS",
"campaign_id": campaign.campaign_id,
"pr_number": facts["pr_number"],
"head_oid": facts["head_oid"],
"owner_ui_interaction_required": False,
}
def main() -> int:
p = argparse.ArgumentParser()
p.add_argument("--campaign", type=Path, required=True)
p.add_argument("--revocation", type=Path)
p.add_argument("--pr-facts", type=Path, required=True)
p.add_argument("--repository", type=Path, required=True)
a = p.parse_args()
campaign = CampaignAuthorization.load(
a.campaign,
revocation=a.revocation,
require_active=True,
)
facts = json.loads(a.pr_facts.read_text(encoding="utf-8"))
print(json.dumps(evaluate(campaign, facts, a.repository), sort_keys=True))
return 0
if __name__ == "__main__":
try:
raise SystemExit(main())
except (CampaignError, GateError) as exc:
print(json.dumps({"status": "FAILURE", "reason": str(exc)}, sort_keys=True))
raise SystemExit(2)
+150
View File
@@ -0,0 +1,150 @@
#!/usr/bin/env python3
"""Automatically accept an exact release binding under an ACTIVE campaign."""
from __future__ import annotations
import argparse
import hashlib
import json
import os
from pathlib import Path
import stat
import tempfile
from typing import Any, Mapping
from release_campaign_authorization import CampaignAuthorization, CampaignError
class AcceptanceError(RuntimeError):
pass
def secure_json(path: Path) -> dict[str, Any]:
if path.is_symlink() or not path.is_file():
raise AcceptanceError(f"invalid_file:{path}")
st = path.lstat()
if st.st_uid != 0 or stat.S_IMODE(st.st_mode) & 0o022:
raise AcceptanceError(f"unsafe_file:{path}")
value = json.loads(path.read_text(encoding="utf-8"))
if not isinstance(value, dict):
raise AcceptanceError(f"json_object_required:{path}")
return value
def atomic_new(path: Path, value: Mapping[str, Any]) -> None:
if path.exists() or path.is_symlink():
raise AcceptanceError("acceptance_receipt_exists")
path.parent.mkdir(parents=True, exist_ok=True)
fd, name = tempfile.mkstemp(prefix=f".{path.name}.", dir=path.parent)
try:
os.fchmod(fd, 0o600)
if os.geteuid() == 0:
os.fchown(fd, 0, 0)
with os.fdopen(fd, "w", encoding="utf-8", closefd=True) as stream:
json.dump(dict(value), stream, sort_keys=True, indent=2)
stream.write("\n")
stream.flush()
os.fsync(stream.fileno())
os.replace(name, path)
os.chmod(path, 0o600)
finally:
try:
os.unlink(name)
except FileNotFoundError:
pass
def evaluate(
campaign: CampaignAuthorization,
binding: Mapping[str, Any],
evidence: Mapping[str, Any],
artifact: Mapping[str, Any],
staging: Mapping[str, Any],
main: Mapping[str, Any],
tag: Mapping[str, Any],
) -> dict[str, Any]:
required_binding = {
"version",
"target_oid",
"evidence_closure_oid",
"artifact_sha256",
"artifact_size",
"approval_plan_id",
}
if set(binding) != required_binding:
raise AcceptanceError("binding_fields_invalid")
if main.get("repository") != "aios/aios":
raise AcceptanceError("repository_identity_mismatch")
if main.get("target_in_main_lineage") is not True:
raise AcceptanceError("target_outside_main_lineage")
if evidence.get("object_type") != "commit":
raise AcceptanceError("evidence_not_commit")
if evidence.get("target_is_ancestor") is not True:
raise AcceptanceError("target_not_evidence_ancestor")
if evidence.get("closure_oid") != binding["evidence_closure_oid"]:
raise AcceptanceError("evidence_oid_mismatch")
if artifact.get("sha256") != binding["artifact_sha256"]:
raise AcceptanceError("artifact_digest_mismatch")
if artifact.get("size") != binding["artifact_size"]:
raise AcceptanceError("artifact_size_mismatch")
if artifact.get("retained") is not True:
raise AcceptanceError("artifact_not_retained")
if staging.get("install") != "PASS":
raise AcceptanceError("staging_install_failed")
if staging.get("health") != "PASS":
raise AcceptanceError("staging_health_failed")
if staging.get("restart") != "PASS":
raise AcceptanceError("staging_restart_failed")
if staging.get("rollback_readiness") != "PASS":
raise AcceptanceError("rollback_readiness_failed")
if tag.get("conflict") is not False:
raise AcceptanceError("tag_conflict")
campaign.assert_operation("derive_campaign_acceptance")
return {
"schema": "aios.campaign-release-acceptance.v1",
"accepted": True,
"binding_replaced": False,
"campaign_id": campaign.campaign_id,
"campaign_generation": campaign.data["generation"],
**dict(binding),
"owner_interaction_required": False,
}
def main() -> int:
p = argparse.ArgumentParser()
p.add_argument("--campaign", type=Path, required=True)
p.add_argument("--revocation", type=Path)
p.add_argument("--binding", type=Path, required=True)
p.add_argument("--evidence", type=Path, required=True)
p.add_argument("--artifact", type=Path, required=True)
p.add_argument("--staging", type=Path, required=True)
p.add_argument("--main-facts", type=Path, required=True)
p.add_argument("--tag-facts", type=Path, required=True)
p.add_argument("--output", type=Path, required=True)
a = p.parse_args()
campaign = CampaignAuthorization.load(
a.campaign,
revocation=a.revocation,
require_active=True,
)
receipt = evaluate(
campaign,
secure_json(a.binding),
secure_json(a.evidence),
secure_json(a.artifact),
secure_json(a.staging),
secure_json(a.main_facts),
secure_json(a.tag_facts),
)
atomic_new(a.output, receipt)
print(json.dumps(receipt, sort_keys=True))
return 0
if __name__ == "__main__":
try:
raise SystemExit(main())
except (CampaignError, AcceptanceError) as exc:
print(json.dumps({"status": "BLOCKED", "reason": str(exc)}, sort_keys=True))
raise SystemExit(2)
@@ -0,0 +1 @@
"""Permanent AIOS governance source components."""
@@ -0,0 +1,75 @@
#!/usr/bin/env python3
import json
import pathlib
import socket
import time
SOCKET = '/run/aios-governance-executor/executor.sock'
STATE = pathlib.Path('/var/lib/aios-gitops/governance-executor/drift-monitor.json')
APPROVED = pathlib.Path('/var/lib/aios-gitops/runtime/tasks/AIOS-PROTECTION-SNAPSHOT-DRIFT-FORENSICS-V1/protection-drift/semantic-analysis.json')
def call():
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
sock.settimeout(20)
sock.connect(SOCKET)
request = {'operation': 'GET_BRANCH_PROTECTION', 'payload': {'repository': 'aios/aios', 'branch': 'main'}}
sock.sendall((json.dumps(request, separators=(',', ':')) + '\n').encode())
chunks = []
while True:
part = sock.recv(1024 * 1024)
if not part:
break
chunks.append(part)
if b'\n' in part:
break
sock.close()
return json.loads(b''.join(chunks).splitlines()[0])
def main():
try:
result = call()
if result.get('status') != 'PASS':
raise RuntimeError(str(result.get('code') or 'EXECUTOR_GET_FAILED'))
approved = json.loads(APPROVED.read_text(encoding='utf-8'))
approved_transport = approved['approved_transport_digest_from_evidence']
approved_semantic = approved['approved_semantic_digest']
transport = result['transport_digest']
semantic = result['semantic_digest']
if semantic != approved_semantic:
state = 'GOVERNANCE_POLICY_DRIFT'
elif transport != approved_transport:
state = 'GOVERNANCE_TRANSPORT_METADATA_CHANGED'
else:
state = 'NO_DRIFT'
payload = {
'schema': 'aios.governance-drift-monitor.v1',
'observed_at_unix': int(time.time()),
'status': state,
'release_blocking': False,
'approved_transport_digest': approved_transport,
'observed_transport_digest': transport,
'approved_semantic_digest': approved_semantic,
'observed_semantic_digest': semantic,
'semantic_schema': result.get('semantic_schema'),
'source': 'Dedicated Governance Executor GET_BRANCH_PROTECTION',
}
except Exception as exc:
payload = {
'schema': 'aios.governance-drift-monitor.v1',
'observed_at_unix': int(time.time()),
'status': 'MONITOR_UNAVAILABLE',
'release_blocking': False,
'error_code': str(exc),
}
STATE.parent.mkdir(parents=True, exist_ok=True)
temporary = STATE.with_suffix('.tmp')
temporary.write_text(json.dumps(payload, sort_keys=True, separators=(',', ':'), ensure_ascii=False) + '\n', encoding='utf-8')
temporary.replace(STATE)
STATE.chmod(0o640)
print(json.dumps(payload, sort_keys=True, separators=(',', ':'), ensure_ascii=False))
if __name__ == '__main__':
main()
+417
View File
@@ -0,0 +1,417 @@
#!/usr/bin/env python3
import base64
import copy
import hashlib
import json
import os
import pathlib
import socket
import time
import urllib.error
import urllib.parse
import urllib.request
SOURCE_GOVERNANCE = "/opt/aios/aios-control-plane/current/active-runtime/app/governance"
if SOURCE_GOVERNANCE not in __import__("sys").path:
__import__("sys").path.insert(0, SOURCE_GOVERNANCE)
from stage2_gate_migration import (
CAMPAIGN_GATE,
CAMPAIGN_ID,
OWNER_GATE,
STAGE1_SEMANTIC_SHA,
Stage2GateError,
build_target,
semantic_digest as stage2_semantic_digest,
validate_stage2_facts,
)
API = "http://127.0.0.1:3000"
REPO = "aios/aios"
SOCKET = "/run/aios-governance-executor/executor.sock"
JOURNAL = pathlib.Path("/var/lib/aios-gitops/governance-executor/executor-journal.jsonl")
RECONCILIATION_BINDING_PATH = pathlib.Path("/var/lib/aios-gitops/governance-executor/reconciliation-binding.json")
PR54_HEAD = "015185d7e5191d18c5c1596160807ac228782a0a"
PR54_REVIEW = 104
PR54_MANIFEST_SHA = "1b11ee42d53a9f6c1646e5f5ddd8ce5b7ecf301483b9f3d47a068290d541ba4b"
PR54_PREVIOUS_PROTECTION_SHA = "8b3370add21cd23c2b4f41d8d84d3eaaa09ddf0451549b72c7941bbd92d5d60a"
SEMANTIC_SCHEMA = "aios.semantic-protection.v1"
APPROVED_SEMANTIC_SHA = "ba15d512ed8c1f43e5f72c84125a3a71a7347ef59e18000bbfca24d6e69f1b19"
STAGE1_SEMANTIC_SHA = "d911e856f71de6105b8efa08e13338a448e0b6d37536332decd80c9f409ecf27"
USER_IDS = {"owner": 2, "aios-autonomy-agent": 153}
def canon(value):
return json.dumps(value, sort_keys=True, separators=(",", ":"), ensure_ascii=False).encode()
def sha(value):
return hashlib.sha256(canon(value)).hexdigest()
def token():
value = (pathlib.Path(os.environ["CREDENTIALS_DIRECTORY"]) / "gitea-governance-executor-token").read_text().strip()
if len(value) < 16:
raise RuntimeError("EXECUTOR_CREDENTIAL_INVALID")
return value
def api(method, path, value, body=None, expected=(200,)):
raw = None if body is None else json.dumps(body, separators=(",", ":")).encode()
req = urllib.request.Request(API + path, data=raw, method=method)
req.add_header("Authorization", "token " + value)
req.add_header("Accept", "application/json")
if raw is not None:
req.add_header("Content-Type", "application/json")
try:
with urllib.request.urlopen(req, timeout=20) as response:
status = response.status
payload = response.read()
except urllib.error.HTTPError as exc:
if exc.code in expected:
payload = exc.read()
return exc.code, json.loads(payload) if payload else None
raise RuntimeError("GITEA_HTTP_" + str(exc.code))
if status not in expected:
raise RuntimeError("GITEA_HTTP_" + str(status))
if not payload or not payload.strip():
raise RuntimeError("GITEA_EMPTY_JSON:" + path)
try:
decoded = json.loads(payload)
except (UnicodeDecodeError, json.JSONDecodeError) as exc:
raise RuntimeError("GITEA_INVALID_JSON:" + path) from exc
return status, decoded
def journal(event, **fields):
JOURNAL.parent.mkdir(parents=True, exist_ok=True)
record = {"schema": "aios.governance-executor-journal.v2", "event": event, "unix": int(time.time()), **fields}
with JOURNAL.open("a", encoding="utf-8") as stream:
stream.write(json.dumps(record, sort_keys=True, separators=(",", ":"), ensure_ascii=False) + "\n")
stream.flush()
os.fsync(stream.fileno())
def typed_json(value):
if value in (None, "null"):
return []
if isinstance(value, str):
return json.loads(value)
return value
def user_ids(names):
result = []
for name in names or []:
if name not in USER_IDS:
raise RuntimeError("UNKNOWN_GOVERNANCE_USER:" + str(name))
result.append(USER_IDS[name])
return sorted(result)
def semantic_policy(value):
policy = {
"schema_version": SEMANTIC_SCHEMA,
"repo_id": 1,
"protection_id": 2,
"branch_name": value.get("branch_name"),
"can_push": value.get("enable_push"),
"can_force_push": value.get("enable_force_push"),
"enable_merge_whitelist": value.get("enable_merge_whitelist"),
"merge_whitelist_user_ids": user_ids(value.get("merge_whitelist_usernames")),
"merge_whitelist_team_ids": [],
"required_approvals": value.get("required_approvals"),
"enable_status_check": value.get("enable_status_check"),
"status_check_contexts": sorted(value.get("status_check_contexts") or []),
"block_admin_merge_override": value.get("block_admin_merge_override"),
"block_on_official_review_requests": value.get("block_on_official_review_requests"),
"block_on_outdated_branch": value.get("block_on_outdated_branch"),
"block_on_rejected_reviews": value.get("block_on_rejected_reviews"),
"dismiss_stale_approvals": value.get("dismiss_stale_approvals"),
"ignore_stale_approvals": value.get("ignore_stale_approvals"),
"enable_approvals_whitelist": value.get("enable_approvals_whitelist"),
"approvals_whitelist_user_ids": user_ids(value.get("approvals_whitelist_username")),
"approvals_whitelist_team_ids": [],
"enable_bypass_allowlist": value.get("enable_bypass_allowlist"),
"bypass_allowlist_user_ids": user_ids(value.get("bypass_allowlist_usernames")),
"bypass_allowlist_team_ids": [],
"enable_force_push_allowlist": value.get("enable_force_push_allowlist"),
"force_push_allowlist_user_ids": user_ids(value.get("force_push_allowlist_usernames")),
"force_push_allowlist_team_ids": [],
"enable_whitelist": value.get("enable_push_whitelist"),
"whitelist_user_ids": user_ids(value.get("push_whitelist_usernames")),
"whitelist_team_ids": [],
"force_push_allowlist_deploy_keys": value.get("force_push_allowlist_deploy_keys"),
"whitelist_deploy_keys": value.get("push_whitelist_deploy_keys"),
"require_signed_commits": value.get("require_signed_commits"),
"priority": value.get("priority"),
"protected_file_patterns": value.get("protected_file_patterns"),
"unprotected_file_patterns": value.get("unprotected_file_patterns"),
}
return policy
def semantic_sha(value):
return sha(semantic_policy(value))
def verify_binding(binding, value):
required = {
"governance_request_PR", "exact_request_head", "Owner_review_id", "Owner_review_exact_head",
"manifest_sha256", "expected_previous_snapshot_sha256", "approved_delta",
"semantic_reconciliation_request_id", "semantic_reconciliation_head",
"semantic_reconciliation_review_id", "semantic_reconciliation_review_head",
"approved_semantic_digest",
}
if set(binding) != required:
raise RuntimeError("GOVERNANCE_BINDING_SCHEMA_INVALID")
if binding["governance_request_PR"] != 54 or binding["exact_request_head"] != PR54_HEAD:
raise RuntimeError("GOVERNANCE_BINDING_PR54_MISMATCH")
if binding["Owner_review_id"] != PR54_REVIEW or binding["Owner_review_exact_head"] != PR54_HEAD:
raise RuntimeError("GOVERNANCE_BINDING_REVIEW_MISMATCH")
if binding["manifest_sha256"] != PR54_MANIFEST_SHA:
raise RuntimeError("GOVERNANCE_BINDING_MANIFEST_MISMATCH")
if binding["expected_previous_snapshot_sha256"] != PR54_PREVIOUS_PROTECTION_SHA:
raise RuntimeError("GOVERNANCE_BINDING_SNAPSHOT_MISMATCH")
if binding["approved_delta"] != {"add_merge_whitelist_user_id": 153}:
raise RuntimeError("GOVERNANCE_BINDING_DELTA_DENIED")
if binding["approved_semantic_digest"] != APPROVED_SEMANTIC_SHA:
raise RuntimeError("GOVERNANCE_BINDING_SEMANTIC_DIGEST_MISMATCH")
if not RECONCILIATION_BINDING_PATH.is_file():
raise RuntimeError("SEMANTIC_RECONCILIATION_BINDING_REQUIRED")
try:
reconciliation = json.loads(RECONCILIATION_BINDING_PATH.read_text(encoding="utf-8"))
except (OSError, ValueError):
raise RuntimeError("SEMANTIC_RECONCILIATION_BINDING_INVALID") from None
required_reconciliation = {
"schema", "request_id", "pr_number", "request_head", "owner_review_id",
"owner_review_head", "approved_semantic_digest", "approved_delta",
}
if set(reconciliation) != required_reconciliation or reconciliation.get("schema") != "aios.semantic-reconciliation-binding.v1":
raise RuntimeError("SEMANTIC_RECONCILIATION_BINDING_INVALID")
if reconciliation["approved_semantic_digest"] != APPROVED_SEMANTIC_SHA or reconciliation["approved_delta"] != binding["approved_delta"]:
raise RuntimeError("SEMANTIC_RECONCILIATION_BINDING_MISMATCH")
if (
reconciliation["request_id"] != binding["semantic_reconciliation_request_id"]
or reconciliation["request_head"] != binding["semantic_reconciliation_head"]
or reconciliation["owner_review_id"] != binding["semantic_reconciliation_review_id"]
or reconciliation["owner_review_head"] != binding["semantic_reconciliation_review_head"]
):
raise RuntimeError("SEMANTIC_RECONCILIATION_BINDING_MISMATCH")
_, reconciliation_pr = api("GET", f"/api/v1/repos/{REPO}/pulls/{reconciliation['pr_number']}", value)
if (
reconciliation_pr.get("state") != "open"
or reconciliation_pr.get("merged")
or reconciliation_pr.get("head", {}).get("sha") != reconciliation["request_head"]
or reconciliation_pr.get("base", {}).get("ref") != "governance/approvals"
):
raise RuntimeError("SEMANTIC_RECONCILIATION_REQUEST_STALE")
_, reconciliation_reviews = api("GET", f"/api/v1/repos/{REPO}/pulls/{reconciliation['pr_number']}/reviews", value)
reconciliation_review = next((x for x in reconciliation_reviews if x.get("id") == reconciliation["owner_review_id"]), None)
if (
not reconciliation_review
or reconciliation_review.get("user", {}).get("id") != 2
or reconciliation_review.get("state") != "APPROVED"
or reconciliation_review.get("commit_id") != reconciliation["owner_review_head"]
or reconciliation_review.get("dismissed") is not False
):
raise RuntimeError("SEMANTIC_RECONCILIATION_OWNER_REVIEW_STALE")
_, pr = api("GET", f"/api/v1/repos/{REPO}/pulls/54", value)
if pr.get("state") != "open" or pr.get("merged") or pr.get("head", {}).get("sha") != PR54_HEAD:
raise RuntimeError("PR54_APPROVAL_STALE")
_, reviews = api("GET", f"/api/v1/repos/{REPO}/pulls/54/reviews", value)
review = next((x for x in reviews if x.get("id") == PR54_REVIEW), None)
if not review or review.get("user", {}).get("id") != 2 or review.get("state") != "APPROVED" or review.get("commit_id") != PR54_HEAD or review.get("dismissed") is not False:
raise RuntimeError("PR54_APPROVAL_STALE")
manifest_path = "approvals/governance-policy/AIOS-AUTONOMOUS-MERGE-GOVERNANCE-SCOPE-EXTENSION-V1.json"
_, content = api("GET", f"/api/v1/repos/{REPO}/contents/{urllib.parse.quote(manifest_path, safe='/')}?ref={PR54_HEAD}", value)
raw = base64.b64decode("".join(content.get("content", "").split()))
if hashlib.sha256(raw).hexdigest() != PR54_MANIFEST_SHA:
raise RuntimeError("PR54_MANIFEST_STALE")
def get_protection(value, branch):
if branch not in {"main", "governance/approvals"}:
raise RuntimeError("BRANCH_DENIED")
_, protection = api("GET", f"/api/v1/repos/{REPO}/branch_protections/{urllib.parse.quote(branch, safe='')}", value)
return protection
def mutation_receipt(before, after, binding, target_semantic):
return {
"before": {"transport_digest": sha(before), "semantic_digest": semantic_sha(before)},
"approved": {
"mutation_predicate_digest": sha({"operation": "ADD_MERGE_EXECUTOR", "subject_user_id": 153, "preserve_all_unmodified_security_fields": True}),
"Owner_review_id": binding["Owner_review_id"],
"semantic_policy_digest": APPROVED_SEMANTIC_SHA,
},
"after": {"transport_digest": sha(after), "semantic_digest": semantic_sha(after)},
"delta": {"add_merge_whitelist_user_id": 153},
"target_semantic_digest": sha(target_semantic),
"unmodified_security_fields": {"verified": True},
}
def apply_stage1(binding, value):
verify_binding(binding, value)
before = get_protection(value, "main")
before_semantic = semantic_policy(before)
before_semantic_sha = sha(before_semantic)
names = list(before.get("merge_whitelist_usernames") or [])
name_ids = user_ids(names)
if name_ids not in ([2], [2, 153]):
raise RuntimeError("PROTECTION_WHITELIST_PRESTATE_INVALID")
target_names = ["owner", "aios-autonomy-agent"]
target_semantic = copy.deepcopy(before_semantic)
target_semantic["merge_whitelist_user_ids"] = [2, 153]
if name_ids == [2, 153]:
if before_semantic_sha != STAGE1_SEMANTIC_SHA:
journal("APPLY_BLOCKED_STAGE1_IDEMPOTENCY_SEMANTIC_CONFLICT", transport_digest=sha(before), semantic_digest=before_semantic_sha, expected_semantic_digest=STAGE1_SEMANTIC_SHA)
raise RuntimeError("PROTECTION_STAGE1_SEMANTIC_CONFLICT")
receipt = mutation_receipt(before, before, binding, target_semantic)
journal("APPLY_APPROVED_PROTECTION_DELTA_IDEMPOTENT_SUCCESS", receipt=receipt)
return {"status": "IDEMPOTENT_SUCCESS", "protection": before, "transport_digest": sha(before), "semantic_digest": before_semantic_sha, "target_semantic_digest": sha(target_semantic)}
if before_semantic_sha != APPROVED_SEMANTIC_SHA:
journal("APPLY_BLOCKED_SEMANTIC_CAS", transport_digest=sha(before), semantic_digest=before_semantic_sha, expected_semantic_digest=APPROVED_SEMANTIC_SHA)
raise RuntimeError("PROTECTION_SEMANTIC_CAS_CONFLICT")
after_api = api("PATCH", f"/api/v1/repos/{REPO}/branch_protections/main", value, {"merge_whitelist_usernames": target_names})[1]
after = get_protection(value, "main")
for key in before:
if key in {"updated_at", "merge_whitelist_usernames"}:
continue
if after.get(key) != before.get(key):
raise RuntimeError("PROTECTION_UNAPPROVED_FIELD_CHANGED:" + key)
if user_ids(after.get("merge_whitelist_usernames")) != [2, 153]:
raise RuntimeError("PROTECTION_TARGET_READBACK_FAILED")
after_semantic = semantic_policy(after)
if after_semantic != target_semantic:
raise RuntimeError("PROTECTION_SEMANTIC_TARGET_READBACK_FAILED")
receipt = mutation_receipt(before, after, binding, target_semantic)
journal("APPLY_APPROVED_PROTECTION_DELTA_COMPLETED", receipt=receipt)
return {"status": "APPLIED", "protection": after, "previous_transport_digest": sha(before), "previous_semantic_digest": before_semantic_sha, "transport_digest": sha(after), "semantic_digest": sha(after_semantic), "receipt": receipt}
def apply_stage2(binding, facts, value):
verify_binding(binding, value)
validate_stage2_facts(facts)
before = get_protection(value, "main")
if OWNER_GATE in (before.get("status_check_contexts") or []) and stage2_semantic_digest(before) != STAGE1_SEMANTIC_SHA:
raise RuntimeError("STAGE2_SEMANTIC_PRECONDITION_INVALID")
try:
status, target = build_target(before)
except Stage2GateError as exc:
journal("MIGRATE_APPROVED_GATE_BLOCKED", reason=str(exc), semantic_digest=stage2_semantic_digest(before))
raise RuntimeError(str(exc)) from exc
target_digest = stage2_semantic_digest(target)
if status == "IDEMPOTENT_SUCCESS":
journal(
"MIGRATE_APPROVED_GATE_IDEMPOTENT_SUCCESS",
before_semantic_digest=stage2_semantic_digest(before),
target_semantic_digest=target_digest,
campaign_id=CAMPAIGN_ID,
)
return {
"status": status,
"protection": before,
"transport_digest": sha(before),
"semantic_digest": stage2_semantic_digest(before),
"target_semantic_digest": target_digest,
"gate_transition": {"removed": OWNER_GATE, "required": CAMPAIGN_GATE},
}
after_api = api(
"PATCH",
f"/api/v1/repos/{REPO}/branch_protections/main",
value,
{"status_check_contexts": target["status_check_contexts"]},
)[1]
after = get_protection(value, "main")
for key in before:
if key in {"updated_at", "status_check_contexts"}:
continue
if after.get(key) != before.get(key):
raise RuntimeError("PROTECTION_UNAPPROVED_FIELD_CHANGED:" + key)
if after.get("status_check_contexts") != target["status_check_contexts"]:
raise RuntimeError("STAGE2_GATE_TARGET_READBACK_FAILED")
if stage2_semantic_digest(after) != target_digest:
raise RuntimeError("STAGE2_SEMANTIC_TARGET_READBACK_FAILED")
receipt = {
"before_transport_digest": sha(before),
"before_semantic_digest": stage2_semantic_digest(before),
"after_transport_digest": sha(after),
"after_semantic_digest": stage2_semantic_digest(after),
"target_semantic_digest": target_digest,
"campaign_id": CAMPAIGN_ID,
"gate_transition": {"removed": OWNER_GATE, "required": CAMPAIGN_GATE},
"preserved_security_fields": True,
"facts_digest": sha(facts),
}
journal("MIGRATE_APPROVED_GATE_COMPLETED", receipt=receipt)
return {
"status": "APPLIED",
"protection": after,
"previous_transport_digest": sha(before),
"previous_semantic_digest": stage2_semantic_digest(before),
"transport_digest": sha(after),
"semantic_digest": stage2_semantic_digest(after),
"target_semantic_digest": target_digest,
"receipt": receipt,
}
def handle(request, value):
if not isinstance(request, dict) or set(request) != {"operation", "payload"}:
raise RuntimeError("REQUEST_SCHEMA_INVALID")
operation = request["operation"]
payload = request["payload"]
if not isinstance(payload, dict) or payload.get("repository") != REPO:
raise RuntimeError("REPOSITORY_DENIED")
if operation == "GET_BRANCH_PROTECTION":
protection = get_protection(value, payload.get("branch"))
journal("GET_BRANCH_PROTECTION", branch=payload.get("branch"), transport_digest=sha(protection), semantic_digest=semantic_sha(protection))
return {"status": "PASS", "protection": protection, "snapshot_sha256": sha(protection), "transport_digest": sha(protection), "semantic_digest": semantic_sha(protection), "semantic_schema": SEMANTIC_SCHEMA}
if operation == "READBACK_BRANCH_PROTECTION":
protection = get_protection(value, payload.get("branch"))
return {"status": "PASS", "protection": protection, "snapshot_sha256": sha(protection), "transport_digest": sha(protection), "semantic_digest": semantic_sha(protection), "semantic_schema": SEMANTIC_SCHEMA}
if operation == "APPLY_APPROVED_PROTECTION_DELTA":
binding = payload.get("GovernanceExecutionBinding")
if not isinstance(binding, dict):
raise RuntimeError("GOVERNANCE_BINDING_REQUIRED")
return apply_stage1(binding, value)
if operation == "MIGRATE_APPROVED_GATE":
binding = payload.get("GovernanceExecutionBinding")
facts = payload.get("Stage2Facts")
if not isinstance(binding, dict) or not isinstance(facts, dict):
raise RuntimeError("STAGE2_BINDING_OR_FACTS_REQUIRED")
return apply_stage2(binding, facts, value)
raise RuntimeError("OPERATION_DENIED")
def main():
value = token()
sock_path = pathlib.Path(SOCKET)
sock_path.unlink(missing_ok=True)
server = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
server.bind(SOCKET)
os.chmod(SOCKET, 0o660)
server.listen(16)
journal("EXECUTOR_STARTED", api_surface=["GET_BRANCH_PROTECTION", "APPLY_APPROVED_PROTECTION_DELTA", "MIGRATE_APPROVED_GATE", "READBACK_BRANCH_PROTECTION"], semantic_schema=SEMANTIC_SCHEMA, approved_semantic_digest=APPROVED_SEMANTIC_SHA, stage1_semantic_digest=STAGE1_SEMANTIC_SHA, campaign_gate=CAMPAIGN_GATE)
try:
while True:
conn, _ = server.accept()
with conn:
data = conn.recv(1024 * 1024 + 1)
if len(data) > 1024 * 1024:
result = {"status": "FAIL", "code": "REQUEST_TOO_LARGE"}
else:
try:
request = json.loads(data.decode())
result = handle(request, value)
except Exception as exc:
result = {"status": "FAIL", "code": str(exc)}
conn.sendall((json.dumps(result, sort_keys=True, separators=(",", ":"), ensure_ascii=False) + "\n").encode())
finally:
server.close()
sock_path.unlink(missing_ok=True)
if __name__ == "__main__":
main()
@@ -0,0 +1,347 @@
#!/usr/bin/env python3
import base64
import hashlib
import json
import os
import pathlib
import shutil
import socket
import subprocess
import tempfile
import time
import urllib.error
import urllib.parse
import urllib.request
LOCAL_API = 'http://127.0.0.1:3000/api/v1'
NETWORK_API = 'https://g.cnnetmkt.top/git/api/v1'
NETWORK_REMOTE = 'https://g.cnnetmkt.top/git/aios/aios.git'
REPO = 'aios/aios'
BASE_BRANCH = 'governance/approvals'
REQUEST_BRANCH = 'governance/request/protection-semantic-reconciliation-v1-20260808'
SOCKET_PATH = '/run/aios-governance-preapproval-publisher/publisher.sock'
CREDENTIAL_NAME = 'gitea-machine-token'
AUTHORITY_REPO = '/data/aios-gitops/repo.git'
TASK_ROOT = pathlib.Path('/var/lib/aios-gitops/runtime/tasks/AIOS-PROTECTION-SNAPSHOT-DRIFT-FORENSICS-V1')
INPUT_ROOT = TASK_ROOT / 'publication-input'
RECEIPT = TASK_ROOT / 'publication-receipt.json'
MANIFEST_REL = 'approvals/governance-policy/AIOS-PROTECTION-SEMANTIC-SNAPSHOT-RECONCILIATION-V1.json'
EVIDENCE_REL = 'approvals/governance-policy/AIOS-PROTECTION-SEMANTIC-SNAPSHOT-RECONCILIATION-V1.evidence.json'
def digest_bytes(data):
return hashlib.sha256(data).hexdigest()
def digest_json(value):
return digest_bytes(json.dumps(value, sort_keys=True, separators=(',', ':'), ensure_ascii=False).encode())
def load_token():
path = pathlib.Path(os.environ['CREDENTIALS_DIRECTORY']) / CREDENTIAL_NAME
value = path.read_text(encoding='utf-8').strip()
if len(value) < 16:
raise RuntimeError('MACHINE_CREDENTIAL_INVALID')
return value
def api(base, method, path, token, body=None, expected=(200,)):
raw = None if body is None else json.dumps(body, separators=(',', ':'), ensure_ascii=False).encode()
request = urllib.request.Request(base + path, data=raw, method=method)
request.add_header('Authorization', 'token ' + token)
request.add_header('Accept', 'application/json')
if raw is not None:
request.add_header('Content-Type', 'application/json')
try:
with urllib.request.urlopen(request, timeout=20) as response:
payload = response.read()
if response.status not in expected:
raise RuntimeError('GITEA_HTTP_' + str(response.status))
return response.status, json.loads(payload) if payload else None
except urllib.error.HTTPError as exc:
payload = exc.read()
if exc.code in expected:
return exc.code, json.loads(payload) if payload else None
raise RuntimeError('GITEA_HTTP_' + str(exc.code) + ':' + method + ':' + path) from None
def authority_head():
result = subprocess.run(
['git', '--git-dir=' + AUTHORITY_REPO, 'rev-parse', 'refs/heads/' + BASE_BRANCH],
text=True, capture_output=True, check=False,
)
if result.returncode != 0:
raise RuntimeError('AUTHORITY_BASE_UNAVAILABLE')
return result.stdout.strip()
def branch_api(base, token, branch, expected=(200,)):
return api(base, 'GET', '/repos/' + REPO + '/branches/' + urllib.parse.quote(branch, safe=''), token, expected=expected)
def verify_base(token):
authority = authority_head()
_, local = branch_api(LOCAL_API, token, BASE_BRANCH)
_, network = branch_api(NETWORK_API, token, BASE_BRANCH)
local_oid = local.get('commit', {}).get('id')
network_oid = network.get('commit', {}).get('id')
if not authority or authority != local_oid or authority != network_oid:
raise RuntimeError('RECONCILIATION_REQUIRED')
return authority
def verify_identity(token):
_, user = api(LOCAL_API, 'GET', '/user', token)
if user.get('login') != 'aios-autonomy-agent' or user.get('id') != 153 or user.get('is_admin') is not False:
raise RuntimeError('MACHINE_IDENTITY_REJECTED')
return {'login': user['login'], 'id': user['id'], 'owner': user.get('is_admin')}
def load_inputs():
manifest = json.dumps(json.loads((INPUT_ROOT / 'manifest.json').read_text(encoding='utf-8')), sort_keys=True, separators=(',', ':'), ensure_ascii=False).encode()
evidence = json.dumps(json.loads((INPUT_ROOT / 'evidence.json').read_text(encoding='utf-8')), sort_keys=True, separators=(',', ':'), ensure_ascii=False).encode()
for data in (manifest, evidence):
if any(secret in data.lower() for secret in (b'authorization:', b'ghp_', b'github_pat_', b'secret=', b'token=')):
raise RuntimeError('SECRET_CONTENT_REJECTED')
return manifest, evidence
def publication_key(base_oid, manifest_sha, evidence_sha):
payload = REPO + base_oid + REQUEST_BRANCH + manifest_sha + evidence_sha
return hashlib.sha256(payload.encode()).hexdigest()
def write_receipt(state, **fields):
record = {
'schema': 'aios.governance-preapproval-publication.v1',
'operation': 'SEMANTIC_RECONCILIATION_REQUEST',
'state': state,
'recorded_unix': int(time.time()),
'request_branch': REQUEST_BRANCH,
'manifest_path': MANIFEST_REL,
'evidence_path': EVIDENCE_REL,
'machine_actor': 'aios-autonomy-agent',
'owner': False,
'credential_ref': {'scheme': 'systemd-credential', 'name': CREDENTIAL_NAME},
'governance_capability_executed': False,
'protection_mutation': False,
'force_push': False,
**fields,
}
RECEIPT.parent.mkdir(parents=True, exist_ok=True)
temporary = RECEIPT.with_suffix('.tmp')
temporary.write_text(json.dumps(record, sort_keys=True, separators=(',', ':'), ensure_ascii=False) + '\n', encoding='utf-8')
temporary.replace(RECEIPT)
RECEIPT.chmod(0o640)
return record
def existing_prs(token):
_, prs = api(LOCAL_API, 'GET', '/repos/' + REPO + '/pulls?state=all&base=' + urllib.parse.quote(BASE_BRANCH, safe='') + '&limit=100', token)
return [pr for pr in prs if pr.get('head', {}).get('ref') == REQUEST_BRANCH and pr.get('base', {}).get('ref') == BASE_BRANCH]
def verify_existing_branch(token, branch_oid, manifest, evidence):
_, commit = api(LOCAL_API, 'GET', '/repos/' + REPO + '/git/commits/' + branch_oid, token)
if commit.get('parents', [{}])[0].get('sha') is None:
raise RuntimeError('REQUEST_BRANCH_COMMIT_INVALID')
for relative, expected in ((MANIFEST_REL, manifest), (EVIDENCE_REL, evidence)):
_, content = api(LOCAL_API, 'GET', '/repos/' + REPO + '/contents/' + urllib.parse.quote(relative, safe='/') + '?ref=' + branch_oid, token)
actual = base64.b64decode(''.join(content.get('content', '').split()))
if actual != expected:
raise RuntimeError('GOVERNANCE_REQUEST_BRANCH_CONFLICT')
def push_one_commit(token, base_oid, manifest, evidence):
work_root = TASK_ROOT / 'publisher-work'
work_root.mkdir(parents=True, exist_ok=True)
temporary = pathlib.Path(tempfile.mkdtemp(prefix='semantic-reconciliation-', dir=work_root))
askpass = temporary / 'askpass.sh'
clone = temporary / 'repo'
credential_path = pathlib.Path(os.environ['CREDENTIALS_DIRECTORY']) / CREDENTIAL_NAME
askpass.write_text(
'#!/bin/sh\ncase "$1" in *Username*) printf "%s\\n" "$GITEA_MACHINE_ACTOR";; *) cat "$CREDENTIAL_FILE";; esac\n',
encoding='utf-8',
)
askpass.chmod(0o700)
env = os.environ.copy()
env.update({
'GIT_ASKPASS': str(askpass),
'GIT_TERMINAL_PROMPT': '0',
'GITEA_MACHINE_ACTOR': 'aios-autonomy-agent',
'CREDENTIAL_FILE': str(credential_path),
'GIT_CONFIG_COUNT': '1',
'GIT_CONFIG_KEY_0': 'safe.directory',
'GIT_CONFIG_VALUE_0': AUTHORITY_REPO,
})
try:
run = subprocess.run(
['git', 'clone', '--branch', BASE_BRANCH, '--single-branch', '--quiet', NETWORK_REMOTE, str(clone)],
text=True, capture_output=True, env=env, check=False,
)
if run.returncode != 0:
detail = ' '.join(run.stderr.strip().split())[:240]
raise RuntimeError('PUBLISHER_CLONE_FAILED:' + detail)
def git(*args, push_env=None):
result = subprocess.run(['git', *args], cwd=clone, text=True, capture_output=True, env=push_env or env, check=False)
if result.returncode != 0:
raise RuntimeError('PUBLISHER_GIT_OPERATION_FAILED')
return result.stdout.strip()
git('checkout', '--detach', base_oid)
git('switch', '--create', REQUEST_BRANCH)
manifest_path = clone / MANIFEST_REL
evidence_path = clone / EVIDENCE_REL
manifest_path.parent.mkdir(parents=True, exist_ok=True)
evidence_path.parent.mkdir(parents=True, exist_ok=True)
manifest_path.write_bytes(manifest)
evidence_path.write_bytes(evidence)
git('add', '--', MANIFEST_REL, EVIDENCE_REL)
staged = git('diff', '--cached', '--name-only').splitlines()
if staged != sorted([MANIFEST_REL, EVIDENCE_REL]):
raise RuntimeError('PUBLISHER_PATH_ALLOWLIST_FAILED')
git('-c', 'user.name=aios-autonomy-agent', '-c', 'user.email=aios-autonomy-agent@aios.invalid', 'commit', '--quiet', '-m', '[governance] publish semantic reconciliation evidence')
if git('rev-list', '--count', base_oid + '..HEAD') != '1':
raise RuntimeError('PUBLISHER_COMMIT_COUNT_FAILED')
git('push', '--porcelain', NETWORK_REMOTE, 'HEAD:refs/heads/' + REQUEST_BRANCH, push_env=env)
head = git('rev-parse', 'HEAD')
return head
finally:
shutil.rmtree(temporary, ignore_errors=True)
def publish():
token = load_token()
identity = verify_identity(token)
base_oid = verify_base(token)
manifest, evidence = load_inputs()
manifest_sha = digest_bytes(manifest)
evidence_sha = digest_bytes(evidence)
key = publication_key(base_oid, manifest_sha, evidence_sha)
write_receipt('REQUESTED', governance_base_oid=base_oid, manifest_sha256=manifest_sha, evidence_sha256=evidence_sha, idempotency_key=key, machine_identity=identity)
write_receipt('LOCAL_CANDIDATE_VERIFIED', governance_base_oid=base_oid, manifest_sha256=manifest_sha, evidence_sha256=evidence_sha, idempotency_key=key, machine_identity=identity)
branch_status, branch = branch_api(LOCAL_API, token, REQUEST_BRANCH, expected=(200, 404))
if branch_status == 200:
branch_oid = branch.get('commit', {}).get('id')
verify_existing_branch(token, branch_oid, manifest, evidence)
else:
branch_oid = push_one_commit(token, base_oid, manifest, evidence)
verify_existing_branch(token, branch_oid, manifest, evidence)
write_receipt('BRANCH_PUSHED', governance_base_oid=base_oid, manifest_sha256=manifest_sha, evidence_sha256=evidence_sha, idempotency_key=key, request_head_oid=branch_oid, machine_identity=identity)
prs = existing_prs(token)
if len(prs) > 1:
raise RuntimeError('MULTIPLE_MATCHING_APPROVAL_PRS')
if prs:
pr = prs[0]
if pr.get('head', {}).get('sha') != branch_oid:
raise RuntimeError('APPROVAL_PR_HEAD_MISMATCH')
pr_number = pr.get('number')
else:
_, pr = api(LOCAL_API, 'POST', '/repos/' + REPO + '/pulls', token, {
'title': '[GOVERNANCE RECONCILIATION] Semantic protection snapshot schema migration',
'head': REQUEST_BRANCH,
'base': BASE_BRANCH,
'body': 'Schema-only reconciliation for the existing PR54 Stage-1 and Stage-2 predicates. No new governance capability; Owner exact-head re-attestation required.',
}, expected=(201,))
pr_number = pr.get('number')
write_receipt('PR_CREATED', governance_base_oid=base_oid, manifest_sha256=manifest_sha, evidence_sha256=evidence_sha, idempotency_key=key, request_head_oid=branch_oid, pr_number=pr_number, machine_identity=identity)
_, remote_branch = branch_api(NETWORK_API, token, REQUEST_BRANCH)
if remote_branch.get('commit', {}).get('id') != branch_oid:
raise RuntimeError('NETWORK_BRANCH_READBACK_MISMATCH')
_, read_pr = api(LOCAL_API, 'GET', '/repos/' + REPO + '/pulls/' + str(pr_number), token)
if read_pr.get('head', {}).get('sha') != branch_oid or read_pr.get('base', {}).get('ref') != BASE_BRANCH:
raise RuntimeError('PR_READBACK_MISMATCH')
result = write_receipt('COMPLETED', governance_base_oid=base_oid, manifest_sha256=manifest_sha, evidence_sha256=evidence_sha, idempotency_key=key, request_head_oid=branch_oid, pr_number=pr_number, pr_head_oid=read_pr.get('head', {}).get('sha'), machine_identity=identity)
return result
def self_test():
token = load_token()
identity = verify_identity(token)
base_oid = verify_base(token)
branch_status, _ = branch_api(LOCAL_API, token, REQUEST_BRANCH, expected=(200, 404))
return {
'status': 'PASS',
'governance_base_oid': base_oid,
'machine_identity': identity,
'credential_ref': {'scheme': 'systemd-credential', 'name': CREDENTIAL_NAME},
'branch_namespace': 'refs/heads/governance/request/*',
'target_base': BASE_BRANCH,
'request_branch_status': 'EXISTS' if branch_status == 200 else 'ABSENT',
'operation_allowlist': ['READ_BASE', 'CREATE_ONE_REQUEST_BRANCH', 'WRITE_TWO_EVIDENCE_PATHS_ONE_COMMIT', 'CREATE_OR_READ_ONE_APPROVAL_PR', 'READBACK'],
'denied': ['MAIN_WRITE', 'DIRECT_APPROVALS_WRITE', 'BRANCH_PROTECTION', 'MERGE', 'FORCE_PUSH', 'TAG', 'PRODUCTION', 'CREDENTIAL_MUTATION'],
'governance_capability_executed': False,
}
def read_publication_status():
token = load_token()
receipt = json.loads(RECEIPT.read_text(encoding='utf-8'))
pr_number = receipt.get('pr_number')
if not pr_number:
raise RuntimeError('PUBLICATION_RECEIPT_PR_MISSING')
_, pr = api(LOCAL_API, 'GET', '/repos/' + REPO + '/pulls/' + str(pr_number), token)
_, reviews = api(LOCAL_API, 'GET', '/repos/' + REPO + '/pulls/' + str(pr_number) + '/reviews', token)
_, branch = branch_api(LOCAL_API, token, REQUEST_BRANCH)
_, network_branch = branch_api(NETWORK_API, token, REQUEST_BRANCH)
return {
'status': 'PASS',
'request_branch': REQUEST_BRANCH,
'branch_head': branch.get('commit', {}).get('id'),
'network_branch_head': network_branch.get('commit', {}).get('id'),
'pr_number': pr.get('number'),
'pr_state': pr.get('state'),
'pr_merged': pr.get('merged'),
'pr_head': pr.get('head', {}).get('sha'),
'pr_base': pr.get('base', {}).get('ref'),
'reviews': [
{
'id': item.get('id'),
'reviewer_id': item.get('user', {}).get('id'),
'state': item.get('state'),
'commit_id': item.get('commit_id'),
'dismissed': item.get('dismissed'),
}
for item in reviews
],
'governance_capability_executed': False,
'protection_mutation': False,
}
def handle(request):
if not isinstance(request, dict) or set(request) != {'operation'}:
raise RuntimeError('REQUEST_SCHEMA_INVALID')
if request['operation'] == 'DRY_RUN_SELF_TEST':
return self_test()
if request['operation'] == 'PUBLISH_SEMANTIC_RECONCILIATION':
return publish()
if request['operation'] == 'READ_SEMANTIC_RECONCILIATION_STATUS':
return read_publication_status()
raise RuntimeError('OPERATION_DENIED')
def serve(listener):
conn, _ = listener.accept()
with conn:
try:
data = conn.recv(1024 * 1024 + 1)
if len(data) > 1024 * 1024:
raise RuntimeError('REQUEST_TOO_LARGE')
result = handle(json.loads(data.decode()))
except Exception as exc:
result = {'status': 'FAIL', 'code': str(exc)}
conn.sendall((json.dumps(result, sort_keys=True, separators=(',', ':'), ensure_ascii=False) + '\n').encode())
def main():
if os.environ.get('LISTEN_FDS') != '1':
raise SystemExit('SOCKET_ACTIVATION_REQUIRED')
listener = socket.fromfd(3, socket.AF_UNIX, socket.SOCK_STREAM)
serve(listener)
if __name__ == '__main__':
main()
@@ -0,0 +1,73 @@
"""Fail-closed Authority/Gitea ref reconciliation after protected merge."""
from __future__ import annotations
from dataclasses import dataclass
from pathlib import Path
import subprocess
class RefReconciliationError(RuntimeError):
pass
def _oid(value: str, field: str) -> str:
if not isinstance(value, str) or len(value) != 40 or any(char not in "0123456789abcdef" for char in value):
raise RefReconciliationError(field + "_INVALID")
return value
@dataclass(frozen=True, slots=True)
class RefReconciler:
git_dir: Path
def read(self, ref: str) -> str:
result = subprocess.run(
["git", f"--git-dir={self.git_dir}", "rev-parse", "--verify", ref],
text=True,
capture_output=True,
check=False,
)
if result.returncode != 0:
raise RefReconciliationError("AUTHORITY_REF_UNAVAILABLE")
return result.stdout.strip()
def is_ancestor(self, ancestor: str, descendant: str) -> bool:
_oid(ancestor, "ancestor")
_oid(descendant, "descendant")
result = subprocess.run(
["git", f"--git-dir={self.git_dir}", "merge-base", "--is-ancestor", ancestor, descendant],
text=True,
capture_output=True,
check=False,
)
if result.returncode == 0:
return True
if result.returncode == 1:
return False
raise RefReconciliationError("AUTHORITY_ANCESTRY_CHECK_FAILED")
def reconcile_main(self, *, expected_oid: str, merged_oid: str, gitea_main_oid: str) -> str:
_oid(expected_oid, "expected_oid")
_oid(merged_oid, "merged_oid")
_oid(gitea_main_oid, "gitea_main_oid")
if gitea_main_oid != merged_oid:
raise RefReconciliationError("GITEA_MAIN_EXPECTATION_MISMATCH")
if not self.is_ancestor(expected_oid, merged_oid):
raise RefReconciliationError("MAIN_FF_REQUIRED")
current = self.read("refs/heads/main")
if current == merged_oid:
return merged_oid
if current != expected_oid:
raise RefReconciliationError("AUTHORITY_MAIN_EXPECTATION_MISMATCH")
result = subprocess.run(
["git", f"--git-dir={self.git_dir}", "update-ref", "refs/heads/main", merged_oid, expected_oid],
text=True,
capture_output=True,
check=False,
)
if result.returncode != 0:
raise RefReconciliationError("AUTHORITY_MAIN_CAS_FAILED")
if self.read("refs/heads/main") != merged_oid:
raise RefReconciliationError("AUTHORITY_MAIN_CAS_READBACK_MISMATCH")
return merged_oid
@@ -0,0 +1,81 @@
"""Canonical semantic branch-protection model used by AIOS governance gates."""
from __future__ import annotations
import hashlib
import json
from typing import Any, Mapping
SCHEMA = "aios.semantic-protection.v1"
USER_IDS = {"owner": 2, "aios-autonomy-agent": 153}
def canonical_json(value: Any) -> bytes:
return json.dumps(value, sort_keys=True, separators=(",", ":"), ensure_ascii=False).encode("utf-8")
def _typed_json(value: Any) -> list[Any]:
if value in (None, "null"):
return []
if isinstance(value, str):
return json.loads(value)
return list(value)
def _user_ids(names: Any) -> list[int]:
result = []
for name in names or []:
if name not in USER_IDS:
raise ValueError("UNKNOWN_GOVERNANCE_USER:" + str(name))
result.append(USER_IDS[name])
return sorted(result)
def semantic_policy(protection: Mapping[str, Any]) -> dict[str, Any]:
"""Drop transport metadata and canonicalize semantic security fields."""
return {
"schema_version": SCHEMA,
"repo_id": 1,
"protection_id": 2,
"branch_name": protection.get("branch_name"),
"can_push": protection.get("enable_push"),
"can_force_push": protection.get("enable_force_push"),
"enable_merge_whitelist": protection.get("enable_merge_whitelist"),
"merge_whitelist_user_ids": _user_ids(protection.get("merge_whitelist_usernames")),
"merge_whitelist_team_ids": [],
"required_approvals": protection.get("required_approvals"),
"enable_status_check": protection.get("enable_status_check"),
"status_check_contexts": sorted(protection.get("status_check_contexts") or []),
"block_admin_merge_override": protection.get("block_admin_merge_override"),
"block_on_official_review_requests": protection.get("block_on_official_review_requests"),
"block_on_outdated_branch": protection.get("block_on_outdated_branch"),
"block_on_rejected_reviews": protection.get("block_on_rejected_reviews"),
"dismiss_stale_approvals": protection.get("dismiss_stale_approvals"),
"ignore_stale_approvals": protection.get("ignore_stale_approvals"),
"enable_approvals_whitelist": protection.get("enable_approvals_whitelist"),
"approvals_whitelist_user_ids": _user_ids(protection.get("approvals_whitelist_username")),
"approvals_whitelist_team_ids": [],
"enable_bypass_allowlist": protection.get("enable_bypass_allowlist"),
"bypass_allowlist_user_ids": _user_ids(protection.get("bypass_allowlist_usernames")),
"bypass_allowlist_team_ids": [],
"enable_force_push_allowlist": protection.get("enable_force_push_allowlist"),
"force_push_allowlist_user_ids": _user_ids(protection.get("force_push_allowlist_usernames")),
"enable_whitelist": protection.get("enable_push_whitelist"),
"whitelist_user_ids": _user_ids(protection.get("push_whitelist_usernames")),
"whitelist_team_ids": [],
"force_push_allowlist_deploy_keys": protection.get("force_push_allowlist_deploy_keys"),
"whitelist_deploy_keys": protection.get("push_whitelist_deploy_keys"),
"require_signed_commits": protection.get("require_signed_commits"),
"priority": protection.get("priority"),
"protected_file_patterns": _typed_json(protection.get("protected_file_patterns")),
"unprotected_file_patterns": _typed_json(protection.get("unprotected_file_patterns")),
}
def semantic_digest(protection: Mapping[str, Any]) -> str:
return hashlib.sha256(canonical_json(semantic_policy(protection))).hexdigest()
def transport_digest(protection: Mapping[str, Any]) -> str:
return hashlib.sha256(canonical_json(protection)).hexdigest()
@@ -0,0 +1,141 @@
"""Fail-closed Stage-2 campaign gate migration predicates."""
from __future__ import annotations
import copy
import hashlib
import json
import re
from typing import Any, Mapping
SEMANTIC_SCHEMA = "aios.semantic-protection.v1"
STAGE1_SEMANTIC_SHA = "d911e856f71de6105b8efa08e13338a448e0b6d37536332decd80c9f409ecf27"
CAMPAIGN_ID = "AIOS-AUTONOMOUS-RELEASE-CAMPAIGN-20260807"
OWNER_GATE = "owner-approval-gate"
CAMPAIGN_GATE = "campaign-approval-gate"
OID_RE = re.compile(r"^[0-9a-f]{40}$")
class Stage2GateError(RuntimeError):
pass
def canonical_json(value: Any) -> bytes:
return json.dumps(value, sort_keys=True, separators=(",", ":"), ensure_ascii=False).encode("utf-8")
def _typed_json(value: Any) -> list[Any]:
if value in (None, "", "null"):
return []
if isinstance(value, str):
return json.loads(value)
return list(value)
def _user_ids(names: Any) -> list[int]:
mapping = {"owner": 2, "aios-autonomy-agent": 153}
result = []
for name in names or []:
if name not in mapping:
raise Stage2GateError("UNKNOWN_GOVERNANCE_USER")
result.append(mapping[name])
return sorted(result)
def semantic_policy(protection: Mapping[str, Any]) -> dict[str, Any]:
return {
"schema_version": SEMANTIC_SCHEMA,
"repo_id": 1,
"protection_id": 2,
"branch_name": protection.get("branch_name"),
"can_push": protection.get("enable_push"),
"can_force_push": protection.get("enable_force_push"),
"enable_merge_whitelist": protection.get("enable_merge_whitelist"),
"merge_whitelist_user_ids": _user_ids(protection.get("merge_whitelist_usernames")),
"merge_whitelist_team_ids": [],
"required_approvals": protection.get("required_approvals"),
"enable_status_check": protection.get("enable_status_check"),
"status_check_contexts": sorted(protection.get("status_check_contexts") or []),
"block_admin_merge_override": protection.get("block_admin_merge_override"),
"block_on_official_review_requests": protection.get("block_on_official_review_requests"),
"block_on_outdated_branch": protection.get("block_on_outdated_branch"),
"block_on_rejected_reviews": protection.get("block_on_rejected_reviews"),
"dismiss_stale_approvals": protection.get("dismiss_stale_approvals"),
"ignore_stale_approvals": protection.get("ignore_stale_approvals"),
"enable_approvals_whitelist": protection.get("enable_approvals_whitelist"),
"approvals_whitelist_user_ids": _user_ids(protection.get("approvals_whitelist_username")),
"approvals_whitelist_team_ids": [],
"enable_bypass_allowlist": protection.get("enable_bypass_allowlist"),
"bypass_allowlist_user_ids": _user_ids(protection.get("bypass_allowlist_usernames")),
"bypass_allowlist_team_ids": [],
"enable_force_push_allowlist": protection.get("enable_force_push_allowlist"),
"force_push_allowlist_user_ids": _user_ids(protection.get("force_push_allowlist_usernames")),
"force_push_allowlist_team_ids": [],
"enable_whitelist": protection.get("enable_push_whitelist"),
"whitelist_user_ids": _user_ids(protection.get("push_whitelist_usernames")),
"whitelist_team_ids": [],
"force_push_allowlist_deploy_keys": protection.get("force_push_allowlist_deploy_keys"),
"whitelist_deploy_keys": protection.get("push_whitelist_deploy_keys"),
"require_signed_commits": protection.get("require_signed_commits"),
"priority": protection.get("priority"),
"protected_file_patterns": protection.get("protected_file_patterns"),
"unprotected_file_patterns": protection.get("unprotected_file_patterns"),
}
def semantic_digest(protection: Mapping[str, Any]) -> str:
return hashlib.sha256(canonical_json(semantic_policy(protection))).hexdigest()
def build_target(protection: Mapping[str, Any]) -> tuple[str, dict[str, Any]]:
current = list(protection.get("status_check_contexts") or [])
has_owner = OWNER_GATE in current
has_campaign = CAMPAIGN_GATE in current
if has_owner and has_campaign:
raise Stage2GateError("GATE_PRESTATE_CONFLICT")
if not has_owner and not has_campaign:
raise Stage2GateError("OWNER_GATE_MISSING")
if has_campaign:
return "IDEMPOTENT_SUCCESS", copy.deepcopy(dict(protection))
if semantic_digest(protection) != STAGE1_SEMANTIC_SHA:
raise Stage2GateError("SEMANTIC_PRECONDITION_INVALID")
target = copy.deepcopy(dict(protection))
target["status_check_contexts"] = sorted(
[gate for gate in current if gate != OWNER_GATE] + [CAMPAIGN_GATE]
)
return "READY", target
def validate_stage2_facts(facts: Mapping[str, Any]) -> None:
required = {
"campaign_id",
"campaign_active",
"campaign_revoked",
"machine_actor",
"required_checks_all_success",
"required_checks_same_head",
"unresolved_conversations",
"mergeable",
"protected_target",
"current_main_oid",
"gitea_main_oid",
"current_main_lineage_verified",
}
if set(facts) != required:
raise Stage2GateError("STAGE2_FACTS_SCHEMA_INVALID")
if facts["campaign_id"] != CAMPAIGN_ID or facts["campaign_active"] is not True or facts["campaign_revoked"] is not False:
raise Stage2GateError("CAMPAIGN_NOT_ACTIVE")
if facts["machine_actor"] != "aios-autonomy-agent":
raise Stage2GateError("MACHINE_ACTOR_MISMATCH")
if facts["required_checks_all_success"] is not True or facts["required_checks_same_head"] is not True:
raise Stage2GateError("REQUIRED_CHECKS_NOT_CURRENT")
if facts["unresolved_conversations"] != 0 or facts["mergeable"] is not True:
raise Stage2GateError("CAMPAIGN_GATE_FACTS_INVALID")
if facts["protected_target"] != "main" or facts["current_main_lineage_verified"] is not True:
raise Stage2GateError("PROTECTED_TARGET_OR_LINEAGE_INVALID")
if (
not isinstance(facts["current_main_oid"], str)
or not OID_RE.fullmatch(facts["current_main_oid"])
or facts["gitea_main_oid"] != facts["current_main_oid"]
):
raise Stage2GateError("MAIN_READBACK_MISMATCH")
@@ -0,0 +1,568 @@
"""Governed V2R2C1 production release orchestration.
This module is deliberately scoped to the Owner-approved V2R2C1 binding. It
uses the existing TagPublisher and metadata adapters; it does not introduce a
second Git writer, a second durable job store, or an alternate baseline.
"""
from __future__ import annotations
import argparse
import fcntl
import hashlib
import json
import os
import re
import subprocess
import tarfile
import tempfile
import time
from dataclasses import dataclass
from pathlib import Path
from typing import Any, Mapping
from gitea_tag_publication import (
GiteaTagClient,
GitTagBackend,
PublicationManualOwnerReviewRequired,
PublicationPending,
PublicationReconciliationRequired,
PublicationRequest,
TagPublisher,
)
VERSION = "V2R2C1"
TARGET_OID = "998d30304787e31e5ae607c0bd86b5edb2b0e5ad"
EVIDENCE_CLOSURE_OID = "a8b906d1bc24088c19647e98de6a679268f6d0a2"
APPROVAL_PLAN_ID = "sha256:c0df52008ae7f64cec5ee2d28947aa7b892066c670ab8295e4549140b1a7500e"
REPOSITORY = "aios/aios"
SERVICE = "aios-v2r1c7-production.service"
PORT = 18927
OID_RE = re.compile(r"^[0-9a-f]{40}$")
SHA_RE = re.compile(r"^[0-9a-f]{64}$")
class ReleaseBlocked(RuntimeError):
"""A release gate failed and no unsafe recovery is permitted."""
class ManualOwnerReviewRequired(ReleaseBlocked):
pass
class ReconciliationRequired(ReleaseBlocked):
pass
def canonical(value: object) -> bytes:
return (json.dumps(value, sort_keys=True, separators=(",", ":")) + "\n").encode()
def sha256_file(path: Path) -> str:
if path.is_symlink() or not path.is_file():
raise ReleaseBlocked("artifact must be a regular file")
digest = hashlib.sha256()
with path.open("rb") as stream:
for block in iter(lambda: stream.read(1024 * 1024), b""):
digest.update(block)
return digest.hexdigest()
def atomic_json(path: Path, value: Mapping[str, Any], mode: int = 0o600) -> None:
path.parent.mkdir(parents=True, exist_ok=True)
temporary = path.with_name(path.name + ".tmp")
temporary.write_bytes(canonical(dict(value)))
os.chmod(temporary, mode)
os.replace(temporary, path)
@dataclass(frozen=True)
class ReleaseBinding:
version: str = VERSION
target_oid: str = TARGET_OID
evidence_closure_oid: str = EVIDENCE_CLOSURE_OID
approval_plan_id: str = APPROVAL_PLAN_ID
repository: str = REPOSITORY
production_service: str = SERVICE
production_port: int = PORT
def validate(self) -> None:
if self.version != VERSION or self.repository != REPOSITORY:
raise ManualOwnerReviewRequired("release binding is outside V2R2C1 scope")
if not OID_RE.fullmatch(self.target_oid) or self.target_oid != TARGET_OID:
raise ManualOwnerReviewRequired("V2R2C1 target_oid mismatch")
if not OID_RE.fullmatch(self.evidence_closure_oid) or self.evidence_closure_oid != EVIDENCE_CLOSURE_OID:
raise ManualOwnerReviewRequired("V2R2C1 evidence closure mismatch")
if self.approval_plan_id != APPROVAL_PLAN_ID:
raise ManualOwnerReviewRequired("V2R2C1 approval plan mismatch")
if self.production_service != SERVICE or int(self.production_port) != PORT:
raise ManualOwnerReviewRequired("production service binding mismatch")
def as_dict(self) -> dict[str, Any]:
return {
"version": self.version,
"target_oid": self.target_oid,
"evidence_closure_oid": self.evidence_closure_oid,
"approval_plan_id": self.approval_plan_id,
"repository": self.repository,
"production_service": self.production_service,
"production_port": self.production_port,
}
class StateLock:
def __init__(self, path: Path):
self.path = path
self.handle = None
def __enter__(self) -> "StateLock":
self.path.parent.mkdir(parents=True, exist_ok=True)
self.handle = self.path.open("a+", encoding="utf-8")
fcntl.flock(self.handle.fileno(), fcntl.LOCK_EX)
return self
def __exit__(self, exc_type, exc, tb) -> None:
if self.handle is not None:
fcntl.flock(self.handle.fileno(), fcntl.LOCK_UN)
self.handle.close()
class ReleaseState:
def __init__(self, root: Path, binding: ReleaseBinding):
self.root = root
self.path = root / "release-state.json"
self.binding = binding
root.mkdir(parents=True, exist_ok=True)
if self.path.exists():
value = json.loads(self.path.read_text(encoding="utf-8"))
if value.get("binding") != binding.as_dict():
raise ManualOwnerReviewRequired("persisted release binding conflict")
self.value = value
else:
self.value = {
"schema": "aios.v2r2c1.governed-production-release-state.v1",
"binding": binding.as_dict(),
"state": "INITIAL",
"completed": {},
"history": [],
}
self.save()
def save(self) -> None:
atomic_json(self.path, self.value)
def record(self, state: str, step: str, result: Mapping[str, Any]) -> None:
safe = json.loads(json.dumps(dict(result)))
self.value["state"] = state
self.value.setdefault("completed", {})[step] = safe
self.value.setdefault("history", []).append(
{"at": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()), "state": state, "step": step, "result": safe}
)
self.save()
def fail(self, state: str, reason: str) -> None:
self.value["state"] = state
self.value.setdefault("history", []).append(
{"at": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()), "state": state, "reason": str(reason)[:500]}
)
self.save()
def _git(git_dir: Path, *args: str, check: bool = True) -> str:
completed = subprocess.run(
["git", f"--git-dir={git_dir}", *args],
check=False,
capture_output=True,
text=True,
timeout=45,
env={"PATH": os.environ.get("PATH", "")},
)
if check and completed.returncode != 0:
raise ReconciliationRequired(completed.stderr.strip() or "authoritative git query failed")
return completed.stdout.strip()
def _ancestor(git_dir: Path, ancestor: str, descendant: str) -> bool:
return subprocess.run(
["git", f"--git-dir={git_dir}", "merge-base", "--is-ancestor", ancestor, descendant],
check=False,
capture_output=True,
timeout=45,
env={"PATH": os.environ.get("PATH", "")},
).returncode == 0
def verify_lineage(git_dir: Path, binding: ReleaseBinding) -> dict[str, Any]:
binding.validate()
if _git(git_dir, "cat-file", "-t", binding.target_oid) != "commit":
raise ReleaseBlocked("approved target_oid is not a commit")
if _git(git_dir, "cat-file", "-t", binding.evidence_closure_oid) != "commit":
raise ReleaseBlocked("evidence closure is not a commit")
if not _ancestor(git_dir, binding.target_oid, binding.evidence_closure_oid):
raise ManualOwnerReviewRequired("approved target is not an ancestor of evidence closure")
return {
"target_oid": binding.target_oid,
"evidence_closure_oid": binding.evidence_closure_oid,
"target_is_ancestor_of_evidence": True,
"main_oid": _git(git_dir, "rev-parse", "refs/heads/main"),
}
def verify_owner_approval(value: Mapping[str, Any], binding: ReleaseBinding) -> dict[str, Any]:
approval = value.get("owner_approval")
if not isinstance(approval, Mapping):
raise ReleaseBlocked("OWNER_APPROVAL_RECEIPT_REQUIRED")
if approval.get("approved") is not True or approval.get("status") != "APPROVED":
raise ReleaseBlocked("OWNER_APPROVAL_NOT_ACCEPTED")
if approval.get("approved_by") != "aiman":
raise ManualOwnerReviewRequired("OWNER_APPROVAL_ACTOR_MISMATCH")
if approval.get("approval_source") != "explicit_owner_approval_in_chat":
raise ManualOwnerReviewRequired("OWNER_APPROVAL_SOURCE_MISMATCH")
for key, expected in binding.as_dict().items():
if key in {"repository", "production_service", "production_port"}:
continue
if approval.get(key) != expected:
raise ManualOwnerReviewRequired("OWNER_APPROVAL_BINDING_MISMATCH:" + key)
return dict(approval)
def _payload_file(git_dir: Path, oid: str, path: str) -> bytes:
completed = subprocess.run(
["git", f"--git-dir={git_dir}", "show", f"{oid}:{path}"],
check=False,
capture_output=True,
timeout=45,
env={"PATH": os.environ.get("PATH", "")},
)
if completed.returncode != 0:
raise ReleaseBlocked("required release source missing:" + path)
return completed.stdout
def _commit_epoch(git_dir: Path, oid: str) -> int:
return int(_git(git_dir, "show", "-s", "--format=%ct", oid))
def build_production_artifact(
git_dir: Path,
binding: ReleaseBinding,
output_root: Path,
) -> dict[str, Any]:
"""Build a deterministic executable V2R2C1 health artifact from target_oid."""
binding.validate()
verify_lineage(git_dir, binding)
epoch = _commit_epoch(git_dir, binding.target_oid)
output_root.mkdir(parents=True, exist_ok=True)
stem = f"aios-{VERSION}-{binding.target_oid[:12]}"
output = output_root / (stem + ".tar.zst")
with tempfile.TemporaryDirectory(prefix="v2r2c1-artifact-") as temp:
root = Path(temp) / "payload"
root.mkdir(parents=True)
source_paths = {
"active-runtime/app/deployment_state.py": "active-runtime/app/deployment_state.py",
"active-runtime/app/v2r1c6_privacy_tools.py": "active-runtime/app/v2r1c6_privacy_tools.py",
}
for destination, source in source_paths.items():
target = root / destination
target.parent.mkdir(parents=True, exist_ok=True)
target.write_bytes(_payload_file(git_dir, binding.target_oid, source))
generated: dict[str, str] = {
"VERSION": VERSION + "\n",
"active-runtime/app/v2r2c1_health_service.py": (
"from v2r1c6_privacy_tools import main\n\n"
"if __name__ == '__main__':\n raise SystemExit(main())\n"
),
"deploy/aios-v2/systemd/aios-v2r1c7-production.service": (
"[Unit]\nDescription=AIOS V2R2C1 governed health contract service\n"
"After=network.target\n\n[Service]\nType=simple\n"
"User=aios-v2r1c7\nGroup=aios-v2r1c7\n"
"WorkingDirectory=/opt/aios/v2r2c1\n"
"EnvironmentFile=/etc/aios/aios-v2r2c1.env\n"
"ExecStart=/usr/bin/python3 /opt/aios/v2r2c1/active-runtime/app/v2r2c1_health_service.py --serve --host 127.0.0.1 --port ${AIOS_V2R1C7_PORT}\n"
"Restart=on-failure\nRestartSec=5\nNoNewPrivileges=true\nPrivateTmp=true\n"
"ProtectHome=true\nProtectSystem=strict\nProtectKernelLogs=true\n"
"ProtectKernelModules=true\nProtectKernelTunables=true\nProtectControlGroups=true\n"
"RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6\nIPAddressDeny=any\n"
"IPAddressAllow=localhost\nUMask=0077\n\n[Install]\nWantedBy=multi-user.target\n"
),
"deploy/aios-v2/v2r2c1-runtime-config.json": json.dumps({
"schema": "aios.v2r2c1.production-runtime.v1",
"version": VERSION,
"target_oid": binding.target_oid,
"service": SERVICE,
"port": PORT,
"health_path": "/health",
"deployment_state_source": "environment_and_state_file",
}, sort_keys=True, separators=(",", ":")) + "\n",
"deploy/aios-v2/v2r2c1-rollback.json": json.dumps({
"schema": "aios.v2r2c1.rollback-metadata.v1",
"rollback_version": "V2R1C7",
"rollback_service": SERVICE,
"rollback_target_is_previous_production": True,
"automatic_rollback": False,
}, sort_keys=True, separators=(",", ":")) + "\n",
"scripts/probe_v2r2c1_health.py": (
"import json,sys,urllib.request\n"
"url=sys.argv[1] if len(sys.argv)>1 else 'http://127.0.0.1:18927/health'\n"
"with urllib.request.urlopen(url, timeout=5) as r: print(json.dumps(json.load(r), sort_keys=True))\n"
),
"scripts/rollback_v2r2c1_release.sh": "#!/bin/sh\nset -eu\nprintf '%s\\n' 'rollback requires governed owner-approved executor'\nexit 2\n",
"release-manifests/V2R2C1.json": json.dumps({
"schema": "aios.release-source.v2",
"version": VERSION,
"repository": REPOSITORY,
"target_oid": binding.target_oid,
"evidence_closure_oid": binding.evidence_closure_oid,
"production_deployed": False,
"deployment_state": "STAGING",
"artifact_kind": "executable-production-health",
}, sort_keys=True, separators=(",", ":")) + "\n",
}
for rel, text in generated.items():
target = root / rel
target.parent.mkdir(parents=True, exist_ok=True)
target.write_text(text, encoding="utf-8")
if rel.startswith("scripts/"):
target.chmod(0o755)
metadata = {
"schema": "aios.release-artifact-build.v3",
"version": VERSION,
"repository": REPOSITORY,
"source_head_oid": binding.target_oid,
"evidence_closure_oid": binding.evidence_closure_oid,
"source_date_epoch": epoch,
"clean_source": True,
"executable_payload": True,
"production_deployed": False,
"tag_created": False,
"baseline_changed": False,
"secrets_included": False,
}
(root / "BUILD-METADATA.json").write_bytes(canonical(metadata))
(root / "ROLLBACK-METADATA.json").write_bytes(canonical(json.loads((root / "deploy/aios-v2/v2r2c1-rollback.json").read_text(encoding="utf-8"))))
names = sorted("payload/" + str(path.relative_to(root)).replace(os.sep, "/") for path in root.rglob("*") if path.is_file())
(root / "ARTIFACT-MANIFEST.json").write_bytes(canonical({
"schema": "aios.release-artifact-manifest.v3",
"version": VERSION,
"source_head_oid": binding.target_oid,
"evidence_closure_oid": binding.evidence_closure_oid,
"repository": REPOSITORY,
"files": names + ["payload/ARTIFACT-MANIFEST.json"],
"deployable": True,
"executable_payload": True,
"production_deployed": False,
"tag_created": False,
"baseline_changed": False,
}))
tar_path = Path(temp) / "release.tar"
with tarfile.open(tar_path, "w") as archive:
for path in sorted(root.rglob("*")):
relative = str(path.relative_to(root)).replace(os.sep, "/")
info = archive.gettarinfo(str(path), arcname="payload/" + relative)
info.uid = info.gid = 0
info.uname = "root"
info.gname = "root"
info.mtime = epoch
info.mode = 0o755 if path.is_dir() or path.suffix == ".sh" else 0o644
if path.is_file():
with path.open("rb") as stream:
archive.addfile(info, stream)
else:
archive.addfile(info)
subprocess.run(["zstd", "-T1", "-19", "--no-progress", "-q", "-f", str(tar_path), "-o", str(output)], check=True, timeout=120)
digest = sha256_file(output)
receipt = {
"schema": "aios.release-artifact-receipt.v3",
"version": VERSION,
"repository": REPOSITORY,
"source_head_oid": binding.target_oid,
"evidence_closure_oid": binding.evidence_closure_oid,
"artifact": str(output),
"artifact_sha256": digest,
"artifact_size": output.stat().st_size,
"deployable": True,
"executable_payload": True,
"production_deployed": False,
"tag_created": False,
"baseline_changed": False,
}
atomic_json(output.with_suffix(output.suffix + ".receipt.json"), receipt, mode=0o640)
return receipt
class GovernedProductionReleaseExecutor:
def __init__(self, config: Any):
self.config = config
self.binding = ReleaseBinding()
self.binding.validate()
self.root = Path(config.state_root) / "v2r2c1-governed-production-release"
self.state = ReleaseState(self.root, self.binding)
workspace = self.root / "publication-workspace"
workspace.mkdir(parents=True, exist_ok=True)
self.publisher = TagPublisher(
workspace,
GitTagBackend(workspace, authoritative_repo=Path(config.authoritative_git_dir)),
GiteaTagClient(config.api_base, REPOSITORY, Path(config.token_path)),
config.remote_name,
state_root=self.root / "tag-publication",
)
def _request(self, request: Mapping[str, Any]) -> PublicationRequest:
binding = self.binding
for key, expected in binding.as_dict().items():
if key in request and request[key] != expected:
raise ManualOwnerReviewRequired("release request binding mismatch:" + key)
return PublicationRequest(binding.version, binding.target_oid, binding.evidence_closure_oid, binding.approval_plan_id)
def approval_request(self, request: Mapping[str, Any]) -> dict[str, Any]:
if request.get("execute") is not False:
raise ReleaseBlocked("TAG_APPROVAL_REQUEST_MUST_BE_DRY_RUN")
owner = verify_owner_approval(request, self.binding)
with StateLock(self.root / "release.lock"):
current = self.state.value.get("approval")
approval_digest = hashlib.sha256(canonical(owner)).hexdigest()
record = {
"schema": "aios.v2r2c1.tag-approval-request.v1",
"binding": self.binding.as_dict(),
"owner_approval": owner,
"owner_approval_digest_sha256": approval_digest,
"execute": False,
"recorded_at": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()),
}
if current and current != record:
raise ManualOwnerReviewRequired("persisted Owner approval differs")
self.state.value["approval"] = record
self.state.record("APPROVAL_REQUEST_RECORDED", "tag_approval_request", record)
return {"status": "RECORDED", "execute": False, "binding": self.binding.as_dict(), "owner_approval_digest_sha256": approval_digest}
def _require_approval(self) -> None:
approval = self.state.value.get("approval")
if not isinstance(approval, Mapping) or approval.get("binding") != self.binding.as_dict():
raise ReleaseBlocked("OWNER_APPROVAL_REQUEST_NOT_RECORDED")
def publish_tag(self, request: Mapping[str, Any]) -> dict[str, Any]:
if request.get("execute") is not True:
raise ReleaseBlocked("TAG_PUBLICATION_REQUIRES_EXECUTE_TRUE")
self._require_approval()
publication = self._request(request)
with StateLock(self.root / "release.lock"):
try:
verified = self.publisher._verify_publication(publication, allow_push=True, previously_deployed=False)
except PublicationManualOwnerReviewRequired as exc:
self.state.fail("MANUAL_OWNER_REVIEW_REQUIRED", str(exc))
raise ManualOwnerReviewRequired(str(exc)) from exc
except PublicationPending as exc:
self.state.fail("REMOTE_PUSH_PENDING", str(exc))
raise ReconciliationRequired(str(exc)) from exc
except PublicationReconciliationRequired as exc:
self.state.fail("RECONCILIATION_REQUIRED", str(exc))
raise ReconciliationRequired(str(exc)) from exc
result = dict(verified)
self.state.record("TAG_VERIFIED", "tag", result)
return {"status": "REMOTE_VERIFIED", **result}
def tag_status(self, request: Mapping[str, Any]) -> dict[str, Any]:
self._request(request)
return self.publisher.get_tag_publication_status(VERSION)
def build_artifact(self, request: Mapping[str, Any]) -> dict[str, Any]:
self._require_approval()
self._request(request)
receipt = build_production_artifact(Path(self.config.authoritative_git_dir), self.binding, self.root / "artifacts")
self.state.record("ARTIFACT_VERIFIED", "production_artifact", receipt)
return {"status": "VERIFIED", **receipt}
def execute_request(self, request: Mapping[str, Any]) -> dict[str, Any]:
self._require_approval()
publication = self._request(request)
tag_state = self.state.value.get("completed", {}).get("tag")
if not isinstance(tag_state, Mapping) or tag_state.get("state") not in {"GITEA_API_CONFIRMED", "REMOTE_VERIFIED"}:
raise ReleaseBlocked("REMOTE_TAG_PROOF_REQUIRED")
artifact = self.state.value.get("completed", {}).get("production_artifact")
if not isinstance(artifact, Mapping):
artifact = self.build_artifact(request)
value = {
**self.binding.as_dict(),
"tag_oid": tag_state.get("tag_oid") or tag_state.get("gitea_tag_oid"),
"artifact_path": artifact["artifact"],
"artifact_size": int(artifact["artifact_size"]),
"artifact_sha256": artifact["artifact_sha256"],
}
# The production adapter is privileged and is run through the root
# approved-release service boundary by the caller. This handler is
# still useful for a root-owned staging invocation and remains
# fail-closed when the adapter is not available.
import v2r2c1_production_adapter
runtime = v2r2c1_production_adapter.execute_request({"schema": "aios.production-executor-request.v1", "operation": "apply", **value})
if runtime.get("status") != "ACTIVE_VERIFIED":
self.state.fail("RECONCILIATION_REQUIRED", "production health did not verify")
raise ReconciliationRequired("production health did not verify")
self.state.record("PRODUCTION_VERIFIED", "production", runtime)
return {"status": "PRODUCTION_VERIFIED", "runtime": runtime, "artifact": artifact, "tag_oid": value["tag_oid"]}
def consistency(self, request: Mapping[str, Any]) -> dict[str, Any]:
publication = self._request(request)
result = self.publisher.get_release_consistency(publication)
result["release_binding"] = self.binding.as_dict()
result["production_health"] = _health(self.config.health_url, self.binding, self.state.value.get("completed", {}).get("production_artifact", {}))
result["registry"] = _read_json(Path(self.config.registry_root) / f"{VERSION}.json")
result["baseline"] = _read_json(Path(self.config.baseline_path))
return result
def acceptance(self, request: Mapping[str, Any]) -> dict[str, Any]:
result = self.consistency(request)
result["accepted"] = result.get("status") == "PASS" and result.get("production_health", {}).get("status") == "ACTIVE_VERIFIED"
self.state.record("ACCEPTED" if result["accepted"] else "RECONCILIATION_REQUIRED", "acceptance", result)
return result
def _read_json(path: Path) -> dict[str, Any]:
try:
value = json.loads(path.read_text(encoding="utf-8"))
except FileNotFoundError:
return {"status": "MISSING", "path": str(path)}
if not isinstance(value, dict):
return {"status": "INVALID", "path": str(path)}
return {"status": "PRESENT", "path": str(path), **value}
def _health(url: str, binding: ReleaseBinding, artifact: Mapping[str, Any]) -> dict[str, Any]:
import urllib.request
try:
with urllib.request.urlopen(url, timeout=5) as response:
body = json.loads(response.read().decode("utf-8"))
status = response.status
except Exception as exc:
return {"status": "UNAVAILABLE", "error": type(exc).__name__}
expected_artifact = artifact.get("artifact_sha256")
exact = (
status == 200
and body.get("version") == binding.version
and body.get("commit_oid") == binding.target_oid
and (not expected_artifact or body.get("artifact_sha256") == expected_artifact)
and body.get("deployment_state") == "ACTIVE"
and body.get("production_deployed") is True
)
return {"status": "ACTIVE_VERIFIED" if exact else "CONFLICT", "http_status": status, **body}
def build_executor_config(config: Any) -> GovernedProductionReleaseExecutor:
return GovernedProductionReleaseExecutor(config)
def main() -> int:
parser = argparse.ArgumentParser()
parser.add_argument("operation", choices=("build-artifact", "status"))
parser.add_argument("--git-dir", type=Path, default=Path("/data/aios-gitops/repo.git"))
parser.add_argument("--output-root", type=Path, default=Path("/var/lib/aios-gitops/approved-artifacts/V2R2C1"))
args = parser.parse_args()
binding = ReleaseBinding()
if args.operation == "build-artifact":
print(json.dumps(build_production_artifact(args.git_dir, binding, args.output_root), sort_keys=True))
else:
print(json.dumps({"binding": binding.as_dict(), "status": "READY"}, sort_keys=True))
return 0
if __name__ == "__main__":
raise SystemExit(main())
+225
View File
@@ -0,0 +1,225 @@
#!/usr/bin/env python3
"""AIOS durable autonomous release-campaign authorization.
The campaign receipt represents one explicit Owner policy delegation. It is not
a reusable Owner credential and it is not an unrestricted Git/production token.
Normal release operations are allowed only when all machine gates in the
campaign policy succeed. Formal tag conflicts, non-fast-forward main divergence,
history rewrite, private-signing-material exposure, repository identity conflict
or campaign revocation remain fail-closed/manual-review conditions.
"""
from __future__ import annotations
from dataclasses import dataclass
import hashlib
import json
import os
from pathlib import Path
import re
import stat
from typing import Any, Mapping
OID_RE = re.compile(r"^[0-9a-f]{40}$")
CAMPAIGN_RE = re.compile(r"^[A-Za-z0-9][A-Za-z0-9._:-]{3,127}$")
SCHEMA = "aios.release-campaign-authorization.v1"
ALLOWED_ENGINEERING = frozenset({
"create_task_branch",
"create_worktree",
"append_commit",
"push_task_branch",
"repair_task_branch",
"create_pr",
"update_pr",
"run_required_checks",
"publish_truthful_status",
"protected_merge",
})
ALLOWED_RECONCILIATION = frozenset({
"fetch_gitea_main",
"verify_ancestor",
"authority_main_ff_cas",
"refresh_runtime_context",
})
ALLOWED_RELEASE = frozenset({
"deterministic_build",
"retain_artifact",
"materialize_approved_artifact",
"staging_install",
"health_check",
"restart_test",
"rollback_readiness_test",
"create_evidence_commit",
"generate_exact_binding",
"derive_campaign_acceptance",
})
ALLOWED_PRODUCTION = frozenset({
"create_annotated_tag",
"publish_tag",
"verify_tag",
"deploy_exact_artifact",
"verify_production_health",
"update_runtime_registry",
"update_production_baseline",
"update_baseline_sidecar",
})
DENIED = frozenset({
"force_push",
"non_fast_forward_main_write",
"direct_main_push",
"published_history_rewrite",
"formal_tag_rewrite",
"formal_tag_delete",
"overwrite_conflicting_same_name_tag",
"expose_private_signing_material",
"commit_plaintext_secret",
"repository_identity_change",
"weaken_branch_protection",
})
class CampaignError(RuntimeError):
pass
def canonical_json(value: Any) -> bytes:
return (
json.dumps(value, sort_keys=True, separators=(",", ":"), ensure_ascii=False)
+ "\n"
).encode("utf-8")
def sha256_bytes(value: bytes) -> str:
return hashlib.sha256(value).hexdigest()
def secure_json(path: Path) -> dict[str, Any]:
if path.is_symlink() or not path.is_file():
raise CampaignError(f"invalid_file:{path}")
st = path.lstat()
if st.st_uid != 0:
raise CampaignError(f"root_owner_required:{path}")
if stat.S_IMODE(st.st_mode) & 0o022:
raise CampaignError(f"group_world_writable_rejected:{path}")
try:
value = json.loads(path.read_text(encoding="utf-8"))
except (UnicodeDecodeError, json.JSONDecodeError) as exc:
raise CampaignError(f"invalid_json:{path}") from exc
if not isinstance(value, dict):
raise CampaignError(f"json_object_required:{path}")
return value
def _exact_set(value: Any, expected: frozenset[str], name: str) -> None:
if not isinstance(value, list) or frozenset(value) != expected:
raise CampaignError(f"{name}_mismatch")
def validate_policy(value: Mapping[str, Any]) -> dict[str, Any]:
required = {
"schema",
"campaign_id",
"generation",
"owner_identity",
"owner_role",
"repository",
"lineage_anchor_oid",
"control_plane_branch",
"activation_state",
"active",
"revoked",
"valid_until_revoked",
"allowed_engineering",
"allowed_reconciliation",
"allowed_release",
"allowed_production",
"denied",
"normal_owner_interaction_required",
}
if set(value) != required:
raise CampaignError("campaign_fields_invalid")
if value["schema"] != SCHEMA:
raise CampaignError("campaign_schema_invalid")
if not isinstance(value["campaign_id"], str) or not CAMPAIGN_RE.fullmatch(value["campaign_id"]):
raise CampaignError("campaign_id_invalid")
if value["generation"] != 1:
raise CampaignError("campaign_generation_invalid")
if value["owner_identity"] != "aiman" or value["owner_role"] != "owner":
raise CampaignError("campaign_owner_invalid")
if value["repository"] != "aios/aios":
raise CampaignError("campaign_repository_invalid")
if not isinstance(value["lineage_anchor_oid"], str) or not OID_RE.fullmatch(value["lineage_anchor_oid"]):
raise CampaignError("campaign_anchor_invalid")
branch = value["control_plane_branch"]
if branch != "agent/autonomous-release-campaign-v1-20260807":
raise CampaignError("campaign_branch_invalid")
if value["activation_state"] not in {"PENDING_CONTROL_PLANE_MERGE", "ACTIVE"}:
raise CampaignError("campaign_activation_state_invalid")
if value["revoked"] is not False or value["valid_until_revoked"] is not True:
raise CampaignError("campaign_lifecycle_invalid")
if value["normal_owner_interaction_required"] is not False:
raise CampaignError("campaign_owner_interaction_policy_invalid")
if value["activation_state"] == "ACTIVE" and value["active"] is not True:
raise CampaignError("campaign_active_state_invalid")
if value["activation_state"] == "PENDING_CONTROL_PLANE_MERGE" and value["active"] is not False:
raise CampaignError("campaign_pending_state_invalid")
_exact_set(value["allowed_engineering"], ALLOWED_ENGINEERING, "allowed_engineering")
_exact_set(value["allowed_reconciliation"], ALLOWED_RECONCILIATION, "allowed_reconciliation")
_exact_set(value["allowed_release"], ALLOWED_RELEASE, "allowed_release")
_exact_set(value["allowed_production"], ALLOWED_PRODUCTION, "allowed_production")
_exact_set(value["denied"], DENIED, "denied")
return dict(value)
@dataclass(frozen=True)
class CampaignAuthorization:
path: Path
data: dict[str, Any]
digest: str
@classmethod
def load(
cls,
path: Path,
*,
sidecar: Path | None = None,
revocation: Path | None = None,
require_active: bool = True,
) -> "CampaignAuthorization":
data = validate_policy(secure_json(path))
raw = path.read_bytes()
digest = sha256_bytes(raw)
sidecar = sidecar or path.with_suffix(path.suffix + ".sha256")
if not sidecar.is_file() or sidecar.is_symlink():
raise CampaignError("campaign_digest_sidecar_missing")
token = sidecar.read_text(encoding="ascii").strip().split()[0]
if token != digest:
raise CampaignError("campaign_digest_sidecar_mismatch")
if revocation and revocation.exists():
revoked = secure_json(revocation)
if revoked.get("campaign_id") == data["campaign_id"] and revoked.get("revoked") is True:
raise CampaignError("campaign_revoked")
if require_active and (data["activation_state"] != "ACTIVE" or data["active"] is not True):
raise CampaignError("campaign_not_active")
return cls(path=path, data=data, digest=digest)
@property
def campaign_id(self) -> str:
return str(self.data["campaign_id"])
@property
def anchor_oid(self) -> str:
return str(self.data["lineage_anchor_oid"])
def assert_operation(self, operation: str) -> None:
if operation in DENIED:
raise CampaignError(f"campaign_operation_denied:{operation}")
universe = (
ALLOWED_ENGINEERING
| ALLOWED_RECONCILIATION
| ALLOWED_RELEASE
| ALLOWED_PRODUCTION
)
if operation not in universe:
raise CampaignError(f"campaign_operation_out_of_scope:{operation}")
+427
View File
@@ -0,0 +1,427 @@
#!/usr/bin/env python3
"""One-interaction bootstrap for the AIOS autonomous release campaign.
A single real local `aiman` login confirmation does two things atomically at the
policy layer:
1. Writes a root-owned, digest-sidecar protected campaign authorization receipt
in PENDING_CONTROL_PLANE_MERGE state. It grants no immediate production side
effect; the campaign becomes ACTIVE only after the campaign control-plane PR
is merged and machine activation verifies lineage and implementation.
2. Invokes the existing OwnerAuthorizationBroker to issue only the narrow
engineering writer needed to implement that control-plane PR.
No reusable Owner token is minted.
"""
from __future__ import annotations
import argparse
import fcntl
import hashlib
import json
import os
from pathlib import Path
import secrets
import socket
import struct
import tempfile
import time
from typing import Any, Mapping
from owner_local_auth_ingress import (
BootstrapError,
canonical_json,
read_frame,
verify_peer_session,
write_frame,
)
from release_campaign_authorization import (
ALLOWED_ENGINEERING,
ALLOWED_PRODUCTION,
ALLOWED_RECONCILIATION,
ALLOWED_RELEASE,
DENIED,
SCHEMA,
validate_policy,
)
BEGIN = "aios.release-campaign-bootstrap.begin.v1"
CHALLENGE = "aios.release-campaign-bootstrap.challenge.v1"
CONFIRM = "aios.release-campaign-bootstrap.confirm.v1"
RESULT = "aios.release-campaign-bootstrap.result.v1"
TASK_ID = "AIOS_AUTONOMOUS_RELEASE_CAMPAIGN_V1"
TARGET_BRANCH = "agent/autonomous-release-campaign-v1-20260807"
REPOSITORY = "aios/aios"
class CampaignBootstrapError(RuntimeError):
pass
def sha256_file(path: Path) -> str:
h = hashlib.sha256()
with path.open("rb") as stream:
for block in iter(lambda: stream.read(1024 * 1024), b""):
h.update(block)
return h.hexdigest()
def atomic_json_new(path: Path, value: Mapping[str, Any]) -> None:
if path.exists() or path.is_symlink():
raise CampaignBootstrapError(f"target_exists:{path}")
path.parent.mkdir(parents=True, exist_ok=True)
fd, name = tempfile.mkstemp(prefix=f".{path.name}.", dir=path.parent)
try:
os.fchmod(fd, 0o600)
if os.geteuid() == 0:
os.fchown(fd, 0, 0)
with os.fdopen(fd, "wb", closefd=True) as stream:
stream.write(canonical_json(dict(value)))
stream.flush()
os.fsync(stream.fileno())
os.replace(name, path)
os.chmod(path, 0o600)
dfd = os.open(path.parent, os.O_RDONLY | os.O_DIRECTORY)
try:
os.fsync(dfd)
finally:
os.close(dfd)
finally:
try:
os.unlink(name)
except FileNotFoundError:
pass
def atomic_sidecar(path: Path, digest: str, target_name: str) -> None:
if path.exists() or path.is_symlink():
raise CampaignBootstrapError(f"sidecar_exists:{path}")
path.parent.mkdir(parents=True, exist_ok=True)
fd, name = tempfile.mkstemp(prefix=f".{path.name}.", dir=path.parent)
try:
os.fchmod(fd, 0o600)
if os.geteuid() == 0:
os.fchown(fd, 0, 0)
with os.fdopen(fd, "w", encoding="ascii", closefd=True) as stream:
stream.write(f"{digest} {target_name}\n")
stream.flush()
os.fsync(stream.fileno())
os.replace(name, path)
os.chmod(path, 0o600)
dfd = os.open(path.parent, os.O_RDONLY | os.O_DIRECTORY)
try:
os.fsync(dfd)
finally:
os.close(dfd)
finally:
try:
os.unlink(name)
except FileNotFoundError:
pass
def writer_request(base_oid: str) -> dict[str, Any]:
return {
"action": "AUTHORIZE_TASK_WRITER_PROVISIONING",
"task_id": TASK_ID,
"repository": REPOSITORY,
"base_ref": "refs/heads/main",
"base_oid": base_oid,
"target_branch": TARGET_BRANCH,
"one_time_use": True,
"expires_in_seconds": 300,
"production_change_authorized": False,
"tag_change_authorized": False,
"baseline_change_authorized": False,
"main_write_authorized": False,
"allowed_operations": [
"create_worktree",
"create_target_branch_if_absent",
"commit_to_target_branch",
"push_target_branch",
],
"denied_operations": [
"direct_main_push",
"force_push",
"tag_create",
"tag_update",
"tag_delete",
"branch_delete",
"repository_admin",
"baseline_write",
"production_deploy",
"production_marker_write",
"history_rewrite",
],
}
def campaign_policy(campaign_id: str, base_oid: str) -> dict[str, Any]:
value = {
"schema": SCHEMA,
"campaign_id": campaign_id,
"generation": 1,
"owner_identity": "aiman",
"owner_role": "owner",
"repository": REPOSITORY,
"lineage_anchor_oid": base_oid,
"control_plane_branch": TARGET_BRANCH,
"activation_state": "PENDING_CONTROL_PLANE_MERGE",
"active": False,
"revoked": False,
"valid_until_revoked": True,
"allowed_engineering": sorted(ALLOWED_ENGINEERING),
"allowed_reconciliation": sorted(ALLOWED_RECONCILIATION),
"allowed_release": sorted(ALLOWED_RELEASE),
"allowed_production": sorted(ALLOWED_PRODUCTION),
"denied": sorted(DENIED),
"normal_owner_interaction_required": False,
}
return validate_policy(value)
def invoke_writer_broker(base_oid: str) -> dict[str, Any]:
from owner_authorization_broker import (
OwnerAuthorizationBroker,
OwnerAuthorizationBrokerConfig,
OwnerPrincipal,
)
config = OwnerAuthorizationBrokerConfig.from_environment()
if "aiman" not in config.allowed_owner_identities:
raise CampaignBootstrapError("aiman_not_allowed_by_live_broker")
if "commit:create" not in config.allowed_intake_scopes:
raise CampaignBootstrapError("commit_create_not_allowed_by_live_broker")
principal = OwnerPrincipal(
identity="aiman",
role="owner",
authenticated_scope="commit:create",
token_scopes=frozenset({"commit:create"}),
credential_id="",
)
return OwnerAuthorizationBroker(config=config).authorize_and_provision(
writer_request(base_oid),
principal,
)
def safe_broker_result(value: Mapping[str, Any]) -> dict[str, Any]:
allowed = {
"status",
"approval_plan_id",
"grant_id",
"grant_digest_sha256",
"branch_oid",
"branch_ref_created",
"grant_consumed",
"owner_authorization_event_id",
"owner_authorization_reference",
"owner_authorization_digest_sha256",
"owner_authorization_consumed",
"owner_authorization_idempotent",
}
lowered = " ".join(str(k).lower() for k in value)
for secret_word in ("token", "password", "private_key", "secret_value", "bearer"):
if secret_word in lowered:
raise CampaignBootstrapError("broker_response_sensitive_field_rejected")
return {k: value[k] for k in sorted(allowed) if k in value}
def serve(
*,
socket_path: Path,
state_root: Path,
base_oid: str,
campaign_id: str,
timeout: int,
) -> int:
if os.geteuid() != 0:
raise CampaignBootstrapError("server_root_required")
if len(base_oid) != 40 or any(c not in "0123456789abcdef" for c in base_oid):
raise CampaignBootstrapError("base_oid_invalid")
policy = campaign_policy(campaign_id, base_oid)
campaign_dir = state_root / campaign_id
campaign_path = campaign_dir / "authorization.json"
sidecar_path = campaign_dir / "authorization.json.sha256"
socket_path.parent.mkdir(parents=True, exist_ok=True)
if socket_path.exists() or socket_path.is_symlink():
raise CampaignBootstrapError("socket_already_exists")
import pwd
owner = pwd.getpwnam("aiman")
os.chown(socket_path.parent, 0, owner.pw_gid)
os.chmod(socket_path.parent, 0o750)
listener = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
try:
listener.bind(str(socket_path))
os.chown(socket_path, 0, owner.pw_gid)
os.chmod(socket_path, 0o660)
listener.listen(1)
listener.settimeout(timeout)
conn, _ = listener.accept()
with conn:
raw = conn.getsockopt(socket.SOL_SOCKET, socket.SO_PEERCRED, struct.calcsize("3i"))
pid, uid, gid = struct.unpack("3i", raw)
peer = verify_peer_session(pid, uid, gid, "aiman")
if read_frame(conn) != {"schema": BEGIN, "action": "BEGIN"}:
raise CampaignBootstrapError("client_begin_invalid")
challenge = secrets.token_hex(12)
issued = time.monotonic()
summary = {
"campaign_id": campaign_id,
"repository": REPOSITORY,
"lineage_anchor_oid": base_oid,
"control_plane_branch": TARGET_BRANCH,
"normal_owner_interaction_after_activation": False,
"allowed_engineering": sorted(ALLOWED_ENGINEERING),
"allowed_reconciliation": sorted(ALLOWED_RECONCILIATION),
"allowed_release": sorted(ALLOWED_RELEASE),
"allowed_production": sorted(ALLOWED_PRODUCTION),
"denied": sorted(DENIED),
"writer_request": writer_request(base_oid),
}
write_frame(
conn,
{
"schema": CHALLENGE,
"challenge": challenge,
"expires_in_seconds": 120,
"summary": summary,
},
)
confirm = read_frame(conn)
phrase = f"AUTHORIZE CAMPAIGN {campaign_id} {challenge}"
if (
confirm.get("schema") != CONFIRM
or confirm.get("challenge") != challenge
or confirm.get("confirmation") != phrase
):
raise CampaignBootstrapError("campaign_confirmation_invalid")
if time.monotonic() - issued > 120:
raise CampaignBootstrapError("campaign_confirmation_expired")
# First issue the narrow engineering writer. If this fails, do not
# persist a campaign authorization that cannot be implemented.
broker = safe_broker_result(invoke_writer_broker(base_oid))
if broker.get("status") != "READY_FOR_CODE_AUTHORING":
raise CampaignBootstrapError("writer_not_ready")
atomic_json_new(campaign_path, policy)
digest = sha256_file(campaign_path)
atomic_sidecar(sidecar_path, digest, campaign_path.name)
receipt = {
"schema": "aios.release-campaign-bootstrap-receipt.v1",
"status": "CAMPAIGN_PENDING_CONTROL_PLANE_MERGE",
"campaign_id": campaign_id,
"campaign_authorization_path": str(campaign_path),
"campaign_authorization_sha256": digest,
"owner_identity": "aiman",
"owner_role": "owner",
"authentication": {
"method": "local_so_peercred_loginuid_tty",
"uid": peer["uid"],
"loginuid": peer["loginuid"],
"tty": peer["tty"],
},
"writer": broker,
"reusable_owner_token_created": False,
"production_side_effect_executed": False,
"completed_at_unix": int(time.time()),
}
receipt_path = campaign_dir / "bootstrap-receipt.json"
atomic_json_new(receipt_path, receipt)
write_frame(
conn,
{
"schema": RESULT,
"ok": True,
"campaign_id": campaign_id,
"campaign_authorization_path": str(campaign_path),
"campaign_authorization_sha256": digest,
"writer": broker,
},
)
return 0
finally:
listener.close()
try:
socket_path.unlink()
except FileNotFoundError:
pass
def client(socket_path: Path) -> int:
import sys
if not sys.stdin.isatty() or not sys.stdout.isatty():
raise CampaignBootstrapError("interactive_tty_required")
conn = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
conn.connect(str(socket_path))
with conn:
write_frame(conn, {"schema": BEGIN, "action": "BEGIN"})
challenge = read_frame(conn)
if challenge.get("schema") != CHALLENGE:
raise CampaignBootstrapError("challenge_invalid")
print("\nAIOS Autonomous Release Campaign authorization")
print(json.dumps(challenge["summary"], indent=2, sort_keys=True))
phrase = (
f"AUTHORIZE CAMPAIGN "
f"{challenge['summary']['campaign_id']} "
f"{challenge['challenge']}"
)
typed = input(f"\nType exactly:\n{phrase}\n> ").strip()
if typed != phrase:
raise CampaignBootstrapError("confirmation_mismatch")
write_frame(
conn,
{
"schema": CONFIRM,
"challenge": challenge["challenge"],
"confirmation": typed,
},
)
result = read_frame(conn)
print(json.dumps(result, indent=2, sort_keys=True))
return 0 if result.get("ok") is True else 3
def main() -> int:
p = argparse.ArgumentParser()
mode = p.add_mutually_exclusive_group(required=True)
mode.add_argument("--serve", action="store_true")
mode.add_argument("--client", action="store_true")
p.add_argument("--socket", type=Path, default=Path("/run/aios-release-campaign/owner.sock"))
p.add_argument(
"--state-root",
type=Path,
default=Path("/var/lib/aios-gitops/release-campaigns"),
)
p.add_argument("--base-oid")
p.add_argument("--campaign-id")
p.add_argument("--timeout", type=int, default=300)
a = p.parse_args()
if a.serve:
if not a.base_oid or not a.campaign_id:
raise CampaignBootstrapError("server_binding_required")
return serve(
socket_path=a.socket,
state_root=a.state_root,
base_oid=a.base_oid,
campaign_id=a.campaign_id,
timeout=a.timeout,
)
return client(a.socket)
if __name__ == "__main__":
try:
raise SystemExit(main())
except (CampaignBootstrapError, BootstrapError) as exc:
print(json.dumps({"status": "BLOCKED", "reason": str(exc)}, sort_keys=True))
raise SystemExit(2)
+113
View File
@@ -0,0 +1,113 @@
#!/usr/bin/env python3
"""Durable state model for the AIOS autonomous release campaign."""
from __future__ import annotations
from dataclasses import dataclass
import json
import os
from pathlib import Path
import tempfile
from typing import Any
STATES = (
"DEVELOPMENT",
"VALIDATING",
"PR_READY",
"MERGING",
"MAIN_RECONCILING",
"POSTMERGE_BUILDING",
"STAGING",
"EVIDENCE_CLOSING",
"BINDING_ACCEPTING",
"TAGGING",
"DEPLOYING",
"VERIFYING",
"REGISTRY_FINALIZING",
"BASELINE_FINALIZING",
"PRODUCTION_DEPLOYED",
)
RECOVERABLE = "RECOVERABLE_FAILURE"
RECONCILIATION = "RECONCILIATION_REQUIRED"
MANUAL = "MANUAL_OWNER_REVIEW_REQUIRED"
NEXT = {STATES[i]: STATES[i + 1] for i in range(len(STATES) - 1)}
MANUAL_REASONS = frozenset({
"formal_same_name_tag_conflict",
"force_push_required",
"published_history_rewrite_required",
"non_fast_forward_main_divergence",
"private_signing_material_exposure_required",
"repository_identity_conflict",
"campaign_revoked",
"operation_outside_campaign_scope",
})
class OrchestratorError(RuntimeError):
pass
@dataclass
class ReleaseState:
version: str
state: str = "DEVELOPMENT"
last_stable_state: str = "DEVELOPMENT"
recoverable_reason: str | None = None
def advance(self) -> None:
if self.state not in NEXT:
raise OrchestratorError(f"cannot_advance:{self.state}")
self.last_stable_state = self.state
self.state = NEXT[self.state]
self.recoverable_reason = None
def recoverable_failure(self, reason: str) -> None:
self.last_stable_state = self.state
self.state = RECOVERABLE
self.recoverable_reason = reason
def reconciliation_required(self, reason: str) -> None:
self.last_stable_state = self.state
self.state = RECONCILIATION
self.recoverable_reason = reason
def manual_review(self, reason: str) -> None:
if reason not in MANUAL_REASONS:
raise OrchestratorError("reason_not_manual_red_line")
self.last_stable_state = self.state
self.state = MANUAL
self.recoverable_reason = reason
def resume(self) -> None:
if self.state not in {RECOVERABLE, RECONCILIATION}:
raise OrchestratorError("state_not_resumable")
self.state = self.last_stable_state
def as_dict(self) -> dict[str, Any]:
return {
"schema": "aios.autonomous-release-state.v1",
"version": self.version,
"state": self.state,
"last_stable_state": self.last_stable_state,
"recoverable_reason": self.recoverable_reason,
"owner_interaction_required": self.state == MANUAL,
}
def atomic_state(path: Path, state: ReleaseState) -> None:
path.parent.mkdir(parents=True, exist_ok=True)
fd, name = tempfile.mkstemp(prefix=f".{path.name}.", dir=path.parent)
try:
os.fchmod(fd, 0o600)
with os.fdopen(fd, "w", encoding="utf-8", closefd=True) as stream:
json.dump(state.as_dict(), stream, sort_keys=True, indent=2)
stream.write("\n")
stream.flush()
os.fsync(stream.fileno())
os.replace(name, path)
finally:
try:
os.unlink(name)
except FileNotFoundError:
pass
@@ -0,0 +1,135 @@
"""V2R2C1 adapter using the existing V2R1C7 deployment primitives.
The service identity and loopback port remain stable for a safe cutover. The
new release is installed under its own immutable directory and keeps the
previous V2R1C7 release available for an explicit, owner-approved rollback.
"""
from __future__ import annotations
import json
import os
import shutil
import sys
from argparse import Namespace
from pathlib import Path
from typing import Any, Mapping
import v2r1c7_production_adapter as base
VERSION = "V2R2C1"
SERVICE = "aios-v2r1c7-production.service"
PORT = 18927
RELEASE_ROOT = Path("/opt/aios/releases")
CURRENT_LINK = Path("/opt/aios/v2r2c1")
ENV_FILE = Path("/etc/aios/aios-v2r2c1.env")
STATE_FILE = Path("/var/lib/aios/v2r2c1/deployment-state.json")
UNIT_FILE = Path("/etc/systemd/system/aios-v2r1c7-production.service")
ROLLBACK_ROOT = Path("/var/lib/aios/v2r2c1/rollback")
REQUIRED_MEMBERS = {
"payload/VERSION",
"payload/active-runtime/app/deployment_state.py",
"payload/active-runtime/app/v2r1c6_privacy_tools.py",
"payload/active-runtime/app/v2r2c1_health_service.py",
"payload/deploy/aios-v2/systemd/aios-v2r1c7-production.service",
"payload/deploy/aios-v2/v2r2c1-runtime-config.json",
"payload/deploy/aios-v2/v2r2c1-rollback.json",
"payload/scripts/probe_v2r2c1_health.py",
"payload/scripts/rollback_v2r2c1_release.sh",
"payload/BUILD-METADATA.json",
"payload/ARTIFACT-MANIFEST.json",
"payload/ROLLBACK-METADATA.json",
}
def _configure_base() -> None:
base.VERSION = VERSION
base.SERVICE = SERVICE
base.PORT = PORT
base.REQUIRED_MEMBERS = REQUIRED_MEMBERS
def _args() -> Namespace:
return Namespace(
release_root=RELEASE_ROOT,
current_link=CURRENT_LINK,
env_file=ENV_FILE,
state_file=STATE_FILE,
unit_file=UNIT_FILE,
health_timeout=45,
)
def load_request() -> dict[str, Any]:
_configure_base()
value = base.load_request()
if value.get("version") != VERSION or value.get("production_service") != SERVICE or int(value.get("production_port", -1)) != PORT:
raise ValueError("V2R2C1 production binding mismatch")
return value
def _backup(path: Path, name: str) -> None:
if path.is_file() and not path.is_symlink():
ROLLBACK_ROOT.mkdir(parents=True, exist_ok=True)
destination = ROLLBACK_ROOT / name
if not destination.exists():
shutil.copy2(path, destination)
def execute_request(value: Mapping[str, Any]) -> dict[str, Any]:
_configure_base()
if value.get("schema") != "aios.production-executor-request.v1":
raise ValueError("request_schema_invalid")
if value.get("operation") == "apply":
_backup(UNIT_FILE, "aios-v2r1c7-production.service.previous")
_backup(Path("/etc/aios/aios-v2r1c7.env"), "aios-v2r1c7.env.previous")
_backup(Path("/var/lib/aios/v2r1c7/deployment-state.json"), "v2r1c7-deployment-state.previous")
return base.install_release(_args(), value)
if value.get("operation") == "probe":
return base.probe(_args(), value)
if value.get("operation") == "rollback":
return rollback(value)
raise ValueError("unsupported_operation")
def rollback(value: Mapping[str, Any]) -> dict[str, Any]:
"""Explicit rollback only; this adapter never invokes it automatically."""
import subprocess
subprocess.run(["systemctl", "stop", SERVICE], check=False, capture_output=True, text=True, timeout=45)
previous_unit = ROLLBACK_ROOT / "aios-v2r1c7-production.service.previous"
previous_env = ROLLBACK_ROOT / "aios-v2r1c7.env.previous"
previous_state = ROLLBACK_ROOT / "v2r1c7-deployment-state.previous"
if not previous_unit.is_file() or not previous_env.is_file():
raise RuntimeError("ROLLBACK_RECEIPT_MISSING")
UNIT_FILE.parent.mkdir(parents=True, exist_ok=True)
ENV_FILE.parent.mkdir(parents=True, exist_ok=True)
shutil.copy2(previous_unit, UNIT_FILE)
shutil.copy2(previous_env, Path("/etc/aios/aios-v2r1c7.env"))
if previous_state.is_file():
Path("/var/lib/aios/v2r1c7").mkdir(parents=True, exist_ok=True)
shutil.copy2(previous_state, Path("/var/lib/aios/v2r1c7/deployment-state.json"))
subprocess.run(["systemctl", "daemon-reload"], check=True, capture_output=True, text=True, timeout=45)
subprocess.run(["systemctl", "restart", SERVICE], check=True, capture_output=True, text=True, timeout=45)
result = base.health("http://127.0.0.1:18927/health", {
"version": "V2R1C7",
"target_oid": str(json.loads(Path("/var/lib/aios/v2r1c7/deployment-state.json").read_text(encoding="utf-8")).get("commit_oid")),
"artifact_sha256": str(json.loads(Path("/var/lib/aios/v2r1c7/deployment-state.json").read_text(encoding="utf-8")).get("artifact_sha256")),
})
return {"status": "ROLLED_BACK", "rollback_health": result, "automatic_rollback": False}
def main() -> int:
try:
value = load_request()
result = execute_request(value)
print(json.dumps(result, sort_keys=True))
return 0
except Exception as exc:
print(json.dumps({"status": "FAIL_CLOSED", "error": type(exc).__name__, "reason": str(exc)}, sort_keys=True))
return 2
if __name__ == "__main__":
raise SystemExit(main())
@@ -0,0 +1,52 @@
#!/bin/sh
set -eu
if [ "$(id -u)" -ne 0 ]; then
echo "ROOT_REQUIRED" >&2
exit 20
fi
SOURCE_ROOT=${1:-}
if [ -z "$SOURCE_ROOT" ]; then
echo "SOURCE_ROOT_REQUIRED" >&2
exit 21
fi
EXECUTOR="$SOURCE_ROOT/active-runtime/app/aios_control_plane_host_seed_executor.py"
SERVICE="$SOURCE_ROOT/deploy/systemd/aios-control-plane-host-seed-executor.service"
TIMER="$SOURCE_ROOT/deploy/systemd/aios-control-plane-host-seed-executor.timer"
for file in "$EXECUTOR" "$SERVICE" "$TIMER"; do
if [ ! -f "$file" ] || [ -L "$file" ]; then
echo "SOURCE_FILE_INVALID:$file" >&2
exit 22
fi
done
AUTHORITY_GIT=/data/aios-gitops/repo.git
REQUEST_REF=refs/heads/agent/host-seed-authorization-requests
if [ ! -d "$AUTHORITY_GIT" ]; then
echo "AUTHORITATIVE_GIT_UNAVAILABLE" >&2
exit 23
fi
if ! id -u aios-bridge-staging >/dev/null 2>&1; then
echo "BRIDGE_SERVICE_ACCOUNT_UNAVAILABLE" >&2
exit 24
fi
if git --git-dir="$AUTHORITY_GIT" show-ref --verify --quiet "$REQUEST_REF"; then
echo "HOST_SEED_REQUEST_REF_ALREADY_PRESENT" >&2
exit 25
fi
install -d -m 0755 /usr/local/libexec
install -m 0755 "$EXECUTOR" /usr/local/libexec/aios-control-plane-host-seed-executor
install -m 0644 "$SERVICE" /etc/systemd/system/aios-control-plane-host-seed-executor.service
install -m 0644 "$TIMER" /etc/systemd/system/aios-control-plane-host-seed-executor.timer
install -d -m 0700 /var/lib/aios-control-plane-host-seed-executor
systemctl daemon-reload
systemctl enable --now aios-control-plane-host-seed-executor.timer
systemctl is-enabled --quiet aios-control-plane-host-seed-executor.timer
systemctl is-active --quiet aios-control-plane-host-seed-executor.timer
echo "HOST_SEED_EXECUTOR_INSTALL=PASS"
@@ -0,0 +1,15 @@
#!/bin/sh
set -eu
if [ "$(id -u)" -ne 0 ]; then
echo "ROOT_REQUIRED" >&2
exit 20
fi
systemctl disable --now aios-control-plane-host-seed-executor.timer 2>/dev/null || true
rm -f /etc/systemd/system/aios-control-plane-host-seed-executor.timer
rm -f /etc/systemd/system/aios-control-plane-host-seed-executor.service
rm -f /usr/local/libexec/aios-control-plane-host-seed-executor
systemctl daemon-reload
echo "HOST_SEED_EXECUTOR_ROLLBACK=PASS"
@@ -0,0 +1,23 @@
[Unit]
Description=AIOS ChatGPT Bridge v2 staging control plane
After=network-online.target
[Service]
Type=simple
User=aios-bridge-staging
Group=aios-bridge-staging
WorkingDirectory=/opt/aios/gitops-control-plane/current
Environment=AIOS_BRIDGE_SCHEMA_VERSION=aios.chatgpt-gitops-bridge.v2
Environment=AIOS_GITOPS_WRITES_ENABLED=false
Environment=AIOS_GITOPS_DURABLE_JOB_EXECUTION_ENABLED=true
Environment=AIOS_DEPLOYMENT_STATE=STAGING
ExecStart=/usr/bin/python3 /opt/aios/gitops-control-plane/current/bridge-v2-staging-server.py
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/var/lib/aios-gitops/bridge-v2-staging
Restart=on-failure
[Install]
WantedBy=multi-user.target
@@ -0,0 +1,41 @@
[Unit]
Description=AIOS governed immutable Host seed executor
After=local-fs.target
[Service]
Type=oneshot
User=root
Group=root
UMask=0077
Environment=PYTHONDONTWRITEBYTECODE=1
Environment=AIOS_HOST_IDENTITY=cn-network-mkt
Environment=AIOS_HOST_SEED_GIT_DIR=/data/aios-gitops/repo.git
Environment=AIOS_HOST_SEED_TASK_BINDING_ROOT=/var/lib/aios-gitops/bridge-v2-staging/task-bindings
Environment=AIOS_HOST_SEED_TASK_BINDING_OWNER=aios-bridge-staging
Environment=AIOS_HOST_SEED_STATE_ROOT=/var/lib/aios-control-plane-host-seed-executor
Environment=AIOS_HOST_SEED_RELEASE_ROOT=/opt/aios/releases
Environment=AIOS_HOST_SEED_REQUEST_REF=refs/heads/agent/host-seed-authorization-requests
Environment=AIOS_HOST_SEED_REQUEST_PATH=control-plane/host-seed/request.json
ExecStart=/usr/bin/python3 -B /usr/local/libexec/aios-control-plane-host-seed-executor execute
NoNewPrivileges=true
PrivateTmp=true
PrivateDevices=true
ProtectSystem=strict
ProtectHome=true
ProtectKernelTunables=true
ProtectKernelModules=true
ProtectControlGroups=true
ProtectKernelLogs=true
LockPersonality=true
RestrictSUIDSGID=true
RestrictRealtime=true
RestrictNamespaces=true
MemoryDenyWriteExecute=true
RestrictAddressFamilies=AF_UNIX
IPAddressDeny=any
CapabilityBoundingSet=CAP_DAC_OVERRIDE CAP_FOWNER CAP_CHOWN
ReadOnlyPaths=/usr/local/libexec/aios-control-plane-host-seed-executor
ReadOnlyPaths=/data/aios-gitops/repo.git
ReadOnlyPaths=/var/lib/aios-gitops/bridge-v2-staging/task-bindings
ReadWritePaths=/var/lib/aios-control-plane-host-seed-executor
ReadWritePaths=/opt/aios/releases
@@ -0,0 +1,12 @@
[Unit]
Description=Poll governed immutable Host seed requests
[Timer]
OnBootSec=2min
OnUnitActiveSec=30s
AccuracySec=1s
Persistent=true
Unit=aios-control-plane-host-seed-executor.service
[Install]
WantedBy=timers.target
@@ -0,0 +1,51 @@
[Unit]
Description=AIOS GitOps Model API V2R2C1 immutable staging runtime
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=aiman
Group=gitops
WorkingDirectory=@RUNTIME_ROOT@
Environment=PYTHONUNBUFFERED=1
Environment=PYTHONDONTWRITEBYTECODE=1
Environment=AIOS_GITOPS_ROOT=/var/lib/aios-gitops
Environment=AIOS_GITOPS_WORKSPACE=@RUNTIME_ROOT@
Environment=AIOS_GITOPS_REPO=/data/aios-gitops/repo.git
Environment=AIOS_GITOPS_BARE_REPO=/data/aios-gitops/repo.git
Environment=AIOS_GITOPS_AUTHORITATIVE_GIT_DIR=/data/aios-gitops/repo.git
Environment=AIOS_GITOPS_REPOSITORY_IDENTITY=aios/aios
Environment=AIOS_GITOPS_BASELINE_AUTHORITY=/var/lib/aios-gitops-runtime-anchor/baseline/current.json
Environment=AIOS_GITOPS_WRITES_ENABLED=false
Environment=AIOS_GITOPS_DURABLE_JOB_EXECUTION_ENABLED=false
Environment=AIOS_GITOPS_JOB_CONTROLLER_ENABLED=false
Environment=AIOS_GITOPS_WORKSPACE_ROLE=runtime-anchor
Environment=AIOS_GITOPS_RUNTIME_OID=@SOURCE_OID@
Environment=AIOS_GITOPS_ARTIFACT_SHA256=@ARTIFACT_SHA256@
Environment=AIOS_GITOPS_DEPLOYMENT_STATE=STAGING
Environment=AIOS_GITOPS_MODEL_API_SERVICE=aios-gitops-model-api-v2r2c1-staging.service
Environment=AIOS_SILENT_RELEASE_EXECUTION_ENABLED=false
Environment=AIOS_SILENT_RELEASE_STATE_ROOT=/var/lib/aios-gitops/model-api-staging/silent-release
Environment=AIOS_BRIDGE_V2_URL=http://127.0.0.1:18892
Environment=AIOS_GITOPS_HOST=127.0.0.1
Environment=AIOS_GITOPS_PORT=18893
Environment=PYTHONPATH=@RUNTIME_ROOT@/app:@RUNTIME_ROOT@/runtime-agent-os
ExecStart=/usr/bin/python3 @RUNTIME_ROOT@/app/aios_model_api_staging.py
Restart=on-failure
RestartSec=2
NoNewPrivileges=true
PrivateTmp=true
ProtectHome=true
ProtectSystem=strict
ProtectKernelTunables=true
ProtectKernelModules=true
ProtectControlGroups=true
LockPersonality=true
RestrictSUIDSGID=true
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6
ReadOnlyPaths=@RUNTIME_ROOT@ /data/aios-gitops/repo.git /var/lib/aios-gitops-runtime-anchor/baseline
ReadWritePaths=/var/lib/aios-gitops/model-api-staging
[Install]
WantedBy=multi-user.target
+33
View File
@@ -0,0 +1,33 @@
[Unit]
Description=AIOS durable GitOps writer worker
Requires=aios-gitops-writer.socket
After=aios-gitops-writer.socket network-online.target
[Service]
Type=simple
User=root
Group=root
UMask=0077
Environment=AIOS_GITOPS_WRITES_ENABLED=false
Environment=AIOS_GITOPS_DURABLE_JOB_EXECUTION_ENABLED=true
Environment=AIOS_GITOPS_JOB_ROOT=/var/lib/aios-gitops/jobs
Environment=AIOS_GITEA_MACHINE_CREDENTIAL_REF=systemd-credential:gitea-machine-token
Environment=AIOS_GITOPS_AUTHORITY_GIT_DIR=/data/aios-gitops/repo.git
Environment=AIOS_GITOPS_WORKTREE_ROOT=/var/lib/aios-gitops/worktrees
Environment=AIOS_GITEA_API=https://g.cnnetmkt.top/git/api/v1
Environment=AIOS_GITEA_REMOTE=https://g.cnnetmkt.top/git/aios/aios.git
Environment=AIOS_GITEA_MACHINE_ACCOUNT=aios-autonomy-agent
Environment=AIOS_RELEASE_RECOVERY_RECEIPT=/var/lib/aios-gitops/release-campaigns/AIOS-AUTONOMOUS-RELEASE-CAMPAIGN-20260807/authorization-recovery.json
LoadCredentialEncrypted=gitea-machine-token:/etc/aios/credentials/gitea-machine.token.cred
Environment=PYTHONDONTWRITEBYTECODE=1
ExecStart=/usr/bin/python3 -B /opt/aios-gitops/active-runtime/app/aios_gitops_writer_worker.py --job-root ${AIOS_GITOPS_JOB_ROOT}
Restart=on-failure
RestartSec=2
NoNewPrivileges=no
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/var/lib/aios-gitops/jobs /var/lib/aios-gitops/payloads /var/lib/aios-gitops/worktrees /data/aios-gitops/repo.git
[Install]
WantedBy=multi-user.target
+14
View File
@@ -0,0 +1,14 @@
[Unit]
Description=AIOS durable GitOps writer socket
[Socket]
ListenStream=/run/aios-gitops/writer.sock
Accept=no
SocketUser=root
SocketGroup=gitops
SocketMode=0660
DirectoryMode=0750
RemoveOnStop=yes
[Install]
WantedBy=sockets.target
@@ -0,0 +1,26 @@
[Unit]
Description=AIOS Semantic Governance Drift Monitor
After=aios-governance-executor.service
[Service]
Type=oneshot
User=root
Group=gitops
ExecStart=/usr/bin/python3 /usr/local/libexec/aios-governance-drift-monitor
NoNewPrivileges=true
PrivateTmp=true
PrivateDevices=true
ProtectSystem=strict
ProtectHome=true
ProtectKernelTunables=true
ProtectKernelModules=true
ProtectControlGroups=true
LockPersonality=true
RestrictSUIDSGID=true
RestrictRealtime=true
MemoryDenyWriteExecute=true
RestrictAddressFamilies=AF_UNIX
IPAddressDeny=any
ReadOnlyPaths=/usr/local/libexec/aios-governance-drift-monitor
ReadOnlyPaths=/var/lib/aios-gitops/runtime/tasks/AIOS-PROTECTION-SNAPSHOT-DRIFT-FORENSICS-V1/protection-drift/semantic-analysis.json
ReadWritePaths=/var/lib/aios-gitops/governance-executor
@@ -0,0 +1,37 @@
[Unit]
Description=AIOS Dedicated Governance Executor
After=aios-gitea.service network-online.target
Wants=network-online.target
ConditionPathExists=/etc/credstore.encrypted/gitea-governance-executor-token
ConditionPathExists=/usr/local/libexec/aios-governance-executor
[Service]
Type=simple
User=root
Group=gitops
LoadCredentialEncrypted=gitea-governance-executor-token:/etc/credstore.encrypted/gitea-governance-executor-token
ExecStart=/usr/local/libexec/aios-governance-executor
Restart=on-failure
RestartSec=3
NoNewPrivileges=true
PrivateTmp=true
ProtectHome=true
ProtectSystem=strict
ProtectKernelLogs=true
ProtectKernelModules=true
ProtectKernelTunables=true
ProtectControlGroups=true
RestrictSUIDSGID=true
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6
IPAddressDeny=any
IPAddressAllow=127.0.0.1
RuntimeDirectory=aios-governance-executor
RuntimeDirectoryMode=0750
StateDirectory=aios-gitops/governance-executor
StateDirectoryMode=0750
ReadOnlyPaths=/usr/local/libexec /etc/credstore.encrypted
ReadWritePaths=/var/lib/aios-gitops/governance-executor /run/aios-governance-executor
UMask=0007
[Install]
WantedBy=multi-user.target
@@ -0,0 +1,33 @@
[Unit]
Description=AIOS Governance Pre-Approval Publication Plane
Requires=aios-governance-preapproval-publisher.socket
After=network-online.target aios-gitea.service
Wants=network-online.target
[Service]
Type=simple
User=root
Group=gitops
UMask=0077
Environment=AIOS_GITOPS_WRITES_ENABLED=false
Environment=AIOS_GOVERNANCE_PUBLISHER_OPERATION_ALLOWLIST=semantic-reconciliation-only
LoadCredentialEncrypted=gitea-machine-token:/etc/aios/credentials/gitea-machine.token.cred
ExecStart=/usr/local/libexec/aios-governance-preapproval-publisher
NoNewPrivileges=true
PrivateTmp=true
PrivateDevices=true
ProtectSystem=strict
ProtectHome=true
ProtectKernelTunables=true
ProtectKernelModules=true
ProtectControlGroups=true
LockPersonality=true
RestrictSUIDSGID=true
RestrictRealtime=true
MemoryDenyWriteExecute=true
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6
ReadOnlyPaths=/usr/local/libexec/aios-governance-preapproval-publisher
ReadOnlyPaths=/etc/aios/credentials
ReadOnlyPaths=/var/lib/aios-gitops/runtime/tasks/AIOS-PROTECTION-SNAPSHOT-DRIFT-FORENSICS-V1/publication-input
ReadWritePaths=/var/lib/aios-gitops/runtime/tasks/AIOS-PROTECTION-SNAPSHOT-DRIFT-FORENSICS-V1
ReadWritePaths=/run/aios-governance-preapproval-publisher
@@ -0,0 +1,13 @@
[Unit]
Description=AIOS Governance Pre-Approval Publication Plane Socket
[Socket]
ListenStream=/run/aios-governance-preapproval-publisher/publisher.sock
SocketUser=root
SocketGroup=gitops
SocketMode=0660
DirectoryMode=0750
RemoveOnStop=true
[Install]
WantedBy=sockets.target
@@ -0,0 +1,30 @@
# AIOS Autonomous Release Campaign v1
This control plane changes the Owner interaction model from per-transaction
approval to one durable, revocable campaign policy delegation.
The single bootstrap confirmation creates:
1. a narrow task-writer grant for the control-plane implementation branch; and
2. a root-owned campaign authorization in PENDING_CONTROL_PLANE_MERGE state.
After the protected control-plane PR merges, machine activation may move that
same receipt to ACTIVE only when:
- the merged main is a descendant of the campaign lineage anchor;
- the campaign modules and tests are present in merged main;
- the campaign receipt digest is valid;
- no revocation exists;
- global AIOS_GITOPS_WRITES_ENABLED remains false.
Once ACTIVE, routine engineering, protected PRs, exact release binding
acceptance, tag publication, deployment, Registry/Baseline finalization and
recoverable failure handling no longer ask the Owner for another interaction.
Manual Owner review remains mandatory only for:
- formal same-name tag conflict;
- force-push requirement;
- published history rewrite;
- non-fast-forward main divergence;
- private signing material exposure;
- repository identity conflict;
- campaign revocation;
- operation outside campaign scope.
+37
View File
@@ -0,0 +1,37 @@
# Durable GitOps Job Controller
The control-plane write boundary is a durable Job, not a synchronous checkout
or commit request. The source of truth is SQLite WAL at
`/var/lib/aios-gitops/jobs/state.sqlite3`, with `jobs`, `idempotency_keys`,
`branch_leases`, `state_transitions`, and `receipts` tables. Per-job
`request.json`, `state.json`, `receipts/`, and `logs-digest/` files are
durable mirrors/evidence, not a second state machine.
The controller owns only binding validation, idempotency, state transitions,
and recovery classification. A supervised worker owns an isolated detached
worktree and all Git side effects. `/api/file` and `/api/commit-file` adapters
must return `job_id` and `side_effect_state`; they must never perform
checkout, file write, `git add`, or `git commit` inline.
`/var/lib/aios-gitops/payloads/sha256/<digest>` is the content-addressed
payload store. SQLite records only `payload_digest`, `payload_ref`, and size;
the worker verifies both digest and size before consumption. The durable
execution gate is `AIOS_GITOPS_DURABLE_JOB_EXECUTION_ENABLED=true`, while
`AIOS_GITOPS_WRITES_ENABLED=false` remains the independent legacy repository
mutation gate.
The idempotency key is the SHA-256 of the canonical tuple:
`campaign_id`, `repository`, `operation`, `branch`, `expected_branch_oid`, and
`payload_digest`.
The writer lease is `CONTINUE_EXISTING` for the recovered campaign. It binds
to lineage anchor `69b45e81dcd2c3707c367003b08c55fd361faae8` and exact initial
head `bba675fab3b7e0c5fba7066720252b3c91b9f20d`. Every ref update is delegated
to a non-Owner machine broker using compare-and-swap; a stale head is a
`BRANCH_LEASE_STALE` condition and never a force push.
Production remains outside this worker contract. The fixed
`aios-gitops-writer.service` uses `Accept=no`, automatic restart, and durable
queue/reconciliation execution; it cannot mutate Git while the legacy global
write gate is false or while the campaign/machine capability is absent.
@@ -0,0 +1,15 @@
# AIOS Mission Source Convergence
This directory contains the exact verified governance runtime sources migrated from the live control plane under `AIOS_AUTONOMOUS_RELEASE_MISSION_COMPLETION_AUTHORIZATION_V1`.
The executables remain capability-separated:
- `aios-governance-executor`: protected branch-policy GET, semantic CAS, and approved Stage-1 delta only.
- `aios-governance-drift-monitor`: read-only semantic/transport drift classification.
- `aios-governance-preapproval-publisher`: governance/request publication only; no protection, main, tag, production, Registry, Baseline, or credential mutation.
- `semantic_protection.py`: canonical `aios.semantic-protection.v1` model.
- `ref_reconciler.py`: Gitea-main to Authority FF-CAS reconciler with idempotent readback.
All credential values remain systemd-projected and are never part of source, logs, arguments, or receipts.
The live source hashes used for this migration are recorded in the mission receipt, while this Git tree is the permanent source of truth for future installs.
@@ -0,0 +1,7 @@
# AIOS Mission Zero-Interaction Self-Test
This marker is intentionally harmless and exists only to exercise the
machine-authored branch, exact-head status publication, campaign approval gate,
protected fast-forward merge, and Authority main CAS recovery path.
Production, tags, Registry, Baseline, and V2R1C7 remain unchanged.
@@ -0,0 +1,15 @@
# Bridge v2.1 Durable Reconciliation Bootstrap
This is the planning-only bootstrap artifact for
`TASK-P0-BRIDGE-V21-DURABLE-RECONCILIATION-20260809`.
The task is bound to the exact base commit
`3f670c387692b8865a10d5b419468d8a0b64d7a4` and the development branch
`agent/p0-bridge-v21-durable-reconciliation-20260809`.
The first governed commit records the design boundary and acceptance contract
only. It does not implement the v2.1 body, mutate main, create tags, change
Baseline, or touch production. Future work must preserve absent-ref CAS,
observation-first recovery, durable JobStore state, exact Authority/Gitea/API
reconciliation, and the existing secret-scanning and credential-projection
boundaries.
@@ -0,0 +1,150 @@
# ChatGPT Bridge v2 Control Plane Convergence Design
Date: 2026-08-09
Task: `TASK-P0-CHATGPT-BRIDGE-V2-CONTROL-PLANE-CONVERGENCE-20260809`
Repository: `aios/aios`
Approved development base: `2e8fa22989808a448065d1d76b5b18948774a637`
Target branch: `agent/p0-chatgpt-bridge-v2-control-plane-convergence-20260809`
## Scope and authority
This design converges the ChatGPT GitOps Bridge, the existing Authorized Task Writer, the continuation lease protocol, the Durable GitOps Job Controller, and immutable control-plane runtime identity.
The development base is exact and must never be replaced with a dynamically resolved `HEAD`, `latest`, or `main`. If Authority `refs/heads/main` differs from the approved OID before the first governed writer push, execution fails with `BASE_OID_CHANGED`.
This task does not authorize production changes. The following remain immutable:
- Production Baseline `V2R1C7`.
- Production target OID `c7adece1cc8beffe7c718d53c775898b45d6c7f8`.
- Production tag OID `82792347937c0df59a8e02d6162775252bcad2e2`.
- Production Registry, services, deployment state, runtime, tags, and release evidence.
- Authority `main`.
Force push, history rewrite, direct main push, production mutation, tag mutation, baseline mutation, and secret commits are always denied.
## Immutable control-plane runtime identity
A control-plane release is installed into a versioned immutable directory equivalent to:
```text
/opt/aios/gitops-control-plane/<release-id-or-oid>/
```
The release manifest binds:
```text
control_plane_release_version
control_plane_target_oid
artifact_sha256
runtime_path
bridge_schema_version
repository_identity
authoritative_git_dir
deployment_state
```
The validator is independent from the mutable Git workspace. It rejects execution when the runtime path is a workspace/worktree, the observed runtime commit or artifact digest differs from the manifest, the repository identity is not `aios/aios`, the authoritative Git directory is absent or mismatched, or the Bridge schema version is absent or unsupported.
Every failure is fail closed with `CONTROL_PLANE_RUNTIME_DRIFT`. No fallback to a mutable workspace is permitted. The installer and systemd template are staging-safe; this task may install and restart only a staging/test service.
## Bridge v2 adapter
The Bridge schema identity is `aios.chatgpt-gitops-bridge.v2`. It is independent from the production baseline, control-plane release version, and runtime release commit.
The Bridge exposes:
- `getRuntimeStatus`
- `getGovernedWriteCapabilities`
- `createGovernedTask`
- `acquireWriterLease`
- `commitFileGoverned`
- `commitFilesGoverned`
- `getGitOpsJob`
- `resumeGitOpsJob`
- `reconcileGitOpsJob`
The adapter reuses existing mechanisms and does not create a second writer, lease protocol, branch state store, job database, or direct Git mutation path.
## Governed task and lease flow
`createGovernedTask` binds an exact task ID, repository, approved base OID, non-main branch, risk, and description. Existing branches are read back and bound to their observed full OID. Idempotent replay returns the original binding; conflicting replay fails closed.
`acquireWriterLease` uses `WriterLeaseRequestV2`. Continuation uses:
```text
branch_mode=CONTINUE_EXISTING
expected_branch_oid=<full OID>
generation=<monotonic integer>
create_target_branch=false
worktree_mode=detached_at_expected_head
cas_ref_update_required=true
```
Credentials remain server-side and never appear in responses, Git, durable job metadata, or logs.
## Governed commit flow
`commitFileGoverned` and `commitFilesGoverned` submit work to the existing `GitOpsJobController` and `JobStore`. The server computes payload digests. Raw file content is stored only in the content-addressed payload domain. Secret scanning, protected-path policy, repository binding, lease validation, and branch authorization run before side effects.
`commitFilesGoverned` is one atomic Git commit and enforces bounded file count, per-file size, and total payload size. Any failure leaves no partial commit or ref update.
All ref updates use compare-and-swap. Stale `expected_branch_oid` returns conflict and never triggers automatic rebase. After push, Authority and remote Gitea refs are independently read and must equal the resulting commit OID.
## Durable job lifecycle
Job reads return only non-sensitive metadata: `job_id`, `state`, `revision`, `side_effect_state`, `retryable`, `binding`, and `receipt_summary`.
Terminal jobs cannot be changed by ordinary resume. Resume requires `job_id` and `expected_revision` and uses revision CAS. Reconciliation is observation-first: for `UNKNOWN`, `RECONCILIATION_REQUIRED`, or `REMOTE_PUSH_PENDING`, local, Authority, and remote refs are read before state changes. A blind repeated push is never reconciliation.
## Legacy behavior
Legacy writes remain disabled while governed writes are enabled:
```text
legacy_writes_enabled=false
governed_writes_enabled=true
```
The global `AIOS_GITOPS_WRITES_ENABLED` flag is never enabled as the solution. Legacy operations remain temporarily visible for compatibility and diagnostics, but return:
```json
{
"error": "LEGACY_WRITE_DISABLED",
"replacement": "createGovernedTask",
"governed_writes_available": true
}
```
## OpenAPI convergence
`docs/gitops/openapi-chatgpt-actions.yaml` is the canonical Bridge v2 schema and integrates the Authorized Task Writer fragment. The canonical CAS field is `expected_branch_oid`. `expected_head_oid` may be accepted only at an explicit deprecated adapter boundary.
Contract tests require exact equality between OpenAPI required fields, runtime parser required fields, and generated GPT Action schema required fields.
## Acceptance
Automated coverage includes runtime path/OID/digest/repository drift, exact task/branch/OID/lease/CAS behavior, job DB failure, restart during remote verification, remote timeout, response loss after remote success, idempotency replay/conflict, concurrent branch movement, unauthorized or main branch writes, protected paths, secret-like payloads, disabled governed writer, missing/expired lease, wrong repository identity, and observation-first reconciliation.
The staging sequence is:
```text
getRuntimeStatus
getGovernedWriteCapabilities
createGovernedTask
acquireWriterLease
commitFilesGoverned
getGitOpsJob
verify Authority branch ref
verify remote Gitea branch ref
restart staging control-plane service
getRuntimeStatus
acquire continuation lease
second governed commit
verify remote ref again
```
Completion requires all tests, staging restart continuation, Authority/remote OID equality, unchanged production invariants, no force push, no tag rewrite, no direct main push, and no secret commit.
## Implementation boundaries
Implementation is limited to the Bridge, existing writer and job-controller adapters, runtime identity/installer, staging systemd template, OpenAPI documents, tests, and engineering evidence. Hermes Adapter integration and production cutover are outside this task and require separate approval.
@@ -0,0 +1,117 @@
# Server-local governed immutable Host seed executor
Date: 2026-08-22
## Goal
Provide a server-local, fail-closed way to publish an already validated
immutable control-plane candidate without depending on Windows, an external
Agent worker, SSH, or an arbitrary root command channel.
The executor is deliberately narrower than deployment:
- it reads only the authoritative local Git object database;
- it consumes one fixed request ref and path;
- it accepts only `HOST_SEED_IMMUTABLE_CANDIDATE`;
- it requires a high-risk governed-task binding whose description binds the
exact candidate intent *and* authorization expiry;
- it materializes `base_oid + candidate.patch`, verifies every release-member
SHA against the candidate manifest, and verifies the candidate Machine Goal;
- it atomically publishes one immutable directory under `/opt/aios/releases`;
- it never starts or restarts services, starts the Projector, creates the fixed
continuation Task/Job, switches Production, or writes `main`, tags, or the
baseline.
## Canonical server path
```text
explicit Owner HOST_SEED authorization
-> high-risk governed authorization task binding
-> governed request commit on refs/heads/agent/host-seed-authorization-requests
-> root timer polls local authoritative Git
-> validate exact intent, expiry, binding SHA, candidate evidence
-> read payload commit from local Git
-> shared disposable clone at exact base
-> apply declared patch
-> verify exact changed set + all release-member bytes
-> atomic immutable publish
-> durable sanitized receipt
```
The Host seed request is not the fixed continuation Task. The fixed
continuation Task remains a separate later gate.
## Trust and authorization binding
The request contains an immutable `intent` with:
- candidate / release id;
- base OID and payload commit;
- fixed patch, manifest, Machine Goal and bootstrap paths;
- validation artifact SHA;
- Manifest, Machine Goal, release-tree and bootstrap SHAs;
- exact target Host identity.
`intent_sha256` is the canonical SHA of that object. Authorization adds
`task_id` and `expires_at`; those are hashed into `authorization_sha256`.
The server-side governed-task binding must contain:
```text
risk=high
description=HOST_SEED_AUTHORIZATION:<authorization_sha256>
target_branch=agent/host-seed-authorization-...
```
The request also binds the exact byte SHA of that server-side binding. This
prevents extending the authorization window or changing the candidate without
creating a new governed authorization binding.
The executor additionally requires the binding file to be a non-symlink
regular file, not group/world writable, and owned by the configured Bridge
service account in production.
## Delivery model
The live delivery payload is server-native Git data, not the external ZIP.
The payload commit carries:
```text
control-plane/candidates/<candidate>/candidate.patch
control-plane/candidates/<candidate>/implementation_manifest_*.json
control-plane/candidates/<candidate>/machine_goal_*.json
control-plane/candidates/<candidate>/aios_control_plane_immutable_seed_*.py
```
The external ZIP SHA remains bound as validation evidence through Machine
Goal, but the executor records `artifact_delivery_used=false`: it does not
claim that ZIP bytes were copied to the Host.
The release tree itself is reconstructed and verified from authoritative Git
objects plus the declared patch.
## Idempotency and recovery
Before publishing, the executor writes a durable `PENDING` operation record
keyed by the exact intent SHA. The final receipt is `EXECUTED`.
If the immutable release directory already exists, every member must match the
manifest before the operation is accepted as idempotent. Any drift fails
closed.
A missing request ref is a normal `NO_REQUEST` poll and has no side effects.
## Systemd confinement
The service:
- runs as root only because immutable release publication requires it;
- has no network access;
- reads authoritative Git and Bridge task bindings read-only;
- can write only its private state root and `/opt/aios/releases`;
- has no service-control, Projector, Task, Git-ref, Production, tag or baseline
operation in its code path.
The timer polls every 30 seconds. Installing/enabling this executor is itself a
separate governed control-plane deployment. Installing it does **not** grant a
Host seed authorization and does not seed any candidate while the fixed
request ref is absent.
+4
View File
@@ -0,0 +1,4 @@
[pytest]
testpaths = tests
python_files = test_*.py
addopts = -ra --import-mode=importlib
@@ -0,0 +1,243 @@
{
"authority_proof": {
"branch_oid": "2c6293160ed567a2098715edbb296741577c5bff",
"commit_visible": true,
"main_oid": "2e8fa22989808a448065d1d76b5b18948774a637",
"v2r2c1_tag_oid": "998d30304787e31e5ae607c0bd86b5edb2b0e5ad"
},
"baseline_authority": {
"artifact_sha256": "492338b895b152a55015fd92ea1d9dba2314894ab00c0ba219a78f3cef544b8a",
"authority_sha256": "d8e23bad49bdb3e4192ba708c3dfe7fc5b7cdc6a513c2f87acd6412a51867231",
"current_baseline": "V2R2C1",
"raw_byte_sidecar_verification": "PASS",
"target_oid": "998d30304787e31e5ae607c0bd86b5edb2b0e5ad"
},
"control_plane_source_oid": "b781129bd5a8b53c05ab648361424d2800166ee4",
"diff_stat": "active-runtime/app/aios_authority_git_executor.py | 24 +\n active-runtime/app/aios_chatgpt_actions_api.py | 48 +-\n active-runtime/app/aios_chatgpt_bridge_v2.py | 167 ++++\n .../app/aios_chatgpt_bridge_v2_server.py | 431 +++++++++\n active-runtime/app/aios_continuation_executor.py | 594 ++++++++++++\n active-runtime/app/aios_continuation_lease.py | 6 +\n active-runtime/app/aios_control_plane_snapshot.py | 26 +\n active-runtime/app/aios_credential_broker.py | 78 ++\n active-runtime/app/aios_gitea_publisher.py | 93 ++\n active-runtime/app/aios_gitops_job_controller.py | 16 +-\n .../app/aios_gitops_reconciliation_core.py | 80 ++\n active-runtime/app/aios_governed_policy.py | 59 ++\n active-runtime/app/aios_governed_task_bootstrap.py | 84 ++\n active-runtime/app/aios_model_api_staging.py | 1022 ++++++++++++++++++++\n .../app/authorized_task_writer_provisioning.py | 4 +-\n active-runtime/app/baseline_authority.py | 84 ++\n active-runtime/app/governed_production_release.py | 568 +++++++++++\n active-runtime/app/v2r2c1_production_adapter.py | 135 +++\n .../systemd/aios-chatgpt-bridge-v2-staging.service | 23 +\n .../aios-gitops-model-api-v2r2c1-staging.service | 51 +\n ...09-bridge-v2.1-durable-reconciliation-design.md | 15 +\n ...t-bridge-v2-control-plane-convergence-design.md | 150 +++\n pytest.ini | 4 +\n scripts/build_v2r2c1_model_api_staging.py | 145 +++\n .../aios_release_controller/manifest.py | 4 +-\n .../test_v2r1c6_release_consistency.py | 4 +-\n .../aios_v2_candidate/test_v2r1c6_release_ready.py | 16 +-\n tests/conftest.py | 65 ++\n tests/test_bridge_v2_governed_http_bootstrap.py | 90 ++\n tests/test_governed_local_writer.py | 6 +-\n tests/test_model_api_immutable_staging.py | 35 +\n 31 files changed, 4108 insertions(+), 19 deletions(-)",
"evidence_closure_oid": "a8b906d1bc24088c19647e98de6a679268f6d0a2",
"forbidden_operations": [
"direct_main_push",
"main_merge_without_owner_approval",
"force_push",
"tag_mutation",
"baseline_mutation",
"registry_mutation",
"production_restart",
"production_cutover",
"live_model_api_cutover",
"history_rewrite",
"secret_commit"
],
"governance": {
"baseline_modified": false,
"direct_main_push": false,
"force_push_used": false,
"history_rewritten": false,
"live_model_api_cutover": false,
"main_merged": false,
"owner_approval_respected": true,
"production_changed": false,
"production_restarted": false,
"registry_modified": false,
"secrets_committed": false,
"tag_rewritten": false
},
"included_path_count": 31,
"integration_branch": "agent/v2r2c1-post-release-mainline-convergence-20260809",
"integration_head_oid": "2c6293160ed567a2098715edbb296741577c5bff",
"logical_commits": [
{
"message": "feat(v2r2c1): converge durable control-plane core",
"oid": "40b685d1c70c767c7a79a2b184348e58e3e319ae",
"parent_oid": "2e8fa22989808a448065d1d76b5b18948774a637"
},
{
"message": "docs(v2r2c1): converge durable runtime and design sources",
"oid": "5f616311bab38b0dac296ed8b0e69660e031a20e",
"parent_oid": "40b685d1c70c767c7a79a2b184348e58e3e319ae"
},
{
"message": "feat(v2r2c1): integrate governed release overlay",
"oid": "57780e316e320a5128cc2c9f12adbb19ae57aef5",
"parent_oid": "5f616311bab38b0dac296ed8b0e69660e031a20e"
},
{
"message": "feat(v2r2c1): wire governed release operations into durable bridge",
"oid": "e41b8d323a3e7a40440f5d32221f524074bea5fe",
"parent_oid": "57780e316e320a5128cc2c9f12adbb19ae57aef5"
},
{
"message": "test: repair canonical pytest repository entrypoint",
"oid": "b20cb8130e4c68267d5d85b02eed51cb8d4f41c0",
"parent_oid": "e41b8d323a3e7a40440f5d32221f524074bea5fe"
},
{
"message": "test: make whole-suite collection deterministic",
"oid": "c6a1693efaede9dace9b929d884a09e162a02c89",
"parent_oid": "b20cb8130e4c68267d5d85b02eed51cb8d4f41c0"
},
{
"message": "test: preserve legacy suite imports under canonical collection",
"oid": "91f2ea0591787d3dbf8c2e27ab0460629d86900e",
"parent_oid": "c6a1693efaede9dace9b929d884a09e162a02c89"
},
{
"message": "test: expose repository test helpers to canonical collection",
"oid": "85897d3f6d708271dff16767265f9d14b2f29801",
"parent_oid": "91f2ea0591787d3dbf8c2e27ab0460629d86900e"
},
{
"message": "fix(v2r2c1): close slash-branch and suite contract regressions",
"oid": "f1110d24f30d8441f2df04e0774ee230b95c2e02",
"parent_oid": "85897d3f6d708271dff16767265f9d14b2f29801"
},
{
"message": "test: align writer regression with durable idempotent replay",
"oid": "c10d2a5dfcc394dde7a7022952d7b423725747a3",
"parent_oid": "f1110d24f30d8441f2df04e0774ee230b95c2e02"
},
{
"message": "feat(v2r2c1): source baseline authority for model-api staging",
"oid": "f969aedaf61501a866435c712be01ff2cfe43fb1",
"parent_oid": "c10d2a5dfcc394dde7a7022952d7b423725747a3"
},
{
"message": "feat(v2r2c1): add immutable model api staging runtime",
"oid": "4ab70bfdb4d34aa9d1dabc4230a3367eaa93cc9b",
"parent_oid": "f969aedaf61501a866435c712be01ff2cfe43fb1"
},
{
"message": "fix(v2r2c1): avoid builder secret-scan false positives",
"oid": "7c4e86bfc1f39e643e672968d1fdedbd72ace56d",
"parent_oid": "4ab70bfdb4d34aa9d1dabc4230a3367eaa93cc9b"
},
{
"message": "fix(v2r2c1): isolate model api staging port",
"oid": "7564363a30bfa7fa81ac5c7c545ec704984ec543",
"parent_oid": "7c4e86bfc1f39e643e672968d1fdedbd72ace56d"
},
{
"message": "fix(v2r2c1): bind baseline authority and openapi staging path",
"oid": "2c6293160ed567a2098715edbb296741577c5bff",
"parent_oid": "7564363a30bfa7fa81ac5c7c545ec704984ec543"
}
],
"main_base_oid": "2e8fa22989808a448065d1d76b5b18948774a637",
"mandatory_gates": {
"baseline_registry_readback_staging_pass": true,
"durable_source_path_manifest_complete": true,
"existing_v2r2c1_suite_pass": true,
"governed_release_suite_pass": true,
"integration_authority_git_api_equal": true,
"integration_branch_governed": true,
"logical_slice_commits_remote_verified": true,
"main_base_exact_before_branch_create": true,
"model_api_immutable_runtime_staging_pass": true,
"owner_merge_request_generated_execute_false": true,
"production_unchanged": true,
"pytest_collection_entrypoints_fixed": true,
"pytest_tests_results_recorded": true
},
"model_api_staging": {
"artifact_sha256": "492338b895b152a55015fd92ea1d9dba2314894ab00c0ba219a78f3cef544b8a",
"baseline": {
"authority_sha256": "d8e23bad49bdb3e4192ba708c3dfe7fc5b7cdc6a513c2f87acd6412a51867231",
"current_baseline": "V2R2C1",
"deployment_state": "ACTIVE",
"evidence_closure_oid": "a8b906d1bc24088c19647e98de6a679268f6d0a2",
"generation": 4,
"previous_baseline": "V2R1C7",
"production_deployed": true,
"status": "PRODUCTION_DEPLOYED",
"tag_oid": "07fc2746b688f5f243f29b39de39f32edc3dd9d2",
"target_oid": "998d30304787e31e5ae607c0bd86b5edb2b0e5ad"
},
"baseline_status": 200,
"deployment_state": "STAGING",
"health": {
"artifact_sha256": "492338b895b152a55015fd92ea1d9dba2314894ab00c0ba219a78f3cef544b8a",
"commit_oid": "2c6293160ed567a2098715edbb296741577c5bff",
"deployment_state": "STAGING",
"governed_release_routing": "bridge-v2",
"legacy_global_writes_enabled": false,
"production_deployed": false,
"runtime_mode": "IMMUTABLE_STAGING",
"status": "ok",
"version": "V2R2C1"
},
"health_status": 200,
"legacy_write_probe": "423_BLOCKED",
"port": 18893,
"restart_acceptance": "PASS",
"runtime_root": "/opt/aios/gitops-model-api/2c6293160ed567a2098715edbb296741577c5bff/runtime",
"source_oid": "2c6293160ed567a2098715edbb296741577c5bff",
"systemd": "MainPID=788782\nExecStart={ path=/usr/bin/python3 ; argv[]=/usr/bin/python3 /opt/aios/gitops-model-api/2c6293160ed567a2098715edbb296741577c5bff/runtime/app/aios_model_api_staging.py ; ignore_errors=no ; start_time=[Sun 2026-08-09 19:11:19 CST] ; stop_time=[n/a] ; pid=788782 ; code=(null) ; status=0/0 }\nActiveState=active\nSubState=running",
"unit": "aios-gitops-model-api-v2r2c1-staging.service",
"versions": {
"authoritative_head_oid": "2e8fa22989808a448065d1d76b5b18948774a637",
"baseline_generation": 4,
"baseline_sha256": "d8e23bad49bdb3e4192ba708c3dfe7fc5b7cdc6a513c2f87acd6412a51867231",
"baseline_version": "V2R2C1",
"query_error": null
},
"versions_status": 200
},
"owner_merge_request": {
"control_plane_source_oid": "b781129bd5a8b53c05ab648361424d2800166ee4",
"execute": false,
"integration_head_oid": "b2cf510e11b712bb99f20ded81459b6524257394",
"main_expected_old_oid": "2e8fa22989808a448065d1d76b5b18948774a637",
"release_target_oid": "998d30304787e31e5ae607c0bd86b5edb2b0e5ad",
"report_commit_oid": "b2cf510e11b712bb99f20ded81459b6524257394",
"status": "OWNER_APPROVAL_REQUIRED"
},
"placeholder_fields": [],
"production": {
"baseline_mutation": false,
"health": {
"artifact_sha256": "ab4ae257346c82863f05abf5e309381a1c86612957608c3a4f4ab5cc9233f052",
"commit_oid": "998d30304787e31e5ae607c0bd86b5edb2b0e5ad",
"deployment_state": "ACTIVE",
"health_timestamp": "2026-08-09T11:21:16.872581+00:00",
"production_deployed": true,
"status": "ok",
"version": "V2R2C1"
},
"health_status": 200,
"live_model_api_show": "MainPID=2816107\nActiveState=active\nSubState=running",
"live_model_api_unchanged": true,
"production_cutover": false,
"production_restart": false,
"registry_mutation": false,
"shadow_service_unchanged": true,
"shadow_show": "MainPID=1505658\nActiveState=active\nSubState=running",
"tag_mutation": false
},
"release_target_oid": "998d30304787e31e5ae607c0bd86b5edb2b0e5ad",
"remote_proof": {
"api_branch_oid": "2c6293160ed567a2098715edbb296741577c5bff",
"api_verification": "paginated_exact_name",
"commit_visible": true,
"git_remote_oid": "2c6293160ed567a2098715edbb296741577c5bff",
"main_api_oid": "2e8fa22989808a448065d1d76b5b18948774a637",
"main_git_oid": "2e8fa22989808a448065d1d76b5b18948774a637"
},
"report_commit_parent_oid": "2c6293160ed567a2098715edbb296741577c5bff",
"repository": "aios/aios",
"schema": "aios.v2r2c1.post-release-mainline-convergence.acceptance.v1",
"status": "V2R2C1_MAINLINE_CONVERGENCE_READY_FOR_OWNER_MERGE",
"success_status": "V2R2C1_MAINLINE_CONVERGENCE_READY_FOR_OWNER_MERGE",
"task_id": "TASK-P0-V2R2C1-POST-RELEASE-MAINLINE-CONVERGENCE-20260809",
"tests": {
"collection": "PASS",
"command": "pytest tests",
"failed": 0,
"passed": 709,
"skipped": 2,
"skipped_tests": [
"tests/test_gitea_tag_publication_live.py (live opt-in)",
"tests/test_signer_contract_compatibility.py (live integration opt-in)"
],
"subtests": 154
}
}
@@ -0,0 +1,89 @@
# V2R2C1 post-release mainline convergence diff summary
- task: `TASK-P0-V2R2C1-POST-RELEASE-MAINLINE-CONVERGENCE-20260809`
- repository: `aios/aios`
- main base: `2e8fa22989808a448065d1d76b5b18948774a637`
- release target: `998d30304787e31e5ae607c0bd86b5edb2b0e5ad`
- control-plane source: `b781129bd5a8b53c05ab648361424d2800166ee4`
- implementation integration head before this report commit: `2c6293160ed567a2098715edbb296741577c5bff`
- branch: `agent/v2r2c1-post-release-mainline-convergence-20260809`
## Logical governed commits
| OID | Parent | Message |
|---|---|---|
| `40b685d1c70c767c7a79a2b184348e58e3e319ae` | `2e8fa22989808a448065d1d76b5b18948774a637` | feat(v2r2c1): converge durable control-plane core |
| `5f616311bab38b0dac296ed8b0e69660e031a20e` | `40b685d1c70c767c7a79a2b184348e58e3e319ae` | docs(v2r2c1): converge durable runtime and design sources |
| `57780e316e320a5128cc2c9f12adbb19ae57aef5` | `5f616311bab38b0dac296ed8b0e69660e031a20e` | feat(v2r2c1): integrate governed release overlay |
| `e41b8d323a3e7a40440f5d32221f524074bea5fe` | `57780e316e320a5128cc2c9f12adbb19ae57aef5` | feat(v2r2c1): wire governed release operations into durable bridge |
| `b20cb8130e4c68267d5d85b02eed51cb8d4f41c0` | `e41b8d323a3e7a40440f5d32221f524074bea5fe` | test: repair canonical pytest repository entrypoint |
| `c6a1693efaede9dace9b929d884a09e162a02c89` | `b20cb8130e4c68267d5d85b02eed51cb8d4f41c0` | test: make whole-suite collection deterministic |
| `91f2ea0591787d3dbf8c2e27ab0460629d86900e` | `c6a1693efaede9dace9b929d884a09e162a02c89` | test: preserve legacy suite imports under canonical collection |
| `85897d3f6d708271dff16767265f9d14b2f29801` | `91f2ea0591787d3dbf8c2e27ab0460629d86900e` | test: expose repository test helpers to canonical collection |
| `f1110d24f30d8441f2df04e0774ee230b95c2e02` | `85897d3f6d708271dff16767265f9d14b2f29801` | fix(v2r2c1): close slash-branch and suite contract regressions |
| `c10d2a5dfcc394dde7a7022952d7b423725747a3` | `f1110d24f30d8441f2df04e0774ee230b95c2e02` | test: align writer regression with durable idempotent replay |
| `f969aedaf61501a866435c712be01ff2cfe43fb1` | `c10d2a5dfcc394dde7a7022952d7b423725747a3` | feat(v2r2c1): source baseline authority for model-api staging |
| `4ab70bfdb4d34aa9d1dabc4230a3367eaa93cc9b` | `f969aedaf61501a866435c712be01ff2cfe43fb1` | feat(v2r2c1): add immutable model api staging runtime |
| `7c4e86bfc1f39e643e672968d1fdedbd72ace56d` | `4ab70bfdb4d34aa9d1dabc4230a3367eaa93cc9b` | fix(v2r2c1): avoid builder secret-scan false positives |
| `7564363a30bfa7fa81ac5c7c545ec704984ec543` | `7c4e86bfc1f39e643e672968d1fdedbd72ace56d` | fix(v2r2c1): isolate model api staging port |
| `2c6293160ed567a2098715edbb296741577c5bff` | `7564363a30bfa7fa81ac5c7c545ec704984ec543` | fix(v2r2c1): bind baseline authority and openapi staging path |
## Exact diff stat
```text
active-runtime/app/aios_authority_git_executor.py | 24 +
active-runtime/app/aios_chatgpt_actions_api.py | 48 +-
active-runtime/app/aios_chatgpt_bridge_v2.py | 167 ++++
.../app/aios_chatgpt_bridge_v2_server.py | 431 +++++++++
active-runtime/app/aios_continuation_executor.py | 594 ++++++++++++
active-runtime/app/aios_continuation_lease.py | 6 +
active-runtime/app/aios_control_plane_snapshot.py | 26 +
active-runtime/app/aios_credential_broker.py | 78 ++
active-runtime/app/aios_gitea_publisher.py | 93 ++
active-runtime/app/aios_gitops_job_controller.py | 16 +-
.../app/aios_gitops_reconciliation_core.py | 80 ++
active-runtime/app/aios_governed_policy.py | 59 ++
active-runtime/app/aios_governed_task_bootstrap.py | 84 ++
active-runtime/app/aios_model_api_staging.py | 1022 ++++++++++++++++++++
.../app/authorized_task_writer_provisioning.py | 4 +-
active-runtime/app/baseline_authority.py | 84 ++
active-runtime/app/governed_production_release.py | 568 +++++++++++
active-runtime/app/v2r2c1_production_adapter.py | 135 +++
.../systemd/aios-chatgpt-bridge-v2-staging.service | 23 +
.../aios-gitops-model-api-v2r2c1-staging.service | 51 +
...09-bridge-v2.1-durable-reconciliation-design.md | 15 +
...t-bridge-v2-control-plane-convergence-design.md | 150 +++
pytest.ini | 4 +
scripts/build_v2r2c1_model_api_staging.py | 145 +++
.../aios_release_controller/manifest.py | 4 +-
.../test_v2r1c6_release_consistency.py | 4 +-
.../aios_v2_candidate/test_v2r1c6_release_ready.py | 16 +-
tests/conftest.py | 65 ++
tests/test_bridge_v2_governed_http_bootstrap.py | 90 ++
tests/test_governed_local_writer.py | 6 +-
tests/test_model_api_immutable_staging.py | 35 +
31 files changed, 4108 insertions(+), 19 deletions(-)
```
## Included durable source domains
- Bridge v2 adapter/server and governed HTTP contracts.
- Durable reconciliation core, continuation executor/lease, Authority Git CAS adapter, job controller compatibility.
- Gitea publisher/verifier, credential broker, governed policy, control-plane snapshot, release routing.
- Canonical pytest entrypoint/conftest and regression coverage.
- Source-controlled immutable model-api staging runtime, builder and systemd template.
- Selected design specifications required for durable mainline context.
## Explicitly excluded
- `main` ref mutation or merge without Owner approval.
- Production service, Registry, Baseline body/sidecar, production tags and mutable `/var/lib` state.
- Installed `/opt` artifacts, temporary staging transition state and task-local receipts/credentials.
- Production cutover/restart, force push, history rewrite and plaintext secrets.
## Verification
- `pytest tests`: **709 passed, 2 skipped, 154 subtests, 0 failed**.
- Staging model-api: immutable OID/digest bound, port 18893, health/base/ver/OpenAPI readback PASS, restart recovery PASS.
- Authority/Gitea Git/Gitea API commit equality and commit visibility PASS.
- Production remains V2R2C1 and was not restarted or cut over.
@@ -0,0 +1,254 @@
{
"control_plane_source_oid": "b781129bd5a8b53c05ab648361424d2800166ee4",
"diff_stat": "active-runtime/app/aios_authority_git_executor.py | 24 +\n active-runtime/app/aios_chatgpt_actions_api.py | 48 +-\n active-runtime/app/aios_chatgpt_bridge_v2.py | 167 ++++\n .../app/aios_chatgpt_bridge_v2_server.py | 431 +++++++++\n active-runtime/app/aios_continuation_executor.py | 594 ++++++++++++\n active-runtime/app/aios_continuation_lease.py | 6 +\n active-runtime/app/aios_control_plane_snapshot.py | 26 +\n active-runtime/app/aios_credential_broker.py | 78 ++\n active-runtime/app/aios_gitea_publisher.py | 93 ++\n active-runtime/app/aios_gitops_job_controller.py | 16 +-\n .../app/aios_gitops_reconciliation_core.py | 80 ++\n active-runtime/app/aios_governed_policy.py | 59 ++\n active-runtime/app/aios_governed_task_bootstrap.py | 84 ++\n active-runtime/app/aios_model_api_staging.py | 1022 ++++++++++++++++++++\n .../app/authorized_task_writer_provisioning.py | 4 +-\n active-runtime/app/baseline_authority.py | 84 ++\n active-runtime/app/governed_production_release.py | 568 +++++++++++\n active-runtime/app/v2r2c1_production_adapter.py | 135 +++\n .../systemd/aios-chatgpt-bridge-v2-staging.service | 23 +\n .../aios-gitops-model-api-v2r2c1-staging.service | 51 +\n ...09-bridge-v2.1-durable-reconciliation-design.md | 15 +\n ...t-bridge-v2-control-plane-convergence-design.md | 150 +++\n pytest.ini | 4 +\n scripts/build_v2r2c1_model_api_staging.py | 145 +++\n .../aios_release_controller/manifest.py | 4 +-\n .../test_v2r1c6_release_consistency.py | 4 +-\n .../aios_v2_candidate/test_v2r1c6_release_ready.py | 16 +-\n tests/conftest.py | 65 ++\n tests/test_bridge_v2_governed_http_bootstrap.py | 90 ++\n tests/test_governed_local_writer.py | 6 +-\n tests/test_model_api_immutable_staging.py | 35 +\n 31 files changed, 4108 insertions(+), 19 deletions(-)",
"evidence_closure_oid": "a8b906d1bc24088c19647e98de6a679268f6d0a2",
"excluded_paths": [
{
"path": "refs/heads/main",
"reason": "main merge requires separate Owner approval; unchanged"
},
{
"path": "refs/tags/*",
"reason": "formal tags are outside engineering convergence scope; unchanged"
},
{
"path": "baseline/current.json and sidecar",
"reason": "production mutable authority and sidecar; read-only verified"
},
{
"path": "/var/lib/aios-gitops/**",
"reason": "host runtime state and receipts excluded from repository source"
},
{
"path": "/opt/aios/**",
"reason": "installed immutable staging artifacts excluded; digest recorded in reports"
},
{
"path": "credentials and secrets",
"reason": "availability-only; no plaintext credential committed"
},
{
"path": "production runtime and Registry",
"reason": "production cutover/registry mutation forbidden"
}
],
"included_paths": [
{
"path": "active-runtime/app/aios_authority_git_executor.py",
"status": "M"
},
{
"path": "active-runtime/app/aios_chatgpt_actions_api.py",
"status": "M"
},
{
"path": "active-runtime/app/aios_chatgpt_bridge_v2.py",
"status": "A"
},
{
"path": "active-runtime/app/aios_chatgpt_bridge_v2_server.py",
"status": "A"
},
{
"path": "active-runtime/app/aios_continuation_executor.py",
"status": "A"
},
{
"path": "active-runtime/app/aios_continuation_lease.py",
"status": "M"
},
{
"path": "active-runtime/app/aios_control_plane_snapshot.py",
"status": "A"
},
{
"path": "active-runtime/app/aios_credential_broker.py",
"status": "A"
},
{
"path": "active-runtime/app/aios_gitea_publisher.py",
"status": "A"
},
{
"path": "active-runtime/app/aios_gitops_job_controller.py",
"status": "M"
},
{
"path": "active-runtime/app/aios_gitops_reconciliation_core.py",
"status": "A"
},
{
"path": "active-runtime/app/aios_governed_policy.py",
"status": "A"
},
{
"path": "active-runtime/app/aios_governed_task_bootstrap.py",
"status": "A"
},
{
"path": "active-runtime/app/aios_model_api_staging.py",
"status": "A"
},
{
"path": "active-runtime/app/authorized_task_writer_provisioning.py",
"status": "M"
},
{
"path": "active-runtime/app/baseline_authority.py",
"status": "A"
},
{
"path": "active-runtime/app/governed_production_release.py",
"status": "A"
},
{
"path": "active-runtime/app/v2r2c1_production_adapter.py",
"status": "A"
},
{
"path": "deploy/systemd/aios-chatgpt-bridge-v2-staging.service",
"status": "A"
},
{
"path": "deploy/systemd/aios-gitops-model-api-v2r2c1-staging.service",
"status": "A"
},
{
"path": "docs/superpowers/specs/2026-08-09-bridge-v2.1-durable-reconciliation-design.md",
"status": "A"
},
{
"path": "docs/superpowers/specs/2026-08-09-chatgpt-bridge-v2-control-plane-convergence-design.md",
"status": "A"
},
{
"path": "pytest.ini",
"status": "A"
},
{
"path": "scripts/build_v2r2c1_model_api_staging.py",
"status": "A"
},
{
"path": "services/aios-release-controller/aios_release_controller/manifest.py",
"status": "M"
},
{
"path": "tests/aios_v2_candidate/test_v2r1c6_release_consistency.py",
"status": "M"
},
{
"path": "tests/aios_v2_candidate/test_v2r1c6_release_ready.py",
"status": "M"
},
{
"path": "tests/conftest.py",
"status": "A"
},
{
"path": "tests/test_bridge_v2_governed_http_bootstrap.py",
"status": "A"
},
{
"path": "tests/test_governed_local_writer.py",
"status": "M"
},
{
"path": "tests/test_model_api_immutable_staging.py",
"status": "A"
}
],
"integration_branch": "agent/v2r2c1-post-release-mainline-convergence-20260809",
"integration_head_oid": "2c6293160ed567a2098715edbb296741577c5bff",
"logical_commits": [
{
"message": "feat(v2r2c1): converge durable control-plane core",
"oid": "40b685d1c70c767c7a79a2b184348e58e3e319ae",
"parent_oid": "2e8fa22989808a448065d1d76b5b18948774a637"
},
{
"message": "docs(v2r2c1): converge durable runtime and design sources",
"oid": "5f616311bab38b0dac296ed8b0e69660e031a20e",
"parent_oid": "40b685d1c70c767c7a79a2b184348e58e3e319ae"
},
{
"message": "feat(v2r2c1): integrate governed release overlay",
"oid": "57780e316e320a5128cc2c9f12adbb19ae57aef5",
"parent_oid": "5f616311bab38b0dac296ed8b0e69660e031a20e"
},
{
"message": "feat(v2r2c1): wire governed release operations into durable bridge",
"oid": "e41b8d323a3e7a40440f5d32221f524074bea5fe",
"parent_oid": "57780e316e320a5128cc2c9f12adbb19ae57aef5"
},
{
"message": "test: repair canonical pytest repository entrypoint",
"oid": "b20cb8130e4c68267d5d85b02eed51cb8d4f41c0",
"parent_oid": "e41b8d323a3e7a40440f5d32221f524074bea5fe"
},
{
"message": "test: make whole-suite collection deterministic",
"oid": "c6a1693efaede9dace9b929d884a09e162a02c89",
"parent_oid": "b20cb8130e4c68267d5d85b02eed51cb8d4f41c0"
},
{
"message": "test: preserve legacy suite imports under canonical collection",
"oid": "91f2ea0591787d3dbf8c2e27ab0460629d86900e",
"parent_oid": "c6a1693efaede9dace9b929d884a09e162a02c89"
},
{
"message": "test: expose repository test helpers to canonical collection",
"oid": "85897d3f6d708271dff16767265f9d14b2f29801",
"parent_oid": "91f2ea0591787d3dbf8c2e27ab0460629d86900e"
},
{
"message": "fix(v2r2c1): close slash-branch and suite contract regressions",
"oid": "f1110d24f30d8441f2df04e0774ee230b95c2e02",
"parent_oid": "85897d3f6d708271dff16767265f9d14b2f29801"
},
{
"message": "test: align writer regression with durable idempotent replay",
"oid": "c10d2a5dfcc394dde7a7022952d7b423725747a3",
"parent_oid": "f1110d24f30d8441f2df04e0774ee230b95c2e02"
},
{
"message": "feat(v2r2c1): source baseline authority for model-api staging",
"oid": "f969aedaf61501a866435c712be01ff2cfe43fb1",
"parent_oid": "c10d2a5dfcc394dde7a7022952d7b423725747a3"
},
{
"message": "feat(v2r2c1): add immutable model api staging runtime",
"oid": "4ab70bfdb4d34aa9d1dabc4230a3367eaa93cc9b",
"parent_oid": "f969aedaf61501a866435c712be01ff2cfe43fb1"
},
{
"message": "fix(v2r2c1): avoid builder secret-scan false positives",
"oid": "7c4e86bfc1f39e643e672968d1fdedbd72ace56d",
"parent_oid": "4ab70bfdb4d34aa9d1dabc4230a3367eaa93cc9b"
},
{
"message": "fix(v2r2c1): isolate model api staging port",
"oid": "7564363a30bfa7fa81ac5c7c545ec704984ec543",
"parent_oid": "7c4e86bfc1f39e643e672968d1fdedbd72ace56d"
},
{
"message": "fix(v2r2c1): bind baseline authority and openapi staging path",
"oid": "2c6293160ed567a2098715edbb296741577c5bff",
"parent_oid": "7564363a30bfa7fa81ac5c7c545ec704984ec543"
}
],
"main_base_oid": "2e8fa22989808a448065d1d76b5b18948774a637",
"release_target_oid": "998d30304787e31e5ae607c0bd86b5edb2b0e5ad",
"remote_proof": {
"api_branch_oid": "2c6293160ed567a2098715edbb296741577c5bff",
"api_verification": "paginated_exact_name",
"commit_visible": true,
"git_remote_oid": "2c6293160ed567a2098715edbb296741577c5bff",
"main_api_oid": "2e8fa22989808a448065d1d76b5b18948774a637",
"main_git_oid": "2e8fa22989808a448065d1d76b5b18948774a637"
},
"report_commit_parent_oid": "2c6293160ed567a2098715edbb296741577c5bff",
"repository": "aios/aios",
"schema": "aios.v2r2c1.post-release-mainline-convergence.path-manifest.v1",
"task_id": "TASK-P0-V2R2C1-POST-RELEASE-MAINLINE-CONVERGENCE-20260809"
}
@@ -0,0 +1,37 @@
{
"artifact_sha256": "492338b895b152a55015fd92ea1d9dba2314894ab00c0ba219a78f3cef544b8a",
"baseline_authority_path": "/var/lib/aios-gitops-runtime-anchor/baseline/current.json",
"checks": {
"artifact_sha256": "PASS",
"baseline_registry_readback": "PASS",
"health_contract": "PASS",
"legacy_write_rejection": "PASS",
"live_service_unchanged": "PASS",
"manifest_binding": "PASS",
"openapi_readback": "PASS",
"restart_recovery": "PASS"
},
"current_live_port": 18890,
"current_live_service": "aios-gitops-model-api.service",
"deployment_state": "STAGING",
"execute": false,
"governed_release_routing": "bridge-v2",
"legacy_global_writes_enabled": false,
"live_cutover": false,
"owner_actions_required": [
"review exact source OID and artifact SHA",
"approve a separate future live model-api cutover",
"approve any production service transition"
],
"production_restart": false,
"repository_identity": "aios/aios",
"runtime_mode": "IMMUTABLE_STAGING",
"schema": "aios.v2r2c1.model-api-runtime-activation-plan.v1",
"source_oid": "2c6293160ed567a2098715edbb296741577c5bff",
"staging_port": 18893,
"staging_runtime_root": "/opt/aios/gitops-model-api/2c6293160ed567a2098715edbb296741577c5bff/runtime",
"staging_service": "aios-gitops-model-api-v2r2c1-staging.service",
"status": "OWNER_APPROVAL_REQUIRED",
"target_version": "V2R2C1",
"task_id": "TASK-P0-V2R2C1-POST-RELEASE-MAINLINE-CONVERGENCE-20260809"
}
+145
View File
@@ -0,0 +1,145 @@
#!/usr/bin/env python3
from __future__ import annotations
import argparse
import hashlib
import json
import os
from pathlib import Path
import tarfile
import tempfile
SCHEMA = 'aios.v2r2c1.model-api-staging-manifest.v1'
REQUIRED_APP = (
'aios_model_api_staging.py',
'aios_gitops_write_guard.py',
'authoritative_git.py',
'baseline_authority.py',
'gitea_tag_publication.py',
'silent_release_controller.py',
'silent_release_hardening.py',
'aios_gitops_job_controller.py',
)
OPTIONAL_APP = (
'aios_chatgpt_actions_api.py',
'aios_continuation_executor.py',
'aios_continuation_lease.py',
'aios_control_plane_snapshot.py',
)
def sha256(path: Path) -> str:
h = hashlib.sha256()
with path.open('rb') as fh:
for chunk in iter(lambda: fh.read(1024 * 1024), b''):
h.update(chunk)
return h.hexdigest()
def private_marker(path: Path) -> bool:
import re
text = path.read_text(encoding='utf-8', errors='ignore')
patterns = (
r'-----BEGIN [A-Z ]*PRIVATE KEY-----',
r'(?<![A-Za-z0-9])sk-[A-Za-z0-9_-]{20,}(?![A-Za-z0-9_-])',
r'(?<![A-Za-z0-9])ghp_[A-Za-z0-9]{20,}(?![A-Za-z0-9])',
r'(?i)aws_secret_access_key\s*[:=]\s*[A-Za-z0-9/+=]{16,}',
)
return any(re.search(pattern, text) for pattern in patterns)
def main() -> int:
ap = argparse.ArgumentParser()
ap.add_argument('--source-root', required=True, type=Path)
ap.add_argument('--output-root', required=True, type=Path)
ap.add_argument('--source-oid', required=True)
args = ap.parse_args()
oid = args.source_oid.lower()
if len(oid) != 40 or any(c not in '0123456789abcdef' for c in oid):
raise SystemExit('source oid must be exact 40 hex')
source = args.source_root.resolve(strict=True)
output = args.output_root.resolve()
app = source / 'active-runtime' / 'app'
runtime = source / 'runtime' / 'agent-os'
docs = source / 'docs' / 'gitops'
if not app.is_dir():
raise SystemExit('active-runtime/app missing')
paths = []
for name in REQUIRED_APP:
path = app / name
if not path.is_file():
raise SystemExit(f'required app dependency missing: {name}')
paths.append((Path('app') / name, path))
for name in OPTIONAL_APP:
path = app / name
if path.is_file():
paths.append((Path('app') / name, path))
for rel in ('orchestrator/__init__.py', 'orchestrator/action_monitor.py'):
path = runtime / rel
if not path.is_file():
raise SystemExit(f'required runtime dependency missing: {rel}')
paths.append((Path('runtime-agent-os') / rel, path))
openapi = docs / 'openapi-chatgpt-actions.yaml'
if openapi.is_file():
paths.append((Path('docs') / 'gitops' / 'openapi-chatgpt-actions.yaml', openapi))
for folder in ('config/model_registry', 'config/model_priority', 'patches', 'versions'):
root = source / folder
if root.is_dir():
for path in sorted(root.rglob('*')):
if path.is_file() and path.suffix.lower() in {'.json', '.yaml', '.yml', '.md'}:
paths.append((Path(folder) / path.relative_to(root), path))
for rel, path in paths:
if private_marker(path):
raise SystemExit(f'private marker rejected: {rel}')
included = [str(rel).replace('\\', '/') for rel, _ in paths]
manifest = {
'schema': SCHEMA,
'target_version': 'V2R2C1',
'source_oid': oid,
'repository_identity': 'aios/aios',
'deployment_state': 'STAGING',
'runtime_mode': 'IMMUTABLE_STAGING',
'runtime_entrypoint': 'app/aios_model_api_staging.py',
'legacy_global_writes_enabled': False,
'governed_release_routing': 'bridge-v2',
'artifact_sha256': 'provided-by-build-receipt-and-install-manifest',
'included_paths': included,
}
output.mkdir(parents=True, exist_ok=True)
archive = output / f'aios-v2r2c1-model-api-{oid}.tar.gz'
with tempfile.TemporaryDirectory(prefix='v2r2c1-model-api-', dir=str(output)) as tmp:
root = Path(tmp) / 'runtime'
for rel, path in paths:
target = root / rel
target.parent.mkdir(parents=True, exist_ok=True)
target.write_bytes(path.read_bytes())
(root / 'manifest.json').write_text(json.dumps(manifest, ensure_ascii=False, indent=2, sort_keys=True) + '\n', encoding='utf-8')
with tarfile.open(archive, 'w:gz', format=tarfile.PAX_FORMAT) as tf:
for path in sorted(root.rglob('*')):
rel = path.relative_to(root.parent)
info = tf.gettarinfo(str(path), arcname=str(rel).replace('\\', '/'))
info.mtime = 0
info.uid = 0
info.gid = 0
info.uname = 'root'
info.gname = 'root'
if path.is_file():
with path.open('rb') as fh:
tf.addfile(info, fh)
else:
tf.addfile(info)
digest = sha256(archive)
receipt = {
'schema': 'aios.v2r2c1.model-api-staging-build-receipt.v1',
'target_version': 'V2R2C1',
'source_oid': oid,
'repository_identity': 'aios/aios',
'artifact_path': str(archive),
'artifact_sha256': digest,
'deployment_state': 'STAGING',
'manifest_schema': SCHEMA,
'included_paths': included,
}
receipt_path = output / f'aios-v2r2c1-model-api-{oid}.receipt.json'
receipt_path.write_text(json.dumps(receipt, ensure_ascii=False, indent=2, sort_keys=True) + '\n', encoding='utf-8')
print(json.dumps(receipt, ensure_ascii=False, indent=2, sort_keys=True))
return 0
if __name__ == '__main__':
raise SystemExit(main())
@@ -2,7 +2,7 @@ from __future__ import annotations
import hashlib,json,re,tarfile
from pathlib import Path
from typing import Any
OID_RE=re.compile(r"^[0-9a-f]{40}$"); SHA_RE=re.compile(r"^[0-9a-f]{64}$"); VERSION_RE=re.compile(r"^V2R1C[0-9]+$")
OID_RE=re.compile(r"^[0-9a-f]{40}$"); SHA_RE=re.compile(r"^[0-9a-f]{64}$"); VERSION_RE=re.compile(r"^V[0-9]+R[0-9]+C[0-9]+(?:-[A-Za-z0-9_.-]+)?$")
REQUIRED_SOURCE={"schema","version","repository","base_ref","base_oid","binding_mode","release_readiness","production_deployed","tag_created","baseline_changed","production_service","production_port","rollback_metadata","controller_schema","required_checks"}
REQUEST_KEYS={"schema","version","repository","base_oid","target_oid","artifact_uri","artifact_sha256","artifact_size","evidence_closure_oid","approval_plan_id","execute","production_deployed","production_service","production_port","rollback_metadata","current_tag_oid","source_manifest_sha256"}
class ManifestValidationError(ValueError):
@@ -62,7 +62,7 @@ def validate_release_request(source_manifest:dict[str,Any],request_path:Path,exp
if not isinstance(request["approval_plan_id"],str) or not request["approval_plan_id"].startswith("sha256:"): raise ManifestValidationError("APPROVAL_PLAN","invalid approval plan")
if request["execute"] is not False or request["production_deployed"] is not False: raise ManifestValidationError("EXECUTION_NOT_ALLOWED","must be non-executing")
if request["production_service"]!=source_manifest["production_service"] or request["production_port"]!=source_manifest["production_port"]: raise ManifestValidationError("SERVICE_IDENTITY","request service mismatch")
if not isinstance(request["current_tag_oid"],str) or request["current_tag_oid"] not in {"V2R1C6","9e65bd59baea392294c0f253046d3ab116f63145"}: raise ManifestValidationError("PROTECTED_TAG","previous tag binding changed")
if not isinstance(request["current_tag_oid"],str) or request["current_tag_oid"] not in {"V2R1C7","82792347937c0df59a8e02d6162775252bcad2e2"}: raise ManifestValidationError("PROTECTED_TAG","previous production tag binding changed")
_sha(request["source_manifest_sha256"],"source_manifest_sha256"); return request
def build_approval_request(*,source_manifest_path:Path,target_oid:str,artifact_uri:str,artifact_sha256:str,artifact_size:int,evidence_closure_oid:str,rollback_metadata:str,current_tag_oid:str)->dict[str,Any]:
source=_json_object(source_manifest_path); _oid(target_oid,"target_oid"); _sha(artifact_sha256,"artifact_sha256"); _oid(evidence_closure_oid,"evidence_closure_oid"); request={"schema":"aios.release-request.v2","version":source["version"],"repository":source["repository"],"base_oid":source["base_oid"],"target_oid":target_oid,"artifact_uri":artifact_uri,"artifact_sha256":artifact_sha256,"artifact_size":artifact_size,"evidence_closure_oid":evidence_closure_oid,"execute":False,"production_deployed":False,"production_service":source["production_service"],"production_port":source["production_port"],"rollback_metadata":rollback_metadata,"current_tag_oid":current_tag_oid,"source_manifest_sha256":sha256_file(source_manifest_path)}; request["approval_plan_id"]="sha256:"+sha256_bytes(_canonical(request)); return request
@@ -3,13 +3,13 @@ from __future__ import annotations
import json
from pathlib import Path
ROOT = Path(__file__).parents[2]
def test_version_and_candidate_binding():
assert (ROOT / "VERSION").read_text(encoding="utf-8").strip() == "V2R1C6"
assert (ROOT / "VERSION").read_text(encoding="utf-8").strip() == "V2R1C7"
metadata = json.loads((ROOT / "versions/V2R1C6.json").read_text(encoding="utf-8"))
assert metadata["version"] == "V2R1C6"
assert metadata["base_oid"] == "a33e2593967a180c89d44454350ccaeaf3f79b25"
assert metadata["candidate_branch"] == "agent/v2r1c6-mainline-reconciliation-20260806"
assert metadata["production_deployed"] is False
@@ -7,17 +7,19 @@ import pytest
ROOT = Path(__file__).resolve().parents[2]
sys.path.insert(0, str(ROOT / "active-runtime" / "app"))
from deployment_state import RuntimeDeploymentState
from v2r1c6_privacy_tools import health_payload, serve # noqa: E402
def test_health_is_engineering_only() -> None:
result = health_payload()
assert result == {
"status": "ok",
"version": "V2R1C6",
"production_deployed": False,
"gitops_writes_enabled": False,
}
result = health_payload(RuntimeDeploymentState("V2R1C7", "a" * 40, "b" * 64, "STAGED"))
assert result["status"] == "ok"
assert result["version"] == "V2R1C7"
assert result["commit_oid"] == "a" * 40
assert result["artifact_sha256"] == "b" * 64
assert result["deployment_state"] == "STAGED"
assert result["production_deployed"] is False
assert result["health_timestamp"]
def test_listener_rejects_non_loopback() -> None:
+65
View File
@@ -0,0 +1,65 @@
from __future__ import annotations
import importlib.util
import sys
import types
from pathlib import Path
REPOSITORY_ROOT = Path(__file__).resolve().parents[1]
TEST_ROOT = REPOSITORY_ROOT / "tests"
SERVICE_PACKAGE_ROOT = REPOSITORY_ROOT / "services" / "aios-autonomous-release-candidate"
ACTIVE_RUNTIME_ROOT = REPOSITORY_ROOT / "active-runtime"
ACTIVE_RUNTIME_APP = ACTIVE_RUNTIME_ROOT / "app"
for path in (TEST_ROOT, SERVICE_PACKAGE_ROOT, ACTIVE_RUNTIME_APP):
value = str(path)
if value not in sys.path:
sys.path.insert(0, value)
def load_module(relative: str):
path = REPOSITORY_ROOT / relative
spec = importlib.util.spec_from_file_location("v2r1c6_privacy_tools", path)
if spec is None or spec.loader is None:
raise RuntimeError("module_load_failed")
module = importlib.util.module_from_spec(spec)
sys.modules[spec.name] = module
spec.loader.exec_module(module)
return module
def _install_active_runtime_namespace() -> None:
package = sys.modules.get("active_runtime")
if package is None:
package = types.ModuleType("active_runtime")
package.__path__ = [str(ACTIVE_RUNTIME_ROOT)]
package.__package__ = "active_runtime"
sys.modules["active_runtime"] = package
app = sys.modules.get("active_runtime.app")
if app is None:
app = types.ModuleType("active_runtime.app")
app.__path__ = [str(ACTIVE_RUNTIME_APP)]
app.__package__ = "active_runtime"
sys.modules["active_runtime.app"] = app
package.app = app
def _install_legacy_test_module(name: str, relative: str) -> None:
if name in sys.modules:
return
path = REPOSITORY_ROOT / relative
spec = importlib.util.spec_from_file_location(name, path)
if spec is None or spec.loader is None:
raise RuntimeError("legacy_test_module_load_failed:" + name)
module = importlib.util.module_from_spec(spec)
sys.modules[name] = module
spec.loader.exec_module(module)
_install_active_runtime_namespace()
sys.modules.setdefault("conftest", sys.modules[__name__])
_install_legacy_test_module(
"test_native_adapter_contracts",
"tests/autonomous_release/test_native_adapter_contracts.py",
)
+118
View File
@@ -0,0 +1,118 @@
from __future__ import annotations
import hashlib
import json
import pytest
from aios_gitops_job_controller import GitOpsJobController, JobError, JobState, JobStore
HEAD = "a" * 40
BASE = "b" * 40
CURRENT = "c" * 40
def _controller(tmp_path):
store = JobStore(tmp_path / "jobs", payload_root=tmp_path / "payloads")
return GitOpsJobController(store), store
def _request():
payload = b"mission-recovery-test"
return {
"campaign_id": "AIOS-AUTONOMOUS-RELEASE-CAMPAIGN-20260807",
"repository": "aios-gitops",
"operation": "PROTECTED_MERGE",
"branch": "agent/recovery-test",
"expected_branch_oid": HEAD,
"expected_main_oid": BASE,
"payload_digest": hashlib.sha256(payload).hexdigest(),
"pr_number": 53,
"task_id": "AIOS_AUTONOMOUS_RELEASE_CAMPAIGN_V1",
}, payload
def _proof(proof_job_id: str, **overrides):
proof = {
"proof_schema": "aios.job-recovery.v1",
"job_id": proof_job_id,
"operation": "PROTECTED_MERGE",
"pr_number": 53,
"pr_merged": True,
"protected_merge": True,
"head_oid": HEAD,
"base_oid": BASE,
"main_oid_after_protected_merge": HEAD,
"gitea_main_oid_after_protected_merge": HEAD,
"authority_main_oid_after_protected_merge": HEAD,
"current_gitea_main_oid": CURRENT,
"current_authority_main_oid": CURRENT,
"current_main_descends_from_protected_merge": True,
"force_push": False,
"machine_actor": "aios-autonomy-agent",
}
proof.update(overrides)
return proof
def _manual_job(tmp_path):
controller, store = _controller(tmp_path)
request, payload = _request()
submitted = controller.submit(request, payload=payload)
manual = controller.advance(
submitted.job_id,
JobState.MANUAL_OWNER_REVIEW_REQUIRED,
side_effect_state="MANUAL_OWNER_REVIEW_REQUIRED",
receipt={"historical": "preserve-me"},
expected_revision=submitted.revision,
)
return controller, store, manual
def test_verified_external_recovery_preserves_history_and_is_idempotent(tmp_path):
controller, store, manual = _manual_job(tmp_path)
proof = _proof(manual.job_id)
completed = controller.complete_verified_external_side_effect(
manual.job_id, proof, expected_revision=manual.revision
)
assert completed.state is JobState.COMPLETED
assert completed.side_effect_state == "COMPLETED"
receipt = json.loads(store.read(manual.job_id)["receipt_json"])
assert receipt["recovered_from_historical_manual"] is True
assert receipt["historical_receipt"] == {"historical": "preserve-me"}
assert receipt["proof"] == proof
duplicate = controller.complete_verified_external_side_effect(manual.job_id, proof)
assert duplicate.state is JobState.COMPLETED
assert duplicate.revision == completed.revision
@pytest.mark.parametrize(
"field,value",
[
("job_id", "job-wrong"),
("head_oid", "d" * 40),
("main_oid_after_protected_merge", "d" * 40),
("force_push", True),
("operation", "PUSH"),
],
)
def test_verified_external_recovery_rejects_mismatched_proof(tmp_path, field, value):
controller, _store, manual = _manual_job(tmp_path)
proof_job_id = value if field == "job_id" else manual.job_id
overrides = {} if field == "job_id" else {field: value}
with pytest.raises(JobError):
controller.complete_verified_external_side_effect(
manual.job_id, _proof(proof_job_id, **overrides)
)
assert controller.store.read(manual.job_id)["state"] == JobState.MANUAL_OWNER_REVIEW_REQUIRED.value
def test_ordinary_terminal_advance_and_resume_remain_denied(tmp_path):
controller, _store, manual = _manual_job(tmp_path)
with pytest.raises(JobError, match="JOB_TERMINAL"):
controller.advance(manual.job_id, JobState.COMPLETED, side_effect_state="COMPLETED")
with pytest.raises(JobError, match="JOB_TERMINAL"):
controller.resume(manual.job_id)
@@ -0,0 +1,78 @@
import subprocess
from pathlib import Path
import pytest
from governance.ref_reconciler import RefReconciler, RefReconciliationError
from governance.semantic_protection import semantic_digest, semantic_policy, transport_digest
def protection(**overrides):
value = {
"branch_name": "main",
"enable_push": False,
"enable_force_push": False,
"enable_merge_whitelist": True,
"merge_whitelist_usernames": ["owner"],
"required_approvals": 0,
"enable_status_check": True,
"status_check_contexts": ["owner-approval-gate", "py-compile"],
"block_admin_merge_override": True,
"block_on_official_review_requests": True,
"block_on_outdated_branch": True,
"block_on_rejected_reviews": True,
"dismiss_stale_approvals": True,
"ignore_stale_approvals": False,
"enable_approvals_whitelist": True,
"approvals_whitelist_username": ["owner"],
"enable_bypass_allowlist": False,
"enable_force_push_allowlist": False,
"enable_push_whitelist": False,
"protected_file_patterns": "[]",
"unprotected_file_patterns": "[]",
}
value.update(overrides)
return value
def test_transport_metadata_does_not_change_semantic_digest():
before = protection(updated_at="old")
after = protection(updated_at="new")
assert transport_digest(before) != transport_digest(after)
assert semantic_digest(before) == semantic_digest(after)
def test_stage1_whitelist_is_a_semantic_change():
before = protection()
after = protection(merge_whitelist_usernames=["owner", "aios-autonomy-agent"])
assert semantic_policy(before)["merge_whitelist_user_ids"] == [2]
assert semantic_policy(after)["merge_whitelist_user_ids"] == [2, 153]
assert semantic_digest(before) != semantic_digest(after)
def test_ref_reconciler_rejects_gitea_mismatch(tmp_path: Path):
reconciler = RefReconciler(tmp_path / "missing.git")
with pytest.raises(RefReconciliationError, match="GITEA_MAIN_EXPECTATION_MISMATCH"):
reconciler.reconcile_main(expected_oid="a" * 40, merged_oid="b" * 40, gitea_main_oid="c" * 40)
def test_ref_reconciler_ff_cas_and_idempotency(tmp_path: Path):
repo = tmp_path / "repo.git"
subprocess.run(["git", "init", "--bare", str(repo)], check=True, capture_output=True)
worktree = tmp_path / "work"
subprocess.run(["git", "clone", str(repo), str(worktree)], check=True, capture_output=True)
subprocess.run(["git", "-C", str(worktree), "config", "user.email", "test@example.invalid"], check=True)
subprocess.run(["git", "-C", str(worktree), "config", "user.name", "test"], check=True)
(worktree / "one.txt").write_text("one\n", encoding="utf-8")
subprocess.run(["git", "-C", str(worktree), "add", "one.txt"], check=True)
subprocess.run(["git", "-C", str(worktree), "commit", "-m", "one"], check=True, capture_output=True)
old = subprocess.check_output(["git", "-C", str(worktree), "rev-parse", "HEAD"], text=True).strip()
subprocess.run(["git", "-C", str(worktree), "push", "origin", "HEAD:refs/heads/main"], check=True, capture_output=True)
(worktree / "two.txt").write_text("two\n", encoding="utf-8")
subprocess.run(["git", "-C", str(worktree), "add", "two.txt"], check=True)
subprocess.run(["git", "-C", str(worktree), "commit", "-m", "two"], check=True, capture_output=True)
new = subprocess.check_output(["git", "-C", str(worktree), "rev-parse", "HEAD"], text=True).strip()
subprocess.run(["git", "-C", str(worktree), "push", "origin", "HEAD:refs/heads/main"], check=True, capture_output=True)
reconciler = RefReconciler(repo)
assert reconciler.reconcile_main(expected_oid=old, merged_oid=new, gitea_main_oid=new) == new
assert reconciler.reconcile_main(expected_oid=old, merged_oid=new, gitea_main_oid=new) == new
+169
View File
@@ -0,0 +1,169 @@
from __future__ import annotations
import copy
import sys
from pathlib import Path
import pytest
ROOT = Path(__file__).resolve().parents[2]
sys.path.insert(0, str(ROOT / "active-runtime" / "app"))
from campaign_activation import CampaignActivationError, build_active_policy
from release_campaign_bootstrap import campaign_policy
import governance.stage2_gate_migration as stage2
ANCHOR = "69b45e81dcd2c3707c367003b08c55fd361faae8"
MAIN = "2a32e30c5cbd894cf007326917776893b3020231"
def protection(statuses=None):
return {
"branch_name": "main",
"enable_push": False,
"enable_force_push": False,
"enable_merge_whitelist": True,
"merge_whitelist_usernames": ["aios-autonomy-agent", "owner"],
"required_approvals": 0,
"enable_status_check": True,
"status_check_contexts": statuses or [
"autonomy-unit-tests", "owner-approval-gate", "py-compile"
],
"block_admin_merge_override": True,
"block_on_official_review_requests": True,
"block_on_outdated_branch": True,
"block_on_rejected_reviews": True,
"dismiss_stale_approvals": True,
"ignore_stale_approvals": False,
"enable_approvals_whitelist": True,
"approvals_whitelist_username": ["owner"],
"enable_bypass_allowlist": False,
"enable_force_push_allowlist": False,
"enable_push_whitelist": False,
"protected_file_patterns": "[]",
"unprotected_file_patterns": "[]",
"updated_at": "transport-only",
}
def facts(**overrides):
value = {
"campaign_id": stage2.CAMPAIGN_ID,
"campaign_active": True,
"campaign_revoked": False,
"machine_actor": "aios-autonomy-agent",
"required_checks_all_success": True,
"required_checks_same_head": True,
"unresolved_conversations": 0,
"mergeable": True,
"protected_target": "main",
"current_main_oid": MAIN,
"gitea_main_oid": MAIN,
"current_main_lineage_verified": True,
}
value.update(overrides)
return value
def test_stage2_replaces_only_gate_and_is_idempotent(monkeypatch):
before = protection()
monkeypatch.setattr(stage2, "STAGE1_SEMANTIC_SHA", stage2.semantic_digest(before))
status, target = stage2.build_target(before)
assert status == "READY"
assert "owner-approval-gate" not in target["status_check_contexts"]
assert "campaign-approval-gate" in target["status_check_contexts"]
for key in before:
if key != "status_check_contexts":
assert target[key] == before[key]
status2, target2 = stage2.build_target(target)
assert status2 == "IDEMPOTENT_SUCCESS"
assert target2 == target
@pytest.mark.parametrize(
"field,value",
[
("campaign_active", False),
("campaign_revoked", True),
("required_checks_all_success", False),
("required_checks_same_head", False),
("unresolved_conversations", 1),
("mergeable", False),
("current_main_oid", "f" * 40),
("machine_actor", "owner"),
],
)
def test_stage2_fails_closed_on_gate_fact_drift(field, value):
with pytest.raises(stage2.Stage2GateError):
stage2.validate_stage2_facts(facts(**{field: value}))
def test_stage2_canonicalizer_accepts_live_empty_pattern_fields():
before = protection()
before["protected_file_patterns"] = ""
before["unprotected_file_patterns"] = ""
monkeypatch = pytest.MonkeyPatch()
try:
monkeypatch.setattr(stage2, "STAGE1_SEMANTIC_SHA", stage2.semantic_digest(before))
status, target = stage2.build_target(before)
assert status == "READY"
assert target["protected_file_patterns"] == ""
assert target["unprotected_file_patterns"] == ""
finally:
monkeypatch.undo()
def test_stage2_fails_closed_on_semantic_drift(monkeypatch):
before = protection()
monkeypatch.setattr(stage2, "STAGE1_SEMANTIC_SHA", "f" * 64)
with pytest.raises(stage2.Stage2GateError, match="SEMANTIC_PRECONDITION_INVALID"):
stage2.build_target(before)
def activation_facts(**overrides):
value = {
"campaign_id": stage2.CAMPAIGN_ID,
"repository": "aios/aios",
"source_main_oid": MAIN,
"gitea_main_oid": MAIN,
"authority_main_oid": MAIN,
"runtime_source_oid": MAIN,
"durable_job_state": "COMPLETED",
"writer_ready": True,
"gitea_machine_authenticated": True,
"governance_executor_ready": True,
"preapproval_publisher_ready": True,
"runtime_exact_main": True,
"main_lineage_verified": True,
"campaign_revoked": False,
}
value.update(overrides)
return value
def test_campaign_activation_changes_only_active_state():
pending = campaign_policy(stage2.CAMPAIGN_ID, ANCHOR)
active = build_active_policy(pending, activation_facts())
assert active["activation_state"] == "ACTIVE"
assert active["active"] is True
for key in pending:
if key not in {"activation_state", "active"}:
assert active[key] == pending[key]
@pytest.mark.parametrize(
"field,value",
[
("source_main_oid", "e" * 40),
("gitea_main_oid", "e" * 40),
("durable_job_state", "MANUAL_OWNER_REVIEW_REQUIRED"),
("writer_ready", False),
("governance_executor_ready", False),
("campaign_revoked", True),
],
)
def test_campaign_activation_fails_closed(field, value):
pending = campaign_policy(stage2.CAMPAIGN_ID, ANCHOR)
with pytest.raises(CampaignActivationError):
build_active_policy(pending, activation_facts(**{field: value}))
+95
View File
@@ -0,0 +1,95 @@
from types import SimpleNamespace
import json
from pathlib import Path
import sys
import tempfile
import pytest
ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(ROOT / "active-runtime" / "app"))
import release_campaign_authorization as a
import release_campaign_bootstrap as b
import campaign_release_acceptance as c
import release_campaign_orchestrator as o
ANCHOR = "69b45e81dcd2c3707c367003b08c55fd361faae8"
def policy(state="ACTIVE", active=True):
value = b.campaign_policy("AIOS-AUTONOMOUS-RELEASE-V1", ANCHOR)
value["activation_state"] = state
value["active"] = active
return value
def test_campaign_policy_exact():
assert a.validate_policy(policy())["normal_owner_interaction_required"] is False
def test_denied_operation_fails():
obj = a.CampaignAuthorization(Path("/tmp/x"), policy(), "0"*64)
with pytest.raises(a.CampaignError):
obj.assert_operation("force_push")
def test_release_acceptance_has_no_second_owner_interaction():
obj = a.CampaignAuthorization(Path("/tmp/x"), policy(), "0"*64)
binding = {
"version": "V2R1C8",
"target_oid": "a"*40,
"evidence_closure_oid": "b"*40,
"artifact_sha256": "c"*64,
"artifact_size": 123,
"approval_plan_id": "sha256:" + "d"*64,
}
receipt = c.evaluate(
obj,
binding,
{"object_type": "commit", "target_is_ancestor": True, "closure_oid": "b"*40},
{"sha256": "c"*64, "size": 123, "retained": True},
{"install": "PASS", "health": "PASS", "restart": "PASS", "rollback_readiness": "PASS"},
{"repository": "aios/aios", "target_in_main_lineage": True},
{"conflict": False},
)
assert receipt["accepted"] is True
assert receipt["owner_interaction_required"] is False
def test_changed_binding_rejected():
obj = a.CampaignAuthorization(Path("/tmp/x"), policy(), "0"*64)
with pytest.raises(c.AcceptanceError):
c.evaluate(
obj,
{
"version": "V2R1C8",
"target_oid": "a"*40,
"evidence_closure_oid": "b"*40,
"artifact_sha256": "c"*64,
"artifact_size": 123,
"approval_plan_id": "sha256:" + "d"*64,
},
{"object_type": "commit", "target_is_ancestor": True, "closure_oid": "b"*40},
{"sha256": "e"*64, "size": 123, "retained": True},
{"install": "PASS", "health": "PASS", "restart": "PASS", "rollback_readiness": "PASS"},
{"repository": "aios/aios", "target_in_main_lineage": True},
{"conflict": False},
)
def test_orchestrator_recoverable_failure_resumes_without_owner():
state = o.ReleaseState("V2R1C8", state="STAGING", last_stable_state="STAGING")
state.recoverable_failure("transient_502")
assert state.as_dict()["owner_interaction_required"] is False
state.resume()
assert state.state == "STAGING"
def test_only_red_line_can_enter_manual_review():
state = o.ReleaseState("V2R1C8")
with pytest.raises(o.OrchestratorError):
state.manual_review("pytest_failed")
state.manual_review("formal_same_name_tag_conflict")
assert state.as_dict()["owner_interaction_required"] is True
@@ -0,0 +1,90 @@
import base64
from pathlib import Path
from aios_authority_git_executor import AuthorityGitExecutor
from aios_chatgpt_bridge_v2_server import BridgeHandler, RuntimeConfig
from aios_continuation_executor import GiteaContinuationRemote, ServerSideCredentialProjection
from aios_continuation_lease import WriterLeaseRequestV2
class Probe:
def __init__(self, present=False):
self.present = present
def branch_exists(self, _branch):
return self.present
def branch_head(self, _branch):
return "a" * 40
def is_ancestor(self, _older, _newer):
return True
def commit_exists(self, _oid):
return True
def test_create_new_lease_is_exact_base_and_absent():
request = WriterLeaseRequestV2(
task_id="TASK-P0-BRIDGE-V21-DURABLE-RECONCILIATION-20260809",
repository="aios/aios",
branch_mode="CREATE_NEW",
base_oid="a" * 40,
expected_branch_oid="a" * 40,
target_branch="agent/p0-bridge-v21-durable-reconciliation-20260809",
generation=1,
)
request.validate(Probe())
def test_create_new_lease_rejects_existing_target():
request = WriterLeaseRequestV2(
task_id="task",
repository="aios/aios",
branch_mode="CREATE_NEW",
base_oid="a" * 40,
expected_branch_oid="a" * 40,
target_branch="agent/existing-target",
generation=1,
)
try:
request.validate(Probe(True))
except Exception as exc:
assert str(exc) == "TARGET_BRANCH_ALREADY_EXISTS"
else:
raise AssertionError("existing CREATE_NEW target must fail")
def test_slash_branch_uses_paginated_collection():
credential = ServerSideCredentialProjection(Path("/tmp/key"), Path("/tmp/token"), active=True)
calls = []
class FakeRemote(GiteaContinuationRemote):
def _api(self, path):
calls.append(path)
if "page=1" in path:
return [{"name": f"agent/other-{i}", "commit": {"id": "b" * 40}} for i in range(50)]
return [{"name": "agent/slash/branch", "commit": {"id": "c" * 40}}]
remote = FakeRemote(Path("/tmp"), credential)
assert remote.read_api_branch("agent/slash/branch") == "c" * 40
assert calls[0].endswith("branches?page=1&limit=50")
assert calls[1].endswith("branches?page=2&limit=50")
def test_http_route_contract_is_complete():
expected = {
"/v2/runtime", "/v2/capabilities", "/v2/governed-task",
"/v2/writer-lease", "/v2/commit-file", "/v2/commit-files",
"/v2/job", "/v2/job/resume", "/v2/job/reconcile",
"/v2/tag/approval-request",
"/v2/tag/publish",
"/v2/tag/status",
"/v2/release/artifact",
"/v2/release/execute",
"/v2/release/consistency",
"/v2/release/acceptance",
}
from aios_chatgpt_bridge_v2_server import ROUTES
assert set(ROUTES) == expected
@@ -0,0 +1,265 @@
from __future__ import annotations
from datetime import datetime, timezone
import tempfile
from concurrent.futures import ThreadPoolExecutor
import ast
from pathlib import Path
import pytest
from aios_continuation_lease import (
CAMPAIGN_HEAD,
LINEAGE_ANCHOR,
ContinuationError,
ContinuationLeaseManager,
WriterLeaseRequestV2,
)
from aios_gitops_job_controller import JobState, JobStore, GitOpsJobController, JobError, PayloadStore
from aios_gitea_machine_broker import MachineBrokerError, MachineCredentialProjection, reconcile_exact_branch, reconcile_fast_forward
from aios_gitops_writer_worker import runtime_policy
from aios_control_plane_runtime_status import RuntimeStatus
from aios_control_plane_sentinel import SentinelInputs, evaluate
from authorized_task_writer_provisioning import validate_writer_lease_request_v2
class Probe:
def __init__(self):
self.head = CAMPAIGN_HEAD
def branch_exists(self, branch):
return branch == "agent/autonomous-release-campaign-v1-20260807"
def branch_head(self, branch):
return self.head
def is_ancestor(self, older, newer):
return (older == LINEAGE_ANCHOR and newer == CAMPAIGN_HEAD) or (
older != newer and older in {CAMPAIGN_HEAD, "c" * 40} and newer in {"c" * 40, "d" * 40}
)
class CAS:
def __init__(self):
self.calls = []
self.stale = False
def compare_and_swap_branch(self, branch, expected, new):
if self.stale:
raise RuntimeError("stale")
self.calls.append((branch, expected, new))
return new
def request(generation=1):
return WriterLeaseRequestV2(
task_id="AIOS_AUTONOMOUS_RELEASE_CAMPAIGN_V1",
repository="aios/aios",
branch_mode="CONTINUE_EXISTING",
base_oid=LINEAGE_ANCHOR,
expected_branch_oid=CAMPAIGN_HEAD,
target_branch="agent/autonomous-release-campaign-v1-20260807",
generation=generation,
)
def test_continue_existing_accepts_exact_head_and_lineage():
manager = ContinuationLeaseManager(
Probe(), CAS(), now=lambda tz: datetime(2026, 8, 8, tzinfo=timezone.utc)
)
lease = manager.open(request())
assert lease.current_head_oid == CAMPAIGN_HEAD
assert lease.multi_commit and lease.append_only and lease.recoverable
def test_continue_existing_rejects_branch_head_conflict():
probe = Probe()
probe.head = "c" * 40
with pytest.raises(ContinuationError, match="EXISTING_BRANCH_OID_CONFLICT"):
request().validate(probe)
def test_provisioning_contract_v2_never_creates_existing_branch():
raw = {
"schema": "aios.task-writer-lease-request.v2",
"task_id": request().task_id,
"repository": request().repository,
"branch_mode": request().branch_mode,
"base_oid": request().base_oid,
"expected_branch_oid": request().expected_branch_oid,
"target_branch": request().target_branch,
"generation": 1,
"lease_seconds": 900,
}
result = validate_writer_lease_request_v2(raw, Probe())
assert result["create_target_branch"] is False
assert result["cas_ref_update_required"] is True
def test_append_uses_compare_and_swap_and_allows_multiple_commits():
cas = CAS()
manager = ContinuationLeaseManager(Probe(), cas)
lease = manager.open(request())
next_lease = manager.append_commit(lease, "c" * 40)
manager.append_commit(next_lease, "d" * 40)
assert cas.calls == [
(request().target_branch, CAMPAIGN_HEAD, "c" * 40),
(request().target_branch, "c" * 40, "d" * 40),
]
def test_stale_cas_fails_closed():
cas = CAS()
cas.stale = True
manager = ContinuationLeaseManager(Probe(), cas)
with pytest.raises(ContinuationError, match="BRANCH_LEASE_STALE"):
manager.append_commit(manager.open(request()), "c" * 40)
def test_job_idempotency_and_recovery():
with tempfile.TemporaryDirectory() as temp:
controller = GitOpsJobController(JobStore(temp))
req = {
"campaign_id": "campaign",
"repository": "aios/aios",
"operation": "commit-file",
"branch": request().target_branch,
"expected_branch_oid": CAMPAIGN_HEAD,
"payload_digest": "b" * 64,
"payload": {"files": ["active-runtime/app/x.py"]},
}
first = controller.submit(req)
duplicate = controller.submit(req)
assert first.job_id == duplicate.job_id
assert duplicate.side_effect_state == "SAME_JOB"
failed = controller.advance(first.job_id, JobState.RECOVERABLE_FAILURE, side_effect_state="UNKNOWN")
assert failed.side_effect_state == "RESUME_SAME_JOB"
resumed = controller.resume(first.job_id)
assert resumed.job_id == first.job_id and resumed.state == JobState.REQUESTED
tables = {"jobs", "idempotency_keys", "branch_leases", "state_transitions", "receipts"}
with JobStore(temp)._connect() as connection:
assert tables.issubset({row["name"] for row in connection.execute("SELECT name FROM sqlite_master WHERE type='table'")})
assert connection.execute("PRAGMA journal_mode").fetchone()[0].lower() == "wal"
def test_job_state_cannot_regress():
with tempfile.TemporaryDirectory() as temp:
controller = GitOpsJobController(JobStore(temp))
req = {
"campaign_id": "campaign",
"repository": "aios/aios",
"operation": "commit-file",
"branch": request().target_branch,
"expected_branch_oid": CAMPAIGN_HEAD,
"payload_digest": "b" * 64,
}
job = controller.submit(req)
controller.advance(job.job_id, JobState.AUTHORIZED, side_effect_state="AUTHORIZED")
with pytest.raises(JobError):
controller.advance(job.job_id, JobState.REQUESTED, side_effect_state="BAD")
def test_machine_broker_rejects_owner_identity_and_plaintext_reference():
with pytest.raises(MachineBrokerError):
MachineCredentialProjection("owner-token-value", "aiman")
projection = MachineCredentialProjection.from_mapping({"scheme": "systemd-credential", "name": "gitea-machine-token", "account": "aios-machine"})
assert projection.reference == "systemd-credential:gitea-machine-token"
def test_ref_reconciler_is_fail_closed():
assert reconcile_exact_branch("a" * 40, None, "a" * 40) == "CREATE_REQUIRED"
assert reconcile_exact_branch("a" * 40, "b" * 40, "a" * 40) == "MANUAL_OWNER_REVIEW_REQUIRED"
assert reconcile_fast_forward("a" * 40, "a" * 40, "b" * 40, old_is_ancestor_of_new=True) == "FAST_FORWARD_REQUIRED"
def test_payload_store_is_content_addressed_and_fail_closed():
with tempfile.TemporaryDirectory() as temp:
store = PayloadStore(temp)
payload = b"approved-payload"
digest = __import__("hashlib").sha256(payload).hexdigest()
reference, size = store.put(payload, digest)
assert store.read_verified(reference, digest, size) == payload
with pytest.raises(JobError, match="PAYLOAD_DIGEST_MISMATCH"):
store.put(b"changed", digest)
def test_concurrent_duplicate_submit_collapses_to_one_job():
with tempfile.TemporaryDirectory() as temp:
controller = GitOpsJobController(JobStore(temp))
req = {"campaign_id": "campaign", "repository": "aios/aios", "operation": "commit-file", "branch": request().target_branch, "expected_branch_oid": CAMPAIGN_HEAD, "payload_digest": "b" * 64}
with ThreadPoolExecutor(max_workers=4) as pool:
receipts = list(pool.map(lambda _: controller.submit(req), range(8)))
assert len({item.job_id for item in receipts}) == 1
def test_gate_split_keeps_legacy_mutation_false():
assert runtime_policy({"AIOS_GITOPS_WRITES_ENABLED": "false", "AIOS_GITOPS_DURABLE_JOB_EXECUTION_ENABLED": "true"}) == {
"legacy_global_write": False,
"durable_job_execution": True,
"repository_mutation_allowed": True,
}
def test_revision_cas_and_receipt_are_transactional():
with tempfile.TemporaryDirectory() as temp:
controller = GitOpsJobController(JobStore(temp))
req = {"campaign_id": "campaign", "repository": "aios/aios", "operation": "commit-file", "branch": request().target_branch, "expected_branch_oid": CAMPAIGN_HEAD, "payload_digest": "b" * 64}
job = controller.submit(req)
authorized = controller.advance(job.job_id, JobState.AUTHORIZED, side_effect_state="AUTHORIZED", expected_revision=0)
with pytest.raises(JobError, match="REVISION_CAS_CONFLICT"):
controller.advance(job.job_id, JobState.LEASED, side_effect_state="LEASED", expected_revision=0)
completed = controller.advance(job.job_id, JobState.COMPLETED, side_effect_state="REMOTE_VERIFIED", expected_revision=authorized.revision, receipt={"remote_verified": True})
assert completed.http_status == 200
assert controller.submit(req).http_status == 200
with JobStore(temp)._connect() as connection:
assert connection.execute("SELECT COUNT(*) FROM receipts").fetchone()[0] == 1
assert connection.execute("SELECT COUNT(*) FROM state_transitions").fetchone()[0] == 3
def test_job_lease_mutation_is_transactional_and_revision_bound():
with tempfile.TemporaryDirectory() as temp:
controller = GitOpsJobController(JobStore(temp))
req = {"campaign_id": "campaign", "repository": "aios/aios", "operation": "commit-file", "branch": request().target_branch, "expected_branch_oid": CAMPAIGN_HEAD, "payload_digest": "b" * 64}
job = controller.submit(req)
leased = controller.lease_job(job.job_id, lease_id="lease-1", lineage_anchor_oid=LINEAGE_ANCHOR, current_head_oid=CAMPAIGN_HEAD, generation=1, lease_expires_at="2026-08-08T00:00:00Z", expected_revision=0)
assert leased.state == JobState.LEASED
controller.update_lease("lease-1", expected_revision=0, current_head_oid="c" * 40, generation=1)
with pytest.raises(JobError, match="LEASE_REVISION_CAS_CONFLICT"):
controller.update_lease("lease-1", expected_revision=0, current_head_oid="d" * 40, generation=1)
def test_reconcile_failure_is_durable_and_not_directly_advanced():
with tempfile.TemporaryDirectory() as temp:
controller = GitOpsJobController(JobStore(temp))
req = {"campaign_id": "campaign", "repository": "aios/aios", "operation": "commit-file", "branch": request().target_branch, "expected_branch_oid": CAMPAIGN_HEAD, "payload_digest": "b" * 64}
job = controller.submit(req)
controller.advance(job.job_id, JobState.RECOVERABLE_FAILURE, side_effect_state="UNKNOWN")
with pytest.raises(JobError, match="RECONCILIATION_REQUIRED"):
controller.reconcile(job.job_id, lambda _row: (_ for _ in ()).throw(RuntimeError("probe failed")))
assert JobStore(temp).read(job.job_id)["state"] == JobState.RECONCILIATION_REQUIRED.value
def test_worker_gate_closed_and_safe_dry_execution():
assert runtime_policy({"AIOS_GITOPS_WRITES_ENABLED": "false", "AIOS_GITOPS_DURABLE_JOB_EXECUTION_ENABLED": "false"})["durable_job_execution"] is False
def test_runtime_planes_do_not_conflate_readiness():
status = RuntimeStatus({"model_api": True}, {"job_controller": True, "gitea_machine_credential": False}, {"artifact_store": True})
assert status.release_control_plane_ready is False
assert status.as_dict()["WRITE_PLANE"]["gitea_machine_credential"] is False
def test_sentinel_is_workspace_independent_and_fail_closed():
inputs = SentinelInputs(tempfile.gettempdir() + "/missing-authority.git", "gitea", "https://gitea/api", "refs/heads/campaign", "a" * 40)
result = evaluate(inputs, network_reader=lambda *_: "a" * 40, gitea_reader=lambda *_: "a" * 40)
assert result.equal is False and result.error == "AUTHORITY_REF_UNAVAILABLE"
def test_api_file_method_has_no_inline_git_mutation_calls():
source = Path(__file__).parents[1] / "active-runtime" / "app" / "aios_chatgpt_actions_api.py"
if not source.exists():
pytest.skip("candidate source is remote-only in this local test runner")
tree = ast.parse(source.read_text(encoding="utf-8"))
method = next(node for node in ast.walk(tree) if isinstance(node, ast.FunctionDef) and node.name == "file")
calls = {node.func.id for node in ast.walk(method) if isinstance(node, ast.Call) and isinstance(node.func, ast.Name)}
assert not calls.intersection({"checkout", "write_file", "commit", "sh"})
@@ -0,0 +1,328 @@
from __future__ import annotations
from datetime import datetime, timedelta, timezone
import hashlib
import importlib.util
import json
from pathlib import Path
import shutil
import subprocess
import pytest
MODULE_PATH = Path(__file__).parents[1] / "active-runtime" / "app" / "aios_control_plane_host_seed_executor.py"
spec = importlib.util.spec_from_file_location("host_seed_executor", MODULE_PATH)
m = importlib.util.module_from_spec(spec)
assert spec and spec.loader
import sys
sys.modules[spec.name] = m
spec.loader.exec_module(m)
def run(*args: str, cwd: Path) -> str:
return subprocess.check_output(args, cwd=cwd, text=True).strip()
def sha(data: bytes) -> str:
return hashlib.sha256(data).hexdigest()
def canonical(value: object) -> bytes:
return (json.dumps(value, sort_keys=True, separators=(",", ":")) + "\n").encode()
def setup_case(tmp_path: Path, *, risk: str = "high", tamper_bootstrap: bool = False):
repo = tmp_path / "src"
bare = tmp_path / "repo.git"
repo.mkdir()
run("git", "init", "-b", "main", cwd=repo)
run("git", "config", "user.email", "test@example.invalid", cwd=repo)
run("git", "config", "user.name", "test", cwd=repo)
(repo / "a.txt").write_text("base\n", encoding="utf-8")
(repo / "bin.sh").write_text("#!/bin/sh\nexit 0\n", encoding="utf-8")
(repo / "bin.sh").chmod(0o755)
run("git", "add", "-A", cwd=repo)
run("git", "commit", "-m", "base", cwd=repo)
base = run("git", "rev-parse", "HEAD", cwd=repo)
run("git", "clone", "--bare", str(repo), str(bare), cwd=tmp_path)
(repo / "a.txt").write_text("changed\n", encoding="utf-8")
(repo / "c.txt").write_text("added\n", encoding="utf-8")
run("git", "add", "-N", "c.txt", cwd=repo)
patch = subprocess.check_output(["git", "diff", "--binary", base], cwd=repo)
after = {
"a.txt": sha((repo / "a.txt").read_bytes()),
"bin.sh": sha((repo / "bin.sh").read_bytes()),
"c.txt": sha((repo / "c.txt").read_bytes()),
}
package_members = {f"payload/repo/after/{k}": v for k, v in after.items()}
release_tree = sha(json.dumps({k: package_members[k] for k in sorted(package_members)}, sort_keys=True, separators=(",", ":")).encode())
manifest = {
"schema": "aios.control-plane-immutable-candidate.v6",
"candidate": "V9",
"repository": "aios/aios",
"base_oid": base,
"release_id": "control-plane-test-v9",
"changed_file_count": 2,
"changed_files": [
{"path": "a.txt", "status": "modified", "after_sha256": after["a.txt"]},
{"path": "c.txt", "status": "added", "after_sha256": after["c.txt"]},
],
"release_member_count": 3,
"package_members": package_members,
"release_tree_sha256": release_tree,
}
manifest_raw = canonical(manifest)
bootstrap = b"#!/usr/bin/env python3\nprint('seed helper evidence only')\n"
bootstrap_sha = sha(bootstrap)
artifact_sha = "a" * 64
goal = {
"schema": "aios.control-plane-immutable-candidate-goal.v6",
"candidate": "V9",
"repository": "aios/aios",
"base_oid": base,
"release_id": "control-plane-test-v9",
"artifact_sha256": artifact_sha,
"manifest_sha256": sha(manifest_raw),
"release_tree_sha256": release_tree,
"seed_bootstrap_sha256": bootstrap_sha,
"host_seed_authorized": False,
}
goal_raw = canonical(goal)
# Reset the source worktree to base; delivery files live in a separate commit.
run("git", "reset", "--hard", base, cwd=repo)
delivery = repo / "control-plane" / "candidates" / "v9"
delivery.mkdir(parents=True)
(delivery / "candidate.patch").write_bytes(patch)
(delivery / "implementation_manifest_v9.json").write_bytes(manifest_raw)
(delivery / "machine_goal_v9.json").write_bytes(goal_raw)
(delivery / "aios_control_plane_immutable_seed_v9.py").write_bytes(
b"tampered\n" if tamper_bootstrap else bootstrap
)
run("git", "add", "-A", cwd=repo)
run("git", "commit", "-m", "candidate payload", cwd=repo)
payload_commit = run("git", "rev-parse", "HEAD", cwd=repo)
run("git", "push", str(bare), f"{payload_commit}:refs/heads/agent/candidate-payload", cwd=repo)
intent = {
"candidate": "V9",
"release_id": "control-plane-test-v9",
"base_oid": base,
"payload_commit": payload_commit,
"patch_path": "control-plane/candidates/v9/candidate.patch",
"manifest_path": "control-plane/candidates/v9/implementation_manifest_v9.json",
"machine_goal_path": "control-plane/candidates/v9/machine_goal_v9.json",
"seed_bootstrap_path": "control-plane/candidates/v9/aios_control_plane_immutable_seed_v9.py",
"artifact_sha256": artifact_sha,
"manifest_sha256": sha(manifest_raw),
"machine_goal_sha256": sha(goal_raw),
"release_tree_sha256": release_tree,
"seed_bootstrap_sha256": bootstrap_sha,
"target_host": "cn-network-mkt",
}
intent_digest = sha(canonical(intent))
task_id = "host-seed-authz-v9-cn-network-mkt"
now = datetime(2026, 8, 22, 12, 0, tzinfo=timezone.utc)
expires_text = (now + timedelta(minutes=30)).isoformat().replace("+00:00", "Z")
authorization_digest = sha(canonical({
"task_id": task_id,
"intent_sha256": intent_digest,
"expires_at": expires_text,
}))
binding = {
"task_id": task_id,
"repository": "aios/aios",
"base_oid": payload_commit,
"target_branch": "agent/host-seed-authorization-v9-cn-network-mkt",
"risk": risk,
"description": "HOST_SEED_AUTHORIZATION:" + authorization_digest,
"idempotency_key": "host-seed-authz-v9-1",
"expected_branch_oid": payload_commit,
"branch_mode": "CREATE_NEW",
"generation": 1,
"job_id": "job-" + "1" * 32,
}
binding_root = tmp_path / "bindings"
binding_root.mkdir()
binding_raw = json.dumps(binding, sort_keys=True).encode()
(binding_root / f"{task_id}.json").write_bytes(binding_raw)
(binding_root / f"{task_id}.json").chmod(0o600)
request = {
"schema": "aios.control-plane-host-seed-request.v1",
"operation": "HOST_SEED_IMMUTABLE_CANDIDATE",
"repository": "aios/aios",
"intent": intent,
"authorization": {
"task_id": task_id,
"binding_sha256": sha(binding_raw),
"expires_at": expires_text,
"intent_sha256": intent_digest,
"authorization_sha256": authorization_digest,
},
}
request_path = repo / "control-plane" / "host-seed" / "request.json"
request_path.parent.mkdir(parents=True, exist_ok=True)
request_path.write_bytes(canonical(request))
run("git", "add", str(request_path.relative_to(repo)), cwd=repo)
run("git", "commit", "-m", "host seed request", cwd=repo)
request_commit = run("git", "rev-parse", "HEAD", cwd=repo)
run("git", "push", str(bare), f"{request_commit}:{m.REQUEST_REF}", cwd=repo)
config = m.Config(
git_dir=bare,
task_binding_root=binding_root,
state_root=tmp_path / "state",
release_root=tmp_path / "releases",
host_identity="cn-network-mkt",
task_binding_owner=None,
)
return config, now, after, request
def test_execute_publishes_exact_release_and_is_idempotent(tmp_path: Path):
config, now, after, request = setup_case(tmp_path)
receipt = m.execute(config, now=now, require_root=False)
assert receipt["state"] == "EXECUTED"
assert receipt["service_mutation"] is False
assert receipt["service_restart"] is False
assert receipt["projector_started"] is False
assert receipt["task_creation"] is False
assert receipt["production_write"] is False
final = config.release_root / "control-plane-test-v9"
assert final.is_dir() and not final.is_symlink()
assert final.stat().st_mode & 0o777 == 0o755
assert sha((final / "a.txt").read_bytes()) == after["a.txt"]
assert sha((final / "bin.sh").read_bytes()) == after["bin.sh"]
assert sha((final / "c.txt").read_bytes()) == after["c.txt"]
assert (final / "bin.sh").stat().st_mode & 0o111
again = m.execute(config, now=now, require_root=False)
assert again == receipt
def test_execute_rejects_non_high_risk_authorization(tmp_path: Path):
config, now, _, _ = setup_case(tmp_path, risk="engineering")
with pytest.raises(m.HostSeedError, match="AUTHORIZATION_RISK_MISMATCH"):
m.execute(config, now=now, require_root=False)
assert not config.release_root.exists()
def test_execute_rejects_tampered_seed_bootstrap(tmp_path: Path):
config, now, _, _ = setup_case(tmp_path, tamper_bootstrap=True)
with pytest.raises(m.HostSeedError, match="SEED_BOOTSTRAP_HASH_MISMATCH"):
m.execute(config, now=now, require_root=False)
assert not config.release_root.exists()
def test_request_rejects_wrong_host(tmp_path: Path):
config, now, _, _ = setup_case(tmp_path)
config = m.Config(
git_dir=config.git_dir,
task_binding_root=config.task_binding_root,
state_root=config.state_root,
release_root=config.release_root,
host_identity="other-host",
task_binding_owner=None,
)
with pytest.raises(m.HostSeedError, match="TARGET_HOST_MISMATCH"):
m.execute(config, now=now, require_root=False)
def test_existing_release_drift_is_fail_closed(tmp_path: Path):
config, now, _, _ = setup_case(tmp_path)
first = m.execute(config, now=now, require_root=False)
assert first["state"] == "EXECUTED"
(config.release_root / "control-plane-test-v9" / "a.txt").write_text("drift\n")
# remove durable receipt so existing-release verification path is exercised.
for p in (config.state_root / "receipts").glob("*.json"):
p.unlink()
with pytest.raises(m.HostSeedError, match="EXISTING_RELEASE_HASH_MISMATCH"):
m.execute(config, now=now, require_root=False)
def test_no_request_ref_is_safe_noop(tmp_path: Path):
repo = tmp_path / "src"
bare = tmp_path / "repo.git"
repo.mkdir()
run("git", "init", "-b", "main", cwd=repo)
run("git", "config", "user.email", "test@example.invalid", cwd=repo)
run("git", "config", "user.name", "test", cwd=repo)
(repo / "a").write_text("x\n")
run("git", "add", "a", cwd=repo)
run("git", "commit", "-m", "base", cwd=repo)
run("git", "clone", "--bare", str(repo), str(bare), cwd=tmp_path)
cfg = m.Config(
git_dir=bare,
task_binding_root=tmp_path / "bindings",
state_root=tmp_path / "state",
release_root=tmp_path / "releases",
host_identity="cn-network-mkt",
task_binding_owner=None,
)
result = m.execute(cfg, now=datetime(2026, 8, 22, tzinfo=timezone.utc), require_root=False)
assert result["state"] == "NO_REQUEST"
assert result["service_mutation"] is False
assert result["task_creation"] is False
assert not cfg.release_root.exists()
def test_authorization_expiry_cannot_be_extended_without_new_binding(tmp_path: Path):
config, now, _, _ = setup_case(tmp_path)
# Rewrite only the request expiry and recompute the request commit, but keep
# the original governed-task binding description. The authorization digest
# therefore no longer matches the binding and must fail closed.
repo = tmp_path / "src"
req_path = repo / "control-plane" / "host-seed" / "request.json"
value = json.loads(req_path.read_text())
value["authorization"]["expires_at"] = (now + timedelta(minutes=45)).isoformat().replace("+00:00", "Z")
value["authorization"]["authorization_sha256"] = sha(canonical({
"task_id": value["authorization"]["task_id"],
"intent_sha256": value["authorization"]["intent_sha256"],
"expires_at": value["authorization"]["expires_at"],
}))
req_path.write_bytes(canonical(value))
run("git", "add", str(req_path.relative_to(repo)), cwd=repo)
run("git", "commit", "-m", "tamper expiry", cwd=repo)
new_commit = run("git", "rev-parse", "HEAD", cwd=repo)
run("git", "push", "--force", str(config.git_dir), f"{new_commit}:{m.REQUEST_REF}", cwd=repo)
with pytest.raises(m.HostSeedError, match="AUTHORIZATION_DESCRIPTION_MISMATCH"):
m.execute(config, now=now, require_root=False)
def test_systemd_contract_has_no_network_or_implicit_activation():
root = Path(__file__).parents[1]
service = (root / "deploy/systemd/aios-control-plane-host-seed-executor.service").read_text()
timer = (root / "deploy/systemd/aios-control-plane-host-seed-executor.timer").read_text()
assert "User=root" in service
assert "IPAddressDeny=any" in service
assert "RestrictAddressFamilies=AF_UNIX" in service
assert "ReadOnlyPaths=/data/aios-gitops/repo.git" in service
assert "ReadWritePaths=/opt/aios/releases" in service
assert "systemctl start" not in service
assert "projector" not in service.lower()
assert "OnUnitActiveSec=30s" in timer
def test_release_root_symlink_is_rejected(tmp_path: Path):
config, now, _, _ = setup_case(tmp_path)
real = tmp_path / "real-releases"
real.mkdir()
config.release_root.symlink_to(real, target_is_directory=True)
with pytest.raises(m.HostSeedError, match="RELEASE_ROOT_SYMLINK_REJECTED"):
m.execute(config, now=now, require_root=False)
def test_install_and_rollback_scripts_have_valid_bash_syntax():
root = Path(__file__).parents[1]
for relative in (
"deploy/install/install_control_plane_host_seed_executor.sh",
"deploy/install/rollback_control_plane_host_seed_executor.sh",
):
subprocess.run(
["/usr/bin/bash", "-n", str(root / relative)],
check=True,
text=True,
capture_output=True,
)
+3 -3
View File
@@ -250,7 +250,7 @@ class GovernedLocalWriterTests(unittest.TestCase):
}
self.assertEqual(service.process_request(secret_request, peer)["status"], "error")
def test_one_shot_consumption(self) -> None:
def test_idempotent_replay_returns_receipt(self) -> None:
with tempfile.TemporaryDirectory() as root:
directory = Path(root)
auth = self._load_auth(self._make_auth_payload(), directory)
@@ -283,8 +283,8 @@ class GovernedLocalWriterTests(unittest.TestCase):
self.assertEqual(service.process_request(create_request, peer)["status"], "ok")
self.assertEqual(service.process_request(append_request, peer)["status"], "ok")
self.assertEqual(service.process_request(append_request, peer)["status"], "error")
self.assertEqual(len(sink.records), 3)
self.assertEqual(service.process_request(append_request, peer)["status"], "receipt")
self.assertEqual(len(sink.records), 2)
self.assertEqual(len(handler.create_calls), 1)
self.assertEqual(len(handler.append_calls), 1)
+164
View File
@@ -0,0 +1,164 @@
from __future__ import annotations
import tempfile
from pathlib import Path
import pytest
import aios_gitops_job_executor as executor_module
from aios_authority_git_executor import AuthorityGitError, AuthorityGitExecutor
from aios_gitea_machine_transport import GiteaMachineTransport
from aios_gitea_machine_broker import CampaignCapabilityReceipt, GiteaMachineBroker, MachineCredentialProjection
from aios_gitops_job_controller import GitOpsJobController, JobError, JobStore, JobState
from aios_gitops_writer_worker import handle_request
OLD = "bba675fab3b7e0c5fba7066720252b3c91b9f20d"
NEW = "3fbbc18fb2131bbbd9e511004a59ee95cf3e86b7"
class FakeAuthority:
def __init__(self, descendant=True):
self.head = OLD
self.descendant = descendant
self.cas_calls = []
def read_ref(self, ref):
return self.head
def ancestry_check(self, older, newer):
return self.descendant and older == OLD and newer == NEW
def advance_task_branch_CAS(self, branch, expected, new):
if self.head != expected:
raise AuthorityGitError("AUTHORITY_BRANCH_EXPECTATION_MISMATCH")
self.cas_calls.append((branch, expected, new))
self.head = new
return new
class FakeBroker:
def __init__(self):
self.remote = None
self.push_calls = []
def verify_identity(self):
return {"login": "aios-autonomy-agent", "repository": "aios/aios"}
def readback(self):
return self.remote
def push_branch(self, new):
self.push_calls.append(new)
self.remote = new
return new
def create_pr(self, *args):
return {"number": 1}
def status(self, *args):
return {"status": "success"}
def merge(self, *args):
return {"merged": True}
def _request(**extra):
value = {
"campaign_id": executor_module.CAMPAIGN_ID,
"repository": executor_module.REPOSITORY,
"operation": "ADVANCE_EXISTING_CAMPAIGN_BRANCH",
"branch": executor_module.BRANCH,
"expected_branch_oid": OLD,
"payload_digest": "0" * 64,
"task_id": executor_module.TASK_ID,
"new_branch_oid": NEW,
"authorization_recovery_digest": executor_module.AUTHORIZATION_DIGEST,
}
value.update(extra)
return value
def _executor(temp, authority=None, broker=None):
controller = GitOpsJobController(JobStore(temp))
job = controller.submit(_request())
executor = executor_module.DurableJobExecutor(controller, authority or FakeAuthority(), broker or FakeBroker(), Path(temp) / "recovery.json")
return controller, job, executor
def _recovery():
return {
"digest": executor_module.AUTHORIZATION_DIGEST,
"campaign_id": executor_module.CAMPAIGN_ID,
"task_id": executor_module.TASK_ID,
"repository": executor_module.REPOSITORY,
"branch": executor_module.BRANCH,
"expected_branch_oid": OLD,
}
def test_advance_existing_branch_success_and_duplicate_execute(monkeypatch):
monkeypatch.setattr(executor_module, "load_recovered_authorization", lambda _path: _recovery())
with tempfile.TemporaryDirectory() as temp:
controller, job, executor = _executor(temp)
first = executor.execute(job.job_id)
second = executor.execute(job.job_id)
assert first["state"] == JobState.COMPLETED.value
assert second["state"] == JobState.COMPLETED.value
assert executor.authority.cas_calls == [(f"refs/heads/{executor_module.BRANCH}", OLD, NEW)]
assert executor.broker.push_calls == [NEW]
def test_authorization_digest_required(monkeypatch):
monkeypatch.setattr(executor_module, "load_recovered_authorization", lambda _path: _recovery())
with tempfile.TemporaryDirectory() as temp:
controller = GitOpsJobController(JobStore(temp))
job = controller.submit(_request(authorization_recovery_digest="bad"))
executor = executor_module.DurableJobExecutor(controller, FakeAuthority(), FakeBroker(), Path(temp) / "recovery.json")
with pytest.raises(executor_module.JobExecutionError, match="AUTHORIZATION_RECOVERY_DIGEST_MISMATCH"):
executor.dry_run(job.job_id)
def test_wrong_branch_rejected(monkeypatch):
monkeypatch.setattr(executor_module, "load_recovered_authorization", lambda _path: _recovery())
with tempfile.TemporaryDirectory() as temp:
controller = GitOpsJobController(JobStore(temp))
job = controller.submit(_request(branch="agent/not-the-campaign"))
executor = executor_module.DurableJobExecutor(controller, FakeAuthority(), FakeBroker(), Path(temp) / "recovery.json")
with pytest.raises(executor_module.JobExecutionError, match="BRANCH_MISMATCH"):
executor.dry_run(job.job_id)
def test_non_descendant_new_oid_rejected(monkeypatch):
monkeypatch.setattr(executor_module, "load_recovered_authorization", lambda _path: _recovery())
with tempfile.TemporaryDirectory() as temp:
authority = FakeAuthority(descendant=False)
controller, job, executor = _executor(temp, authority=authority)
with pytest.raises(executor_module.JobExecutionError, match="NEW_OID_NOT_DESCENDANT"):
executor.dry_run(job.job_id)
def test_worker_execute_action_consumes_job():
with tempfile.TemporaryDirectory() as temp:
controller = GitOpsJobController(JobStore(temp))
job = controller.submit(_request())
class Stub:
def execute(self, job_id, *, dry_run=False):
assert job_id == job.job_id
assert dry_run is True
return {"decision": "SAFE_TO_ADVANCE"}
result = handle_request(controller, {"action": "execute", "job_id": job.job_id, "dry_run": True}, Stub())
assert result["job"]["decision"] == "SAFE_TO_ADVANCE"
def test_machine_transport_branch_cas_is_not_force_push(monkeypatch):
transport = GiteaMachineTransport("http://127.0.0.1:3000/api/v1", "aios/aios", "https://g.cnnetmkt.top/git/aios/aios.git", Path("/tmp/repo.git"))
calls = []
def read_branch(_self, _branch):
return NEW if calls else OLD
monkeypatch.setattr(GiteaMachineTransport, "read_branch", read_branch)
monkeypatch.setattr(GiteaMachineTransport, "_push_branch_fast_forward", lambda _self, branch, new: calls.append((branch, new)))
assert transport.compare_and_swap_branch(executor_module.BRANCH, OLD, NEW) == NEW
assert calls == [(executor_module.BRANCH, NEW)]
+35
View File
@@ -0,0 +1,35 @@
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
def test_model_api_source_is_immutable_and_authority_bound():
source = (ROOT / 'active-runtime' / 'app' / 'aios_model_api_staging.py').read_text(encoding='utf-8')
assert 'BaselineAuthority' in source
assert 'BASELINE_AUTHORITY = BaselineAuthority()' in source
assert 'LEGACY_GIT_WRITE_COMMANDS' in source
assert 'BaselineAuthorityError' in source
assert 'IMMUTABLE_STAGING' in source
assert 'LEGACY_GLOBAL_WRITES_DISABLED' in source
assert "'production_deployed': False" in source
assert "'governed_release_routing': 'bridge-v2'" in source
def test_model_api_staging_unit_is_non_production_and_read_only():
unit = (ROOT / 'deploy' / 'systemd' / 'aios-gitops-model-api-v2r2c1-staging.service').read_text(encoding='utf-8')
assert 'aios-gitops-model-api-v2r2c1-staging.service' in unit
assert 'AIOS_GITOPS_WRITES_ENABLED=false' in unit
assert 'AIOS_GITOPS_DURABLE_JOB_EXECUTION_ENABLED=false' in unit
assert 'AIOS_GITOPS_PORT=18893' in unit
assert 'aios_model_api_staging.py' in unit
assert 'production' not in unit.lower().replace('model-api-v2r2c1-staging', '')
def test_model_api_builder_manifest_contract():
builder = (ROOT / 'scripts' / 'build_v2r2c1_model_api_staging.py').read_text(encoding='utf-8')
assert 'aios.v2r2c1.model-api-staging-manifest.v1' in builder
assert "'target_version': 'V2R2C1'" in builder
assert "'repository_identity': 'aios/aios'" in builder
assert "'deployment_state': 'STAGING'" in builder
assert "'legacy_global_writes_enabled': False" in builder
assert 'private marker rejected' in builder