Compare commits

..
97 changed files with 4779 additions and 11525 deletions
File diff suppressed because it is too large Load Diff
@@ -1,811 +0,0 @@
"""Canonical Task projection intent and same-identity recovery primitives.
This module is intentionally Task-Plane-owned. It does not open a network
listener and it does not authenticate callers; existing control-plane services
must enforce their normal service-side ``agent-task:control`` boundary before
calling it.
The recovery primitive has one purpose: converge a durable, complete
ProjectionIntent to exactly one canonical Task row. It never fabricates task
semantics from a Bridge binding and it never generates a replacement task id.
"""
from __future__ import annotations
from dataclasses import asdict, dataclass
from datetime import datetime, timezone
from enum import Enum
import hashlib
import json
from pathlib import Path
import re
import sqlite3
from typing import Any, Mapping, Sequence
TASK_ID_RE = re.compile(r"^[A-Za-z0-9][A-Za-z0-9._:-]{2,179}$")
JOB_ID_RE = TASK_ID_RE
SHA40_RE = re.compile(r"^[0-9a-f]{40}$")
SHA64_RE = re.compile(r"^[0-9a-f]{64}$")
BRANCH_RE = re.compile(r"^agent/[A-Za-z0-9._/-]{1,220}$")
SCHEMA = "aios.canonical-task-projection-intent.v1"
RECEIPT_SCHEMA = "aios.canonical-task-projection-receipt.v1"
class ProjectionRecoveryError(RuntimeError):
def __init__(self, code: str, status: int = 409) -> None:
super().__init__(code)
self.code = code
self.status = status
class ProjectionState(str, Enum):
PENDING = "PENDING"
CONVERGED = "CONVERGED"
DRIFT = "DRIFT"
FAILED = "FAILED"
BLOCKED = "BLOCKED"
UNKNOWN = "UNKNOWN"
BACKFILL_REQUIRED = "BACKFILL_REQUIRED"
def _now() -> str:
return datetime.now(timezone.utc).isoformat().replace("+00:00", "Z")
def _canonical(value: object) -> bytes:
return json.dumps(
value,
ensure_ascii=False,
sort_keys=True,
separators=(",", ":"),
).encode("utf-8")
def sha256_json(value: object) -> str:
return hashlib.sha256(_canonical(value)).hexdigest()
def _string(value: object, code: str, pattern: re.Pattern[str] | None = None) -> str:
if not isinstance(value, str) or not value.strip():
raise ProjectionRecoveryError(code, 400)
result = value.strip()
if pattern is not None and pattern.fullmatch(result) is None:
raise ProjectionRecoveryError(code, 400)
return result
def _integer(value: object, code: str, *, minimum: int = 0) -> int:
if isinstance(value, bool) or not isinstance(value, int) or value < minimum:
raise ProjectionRecoveryError(code, 400)
return value
def _plain_mapping(value: object, code: str) -> dict[str, Any]:
if not isinstance(value, Mapping):
raise ProjectionRecoveryError(code, 400)
return {str(k): v for k, v in value.items()}
def _plain_list(value: object, code: str) -> list[Any]:
if not isinstance(value, list):
raise ProjectionRecoveryError(code, 400)
return list(value)
@dataclass(frozen=True, slots=True)
class CanonicalTaskPayload:
task_id: str
goal: str
instructions: str
success_criteria: tuple[Mapping[str, Any], ...]
constraints: Mapping[str, Any]
required_capabilities: tuple[str, ...]
metadata: Mapping[str, Any]
priority: int = 100
max_attempts: int = 20
deadline: str | None = None
@classmethod
def from_mapping(cls, value: Mapping[str, Any]) -> "CanonicalTaskPayload":
allowed = {
"task_id",
"goal",
"instructions",
"success_criteria",
"constraints",
"required_capabilities",
"metadata",
"priority",
"max_attempts",
"deadline",
}
if set(value) - allowed:
raise ProjectionRecoveryError("TASK_PAYLOAD_UNKNOWN_FIELDS", 400)
task_id = _string(value.get("task_id"), "TASK_ID_INVALID", TASK_ID_RE)
goal = _string(value.get("goal"), "TASK_GOAL_INVALID")
instructions = _string(value.get("instructions"), "TASK_INSTRUCTIONS_INVALID")
if len(goal) > 8192 or len(instructions) > 65536:
raise ProjectionRecoveryError("TASK_TEXT_TOO_LARGE", 413)
criteria_raw = _plain_list(value.get("success_criteria"), "TASK_SUCCESS_CRITERIA_INVALID")
criteria: list[Mapping[str, Any]] = []
for item in criteria_raw:
if not isinstance(item, Mapping):
raise ProjectionRecoveryError("TASK_SUCCESS_CRITERIA_INVALID", 400)
criteria.append(dict(item))
if not criteria:
raise ProjectionRecoveryError("TASK_SUCCESS_CRITERIA_REQUIRED", 400)
constraints = _plain_mapping(value.get("constraints"), "TASK_CONSTRAINTS_INVALID")
capabilities_raw = _plain_list(
value.get("required_capabilities", []),
"TASK_CAPABILITIES_INVALID",
)
capabilities: list[str] = []
for item in capabilities_raw:
capabilities.append(_string(item, "TASK_CAPABILITY_INVALID"))
# Recovery recreates the pristine canonical row. Initial execution
# projection remains the sole owner of runtime capabilities.
if capabilities:
raise ProjectionRecoveryError(
"TASK_RECOVERY_REQUIRES_PRISTINE_CAPABILITIES",
409,
)
metadata = _plain_mapping(value.get("metadata", {}), "TASK_METADATA_INVALID")
if "execution" in metadata or "execution_projection" in metadata:
raise ProjectionRecoveryError(
"TASK_RECOVERY_REQUIRES_PRISTINE_METADATA",
409,
)
priority = _integer(value.get("priority", 100), "TASK_PRIORITY_INVALID", minimum=0)
max_attempts = _integer(
value.get("max_attempts", 20),
"TASK_MAX_ATTEMPTS_INVALID",
minimum=1,
)
deadline = value.get("deadline")
if deadline is not None:
deadline = _string(deadline, "TASK_DEADLINE_INVALID")
return cls(
task_id=task_id,
goal=goal,
instructions=instructions,
success_criteria=tuple(criteria),
constraints=constraints,
required_capabilities=tuple(capabilities),
metadata=metadata,
priority=priority,
max_attempts=max_attempts,
deadline=deadline,
)
def public(self) -> dict[str, Any]:
return {
"task_id": self.task_id,
"goal": self.goal,
"instructions": self.instructions,
"success_criteria": [dict(x) for x in self.success_criteria],
"constraints": dict(self.constraints),
"required_capabilities": list(self.required_capabilities),
"metadata": dict(self.metadata),
"priority": self.priority,
"max_attempts": self.max_attempts,
"deadline": self.deadline,
}
@property
def payload_sha256(self) -> str:
return sha256_json(self.public())
@dataclass(frozen=True, slots=True)
class GovernedProjectionBinding:
governed_task_id: str
bridge_job_id: str
repository: str
branch: str
generation: int
base_oid: str
expected_branch_oid: str
@classmethod
def from_mapping(cls, value: Mapping[str, Any]) -> "GovernedProjectionBinding":
allowed = {
"governed_task_id",
"bridge_job_id",
"repository",
"branch",
"generation",
"base_oid",
"expected_branch_oid",
}
if set(value) != allowed:
raise ProjectionRecoveryError("GOVERNED_BINDING_FIELDS_INVALID", 400)
repository = _string(value["repository"], "REPOSITORY_INVALID")
if repository != "aios/aios":
raise ProjectionRecoveryError("REPOSITORY_MISMATCH", 409)
return cls(
governed_task_id=_string(
value["governed_task_id"],
"GOVERNED_TASK_ID_INVALID",
TASK_ID_RE,
),
bridge_job_id=_string(
value["bridge_job_id"],
"BRIDGE_JOB_ID_INVALID",
JOB_ID_RE,
),
repository=repository,
branch=_string(value["branch"], "BRANCH_INVALID", BRANCH_RE),
generation=_integer(value["generation"], "GENERATION_INVALID", minimum=1),
base_oid=_string(value["base_oid"], "BASE_OID_INVALID", SHA40_RE),
expected_branch_oid=_string(
value["expected_branch_oid"],
"EXPECTED_OID_INVALID",
SHA40_RE,
),
)
def public(self) -> dict[str, Any]:
return asdict(self)
@dataclass(frozen=True, slots=True)
class ProjectionSourceArtifact:
ref: str
sha256: str
@classmethod
def from_mapping(cls, value: Mapping[str, Any]) -> "ProjectionSourceArtifact":
if set(value) != {"ref", "sha256"}:
raise ProjectionRecoveryError("SOURCE_ARTIFACT_FIELDS_INVALID", 400)
return cls(
ref=_string(value["ref"], "SOURCE_ARTIFACT_REF_INVALID"),
sha256=_string(value["sha256"], "SOURCE_ARTIFACT_SHA_INVALID", SHA64_RE),
)
@dataclass(frozen=True, slots=True)
class CanonicalTaskProjectionIntent:
projection_id: str
projection_generation: int
idempotency_key: str
task: CanonicalTaskPayload
binding: GovernedProjectionBinding
source_artifact: ProjectionSourceArtifact
schema: str = SCHEMA
@classmethod
def from_mapping(cls, value: Mapping[str, Any]) -> "CanonicalTaskProjectionIntent":
allowed = {
"schema",
"projection_id",
"projection_generation",
"idempotency_key",
"task",
"governed_binding",
"source_artifact",
"task_payload_sha256",
}
if set(value) != allowed:
raise ProjectionRecoveryError("PROJECTION_INTENT_FIELDS_INVALID", 400)
if value["schema"] != SCHEMA:
raise ProjectionRecoveryError("PROJECTION_INTENT_SCHEMA_INVALID", 400)
task = CanonicalTaskPayload.from_mapping(
_plain_mapping(value["task"], "TASK_PAYLOAD_INVALID")
)
binding = GovernedProjectionBinding.from_mapping(
_plain_mapping(value["governed_binding"], "GOVERNED_BINDING_INVALID")
)
source = ProjectionSourceArtifact.from_mapping(
_plain_mapping(value["source_artifact"], "SOURCE_ARTIFACT_INVALID")
)
if binding.governed_task_id != task.task_id:
raise ProjectionRecoveryError("PROJECTION_TASK_IDENTITY_MISMATCH", 409)
expected_hash = _string(
value["task_payload_sha256"],
"TASK_PAYLOAD_SHA_INVALID",
SHA64_RE,
)
if task.payload_sha256 != expected_hash:
raise ProjectionRecoveryError("TASK_PAYLOAD_SHA_MISMATCH", 409)
projection_id = _string(
value["projection_id"],
"PROJECTION_ID_INVALID",
TASK_ID_RE,
)
idempotency_key = _string(
value["idempotency_key"],
"IDEMPOTENCY_KEY_INVALID",
TASK_ID_RE,
)
generation = _integer(
value["projection_generation"],
"PROJECTION_GENERATION_INVALID",
minimum=1,
)
return cls(
projection_id=projection_id,
projection_generation=generation,
idempotency_key=idempotency_key,
task=task,
binding=binding,
source_artifact=source,
)
def public(self) -> dict[str, Any]:
return {
"schema": self.schema,
"projection_id": self.projection_id,
"projection_generation": self.projection_generation,
"idempotency_key": self.idempotency_key,
"task": self.task.public(),
"governed_binding": self.binding.public(),
"source_artifact": asdict(self.source_artifact),
"task_payload_sha256": self.task.payload_sha256,
}
@property
def request_sha256(self) -> str:
return sha256_json(self.public())
class CanonicalTaskProjectionStore:
"""Task-Plane-owned intent/recovery store.
The caller must already be inside the Task Plane authorization boundary.
"""
def __init__(self, db_path: Path | str) -> None:
self.db_path = Path(db_path)
def _connect(self) -> sqlite3.Connection:
connection = sqlite3.connect(self.db_path, timeout=30.0)
connection.row_factory = sqlite3.Row
connection.execute("PRAGMA foreign_keys=ON")
connection.execute("PRAGMA busy_timeout=30000")
return connection
@staticmethod
def _columns(connection: sqlite3.Connection, table: str) -> dict[str, sqlite3.Row]:
return {
str(row["name"]): row
for row in connection.execute(f'PRAGMA table_info("{table}")')
}
@staticmethod
def _table_names(connection: sqlite3.Connection) -> set[str]:
return {
str(row[0])
for row in connection.execute(
"SELECT name FROM sqlite_master WHERE type='table'"
)
}
@classmethod
def _event_table(
cls,
connection: sqlite3.Connection,
) -> tuple[str, dict[str, sqlite3.Row]]:
tables = cls._table_names(connection)
for name in ("task_events", "events"):
if name in tables:
columns = cls._columns(connection, name)
if "task_id" in columns and "event_type" in columns:
return name, columns
raise ProjectionRecoveryError("TASK_EVENT_TABLE_UNSUPPORTED", 503)
@staticmethod
def _ensure_control_tables(connection: sqlite3.Connection) -> None:
connection.execute(
"""
CREATE TABLE IF NOT EXISTS task_projection_intents (
projection_id TEXT PRIMARY KEY,
task_id TEXT NOT NULL,
bridge_job_id TEXT NOT NULL,
projection_generation INTEGER NOT NULL,
idempotency_key TEXT NOT NULL,
request_sha256 TEXT NOT NULL,
task_payload_sha256 TEXT NOT NULL,
intent_json TEXT NOT NULL,
state TEXT NOT NULL,
last_error TEXT,
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL,
UNIQUE(task_id, projection_generation),
UNIQUE(task_id, idempotency_key)
)
"""
)
connection.execute(
"""
CREATE TABLE IF NOT EXISTS task_projection_receipts (
projection_id TEXT PRIMARY KEY,
task_id TEXT NOT NULL,
bridge_job_id TEXT NOT NULL,
projection_generation INTEGER NOT NULL,
task_payload_sha256 TEXT NOT NULL,
canonical_revision INTEGER NOT NULL,
state TEXT NOT NULL,
created_new_row INTEGER NOT NULL,
observed_at TEXT NOT NULL,
receipt_json TEXT NOT NULL
)
"""
)
def persist_intent(
self,
intent: CanonicalTaskProjectionIntent,
) -> dict[str, Any]:
now = _now()
request_sha = intent.request_sha256
intent_text = _canonical(intent.public()).decode("utf-8")
with self._connect() as connection:
connection.execute("BEGIN IMMEDIATE")
self._ensure_control_tables(connection)
existing = connection.execute(
"""
SELECT * FROM task_projection_intents
WHERE task_id=? AND projection_generation=?
""",
(intent.task.task_id, intent.projection_generation),
).fetchone()
if existing is not None:
if (
str(existing["projection_id"]) != intent.projection_id
or str(existing["idempotency_key"]) != intent.idempotency_key
or str(existing["request_sha256"]) != request_sha
):
raise ProjectionRecoveryError(
"PROJECTION_INTENT_IDEMPOTENCY_CONFLICT",
409,
)
connection.commit()
return {
"projection_id": intent.projection_id,
"task_id": intent.task.task_id,
"state": str(existing["state"]),
"idempotent": True,
}
connection.execute(
"""
INSERT INTO task_projection_intents(
projection_id,task_id,bridge_job_id,projection_generation,
idempotency_key,request_sha256,task_payload_sha256,intent_json,
state,last_error,created_at,updated_at
) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)
""",
(
intent.projection_id,
intent.task.task_id,
intent.binding.bridge_job_id,
intent.projection_generation,
intent.idempotency_key,
request_sha,
intent.task.payload_sha256,
intent_text,
ProjectionState.PENDING.value,
None,
now,
now,
),
)
connection.commit()
return {
"projection_id": intent.projection_id,
"task_id": intent.task.task_id,
"state": ProjectionState.PENDING.value,
"idempotent": False,
}
def _task_row_payload(
self,
row: sqlite3.Row,
) -> dict[str, Any]:
try:
success = json.loads(row["success_criteria_json"])
constraints = json.loads(row["constraints_json"])
capabilities = json.loads(row["required_capabilities_json"])
metadata = json.loads(row["metadata_json"])
except (TypeError, json.JSONDecodeError) as exc:
raise ProjectionRecoveryError("CANONICAL_TASK_JSON_INVALID", 409) from exc
if (
not isinstance(success, list)
or not isinstance(constraints, Mapping)
or not isinstance(capabilities, list)
or not isinstance(metadata, Mapping)
):
raise ProjectionRecoveryError("CANONICAL_TASK_SHAPE_INVALID", 409)
return {
"task_id": str(row["task_id"]),
"goal": str(row["goal"]),
"instructions": str(row["instructions"]),
"success_criteria": success,
"constraints": dict(constraints),
"required_capabilities": capabilities,
"metadata": dict(metadata),
"priority": int(row["priority"]),
"max_attempts": int(row["max_attempts"]),
"deadline": row["deadline"],
}
def _verify_existing_task(
self,
row: sqlite3.Row,
intent: CanonicalTaskProjectionIntent,
) -> int:
observed = self._task_row_payload(row)
observed_hash = sha256_json(observed)
if observed_hash != intent.task.payload_sha256:
raise ProjectionRecoveryError(
"CANONICAL_TASK_PROJECTION_IDENTITY_CONFLICT",
409,
)
return int(row["revision"])
@staticmethod
def _required_task_columns() -> set[str]:
return {
"task_id",
"goal",
"instructions",
"success_criteria_json",
"required_capabilities_json",
"constraints_json",
"metadata_json",
"priority",
"max_attempts",
"attempt",
"status",
"claimed_by",
"lease_token_hash",
"lease_expires_at",
"result_json",
"verification_json",
"revision",
"created_at",
"updated_at",
}
def _insert_task(
self,
connection: sqlite3.Connection,
intent: CanonicalTaskProjectionIntent,
) -> int:
columns = self._columns(connection, "tasks")
if not self._required_task_columns().issubset(columns):
raise ProjectionRecoveryError("TASK_SCHEMA_RECOVERY_UNSUPPORTED", 503)
now = _now()
values: dict[str, Any] = {
"task_id": intent.task.task_id,
"goal": intent.task.goal,
"instructions": intent.task.instructions,
"success_criteria_json": _canonical(
[dict(x) for x in intent.task.success_criteria]
).decode("utf-8"),
"required_capabilities_json": "[]",
"constraints_json": _canonical(dict(intent.task.constraints)).decode("utf-8"),
"metadata_json": _canonical(dict(intent.task.metadata)).decode("utf-8"),
"deadline": intent.task.deadline,
"priority": intent.task.priority,
"max_attempts": intent.task.max_attempts,
"attempt": 0,
"status": "QUEUED",
"claimed_by": None,
"lease_token_hash": None,
"lease_expires_at": None,
"result_json": None,
"verification_json": None,
"revision": 1,
"created_at": now,
"updated_at": now,
}
for optional in (
"result_digest",
"result_idempotency_key",
):
if optional in columns:
values[optional] = None
names = [name for name in values if name in columns]
placeholders = ",".join("?" for _ in names)
connection.execute(
f'INSERT INTO tasks ({",".join(chr(34)+n+chr(34) for n in names)}) '
f"VALUES ({placeholders})",
tuple(values[name] for name in names),
)
self._insert_task_created_event(connection, intent)
return 1
def _insert_task_created_event(
self,
connection: sqlite3.Connection,
intent: CanonicalTaskProjectionIntent,
) -> None:
table, columns = self._event_table(connection)
event = {
"schema": "aios.task-projection-recovery.event.v1",
"task_id": intent.task.task_id,
"bridge_job_id": intent.binding.bridge_job_id,
"projection_id": intent.projection_id,
"projection_generation": intent.projection_generation,
"task_payload_sha256": intent.task.payload_sha256,
"source_artifact_ref": intent.source_artifact.ref,
"source_artifact_sha256": intent.source_artifact.sha256,
"recovery": True,
}
values: dict[str, Any] = {
"task_id": intent.task.task_id,
"event_type": "TASK_CREATED",
}
payload_text = _canonical(event).decode("utf-8")
if "payload_json" in columns:
values["payload_json"] = payload_text
elif "payload" in columns:
values["payload"] = payload_text
else:
raise ProjectionRecoveryError("TASK_EVENT_PAYLOAD_UNSUPPORTED", 503)
if "created_at" in columns:
values["created_at"] = _now()
if "agent_id" in columns:
values["agent_id"] = "control-plane-projection-recovery"
if "actor_id" in columns:
values["actor_id"] = "control-plane-projection-recovery"
names = list(values)
connection.execute(
f'INSERT INTO "{table}" ({",".join(chr(34)+n+chr(34) for n in names)}) '
f'VALUES ({",".join("?" for _ in names)})',
tuple(values[name] for name in names),
)
def reconcile(
self,
intent: CanonicalTaskProjectionIntent,
) -> dict[str, Any]:
# Persist first so a crash before Task creation leaves a durable PENDING
# intent for startup/supervisor reconciliation.
self.persist_intent(intent)
with self._connect() as connection:
connection.execute("BEGIN IMMEDIATE")
self._ensure_control_tables(connection)
existing_receipt = connection.execute(
"SELECT * FROM task_projection_receipts WHERE projection_id=?",
(intent.projection_id,),
).fetchone()
if existing_receipt is not None:
if (
str(existing_receipt["task_id"]) != intent.task.task_id
or str(existing_receipt["bridge_job_id"]) != intent.binding.bridge_job_id
or str(existing_receipt["task_payload_sha256"])
!= intent.task.payload_sha256
):
raise ProjectionRecoveryError(
"PROJECTION_RECEIPT_IDENTITY_CONFLICT",
409,
)
connection.commit()
receipt = json.loads(existing_receipt["receipt_json"])
receipt["idempotent"] = True
return receipt
row = connection.execute(
"SELECT * FROM tasks WHERE task_id=?",
(intent.task.task_id,),
).fetchone()
created_new = False
if row is None:
revision = self._insert_task(connection, intent)
created_new = True
row = connection.execute(
"SELECT * FROM tasks WHERE task_id=?",
(intent.task.task_id,),
).fetchone()
if row is None:
raise ProjectionRecoveryError(
"TASK_RECOVERY_OBSERVATION_FAILED",
503,
)
else:
revision = self._verify_existing_task(row, intent)
# Observation after mutation is mandatory.
revision = self._verify_existing_task(row, intent)
observed_at = _now()
receipt = {
"schema": RECEIPT_SCHEMA,
"projection_id": intent.projection_id,
"governed_task_id": intent.binding.governed_task_id,
"canonical_task_id": intent.task.task_id,
"bridge_job_id": intent.binding.bridge_job_id,
"projection_generation": intent.projection_generation,
"task_payload_sha256": intent.task.payload_sha256,
"canonical_revision": revision,
"state": ProjectionState.CONVERGED.value,
"created_new_row": created_new,
"source_artifact": asdict(intent.source_artifact),
"observed_at": observed_at,
"idempotent": False,
}
connection.execute(
"""
INSERT INTO task_projection_receipts(
projection_id,task_id,bridge_job_id,projection_generation,
task_payload_sha256,canonical_revision,state,created_new_row,
observed_at,receipt_json
) VALUES (?,?,?,?,?,?,?,?,?,?)
""",
(
intent.projection_id,
intent.task.task_id,
intent.binding.bridge_job_id,
intent.projection_generation,
intent.task.payload_sha256,
revision,
ProjectionState.CONVERGED.value,
1 if created_new else 0,
observed_at,
_canonical(receipt).decode("utf-8"),
),
)
connection.execute(
"""
UPDATE task_projection_intents
SET state=?, last_error=NULL, updated_at=?
WHERE projection_id=?
""",
(
ProjectionState.CONVERGED.value,
observed_at,
intent.projection_id,
),
)
connection.commit()
return receipt
def get_projection(
self,
task_id: str,
) -> dict[str, Any] | None:
task_id = _string(task_id, "TASK_ID_INVALID", TASK_ID_RE)
with self._connect() as connection:
self._ensure_control_tables(connection)
intent = connection.execute(
"""
SELECT * FROM task_projection_intents
WHERE task_id=?
ORDER BY projection_generation DESC
LIMIT 1
""",
(task_id,),
).fetchone()
if intent is None:
return None
receipt = connection.execute(
"SELECT receipt_json FROM task_projection_receipts WHERE projection_id=?",
(intent["projection_id"],),
).fetchone()
return {
"projection_id": intent["projection_id"],
"task_id": task_id,
"bridge_job_id": intent["bridge_job_id"],
"projection_generation": int(intent["projection_generation"]),
"state": intent["state"],
"last_error": intent["last_error"],
"task_payload_sha256": intent["task_payload_sha256"],
"receipt": json.loads(receipt[0]) if receipt else None,
}
@@ -1,44 +0,0 @@
"""HTTP integration helpers for canonical Task projection recovery.
This module intentionally contains no HTTP server. Existing Task Plane /
projection-owner handlers should call ``recover_from_json`` only after their
normal service-side ``agent-task:control`` authorization succeeds.
"""
from __future__ import annotations
from pathlib import Path
from typing import Any, Mapping
from aios_agent_task_projection_recovery import (
CanonicalTaskProjectionIntent,
CanonicalTaskProjectionStore,
ProjectionRecoveryError,
)
def recover_from_json(
db_path: Path | str,
task_id: str,
body: Mapping[str, Any],
) -> tuple[int, dict[str, Any]]:
try:
intent = CanonicalTaskProjectionIntent.from_mapping(body)
if intent.task.task_id != task_id:
raise ProjectionRecoveryError("PROJECTION_TASK_IDENTITY_MISMATCH", 409)
result = CanonicalTaskProjectionStore(db_path).reconcile(intent)
return (200 if result.get("idempotent") else 201), result
except ProjectionRecoveryError as exc:
return exc.status, {"error": exc.code}
def get_projection(
db_path: Path | str,
task_id: str,
) -> tuple[int, dict[str, Any]]:
try:
result = CanonicalTaskProjectionStore(db_path).get_projection(task_id)
except ProjectionRecoveryError as exc:
return exc.status, {"error": exc.code}
if result is None:
return 404, {"error": "TASK_PROJECTION_NOT_FOUND"}
return 200, result
@@ -94,25 +94,6 @@ class AuthorityGitExecutor:
self._git(["worktree", "add", "--detach", str(path), base_oid])
return path
def create_task_branch(self, branch: str, base_oid: str) -> str:
"""Create an isolated agent branch once, using an empty-ref CAS."""
ref = branch if branch.startswith("refs/heads/") else "refs/heads/" + branch
if not TASK_BRANCH_RE.fullmatch(ref):
raise AuthorityGitError("TASK_BRANCH_SCOPE_DENIED")
_oid(base_oid, "base_oid")
if not self.commit_exists(base_oid):
raise AuthorityGitError("BASE_COMMIT_NOT_FOUND")
current = self.read_ref(ref)
if current is not None:
if current == base_oid:
return current
raise AuthorityGitError("AUTHORITY_BRANCH_EXPECTATION_MISMATCH")
self._git(["update-ref", ref, base_oid, "0" * 40])
readback = self.read_ref(ref)
if readback != base_oid:
raise AuthorityGitError("AUTHORITY_CAS_READBACK_MISMATCH")
return readback
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")
+6 -23
View File
@@ -22,7 +22,6 @@ from gitea_tag_publication import (
TagPublisher,
redact,
validate_version,
validate_gitea_api_url,
)
from orchestrator.action_monitor import ActionMonitor
from silent_release_hardening import (
@@ -309,7 +308,8 @@ def resolve_commit_oid(value: str | None, field: str = 'target_oid') -> str:
raise ValueError(f'{field} does not resolve to the exact approved commit') from exc
def existing_tag_target(tag: str) -> str | None:
validate_version(tag)
if not re.fullmatch(r'V\d+R\d+C\d+(?:-[A-Za-z0-9_.-]+)?$', str(tag or '')):
raise ValueError('invalid release version')
try:
result = run_authoritative_git(
['rev-parse', '--verify', f'refs/tags/{tag}^{{commit}}'],
@@ -344,7 +344,7 @@ def required_authoritative_git_dir() -> Path:
def publication_publisher() -> TagPublisher:
authoritative_repo = required_authoritative_git_dir()
publisher = TagPublisher(
return TagPublisher(
WS,
GitTagBackend(WS, authoritative_repo=authoritative_repo),
GiteaTagClient(
@@ -355,10 +355,6 @@ def publication_publisher() -> TagPublisher:
),
os.getenv('AIOS_GITOPS_GITEA_REMOTE','gitea'),
)
publisher.baseline_path = Path(os.getenv(
'AIOS_GITOPS_BASELINE_AUTHORITY', str(WS / 'baseline' / 'current.json')
))
return publisher
def commit(paths, msg):
ensure_git(); paths = [relpath(p) for p in paths] if paths else ['.']; sh(['add'] + paths, True)
@@ -514,15 +510,7 @@ class H(BaseHTTPRequestHandler):
return self.sendj({'error':redact(exc)},413)
except (AuthoritativeGitError, RuntimeError) as exc:
return self.sendj({'error':redact(exc)},503)
payload = {'from': frm, 'to': to, 'diff': diff}
payload.update({
key: diff[key]
for key in (
'from_oid', 'to_oid', 'changed_paths', 'stats',
'total_size_bytes', 'truncated', 'patch_limit_bytes',
)
})
return self.sendj(payload)
return self.sendj({'from':frm,'to':to,'diff':diff})
if path in ('/api/patch','/api/patches','/api/branches','/patch'):
snapshot = repository_snapshot(
git_dir=AUTHORITATIVE_GIT_DIR,
@@ -634,8 +622,7 @@ class H(BaseHTTPRequestHandler):
return self.sendj(
service.get_capability_status(candidate_id)
)
result = service.get_release_saga(candidate_id)
return self.sendj(result, 404 if result.get('status') == 'NOT_FOUND' else 200)
return self.sendj(service.get_release_saga(candidate_id))
if path == '/getRecoveryStatus':
return self.sendj(
silent_release_service().get_recovery_status()
@@ -786,10 +773,7 @@ class H(BaseHTTPRequestHandler):
a=self.need('tag:create');
if not a: return
ver=data.get('version') or data.get('tag')
try:
validate_version(ver)
except ValueError:
return self.sendj({'error':'version must match VxRxCx'},400)
if not re.match(r'^V\d+R\d+C\d+(-[A-Za-z0-9_.-]+)?$', ver or ''): return self.sendj({'error':'version must match VxRxCx'},400)
if not data.get('target_oid') or not data.get('evidence_closure_oid') or not data.get('approval_plan_id'):
return self.sendj({
'error':'production_tag_evidence_required',
@@ -963,7 +947,6 @@ def api_spec():
return {'version':'V1R1C0-chatgpt-actions','openapi':'/api/openapi.yaml','read':['GET /api/base','GET /api/ver','GET /api/diff','GET /api/patch','GET /api/model','GET /api/run','GET /getReleaseCandidate','GET /getApprovalEnvelope','GET /getReleaseSaga','GET /admin/ai-evolution'],'write':['POST /api/branch','POST /api/file','POST /api/commit','POST /api/patch','POST /api/tag','POST /api/rollback','POST /api/model','PUT /api/model-priority','POST /api/nl','POST /createReleaseCandidate','POST /submitOwnerApproval','POST /startDeployment'],'policy':{'bearer_required_for_write':True,'direct_main_push':False,'delete_tag':False,'owner_approval_required_for_v_level':True,'api_keys_in_git':False,'git_secret_field':'secret_ref','online_git_sync':True,'silent_release_execution_enabled':SILENT_RELEASE_EXECUTION_ENABLED,'silent_release_shadow_default':True}}
def main():
validate_gitea_api_url(os.getenv('AIOS_GITOPS_GITEA_API_URL', ''))
print(f'AIOS ChatGPT GitOps adapter listening on {HOST}:{PORT}')
ThreadingHTTPServer((HOST,PORT), H).serve_forever()
if __name__ == '__main__': main()
@@ -12,7 +12,6 @@ BRIDGE_OPERATIONS = (
"getRuntimeStatus",
"getGovernedWriteCapabilities",
"createGovernedTask",
"projectOnlineAgentExecution",
"acquireWriterLease",
"commitFileGoverned",
"commitFilesGoverned",
@@ -31,7 +30,6 @@ BRIDGE_OPERATIONS = (
OPERATION_REQUIRED_FIELDS = {
"createGovernedTask": ("task_id", "repository", "base_oid", "target_branch", "risk", "description", "idempotency_key"),
"projectOnlineAgentExecution": ("task_id", "required_capabilities"),
"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"),
@@ -48,78 +46,6 @@ OPERATION_REQUIRED_FIELDS = {
"getControlPlaneSnapshot": (),
}
_OPERATION_PATHS = {
"createGovernedTask": "/v2/governed-task",
"projectOnlineAgentExecution": "/v2/execution-envelope",
"acquireWriterLease": "/v2/writer-lease",
"commitFileGoverned": "/v2/commit-file",
"commitFilesGoverned": "/v2/commit-files",
"getGitOpsJob": "/v2/job",
"resumeGitOpsJob": "/v2/job/resume",
"reconcileGitOpsJob": "/v2/job/reconcile",
"getTagPublicationStatus": "/v2/tag/status",
"getReleaseConsistency": "/v2/release/consistency",
"runReleaseAcceptance": "/v2/release/acceptance",
}
def operation_contracts() -> dict[str, dict[str, object]]:
"""Return the runtime contract consumed by adapters and schema tooling."""
return {
operation: {
"method": "POST",
"path": _OPERATION_PATHS.get(operation),
"required_fields": list(fields),
}
for operation, fields in OPERATION_REQUIRED_FIELDS.items()
}
def render_openapi_contract_fragment() -> str:
required = "\n".join(
f" - {field}"
for field in OPERATION_REQUIRED_FIELDS["commitFilesGoverned"]
)
return (
" /v2/commit-files:\n"
" post:\n"
" operationId: commitFilesGoverned\n"
" summary: Submit one atomic governed commit containing multiple files\n"
" requestBody:\n"
" required: true\n"
" content:\n"
" application/json:\n"
" schema:\n"
" type: object\n"
" additionalProperties: false\n"
" required:\n"
f"{required}\n"
" properties:\n"
" task_id: {type: string}\n"
" job_id: {type: string, description: External durable Job identity for exact completed-replay reconciliation}\n"
" source_lease_epoch: {type: integer, minimum: 1}\n"
" repository: {type: string, example: aios/aios}\n"
" expected_branch_oid: {type: string, pattern: '^[0-9a-f]{40}$'}\n"
" target_branch: {type: string, pattern: '^agent/[A-Za-z0-9._/-]+$'}\n"
" generation: {type: integer, minimum: 1}\n"
" idempotency_key: {type: string}\n"
" files:\n"
" type: array\n"
" minItems: 1\n"
" items:\n"
" type: object\n"
" additionalProperties: false\n"
" required: [path, content_base64]\n"
" properties:\n"
" path: {type: string, minLength: 1, maxLength: 512}\n"
" content_base64: {type: string}\n"
" message: {type: string, minLength: 1, maxLength: 200}\n"
" responses:\n"
" '200': {description: Governed commit job and verified branch result}\n"
" '400': {description: Contract or policy rejection}\n"
" '409': {description: Branch CAS or idempotency conflict}\n"
)
class RuntimeDriftError(RuntimeError):
pass
@@ -196,7 +122,6 @@ class BridgeV2Adapter:
"legacy_writes_enabled": False,
"governed_writes_enabled": True,
"operations": list(BRIDGE_OPERATIONS),
"operation_contracts": operation_contracts(),
"force_push": False,
"main_write": False,
"tag_write": False,
@@ -225,7 +150,6 @@ class BridgeV2Adapter:
return handler(dict(request))
def createGovernedTask(self, request: Mapping[str, Any]) -> Any: return self._call("createGovernedTask", request)
def projectOnlineAgentExecution(self, request: Mapping[str, Any]) -> Any: return self._call("projectOnlineAgentExecution", 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)
@@ -4,7 +4,6 @@ from __future__ import annotations
from dataclasses import dataclass
from datetime import datetime, timedelta, timezone
import base64
import hashlib
import json
import os
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
@@ -15,11 +14,10 @@ from typing import Any, Mapping
from aios_authority_git_executor import AuthorityGitExecutor
from aios_chatgpt_bridge_v2 import BridgeV2Adapter, BridgeV2Error, OPERATION_REQUIRED_FIELDS
from gitea_tag_publication import validate_gitea_api_url
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, JobState, JobStore, _RECOVERABLE, sha256_json
from aios_gitops_job_controller import GitOpsJobController, JobStore
from aios_governed_task_bootstrap import BootstrapError, GovernedTaskBootstrap
from governed_production_release import build_executor_config
@@ -28,7 +26,6 @@ ROUTES = {
"/v2/runtime": "getRuntimeStatus",
"/v2/capabilities": "getGovernedWriteCapabilities",
"/v2/governed-task": "createGovernedTask",
"/v2/execution-envelope": "projectOnlineAgentExecution",
"/v2/writer-lease": "acquireWriterLease",
"/v2/commit-file": "commitFileGoverned",
"/v2/commit-files": "commitFilesGoverned",
@@ -55,14 +52,13 @@ class RuntimeConfig:
ssh_key_path: Path
remote_name: str = "gitea-http"
repository: str = "aios/aios"
api_base: str = ""
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"
release_plan_path: Path | None = None
def _safe(value: str) -> str:
@@ -119,30 +115,6 @@ class GovernedRuntime:
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)))
original_branch_mode = lease.branch_mode
if lease.branch_mode == "CREATE_NEW":
try:
lease.validate(AuthorityGitAdapter(self.authority))
except Exception as exc:
# A request replay after the authority CAS may observe the
# already-created branch. Only the exact base identity is
# idempotent; all other validation failures remain fail-closed.
if str(exc) != "TARGET_BRANCH_ALREADY_EXISTS":
raise
# Bootstrap owns the durable binding; the runtime performs the one-time
# branch materialization through the authority CAS before continuation
# validation. No direct Git or legacy writer path is used.
self.authority.create_task_branch(lease.target_branch, lease.base_oid)
lease = WriterLeaseRequestV2(
task_id=lease.task_id,
repository=lease.repository,
branch_mode="CONTINUE_EXISTING",
base_oid=lease.base_oid,
expected_branch_oid=lease.expected_branch_oid,
target_branch=lease.target_branch,
generation=lease.generation,
lease_seconds=lease.lease_seconds,
)
lease.validate(AuthorityGitAdapter(self.authority))
worktree_path = self.config.state_root / "worktrees" / _safe(lease.task_id)
if worktree_path.exists():
@@ -152,33 +124,13 @@ class GovernedRuntime:
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(), remote_branch_create_allowed=original_branch_mode == "CREATE_NEW", external_job_id=str(request.get("job_id") or "") or None)
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 project_online_agent_execution(self, request: Mapping[str, Any]) -> dict[str, Any]:
capabilities = request.get("required_capabilities") or []
if not isinstance(capabilities, list) or not all(isinstance(item, str) for item in capabilities):
raise ValueError("CAPABILITY_SCOPE_INVALID")
execution = request.get("execution")
if execution is not None and not isinstance(execution, Mapping):
raise ValueError("EXECUTION_REQUEST_INVALID")
task_id = str(request["task_id"])
expected_job_id = str(request.get("job_id") or "") or None
return self.bootstrap.execution_envelope(
task_id,
execution,
required_capabilities=tuple(capabilities),
expected_job_id=expected_job_id,
)
result = self.bootstrap.create(request); self.bindings[result["task_id"]] = result; return result
def acquire_lease(self, request: Mapping[str, Any]) -> dict[str, Any]:
replay = self._durable_completed_replay_lease(request)
if replay is not None:
return replay
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}
@@ -190,9 +142,6 @@ class GovernedRuntime:
return files
def commit_files(self, request: Mapping[str, Any]) -> dict[str, Any]:
replay = self._durable_completed_replay(request)
if replay is not None:
return replay
runtime = self.runtimes.get((str(request["task_id"]), int(request["generation"])))
if runtime is None: raise RuntimeError("LEASE_NOT_FOUND")
binding, _, executor = runtime
@@ -201,316 +150,6 @@ class GovernedRuntime:
executor.workspace.materialize_files(commit.files)
return executor.execute(binding, commit)
def _find_completed_replay_job(self, external_job_id: str) -> tuple[Path, dict[str, Any]] | None:
matches: list[tuple[Path, dict[str, Any]]] = []
roots = [self.config.state_root / "jobs"]
for controller in self.controllers.values():
roots.append(controller.store.root.parent)
seen: set[str] = set()
for jobs_root in roots:
if not jobs_root.is_dir():
continue
candidates = []
if (jobs_root / "state.sqlite3").is_file():
candidates.append(jobs_root)
candidates.extend(root for root in jobs_root.iterdir() if root.is_dir())
for root in candidates:
db = root / "state.sqlite3"
key = str(db.resolve()) if db.exists() else str(db)
if key in seen:
continue
seen.add(key)
if not db.is_file() or db.is_symlink():
continue
try:
connection = sqlite3.connect(f"file:{db}?mode=ro", uri=True)
connection.row_factory = sqlite3.Row
try:
rows = connection.execute("SELECT * FROM jobs").fetchall()
finally:
connection.close()
for raw in rows:
row = dict(raw)
durable = json.loads(row["request_json"])
binding = durable.get("binding", durable)
if str(binding.get("external_job_id") or "") == external_job_id:
matches.append((root, row))
except (OSError, ValueError, TypeError, sqlite3.Error):
continue
if not matches:
return None
if len(matches) != 1:
raise RuntimeError("EXTERNAL_JOB_MAPPING_AMBIGUOUS")
return matches[0]
def _durable_completed_replay_lease(self, request: Mapping[str, Any]) -> dict[str, Any] | None:
"""Admit an exact completed replay before ordinary lease validation."""
external_job_id = str(request.get("job_id") or "").strip()
if not external_job_id:
if request.get("source_lease_epoch") is not None:
raise RuntimeError("COMPLETED_REPLAY_EXTERNAL_JOB_ID_REQUIRED")
return None
match = self._find_completed_replay_job(external_job_id)
if match is None:
return None
root, row = match
if str(row["state"]) != JobState.COMPLETED.value:
return None
db = root / "state.sqlite3"
connection = sqlite3.connect(f"file:{db}?mode=ro", uri=True)
connection.row_factory = sqlite3.Row
try:
evidence_rows = connection.execute(
"SELECT * FROM completed_replay_reconciliations WHERE job_id=? ORDER BY created_at ASC",
(row["job_id"],),
).fetchall()
finally:
connection.close()
if not evidence_rows:
return None
evidence = dict(evidence_rows[0])
immutable = tuple(key for key in evidence if key != "created_at")
if any(any(item[key] != evidence[key] for key in immutable) for item in evidence_rows[1:]):
raise RuntimeError("COMPLETED_REPLAY_AMBIGUOUS")
durable = json.loads(row["request_json"])
binding = durable.get("binding", durable)
target_branch = str(binding.get("target_branch") or binding.get("branch") or "")
for field, incoming in (
("task_id", request.get("task_id")),
("repository", request.get("repository")),
("target_branch", request.get("target_branch")),
("generation", request.get("generation")),
("branch_mode", request.get("branch_mode")),
):
durable_value = target_branch if field == "target_branch" else binding.get(field)
if str(durable_value) != str(incoming):
raise RuntimeError("COMPLETED_REPLAY_BINDING_CONFLICT")
historical_head = str(evidence.get("historical_lease_head_oid") or "")
if str(request.get("base_oid") or "") != historical_head or str(request.get("expected_branch_oid") or "") != historical_head:
raise RuntimeError("COMPLETED_REPLAY_EXPECTED_HEAD_CONFLICT")
if request.get("source_lease_epoch") is not None and int(request["source_lease_epoch"]) != int(evidence["source_lease_epoch"]):
raise RuntimeError("COMPLETED_REPLAY_LEASE_EPOCH_CONFLICT")
receipt = json.loads(row["receipt_json"] or "null")
if not isinstance(receipt, Mapping) or receipt.get("commit_oid") != receipt.get("authority_oid"):
raise RuntimeError("COMPLETED_REPLAY_RECEIPT_INVALID")
authority_head = self.authority.read_ref("refs/heads/" + target_branch)
if authority_head != evidence.get("current_head_oid"):
raise RuntimeError("COMPLETED_REPLAY_AUTHORITY_CONFLICT")
if not (
evidence.get("commit_oid")
== evidence.get("authority_head_oid")
== evidence.get("current_head_oid")
== receipt.get("commit_oid")
and evidence.get("commit_oid") != historical_head
):
raise RuntimeError("COMPLETED_REPLAY_EVIDENCE_INCOHERENT")
return {
"schema": WriterLeaseRequestV2.schema,
"task_id": request["task_id"],
"job_id": external_job_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": int(request["generation"]),
"lease_seconds": int(request.get("lease_seconds") or 900),
"create_target_branch": False,
"worktree_mode": "detached_at_expected_head",
"cas_ref_update_required": True,
"lease_created": False,
"completed_replay": True,
"current_head_oid": evidence["current_head_oid"],
"completed_replay_reconciliation_id": evidence["reconciliation_id"],
}
def _durable_completed_replay(self, request: Mapping[str, Any]) -> dict[str, Any] | None:
"""Reconcile an exact completed replay before any worktree mutation."""
external_job_id = str(request.get("job_id") or "").strip()
if not external_job_id:
if request.get("source_lease_epoch") is not None:
raise RuntimeError("COMPLETED_REPLAY_EXTERNAL_JOB_ID_REQUIRED")
return None
matches: list[tuple[Path, dict[str, Any]]] = []
roots = [self.config.state_root / "jobs"]
for controller in self.controllers.values():
roots.append(controller.store.root.parent)
seen: set[str] = set()
for jobs_root in roots:
if not jobs_root.is_dir():
continue
candidates = []
if (jobs_root / "state.sqlite3").is_file():
candidates.append(jobs_root)
candidates.extend(root for root in jobs_root.iterdir() if root.is_dir())
for root in candidates:
db = root / "state.sqlite3"
key = str(db.resolve()) if db.exists() else str(db)
if key in seen:
continue
seen.add(key)
if not db.is_file() or db.is_symlink():
continue
try:
connection = sqlite3.connect(f"file:{db}?mode=ro", uri=True)
connection.row_factory = sqlite3.Row
try:
rows = connection.execute("SELECT * FROM jobs").fetchall()
finally:
connection.close()
for raw in rows:
row = dict(raw)
durable = json.loads(row["request_json"])
binding = durable.get("binding", durable)
if str(binding.get("external_job_id") or "") == external_job_id:
matches.append((root, row))
except (OSError, ValueError, TypeError, sqlite3.Error):
continue
if not matches:
return None
if len(matches) != 1:
raise RuntimeError("EXTERNAL_JOB_MAPPING_AMBIGUOUS")
root, row = matches[0]
if str(row["state"]) != JobState.COMPLETED.value:
return None
durable = json.loads(row["request_json"])
binding = durable.get("binding", durable)
source_lease_epoch = request.get("source_lease_epoch")
if source_lease_epoch is None:
db = root / "state.sqlite3"
connection = sqlite3.connect(f"file:{db}?mode=ro", uri=True)
connection.row_factory = sqlite3.Row
try:
evidence = connection.execute(
"SELECT source_lease_epoch FROM completed_replay_reconciliations WHERE job_id=? ORDER BY created_at ASC LIMIT 1",
(row["job_id"],),
).fetchone()
finally:
connection.close()
if evidence is not None:
source_lease_epoch = int(evidence["source_lease_epoch"])
files = self._files(request)
commit = ContinuationCommit(str(request["idempotency_key"]), files, str(request["message"]))
payload = commit.payload()
receipt = json.loads(row["receipt_json"] or "null")
if not isinstance(receipt, Mapping):
raise RuntimeError("COMPLETED_REPLAY_RECEIPT_INVALID")
incoming = {
"task_id": str(request.get("task_id") or ""),
"external_job_id": external_job_id,
"internal_job_id": str(row["job_id"]),
"repository": str(request.get("repository") or ""),
"target_branch": str(request.get("target_branch") or ""),
"generation": int(request.get("generation") or 0),
"branch_mode": str(request.get("branch_mode") or ""),
"payload_digest": hashlib.sha256(payload).hexdigest(),
"files_digest": hashlib.sha256(json.dumps({path: hashlib.sha256(content).hexdigest() for path, content in sorted(files.items())}, sort_keys=True, separators=(",", ":")).encode("utf-8")).hexdigest(),
"message_digest": hashlib.sha256(str(request["message"]).encode("utf-8")).hexdigest(),
"worker_principal": str(binding.get("worker_principal") or ""),
"worker_lineage": str(binding.get("worker_lineage") or ""),
"source_lease_epoch": int(source_lease_epoch or 0),
"durable_revision": int(row["revision"]),
"completion_identity": sha256_json({"job_id": row["job_id"], "revision": int(row["revision"]), "receipt": receipt}),
"reason": "COMPLETED_REPLAY_LEASE_EVIDENCE_RECONCILIATION",
"provenance": {"source": "governed-completed-replay", "authority_readback": True},
}
for field in ("task_id", "repository", "target_branch", "generation", "branch_mode", "payload_digest", "files_digest", "message_digest", "worker_principal", "worker_lineage"):
durable_value = binding.get(field)
if field == "target_branch":
durable_value = binding.get("target_branch") or binding.get("branch")
elif field in {"worker_principal", "worker_lineage"}:
durable_value = durable_value or ""
if durable_value not in (None, "") and str(durable_value) != str(incoming[field]):
raise RuntimeError("COMPLETED_REPLAY_BINDING_CONFLICT")
db = root / "state.sqlite3"
connection = sqlite3.connect(f"file:{db}?mode=ro", uri=True)
connection.row_factory = sqlite3.Row
try:
columns = {str(item[1]) for item in connection.execute("PRAGMA table_info(branch_leases)").fetchall()}
if "epoch" in columns:
lease = connection.execute("SELECT lease_id, state, current_head_oid, generation FROM branch_leases WHERE job_id=? AND epoch=? ORDER BY rowid DESC LIMIT 1", (row["job_id"], incoming["source_lease_epoch"])).fetchone()
else:
if incoming["source_lease_epoch"] != 1:
raise RuntimeError("COMPLETED_REPLAY_LEASE_EPOCH_UNAVAILABLE")
lease = connection.execute("SELECT lease_id, state, current_head_oid, generation FROM branch_leases WHERE job_id=? ORDER BY rowid DESC LIMIT 1", (row["job_id"],)).fetchone()
if lease is None:
raise RuntimeError("COMPLETED_REPLAY_LEASE_MISSING")
historical_lease_head = str(lease["current_head_oid"])
event_tables = {str(item[0]) for item in connection.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='branch_lease_events'").fetchall()}
lease_state = str(lease["state"])
lease_generation = int(lease["generation"])
if "branch_lease_events" in event_tables:
event = connection.execute("SELECT state, current_head_oid, generation FROM branch_lease_events WHERE lease_id=? ORDER BY rowid DESC LIMIT 1", (lease["lease_id"],)).fetchone()
if event is not None:
lease_state = str(event["state"])
historical_lease_head = str(event["current_head_oid"])
lease_generation = int(event["generation"])
if lease_state not in {"ACTIVE", "USED", "EXPIRED"}:
raise RuntimeError("COMPLETED_REPLAY_LEASE_STATE_INVALID")
if lease_generation != incoming["generation"]:
raise RuntimeError("COMPLETED_REPLAY_LEASE_GENERATION_CONFLICT")
finally:
connection.close()
incoming["historical_lease_head_oid"] = historical_lease_head
authority_head = self.authority.read_ref("refs/heads/" + incoming["target_branch"])
if authority_head is None:
raise RuntimeError("COMPLETED_REPLAY_AUTHORITY_MISSING")
if receipt.get("commit_oid") != receipt.get("authority_oid"):
raise RuntimeError("COMPLETED_REPLAY_RECEIPT_INVALID")
incoming["commit_oid"] = str(receipt["commit_oid"])
incoming["authority_head_oid"] = authority_head
connection = sqlite3.connect(f"file:{db}?mode=ro", uri=True)
connection.row_factory = sqlite3.Row
try:
evidence_rows = connection.execute("SELECT * FROM completed_replay_reconciliations WHERE job_id=? ORDER BY created_at ASC", (row["job_id"],)).fetchall()
finally:
connection.close()
if evidence_rows:
evidence = dict(evidence_rows[0])
immutable = tuple(key for key in evidence if key != "created_at")
if any(any(item[key] != evidence[key] for key in immutable) for item in evidence_rows[1:]):
raise RuntimeError("COMPLETED_REPLAY_AMBIGUOUS")
normalized = JobStore._completed_replay_identity(incoming)
columns = {
"task_id": "task_id",
"external_job_id": "external_job_id",
"internal_job_id": "internal_job_id",
"repository": "repository",
"target_branch": "target_branch",
"generation": "generation",
"branch_mode": "branch_mode",
"payload_digest": "payload_digest",
"files_digest": "files_digest",
"message_digest": "message_digest",
"commit_oid": "commit_oid",
"authority_head_oid": "authority_head_oid",
"historical_lease_head_oid": "historical_lease_head_oid",
"worker_principal": "worker_principal",
"worker_lineage": "worker_lineage",
"source_lease_epoch": "source_lease_epoch",
"durable_revision": "durable_revision",
"completion_identity": "completion_identity",
"reason": "reason",
}
if any(str(evidence[column]) != str(normalized[field]) for field, column in columns.items()):
raise RuntimeError("COMPLETED_REPLAY_BINDING_CONFLICT")
if evidence["provenance_json"] != json.dumps(normalized["provenance"], sort_keys=True, separators=(",", ":")):
raise RuntimeError("COMPLETED_REPLAY_PROVENANCE_CONFLICT")
if evidence["identity_digest"] != sha256_json(normalized):
raise RuntimeError("COMPLETED_REPLAY_IDENTITY_CONFLICT")
if not (evidence["commit_oid"] == evidence["authority_head_oid"] == evidence["current_head_oid"] and evidence["commit_oid"] != evidence["historical_lease_head_oid"]):
raise RuntimeError("COMPLETED_REPLAY_EVIDENCE_INCOHERENT")
if self.authority.read_ref("refs/heads/" + incoming["target_branch"]) != evidence["current_head_oid"]:
raise RuntimeError("COMPLETED_REPLAY_AUTHORITY_CONFLICT")
result = {"job_id": external_job_id, "task_id": incoming["task_id"], "repository": incoming["repository"], "branch_mode": incoming["branch_mode"], "base_oid": request.get("base_oid"), "expected_branch_oid": request.get("expected_branch_oid"), "target_branch": incoming["target_branch"], "generation": incoming["generation"], "commit_oid": evidence["commit_oid"], "idempotency_key": row["idempotency_key"], "state": JobState.COMPLETED.value, "side_effect_state": row["side_effect_state"], "http_status": 200, "revision": int(row["revision"]), "receipt": {**receipt, "commit_oid": evidence["commit_oid"], "authority_oid": evidence["authority_head_oid"], "current_head_oid": evidence["current_head_oid"], "external_job_id": evidence["external_job_id"], "completed_replay_reconciliation_id": evidence["reconciliation_id"]}, "current_head_oid": evidence["current_head_oid"], "completed_replay_reconciliation_id": evidence["reconciliation_id"]}
return result
controller = GitOpsJobController(JobStore.for_existing_append(root))
reconciliation = controller.store.append_completed_replay_reconciliation(str(row["job_id"]), incoming, authority_head_oid=authority_head)
evidence = {**receipt, "commit_oid": reconciliation["commit_oid"], "authority_oid": reconciliation["authority_head_oid"], "current_head_oid": reconciliation["current_head_oid"], "completed_replay_reconciliation_id": reconciliation["reconciliation_id"], "external_job_id": external_job_id}
return {"job_id": external_job_id, "task_id": incoming["task_id"], "repository": incoming["repository"], "branch_mode": incoming["branch_mode"], "base_oid": request.get("base_oid"), "expected_branch_oid": request.get("expected_branch_oid"), "target_branch": incoming["target_branch"], "generation": incoming["generation"], "commit_oid": reconciliation["commit_oid"], "idempotency_key": row["idempotency_key"], "state": JobState.COMPLETED.value, "side_effect_state": row["side_effect_state"], "http_status": 200, "revision": int(row["revision"]), "receipt": evidence, "current_head_oid": reconciliation["current_head_oid"], "completed_replay_reconciliation_id": reconciliation["reconciliation_id"]}
def _controller_for_job(self, job_id: str) -> GitOpsJobController:
for controller in self.controllers.values():
try:
@@ -523,32 +162,12 @@ class GovernedRuntime:
for root in jobs_root.iterdir():
if not root.is_dir():
continue
db = root / "state.sqlite3"
if not db.is_file() or db.is_symlink():
continue
controller = GitOpsJobController(JobStore(root, payload_root=root / "payloads"))
try:
connection = sqlite3.connect(f"file:{db}?mode=ro", uri=True)
connection.row_factory = sqlite3.Row
try:
rows = connection.execute("SELECT job_id, request_json FROM jobs").fetchall()
finally:
connection.close()
matched = False
for row in rows:
if str(row["job_id"]) == job_id:
matched = True
break
durable = json.loads(row["request_json"])
binding = durable.get("binding", durable)
if str(binding.get("external_job_id") or "") == job_id:
matched = True
break
if not matched:
continue
controller = GitOpsJobController(JobStore(root, payload_root=root / "payloads"))
controller.store.read(job_id)
self.controllers[str(job_id)] = controller
return controller
except (OSError, ValueError, TypeError, sqlite3.Error):
except Exception:
continue
raise RuntimeError("JOB_NOT_FOUND")
@@ -658,30 +277,9 @@ class GovernedRuntime:
controller = self._controller_for_job(job_id)
row = controller.store.read(job_id)
if action == "get":
result = controller.store._receipt(row, duplicate=True).as_dict()
evidence_reader = getattr(controller.store, "completed_replay_evidence", None)
evidence = evidence_reader(job_id) if callable(evidence_reader) else None
if evidence is not None:
branch = str(json.loads(row["request_json"]).get("binding", {}).get("branch") or row["branch"])
authority_head = self.authority.read_ref("refs/heads/" + branch)
if authority_head != evidence["authority_head_oid"] or evidence["current_head_oid"] != authority_head:
raise RuntimeError("COMPLETED_REPLAY_AUTHORITY_CONFLICT")
result["job_id"] = evidence["external_job_id"]
result["current_head_oid"] = evidence["current_head_oid"]
result["completed_replay_reconciliation_id"] = evidence["reconciliation_id"]
result["receipt"] = {
**(result.get("receipt") or {}),
"commit_oid": evidence["commit_oid"],
"authority_oid": evidence["authority_head_oid"],
"current_head_oid": evidence["current_head_oid"],
"external_job_id": evidence["external_job_id"],
"completed_replay_reconciliation_id": evidence["reconciliation_id"],
}
return result
return controller.store._receipt(row, duplicate=True).as_dict()
if action == "resume":
return controller.resume(job_id).as_dict()
if JobState(row["state"]) not in _RECOVERABLE:
return controller.store._receipt(row, duplicate=True).as_dict()
return self._executor_from_row(row, controller).reconcile(job_id)
def _snapshot_task(self) -> dict[str, Any]:
@@ -697,38 +295,45 @@ class GovernedRuntime:
except (TypeError, ValueError):
generation = 0
reconciliation_state = "OBSERVATION_REQUIRED"
projection: dict[str, Any] = {}
db = self.config.state_root / "jobs" / _safe(task_id) / "state.sqlite3"
if db.is_file() and not db.is_symlink():
if db.is_file():
connection = sqlite3.connect(f"file:{db}?mode=ro", uri=True)
connection.row_factory = sqlite3.Row
try:
lease = connection.execute("SELECT MAX(generation) FROM branch_leases WHERE branch=?", (branch,)).fetchone()
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 job_id, state, side_effect_state FROM jobs WHERE branch=? ORDER BY updated_at DESC LIMIT 1", (branch,)).fetchone()
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["state"] == "COMPLETED" else str(latest["side_effect_state"] or latest["state"])
evidence_rows = connection.execute("SELECT * FROM completed_replay_reconciliations WHERE job_id=? ORDER BY created_at ASC", (latest["job_id"],)).fetchall()
if evidence_rows:
evidence = dict(evidence_rows[0])
immutable = tuple(key for key in evidence if key != "created_at")
if any(any(item[key] != evidence[key] for key in immutable) for item in evidence_rows[1:]):
raise RuntimeError("COMPLETED_REPLAY_AMBIGUOUS")
if not (evidence["commit_oid"] == evidence["authority_head_oid"] == evidence["current_head_oid"] and evidence["commit_oid"] != evidence["historical_lease_head_oid"]):
raise RuntimeError("COMPLETED_REPLAY_EVIDENCE_INCOHERENT")
current = self.authority.read_ref("refs/heads/" + branch)
if current != evidence["authority_head_oid"] or current != evidence["current_head_oid"]:
raise RuntimeError("COMPLETED_REPLAY_AUTHORITY_CONFLICT")
reconciliation_state = "COMPLETED_REPLAY_RECONCILED"
projection = {"external_job_id": evidence["external_job_id"], "commit_oid": evidence["commit_oid"], "current_head_oid": evidence["current_head_oid"], "completed_replay_reconciliation_id": evidence["reconciliation_id"], "receipt_authority_oid": evidence["authority_head_oid"]}
reconciliation_state = (
"COMPLETED"
if latest[0] == "COMPLETED"
else str(latest[1] or latest[0])
)
finally:
connection.close()
candidate = {"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}
candidate.update(projection)
candidates.append(candidate)
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"])))
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()
@@ -736,7 +341,6 @@ class GovernedRuntime:
def handlers(self) -> dict[str, Any]:
return {
"projectOnlineAgentExecution": self.project_online_agent_execution,
"createGovernedTask": self.create_task,
"acquireWriterLease": self.acquire_lease,
"commitFileGoverned": lambda request: self.commit_files({**request, "files": {request["path"]: request["content_base64"]}}),
@@ -776,9 +380,9 @@ class BridgeHandler(BaseHTTPRequestHandler):
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", "source_lease_epoch"}
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", "job_id", "historical_lease_head_oid"})
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
@@ -814,12 +418,8 @@ def main() -> None:
token_path,
ssh_key_path,
remote_name=os.environ.get("AIOS_GITEA_REMOTE", "gitea-http"),
api_base=validate_gitea_api_url(os.environ.get("AIOS_GITEA_API_BASE", "")),
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"),
baseline_path=Path(os.environ.get(
"AIOS_GITOPS_BASELINE_AUTHORITY",
"/var/lib/aios-gitops-runtime-anchor/baseline/current.json",
)),
)
runtime = GovernedRuntime(config)
serve(
@@ -56,7 +56,7 @@ class AuthorityPort(Protocol):
class RemotePort(Protocol):
def repository_identity(self) -> str: ...
def push_exact(self, branch: str, expected: str, new: str, *, allow_absent: bool = False) -> None: ...
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: ...
@@ -258,11 +258,10 @@ class GiteaContinuationRemote:
raise ContinuationExecutionError("GITEA_GIT_READBACK_INVALID")
return oid
def push_exact(self, branch: str, expected: str, new: str, *, allow_absent: bool = False) -> None:
def push_exact(self, branch: str, expected: str, new: str) -> None:
self._require_projected()
self.repository_identity()
current = self.read_git_ref(branch)
if current != expected and not (allow_absent and current is None):
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)
@@ -336,10 +335,6 @@ class ContinuationRuntimeBinding:
original_receipt_valid: bool
original_grant_consumed: bool
permissions: ContinuationPermissions
remote_branch_create_allowed: bool = False
external_job_id: str | None = None
worker_principal: str = "aios-autonomy-agent"
worker_lineage: str = "governed-continuation"
@dataclass(frozen=True, slots=True)
@@ -455,17 +450,6 @@ class ContinuationExecutor:
"payload_digest": hashlib.sha256(payload).hexdigest(),
"lineage_anchor_oid": request.base_oid,
"generation": request.generation,
"remote_branch_create_allowed": binding.remote_branch_create_allowed,
"external_job_id": binding.external_job_id,
"files_digest": hashlib.sha256(
json.dumps(
{path: hashlib.sha256(content).hexdigest() for path, content in sorted(commit.files.items())},
sort_keys=True, separators=(",", ":"),
).encode("utf-8")
).hexdigest(),
"message_digest": hashlib.sha256(commit.message.encode("utf-8")).hexdigest(),
"worker_principal": binding.worker_principal,
"worker_lineage": binding.worker_lineage,
}
def execute(self, binding: ContinuationRuntimeBinding, commit: ContinuationCommit) -> dict[str, object]:
@@ -515,7 +499,7 @@ class ContinuationExecutor:
)
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, allow_absent=binding.remote_branch_create_allowed)
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)
@@ -529,7 +513,6 @@ class ContinuationExecutor:
def _remote_pending(self, job_id: str, authority_oid: str, new_oid: str) -> dict[str, object]:
receipt = {
"authority_oid": authority_oid,
"commit_oid": new_oid,
"desired_remote_oid": new_oid,
"remote_verified": False,
"force_push": False,
@@ -552,7 +535,6 @@ class ContinuationExecutor:
return self._remote_pending(job_id, authority_oid, new_oid)
receipt = {
"authority_oid": authority_oid,
"commit_oid": new_oid,
"git_remote_oid": git_oid,
"api_branch_oid": api_oid,
"commit_visible": True,
@@ -601,14 +583,11 @@ class ContinuationExecutor:
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, allow_absent=bool(request.get("remote_branch_create_allowed")) or request.get("branch_mode") == "CREATE_NEW")
self.remote.push_exact(row["branch"], git_oid, new_oid)
return self._finish_remote_verification(job_id, new_oid, new_oid)
allow_remote_create = bool(request.get("remote_branch_create_allowed")) or request.get("branch_mode") == "CREATE_NEW" or (git_oid is None and api_oid is None)
remote_matches_expected = git_oid == api_oid == expected_oid
remote_absent_for_create = git_oid is None and api_oid is None
if authority_oid == new_oid and (remote_matches_expected or remote_absent_for_create):
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, allow_absent=allow_remote_create)
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:
@@ -10,16 +10,12 @@ def _copy(value: Mapping[str, Any] | None, keys: tuple[str, ...]) -> dict[str, A
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]:
task_snapshot = _copy(task, ("task_id", "branch", "authority_branch_oid", "gitea_git_oid", "gitea_api_oid", "commit_visible", "generation", "reconciliation_state"))
for key in ("external_job_id", "commit_oid", "current_head_oid", "completed_replay_reconciliation_id", "receipt_authority_oid"):
if task is not None and key in task:
task_snapshot[key] = task[key]
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": task_snapshot,
"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,
@@ -1,478 +0,0 @@
"""Minimal convergence contracts for AIOS V2R2C2.
This module deliberately contains no external side effects. It provides the shared
data contracts and pure evaluation rules used by domain controllers. Domain
controllers remain authoritative for their own final states.
"""
from __future__ import annotations
from dataclasses import asdict, dataclass, field
from datetime import datetime, timezone
from enum import Enum
import hashlib
import json
from typing import Any, Iterable, Mapping, Sequence
class ConvergenceContractError(ValueError):
"""Raised when an invalid convergence contract is constructed."""
class EvidenceState(str, Enum):
PASS = "PASS"
FAIL = "FAIL"
UNKNOWN = "UNKNOWN"
BLOCKED = "BLOCKED"
NOT_APPLICABLE = "NOT_APPLICABLE"
class AcceptanceDecision(str, Enum):
ACCEPT = "ACCEPT"
REJECT = "REJECT"
BLOCKED = "BLOCKED"
UNKNOWN = "UNKNOWN"
class ObservedHealth(str, Enum):
HEALTHY = "HEALTHY"
DEGRADED = "DEGRADED"
FAILED = "FAILED"
UNKNOWN = "UNKNOWN"
class ReconciliationState(str, Enum):
PENDING = "PENDING"
OBSERVING = "OBSERVING"
DIFF_FOUND = "DIFF_FOUND"
APPLYING = "APPLYING"
VERIFYING = "VERIFYING"
CONVERGED = "CONVERGED"
DEGRADED = "DEGRADED"
BLOCKED = "BLOCKED"
class ContinuationStopReason(str, Enum):
CONTEXT_BOUNDARY = "context_boundary"
WORKER_RESTART = "worker_restart"
HANDOFF = "handoff"
AUTHORITY_GATE = "authority_gate"
WAIT_EXTERNAL = "wait_external"
def _utc_now() -> str:
return datetime.now(timezone.utc).isoformat().replace("+00:00", "Z")
def _canonical_json(value: Mapping[str, Any]) -> bytes:
return json.dumps(value, sort_keys=True, separators=(",", ":"), ensure_ascii=False).encode("utf-8")
def _sha256(value: Mapping[str, Any]) -> str:
return "sha256:" + hashlib.sha256(_canonical_json(value)).hexdigest()
def _nonempty(value: object, field_name: str) -> str:
text = str(value or "").strip()
if not text:
raise ConvergenceContractError(f"{field_name}_required")
return text
def _generation(value: object, field_name: str = "generation") -> int:
try:
parsed = int(value)
except (TypeError, ValueError) as exc:
raise ConvergenceContractError(f"{field_name}_invalid") from exc
if parsed < 1:
raise ConvergenceContractError(f"{field_name}_invalid")
return parsed
@dataclass(frozen=True, slots=True)
class StateOwnership:
object_type: str
authoritative_service: str
authoritative_store: str
final_writer: str | None = None
projections: tuple[str, ...] = ()
schema_version: str = "aios.state-ownership.v1"
def __post_init__(self) -> None:
_nonempty(self.object_type, "object_type")
_nonempty(self.authoritative_service, "authoritative_service")
_nonempty(self.authoritative_store, "authoritative_store")
if self.final_writer is not None:
_nonempty(self.final_writer, "final_writer")
def as_dict(self) -> dict[str, Any]:
return asdict(self)
DEFAULT_STATE_OWNERSHIP: tuple[StateOwnership, ...] = (
StateOwnership(
object_type="goal",
authoritative_service="goal-kernel",
authoritative_store="goal-store",
final_writer="goal-kernel",
projections=("admin-web", "context-pack"),
),
StateOwnership(
object_type="task_run",
authoritative_service="task-plane-18940",
authoritative_store="durable-task-store",
final_writer="task-plane-18940",
projections=("admin-web", "context-pack"),
),
StateOwnership(
object_type="release",
authoritative_service="release-controller",
authoritative_store="release-registry",
final_writer="release-controller",
projections=("baseline-authority", "admin-web"),
),
StateOwnership(
object_type="git_branch",
authoritative_service="authority-git-executor",
authoritative_store="authoritative-git",
final_writer="authority-git-executor",
projections=("gitea",),
),
StateOwnership(
object_type="external_projection",
authoritative_service="projection-controller",
authoritative_store="projection-store",
final_writer="projection-controller",
projections=("openapi", "gpt-action", "nginx"),
),
)
def ownership_index(
ownership: Iterable[StateOwnership] = DEFAULT_STATE_OWNERSHIP,
) -> dict[str, StateOwnership]:
result: dict[str, StateOwnership] = {}
for row in ownership:
if row.object_type in result:
raise ConvergenceContractError(f"duplicate_state_owner:{row.object_type}")
result[row.object_type] = row
return result
@dataclass(frozen=True, slots=True)
class AcceptanceCriterion:
criterion_id: str
required: bool
state: EvidenceState
verifier_id: str
evidence_refs: tuple[str, ...] = ()
def __post_init__(self) -> None:
_nonempty(self.criterion_id, "criterion_id")
_nonempty(self.verifier_id, "verifier_id")
@dataclass(frozen=True, slots=True)
class AcceptanceInvariant:
invariant_id: str
state: EvidenceState
evidence_refs: tuple[str, ...] = ()
def __post_init__(self) -> None:
_nonempty(self.invariant_id, "invariant_id")
@dataclass(frozen=True, slots=True)
class AcceptanceVector:
subject_type: str
subject_id: str
subject_revision: int
criteria: tuple[AcceptanceCriterion, ...]
invariants: tuple[AcceptanceInvariant, ...] = ()
unresolved_side_effects: tuple[str, ...] = ()
unresolved_blockers: tuple[str, ...] = ()
runtime_proofs: tuple[str, ...] = ()
schema: str = "aios.acceptance-vector.v1"
def __post_init__(self) -> None:
_nonempty(self.subject_type, "subject_type")
_nonempty(self.subject_id, "subject_id")
_generation(self.subject_revision, "subject_revision")
ids = [row.criterion_id for row in self.criteria]
if len(ids) != len(set(ids)):
raise ConvergenceContractError("duplicate_acceptance_criterion")
invariant_ids = [row.invariant_id for row in self.invariants]
if len(invariant_ids) != len(set(invariant_ids)):
raise ConvergenceContractError("duplicate_acceptance_invariant")
def as_dict(self) -> dict[str, Any]:
value = asdict(self)
for row in value["criteria"]:
row["state"] = row["state"].value
for row in value["invariants"]:
row["state"] = row["state"].value
return value
@dataclass(frozen=True, slots=True)
class AcceptanceResult:
decision: AcceptanceDecision
reasons: tuple[str, ...]
vector_hash: str
def as_dict(self) -> dict[str, Any]:
return {
"decision": self.decision.value,
"reasons": list(self.reasons),
"vector_hash": self.vector_hash,
}
def evaluate_acceptance(vector: AcceptanceVector) -> AcceptanceResult:
"""Pure fail-closed acceptance evaluator.
The evaluator never writes a domain final state. The domain owner consumes this
result and remains the only final writer for Goal/Release/Product state.
"""
reasons: list[str] = []
required = [row for row in vector.criteria if row.required]
for row in required:
if row.state == EvidenceState.FAIL:
reasons.append(f"criterion_failed:{row.criterion_id}")
elif row.state == EvidenceState.BLOCKED:
reasons.append(f"criterion_blocked:{row.criterion_id}")
elif row.state == EvidenceState.UNKNOWN:
reasons.append(f"criterion_unknown:{row.criterion_id}")
for row in vector.invariants:
if row.state == EvidenceState.FAIL:
reasons.append(f"invariant_failed:{row.invariant_id}")
elif row.state in {EvidenceState.UNKNOWN, EvidenceState.BLOCKED}:
reasons.append(f"invariant_unknown:{row.invariant_id}")
if vector.unresolved_side_effects:
reasons.append("unresolved_side_effects")
if vector.unresolved_blockers:
reasons.append("unresolved_blockers")
digest = _sha256(vector.as_dict())
if any(reason.startswith(("criterion_failed:", "invariant_failed:")) for reason in reasons):
decision = AcceptanceDecision.REJECT
elif any(reason.startswith("criterion_blocked:") for reason in reasons) or vector.unresolved_blockers:
decision = AcceptanceDecision.BLOCKED
elif reasons:
decision = AcceptanceDecision.UNKNOWN
else:
decision = AcceptanceDecision.ACCEPT
return AcceptanceResult(decision=decision, reasons=tuple(reasons), vector_hash=digest)
@dataclass(frozen=True, slots=True)
class DesiredState:
resource_id: str
resource_type: str
desired_generation: int
desired_spec: Mapping[str, Any]
owner_goal_id: str | None = None
lifecycle: str = "active"
schema: str = "aios.desired-state.v1"
def __post_init__(self) -> None:
_nonempty(self.resource_id, "resource_id")
_nonempty(self.resource_type, "resource_type")
_generation(self.desired_generation, "desired_generation")
if self.lifecycle not in {"active", "suspended", "deleted"}:
raise ConvergenceContractError("desired_lifecycle_invalid")
def identity(self) -> tuple[str, int]:
return self.resource_id, self.desired_generation
@dataclass(frozen=True, slots=True)
class ObservedState:
resource_id: str
observed_generation: int | None
observed_spec: Mapping[str, Any]
health: ObservedHealth
source_identity: str
observed_at: str
evidence_refs: tuple[str, ...] = ()
schema: str = "aios.observed-state.v1"
def __post_init__(self) -> None:
_nonempty(self.resource_id, "resource_id")
_nonempty(self.source_identity, "source_identity")
_nonempty(self.observed_at, "observed_at")
if self.observed_generation is not None:
_generation(self.observed_generation, "observed_generation")
@dataclass(frozen=True, slots=True)
class ReconciliationPlan:
resource_id: str
desired_generation: int
state: ReconciliationState
diff: Mapping[str, Any]
observation_required: bool
idempotency_key: str
reason: str
def as_dict(self) -> dict[str, Any]:
value = asdict(self)
value["state"] = self.state.value
return value
def plan_reconciliation(
desired: DesiredState,
observed: ObservedState | None,
) -> ReconciliationPlan:
"""Return a side-effect-free reconciliation decision.
UNKNOWN/missing observed state always requires observation and never converges.
"""
identity = {
"resource_id": desired.resource_id,
"desired_generation": desired.desired_generation,
"desired_spec": desired.desired_spec,
}
key = _sha256(identity)
if observed is None:
return ReconciliationPlan(
desired.resource_id,
desired.desired_generation,
ReconciliationState.OBSERVING,
{"observed": None},
True,
key,
"observed_state_missing",
)
if observed.resource_id != desired.resource_id:
raise ConvergenceContractError("resource_identity_mismatch")
if observed.health == ObservedHealth.UNKNOWN:
return ReconciliationPlan(
desired.resource_id,
desired.desired_generation,
ReconciliationState.OBSERVING,
{"health": observed.health.value},
True,
key,
"observed_state_unknown",
)
same_generation = observed.observed_generation == desired.desired_generation
same_spec = dict(observed.observed_spec) == dict(desired.desired_spec)
if same_generation and same_spec and observed.health == ObservedHealth.HEALTHY:
return ReconciliationPlan(
desired.resource_id,
desired.desired_generation,
ReconciliationState.CONVERGED,
{},
False,
key,
"desired_equals_observed",
)
return ReconciliationPlan(
desired.resource_id,
desired.desired_generation,
ReconciliationState.DIFF_FOUND,
{
"desired_generation": desired.desired_generation,
"observed_generation": observed.observed_generation,
"desired_spec": dict(desired.desired_spec),
"observed_spec": dict(observed.observed_spec),
"observed_health": observed.health.value,
},
False,
key,
"desired_observed_drift",
)
@dataclass(frozen=True, slots=True)
class SideEffectReceipt:
mutation_id: str
external_system: str
state: str
observed_at: str
external_request_id: str | None = None
external_object_id: str | None = None
observed_result_hash: str | None = None
evidence_refs: tuple[str, ...] = ()
schema: str = "aios.side-effect-receipt.v1"
def __post_init__(self) -> None:
_nonempty(self.mutation_id, "mutation_id")
_nonempty(self.external_system, "external_system")
_nonempty(self.observed_at, "observed_at")
if self.state not in {
"confirmed_applied",
"confirmed_not_applied",
"unknown",
}:
raise ConvergenceContractError("side_effect_receipt_state_invalid")
@dataclass(frozen=True, slots=True)
class ContinuationManifest:
trace_id: str
goal_id: str
goal_revision: int
task_id: str
run_id: str
current_node_id: str
acceptance_vector_ref: str
next_action: str
stop_reason: ContinuationStopReason
checkpoint_ref: str | None = None
unresolved_blocker_refs: tuple[str, ...] = ()
authoritative_decision_refs: tuple[str, ...] = ()
resource_refs: tuple[str, ...] = ()
artifact_refs: tuple[str, ...] = ()
evidence_refs: tuple[str, ...] = ()
created_at: str = field(default_factory=_utc_now)
schema: str = "aios.continuation-manifest.v1"
def __post_init__(self) -> None:
for name in (
"trace_id",
"goal_id",
"task_id",
"run_id",
"current_node_id",
"acceptance_vector_ref",
"next_action",
"created_at",
):
_nonempty(getattr(self, name), name)
_generation(self.goal_revision, "goal_revision")
def as_dict(self) -> dict[str, Any]:
value = asdict(self)
value["stop_reason"] = self.stop_reason.value
return value
@property
def content_hash(self) -> str:
return _sha256(self.as_dict())
def validate_state_ownership(
object_type: str,
writer: str,
ownership: Iterable[StateOwnership] = DEFAULT_STATE_OWNERSHIP,
*,
final_state: bool = False,
) -> None:
row = ownership_index(ownership).get(object_type)
if row is None:
raise ConvergenceContractError(f"state_owner_missing:{object_type}")
expected = row.final_writer if final_state and row.final_writer else row.authoritative_service
if writer != expected:
raise ConvergenceContractError(
f"STATE_OWNERSHIP_VIOLATION:{object_type}:{writer}:{expected}"
)
@@ -41,8 +41,6 @@ class JobState(str, Enum):
REMOTE_SYNCING = "REMOTE_SYNCING"
REMOTE_VERIFIED = "REMOTE_VERIFIED"
COMPLETED = "COMPLETED"
FAILED = "FAILED"
CANCELLED = "CANCELLED"
RECOVERABLE_FAILURE = "RECOVERABLE_FAILURE"
RECONCILING = "RECONCILING"
RECONCILIATION_REQUIRED = "RECONCILIATION_REQUIRED"
@@ -61,12 +59,7 @@ _ORDER = {
JobState.COMPLETED: 8,
}
_RECOVERABLE = {JobState.RECOVERABLE_FAILURE, JobState.RECONCILIATION_REQUIRED}
_TERMINAL = {
JobState.COMPLETED,
JobState.FAILED,
JobState.CANCELLED,
JobState.MANUAL_OWNER_REVIEW_REQUIRED,
}
_TERMINAL = {JobState.COMPLETED, JobState.MANUAL_OWNER_REVIEW_REQUIRED}
def utc_now() -> str:
@@ -178,63 +171,6 @@ class JobStore:
connection.execute("PRAGMA busy_timeout=30000")
return connection
@classmethod
def for_existing_append(cls, root: str | os.PathLike[str]) -> "JobStore":
"""Open an existing job database without initialization side effects."""
path = Path(root)
if (
not path.is_absolute()
or path.is_symlink()
or not path.is_dir()
or not (path / "state.sqlite3").is_file()
or (path / "state.sqlite3").is_symlink()
):
raise JobError("JOB_ROOT_NOT_FOUND")
instance = cls.__new__(cls)
instance.root = path
instance.db_path = path / "state.sqlite3"
return instance
@staticmethod
def _ensure_completed_replay_schema(connection: sqlite3.Connection) -> None:
connection.executescript(
"""
CREATE TABLE IF NOT EXISTS completed_replay_reconciliations (
reconciliation_id TEXT PRIMARY KEY, job_id TEXT NOT NULL,
identity_digest TEXT NOT NULL UNIQUE,
source_lease_epoch INTEGER NOT NULL,
task_id TEXT NOT NULL, external_job_id TEXT NOT NULL,
internal_job_id TEXT NOT NULL, repository TEXT NOT NULL,
target_branch TEXT NOT NULL, generation INTEGER NOT NULL,
branch_mode TEXT NOT NULL, payload_digest TEXT NOT NULL,
files_digest TEXT NOT NULL, message_digest TEXT NOT NULL,
commit_oid TEXT NOT NULL, authority_head_oid TEXT NOT NULL,
current_head_oid TEXT NOT NULL, historical_lease_head_oid TEXT NOT NULL,
worker_principal TEXT NOT NULL, worker_lineage TEXT NOT NULL,
durable_revision INTEGER NOT NULL, completion_identity TEXT NOT NULL,
reason TEXT NOT NULL, provenance_json TEXT NOT NULL,
created_at TEXT NOT NULL, FOREIGN KEY(job_id) REFERENCES jobs(job_id)
);
CREATE INDEX IF NOT EXISTS completed_replay_job_idx
ON completed_replay_reconciliations(job_id);
CREATE TRIGGER IF NOT EXISTS completed_replay_no_update
BEFORE UPDATE ON completed_replay_reconciliations
BEGIN SELECT RAISE(ABORT, 'COMPLETED_REPLAY_APPEND_ONLY'); END;
CREATE TRIGGER IF NOT EXISTS completed_replay_no_delete
BEFORE DELETE ON completed_replay_reconciliations
BEGIN SELECT RAISE(ABORT, 'COMPLETED_REPLAY_APPEND_ONLY'); END;
"""
)
columns = {
str(item[1]) for item in connection.execute(
"PRAGMA table_info(completed_replay_reconciliations)"
).fetchall()
}
if columns and "historical_lease_head_oid" not in columns:
connection.execute(
"ALTER TABLE completed_replay_reconciliations ADD COLUMN historical_lease_head_oid TEXT"
)
def _initialize(self) -> None:
try:
with self._connect() as connection:
@@ -273,43 +209,9 @@ class JobStore:
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)
);
CREATE TABLE IF NOT EXISTS completed_replay_reconciliations (
reconciliation_id TEXT PRIMARY KEY, job_id TEXT NOT NULL,
identity_digest TEXT NOT NULL UNIQUE,
source_lease_epoch INTEGER NOT NULL,
task_id TEXT NOT NULL, external_job_id TEXT NOT NULL,
internal_job_id TEXT NOT NULL, repository TEXT NOT NULL,
target_branch TEXT NOT NULL, generation INTEGER NOT NULL,
branch_mode TEXT NOT NULL, payload_digest TEXT NOT NULL,
files_digest TEXT NOT NULL, message_digest TEXT NOT NULL,
commit_oid TEXT NOT NULL, authority_head_oid TEXT NOT NULL,
current_head_oid TEXT NOT NULL, historical_lease_head_oid TEXT NOT NULL,
worker_principal TEXT NOT NULL, worker_lineage TEXT NOT NULL,
durable_revision INTEGER NOT NULL, completion_identity TEXT NOT NULL,
reason TEXT NOT NULL, provenance_json TEXT NOT NULL,
created_at TEXT NOT NULL, FOREIGN KEY(job_id) REFERENCES jobs(job_id)
);
CREATE INDEX IF NOT EXISTS completed_replay_job_idx
ON completed_replay_reconciliations(job_id);
CREATE TRIGGER IF NOT EXISTS completed_replay_no_update
BEFORE UPDATE ON completed_replay_reconciliations
BEGIN SELECT RAISE(ABORT, 'COMPLETED_REPLAY_APPEND_ONLY'); END;
CREATE TRIGGER IF NOT EXISTS completed_replay_no_delete
BEFORE DELETE ON completed_replay_reconciliations
BEGIN SELECT RAISE(ABORT, 'COMPLETED_REPLAY_APPEND_ONLY'); END;
INSERT OR REPLACE INTO metadata(key, value) VALUES('schema_version', '1');
"""
)
columns = {
str(item[1]) for item in connection.execute(
"PRAGMA table_info(completed_replay_reconciliations)"
).fetchall()
}
if columns and "historical_lease_head_oid" not in columns:
connection.execute(
"ALTER TABLE completed_replay_reconciliations "
"ADD COLUMN historical_lease_head_oid TEXT"
)
except sqlite3.Error as exc:
raise ServiceUnavailable("JOB_DB_UNAVAILABLE") from exc
@@ -342,227 +244,6 @@ class JobStore:
raise JobError("JOB_NOT_FOUND")
return dict(row)
@staticmethod
def _completed_replay_identity(envelope: Mapping[str, Any]) -> dict[str, Any]:
required = (
"task_id", "external_job_id", "internal_job_id", "repository",
"target_branch", "generation", "branch_mode", "payload_digest",
"files_digest", "message_digest", "commit_oid", "authority_head_oid",
"historical_lease_head_oid", "worker_principal", "worker_lineage", "source_lease_epoch",
"durable_revision", "completion_identity", "reason", "provenance",
)
if any(key not in envelope for key in required):
raise BindingConflict("COMPLETED_REPLAY_BINDING_INCOMPLETE")
text_fields = (
"task_id", "external_job_id", "internal_job_id", "repository",
"target_branch", "branch_mode", "payload_digest", "files_digest",
"message_digest", "commit_oid", "authority_head_oid", "worker_principal",
"worker_lineage", "historical_lease_head_oid", "completion_identity", "reason",
)
result: dict[str, Any] = {}
for key in text_fields:
value = envelope[key]
if not isinstance(value, str) or not value.strip():
raise BindingConflict("COMPLETED_REPLAY_BINDING_INVALID")
result[key] = value
for key in ("generation", "source_lease_epoch", "durable_revision"):
value = envelope[key]
if isinstance(value, bool) or not isinstance(value, int) or value < 1:
raise BindingConflict("COMPLETED_REPLAY_BINDING_INVALID")
result[key] = value
for key in ("commit_oid", "authority_head_oid", "historical_lease_head_oid"):
if len(result[key]) != 40 or any(ch not in "0123456789abcdef" for ch in result[key]):
raise BindingConflict("COMPLETED_REPLAY_BINDING_INVALID")
for key in ("payload_digest", "files_digest", "message_digest"):
if len(result[key]) != 64 or any(ch not in "0123456789abcdef" for ch in result[key]):
raise BindingConflict("COMPLETED_REPLAY_BINDING_INVALID")
if result["repository"] != "aios/aios" or result["branch_mode"] not in {"CREATE_NEW", "CONTINUE_EXISTING"}:
raise BindingConflict("COMPLETED_REPLAY_BINDING_INVALID")
if not result["target_branch"].startswith("agent/") or result["target_branch"] in {"main", "master"}:
raise BindingConflict("COMPLETED_REPLAY_BINDING_INVALID")
provenance = envelope["provenance"]
if not isinstance(provenance, Mapping) or not provenance:
raise BindingConflict("COMPLETED_REPLAY_PROVENANCE_INVALID")
result["provenance"] = dict(provenance)
if result["commit_oid"] != result["authority_head_oid"]:
raise BindingConflict("COMPLETED_REPLAY_AUTHORITY_CONFLICT")
return result
def append_completed_replay_reconciliation(
self,
job_id: str,
envelope: Mapping[str, Any],
*,
authority_head_oid: str,
) -> dict[str, Any]:
"""Append exact completed-replay evidence without rewriting history."""
identity = self._completed_replay_identity(envelope)
if authority_head_oid != identity["authority_head_oid"]:
raise BindingConflict("COMPLETED_REPLAY_AUTHORITY_CONFLICT")
if identity["internal_job_id"] != job_id:
raise BindingConflict("COMPLETED_REPLAY_INTERNAL_JOB_CONFLICT")
identity_digest = sha256_json(identity)
reconciliation_id = "reconcile-" + identity_digest[:32]
now = utc_now()
try:
with self._connect() as connection:
self._ensure_completed_replay_schema(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 JobState(row["state"]) != JobState.COMPLETED:
raise BindingConflict("COMPLETED_REPLAY_JOB_NOT_COMPLETED")
durable_request = json.loads(row["request_json"])
binding = durable_request.get("binding", durable_request)
exact_binding = {
"task_id": binding.get("task_id") or binding.get("campaign_id"),
"external_job_id": binding.get("external_job_id"),
"internal_job_id": job_id,
"repository": binding.get("repository") or row["repository"],
"target_branch": binding.get("target_branch") or binding.get("branch"),
"generation": binding.get("generation"),
"branch_mode": binding.get("branch_mode"),
"payload_digest": binding.get("payload_digest") or row["payload_digest"],
"files_digest": binding.get("files_digest"),
"message_digest": binding.get("message_digest"),
"worker_principal": binding.get("worker_principal"),
"worker_lineage": binding.get("worker_lineage"),
}
for key, value in exact_binding.items():
if str(value) != str(identity[key]):
raise BindingConflict("COMPLETED_REPLAY_BINDING_CONFLICT")
receipt = json.loads(row["receipt_json"]) if row["receipt_json"] else None
if not isinstance(receipt, Mapping):
raise BindingConflict("COMPLETED_REPLAY_RECEIPT_MISSING")
if receipt.get("commit_oid") != identity["commit_oid"] or receipt.get("authority_oid") != identity["authority_head_oid"]:
raise BindingConflict("COMPLETED_REPLAY_RECEIPT_AUTHORITY_CONFLICT")
completion_identity = sha256_json({
"job_id": row["job_id"],
"revision": int(row["revision"]),
"receipt": receipt,
})
if identity["completion_identity"] != completion_identity:
raise BindingConflict("COMPLETED_REPLAY_COMPLETION_IDENTITY_CONFLICT")
lease_columns = {
str(item[1]) for item in connection.execute("PRAGMA table_info(branch_leases)").fetchall()
}
if "epoch" in lease_columns:
lease = connection.execute(
"SELECT lease_id, state, current_head_oid, generation FROM branch_leases WHERE job_id=? AND epoch=? ORDER BY rowid DESC LIMIT 1",
(job_id, identity["source_lease_epoch"]),
).fetchone()
else:
if identity["source_lease_epoch"] != 1:
raise BindingConflict("COMPLETED_REPLAY_LEASE_EPOCH_UNAVAILABLE")
lease = connection.execute(
"SELECT lease_id, state, current_head_oid, generation FROM branch_leases WHERE job_id=? ORDER BY rowid DESC LIMIT 1",
(job_id,),
).fetchone()
if lease is None:
raise BindingConflict("COMPLETED_REPLAY_LEASE_MISSING")
lease_state = str(lease["state"])
lease_head = str(lease["current_head_oid"])
lease_generation = int(lease["generation"])
event_tables = {
str(item[0]) for item in connection.execute(
"SELECT name FROM sqlite_master WHERE type='table' AND name='branch_lease_events'"
).fetchall()
}
if "branch_lease_events" in event_tables:
event = connection.execute(
"SELECT state, current_head_oid, generation FROM branch_lease_events WHERE lease_id=? ORDER BY rowid DESC LIMIT 1",
(lease["lease_id"],),
).fetchone()
if event is not None:
lease_state = str(event["state"])
lease_head = str(event["current_head_oid"])
lease_generation = int(event["generation"])
if lease_state not in {"ACTIVE", "USED", "EXPIRED"}:
raise BindingConflict("COMPLETED_REPLAY_LEASE_STATE_INVALID")
if lease_generation != identity["generation"]:
raise BindingConflict("COMPLETED_REPLAY_LEASE_GENERATION_CONFLICT")
if len(lease_head) != 40 or any(ch not in "0123456789abcdef" for ch in lease_head):
raise BindingConflict("COMPLETED_REPLAY_LEASE_HEAD_INVALID")
if lease_head != identity["historical_lease_head_oid"]:
raise BindingConflict("COMPLETED_REPLAY_HISTORICAL_LEASE_CONFLICT")
if identity["commit_oid"] == identity["historical_lease_head_oid"]:
raise BindingConflict("COMPLETED_REPLAY_EVIDENCE_INCOHERENT")
existing = connection.execute(
"SELECT * FROM completed_replay_reconciliations WHERE job_id=? ORDER BY created_at ASC",
(job_id,),
).fetchall()
for prior in existing:
if prior["identity_digest"] != identity_digest:
raise BindingConflict("COMPLETED_REPLAY_AMBIGUOUS")
if existing:
result = dict(existing[0])
connection.commit()
return result
provenance_json = json.dumps(identity["provenance"], sort_keys=True, separators=(",", ":"))
connection.execute(
"INSERT INTO completed_replay_reconciliations(reconciliation_id,job_id,identity_digest,source_lease_epoch,task_id,external_job_id,internal_job_id,repository,target_branch,generation,branch_mode,payload_digest,files_digest,message_digest,commit_oid,authority_head_oid,worker_principal,worker_lineage,current_head_oid,historical_lease_head_oid,durable_revision,completion_identity,reason,provenance_json,created_at) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)",
(
reconciliation_id, job_id, identity["identity_digest"] if "identity_digest" in identity else identity_digest, identity["source_lease_epoch"],
identity["task_id"], identity["external_job_id"], identity["internal_job_id"],
identity["repository"], identity["target_branch"], identity["generation"],
identity["branch_mode"], identity["payload_digest"], identity["files_digest"],
identity["message_digest"], identity["commit_oid"], identity["authority_head_oid"],
identity["worker_principal"], identity["worker_lineage"], identity["authority_head_oid"], identity["historical_lease_head_oid"], identity["durable_revision"],
identity["completion_identity"], identity["reason"], provenance_json, now,
),
)
result = dict(connection.execute(
"SELECT * FROM completed_replay_reconciliations WHERE reconciliation_id=?",
(reconciliation_id,),
).fetchone())
connection.commit()
return result
except (JobError, BindingConflict):
raise
except sqlite3.IntegrityError as exc:
try:
with self._connect() as connection:
existing = connection.execute(
"SELECT * FROM completed_replay_reconciliations WHERE identity_digest=?",
(identity_digest,),
).fetchone()
if existing is not None:
return dict(existing)
except sqlite3.Error:
pass
raise BindingConflict("COMPLETED_REPLAY_IDEMPOTENCY_CONFLICT") from exc
except sqlite3.Error as exc:
raise ServiceUnavailable("JOB_DB_UNAVAILABLE") from exc
def completed_replay_evidence(self, job_id: str) -> dict[str, Any] | None:
try:
with self._connect() as connection:
rows = connection.execute(
"SELECT * FROM completed_replay_reconciliations WHERE job_id=? ORDER BY created_at ASC",
(job_id,),
).fetchall()
except sqlite3.Error as exc:
raise ServiceUnavailable("JOB_DB_UNAVAILABLE") from exc
if not rows:
return None
if len({str(row["identity_digest"]) for row in rows}) != 1:
raise BindingConflict("COMPLETED_REPLAY_AMBIGUOUS")
first = dict(rows[0])
immutable_fields = tuple(
key for key in first
if key != "created_at"
)
for row in rows[1:]:
if any(row[key] != first[key] for key in immutable_fields):
raise BindingConflict("COMPLETED_REPLAY_AMBIGUOUS")
if not (
first["commit_oid"] == first["authority_head_oid"] == first["current_head_oid"]
and first["commit_oid"] != first["historical_lease_head_oid"]
):
raise BindingConflict("COMPLETED_REPLAY_EVIDENCE_INCOHERENT")
return first
def _receipt(self, row: Mapping[str, Any], *, duplicate: bool) -> JobReceipt:
state = JobState(row["state"])
if state == JobState.COMPLETED:
@@ -614,11 +295,6 @@ class GitOpsJobController:
"check_context",
"check_state",
"check_description",
"external_job_id",
"files_digest",
"message_digest",
"worker_principal",
"worker_lineage",
)
for field in optional:
value = request.get(field)
+1 -7
View File
@@ -5,8 +5,6 @@ from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
from pathlib import Path
from typing import Any
from release_version import validate_release_version
ROOT = Path(os.getenv('AIOS_GITOPS_ROOT', '/data/aios-gitops'))
WORKSPACE = Path(os.getenv('AIOS_GITOPS_WORKSPACE', str(ROOT / 'workspace')))
DB_PATH = Path(os.getenv('AIOS_GITOPS_DB', str(ROOT / 'runtime' / 'gitops_model.sqlite3')))
@@ -220,11 +218,7 @@ class Handler(BaseHTTPRequestHandler):
a = self.require_write(['owner']);
if not a: return
ver=data.get('version') or data.get('tag')
try:
validate_release_version(ver)
except ValueError:
self.send_json({'error':'version must match VxRxCx'},400)
return
if not re.match(r'^V\d+R\d+C\d+(-[a-zA-Z0-9_.-]+)?$', ver or ''): self.send_json({'error':'version must match VxRxCx'},400); return
msg=data.get('message') or f'release {ver}'; out=git_cmd(['tag','-a',ver,'-m',msg]);
if data.get('push',False): git_cmd(['push','origin',ver])
level='V' if re.match(r'^V[1-9]\d*R0C0', ver) else ('R' if re.match(r'^V\d+R[1-9]\d*C0', ver) else 'C'); exec_sql('INSERT OR REPLACE INTO gitops_versions(version,tag,level,summary,status,created_by,created_at) VALUES(?,?,?,?,?,?,?)',(ver,ver,level,msg,'ready',a['username'],now())); exec_sql('UPDATE gitops_baseline SET active_version=?,active_tag=?,updated_at=? WHERE id=?',(ver,ver,now(),'current')); audit(a['username'],'git_tag',ver,{'level':level}); self.send_json({'status':'tagged','version':ver,'git':out}); return
@@ -9,7 +9,6 @@ import re
from typing import Any, Mapping
from aios_gitops_job_controller import GitOpsJobController, JobStore
from aios_online_agent_execution import GovernedExecutionBinding, project_execution_envelope
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}$")
@@ -54,46 +53,7 @@ class GovernedTaskBootstrap:
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 get_binding(self, task_id: str) -> GovernedTaskBinding:
"""Read the exact durable binding; never synthesize missing lineage."""
binding = self._read_existing(str(task_id))
if binding is None or not binding.job_id:
raise BootstrapError("TASK_EXECUTION_PROJECTION_MISSING")
return binding
def execution_binding(self, binding: GovernedTaskBinding) -> GovernedExecutionBinding:
return GovernedExecutionBinding.from_mapping(
{
"task_id": binding.task_id,
"job_id": binding.job_id,
"repository": binding.repository,
"base_oid": binding.base_oid,
"expected_branch_oid": binding.expected_branch_oid,
"target_branch": binding.target_branch,
"branch_mode": binding.branch_mode,
"generation": binding.generation,
"idempotency_key": binding.idempotency_key,
}
)
def execution_envelope(
self,
task_id: str,
execution_request: Mapping[str, Any] | None = None,
*,
required_capabilities: tuple[str, ...] = (),
expected_job_id: str | None = None,
) -> dict[str, Any]:
binding = self.get_binding(task_id)
if expected_job_id and binding.job_id != expected_job_id:
raise BootstrapError("JOB_BINDING_CONFLICT")
return project_execution_envelope(
self.execution_binding(binding),
execution_request,
required_capabilities=required_capabilities,
)
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)
+6 -216
View File
@@ -4,7 +4,6 @@ import hashlib, hmac, json, os, re, subprocess, sys, time, urllib.parse, urllib.
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
from pathlib import Path
from baseline_authority import BaselineAuthority, BaselineAuthorityError
from silent_release_controller import ReleaseCandidate
from typing import Any
try:
@@ -29,18 +28,11 @@ from orchestrator.action_monitor import ActionMonitor
from silent_release_hardening import (
ApprovalKeyRing,
ApprovalRevocationStore,
BuilderAttestation,
BuilderAttestationKeyRing,
BuilderAttestationVerifier,
CapabilityAuthorizationError,
ReleasePlan,
SagaConflict,
SignedApprovalVerifier,
SignedReleasePlanApproval,
SilentReleaseActionService,
TrustedCandidateBuilderUnavailable,
TrustedCandidateBundle,
TrustedReleasePolicy,
)
try:
from aios_gitops_job_controller import GitOpsJobController, JobError, ServiceUnavailable, JobStore
@@ -90,34 +82,13 @@ SILENT_RELEASE_STATE_ROOT = Path(os.getenv(
'AIOS_SILENT_RELEASE_STATE_ROOT',
'/var/lib/aios-gitops/silent-release',
))
# This immutable staging API can prepare only a shadow candidate. An
# environment value must not turn its HTTP process into a release executor.
SILENT_RELEASE_EXECUTION_ENABLED = False
SILENT_RELEASE_EXECUTION_ENABLED = str(os.getenv(
'AIOS_SILENT_RELEASE_EXECUTION_ENABLED', 'false'
)).lower() in {'1', 'true', 'yes', 'on'}
SILENT_RELEASE_PUBLIC_KEYS = Path(os.getenv(
'AIOS_SILENT_RELEASE_PUBLIC_KEYS',
'/etc/aios-gitops/silent-release-owner-public-keys.json',
))
TRUSTED_BUILDER_ENABLED = str(os.getenv(
'AIOS_TRUSTED_CANDIDATE_BUILDER_ENABLED', 'false'
)).lower() in {'1', 'true', 'yes', 'on'}
TRUSTED_BUILDER_POLICY = Path(os.getenv(
'AIOS_TRUSTED_CANDIDATE_BUILDER_POLICY', ''
))
TRUSTED_BUILDER_CREDENTIALS_DIRECTORY = Path(os.getenv(
'AIOS_TRUSTED_CANDIDATE_BUILDER_CREDENTIALS_DIRECTORY',
os.getenv('CREDENTIALS_DIRECTORY', ''),
))
TRUSTED_BUILDER_SOCKET = Path(os.getenv(
'AIOS_TRUSTED_CANDIDATE_BUILDER_SOCKET',
'/run/aios-trusted-candidate-builder.sock',
))
TRUSTED_BUILDER_PUBLIC_KEYRING = Path(os.getenv(
'AIOS_TRUSTED_CANDIDATE_BUILDER_PUBLIC_KEYRING', ''
))
TRUSTED_RELEASE_POLICY = Path(os.getenv(
'AIOS_SILENT_RELEASE_TRUSTED_POLICY',
str(WS / 'config' / 'gitops' / 'silent-release-policy.json'),
))
monitor = ActionMonitor()
BASE_MODELS = [
@@ -195,86 +166,6 @@ def _file_sha256(path: Path) -> str:
digest.update(chunk)
return digest.hexdigest()
def _trusted_release_policy_from_file(path: Path) -> TrustedReleasePolicy:
payload = json.loads(path.read_text(encoding='utf-8'))
required = {
'services', 'release_roots', 'cutover_links', 'dropin_paths',
'authoritative_git_dirs', 'repository_identities',
}
if not isinstance(payload, dict) or set(payload) != required:
raise ValueError('trusted release policy schema is invalid')
values = {}
for name in required:
raw = payload[name]
if not isinstance(raw, list) or not raw or not all(
isinstance(item, str) and item for item in raw
):
raise ValueError('trusted release policy values are invalid')
values[name] = frozenset(raw)
return TrustedReleasePolicy(**values)
class _HostTrustedCandidateBuilderAdapter:
"""Maps only a complete host-owned silent-release bundle response.
The API never manufactures candidate evidence or a builder signature. A
host builder protocol that returns a different response is therefore
unavailable, rather than silently downgraded to caller-owned evidence.
"""
def __init__(self, client):
self._client = client
def build(self, intent):
import secrets
from aios_trusted_candidate_builder import BuilderError
try:
if not isinstance(intent, dict):
raise TypeError('intent must be a mapping')
version = str(intent.get('version') or 'V2R2C2')
# A createReleaseIntent request is intentionally identity-light.
# Candidate identity is resolved by the host builder policy, not by
# the caller. Explicit release-worker requests may still carry it.
candidate_id = str(intent.get('candidate_id') or '')
source_commit = str(intent.get('source_commit') or '')
if not version or not source_commit:
raise KeyError('version/source_commit')
except (KeyError, TypeError) as exc:
raise CapabilityAuthorizationError(
'trusted builder requires a release intent or worker request'
) from exc
try:
response = self._client.build(
version,
candidate_id,
source_commit,
nonce=secrets.token_hex(32),
)
except BuilderError as exc:
raise TrustedCandidateBuilderUnavailable(
'trusted builder request failed'
) from exc
raw_bundle = response.get('silent_release_bundle')
if not isinstance(raw_bundle, dict):
raise CapabilityAuthorizationError(
'trusted builder response is not a silent release bundle'
)
try:
return TrustedCandidateBundle(
candidate=ReleaseCandidate.from_dict(raw_bundle['candidate']),
release_plan=ReleasePlan.from_dict(raw_bundle['release_plan']),
checks=raw_bundle['checks'],
attestation=BuilderAttestation.from_dict(
raw_bundle['attestation'], require_fingerprint=True
),
)
except (KeyError, TypeError, ValueError) as exc:
raise CapabilityAuthorizationError(
'trusted builder response is invalid'
) from exc
def _build_silent_release_service() -> SilentReleaseActionService:
verifier=None
if SILENT_RELEASE_PUBLIC_KEYS.is_file():
@@ -284,88 +175,12 @@ def _build_silent_release_service() -> SilentReleaseActionService:
SILENT_RELEASE_STATE_ROOT / 'revocations'
),
)
policy_path=TRUSTED_RELEASE_POLICY
policy_path=WS / 'config' / 'gitops' / 'silent-release-policy.json'
policy_sha=_file_sha256(policy_path) if policy_path.is_file() else ''
candidate_builder = None
builder_attestation_verifier = None
trusted_policy = None
builder_readiness_probe = None
if TRUSTED_BUILDER_ENABLED:
# Every path is opt-in. In particular, this API receives its own
# client HMAC credential, never the builder's signing private key.
if not (
TRUSTED_BUILDER_POLICY.is_file()
and TRUSTED_BUILDER_CREDENTIALS_DIRECTORY.is_dir()
and TRUSTED_BUILDER_PUBLIC_KEYRING.is_file()
and TRUSTED_RELEASE_POLICY.is_file()
):
raise CapabilityAuthorizationError(
'trusted builder configuration is incomplete'
)
try:
from aios_trusted_candidate_builder import (
BuilderKeyring,
TrustedCandidateBuilderConfig,
UnixTrustedCandidateBuilderClient,
load_builder_policy,
readiness_probe,
)
builder_policy = load_builder_policy(TRUSTED_BUILDER_POLICY)
builder_config = TrustedCandidateBuilderConfig.from_policy(
builder_policy
)
client_hmac = (
TRUSTED_BUILDER_CREDENTIALS_DIRECTORY / 'builder_hmac'
).read_bytes().strip()
client_keyring = BuilderKeyring(builder_hmac=client_hmac)
client = UnixTrustedCandidateBuilderClient(
builder_config,
client_keyring,
socket_path=TRUSTED_BUILDER_SOCKET,
)
candidate_builder = _HostTrustedCandidateBuilderAdapter(client)
builder_attestation_verifier = BuilderAttestationVerifier(
BuilderAttestationKeyRing.from_file(
TRUSTED_BUILDER_PUBLIC_KEYRING
)
)
trusted_policy = _trusted_release_policy_from_file(
TRUSTED_RELEASE_POLICY
)
def builder_readiness_probe():
if not TRUSTED_BUILDER_SOCKET.exists():
return {'status': 'UNAVAILABLE'}
remote = dict(readiness_probe(client))
if remote.get('policy_sha256') != builder_config.policy_sha256:
return {'status': 'UNAVAILABLE'}
builder_attestation_verifier.keyring.require_active_identity(
key_id=str(remote.get('builder_key_id') or ''),
generation=remote.get('builder_key_generation'),
algorithm=str(remote.get('signature_algorithm') or ''),
purpose=str(remote.get('signature_purpose') or ''),
)
return {
**remote,
'hmac_credential_loaded': True,
'peer_verification_configured': True,
'public_key_trusted': True,
'policy_loaded': True,
}
except Exception as exc:
raise CapabilityAuthorizationError(
'trusted builder configuration is invalid'
) from exc
return SilentReleaseActionService(
SILENT_RELEASE_STATE_ROOT,
execution_enabled=SILENT_RELEASE_EXECUTION_ENABLED,
approval_verifier=verifier,
candidate_builder=candidate_builder,
builder_attestation_verifier=builder_attestation_verifier,
trusted_policy=trusted_policy,
builder_readiness_probe=builder_readiness_probe,
allow_legacy_candidate_input=False,
expected_repository=REPOSITORY_IDENTITY,
policy_sha256=policy_sha,
)
@@ -528,15 +343,6 @@ def publication_request_from_query(q: dict[str, list[str]]) -> PublicationReques
(q['approval_plan_id'])[0],
)
def governed_release_query_payload(q: dict[str, list[str]]) -> dict[str, str]:
request = publication_request_from_query(q)
return {
'version': request.version,
'target_oid': request.target_oid,
'evidence_closure_oid': request.evidence_closure_oid,
'approval_plan_id': request.approval_plan_id,
}
def required_authoritative_git_dir() -> Path:
snapshot = repository_snapshot(
git_dir=AUTHORITATIVE_GIT_DIR,
@@ -660,11 +466,8 @@ class H(BaseHTTPRequestHandler):
q=urllib.parse.parse_qs(u.query)
try:
if path in ('/health','/api/health'):
builder_readiness = silent_release_service().get_builder_readiness()
return self.sendj({
'status': 'ok',
'process_liveness': 'PASS',
'builder_readiness': builder_readiness,
'service': os.getenv('AIOS_GITOPS_MODEL_API_SERVICE', 'aios-gitops-model-api-v2r2c1-staging.service'),
'workspace': str(WS),
'version': 'V2R2C1',
@@ -903,15 +706,8 @@ class H(BaseHTTPRequestHandler):
if path == '/createReleaseCandidate':
a=self.need('release:candidate')
if not a: return
service = silent_release_service()
readiness = service.get_builder_readiness()
if readiness.get('status') != 'READY':
return self.sendj({
'error': 'TRUSTED_BUILDER_UNAVAILABLE',
'builder_readiness': readiness,
}, 503)
return self.sendj(
service.create_release_candidate(data)
silent_release_service().create_release_candidate(data)
)
if path == '/createReleaseIntent':
a=self.need('release:candidate')
@@ -951,13 +747,6 @@ class H(BaseHTTPRequestHandler):
error=redact(e)
monitor.record_failure('do_POST', error, {'path':path})
return self.sendj({'error':error},403)
except TrustedCandidateBuilderUnavailable:
monitor.record_failure(
'do_POST', 'TRUSTED_BUILDER_UNAVAILABLE', {'path': path}
)
return self.sendj(
{'error': 'TRUSTED_BUILDER_UNAVAILABLE'}, 503
)
except SagaConflict as e:
error=redact(e)
monitor.record_failure('do_POST', error, {'path':path})
@@ -1230,3 +1019,4 @@ def main():
print(f'AIOS ChatGPT GitOps adapter listening on {HOST}:{PORT}')
ThreadingHTTPServer((HOST,PORT), H).serve_forever()
if __name__ == '__main__': main()
@@ -1,528 +0,0 @@
"""Canonical governed execution projection for Online Agent Tasks.
The durable governed binding is authoritative. Natural-language task fields and
worker configuration are never allowed to invent repository, ref, generation, or
idempotency coordinates.
"""
from __future__ import annotations
from dataclasses import asdict, dataclass
import hashlib
import json
import re
from pathlib import Path
from typing import Any, Mapping, Sequence
import urllib.error
import urllib.request
EXECUTION_SCHEMA = "aios.online-agent.execution.v2"
PROJECTION_SCHEMA = "aios.online-agent.task-projection.v1"
OID_RE = re.compile(r"^[0-9a-f]{40}$")
TASK_RE = re.compile(r"^[A-Za-z0-9][A-Za-z0-9._:-]{2,179}$")
BRANCH_RE = re.compile(r"^agent/[A-Za-z0-9][A-Za-z0-9._/-]{2,179}$")
KEY_RE = re.compile(r"^[A-Za-z0-9][A-Za-z0-9._:-]{7,159}$")
PROTECTED_PREFIXES = ("main", "master", "refs/", "tags/", "tag/", "release/", "baseline/", "production/")
MUTATION_ACTIONS = frozenset({"deterministic"})
READ_ONLY_ACTIONS = frozenset({"source_capture", "agent_runtime"})
SUPPORTED_ACTIONS = frozenset({"deterministic", "source_capture", "agent_runtime"})
AGENT_RUNTIME_ADAPTERS = frozenset({"hermes", "openclaw"})
MAX_AGENT_RUNTIME_INPUT_BYTES = 64 * 1024
class ExecutionProjectionError(RuntimeError):
"""A canonical fail-closed projection or validation error."""
class OnlineTaskProjectionClient:
"""Official HTTP projection client using only a control credential reference."""
def __init__(self, api_base: str, control_token_path: Path) -> None:
self.api_base = api_base.rstrip("/")
self.control_token_path = Path(control_token_path)
def _token(self) -> str:
path = self.control_token_path
if path.is_symlink() or not path.is_file():
_error("ONLINE_TASK_PROJECTION_CONTROL_UNAVAILABLE")
value = path.read_text(encoding="utf-8").strip()
if len(value) < 32:
_error("ONLINE_TASK_PROJECTION_CONTROL_INVALID")
return value
def create(
self,
projected_task: Mapping[str, Any],
*,
binding: "GovernedExecutionBinding",
) -> dict[str, Any]:
body = dict(projected_task)
payload = _canonical(body).encode("utf-8")
request = urllib.request.Request(
self.api_base + "/v1/tasks",
method="POST",
data=payload,
headers={
"Authorization": "Bearer " + self._token(),
"Content-Type": "application/json",
"Accept": "application/json",
},
)
try:
with urllib.request.urlopen(request, timeout=15) as response:
result = json.loads(response.read().decode("utf-8"))
status = response.status
except urllib.error.HTTPError as exc:
raw = exc.read(8192).decode("utf-8", "replace")
try:
error = json.loads(raw).get("error")
except (AttributeError, json.JSONDecodeError):
error = "HTTP_ERROR"
raise ExecutionProjectionError(f"ONLINE_TASK_PROJECTION_FAILED:{exc.code}:{error}") from exc
except (OSError, urllib.error.URLError, TimeoutError) as exc:
raise ExecutionProjectionError("ONLINE_TASK_PROJECTION_UNAVAILABLE") from exc
if status not in {200, 201} or not isinstance(result, dict):
_error("ONLINE_TASK_PROJECTION_RESPONSE_INVALID")
if result.get("task_id") != binding.task_id or result.get("status") not in {"QUEUED", "VERIFYING", "SUCCEEDED"}:
_error("ONLINE_TASK_PROJECTION_RESPONSE_INVALID")
metadata = result.get("metadata")
if not isinstance(metadata, Mapping):
_error("ONLINE_TASK_PROJECTION_RESPONSE_INVALID")
returned_execution = validate_execution_envelope(
_object(metadata.get("execution"), "ONLINE_TASK_PROJECTION_RESPONSE_INVALID"),
binding=binding,
)
submitted_metadata = _object(body.get("metadata"), "ONLINE_TASK_PROJECTION_REQUEST_INVALID")
submitted_execution = validate_execution_envelope(
_object(submitted_metadata.get("execution"), "ONLINE_TASK_PROJECTION_REQUEST_INVALID"),
binding=binding,
)
if _canonical(returned_execution) != _canonical(submitted_execution):
_error("ONLINE_TASK_PROJECTION_RESPONSE_BINDING_INVALID")
return result
def _error(code: str) -> None:
raise ExecutionProjectionError(code)
def _object(value: object, code: str) -> dict[str, Any]:
if not isinstance(value, Mapping):
_error(code)
return dict(value)
def _string(value: object, code: str) -> str:
if not isinstance(value, str) or not value.strip():
_error(code)
return value.strip()
def _task_id(value: object) -> str:
value = _string(value, "TASK_ID_INVALID")
if TASK_RE.fullmatch(value) is None:
_error("TASK_ID_INVALID")
return value
def _oid(value: object, code: str) -> str:
value = _string(value, code)
if OID_RE.fullmatch(value) is None:
_error(code)
return value
def _branch(value: object) -> str:
value = _string(value, "TARGET_BRANCH_INVALID")
if BRANCH_RE.fullmatch(value) is None or value.startswith(PROTECTED_PREFIXES):
_error("TARGET_BRANCH_INVALID")
return value
def _generation(value: object) -> int:
if not isinstance(value, int) or isinstance(value, bool) or value < 1:
_error("GENERATION_INVALID")
return value
def _canonical(value: object) -> str:
return json.dumps(value, ensure_ascii=False, sort_keys=True, separators=(",", ":"))
def _digest(value: object) -> str:
return hashlib.sha256(_canonical(value).encode("utf-8")).hexdigest()
def _source_signals(value: object) -> tuple[str, ...]:
"""Normalize untrusted natural-language hints before they enter a signed envelope."""
if value is None:
return ()
if not isinstance(value, Sequence) or isinstance(value, (str, bytes)):
_error("SOURCE_CAPTURE_SIGNALS_INVALID")
result: list[str] = []
for item in value:
if not isinstance(item, str):
_error("SOURCE_CAPTURE_SIGNALS_INVALID")
text = " ".join(item.split()).strip()
if not text:
continue
# Projection may accept long task prose, but execution envelopes stay bounded.
text = text[:512].rstrip()
if text not in result:
result.append(text)
if len(result) == 16:
break
return tuple(result)
def _agent_runtime_input(value: object) -> dict[str, Any]:
if not isinstance(value, Mapping):
_error("AGENT_RUNTIME_INPUT_REQUIRED")
raw = dict(value)
allowed = {
"goal", "instructions", "success_criteria", "constraints",
"scope", "trace_id",
}
if set(raw) - allowed:
_error("AGENT_RUNTIME_INPUT_FIELDS_INVALID")
goal = _string(raw.get("goal"), "AGENT_RUNTIME_GOAL_REQUIRED")
instructions = raw.get("instructions", "")
if not isinstance(instructions, str):
_error("AGENT_RUNTIME_INSTRUCTIONS_INVALID")
criteria = raw.get("success_criteria", [])
if not isinstance(criteria, list) or len(criteria) > 32:
_error("AGENT_RUNTIME_SUCCESS_CRITERIA_INVALID")
normalized_criteria: list[dict[str, str]] = []
for item in criteria:
if not isinstance(item, Mapping):
_error("AGENT_RUNTIME_SUCCESS_CRITERIA_INVALID")
criterion_id = _string(item.get("id"), "AGENT_RUNTIME_SUCCESS_CRITERIA_INVALID")
description = _string(
item.get("description"), "AGENT_RUNTIME_SUCCESS_CRITERIA_INVALID"
)
normalized_criteria.append(
{"id": criterion_id[:120], "description": description[:1000]}
)
constraints = raw.get("constraints", {})
if not isinstance(constraints, Mapping):
_error("AGENT_RUNTIME_CONSTRAINTS_INVALID")
sensitive_markers = ("token", "secret", "password", "credential", "authorization", "api_key", "apikey")
for key in constraints:
lowered = str(key).lower()
if any(marker in lowered for marker in sensitive_markers):
_error("AGENT_RUNTIME_SECRET_FIELD_DENY")
scope = raw.get("scope", {})
if not isinstance(scope, Mapping):
_error("AGENT_RUNTIME_SCOPE_INVALID")
scope = {
"scope_type": str(scope.get("scope_type") or "legacy"),
"tenant_id": scope.get("tenant_id"),
"project_id": scope.get("project_id"),
}
if scope["scope_type"] not in {"legacy", "system", "tenant"}:
_error("AGENT_RUNTIME_SCOPE_INVALID")
if scope["scope_type"] == "tenant":
if not isinstance(scope["tenant_id"], str) or not scope["tenant_id"].strip():
_error("AGENT_RUNTIME_SCOPE_INVALID")
elif scope["tenant_id"] not in (None, "") or scope["project_id"] not in (None, ""):
_error("AGENT_RUNTIME_SCOPE_INVALID")
trace_id = str(raw.get("trace_id") or "").strip()
if not trace_id or len(trace_id) > 255:
_error("AGENT_RUNTIME_TRACE_ID_INVALID")
normalized = {
"goal": goal[:12000],
"instructions": instructions[:32000],
"success_criteria": normalized_criteria,
"constraints": dict(constraints),
"scope": scope,
"trace_id": trace_id,
}
if len(_canonical(normalized).encode("utf-8")) > MAX_AGENT_RUNTIME_INPUT_BYTES:
_error("AGENT_RUNTIME_INPUT_TOO_LARGE")
return normalized
def _capabilities(value: object) -> tuple[str, ...]:
if value is None:
return ()
if not isinstance(value, Sequence) or isinstance(value, (str, bytes)):
_error("CAPABILITY_SCOPE_INVALID")
result = tuple(sorted({_string(item, "CAPABILITY_SCOPE_INVALID") for item in value}))
if any(len(item) > 120 for item in result):
_error("CAPABILITY_SCOPE_INVALID")
return result
@dataclass(frozen=True, slots=True)
class GovernedExecutionBinding:
task_id: str
job_id: str
repository: str
base_oid: str
expected_branch_oid: str
target_branch: str
branch_mode: str
generation: int
idempotency_key: str
@classmethod
def from_mapping(cls, value: Mapping[str, Any]) -> "GovernedExecutionBinding":
data = dict(value)
task_id = _task_id(data.get("task_id"))
job_id = _string(data.get("job_id"), "JOB_ID_REQUIRED")
if TASK_RE.fullmatch(job_id) is None:
_error("JOB_ID_INVALID")
repository = _string(data.get("repository"), "REPOSITORY_INVALID")
if repository != "aios/aios":
_error("WRONG_REPOSITORY_ENVELOPE_DENY")
base_oid = _oid(data.get("base_oid"), "BASE_OID_INVALID")
expected = _oid(data.get("expected_branch_oid"), "EXPECTED_BRANCH_OID_INVALID")
target = _branch(data.get("target_branch"))
mode = _string(data.get("branch_mode"), "BRANCH_MODE_MISSING")
if mode not in {"CREATE_NEW", "CONTINUE_EXISTING"}:
_error("BRANCH_MODE_INVALID")
generation = _generation(data.get("generation"))
if mode == "CREATE_NEW" and expected != base_oid:
_error("EXPECTED_BASE_CONFLICT")
key = _string(data.get("idempotency_key"), "IDEMPOTENCY_KEY_INVALID")
if KEY_RE.fullmatch(key) is None:
_error("IDEMPOTENCY_KEY_INVALID")
return cls(task_id, job_id, repository, base_oid, expected, target, mode, generation, key)
def public(self) -> dict[str, Any]:
return asdict(self)
def binding_from_bootstrap(value: Mapping[str, Any]) -> GovernedExecutionBinding:
return GovernedExecutionBinding.from_mapping(value)
def validate_execution_envelope(
value: Mapping[str, Any],
*,
binding: GovernedExecutionBinding | None = None,
worker_capabilities: Sequence[str] | None = None,
) -> dict[str, Any]:
envelope = dict(value)
if envelope.get("schema") != EXECUTION_SCHEMA:
_error("EXECUTION_ENVELOPE_SCHEMA_VALIDATION")
candidate = GovernedExecutionBinding.from_mapping(envelope)
if binding is not None and candidate != binding:
comparisons = (
(candidate.task_id, binding.task_id, "TASK_BINDING_CONFLICT"),
(candidate.job_id, binding.job_id, "JOB_BINDING_CONFLICT"),
(candidate.repository, binding.repository, "WRONG_REPOSITORY_ENVELOPE_DENY"),
(candidate.base_oid, binding.base_oid, "WRONG_BASE_ENVELOPE_DENY"),
(candidate.expected_branch_oid, binding.expected_branch_oid, "WRONG_EXPECTED_HEAD_ENVELOPE_DENY"),
(candidate.target_branch, binding.target_branch, "WRONG_BRANCH_ENVELOPE_DENY"),
(candidate.generation, binding.generation, "WRONG_GENERATION_ENVELOPE_DENY"),
(candidate.branch_mode, binding.branch_mode, "BRANCH_MODE_BINDING_CONFLICT"),
(candidate.idempotency_key, binding.idempotency_key, "IDEMPOTENCY_BINDING_CONFLICT"),
)
for actual, expected, code in comparisons:
if actual != expected:
_error(code)
action = _string(envelope.get("action"), "EXECUTION_ACTION_MISSING")
if action not in SUPPORTED_ACTIONS:
_error("EXECUTION_ACTION_UNSUPPORTED")
capability_scope = _capabilities(envelope.get("capability_scope"))
if worker_capabilities is not None and not set(capability_scope).issubset(set(worker_capabilities)):
_error("CAPABILITY_MISMATCH_DENY")
workspace = _object(envelope.get("workspace"), "WORKSPACE_POLICY_REQUIRED")
if workspace.get("authority_git_dir") != "/data/aios-gitops/repo.git" or workspace.get("read_only_repository") is not True:
_error("WORKSPACE_POLICY_DENY")
governance = _object(envelope.get("governance"), "GOVERNANCE_POLICY_REQUIRED")
governed_coordinates = (
(governance.get("task_id"), candidate.task_id, "TASK_BINDING_CONFLICT"),
(governance.get("job_id"), candidate.job_id, "JOB_BINDING_CONFLICT"),
(governance.get("idempotency_key"), candidate.idempotency_key, "IDEMPOTENCY_BINDING_CONFLICT"),
)
for actual_coordinate, expected_coordinate, code in governed_coordinates:
if actual_coordinate != expected_coordinate:
_error(code)
commit_key = _string(governance.get("commit_idempotency_key"), "COMMIT_IDEMPOTENCY_KEY_INVALID")
if KEY_RE.fullmatch(commit_key) is None:
_error("COMMIT_IDEMPOTENCY_KEY_INVALID")
if governance.get("writer_lease") is not (action in MUTATION_ACTIONS):
_error("WRITER_LEASE_POLICY_DENY")
for field in ("main_write", "production_write", "tag_write", "baseline_write", "force_push", "protected_ref_bypass"):
if governance.get(field, False) is not False:
_error("POLICY_DENY")
changes = envelope.get("change_set")
verification = envelope.get("verification")
if not isinstance(changes, list) or not isinstance(verification, list):
_error("EXECUTION_OPERATIONS_INVALID")
if action == "source_capture":
if changes or verification:
_error("SOURCE_CAPTURE_MUTATION_DENY")
signals = list(_source_signals(envelope.get("source_signals") or []))
if envelope.get("source_signals") != signals:
_error("SOURCE_CAPTURE_SIGNALS_INVALID")
if envelope.get("runtime_input") not in (None, {}):
_error("SOURCE_CAPTURE_RUNTIME_INPUT_DENY")
elif action == "agent_runtime":
if changes or verification:
_error("AGENT_RUNTIME_MUTATION_DENY")
adapter = str(envelope.get("adapter") or "")
if adapter not in AGENT_RUNTIME_ADAPTERS:
_error("AGENT_RUNTIME_ADAPTER_INVALID")
runtime_input = _agent_runtime_input(envelope.get("runtime_input"))
if envelope.get("runtime_input") != runtime_input:
_error("AGENT_RUNTIME_INPUT_NOT_CANONICAL")
required_runtime_capability = f"agent.runtime.{adapter}"
if required_runtime_capability not in capability_scope:
_error("AGENT_RUNTIME_CAPABILITY_REQUIRED")
if envelope.get("source_signals") not in (None, []):
_error("AGENT_RUNTIME_SOURCE_SIGNALS_DENY")
elif not changes:
_error("CHANGE_SET_INVALID")
declared = str(envelope.get("execution_digest") or "")
material = {key: item for key, item in envelope.items() if key != "execution_digest"}
actual = _digest(material)
if declared != actual:
_error("EXECUTION_DIGEST_INVALID")
return {**material, "execution_digest": actual}
def project_execution_envelope(
binding: GovernedExecutionBinding,
execution_request: Mapping[str, Any] | None = None,
*,
required_capabilities: Sequence[str] = (),
) -> dict[str, Any]:
request = dict(execution_request or {})
action = str(request.get("action") or "source_capture")
if action not in SUPPORTED_ACTIONS:
_error("EXECUTION_ACTION_UNSUPPORTED")
capabilities = _capabilities(request.get("capability_scope", required_capabilities))
governance = {
"task_id": binding.task_id,
"job_id": binding.job_id,
"idempotency_key": binding.idempotency_key,
"commit_idempotency_key": str(request.get("commit_idempotency_key") or f"{binding.task_id}-commit-g{binding.generation}"),
"writer_lease": action in MUTATION_ACTIONS,
"main_write": False,
"production_write": False,
"tag_write": False,
"baseline_write": False,
"force_push": False,
"protected_ref_bypass": False,
}
supplied_governance = request.get("governance")
if supplied_governance is not None:
governance.update(_object(supplied_governance, "GOVERNANCE_POLICY_REQUIRED"))
material: dict[str, Any] = {
"schema": EXECUTION_SCHEMA,
**binding.public(),
"action": action,
"adapter": str(
request.get("adapter")
or ("deterministic" if action == "deterministic" else "source_capture")
),
"capability_scope": list(capabilities),
"change_set": list(request.get("change_set") or []),
"verification": list(request.get("verification") or []),
"source_signals": list(_source_signals(request.get("source_signals") or [])) if action == "source_capture" else [],
"runtime_input": _agent_runtime_input(request.get("runtime_input")) if action == "agent_runtime" else {},
"governance": governance,
"workspace": {
"authority_git_dir": "/data/aios-gitops/repo.git",
"read_only_repository": True,
"cleanup_policy": "always",
},
}
envelope = {**material, "execution_digest": _digest(material)}
return validate_execution_envelope(envelope, binding=binding)
def project_online_task(
binding: GovernedExecutionBinding,
task_request: Mapping[str, Any],
execution_request: Mapping[str, Any] | None = None,
) -> dict[str, Any]:
request = dict(task_request)
required = _capabilities(request.get("required_capabilities"))
execution_value = dict(execution_request or {})
if str(execution_value.get("action") or "source_capture") == "agent_runtime":
adapter = str(execution_value.get("adapter") or "")
if adapter not in AGENT_RUNTIME_ADAPTERS:
_error("AGENT_RUNTIME_ADAPTER_INVALID")
runtime_input = execution_value.get("runtime_input")
tenant_id = request.get("tenant_id")
project_id = request.get("project_id")
declared_scope = str(
request.get("scope_type")
or ("tenant" if isinstance(tenant_id, str) and tenant_id.strip() else "legacy")
)
if runtime_input is None:
runtime_input = {
"goal": request.get("goal"),
"instructions": request.get("instructions") or "",
"success_criteria": request.get("success_criteria") or [],
"constraints": request.get("constraints") or {},
}
elif not isinstance(runtime_input, Mapping):
_error("AGENT_RUNTIME_INPUT_REQUIRED")
runtime_input = dict(runtime_input)
# Scope/trace are authoritative Task identity, never caller-selected runtime hints.
runtime_input["scope"] = {
"scope_type": declared_scope,
"tenant_id": tenant_id,
"project_id": project_id,
}
runtime_input["trace_id"] = str(
request.get("trace_id")
or request.get("run_id")
or binding.task_id
)
execution_value["runtime_input"] = runtime_input
execution = project_execution_envelope(
binding,
execution_value,
required_capabilities=required,
)
metadata = dict(request.get("metadata") or {})
if "execution" in metadata:
validate_execution_envelope(_object(metadata["execution"], "EXECUTION_ENVELOPE_INVALID"), binding=binding)
if metadata["execution"] != execution:
_error("EXECUTION_PROJECTION_CONFLICT")
metadata["execution"] = execution
metadata["execution_projection"] = {
"schema": PROJECTION_SCHEMA,
"task_id": binding.task_id,
"job_id": binding.job_id,
"execution_digest": execution["execution_digest"],
}
return {
**request,
"task_id": binding.task_id,
"metadata": metadata,
"required_capabilities": list(required),
"idempotency_key": str(request.get("idempotency_key") or f"online-task:{binding.idempotency_key}"),
}
def resolve_task_execution(
task: Mapping[str, Any],
binding: GovernedExecutionBinding,
*,
worker_capabilities: Sequence[str] | None = None,
) -> tuple[dict[str, Any], str]:
metadata = task.get("metadata")
if isinstance(metadata, Mapping) and "execution" in metadata:
value = validate_execution_envelope(
_object(metadata["execution"], "EXECUTION_ENVELOPE_INVALID"),
binding=binding,
worker_capabilities=worker_capabilities,
)
return value, "persisted"
projected = project_execution_envelope(
binding,
{"action": "source_capture", "capability_scope": task.get("required_capabilities") or []},
required_capabilities=task.get("required_capabilities") or [],
)
validate_execution_envelope(projected, binding=binding, worker_capabilities=worker_capabilities)
return projected, "recovered_projection"
@@ -1,148 +0,0 @@
"""Fail-closed signed deterministic execution amendment for R7."""
from datetime import datetime, timezone
import hashlib, json, sqlite3
from pathlib import Path
from typing import Mapping
from aios_task_terminal_recovery import DeterministicPlan, RecoveryError, canonical_json
AMENDMENT_SCHEMA="aios.deterministic-execution-amendment.v1"
APPLY_SCHEMA="aios.deterministic-execution-amendment-apply.v1"
NAMESPACE="aios-owner-approval-v1"
EVENT="TASK_RECOVERY_EXECUTION_AMENDMENT_APPLIED"
APPLY_STATES=frozenset({"FAILED", "QUEUED"})
def _err(code): raise RecoveryError(code)
def _sha(v): return hashlib.sha256(canonical_json(v).encode()).hexdigest()
def amendment_body(x):
return {k:v for k,v in x.items() if k not in {"signature","amendment_id"}}
def amendment_id(x): return "sha256:"+_sha(amendment_body(x))
def signed_payload(x):
return canonical_json({"amendment_id":x["amendment_id"],**amendment_body(x)}).encode()
def parse_amendment(raw):
x=dict(raw)
required={"schema","amendment_id","nonce","task_id","job_id","generation",
"repository","branch","expected_branch_oid","recovery_epoch","decision_id",
"plan_id","plan_sha256","expected_apply_status","expected_apply_revision",
"expected_apply_attempt","expected_current_capabilities","envelope",
"owner_identity","key_id","issued_at","not_before","expires_at",
"one_time_use","signature"}
if set(x)<required or x.get("schema")!=AMENDMENT_SCHEMA: _err("AMENDMENT_SCHEMA_INVALID")
if x["expected_apply_status"] not in APPLY_STATES: _err("AMENDMENT_APPLY_STATUS_INVALID")
if x.get("one_time_use") is not True: _err("AMENDMENT_ONE_TIME_REQUIRED")
if not isinstance(x["envelope"],Mapping): _err("AMENDMENT_ENVELOPE_INVALID")
if not isinstance(x["expected_current_capabilities"],list): _err("AMENDMENT_CAPABILITIES_INVALID")
for k in ("generation","expected_apply_revision","expected_apply_attempt"):
if isinstance(x[k],bool) or not isinstance(x[k],int) or x[k]<0: _err("AMENDMENT_CAS_INVALID")
for k in ("issued_at","not_before","expires_at"):
try:d=datetime.fromisoformat(str(x[k]).replace("Z","+00:00"))
except ValueError as e: raise RecoveryError("AMENDMENT_TIME_INVALID") from e
if d.tzinfo is None:_err("AMENDMENT_TIME_INVALID")
x[k]=d.astimezone(timezone.utc).isoformat().replace("+00:00","Z")
x["expected_current_capabilities"]=sorted(set(x["expected_current_capabilities"]))
x["envelope"]=dict(x["envelope"])
return x
def _validate_envelope(env,plan,x):
try:
from aios_online_agent_execution import GovernedExecutionBinding,validate_execution_envelope
binding=GovernedExecutionBinding.from_mapping(env)
e=dict(validate_execution_envelope(env,binding=binding,worker_capabilities=plan.required_capabilities))
except Exception as z: raise RecoveryError("AMENDMENT_EXECUTION_ENVELOPE_INVALID") from z
if e.get("action")!="deterministic" or e.get("adapter")!="deterministic":_err("AMENDMENT_EXECUTION_ACTION_INVALID")
if tuple(sorted(e.get("capability_scope") or ()))!=plan.required_capabilities:_err("AMENDMENT_EXECUTION_CAPABILITIES_MISMATCH")
if not e.get("change_set") or not e.get("verification"):_err("AMENDMENT_EXECUTION_OPERATIONS_REQUIRED")
if e.get("runtime_input") not in (None,{}) or e.get("source_signals") not in (None,[]):_err("AMENDMENT_STALE_RUNTIME_FIELDS_FORBIDDEN")
for k,w in (("task_id",x["task_id"]),("job_id",x["job_id"]),("repository",x["repository"]),
("generation",x["generation"]),("target_branch",x["branch"]),
("expected_branch_oid",x["expected_branch_oid"]),("base_oid",x["expected_branch_oid"]),
("plan_id",x["plan_id"]),("plan_sha256",x["plan_sha256"]),("recovery_epoch",x["recovery_epoch"])):
if e.get(k)!=w:_err("AMENDMENT_EXECUTION_BINDING_MISMATCH")
g=e.get("governance") or {}
if g.get("writer_lease") is not True:_err("AMENDMENT_WRITER_LEASE_REQUIRED")
if any(g.get(k,False) for k in ("main_write","production_write","tag_write","baseline_write","force_push","protected_ref_bypass")):_err("AMENDMENT_PROTECTED_WRITE_FORBIDDEN")
return e
class DeterministicExecutionAmendmentController:
def __init__(self,db_path,verifier,*,clock=None,envelope_validator=None):
self.db_path=Path(db_path); self.verifier=verifier
self.clock=clock or (lambda:datetime.now(timezone.utc)); self._init()
def _db(self):
c=sqlite3.connect(self.db_path,timeout=10,isolation_level=None); c.row_factory=sqlite3.Row
c.execute("PRAGMA busy_timeout=10000"); return c
def _now(self): return self.clock().astimezone(timezone.utc).isoformat().replace("+00:00","Z")
def _init(self):
with self._db() as c:
c.execute("DROP TRIGGER IF EXISTS aios_r7_recovery_capability_projection")
c.executescript("""
CREATE TABLE IF NOT EXISTS deterministic_execution_amendments(
amendment_id TEXT PRIMARY KEY,nonce TEXT UNIQUE NOT NULL,task_id TEXT NOT NULL,
decision_id TEXT NOT NULL,plan_id TEXT NOT NULL,plan_sha256 TEXT NOT NULL,
envelope_digest TEXT NOT NULL,envelope_json TEXT NOT NULL,payload_json TEXT NOT NULL,
created_at TEXT NOT NULL,UNIQUE(task_id,decision_id));
DROP TRIGGER IF EXISTS aios_r7_recovery_requires_executable_amendment;
CREATE TRIGGER aios_r7_recovery_requires_executable_amendment
BEFORE UPDATE OF status ON tasks
WHEN OLD.status='FAILED' AND NEW.status='QUEUED' BEGIN
SELECT CASE WHEN NOT EXISTS(
SELECT 1 FROM deterministic_execution_amendments a
WHERE a.task_id=OLD.task_id
AND a.decision_id=json_extract(NEW.metadata_json,'$.recovery.decision_id')
AND a.amendment_id=json_extract(OLD.metadata_json,'$.recovery.execution_amendment_id')
AND a.envelope_digest=json_extract(OLD.metadata_json,'$.execution.execution_digest')
) THEN RAISE(ABORT,'SIGNED_EXECUTION_AMENDMENT_REQUIRED') END;
END;""")
def _plan(self,c,x):
r=c.execute("SELECT * FROM deterministic_plans WHERE plan_id=?",(x["plan_id"],)).fetchone()
if r is None or r["plan_sha256"]!=x["plan_sha256"]:_err("AMENDMENT_PLAN_NOT_FOUND_OR_MISMATCH")
p=DeterministicPlan.from_dict(json.loads(r["payload_json"]))
if (p.task_id,p.job_id,p.generation,p.repository,p.branch,p.expected_branch_oid,p.recovery_epoch)!=(x["task_id"],x["job_id"],x["generation"],x["repository"],x["branch"],x["expected_branch_oid"],x["recovery_epoch"]):_err("AMENDMENT_PLAN_BINDING_MISMATCH")
return p
def persist(self,raw):
x=parse_amendment(raw)
if x["amendment_id"]!=amendment_id(x):_err("AMENDMENT_ID_DIGEST_MISMATCH")
now=self._now()
if now<x["not_before"] or now>=x["expires_at"]:_err("AMENDMENT_EXPIRED_OR_NOT_YET_VALID")
if not self.verifier.verify(namespace=NAMESPACE,payload=signed_payload(x),signature=x["signature"],key_id=x["key_id"]):_err("AMENDMENT_SIGNATURE_INVALID")
with self._db() as c:
c.execute("BEGIN IMMEDIATE"); e=_validate_envelope(x["envelope"],self._plan(c,x),x)
ej=canonical_json(e); ed=e["execution_digest"]; stored=dict(x); stored["signature"]="<present>"
old=c.execute("SELECT * FROM deterministic_execution_amendments WHERE amendment_id=?",(x["amendment_id"],)).fetchone()
if old:
if old["envelope_json"]!=ej:_err("AMENDMENT_ID_REUSE_MISMATCH")
c.commit(); return {"status":"PRESENT","amendment_id":x["amendment_id"],"task_id":x["task_id"],"decision_id":x["decision_id"],"plan_id":x["plan_id"],"execution_digest":ed}
if c.execute("SELECT 1 FROM deterministic_execution_amendments WHERE nonce=? OR (task_id=? AND decision_id=?)",(x["nonce"],x["task_id"],x["decision_id"])).fetchone():_err("AMENDMENT_REPLAY_OR_DECISION_REUSE")
c.execute("INSERT INTO deterministic_execution_amendments VALUES(?,?,?,?,?,?,?,?,?,?)",(x["amendment_id"],x["nonce"],x["task_id"],x["decision_id"],x["plan_id"],x["plan_sha256"],ed,ej,canonical_json(stored),now)); c.commit()
return {"status":"PERSISTED","amendment_id":x["amendment_id"],"task_id":x["task_id"],"decision_id":x["decision_id"],"plan_id":x["plan_id"],"execution_digest":ed}
def apply(self,raw):
if raw.get("schema")!=APPLY_SCHEMA:_err("AMENDMENT_APPLY_SCHEMA_INVALID")
aid=raw.get("amendment_id"); tid=raw.get("task_id")
with self._db() as c:
c.execute("BEGIN IMMEDIATE"); a=c.execute("SELECT * FROM deterministic_execution_amendments WHERE amendment_id=? AND task_id=?",(aid,tid)).fetchone()
if a is None:_err("AMENDMENT_NOT_FOUND")
x=json.loads(a["payload_json"]); d=json.loads(a["envelope_json"])
t=c.execute("SELECT * FROM tasks WHERE task_id=?",(tid,)).fetchone()
if t is None:_err("TASK_NOT_FOUND")
m=json.loads(t["metadata_json"] or "{}"); cp=tuple(sorted(json.loads(t["required_capabilities_json"] or "[]"))); dc=tuple(sorted(d.get("capability_scope") or ()))
rec=m.get("recovery"); rec=dict(rec) if isinstance(rec,Mapping) else {}
old=m.get("execution")
if isinstance(old,Mapping) and old.get("execution_digest")==a["envelope_digest"] and cp==dc and rec.get("execution_amendment_id")==aid:
c.commit(); return {"status":"PRESENT","task_id":tid,"amendment_id":aid,"execution_digest":a["envelope_digest"],"revision":int(t["revision"]),"attempt":int(t["attempt"])}
if t["status"]!=x["expected_apply_status"] or int(t["revision"])!=x["expected_apply_revision"] or int(t["attempt"])!=x["expected_apply_attempt"] or cp!=tuple(x["expected_current_capabilities"]) or t["claimed_by"] is not None or t["lease_token_hash"] is not None:_err("AMENDMENT_APPLY_TASK_CAS_MISMATCH")
if t["status"]=="QUEUED" and rec.get("decision_id")!=x["decision_id"]:_err("AMENDMENT_DECISION_BINDING_MISMATCH")
m["execution"]=d; m["execution_projection"]={"schema":"aios.online-agent.task-projection.v1","task_id":tid,"job_id":d.get("job_id"),"execution_digest":a["envelope_digest"]}
rec["execution_amendment_id"]=aid; m["recovery"]=rec; now=self._now()
q=c.execute("""UPDATE tasks SET required_capabilities_json=?,metadata_json=?,revision=revision+1,updated_at=? WHERE task_id=? AND status=? AND revision=? AND attempt=? AND claimed_by IS NULL AND lease_token_hash IS NULL""",(canonical_json(list(dc)),canonical_json(m),now,tid,x["expected_apply_status"],x["expected_apply_revision"],x["expected_apply_attempt"]))
if q.rowcount!=1:_err("AMENDMENT_APPLY_TASK_CAS_FAILED")
c.execute("INSERT INTO events(task_id,agent_id,event_type,payload_json,created_at) VALUES(?,?,?,?,?)",(tid,None,EVENT,canonical_json({"amendment_id":aid,"decision_id":x["decision_id"],"plan_id":x["plan_id"],"execution_digest":a["envelope_digest"],"required_capabilities":list(dc)}),now)); c.commit()
return {"status":"APPLIED","task_id":tid,"amendment_id":aid,"decision_id":x["decision_id"],"plan_id":x["plan_id"],"execution_digest":a["envelope_digest"],"required_capabilities":list(dc),"revision":x["expected_apply_revision"]+1,"attempt":x["expected_apply_attempt"]}
@@ -1,798 +0,0 @@
#!/usr/bin/env python3
"""Fail-closed AF_UNIX builder for server-owned trusted candidate evidence."""
from __future__ import annotations
import argparse
import base64
import hashlib
import hmac
import json
import os
import secrets
import socket
import stat
import struct
import subprocess
import time
from datetime import datetime, timezone
from dataclasses import dataclass
from pathlib import Path
from typing import Any, Callable, Mapping, Protocol
class BuilderError(RuntimeError):
"""A stable, non-sensitive error code returned by the builder boundary."""
def _canonical_json(value: Mapping[str, Any]) -> bytes:
return json.dumps(value, sort_keys=True, separators=(",", ":"), ensure_ascii=True).encode("utf-8")
def _sha256(*parts: str) -> str:
return hashlib.sha256("\0".join(parts).encode("utf-8")).hexdigest()
@dataclass(frozen=True)
class PeerIdentity:
pid: int
uid: int
gid: int
class DomainPurposeSigner(Protocol):
"""A signer deliberately limited to the builder's one domain purpose."""
@property
def key_id(self) -> str:
...
@property
def key_generation(self) -> int:
...
def sign_for_purpose(self, purpose: str, payload: bytes) -> bytes:
...
class CredentialPathEd25519Signer:
"""Reads a systemd-mounted credential path; never exports or logs key bytes."""
def __init__(self, credential_path: Path, *, key_id: str, key_generation: int, allowed_purpose: str):
self._credential_path = Path(credential_path)
self._key_id = str(key_id)
self._key_generation = key_generation
self._allowed_purpose = str(allowed_purpose)
if not self._key_id or not isinstance(key_generation, int) or key_generation < 1:
raise BuilderError("SIGNING_IDENTITY_INVALID")
if not self._credential_path.is_file():
raise BuilderError("SIGNING_CREDENTIAL_UNAVAILABLE")
try:
from cryptography.hazmat.primitives.serialization import load_pem_private_key
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
self._private_key = load_pem_private_key(self._credential_path.read_bytes(), password=None)
if not isinstance(self._private_key, Ed25519PrivateKey):
raise TypeError("signing key is not Ed25519")
except Exception as exc: # do not expose parse/key material details
raise BuilderError("SIGNING_CREDENTIAL_INVALID") from exc
@property
def key_id(self) -> str:
return self._key_id
@property
def key_generation(self) -> int:
return self._key_generation
def sign_for_purpose(self, purpose: str, payload: bytes) -> bytes:
if purpose != self._allowed_purpose:
raise BuilderError("SIGNING_PURPOSE_DENIED")
return self._private_key.sign(payload)
@dataclass(frozen=True)
class BuilderKeyring:
builder_hmac: bytes
signing_private_key_path: Path | None = None
def __post_init__(self) -> None:
if len(self.builder_hmac) < 32:
raise BuilderError("BUILDER_HMAC_INVALID")
@classmethod
def from_credentials_directory(cls, credentials_directory: Path) -> "BuilderKeyring":
root = Path(credentials_directory)
hmac_path = root / "builder_hmac"
signing_path = root / "signing_private_key"
try:
secret = hmac_path.read_bytes().strip()
except OSError as exc:
raise BuilderError("BUILDER_HMAC_UNAVAILABLE") from exc
if not signing_path.is_file():
raise BuilderError("SIGNING_CREDENTIAL_UNAVAILABLE")
return cls(builder_hmac=secret, signing_private_key_path=signing_path)
@dataclass(frozen=True)
class TrustedCandidateBuilderConfig:
policy_schema: str
repository: str
request_schema: str
response_schema: str
operation: str
readiness_operation: str
maximum_request_age_seconds: int
request_timeout_seconds: int
allowed_peer_uid: int
allowed_peer_cgroups: tuple[str, ...]
allowed_peer_executable_prefix: str
allowed_peer_runtime_prefix: str
allowed_peer_runtime_suffix: str
required_checks: tuple[str, ...]
evidence_layout: Mapping[str, str]
recipes: Mapping[str, Mapping[str, str]]
governed_recipes: Mapping[str, Mapping[str, str]]
policy_sha256: str
@classmethod
def from_policy(cls, policy: Mapping[str, Any]) -> "TrustedCandidateBuilderConfig":
required = {
"schema", "repository", "request_schema", "response_schema", "operation",
"readiness_operation", "maximum_request_age_seconds", "request_timeout_seconds",
"allowed_peer_uid", "allowed_peer_cgroups", "allowed_peer_executable_prefix",
"allowed_peer_runtime_prefix", "allowed_peer_runtime_suffix",
"required_checks", "evidence_layout", "recipes", "governed_recipes",
}
if set(policy) != required or policy.get("schema") != "aios.trusted-candidate-builder-policy.v1":
raise BuilderError("POLICY_SCHEMA_INVALID")
recipes = policy.get("recipes")
if not isinstance(recipes, Mapping) or set(recipes) != {"V2R2C2", "V2R2C3"}:
raise BuilderError("POLICY_RECIPES_INVALID")
c2, c3 = recipes["V2R2C2"], recipes["V2R2C3"]
if not isinstance(c2, Mapping) or set(c2) != {"mode", "task_id", "candidate_id", "base_oid", "source_commit", "version", "purpose"} or c2.get("mode") != "fixed":
raise BuilderError("POLICY_V2R2C2_INVALID")
if not isinstance(c3, Mapping) or set(c3) != {"mode", "derives_from", "version", "purpose"} or c3.get("mode") != "governed-derived-only" or c3.get("derives_from") != "V2R2C2":
raise BuilderError("POLICY_V2R2C3_INVALID")
if (
policy["repository"] != "aios/aios"
or policy["operation"] != "BuildTrustedCandidateV1"
or policy["readiness_operation"] != "ProbeTrustedCandidateBuilderV1"
):
raise BuilderError("POLICY_SCOPE_INVALID")
if not isinstance(policy["maximum_request_age_seconds"], int) or not 1 <= policy["maximum_request_age_seconds"] <= 300:
raise BuilderError("POLICY_FRESHNESS_INVALID")
if not isinstance(policy["request_timeout_seconds"], int) or not 1 <= policy["request_timeout_seconds"] <= 120:
raise BuilderError("POLICY_TIMEOUT_INVALID")
if not isinstance(policy["allowed_peer_uid"], int) or policy["allowed_peer_uid"] != 1000:
raise BuilderError("POLICY_PEER_UID_INVALID")
cgroups, checks = policy["allowed_peer_cgroups"], policy["required_checks"]
if not isinstance(cgroups, list) or not cgroups or not all(isinstance(value, str) and value.startswith("/") for value in cgroups):
raise BuilderError("POLICY_CGROUPS_INVALID")
for field in (
"allowed_peer_executable_prefix",
"allowed_peer_runtime_prefix",
"allowed_peer_runtime_suffix",
):
if not isinstance(policy[field], str) or not policy[field].startswith("/"):
raise BuilderError("POLICY_PROCESS_IDENTITY_INVALID")
if not isinstance(checks, list) or not checks or len(set(checks)) != len(checks) or not all(isinstance(value, str) for value in checks):
raise BuilderError("POLICY_CHECKS_INVALID")
layout = policy["evidence_layout"]
evidence_names = {
"artifact", "manifest", "package_closure", "tests", "canary",
"gitea", "rollback", "policy", "secret_scan",
}
if (
not isinstance(layout, Mapping)
or set(layout) != evidence_names
or not all(
isinstance(value, str)
and value.startswith("{version}/")
and ".." not in Path(value).parts
and not Path(value).is_absolute()
for value in layout.values()
)
):
raise BuilderError("POLICY_EVIDENCE_LAYOUT_INVALID")
return cls(
policy_schema=str(policy["schema"]), repository=str(policy["repository"]),
request_schema=str(policy["request_schema"]), response_schema=str(policy["response_schema"]),
operation=str(policy["operation"]), readiness_operation=str(policy["readiness_operation"]),
maximum_request_age_seconds=policy["maximum_request_age_seconds"],
request_timeout_seconds=policy["request_timeout_seconds"],
allowed_peer_uid=policy["allowed_peer_uid"], allowed_peer_cgroups=tuple(cgroups),
allowed_peer_executable_prefix=policy["allowed_peer_executable_prefix"],
allowed_peer_runtime_prefix=policy["allowed_peer_runtime_prefix"],
allowed_peer_runtime_suffix=policy["allowed_peer_runtime_suffix"],
required_checks=tuple(checks), evidence_layout=dict(layout),
recipes={name: dict(value) for name, value in recipes.items()},
governed_recipes={name: dict(value) for name, value in policy["governed_recipes"].items()} if isinstance(policy["governed_recipes"], Mapping) else _invalid_governed_recipes(),
policy_sha256=hashlib.sha256(_canonical_json(dict(policy))).hexdigest(),
)
def fixed_recipe(self, name: str) -> Mapping[str, str]:
recipe = self.recipes.get(name)
if recipe is None:
raise BuilderError("RECIPE_UNKNOWN")
if recipe["mode"] == "governed-derived-only":
derived = self.governed_recipes.get(name)
required = {"mode", "task_id", "candidate_id", "base_oid", "source_commit", "version", "purpose", "derived_from_v2r2c2_attestation", "builder_engineering_oid"}
if not isinstance(derived, Mapping) or set(derived) != required or derived.get("mode") != "governed-derived" or derived.get("version") != name or derived.get("purpose") != recipe.get("purpose"):
raise BuilderError("GOVERNED_V2R2C3_DERIVATION_REQUIRED")
for name in ("base_oid", "source_commit", "builder_engineering_oid"):
value = derived.get(name)
if not isinstance(value, str) or len(value) != 40 or any(char not in "0123456789abcdef" for char in value.lower()):
raise BuilderError("GOVERNED_V2R2C3_DERIVATION_REQUIRED")
return dict(derived)
return recipe
def resolve_recipe(self, version: str, candidate_id: str | None, source_commit: str) -> Mapping[str, str]:
recipe = self.fixed_recipe(version)
# Intent-only callers do not choose a candidate identity. The
# policy-owned recipe supplies it; explicit worker requests are
# still checked against the same immutable recipe.
effective_candidate_id = str(candidate_id or recipe.get("candidate_id") or "")
if {"version": recipe.get("version"), "candidate_id": recipe.get("candidate_id"), "source_commit": recipe.get("source_commit")} != {"version": version, "candidate_id": effective_candidate_id, "source_commit": source_commit}:
raise BuilderError("REQUEST_RELEASE_BINDING_INVALID")
return recipe
def _invalid_governed_recipes() -> Mapping[str, Mapping[str, str]]:
raise BuilderError("POLICY_GOVERNED_RECIPES_INVALID")
def load_builder_policy(path: Path) -> Mapping[str, Any]:
try:
decoded = json.loads(Path(path).read_text(encoding="utf-8"))
except (OSError, json.JSONDecodeError) as exc:
raise BuilderError("POLICY_UNAVAILABLE") from exc
if not isinstance(decoded, Mapping):
raise BuilderError("POLICY_SCHEMA_INVALID")
TrustedCandidateBuilderConfig.from_policy(decoded)
return decoded
class ServerOwnedEvidenceSource:
"""Recomputes a fixed on-host evidence closure without caller-owned paths."""
def __init__(
self,
config: TrustedCandidateBuilderConfig,
authority_git_dir: Path,
evidence_root: Path,
*,
expected_owner_uid: int | None = 0,
git_runner: Callable[..., Any] = subprocess.run,
):
self._config = config
self._authority_git_dir = Path(authority_git_dir)
self._evidence_root = Path(evidence_root)
self._expected_owner_uid = expected_owner_uid
self._git_runner = git_runner
def _regular_path(self, relative: str) -> Path:
root = self._evidence_root.resolve(strict=True)
candidate = self._evidence_root / relative
current = self._evidence_root
for part in Path(relative).parts:
current = current / part
if current.is_symlink():
raise BuilderError("COLLECTOR_SYMLINK_DENIED")
try:
resolved = candidate.resolve(strict=True)
resolved.relative_to(root)
metadata = resolved.stat(follow_symlinks=False)
except (OSError, ValueError) as exc:
raise BuilderError("COLLECTOR_EVIDENCE_UNAVAILABLE") from exc
if not stat.S_ISREG(metadata.st_mode):
raise BuilderError("COLLECTOR_SPECIAL_FILE_DENIED")
if os.name != "nt" and metadata.st_mode & 0o022:
raise BuilderError("COLLECTOR_EVIDENCE_MODE_DENIED")
if (
os.name != "nt"
and
self._expected_owner_uid is not None
and metadata.st_uid != self._expected_owner_uid
):
raise BuilderError("COLLECTOR_EVIDENCE_OWNER_DENIED")
return resolved
def _digest(self, path: Path) -> str:
digest = hashlib.sha256()
with path.open("rb") as handle:
for chunk in iter(lambda: handle.read(1024 * 1024), b""):
digest.update(chunk)
return digest.hexdigest()
def _json(self, path: Path) -> Mapping[str, Any]:
try:
if path.stat().st_size > 2 * 1024 * 1024:
raise BuilderError("COLLECTOR_EVIDENCE_SIZE_INVALID")
value = json.loads(path.read_text(encoding="utf-8"))
except (OSError, UnicodeDecodeError, json.JSONDecodeError) as exc:
raise BuilderError("COLLECTOR_EVIDENCE_INVALID") from exc
if not isinstance(value, Mapping):
raise BuilderError("COLLECTOR_EVIDENCE_INVALID")
return value
def __call__(self, recipe_name: str, recipe: Mapping[str, str]) -> Mapping[str, Any]:
try:
tree_result = self._git_runner(
["git", f"--git-dir={self._authority_git_dir}", "rev-parse", f"{recipe['source_commit']}^{{tree}}"],
check=True, capture_output=True, text=True, timeout=15,
env={"PATH": os.environ.get("PATH", "")},
)
ancestry_result = self._git_runner(
["git", f"--git-dir={self._authority_git_dir}", "merge-base", "--is-ancestor", recipe["base_oid"], recipe["source_commit"]],
check=False, capture_output=True, text=True, timeout=15,
env={"PATH": os.environ.get("PATH", "")},
)
tree_oid = tree_result.stdout.strip().lower()
except (OSError, subprocess.SubprocessError) as exc:
raise BuilderError("COLLECTOR_EVIDENCE_UNAVAILABLE") from exc
if getattr(ancestry_result, "returncode", 1) != 0:
raise BuilderError("COLLECTOR_GIT_MISMATCH")
receipt = self._json(self._regular_path(f"{recipe_name}/receipt.json"))
if set(receipt) != {"schema", "version", "candidate", "release_plan"}:
raise BuilderError("COLLECTOR_EVIDENCE_INVALID")
if (
receipt.get("schema") != "aios.trusted-candidate-builder.evidence.v1"
or receipt.get("version") != recipe_name
):
raise BuilderError("COLLECTOR_EVIDENCE_INVALID")
candidate = receipt.get("candidate")
release_plan = receipt.get("release_plan")
if not isinstance(candidate, Mapping) or not isinstance(release_plan, Mapping):
raise BuilderError("COLLECTOR_EVIDENCE_INVALID")
if (
candidate.get("candidate_id") != recipe["candidate_id"]
or candidate.get("source_commit") != recipe["source_commit"]
or candidate.get("tree_oid") != tree_oid
):
raise BuilderError("COLLECTOR_GIT_MISMATCH")
git_consistency = candidate.get("git_consistency")
if (
not isinstance(git_consistency, Mapping)
or git_consistency.get("consistent") is not True
or git_consistency.get("source_commit") != recipe["source_commit"]
or git_consistency.get("tree_oid") != tree_oid
or git_consistency.get("repository") != self._config.repository
):
raise BuilderError("COLLECTOR_GIT_MISMATCH")
paths = {
name: self._regular_path(template.format(version=recipe_name))
for name, template in self._config.evidence_layout.items()
}
deployment = release_plan.get("deployment")
if not isinstance(deployment, Mapping):
raise BuilderError("COLLECTOR_EVIDENCE_INVALID")
digests = {name: self._digest(path) for name, path in paths.items()}
if (
digests["artifact"] != candidate.get("artifact_sha256")
or digests["manifest"] != candidate.get("manifest_sha256")
or digests["package_closure"] != deployment.get("package_closure_sha256")
or digests["policy"] != release_plan.get("policy_sha256")
or digests["policy"] != deployment.get("policy_sha256")
):
raise BuilderError("COLLECTOR_DIGEST_MISMATCH")
tests = self._json(paths["tests"])
canary = self._json(paths["canary"])
gitea = self._json(paths["gitea"])
rollback = self._json(paths["rollback"])
secret_scan = self._json(paths["secret_scan"])
if tests != candidate.get("tests") or tests.get("passed") is not True:
raise BuilderError("COLLECTOR_TESTS_MISMATCH")
if canary != candidate.get("canary") or canary.get("passed") is not True:
raise BuilderError("COLLECTOR_CANARY_MISMATCH")
if (
digests["tests"] != hashlib.sha256(_canonical_json(dict(tests))).hexdigest()
or digests["canary"] != hashlib.sha256(_canonical_json(dict(canary))).hexdigest()
):
raise BuilderError("COLLECTOR_EVIDENCE_CANONICALIZATION_MISMATCH")
if (
gitea != candidate.get("gitea_consistency")
or gitea.get("consistent") is not True
or gitea.get("repository") != self._config.repository
or gitea.get("source_commit") != recipe["source_commit"]
or gitea.get("tree_oid") != tree_oid
or gitea.get("remote_ref_oid") != recipe["source_commit"]
):
raise BuilderError("COLLECTOR_GITEA_MISMATCH")
if (
candidate.get("rollback_ready") is not True
or rollback.get("ready") is not True
or rollback.get("source_commit") != recipe["source_commit"]
or rollback.get("rollback_sha256") != deployment.get("rollback_sha256")
):
raise BuilderError("COLLECTOR_ROLLBACK_MISMATCH")
if (
digests["gitea"] != hashlib.sha256(_canonical_json(dict(gitea))).hexdigest()
or digests["rollback"] != hashlib.sha256(_canonical_json(dict(rollback))).hexdigest()
):
raise BuilderError("COLLECTOR_EVIDENCE_CANONICALIZATION_MISMATCH")
if (
secret_scan.get("passed") is not True
or secret_scan.get("artifact_sha256") != candidate.get("artifact_sha256")
):
raise BuilderError("COLLECTOR_SECRET_SCAN_MISMATCH")
checks = {name: True for name in self._config.required_checks}
return {
"candidate": dict(candidate),
"release_plan": dict(release_plan),
"checks": checks,
"evidence_digests": digests,
}
class DeterministicEvidenceCollector:
"""Validates a server-owned source; request fields never influence collection."""
_BUNDLE_FIELDS = {"candidate", "release_plan", "checks", "evidence_digests"}
def __init__(self, config: TrustedCandidateBuilderConfig, *, evidence_source: Callable[[str, Mapping[str, str]], Mapping[str, Any]] | None = None):
self._config = config
self._evidence_source = evidence_source
def collect(self, recipe_name: str, recipe: Mapping[str, str]) -> Mapping[str, Any]:
if self._evidence_source is None:
raise BuilderError("COLLECTOR_UNCONFIGURED")
bundle = self._evidence_source(recipe_name, recipe)
if not isinstance(bundle, Mapping) or set(bundle) != self._BUNDLE_FIELDS:
raise BuilderError("COLLECTOR_EVIDENCE_INVALID")
raw_checks = bundle.get("checks")
if (
not isinstance(raw_checks, Mapping)
or set(raw_checks) != set(self._config.required_checks)
or any(raw_checks.get(name) is not True for name in self._config.required_checks)
):
raise BuilderError("COLLECTOR_CHECKS_INCOMPLETE")
evidence_digests = bundle.get("evidence_digests")
if (
not isinstance(evidence_digests, Mapping)
or set(evidence_digests) != set(self._config.evidence_layout)
or any(
not isinstance(value, str)
or len(value) != 64
or any(char not in "0123456789abcdef" for char in value.lower())
for value in evidence_digests.values()
)
):
raise BuilderError("COLLECTOR_EVIDENCE_DIGESTS_INVALID")
try:
from silent_release_controller import ReleaseCandidate
from silent_release_hardening import ReleasePlan, TrustedCandidateBundle
candidate = ReleaseCandidate.from_dict(dict(bundle["candidate"]))
release_plan = ReleasePlan.from_dict(dict(bundle["release_plan"]))
checks = dict(raw_checks)
TrustedCandidateBundle(candidate, release_plan, checks).validate()
except (ImportError, KeyError, TypeError, ValueError, RuntimeError) as exc:
raise BuilderError("COLLECTOR_EVIDENCE_INVALID") from exc
if candidate.source_commit != recipe["source_commit"] or candidate.candidate_id != recipe["candidate_id"]:
raise BuilderError("COLLECTOR_SOURCE_MISMATCH")
return {
"candidate": candidate.to_dict(),
"release_plan": release_plan.to_dict(),
"checks": checks,
"evidence_digests": dict(evidence_digests),
}
def _extract_peer_identity(connection: socket.socket) -> PeerIdentity:
if not hasattr(socket, "SO_PEERCRED"):
raise BuilderError("SO_PEERCRED_UNAVAILABLE")
raw = connection.getsockopt(socket.SOL_SOCKET, socket.SO_PEERCRED, struct.calcsize("3i"))
if len(raw) != struct.calcsize("3i"):
raise BuilderError("SO_PEERCRED_INVALID")
return PeerIdentity(*struct.unpack("3i", raw))
def _resolve_cgroups(pid: int) -> tuple[str, ...]:
try:
return tuple(
line.rsplit(":", 1)[1].strip()
for line in Path(f"/proc/{pid}/cgroup").read_text(encoding="utf-8").splitlines()
if ":" in line and line.rsplit(":", 1)[1].strip().startswith("/")
)
except OSError as exc:
raise BuilderError("PEER_CGROUP_UNAVAILABLE") from exc
def _resolve_process_identity(pid: int) -> tuple[str, tuple[str, ...]]:
try:
executable = os.path.realpath(f"/proc/{pid}/exe")
argv = tuple(
item.decode("utf-8", "strict")
for item in Path(f"/proc/{pid}/cmdline").read_bytes().split(b"\0")
if item
)
except (OSError, UnicodeDecodeError) as exc:
raise BuilderError("PEER_PROCESS_UNAVAILABLE") from exc
if not executable or not argv:
raise BuilderError("PEER_PROCESS_UNAVAILABLE")
return executable, argv
class TrustedCandidateBuilder:
def __init__(
self, config: TrustedCandidateBuilderConfig, keyring: BuilderKeyring, *,
collector: DeterministicEvidenceCollector, signer: DomainPurposeSigner,
peer_identity_resolver: Callable[[socket.socket], PeerIdentity] = _extract_peer_identity,
cgroup_resolver: Callable[[int], tuple[str, ...] | str] = _resolve_cgroups,
process_identity_resolver: Callable[[int], tuple[str, tuple[str, ...]]] = _resolve_process_identity,
clock: Callable[[], float] = time.time, nonce_ledger_path: Path | None = None,
) -> None:
self.config, self.keyring, self.collector, self.signer = config, keyring, collector, signer
self.peer_identity_resolver, self.cgroup_resolver, self.clock = peer_identity_resolver, cgroup_resolver, clock
self.process_identity_resolver = process_identity_resolver
self._nonce_ledger_path = Path(nonce_ledger_path) if nonce_ledger_path else None
self._nonce_ledger: dict[str, tuple[bytes, dict[str, Any]]] = self._load_nonce_ledger()
def _load_nonce_ledger(self) -> dict[str, tuple[bytes, dict[str, Any]]]:
if self._nonce_ledger_path is None or not self._nonce_ledger_path.exists():
return {}
if self._nonce_ledger_path.is_symlink():
raise BuilderError("NONCE_LEDGER_INVALID")
try:
raw = json.loads(self._nonce_ledger_path.read_text(encoding="utf-8"))
return {nonce: (bytes.fromhex(value["request_sha256"]), dict(value["result"])) for nonce, value in raw.items()}
except (OSError, ValueError, TypeError, KeyError, json.JSONDecodeError) as exc:
raise BuilderError("NONCE_LEDGER_INVALID") from exc
def _persist_nonce_ledger(self) -> None:
if self._nonce_ledger_path is None:
return
try:
self._nonce_ledger_path.parent.mkdir(mode=0o700, parents=True, exist_ok=True)
temporary = self._nonce_ledger_path.with_suffix(".tmp")
payload = {nonce: {"request_sha256": digest.hex(), "result": result} for nonce, (digest, result) in self._nonce_ledger.items()}
with temporary.open("wb") as handle:
handle.write(_canonical_json(payload))
handle.flush()
os.fsync(handle.fileno())
os.chmod(temporary, 0o600)
os.replace(temporary, self._nonce_ledger_path)
if os.name != "nt":
descriptor = os.open(self._nonce_ledger_path.parent, os.O_RDONLY)
try:
os.fsync(descriptor)
finally:
os.close(descriptor)
except OSError as exc:
raise BuilderError("NONCE_LEDGER_PERSIST_FAILED") from exc
def _verify_peer(self, connection: socket.socket) -> None:
peer = self.peer_identity_resolver(connection)
if peer.uid != self.config.allowed_peer_uid or peer.pid <= 0:
raise BuilderError("PEER_IDENTITY_DENIED")
resolved = self.cgroup_resolver(peer.pid)
cgroups = (resolved,) if isinstance(resolved, str) else tuple(resolved)
if not cgroups or not any(item in self.config.allowed_peer_cgroups for item in cgroups):
raise BuilderError("PEER_CGROUP_DENIED")
executable, argv = self.process_identity_resolver(peer.pid)
if not executable.startswith(self.config.allowed_peer_executable_prefix):
raise BuilderError("PEER_PROCESS_DENIED")
runtime_arguments = [
value
for value in argv
if value.startswith(self.config.allowed_peer_runtime_prefix)
and value.endswith(self.config.allowed_peer_runtime_suffix)
]
if len(runtime_arguments) != 1:
raise BuilderError("PEER_PROCESS_DENIED")
def handle_request(self, request: Mapping[str, Any], *, connection: socket.socket) -> dict[str, Any]:
if not isinstance(request, Mapping):
raise BuilderError("REQUEST_SCHEMA_INVALID")
expected_keys = {"schema", "operation", "recipe", "repository", "task_id", "candidate_id", "base_oid", "source_commit", "version", "purpose", "issued_at", "nonce", "hmac"}
if (
set(request) != expected_keys
or request.get("schema") != self.config.request_schema
or request.get("operation") not in {
self.config.operation,
self.config.readiness_operation,
}
):
raise BuilderError("REQUEST_SCHEMA_INVALID")
supplied_mac = request.get("hmac")
unsigned = {key: value for key, value in request.items() if key != "hmac"}
if not isinstance(supplied_mac, str) or not hmac.compare_digest(hmac.new(self.keyring.builder_hmac, _canonical_json(unsigned), hashlib.sha256).hexdigest(), supplied_mac):
raise BuilderError("REQUEST_HMAC_INVALID")
issued_at, nonce = request.get("issued_at"), request.get("nonce")
if not isinstance(issued_at, int) or not isinstance(nonce, str) or len(nonce) < 16 or len(nonce) > 256:
raise BuilderError("REQUEST_SCHEMA_INVALID")
self._verify_peer(connection)
request_bytes = _canonical_json(dict(request))
replay = self._nonce_ledger.get(nonce)
if replay is not None:
if hmac.compare_digest(replay[0], hashlib.sha256(request_bytes).digest()):
return dict(replay[1])
raise BuilderError("REPLAY_CONFLICT")
if abs(int(self.clock()) - issued_at) > self.config.maximum_request_age_seconds:
raise BuilderError("REQUEST_STALE")
recipe_name = request.get("recipe")
if not isinstance(recipe_name, str):
raise BuilderError("REQUEST_SCHEMA_INVALID")
recipe = self.config.resolve_recipe(str(request.get("version") or ""), str(request.get("candidate_id") or ""), str(request.get("source_commit") or ""))
if recipe_name != recipe.get("version"):
raise BuilderError("REQUEST_RELEASE_BINDING_INVALID")
bound = {"repository": self.config.repository, "task_id": recipe["task_id"], "candidate_id": recipe["candidate_id"], "base_oid": recipe["base_oid"], "source_commit": recipe["source_commit"], "version": recipe["version"], "purpose": recipe["purpose"]}
if {key: request.get(key) for key in bound} != bound:
raise BuilderError("REQUEST_SCOPE_INVALID")
bundle = dict(self.collector.collect(recipe_name, recipe))
candidate = bundle["candidate"]
release_plan = bundle["release_plan"]
checks = bundle.get("checks")
evidence_digests = bundle["evidence_digests"]
if not isinstance(checks, Mapping) or set(checks) != set(self.config.required_checks) or not all(value is True for value in checks.values()):
raise BuilderError("COLLECTOR_CHECKS_INCOMPLETE")
if request["operation"] == self.config.readiness_operation:
result = {
"schema": self.config.response_schema,
"status": "READY",
"repository": self.config.repository,
"recipe": recipe_name,
"candidate_id": recipe["candidate_id"],
"source_commit": recipe["source_commit"],
"builder_key_id": self.signer.key_id,
"builder_key_generation": self.signer.key_generation,
"signature_algorithm": "Ed25519",
"signature_purpose": "AIOS_TRUSTED_RELEASE_BUILDER",
"policy_sha256": self.config.policy_sha256,
"collector_ready": True,
"signer_ready": True,
}
if len(self._nonce_ledger) >= 2048:
self._nonce_ledger.pop(next(iter(self._nonce_ledger)))
self._nonce_ledger[nonce] = (
hashlib.sha256(request_bytes).digest(),
dict(result),
)
self._persist_nonce_ledger()
return result
issued = datetime.now(timezone.utc).replace(microsecond=0).isoformat().replace("+00:00", "Z")
attestation = {
"builder_attestation_id": "builder-attestation-" + _sha256(recipe_name, recipe["task_id"], candidate["artifact_sha256"])[:24],
"builder_key_id": self.signer.key_id,
"builder_key_generation": self.signer.key_generation,
"signature_algorithm": "Ed25519",
"signature_purpose": "AIOS_TRUSTED_RELEASE_BUILDER",
"source_commit": candidate["source_commit"], "tree_oid": candidate["tree_oid"],
"artifact_sha256": candidate["artifact_sha256"], "manifest_sha256": candidate["manifest_sha256"],
"package_closure_sha256": release_plan["deployment"]["package_closure_sha256"],
"test_evidence_sha256": evidence_digests["tests"],
"canary_evidence_sha256": evidence_digests["canary"],
"gitea_evidence_sha256": evidence_digests["gitea"],
"rollback_evidence_sha256": evidence_digests["rollback"],
"policy_evidence_sha256": evidence_digests["policy"],
"secret_scan_evidence_sha256": evidence_digests["secret_scan"],
"issued_at": issued, "trust_domain": "aios.trusted-builder.attestation.v1",
}
canonical_attestation = _canonical_json(attestation)
fingerprint = hashlib.sha256(canonical_attestation).hexdigest()
signature = base64.b64encode(self.signer.sign_for_purpose("AIOS_TRUSTED_RELEASE_BUILDER", canonical_attestation)).decode("ascii")
result = {
"schema": self.config.response_schema, "status": "BUILT", "recipe": recipe_name,
"repository": self.config.repository, "task_id": recipe["task_id"], "base_oid": recipe["base_oid"],
"purpose": recipe["purpose"], "checks": dict(checks),
"evidence": {"candidate_id": candidate["candidate_id"], "source_commit": candidate["source_commit"], "tree_oid": candidate["tree_oid"], "artifact_sha256": candidate["artifact_sha256"], "manifest_sha256": candidate["manifest_sha256"], "package_closure_sha256": release_plan["deployment"]["package_closure_sha256"], "test_evidence_sha256": attestation["test_evidence_sha256"], "canary_evidence_sha256": attestation["canary_evidence_sha256"]},
"attestation": {**attestation, "fingerprint": fingerprint, "signature": signature},
}
result["silent_release_bundle"] = {"candidate": candidate, "release_plan": release_plan, "checks": dict(checks), "attestation": result["attestation"]}
if len(self._nonce_ledger) >= 2048:
self._nonce_ledger.pop(next(iter(self._nonce_ledger)))
self._nonce_ledger[nonce] = (hashlib.sha256(request_bytes).digest(), dict(result))
self._persist_nonce_ledger()
return result
def serve_forever(self, listener: socket.socket) -> None:
while True:
connection, _ = listener.accept()
with connection:
try:
raw = connection.makefile("rb").readline(65537)
if not raw or len(raw) > 65536:
raise BuilderError("REQUEST_SIZE_INVALID")
result = self.handle_request(json.loads(raw), connection=connection)
except (BuilderError, UnicodeDecodeError, json.JSONDecodeError):
result = {"schema": self.config.response_schema, "status": "REJECTED", "error_code": "REQUEST_REJECTED"}
connection.sendall(_canonical_json(result) + b"\n")
class UnixTrustedCandidateBuilderClient:
def __init__(self, config: TrustedCandidateBuilderConfig, keyring: BuilderKeyring, *, socket_path: Path | None = None, clock: Callable[[], float] = time.time) -> None:
self.config, self.keyring, self.socket_path, self.clock = config, keyring, socket_path, clock
def create_request(self, version: str, candidate_id: str | None, source_commit: str, *, nonce: str, operation: str | None = None) -> dict[str, Any]:
selected = self.config.resolve_recipe(version, candidate_id, source_commit)
selected_operation = operation or self.config.operation
if selected_operation not in {self.config.operation, self.config.readiness_operation}:
raise BuilderError("REQUEST_OPERATION_INVALID")
request = {"schema": self.config.request_schema, "operation": selected_operation, "recipe": version, "repository": self.config.repository, "task_id": selected["task_id"], "candidate_id": selected["candidate_id"], "base_oid": selected["base_oid"], "source_commit": selected["source_commit"], "version": selected["version"], "purpose": selected["purpose"], "issued_at": int(self.clock()), "nonce": nonce}
request["hmac"] = hmac.new(self.keyring.builder_hmac, _canonical_json(request), hashlib.sha256).hexdigest()
return request
def _exchange(self, request: Mapping[str, Any]) -> Mapping[str, Any]:
if self.socket_path is None:
raise BuilderError("BUILDER_SOCKET_UNCONFIGURED")
try:
with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as connection:
connection.settimeout(self.config.request_timeout_seconds)
connection.connect(os.fspath(self.socket_path))
connection.sendall(_canonical_json(dict(request)) + b"\n")
raw = connection.makefile("rb").readline(65537)
except (OSError, TimeoutError) as exc:
raise BuilderError("BUILDER_TRANSPORT_UNAVAILABLE") from exc
try:
result = json.loads(raw)
except (UnicodeDecodeError, json.JSONDecodeError) as exc:
raise BuilderError("BUILDER_RESPONSE_INVALID") from exc
if not isinstance(result, Mapping) or result.get("schema") != self.config.response_schema:
raise BuilderError("BUILDER_RESPONSE_INVALID")
return result
def build(self, version: str, candidate_id: str | None, source_commit: str, *, nonce: str) -> Mapping[str, Any]:
result = self._exchange(
self.create_request(version, candidate_id, source_commit, nonce=nonce)
)
if result.get("status") != "BUILT":
raise BuilderError("BUILDER_REQUEST_REJECTED")
return result
def probe(self, *, nonce: str | None = None) -> Mapping[str, Any]:
recipe = self.config.fixed_recipe("V2R2C2")
result = self._exchange(
self.create_request(
"V2R2C2",
recipe["candidate_id"],
recipe["source_commit"],
nonce=nonce or secrets.token_hex(32),
operation=self.config.readiness_operation,
)
)
if result.get("status") != "READY":
raise BuilderError("BUILDER_NOT_READY")
return result
def readiness_probe(client: UnixTrustedCandidateBuilderClient) -> Mapping[str, Any]:
result = dict(client.probe())
required_true = {"collector_ready", "signer_ready"}
if any(result.get(name) is not True for name in required_true):
raise BuilderError("BUILDER_NOT_READY")
return result
def _main() -> None:
parser = argparse.ArgumentParser()
parser.add_argument("--socket-fd", required=True, type=int)
parser.add_argument("--policy", required=True, type=Path)
arguments = parser.parse_args()
config = TrustedCandidateBuilderConfig.from_policy(load_builder_policy(arguments.policy))
credentials = os.environ.get("CREDENTIALS_DIRECTORY")
if not credentials:
raise SystemExit("CREDENTIALS_DIRECTORY is required")
keyring = BuilderKeyring.from_credentials_directory(Path(credentials))
signer = CredentialPathEd25519Signer(
keyring.signing_private_key_path,
key_id=os.environ["AIOS_TRUSTED_BUILDER_KEY_ID"],
key_generation=int(os.environ["AIOS_TRUSTED_BUILDER_KEY_GENERATION"]),
allowed_purpose="AIOS_TRUSTED_RELEASE_BUILDER",
)
listener = socket.fromfd(arguments.socket_fd, socket.AF_UNIX, socket.SOCK_STREAM)
collector = DeterministicEvidenceCollector(
config,
evidence_source=ServerOwnedEvidenceSource(
config,
Path(os.environ["AIOS_TRUSTED_BUILDER_GIT_DIR"]),
Path(os.environ["AIOS_TRUSTED_BUILDER_EVIDENCE_ROOT"]),
),
)
TrustedCandidateBuilder(config, keyring, collector=collector, signer=signer, nonce_ledger_path=Path(os.environ["AIOS_TRUSTED_BUILDER_NONCE_LEDGER"])).serve_forever(listener)
if __name__ == "__main__":
_main()
+21 -56
View File
@@ -31,8 +31,6 @@ import time
from pathlib import Path
from typing import Any, Mapping, Sequence
from trusted_release_execution_plan import PlanError, TrustedReleasePlan
OID_RE = re.compile(r"^[0-9a-f]{40}$")
SHA_RE = re.compile(r"^[0-9a-f]{64}$")
PLAN_RE = re.compile(r"^sha256:[0-9a-f]{64}$")
@@ -75,24 +73,6 @@ class Binding:
production_port: int
repository_identity: str = "aios/aios"
candidate_id: str | None = None
release_plan_sha256: str | None = None
@classmethod
def from_trusted_plan(cls, plan: TrustedReleasePlan) -> "Binding":
value = plan.value
adapter = value["adapter_policy"]
binding = cls(
version=str(value["version"]), target_oid=str(value["source_commit"]),
evidence_closure_oid=str(value["evidence_closure_oid"]), artifact_sha256=str(value["artifact_sha256"]),
approval_plan_id=str(value["approval_plan_id"]), artifact_path=str(value["artifact"]["path"]),
artifact_size=int(value["artifact"]["size"]), production_service=str(adapter["service"]),
production_port=int(adapter["port"]), candidate_id=str(value["candidate_id"]),
release_plan_sha256=plan.release_plan_sha256,
)
plan.assert_binding(dataclasses.asdict(binding))
return binding
def __post_init__(self) -> None:
if not self.version or not OID_RE.fullmatch(self.target_oid):
raise ValueError("invalid release target binding")
@@ -127,8 +107,6 @@ class Binding:
"production_service": self.production_service,
"production_port": self.production_port,
"repository_identity": self.repository_identity,
"candidate_id": self.candidate_id,
"release_plan_sha256": self.release_plan_sha256,
}
return hashlib.sha256(canonical_json(payload)).hexdigest()
@@ -148,12 +126,9 @@ class Binding:
"production_service": self.production_service,
"production_port": self.production_port,
"repository_identity": self.repository_identity,
"candidate_id": self.candidate_id,
"release_plan_sha256": self.release_plan_sha256,
}
def canonical_json(value: object) -> bytes:
return (json.dumps(value, sort_keys=True, separators=(",", ":")) + "\n").encode("utf-8")
@@ -307,12 +282,6 @@ def verify_authorization(receipt: Mapping[str, Any], binding: Binding) -> dict[s
"artifact_sha256": binding.artifact_sha256,
"approval_plan_id": binding.approval_plan_id,
}
if binding.candidate_id is not None:
expected["candidate_id"] = binding.candidate_id
if binding.release_plan_sha256 is not None:
expected["release_plan_sha256"] = binding.release_plan_sha256
if receipt.get("candidate_binding_sha256") is None:
raise ManualOwnerReviewRequired("Owner authorization missing candidate binding")
for key, value in expected.items():
if receipt.get(key) != value:
raise ManualOwnerReviewRequired(f"Owner authorization mismatch: {key}")
@@ -529,14 +498,7 @@ def execute(
deploy_command: Sequence[str],
registry_command: Sequence[str],
baseline_command: Sequence[str],
release_plan_path: Path | None = None,
) -> dict[str, Any]:
plan = TrustedReleasePlan.load(release_plan_path) if release_plan_path is not None else None
if plan is not None:
try:
plan.assert_binding(dataclasses.asdict(binding))
except PlanError as exc:
raise ReleaseBlocked(f"trusted release plan mismatch: {exc}") from exc
authorization = verify_authorization(secure_json(authorization_path), binding)
lineage = verify_git_lineage(repository, binding)
artifact = verify_artifact(binding)
@@ -629,18 +591,17 @@ def execute(
def parser() -> argparse.ArgumentParser:
p = argparse.ArgumentParser()
p.add_argument("--authorization", required=True, type=Path)
p.add_argument("--release-plan", type=Path)
p.add_argument("--repository", required=True, type=Path)
p.add_argument("--state-root", required=True, type=Path)
p.add_argument("--artifact", type=Path)
p.add_argument("--artifact-size", type=int)
p.add_argument("--version")
p.add_argument("--target-oid")
p.add_argument("--evidence-closure-oid")
p.add_argument("--artifact-sha256")
p.add_argument("--approval-plan-id")
p.add_argument("--production-service")
p.add_argument("--production-port", type=int)
p.add_argument("--artifact", required=True, type=Path)
p.add_argument("--artifact-size", required=True, type=int)
p.add_argument("--version", required=True)
p.add_argument("--target-oid", required=True)
p.add_argument("--evidence-closure-oid", required=True)
p.add_argument("--artifact-sha256", required=True)
p.add_argument("--approval-plan-id", required=True)
p.add_argument("--production-service", required=True)
p.add_argument("--production-port", required=True, type=int)
p.add_argument("--repository-identity", default="aios/aios")
p.add_argument("--tag-adapter", required=True, nargs="+")
p.add_argument("--deploy-adapter", required=True, nargs="+")
@@ -651,13 +612,18 @@ def parser() -> argparse.ArgumentParser:
def main() -> int:
args = parser().parse_args()
plan = TrustedReleasePlan.load(args.release_plan) if args.release_plan else None
if plan is not None:
binding = Binding.from_trusted_plan(plan)
else:
legacy=(args.artifact,args.artifact_size,args.version,args.target_oid,args.evidence_closure_oid,args.artifact_sha256,args.approval_plan_id,args.production_service,args.production_port)
if any(value is None for value in legacy): raise SystemExit("legacy binding is incomplete")
binding = Binding(version=args.version,target_oid=args.target_oid,evidence_closure_oid=args.evidence_closure_oid,artifact_sha256=args.artifact_sha256,approval_plan_id=args.approval_plan_id,artifact_path=str(args.artifact),artifact_size=args.artifact_size,production_service=args.production_service,production_port=args.production_port,repository_identity=args.repository_identity)
binding = Binding(
version=args.version,
target_oid=args.target_oid,
evidence_closure_oid=args.evidence_closure_oid,
artifact_sha256=args.artifact_sha256,
approval_plan_id=args.approval_plan_id,
artifact_path=str(args.artifact),
artifact_size=args.artifact_size,
production_service=args.production_service,
production_port=args.production_port,
repository_identity=args.repository_identity,
)
try:
result = execute(
binding=binding,
@@ -668,7 +634,6 @@ def main() -> int:
deploy_command=parse_command(args.deploy_adapter),
registry_command=parse_command(args.registry_adapter),
baseline_command=parse_command(args.baseline_adapter),
release_plan_path=args.release_plan,
)
print(json.dumps(result, sort_keys=True))
return 0
+2 -65
View File
@@ -4,7 +4,6 @@ from __future__ import annotations
import os
import re
import subprocess
import time
import urllib.parse
from pathlib import Path
from typing import Iterable
@@ -307,76 +306,14 @@ def authoritative_diff(
from_oid = validate_git_revision(from_revision, "from", git_dir=path)
to_oid = validate_git_revision(to_revision, "to", git_dir=path)
args = ["diff", "--no-ext-diff", "--no-textconv", from_oid, to_oid]
patch_limit = max(1, min(_MAX_OUTPUT, 256 * 1024))
process = subprocess.Popen(
["git", "--git-dir", str(path), *args],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
env={**os.environ, "GIT_OPTIONAL_LOCKS": "0"},
)
chunks: list[bytes] = []
stored_bytes = 0
total_bytes = 0
started = time.monotonic()
try:
while process.stdout is not None:
chunk = process.stdout.read(64 * 1024)
if not chunk:
break
total_bytes += len(chunk)
if stored_bytes < patch_limit:
remaining = patch_limit - stored_bytes
selected = chunk[:remaining]
chunks.append(selected)
stored_bytes += len(selected)
if time.monotonic() - started > _TIMEOUT:
process.kill()
raise AuthoritativeGitError("GIT_REF_QUERY_TIMEOUT")
stderr = (
process.stderr.read(8192).decode("utf-8", errors="replace")
if process.stderr is not None
else ""
)
returncode = process.wait(timeout=max(0.1, _TIMEOUT - (time.monotonic() - started)))
except (OSError, subprocess.TimeoutExpired) as exc:
process.kill()
process.wait()
raise AuthoritativeGitError("GIT_REF_QUERY_FAILED") from exc
if returncode != 0:
raise AuthoritativeGitError(_redact(stderr.strip() or "GIT_REF_QUERY_FAILED")[:200])
try:
names_result = run_authoritative_git(
["diff", "--no-ext-diff", "--no-textconv", "--name-status", from_oid, to_oid],
git_dir=path,
)
changed_paths = [
line.split("\t", 1)[-1]
for line in names_result.stdout.splitlines()
if line.strip()
]
except AuthoritativeGitOutputTooLarge:
changed_paths = []
try:
stats_result = run_authoritative_git(
["diff", "--no-ext-diff", "--no-textconv", "--stat", from_oid, to_oid],
git_dir=path,
)
stats = stats_result.stdout.strip()
except AuthoritativeGitOutputTooLarge:
stats = "GIT_STAT_OUTPUT_TRUNCATED"
patch = b"".join(chunks).decode("utf-8", errors="replace")
result = run_authoritative_git(args, git_dir=path)
return {
"code": 0,
"stdout": patch,
"stdout": result.stdout,
"stderr": "",
"cmd": ["git", f"--git-dir={path}", *args],
"from_oid": from_oid,
"to_oid": to_oid,
"changed_paths": changed_paths,
"stats": stats,
"total_size_bytes": total_bytes,
"truncated": total_bytes > patch_limit,
"patch_limit_bytes": patch_limit,
}
@@ -12,7 +12,6 @@ import tempfile
from typing import Any, Mapping
from release_campaign_authorization import CampaignAuthorization, CampaignError
from trusted_release_execution_plan import PlanError, TrustedReleasePlan
class AcceptanceError(RuntimeError):
@@ -62,7 +61,6 @@ def evaluate(
staging: Mapping[str, Any],
main: Mapping[str, Any],
tag: Mapping[str, Any],
release_plan: Mapping[str, Any] | None = None,
) -> dict[str, Any]:
required_binding = {
"version",
@@ -100,27 +98,8 @@ def evaluate(
raise AcceptanceError("rollback_readiness_failed")
if tag.get("conflict") is not False:
raise AcceptanceError("tag_conflict")
trusted_plan: TrustedReleasePlan | None = None
if release_plan is not None:
try:
trusted_plan = TrustedReleasePlan.from_mapping(release_plan)
trusted_plan.assert_binding(binding)
except PlanError as exc:
raise AcceptanceError(f"trusted_release_plan_invalid:{exc}") from exc
if trusted_plan.value["campaign_authorization_sha256"] != campaign.digest:
raise AcceptanceError("campaign_authorization_digest_mismatch")
if int(trusted_plan.value["campaign_generation"]) != int(campaign.data["generation"]):
raise AcceptanceError("campaign_generation_mismatch")
if main.get("tree_oid") not in (None, trusted_plan.value["tree_oid"]):
raise AcceptanceError("tree_oid_mismatch")
if evidence.get("release_plan_sha256") not in (None, trusted_plan.release_plan_sha256):
raise AcceptanceError("evidence_release_plan_mismatch")
if artifact.get("manifest_sha256") not in (None, trusted_plan.value["manifest_sha256"]):
raise AcceptanceError("manifest_digest_mismatch")
if artifact.get("package_closure_sha256") not in (None, trusted_plan.value["package_closure_sha256"]):
raise AcceptanceError("package_closure_digest_mismatch")
campaign.assert_operation("derive_campaign_acceptance")
result = {
return {
"schema": "aios.campaign-release-acceptance.v1",
"accepted": True,
"binding_replaced": False,
@@ -129,22 +108,6 @@ def evaluate(
**dict(binding),
"owner_interaction_required": False,
}
if trusted_plan is not None:
result.update({
"candidate_id": trusted_plan.value["candidate_id"],
"release_plan_sha256": trusted_plan.release_plan_sha256,
"candidate_binding_sha256": trusted_plan.candidate_binding_sha256,
"campaign_authorization_sha256": trusted_plan.value["campaign_authorization_sha256"],
"campaign_generation": trusted_plan.value["campaign_generation"],
"tree_oid": trusted_plan.value["tree_oid"],
"manifest_sha256": trusted_plan.value["manifest_sha256"],
"package_closure_sha256": trusted_plan.value["package_closure_sha256"],
"deployment_plan_sha256": trusted_plan.value["deployment_plan_sha256"],
"policy_sha256": trusted_plan.value["policy_sha256"],
"rollback": trusted_plan.value["rollback"],
"baseline": trusted_plan.value["baseline"],
})
return result
def main() -> int:
@@ -157,7 +120,6 @@ def main() -> int:
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("--release-plan", type=Path)
p.add_argument("--output", type=Path, required=True)
a = p.parse_args()
@@ -174,7 +136,6 @@ def main() -> int:
secure_json(a.staging),
secure_json(a.main_facts),
secure_json(a.tag_facts),
secure_json(a.release_plan) if a.release_plan else None,
)
atomic_new(a.output, receipt)
print(json.dumps(receipt, sort_keys=True))
+7 -16
View File
@@ -8,7 +8,6 @@ import json
import os
import re
import subprocess
import sys
import tempfile
import time
import urllib.error
@@ -24,15 +23,10 @@ try:
from authoritative_git import AuthoritativeGitError, run_authoritative_git
except ModuleNotFoundError:
from .authoritative_git import AuthoritativeGitError, run_authoritative_git
try:
from release_version import RELEASE_VERSION_RE, validate_release_version
except ModuleNotFoundError:
sys.path.insert(0, str(Path(__file__).resolve().parents[2]))
from release_version import RELEASE_VERSION_RE, validate_release_version
OID_RE = re.compile(r"^[0-9a-f]{40}$")
VERSION_RE = RELEASE_VERSION_RE
VERSION_RE = re.compile(r"^V\d+R\d+C\d+(?:-[A-Za-z0-9_.-]+)?$")
DEPLOYMENT_EVIDENCE_RE = re.compile(r"^(?:sha256:[0-9a-f]{64}|git:[0-9a-f]{40})$")
FINGERPRINT_FIELDS = (
"version",
@@ -89,14 +83,9 @@ class PublicationManualOwnerReviewRequired(
def validate_version(version: str) -> str:
return validate_release_version(version)
def validate_gitea_api_url(api_url: str) -> str:
value = str(api_url or "").strip().rstrip("/")
parsed = urllib.parse.urlsplit(value)
if parsed.scheme not in {"http", "https"} or not parsed.netloc:
raise ValueError("Gitea API URL must be HTTP(S)")
value = str(version or "")
if not VERSION_RE.fullmatch(value):
raise ValueError("invalid release version")
return value
@@ -422,11 +411,13 @@ class GiteaTagClient:
timeout: float = 15,
token_line: int = 1,
):
self.api_url = validate_gitea_api_url(api_url)
self.api_url = api_url.rstrip("/")
self.repository = repository.strip("/")
self.token_file = token_file
self.timeout = timeout
self.token_line = token_line
if not self.api_url.startswith(("https://", "http://")):
raise ValueError("Gitea API URL must be HTTP(S)")
if self.repository.count("/") != 1:
raise ValueError("Gitea repository must be owner/name")
@@ -1,7 +1,4 @@
"""Governed release orchestration with a trusted-plan V2R2C2 path.
The legacy V2R2C1 implementation remains available only for compatibility;
new release requests are dispatched to the plan-driven executor below.
"""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
@@ -23,8 +20,6 @@ from dataclasses import dataclass
from pathlib import Path
from typing import Any, Mapping
from trusted_release_execution_plan import PlanError, TrustedReleasePlan
from gitea_tag_publication import (
GiteaTagClient,
GitTagBackend,
@@ -411,14 +406,6 @@ class GovernedProductionReleaseExecutor:
config.remote_name,
state_root=self.root / "tag-publication",
)
# Release checks must use the same explicitly configured staging baseline
# as the Action/model runtime; do not fall back to the publication workspace.
self.publisher.baseline_path = Path(
os.environ.get("AIOS_GITOPS_BASELINE_AUTHORITY", str(config.baseline_path))
)
# The immutable runtime package is the source of the version projection;
# the mutable state workspace only stores publication state.
self.publisher.versions_root = Path(__file__).resolve().parents[1] / "versions"
def _request(self, request: Mapping[str, Any]) -> PublicationRequest:
binding = self.binding
@@ -559,139 +546,7 @@ def _health(url: str, binding: ReleaseBinding, artifact: Mapping[str, Any]) -> d
return {"status": "ACTIVE_VERIFIED" if exact else "CONFLICT", "http_status": status, **body}
class TrustedPlanReleaseExecutor:
"""Release handler whose identity comes only from an immutable trusted plan.
The bridge may prepare/read evidence and request tag publication, but it does
not perform production side effects. The privileged approved-release
service consumes the same plan and exact Owner approval envelope.
"""
def __init__(self, config: Any):
self.config = config
configured = getattr(config, "release_plan_path", None)
self.plan_path = Path(configured) if configured else None
self.state_root = Path(config.state_root) / "trusted-release"
self.state_root.mkdir(parents=True, exist_ok=True)
self._approval: dict[str, Any] | None = None
self._tag: dict[str, Any] | None = None
def _plan(self) -> TrustedReleasePlan:
path = self.plan_path
if path is None:
env_path = os.environ.get("AIOS_RELEASE_PLAN_PATH", "")
path = Path(env_path) if env_path else None
if path is None:
raise ReleaseBlocked("TRUSTED_RELEASE_PLAN_REQUIRED")
try:
return TrustedReleasePlan.load(path)
except PlanError as exc:
raise ReleaseBlocked(f"TRUSTED_RELEASE_PLAN_INVALID:{exc}") from exc
@staticmethod
def _binding(plan: TrustedReleasePlan) -> dict[str, Any]:
value = plan.value
return {
"version": value["version"],
"target_oid": value["source_commit"],
"evidence_closure_oid": value["evidence_closure_oid"],
"artifact_sha256": value["artifact_sha256"],
"artifact_size": value["artifact"]["size"],
"approval_plan_id": value["approval_plan_id"],
"candidate_id": value["candidate_id"],
"release_plan_sha256": plan.release_plan_sha256,
}
def _request(self, request: Mapping[str, Any]) -> tuple[TrustedReleasePlan, dict[str, Any]]:
plan = self._plan()
binding = self._binding(plan)
for key, expected in binding.items():
if key in request and request[key] != expected:
raise ManualOwnerReviewRequired("trusted release request binding mismatch:" + key)
return plan, binding
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")
plan, binding = self._request(request)
approval = request.get("owner_approval")
if not isinstance(approval, Mapping) or approval.get("approved") is not True:
raise ReleaseBlocked("OWNER_RELEASE_APPROVAL_REQUIRED")
for key in ("candidate_id", "release_plan_sha256", "approval_plan_id"):
if approval.get(key) != binding[key]:
raise ManualOwnerReviewRequired("OWNER_APPROVAL_BINDING_MISMATCH:" + key)
record = {"plan": plan.as_dict(), "owner_approval": dict(approval)}
digest = hashlib.sha256(canonical(record)).hexdigest()
if self._approval is not None and self._approval != record:
raise ManualOwnerReviewRequired("OWNER_APPROVAL_REPLAY_CONFLICT")
self._approval = record
return {"status": "RECORDED", "execute": False, "release_plan_sha256": plan.release_plan_sha256, "approval_digest_sha256": digest}
def _require_approval(self, plan: TrustedReleasePlan) -> None:
if self._approval is None or self._approval.get("plan") != plan.as_dict():
raise ReleaseBlocked("OWNER_RELEASE_APPROVAL_NOT_RECORDED")
def build_artifact(self, request: Mapping[str, Any]) -> dict[str, Any]:
plan, _ = self._request(request)
artifact = dict(plan.value["artifact"])
path = Path(str(artifact["path"]))
if path.is_symlink() or not path.is_file() or path.stat().st_size != artifact["size"]:
raise ReleaseBlocked("TRUSTED_ARTIFACT_NOT_PRESENT")
actual = hashlib.sha256(path.read_bytes()).hexdigest()
if actual != plan.value["artifact_sha256"]:
raise ReleaseBlocked("TRUSTED_ARTIFACT_DIGEST_MISMATCH")
return {"status": "VERIFIED", "version": plan.value["version"], "source_commit": plan.value["source_commit"], "tree_oid": plan.value["tree_oid"], "artifact": str(path), "artifact_size": artifact["size"], "artifact_sha256": actual, "manifest_sha256": plan.value["manifest_sha256"], "package_closure_sha256": plan.value["package_closure_sha256"], "release_plan_sha256": plan.release_plan_sha256}
def _publisher(self, plan: TrustedReleasePlan) -> TagPublisher:
root = self.state_root / plan.value["candidate_id"]
root.mkdir(parents=True, exist_ok=True)
publisher = TagPublisher(root / "publication-workspace", GitTagBackend(root / "publication-workspace", authoritative_repo=Path(self.config.authoritative_git_dir)), GiteaTagClient(self.config.api_base, "aios/aios", Path(self.config.token_path)), self.config.remote_name, state_root=root / "tag-publication")
publisher.baseline_path = Path(os.environ.get("AIOS_GITOPS_BASELINE_AUTHORITY", str(self.config.baseline_path)))
publisher.versions_root = Path(__file__).resolve().parents[1] / "versions"
return publisher
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")
plan, _ = self._request(request)
self._require_approval(plan)
publication = PublicationRequest(plan.value["version"], plan.value["source_commit"], plan.value["evidence_closure_oid"], plan.value["approval_plan_id"])
try:
result = self._publisher(plan)._verify_publication(publication, allow_push=True, previously_deployed=False)
except (PublicationManualOwnerReviewRequired, PublicationPending, PublicationReconciliationRequired) as exc:
raise ReconciliationRequired(str(exc)) from exc
self._tag = dict(result)
return {"status": "REMOTE_VERIFIED", **result, "release_plan_sha256": plan.release_plan_sha256}
def tag_status(self, request: Mapping[str, Any]) -> dict[str, Any]:
plan, _ = self._request(request)
return self._publisher(plan).get_tag_publication_status(plan.value["version"])
def execute_request(self, request: Mapping[str, Any]) -> dict[str, Any]:
plan, _ = self._request(request)
self._require_approval(plan)
raise ReleaseBlocked("PRODUCTION_EXECUTION_REQUIRES_APPROVED_RELEASE_EXECUTOR")
def consistency(self, request: Mapping[str, Any]) -> dict[str, Any]:
plan, binding = self._request(request)
result = {"status": "PASS", "release_binding": binding, "trusted_plan": plan.as_dict(), "production_mutation": False}
result["baseline"] = _read_json(Path(self.config.baseline_path))
result["registry"] = _read_json(Path(self.config.registry_root) / f"{plan.value['version']}.json")
return result
def acceptance(self, request: Mapping[str, Any]) -> dict[str, Any]:
result = self.consistency(request)
result["accepted"] = result["status"] == "PASS"
return result
def build_executor_config(config: Any) -> Any:
"""Select legacy compatibility only when no trusted new-release plan exists."""
plan_path = getattr(config, "release_plan_path", None) or os.environ.get("AIOS_RELEASE_PLAN_PATH", "")
target_version = getattr(config, "target_version", "")
if plan_path or target_version != VERSION:
return TrustedPlanReleaseExecutor(config)
def build_executor_config(config: Any) -> GovernedProductionReleaseExecutor:
return GovernedProductionReleaseExecutor(config)
+2 -6
View File
@@ -34,8 +34,6 @@ from gitea_tag_publication import (
TagPublisher,
)
from release_version import validate_release_version
OID_RE = re.compile(r"^[0-9a-f]{40}$")
PLAN_RE = re.compile(r"^sha256:[0-9a-f]{64}$")
@@ -46,10 +44,8 @@ def load_request() -> dict[str, Any]:
raise ValueError("request_object_required")
if value.get("schema") != "aios.production-executor-request.v1":
raise ValueError("request_schema_invalid")
try:
validate_release_version(value.get("version", ""))
except ValueError as exc:
raise ValueError("version_invalid") from exc
if not re.fullmatch(r"V\d+R\d+C\d+(?:-[A-Za-z0-9_.-]+)?", str(value.get("version", ""))):
raise ValueError("version_invalid")
for name in ("target_oid", "evidence_closure_oid"):
if not OID_RE.fullmatch(str(value.get(name, ""))):
raise ValueError(f"{name}_invalid")
@@ -27,8 +27,6 @@ import urllib.request
from pathlib import Path
from typing import Any, Mapping
from release_version import validate_release_version
OID_RE = re.compile(r"^[0-9a-f]{40}$")
SHA_RE = re.compile(r"^[0-9a-f]{64}$")
PLAN_RE = re.compile(r"^sha256:[0-9a-f]{64}$")
@@ -88,10 +86,8 @@ def load_request() -> dict[str, Any]:
raise ValueError("request_schema_invalid")
if value.get("repository_identity") != "aios/aios":
raise ValueError("repository_identity_invalid")
try:
validate_release_version(value.get("version", ""))
except ValueError as exc:
raise ValueError("version_invalid") from exc
if not re.fullmatch(r"V\d+R\d+C\d+(?:-[A-Za-z0-9_.-]+)?", str(value.get("version", ""))):
raise ValueError("version_invalid")
for name in ("target_oid", "evidence_closure_oid"):
if not OID_RE.fullmatch(str(value.get(name, ""))):
raise ValueError(f"{name}_invalid")
@@ -22,12 +22,11 @@ from enum import Enum
from pathlib import Path
from typing import Any, Protocol
from release_version import validate_release_version
OID_RE = re.compile(r"^[0-9a-f]{40}$")
SHA256_RE = re.compile(r"^[0-9a-f]{64}$")
CANDIDATE_ID_RE = re.compile(r"^[A-Za-z0-9][A-Za-z0-9_.-]{2,127}$")
VERSION_RE = re.compile(r"^V\d+R\d+C\d+(?:-[A-Za-z0-9_.-]+)?$")
SECRET_PATTERNS = (
re.compile(r"(?i)(authorization\s*[:=]\s*)(?:bearer|token)\s+\S+"),
re.compile(r"(?i)((?:access_)?token\s*[:=]\s*)\S+"),
@@ -770,10 +769,8 @@ class TagApprovalEnvelope:
def validate(self) -> None:
if self.action != "production_tag":
raise ApprovalError("approval does not authorize production tag")
try:
validate_release_version(self.version)
except ValueError as exc:
raise ApprovalError("invalid release version") from exc
if not VERSION_RE.fullmatch(self.version):
raise ApprovalError("invalid release version")
_validate_oid("tag_oid", self.tag_oid)
_validate_oid("target_oid", self.target_oid)
if _parse_utc(self.expires_at) <= datetime.now(timezone.utc):
+32 -603
View File
@@ -9,7 +9,6 @@ from __future__ import annotations
import base64
import hashlib
import hmac
import json
import os
import re
@@ -41,8 +40,6 @@ from silent_release_controller import (
IDENTIFIER_RE = re.compile(r"^[A-Za-z0-9][A-Za-z0-9_.:-]{2,255}$")
NONCE_RE = re.compile(r"^[A-Za-z0-9][A-Za-z0-9_.:-]{15,255}$")
BUILDER_ATTESTATION_TRUST_DOMAIN = "aios.trusted-builder.attestation.v1"
BUILDER_ATTESTATION_PURPOSE = "AIOS_TRUSTED_RELEASE_BUILDER"
ALLOWED_CAPABILITIES = frozenset(
{
"runtime_switch",
@@ -65,10 +62,6 @@ class CapabilityAuthorizationError(RuntimeError):
"""The signed plan cannot authorize the requested operation."""
class TrustedCandidateBuilderUnavailable(RuntimeError):
"""The purpose-bound builder dependency cannot currently serve a build."""
class StaleApprovalError(CapabilityAuthorizationError):
"""Live release evidence no longer matches the signed approval."""
@@ -229,10 +222,6 @@ JOURNAL_GENESIS_HASH = "0" * 64
def _fsync_directory(path: Path) -> None:
if os.name == "nt":
# Windows does not permit opening a directory with os.open. Durable
# Linux host execution retains the directory fsync below.
return
descriptor = os.open(Path(path), os.O_RDONLY)
try:
os.fsync(descriptor)
@@ -726,14 +715,6 @@ class BuilderAttestation:
canary_evidence_sha256: str
issued_at: str
signature: str
trust_domain: str = BUILDER_ATTESTATION_TRUST_DOMAIN
builder_key_generation: int | None = None
signature_algorithm: str | None = None
signature_purpose: str | None = None
gitea_evidence_sha256: str = ""
rollback_evidence_sha256: str = ""
policy_evidence_sha256: str = ""
secret_scan_evidence_sha256: str = ""
def __post_init__(self) -> None:
_validate_identifier(
@@ -750,35 +731,7 @@ class BuilderAttestation:
"canary_evidence_sha256",
):
_validate_sha(name, getattr(self, name))
for name in (
"gitea_evidence_sha256",
"rollback_evidence_sha256",
"policy_evidence_sha256",
"secret_scan_evidence_sha256",
):
if getattr(self, name):
_validate_sha(name, getattr(self, name))
_parse_utc(self.issued_at)
if self.trust_domain != BUILDER_ATTESTATION_TRUST_DOMAIN:
raise CapabilityAuthorizationError(
"builder attestation trust domain is invalid"
)
metadata = (
self.builder_key_generation,
self.signature_algorithm,
self.signature_purpose,
)
if any(value is not None for value in metadata):
if (
not isinstance(self.builder_key_generation, int)
or isinstance(self.builder_key_generation, bool)
or self.builder_key_generation < 0
or self.signature_algorithm != "Ed25519"
or self.signature_purpose != BUILDER_ATTESTATION_PURPOSE
):
raise CapabilityAuthorizationError(
"builder attestation signing metadata is invalid"
)
@property
def canonical_payload(self) -> dict[str, Any]:
@@ -801,290 +754,20 @@ class BuilderAttestation:
}
@classmethod
def from_dict(
cls,
payload: Mapping[str, Any],
*,
require_fingerprint: bool = False,
) -> "BuilderAttestation":
def from_dict(cls, payload: Mapping[str, Any]) -> "BuilderAttestation":
values = dict(payload)
supplied_fingerprint = values.pop("fingerprint", None)
# Older local records did not spell out the domain. They are only
# usable when re-signed by the current builder; their original
# signatures cannot validate against this domain-separated payload.
values.setdefault("trust_domain", BUILDER_ATTESTATION_TRUST_DOMAIN)
value = cls(**values)
if supplied_fingerprint is None:
if require_fingerprint:
raise CapabilityAuthorizationError(
"builder attestation digest is required"
)
return value
expected = _validate_sha(
"builder attestation digest", str(supplied_fingerprint)
)
if not hmac.compare_digest(expected, value.fingerprint):
raise CapabilityAuthorizationError(
"builder attestation digest is invalid"
)
return value
@dataclass(frozen=True)
class BuilderAttestationKeyRecord:
"""One purpose-bound public verification key from the host keyring."""
key_id: str
public_key: bytes
algorithm: str
purpose: str
generation: int
not_before: str
expires_at: str
status: str
def __post_init__(self) -> None:
_validate_identifier("builder_key_id", self.key_id)
if self.algorithm != "Ed25519":
raise ValueError("unsupported builder signature algorithm")
if self.purpose != BUILDER_ATTESTATION_PURPOSE:
raise ValueError("builder key purpose is invalid")
if not isinstance(self.generation, int) or isinstance(
self.generation, bool
) or self.generation < 0:
raise ValueError("builder key generation is invalid")
if self.status not in {"ACTIVE", "REVOKED"}:
raise ValueError("builder key status is invalid")
if len(self.public_key) != 32:
raise ValueError("Ed25519 public key must contain 32 bytes")
Ed25519PublicKey.from_public_bytes(self.public_key)
if _parse_utc(self.not_before) >= _parse_utc(self.expires_at):
raise ValueError("builder key validity window is invalid")
@property
def public_key_sha256(self) -> str:
return hashlib.sha256(self.public_key).hexdigest()
def validate_at(self, instant: datetime) -> None:
if self.status != "ACTIVE":
raise CapabilityAuthorizationError(
"builder attestation key is unavailable or revoked"
)
if instant.tzinfo is None:
raise CapabilityAuthorizationError(
"builder attestation verification time is invalid"
)
if not _parse_utc(self.not_before) <= instant <= _parse_utc(
self.expires_at
):
raise CapabilityAuthorizationError(
"builder attestation key is outside its validity window"
)
@classmethod
def from_dict(
cls, payload: Mapping[str, Any]
) -> "BuilderAttestationKeyRecord":
values = dict(payload)
encoded = values.pop("public_key", values.pop("public_key_b64", None))
if not isinstance(encoded, str):
raise ValueError("builder public key is missing")
try:
public_key = base64.b64decode(encoded, validate=True)
except (TypeError, ValueError) as exc:
raise ValueError("builder public key encoding is invalid") from exc
supplied_digest = values.pop(
"public_key_sha256",
values.pop("key_sha256", values.pop("digest", None)),
)
if not isinstance(supplied_digest, str):
raise ValueError("builder public key digest is missing")
digest = _validate_sha("builder public key digest", supplied_digest)
if not hmac.compare_digest(
digest, hashlib.sha256(public_key).hexdigest()
):
raise ValueError("builder public key digest is invalid")
allowed = {
"algorithm",
"purpose",
"key_id",
"generation",
"not_before",
"expires_at",
"status",
}
unknown = set(values).difference(allowed)
if unknown:
raise ValueError("builder key record contains unknown fields")
return cls(public_key=public_key, **values)
class BuilderAttestationKeyRing:
"""Validated, public-only keyring for builder attestations.
The file format deliberately cannot carry Owner approval keys: both the
keyring and every record must declare the builder-attestation purpose.
"""
def __init__(
self,
records: Iterable[BuilderAttestationKeyRecord],
*,
require_attestation_metadata: bool = False,
):
self._records: dict[str, BuilderAttestationKeyRecord] = {}
self._require_attestation_metadata = require_attestation_metadata
for record in records:
if record.key_id in self._records:
raise ValueError("duplicate builder key id")
self._records[record.key_id] = record
@classmethod
def from_public_keys(
cls, public_keys: Mapping[str, bytes]
) -> "BuilderAttestationKeyRing":
"""Compatibility adapter for explicit in-process builder key maps.
This is intentionally not used for host configuration; host keyrings
must use :meth:`from_file` so purpose, digest, validity and status are
checked before the API accepts traffic.
"""
return cls(
BuilderAttestationKeyRecord(
key_id=_validate_identifier("builder_key_id", key_id),
public_key=bytes(public_key),
algorithm="Ed25519",
purpose=BUILDER_ATTESTATION_PURPOSE,
generation=0,
not_before="1970-01-01T00:00:00Z",
expires_at="9999-12-31T23:59:59Z",
status="ACTIVE",
)
for key_id, public_key in public_keys.items()
)
@classmethod
def from_file(cls, path: Path) -> "BuilderAttestationKeyRing":
payload = json.loads(Path(path).read_text(encoding="utf-8"))
if payload.get("algorithm") != "Ed25519":
raise ValueError("unsupported builder keyring algorithm")
if payload.get("purpose") != BUILDER_ATTESTATION_PURPOSE:
raise ValueError("builder keyring purpose is invalid")
raw_records = payload.get("keys")
if not isinstance(raw_records, list) or not raw_records:
raise ValueError("builder keyring keys are invalid")
allowed = {"algorithm", "purpose", "keys", "keyring_sha256"}
if set(payload).difference(allowed):
raise ValueError("builder keyring contains unknown fields")
if "keyring_sha256" not in payload:
raise ValueError("builder keyring digest is missing")
expected = _validate_sha(
"builder keyring digest", str(payload["keyring_sha256"])
)
canonical = _canonical_json(
{
"algorithm": payload["algorithm"],
"purpose": payload["purpose"],
"keys": raw_records,
}
)
if not hmac.compare_digest(
expected, hashlib.sha256(canonical).hexdigest()
):
raise ValueError("builder keyring digest is invalid")
if not all(isinstance(record, Mapping) for record in raw_records):
raise ValueError("builder keyring record is invalid")
return cls(
(
BuilderAttestationKeyRecord.from_dict(record)
for record in raw_records
),
require_attestation_metadata=True,
)
def verify(
self,
key_id: str,
signature: bytes,
message: bytes,
*,
at: datetime,
generation: int | None,
algorithm: str | None,
purpose: str | None,
) -> BuilderAttestationKeyRecord:
record = self._records.get(key_id)
if record is None:
raise CapabilityAuthorizationError(
"builder attestation key is not trusted"
)
record.validate_at(at)
metadata = (generation, algorithm, purpose)
if self._require_attestation_metadata and any(
value is None for value in metadata
):
raise CapabilityAuthorizationError(
"builder attestation signing metadata is required"
)
if any(value is not None for value in metadata) and metadata != (
record.generation,
record.algorithm,
record.purpose,
):
raise CapabilityAuthorizationError(
"builder attestation signing metadata does not match key"
)
try:
Ed25519PublicKey.from_public_bytes(record.public_key).verify(
signature, message
)
except InvalidSignature as exc:
raise CapabilityAuthorizationError(
"builder attestation signature is invalid"
) from exc
return record
def require_active_identity(
self,
*,
key_id: str,
generation: int,
algorithm: str,
purpose: str,
at: datetime | None = None,
) -> BuilderAttestationKeyRecord:
record = self._records.get(key_id)
if record is None:
raise CapabilityAuthorizationError(
"builder attestation key is not trusted"
)
if (
record.generation != generation
or record.algorithm != algorithm
or record.purpose != purpose
):
raise CapabilityAuthorizationError(
"builder attestation key metadata is invalid"
)
record.validate_at(at or datetime.now(timezone.utc))
return record
values.pop("fingerprint", None)
return cls(**values)
class BuilderAttestationVerifier:
"""Public-key-only verifier; no builder private material is accepted."""
def __init__(
self,
public_keys: BuilderAttestationKeyRing | Mapping[str, bytes],
):
if isinstance(public_keys, BuilderAttestationKeyRing):
self.keyring = public_keys
elif isinstance(public_keys, Mapping):
self.keyring = BuilderAttestationKeyRing.from_public_keys(
public_keys
)
else:
raise TypeError("builder verifier requires a builder keyring")
def __init__(self, public_keys: Mapping[str, bytes]):
self.public_keys = {
_validate_identifier("builder_key_id", key_id): bytes(value)
for key_id, value in public_keys.items()
}
def verify(
self,
@@ -1110,54 +793,22 @@ class BuilderAttestationVerifier:
raise CapabilityAuthorizationError(
"builder attestation does not bind candidate evidence"
)
key = self.public_keys.get(attestation.builder_key_id)
if key is None:
raise CapabilityAuthorizationError(
"builder attestation key is not trusted"
)
try:
signature = base64.b64decode(
attestation.signature, validate=True
)
key_record = self.keyring.verify(
attestation.builder_key_id,
signature,
attestation.canonical_bytes(),
at=_parse_utc(attestation.issued_at),
generation=attestation.builder_key_generation,
algorithm=attestation.signature_algorithm,
purpose=attestation.signature_purpose,
Ed25519PublicKey.from_public_bytes(key).verify(
signature, attestation.canonical_bytes()
)
except (InvalidSignature, ValueError, TypeError) as exc:
raise CapabilityAuthorizationError(
"builder attestation signature is invalid"
) from exc
if key_record.generation >= 1:
expected_gitea = hashlib.sha256(
_canonical_json(candidate.gitea_consistency)
).hexdigest()
expected_rollback = hashlib.sha256(
_canonical_json(
{
"ready": candidate.rollback_ready,
"rollback_sha256": release_plan.deployment.rollback_sha256,
"source_commit": candidate.source_commit,
}
)
).hexdigest()
production_evidence = (
attestation.gitea_evidence_sha256,
attestation.rollback_evidence_sha256,
attestation.policy_evidence_sha256,
attestation.secret_scan_evidence_sha256,
)
if any(not value for value in production_evidence):
raise CapabilityAuthorizationError(
"builder attestation production evidence is incomplete"
)
if (
attestation.gitea_evidence_sha256 != expected_gitea
or attestation.rollback_evidence_sha256 != expected_rollback
or attestation.policy_evidence_sha256 != release_plan.policy_sha256
):
raise CapabilityAuthorizationError(
"builder attestation production evidence is invalid"
)
return attestation
@@ -3530,12 +3181,9 @@ class SilentReleaseActionService:
builder_attestation_verifier: BuilderAttestationVerifier | None = None,
trusted_policy: TrustedReleasePolicy | None = None,
orchestrator: ReleaseOrchestrator | None = None,
builder_readiness_probe: Callable[[], Mapping[str, Any]] | None = None,
allow_legacy_candidate_input: bool = False,
expected_repository: str = "",
policy_sha256: str = "",
max_builder_attempts: int = 3,
builder_attempt_timeout_seconds: int = 180,
):
self.root = Path(root)
self.execution_enabled = bool(execution_enabled)
@@ -3546,28 +3194,11 @@ class SilentReleaseActionService:
self.builder_attestation_verifier = builder_attestation_verifier
self.trusted_policy = trusted_policy
self.orchestrator = orchestrator
self.builder_readiness_probe = builder_readiness_probe
self.allow_legacy_candidate_input = bool(
allow_legacy_candidate_input
)
self.expected_repository = expected_repository
self.policy_sha256 = policy_sha256
if (
not isinstance(max_builder_attempts, int)
or isinstance(max_builder_attempts, bool)
or not 1 <= max_builder_attempts <= 10
):
raise ValueError("max_builder_attempts must be between 1 and 10")
self.max_builder_attempts = max_builder_attempts
if (
not isinstance(builder_attempt_timeout_seconds, int)
or isinstance(builder_attempt_timeout_seconds, bool)
or not 1 <= builder_attempt_timeout_seconds <= 3600
):
raise ValueError(
"builder_attempt_timeout_seconds must be between 1 and 3600"
)
self.builder_attempt_timeout_seconds = builder_attempt_timeout_seconds
self.candidates = ReleaseCandidateStore(
self.root / "candidates"
)
@@ -3604,26 +3235,10 @@ class SilentReleaseActionService:
+ ", ".join(missing)
)
def get_builder_readiness(self) -> dict[str, Any]:
"""Report dependency readiness without conflating it with API liveness."""
if self.candidate_builder is None:
return {"status": "UNAVAILABLE"}
if self.builder_readiness_probe is None:
# Explicit in-process test/adaptor construction remains usable;
# production wiring provides a probe backed by its credentials.
return {"status": "READY"}
try:
value = dict(self.builder_readiness_probe())
except Exception:
return {"status": "UNAVAILABLE"}
if value.get("status") != "READY":
return {"status": "UNAVAILABLE"}
return _sanitize(value)
def create_release_intent(
self, payload: Mapping[str, Any]
) -> dict[str, Any]:
allowed = {"event_id", "source_commit", "version"}
allowed = {"event_id", "source_commit"}
unknown = set(payload).difference(allowed)
if unknown:
raise CapabilityAuthorizationError(
@@ -3635,17 +3250,12 @@ class SilentReleaseActionService:
source_commit = _validate_oid(
"source_commit", str(payload.get("source_commit") or "")
)
version = str(payload.get("version") or "")
if version:
_validate_identifier("version", version)
intent_id = "intent-" + hashlib.sha256(
f"{event_id}\0{source_commit}".encode("utf-8")
).hexdigest()[:24]
path = _safe_child(self.intents_root, intent_id)
was_existing = path.exists()
record = {
"created_at": utc_now(),
"attempt_count": 0,
"event_id": event_id,
"intent_id": intent_id,
"source_commit": source_commit,
@@ -3655,153 +3265,42 @@ class SilentReleaseActionService:
else "BUILDER_UNAVAILABLE"
),
}
if version:
record["version"] = version
if was_existing:
if path.exists():
existing = json.loads(path.read_text(encoding="utf-8"))
if (
existing.get("event_id") != event_id
or existing.get("source_commit") != source_commit
or str(existing.get("version") or "") != version
):
raise SagaConflict(
"release intent id is bound to different evidence"
)
if existing.get("status") == "BUILDING":
try:
started_at = _parse_utc(
str(existing.get("attempt_started_at") or "")
)
elapsed = (
datetime.now(timezone.utc) - started_at
).total_seconds()
except (TypeError, ValueError):
elapsed = self.builder_attempt_timeout_seconds + 1
if elapsed <= self.builder_attempt_timeout_seconds:
return self._intent_response(existing, idempotent=True)
existing.update(
{
"failure_code": "TRUSTED_BUILDER_CRASH_RECOVERED",
"last_recovery_code": "TRUSTED_BUILDER_CRASH_RECOVERED",
"recovery_count": int(existing.get("recovery_count") or 0) + 1,
"retryable": int(existing.get("attempt_count") or 0)
< self.max_builder_attempts,
"status": "BUILDER_RETRYABLE_FAILURE",
"updated_at": utc_now(),
}
)
_durable_json(path, existing)
if (
existing.get("status") != "BUILDER_RETRYABLE_FAILURE"
or int(existing.get("attempt_count") or 0)
>= self.max_builder_attempts
):
return self._intent_response(existing, idempotent=True)
record = dict(existing)
if self.candidate_builder is None:
record.update(
{
"retryable": False,
"status": "BUILDER_UNAVAILABLE",
}
)
_durable_json(path, record)
return self._intent_response(record, idempotent=was_existing)
record.update(
{
"attempt_count": int(record.get("attempt_count") or 0) + 1,
"attempt_started_at": utc_now(),
"retryable": False,
"status": "BUILDING",
"updated_at": utc_now(),
return {
**_sanitize(existing),
"idempotent": True,
"execution_enabled": self.execution_enabled,
"shadow_mode": not self.execution_enabled,
"side_effects_executed": False,
}
)
_durable_json(path, record)
try:
result = dict(record)
if self.candidate_builder is not None:
candidate = self.create_release_candidate(
{
"event_id": event_id,
"intent_id": intent_id,
"source_commit": source_commit,
**({"version": version} if version else {}),
}
)
except TrustedCandidateBuilderUnavailable:
record.update(
result.update(
{
"failure_code": "TRUSTED_BUILDER_UNAVAILABLE",
"retryable": record["attempt_count"]
< self.max_builder_attempts,
"status": (
"BUILDER_RETRYABLE_FAILURE"
if record["attempt_count"] < self.max_builder_attempts
else "BUILDER_TERMINAL_FAILURE"
),
"candidate_id": candidate["candidate_id"],
"status": candidate["saga_state"],
}
)
_durable_json(path, record)
return self._intent_response(record, idempotent=False)
except CapabilityAuthorizationError:
record.update(
{
"failure_code": "TRUSTED_BUILDER_REJECTED",
"retryable": False,
"status": "BUILDER_TERMINAL_FAILURE",
}
)
_durable_json(path, record)
return self._intent_response(record, idempotent=False)
except (ConnectionError, OSError, TimeoutError):
record.update(
{
"failure_code": "TRUSTED_BUILDER_UNAVAILABLE",
"retryable": record["attempt_count"]
< self.max_builder_attempts,
"status": (
"BUILDER_RETRYABLE_FAILURE"
if record["attempt_count"] < self.max_builder_attempts
else "BUILDER_TERMINAL_FAILURE"
),
}
)
_durable_json(path, record)
return self._intent_response(record, idempotent=False)
except Exception:
# The durable intent is recoverable without serializing an
# arbitrary exception (which may contain a credential or host
# path). Repeated requests consume the small fixed retry budget.
record.update(
{
"failure_code": "TRUSTED_BUILDER_FAILURE",
"retryable": record["attempt_count"]
< self.max_builder_attempts,
"status": (
"BUILDER_RETRYABLE_FAILURE"
if record["attempt_count"] < self.max_builder_attempts
else "BUILDER_TERMINAL_FAILURE"
),
}
)
_durable_json(path, record)
return self._intent_response(record, idempotent=False)
result = dict(record)
result.update(
{
"candidate_id": candidate["candidate_id"],
"retryable": False,
"status": candidate["saga_state"],
}
)
_durable_json(path, result)
return self._intent_response(result, idempotent=False)
def _intent_response(
self, record: Mapping[str, Any], *, idempotent: bool
) -> dict[str, Any]:
_durable_json(path, result)
return {
**_sanitize(dict(record)),
"idempotent": idempotent,
**_sanitize(result),
"idempotent": False,
"execution_enabled": self.execution_enabled,
"shadow_mode": not self.execution_enabled,
"side_effects_executed": False,
@@ -3822,7 +3321,6 @@ class SilentReleaseActionService:
self, payload: Mapping[str, Any]
) -> dict[str, Any]:
if self.candidate_builder is not None:
values = dict(payload)
forbidden_evidence = {
"artifact_sha256",
"canary",
@@ -3835,69 +3333,13 @@ class SilentReleaseActionService:
"tests",
"tree_oid",
}
if forbidden_evidence.intersection(values):
if forbidden_evidence.intersection(payload):
raise CapabilityAuthorizationError(
"candidate evidence must come from the trusted builder"
)
worker_fields = {"candidate_id", "source_commit", "version"}
intent_fields = {"event_id", "intent_id", "source_commit", "version"}
supplied = set(values)
is_worker_request = worker_fields.issubset(supplied)
if is_worker_request:
if supplied != worker_fields:
raise ValueError("release worker request contains unknown fields")
_validate_identifier(
"candidate_id", str(values.get("candidate_id") or "")
)
_validate_identifier(
"version", str(values.get("version") or "")
)
else:
if not supplied.issubset(intent_fields):
raise ValueError("trusted builder intent contains unknown fields")
# Preserve the established internal intent contract
# (source_commit + version) while allowing newer callers to
# add an event/intent identifier. Neither form can supply
# acceptance evidence; the host builder owns all collectors.
if values.get("event_id"):
_validate_identifier(
"event_id", str(values["event_id"])
)
if values.get("intent_id"):
_validate_identifier(
"intent_id", str(values["intent_id"])
)
if values.get("version"):
_validate_identifier(
"version", str(values["version"])
)
_validate_oid(
"source_commit", str(values.get("source_commit") or "")
)
readiness = self.get_builder_readiness()
if readiness.get("status") != "READY":
raise TrustedCandidateBuilderUnavailable(
"trusted builder dependency is unavailable"
)
bundle = self.candidate_builder.build(dict(payload)).validate(
self.builder_attestation_verifier
)
requested_source = str(payload.get("source_commit") or "")
if (
requested_source
and bundle.candidate.source_commit != requested_source
):
raise CapabilityAuthorizationError(
"trusted builder source commit does not match intent"
)
if (
is_worker_request
and bundle.candidate.candidate_id
!= str(values["candidate_id"])
):
raise CapabilityAuthorizationError(
"trusted builder candidate id does not match request"
)
if self.trusted_policy is None:
raise CapabilityAuthorizationError(
"trusted release policy is not configured"
@@ -4015,19 +3457,6 @@ class SilentReleaseActionService:
}
def get_release_saga(self, candidate_id: str) -> dict[str, Any]:
# A read of a never-created candidate is a normal empty-state. Do
# not turn a missing journal into a durable manual-review snapshot.
saga_path = self.sagas._path(candidate_id)
journal_path = self.sagas._journal(candidate_id)
if not saga_path.exists() and not journal_path.exists():
return {
"candidate_id": candidate_id,
"status": "NOT_FOUND",
"state": "NOT_FOUND",
"execution_enabled": self.execution_enabled,
"shadow_mode": not self.execution_enabled,
"side_effects_executed": False,
}
return {
**self.sagas.load(candidate_id).to_dict(),
"execution_enabled": self.execution_enabled,
@@ -1,679 +0,0 @@
#!/usr/bin/env python3
"""Root-only, purpose-bound trusted-builder signer provisioner.
The provisioner consumes one exact Owner-signed request after the normal
engineering review has prepared it. It creates the Ed25519 private material
and builder HMAC on the host, encrypts both for systemd, publishes only the
public verification key, and appends one replay-protected receipt. It never
accepts or writes main, tag, baseline, production, or Owner-approval keys.
"""
from __future__ import annotations
import argparse
import base64
import datetime as dt
try:
import fcntl
except ImportError: # pragma: no cover - Windows only affects local payload tooling.
class _NoopFcntl:
LOCK_EX = 0
@staticmethod
def flock(_file, _mode):
return None
fcntl = _NoopFcntl()
import hashlib
import json
import os
from pathlib import Path
import secrets
import shutil
import stat
import subprocess
import tempfile
from typing import Any, Mapping
PURPOSE = "AIOS_TRUSTED_RELEASE_BUILDER"
TRUST_DOMAIN = "aios.trusted-builder.attestation.v1"
OWNER_NAMESPACE = "aios-owner-approval-v1"
OWNER_PRINCIPAL = "aios-task-grant-signer"
SCHEMA = "aios.trusted-builder-signer-provisioning.v2"
ALLOWED_CAPABILITIES = frozenset(
{
"builder_receipt_signing",
"artifact_provenance_signing",
"manifest_provenance_signing",
"builder_owned_release_evidence_signing",
}
)
DENIED_CAPABILITIES = frozenset(
{
"owner_release_approval",
"protected_main_merge",
"arbitrary_ref_write",
"tag_publication",
"baseline_finalize",
"production_runtime_switch",
}
)
OID_FIELDS = {
"gitea_main_oid",
"authority_main_oid_at_capture",
"governed_main_oid",
"gitea_main_tree_oid",
"pr_head_oid",
"protected_merge_commit",
}
SHA_FIELDS = {"source_evidence_sha256"}
PACKAGE_MANIFEST_PATH = "deploy/trusted-builder/aios-trusted-builder-package-manifest.json"
PACKAGE_MANIFEST_SCHEMA = "aios.trusted-builder-package.v1"
SOURCE_EVIDENCE_PATHS = (
"active-runtime/app/trusted_builder_signer_provisioning.py",
"active-runtime/app/aios_trusted_candidate_builder.py",
"config/gitops/trusted-candidate-builder-policy.json",
"deploy/systemd/aios-trusted-candidate-builder.service",
"deploy/systemd/aios-trusted-candidate-builder.socket",
"deploy/systemd/aios-gitops-model-api-trusted-builder.conf.example",
"deploy/libexec/aios-trusted-builder-signer-provisioner",
PACKAGE_MANIFEST_PATH,
)
SOURCE_EVIDENCE_MODES = {path: frozenset({0o600, 0o640, 0o644, 0o664}) for path in SOURCE_EVIDENCE_PATHS}
def package_contract_document() -> dict[str, Any]:
"""The non-secret package/activation contract for a fresh B2 payload."""
files = []
for path in SOURCE_EVIDENCE_PATHS:
install_mode = "0755" if path == "deploy/libexec/aios-trusted-builder-signer-provisioner" else "0644"
files.append({"source_path": path, "source_mode": "0644", "install_mode": install_mode})
return {
"schema": PACKAGE_MANIFEST_SCHEMA,
"version": 1,
"package_name": "aios-trusted-candidate-builder",
"source_root": "/opt/aios/trusted-candidate-builder/current",
"files": files,
"activation": {
"daemon_reload": True,
"enable_units": ["aios-trusted-candidate-builder.socket"],
"service_start": "socket-activation-only",
"credential_installation": False,
"signer_invocation": False,
},
}
def _source_evidence(source_root: Path, source_files: list[str] | tuple[str, ...]) -> dict[str, str]:
if set(source_files) != set(SOURCE_EVIDENCE_PATHS) or len(source_files) != len(SOURCE_EVIDENCE_PATHS):
raise ProvisioningError("SOURCE_EVIDENCE_SET_INVALID")
evidence: dict[str, str] = {}
for path in SOURCE_EVIDENCE_PATHS:
candidate = source_root / path
if candidate.is_symlink() or not candidate.is_file():
raise ProvisioningError("SOURCE_EVIDENCE_MISSING")
if stat.S_IMODE(candidate.stat().st_mode) not in SOURCE_EVIDENCE_MODES[path]:
raise ProvisioningError("SOURCE_EVIDENCE_MODE_INVALID")
evidence[path] = _digest_file(candidate)
return evidence
def verify_package_contract(source_root: Path) -> None:
manifest = source_root / PACKAGE_MANIFEST_PATH
raw, value = _read_json(manifest)
if value != package_contract_document() or raw != pretty_json(package_contract_document()):
raise ProvisioningError("PACKAGE_CONTRACT_INVALID")
_source_evidence(source_root, list(SOURCE_EVIDENCE_PATHS))
class ProvisioningError(RuntimeError):
"""Stable fail-closed error code; never includes secret material."""
def __init__(self, code: str) -> None:
super().__init__(code)
self.code = code
def canonical_json(value: Any) -> bytes:
return json.dumps(value, ensure_ascii=False, sort_keys=True, separators=(",", ":")).encode("utf-8")
def pretty_json(value: Any) -> bytes:
return (json.dumps(value, ensure_ascii=False, sort_keys=True, indent=2) + "\n").encode("utf-8")
def sha256_bytes(value: bytes) -> str:
return hashlib.sha256(value).hexdigest()
def utc_now() -> dt.datetime:
return dt.datetime.now(dt.timezone.utc)
def iso(value: dt.datetime) -> str:
return value.astimezone(dt.timezone.utc).isoformat().replace("+00:00", "Z")
def _require_oid(value: Any, code: str) -> str:
if not isinstance(value, str) or len(value) != 40 or any(c not in "0123456789abcdef" for c in value.lower()):
raise ProvisioningError(code)
return value.lower()
def _require_sha(value: Any, code: str) -> str:
if not isinstance(value, str) or len(value) != 64 or any(c not in "0123456789abcdef" for c in value.lower()):
raise ProvisioningError(code)
return value.lower()
def _read_json(path: Path) -> tuple[bytes, dict[str, Any]]:
if path.is_symlink() or not path.is_file():
raise ProvisioningError("PROVISIONING_INPUT_MISSING")
try:
raw = path.read_bytes()
value = json.loads(raw.decode("utf-8"))
except (OSError, UnicodeDecodeError, json.JSONDecodeError) as exc:
raise ProvisioningError("PROVISIONING_INPUT_INVALID") from exc
if not isinstance(value, dict):
raise ProvisioningError("PROVISIONING_OBJECT_REQUIRED")
return raw, value
def _digest_file(path: Path) -> str:
if path.is_symlink() or not path.is_file():
raise ProvisioningError("SOURCE_EVIDENCE_MISSING")
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 _validate_payload(payload: Mapping[str, Any]) -> None:
expected = {
"schema", "status", "operation", "repository", "source",
"signing_identity", "allowed_capabilities", "denied_capabilities",
"runtime_binding", "provisioning_constraints", "required_preconditions",
"provisioner_contract", "issued_at", "expires_in_seconds", "nonce",
}
if set(payload) != expected or payload.get("schema") != SCHEMA or payload.get("status") != "OWNER_SIGNED_PROVISIONING_REQUEST":
raise ProvisioningError("PROVISIONING_SCHEMA_INVALID")
if payload.get("operation") != "PROVISION_PURPOSE_BOUND_TRUSTED_RELEASE_BUILDER_SIGNER" or payload.get("repository") != "aios/aios":
raise ProvisioningError("PROVISIONING_SCOPE_INVALID")
source = payload.get("source")
identity = payload.get("signing_identity")
binding = payload.get("runtime_binding")
constraints = payload.get("provisioning_constraints")
contract = payload.get("provisioner_contract")
if not all(isinstance(item, Mapping) for item in (source, identity, binding, constraints, contract)):
raise ProvisioningError("PROVISIONING_BINDING_INVALID")
required_source = OID_FIELDS | {"authority_reconciliation_completed", "pr_number", "merge_parent_oids", "source_repository_identity", "source_evidence_sha256"}
if set(source) != required_source:
raise ProvisioningError("SOURCE_BINDING_INVALID")
for name in OID_FIELDS:
_require_oid(source.get(name), "SOURCE_OID_INVALID")
if not (
source["gitea_main_oid"] == source["authority_main_oid_at_capture"] == source["governed_main_oid"] == source["protected_merge_commit"]
) or source.get("authority_reconciliation_completed") is not True:
raise ProvisioningError("SOURCE_MAIN_NOT_CONVERGED")
if not isinstance(source.get("pr_number"), int) or isinstance(source["pr_number"], bool) or source["pr_number"] < 1 or source.get("source_repository_identity") != "aios/aios":
raise ProvisioningError("SOURCE_PR_BINDING_INVALID")
parents = source.get("merge_parent_oids")
if not isinstance(parents, list) or len(parents) != 2:
raise ProvisioningError("SOURCE_ANCESTRY_INVALID")
for item in parents:
_require_oid(item, "SOURCE_ANCESTRY_INVALID")
if parents[1] != source["pr_head_oid"] or parents[0] == parents[1]:
raise ProvisioningError("SOURCE_PR_HEAD_SECOND_PARENT_INVALID")
evidence = source.get("source_evidence_sha256")
if not isinstance(evidence, Mapping) or set(evidence) != set(SOURCE_EVIDENCE_PATHS):
raise ProvisioningError("SOURCE_EVIDENCE_SET_INVALID")
for path, digest in evidence.items():
if path not in SOURCE_EVIDENCE_MODES:
raise ProvisioningError("SOURCE_EVIDENCE_PATH_INVALID")
_require_sha(digest, "SOURCE_EVIDENCE_DIGEST_INVALID")
required_identity = {
"key_id", "key_generation", "algorithm", "purpose", "trust_domain",
"private_key_export", "private_key_delivery", "public_key_registration",
"service_principal_bound", "service_principal",
}
if set(identity) != required_identity or identity.get("algorithm") != "Ed25519" or identity.get("purpose") != PURPOSE or identity.get("trust_domain") != TRUST_DOMAIN:
raise ProvisioningError("SIGNING_IDENTITY_INVALID")
if identity.get("private_key_export") is not False or identity.get("service_principal_bound") is not True:
raise ProvisioningError("SIGNING_CAPABILITY_INVALID")
if not isinstance(identity.get("key_id"), str) or not identity["key_id"] or not isinstance(identity.get("key_generation"), int) or isinstance(identity["key_generation"], bool) or identity["key_generation"] < 1:
raise ProvisioningError("SIGNING_IDENTITY_INVALID")
if not isinstance(identity.get("service_principal"), str) or identity["service_principal"] != "aios-trusted-candidate-builder.service":
raise ProvisioningError("SERVICE_PRINCIPAL_INVALID")
if set(payload["allowed_capabilities"]) != ALLOWED_CAPABILITIES or set(payload["denied_capabilities"]) != DENIED_CAPABILITIES:
raise ProvisioningError("CAPABILITY_SCOPE_INVALID")
required_binding = {
"builder_service", "builder_socket", "credential_directory", "builder_policy",
"builder_public_keyring", "builder_hmac_credential", "signing_private_key_credential",
"hmac_credential_required", "peer_authentication_required", "execution_user",
}
if set(binding) != required_binding or binding.get("builder_service") != identity["service_principal"] or binding.get("hmac_credential_required") is not True or binding.get("peer_authentication_required") is not True or binding.get("execution_user") != "root":
raise ProvisioningError("RUNTIME_BINDING_INVALID")
required_constraints = {
"one_shot", "replay_protected", "append_only_receipt", "owner_approval_scope",
"production_mutation", "protected_ref_mutation", "tag_mutation", "baseline_mutation", "credential_material_in_payload",
}
if set(constraints) != required_constraints or any(constraints.get(name) is not False for name in ("production_mutation", "protected_ref_mutation", "tag_mutation", "baseline_mutation", "credential_material_in_payload")) or constraints.get("one_shot") is not True or constraints.get("replay_protected") is not True or constraints.get("append_only_receipt") is not True or constraints.get("owner_approval_scope") != "trusted-builder-signer-provisioning-only":
raise ProvisioningError("PROVISIONING_CONSTRAINTS_INVALID")
if not isinstance(contract.get("command"), str) or not contract["command"].startswith("/") or contract.get("credential_store") != "/etc/credstore.encrypted/aios-trusted-candidate-builder" or contract.get("receipt_log") != "/var/lib/aios-gitops/trusted-builder/provisioning-receipts.jsonl":
raise ProvisioningError("PROVISIONER_CONTRACT_INVALID")
if not isinstance(payload.get("required_preconditions"), list) or not payload["required_preconditions"]:
raise ProvisioningError("PROVISIONING_PRECONDITIONS_INVALID")
try:
issued = dt.datetime.fromisoformat(str(payload["issued_at"]).replace("Z", "+00:00"))
except ValueError as exc:
raise ProvisioningError("PROVISIONING_TIME_INVALID") from exc
if issued.tzinfo is None or not isinstance(payload.get("expires_in_seconds"), int) or isinstance(payload["expires_in_seconds"], bool) or not 60 <= payload["expires_in_seconds"] <= 900:
raise ProvisioningError("PROVISIONING_TIME_INVALID")
if not isinstance(payload.get("nonce"), str) or len(payload["nonce"]) < 16:
raise ProvisioningError("PROVISIONING_NONCE_INVALID")
def build_payload(
source_root: Path,
output: Path,
*,
main_oid: str,
tree_oid: str,
pr_number: int,
pr_head_oid: str,
merge_parent_oids: list[str],
source_files: list[str],
nonce: str | None = None,
) -> dict[str, Any]:
main_oid = _require_oid(main_oid, "MAIN_OID_INVALID")
tree_oid = _require_oid(tree_oid, "TREE_OID_INVALID")
pr_head_oid = _require_oid(pr_head_oid, "PR_HEAD_OID_INVALID")
if not isinstance(pr_number, int) or isinstance(pr_number, bool) or pr_number < 1:
raise ProvisioningError("PR_NUMBER_INVALID")
if len(merge_parent_oids) != 2:
raise ProvisioningError("MERGE_PARENTS_INVALID")
parents = [_require_oid(value, "MERGE_PARENT_INVALID") for value in merge_parent_oids]
if parents[1] != pr_head_oid or parents[0] == parents[1]:
raise ProvisioningError("MERGE_PARENT_ORDER_INVALID")
evidence = _source_evidence(source_root, source_files)
payload: dict[str, Any] = {
"schema": SCHEMA,
"status": "OWNER_SIGNED_PROVISIONING_REQUEST",
"operation": "PROVISION_PURPOSE_BOUND_TRUSTED_RELEASE_BUILDER_SIGNER",
"repository": "aios/aios",
"source": {
"gitea_main_oid": main_oid,
"gitea_main_tree_oid": tree_oid,
"authority_main_oid_at_capture": main_oid,
"governed_main_oid": main_oid,
"authority_reconciliation_completed": True,
"pr_number": pr_number,
"pr_head_oid": pr_head_oid,
"protected_merge_commit": main_oid,
"merge_parent_oids": parents,
"source_repository_identity": "aios/aios",
"source_evidence_sha256": evidence,
},
"signing_identity": {
"key_id": "builder-attestation-ed25519-g1",
"key_generation": 1,
"algorithm": "Ed25519",
"purpose": PURPOSE,
"trust_domain": TRUST_DOMAIN,
"private_key_export": False,
"private_key_delivery": "server_side_systemd_credential_only",
"public_key_registration": "/etc/aios-gitops/trusted-builder/public-keyring.json",
"service_principal_bound": True,
"service_principal": "aios-trusted-candidate-builder.service",
},
"allowed_capabilities": sorted(ALLOWED_CAPABILITIES),
"denied_capabilities": sorted(DENIED_CAPABILITIES),
"runtime_binding": {
"builder_service": "aios-trusted-candidate-builder.service",
"builder_socket": "/run/aios-trusted-candidate-builder.sock",
"credential_directory": "/run/credentials/aios-trusted-candidate-builder.service",
"builder_policy": "config/gitops/trusted-candidate-builder-policy.json",
"builder_public_keyring": "/etc/aios-gitops/trusted-builder/public-keyring.json",
"builder_hmac_credential": "builder_hmac",
"signing_private_key_credential": "signing_private_key",
"hmac_credential_required": True,
"peer_authentication_required": True,
"execution_user": "root",
},
"provisioning_constraints": {
"one_shot": True,
"replay_protected": True,
"append_only_receipt": True,
"owner_approval_scope": "trusted-builder-signer-provisioning-only",
"production_mutation": False,
"protected_ref_mutation": False,
"tag_mutation": False,
"baseline_mutation": False,
"credential_material_in_payload": False,
},
"required_preconditions": [
"FINAL_COMMON_MAIN_PROTECTED_MERGE_VERIFIED",
"AUTHORITY_MAIN_RECONCILIATION_COMPLETED",
"FINAL_MAIN_MERGE_PARENT_ORDER_VERIFIED",
"PR_HEAD_SECOND_PARENT_VERIFIED",
"TRUSTED_BUILDER_IMMUTABLE_PACKAGE_CONTRACT_VERIFIED",
"PROVISIONER_GENERATES_PRIVATE_KEY_SERVER_SIDE",
"PUBLIC_KEYRING_PURPOSE_AND_KEY_ID_MATCH",
"BUILDER_HMAC_PROVISIONED_SERVER_SIDE",
"BUILDER_SERVICE_PEER_POLICY_VERIFIED",
"APPROVED_SIGNER_RECEIPT_APPEND_ONLY",
],
"provisioner_contract": {
"command": "/usr/local/libexec/aios-trusted-builder-signer-provisioner",
"credential_store": "/etc/credstore.encrypted/aios-trusted-candidate-builder",
"receipt_log": "/var/lib/aios-gitops/trusted-builder/provisioning-receipts.jsonl",
"signature_namespace": OWNER_NAMESPACE,
"signature_principal": OWNER_PRINCIPAL,
"private_key_export": False,
},
"issued_at": iso(utc_now()),
"expires_in_seconds": 900,
"nonce": nonce or "trusted-builder-provisioning-" + secrets.token_hex(16),
}
_validate_payload(payload)
output.parent.mkdir(parents=True, exist_ok=True)
if output.exists():
raise ProvisioningError("PROVISIONING_OUTPUT_EXISTS")
output.write_bytes(pretty_json(payload))
os.chmod(output, 0o600)
return payload
class ProvisionerConfig:
def __init__(self) -> None:
self.credential_store = Path(os.getenv("AIOS_TRUSTED_BUILDER_CREDENTIAL_STORE", "/etc/credstore.encrypted/aios-trusted-candidate-builder"))
self.public_keyring = Path(os.getenv("AIOS_TRUSTED_BUILDER_PUBLIC_KEYRING", "/etc/aios-gitops/trusted-builder/public-keyring.json"))
self.binding_file = Path(os.getenv("AIOS_TRUSTED_BUILDER_BINDING_FILE", "/etc/aios-gitops/trusted-builder/service-principal-binding.json"))
self.receipt_log = Path(os.getenv("AIOS_TRUSTED_BUILDER_RECEIPT_LOG", "/var/lib/aios-gitops/trusted-builder/provisioning-receipts.jsonl"))
self.allowed_signers = Path(os.getenv("AIOS_TRUSTED_BUILDER_ALLOWED_SIGNERS", "/etc/aios/trust/aios-task-grant-allowed-signers"))
self.source_root = Path(os.getenv("AIOS_TRUSTED_BUILDER_SOURCE_ROOT", "/opt/aios/trusted-candidate-builder/current"))
self.authority_git_dir = Path(os.getenv("AIOS_TRUSTED_BUILDER_AUTHORITY_GIT_DIR", "/data/aios-gitops/repo.git"))
self.service_unit = Path(os.getenv("AIOS_TRUSTED_BUILDER_SERVICE_UNIT", "/etc/systemd/system/aios-trusted-candidate-builder.service"))
self.systemd_creds = Path(os.getenv("AIOS_SYSTEMD_CREDS", "/usr/bin/systemd-creds"))
self.ssh_keygen = Path(os.getenv("AIOS_SSH_KEYGEN", "/usr/bin/ssh-keygen"))
def _safe_parent(path: Path) -> None:
if not path.is_absolute() or path.is_symlink():
raise ProvisioningError("PROVISIONING_PATH_INVALID")
path.parent.mkdir(parents=True, exist_ok=True)
if path.parent.is_symlink():
raise ProvisioningError("PROVISIONING_PATH_SYMLINK")
def _atomic_write(path: Path, data: bytes, mode: int = 0o600) -> None:
_safe_parent(path)
fd, temporary = tempfile.mkstemp(prefix=".trusted-builder-", dir=path.parent)
try:
os.fchmod(fd, mode)
with os.fdopen(fd, "wb", closefd=True) as stream:
stream.write(data)
stream.flush()
os.fsync(stream.fileno())
os.replace(temporary, path)
os.chmod(path, mode)
finally:
if os.path.exists(temporary):
os.unlink(temporary)
def _append(path: Path, row: Mapping[str, Any]) -> None:
_safe_parent(path)
fd = os.open(path, os.O_WRONLY | os.O_CREAT | os.O_APPEND, 0o600)
try:
fcntl.flock(fd, fcntl.LOCK_EX)
os.write(fd, pretty_json(dict(row)))
os.fsync(fd)
fcntl.flock(fd, fcntl.LOCK_UN)
finally:
os.close(fd)
os.chmod(path, 0o600)
def _receipts(path: Path) -> list[dict[str, Any]]:
if not path.is_file() or path.is_symlink():
return []
result: list[dict[str, Any]] = []
for line in path.read_text(encoding="utf-8").splitlines():
try:
value = json.loads(line)
except json.JSONDecodeError:
continue
if isinstance(value, dict):
result.append(value)
return result
def _run_signature_verify(config: ProvisionerConfig, payload: bytes, signature: Path) -> None:
for path in (config.allowed_signers, signature, config.ssh_keygen):
if path.is_symlink() or not path.is_file():
raise ProvisioningError("OWNER_SIGNATURE_INPUT_INVALID")
result = subprocess.run(
[str(config.ssh_keygen), "-Y", "verify", "-f", str(config.allowed_signers), "-I", OWNER_PRINCIPAL, "-n", OWNER_NAMESPACE, "-s", str(signature)],
input=payload,
capture_output=True,
check=False,
timeout=30,
)
if result.returncode != 0:
raise ProvisioningError("OWNER_SIGNATURE_VERIFY_FAILED")
def _verify_source(config: ProvisionerConfig, source: Mapping[str, Any]) -> None:
live_main = subprocess.run(
["git", "--git-dir", str(config.authority_git_dir), "rev-parse", "refs/heads/main"],
capture_output=True, text=True, check=False, timeout=30,
)
live_tree = subprocess.run(
["git", "--git-dir", str(config.authority_git_dir), "rev-parse", f"{source['protected_merge_commit']}^{{tree}}"],
capture_output=True, text=True, check=False, timeout=30,
)
parents = subprocess.run(
["git", "--git-dir", str(config.authority_git_dir), "rev-list", "--parents", "-n", "1", source["protected_merge_commit"]],
capture_output=True, text=True, check=False, timeout=30,
)
ancestry = subprocess.run(
["git", "--git-dir", str(config.authority_git_dir), "merge-base", "--is-ancestor", source["pr_head_oid"], source["protected_merge_commit"]],
capture_output=True, text=True, check=False, timeout=30,
)
expected_parents = [source["protected_merge_commit"], *source["merge_parent_oids"]]
if (live_main.returncode != 0 or live_main.stdout.strip().lower() != source["gitea_main_oid"]
or live_tree.returncode != 0 or live_tree.stdout.strip().lower() != source["gitea_main_tree_oid"]
or parents.returncode != 0 or parents.stdout.strip().split() != expected_parents
or ancestry.returncode != 0):
raise ProvisioningError("AUTHORITY_MAIN_SOURCE_DRIFT")
if source["merge_parent_oids"][1] != source["pr_head_oid"]:
raise ProvisioningError("SOURCE_PR_HEAD_SECOND_PARENT_INVALID")
verify_package_contract(config.source_root)
for relative, expected in source["source_evidence_sha256"].items():
actual = _digest_file(config.source_root / relative)
if actual != expected:
raise ProvisioningError("SOURCE_EVIDENCE_DIGEST_MISMATCH")
if config.service_unit.is_symlink() or not config.service_unit.is_file():
raise ProvisioningError("BUILDER_SERVICE_UNIT_MISSING")
text = config.service_unit.read_text(encoding="utf-8")
required = (
"User=root", "LoadCredentialEncrypted=signing_private_key:",
"LoadCredentialEncrypted=builder_hmac:", "NoNewPrivileges=true",
"PrivateNetwork=true", "RestrictAddressFamilies=AF_UNIX",
)
if any(item not in text for item in required):
raise ProvisioningError("BUILDER_SERVICE_POLICY_INVALID")
def _keyring(identity: Mapping[str, Any], public: bytes, now: dt.datetime) -> dict[str, Any]:
record = {
"algorithm": "Ed25519",
"purpose": PURPOSE,
"key_id": identity["key_id"],
"generation": identity["key_generation"],
"not_before": iso(now - dt.timedelta(minutes=1)),
"expires_at": iso(now + dt.timedelta(days=365)),
"status": "ACTIVE",
"public_key": base64.b64encode(public).decode("ascii"),
"public_key_sha256": sha256_bytes(public),
}
body = {"algorithm": "Ed25519", "purpose": PURPOSE, "keys": [record]}
return {**body, "keyring_sha256": sha256_bytes(canonical_json(body))}
def _encrypt(config: ProvisionerConfig, plain: Path, target: Path, name: str) -> None:
if config.systemd_creds.is_symlink() or not config.systemd_creds.is_file():
raise ProvisioningError("SYSTEMD_CREDS_UNAVAILABLE")
result = subprocess.run(
[str(config.systemd_creds), "--with-key=host", "--name=" + name, "encrypt", str(plain), str(target)],
capture_output=True,
check=False,
timeout=60,
)
if result.returncode != 0 or target.is_symlink() or not target.is_file() or target.stat().st_size <= 0:
raise ProvisioningError("CREDENTIAL_ENCRYPTION_FAILED")
os.chmod(target, 0o600)
def provision(config: ProvisionerConfig, payload_path: Path, signature_path: Path) -> dict[str, Any]:
if os.name != "nt" and os.geteuid() != 0:
raise ProvisioningError("ROOT_REQUIRED")
raw, payload = _read_json(payload_path)
_validate_payload(payload)
_run_signature_verify(config, raw, signature_path)
_verify_source(config, payload["source"])
operation_key = "payload:" + sha256_bytes(raw) + ":signature:" + sha256_bytes(signature_path.read_bytes())
lock_path = config.receipt_log.with_suffix(".lock")
_safe_parent(lock_path)
with lock_path.open("a+b") as lock:
fcntl.flock(lock.fileno(), fcntl.LOCK_EX)
rows = _receipts(config.receipt_log)
previous = [row for row in rows if row.get("operation_key") == operation_key]
if previous:
row = previous[-1]
if row.get("status") != "COMPLETED" or row.get("purpose") != PURPOSE or row.get("key_id") != payload["signing_identity"]["key_id"]:
raise ProvisioningError("RECEIPT_CONFLICT")
for required in (config.public_keyring, config.binding_file, config.credential_store / "signing_private_key", config.credential_store / "builder_hmac"):
if required.is_symlink() or not required.is_file():
raise ProvisioningError("RECEIPT_OUTPUT_MISSING")
return {"status": "IDEMPOTENT_REPLAY_PASS", "idempotent": True, **{key: row[key] for key in ("operation_key", "payload_sha256", "signature_sha256", "key_id", "generation", "public_key_sha256", "purpose", "service_principal_bound", "private_key_exportable")}}
outputs = [config.public_keyring, config.binding_file, config.credential_store / "signing_private_key", config.credential_store / "builder_hmac"]
if any(path.exists() or path.is_symlink() for path in outputs):
raise ProvisioningError("PREEXISTING_STATE_CONFLICT")
config.credential_store.mkdir(parents=True, exist_ok=True, mode=0o700)
os.chmod(config.credential_store, 0o700)
temp_root = Path(tempfile.mkdtemp(prefix="trusted-builder-provision-"))
try:
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
now = utc_now()
private = Ed25519PrivateKey.generate()
public = private.public_key().public_bytes(serialization.Encoding.Raw, serialization.PublicFormat.Raw)
private_path = temp_root / "signing_private_key.pem"
private_path.write_bytes(private.private_bytes(serialization.Encoding.PEM, serialization.PrivateFormat.PKCS8, serialization.NoEncryption()))
os.chmod(private_path, 0o600)
hmac_path = temp_root / "builder_hmac"
hmac_path.write_bytes(secrets.token_bytes(32))
os.chmod(hmac_path, 0o600)
enc_private = temp_root / "signing_private_key.cred"
enc_hmac = temp_root / "builder_hmac.cred"
_encrypt(config, private_path, enc_private, "signing_private_key")
_encrypt(config, hmac_path, enc_hmac, "builder_hmac")
keyring = _keyring(payload["signing_identity"], public, now)
binding = {
"schema": "aios.trusted-builder.service-principal-binding.v1",
"purpose": PURPOSE,
"service_principal": payload["signing_identity"]["service_principal"],
"key_id": payload["signing_identity"]["key_id"],
"generation": payload["signing_identity"]["key_generation"],
"algorithm": "Ed25519",
"private_key_export": False,
"public_key_sha256": sha256_bytes(public),
"source_commit": payload["source"]["gitea_main_oid"],
"source_tree": payload["source"]["gitea_main_tree_oid"],
"payload_sha256": sha256_bytes(raw),
}
_atomic_write(temp_root / "public-keyring.json", pretty_json(keyring))
_atomic_write(temp_root / "service-principal-binding.json", pretty_json(binding))
config.public_keyring.parent.mkdir(parents=True, exist_ok=True, mode=0o700)
config.binding_file.parent.mkdir(parents=True, exist_ok=True, mode=0o700)
os.replace(temp_root / "signing_private_key.cred", config.credential_store / "signing_private_key")
os.replace(temp_root / "builder_hmac.cred", config.credential_store / "builder_hmac")
os.replace(temp_root / "public-keyring.json", config.public_keyring)
os.replace(temp_root / "service-principal-binding.json", config.binding_file)
for path in outputs:
os.chmod(path, 0o600)
receipt = {
"schema": "aios.trusted-builder-signer-provisioning-receipt.v1",
"status": "COMPLETED",
"created_at": iso(now),
"operation_key": operation_key,
"payload_sha256": sha256_bytes(raw),
"signature_sha256": sha256_bytes(signature_path.read_bytes()),
"source_commit": payload["source"]["gitea_main_oid"],
"source_tree": payload["source"]["gitea_main_tree_oid"],
"key_id": payload["signing_identity"]["key_id"],
"generation": payload["signing_identity"]["key_generation"],
"algorithm": "Ed25519",
"purpose": PURPOSE,
"service_principal": payload["signing_identity"]["service_principal"],
"service_principal_bound": True,
"public_key_sha256": sha256_bytes(public),
"private_key_exportable": False,
"credential_projection": "systemd-encrypted-host-credential",
"negative_capability_tests": {name: True for name in sorted(DENIED_CAPABILITIES)},
"production_mutation": False,
"protected_ref_mutation": False,
"tag_mutation": False,
"baseline_mutation": False,
}
_append(config.receipt_log, receipt)
return {"status": "PROVISIONED", "idempotent": False, **{key: receipt[key] for key in ("operation_key", "payload_sha256", "signature_sha256", "source_commit", "source_tree", "key_id", "generation", "purpose", "service_principal_bound", "public_key_sha256", "private_key_exportable")}, "negative_capability_tests": receipt["negative_capability_tests"]}
finally:
shutil.rmtree(temp_root, ignore_errors=True)
def main(argv: list[str] | None = None) -> int:
parser = argparse.ArgumentParser(description="Provision the purpose-bound trusted-builder signer")
parser.add_argument("--payload", type=Path)
parser.add_argument("--signature", type=Path)
parser.add_argument("--result-json", type=Path)
parser.add_argument("--self-test", action="store_true")
parser.add_argument("--verify-package", action="store_true")
parser.add_argument("--source-root", type=Path)
args = parser.parse_args(argv)
if args.self_test:
print("PASS")
return 0
if args.verify_package:
if args.source_root is None:
parser.error("--verify-package requires --source-root")
try:
verify_package_contract(args.source_root)
print("PACKAGE_CONTRACT_VALID=PASS")
return 0
except ProvisioningError as exc:
print(json.dumps({"status": "BLOCKED", "error_code": exc.code}, sort_keys=True))
return 42
if not args.payload or not args.signature or not args.result_json:
parser.error("--payload, --signature and --result-json are required")
try:
result = provision(ProvisionerConfig(), args.payload, args.signature)
_atomic_write(args.result_json, pretty_json(result))
print(json.dumps(result, sort_keys=True))
return 0
except ProvisioningError as exc:
result = {"schema": "aios.trusted-builder-signer-provisioning-result.v1", "status": "BLOCKED", "error_code": exc.code}
try:
_atomic_write(args.result_json, pretty_json(result))
except Exception:
pass
print(json.dumps(result, sort_keys=True))
return 42
if __name__ == "__main__":
raise SystemExit(main())
@@ -1,117 +0,0 @@
#!/usr/bin/env python3
"""Plan-bound production adapter for releases newer than the legacy baseline.
All identity comes from the already verified executor request. The adapter has
no Git/ref/tag authority and refuses a missing candidate/release-plan binding.
"""
from __future__ import annotations
import hashlib, json, os, shutil, subprocess, sys, tempfile, urllib.error, urllib.request
from pathlib import Path
from typing import Any, Mapping
class AdapterBlocked(RuntimeError): pass
ALLOWED_SERVICE_PORTS = {"aios-v2r2c2-production.service": 18928}
def _safe(value: str) -> str:
if not value or any(ch not in "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_.-" for ch in value):
raise AdapterBlocked("invalid_release_identity")
return value
def _run(*args: str, check: bool = True) -> subprocess.CompletedProcess[str]:
result = subprocess.run(args, check=False, capture_output=True, text=True, timeout=60, env={"PATH": os.environ.get("PATH", "")})
if check and result.returncode != 0: raise AdapterBlocked(result.stderr.strip() or "system_command_failed")
return result
def _sha(path: Path) -> str:
if path.is_symlink() or not path.is_file(): raise AdapterBlocked("artifact_must_be_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 _binding(value: Mapping[str, Any]) -> dict[str, Any]:
required=("version","target_oid","artifact_sha256","artifact_path","artifact_size","production_service","production_port","candidate_id","release_plan_sha256")
if any(key not in value for key in required): raise AdapterBlocked("trusted_binding_incomplete")
if str(value["version"]).startswith("V2R1") or not value["candidate_id"] or not value["release_plan_sha256"]: raise AdapterBlocked("legacy_or_unbound_release_rejected")
service = str(value["production_service"])
port = int(value["production_port"])
if ALLOWED_SERVICE_PORTS.get(service) != port:
raise AdapterBlocked("production_adapter_policy_rejected")
return {key:value[key] for key in required}
def _paths(binding: Mapping[str, Any]) -> dict[str, Path]:
version=_safe(str(binding["version"])); service=_safe(str(binding["production_service"]))
root=Path(os.environ.get("AIOS_RELEASE_ROOT", "/opt/aios/releases"))
slug=version.lower()
return {"release_dir":root/(version+"-"+str(binding["target_oid"])[:12]+"-"+str(binding["artifact_sha256"])[:12]),"current_link":Path("/opt/aios")/slug,"env_file":Path("/etc/aios")/(slug+".env"),"state_file":Path("/var/lib/aios")/slug/"deployment-state.json","unit_file":Path("/etc/systemd/system")/(service+".service" if not service.endswith(".service") else service),"health_url":f"http://127.0.0.1:{int(binding['production_port'])}/health"}
def _health(url: str, binding: Mapping[str, Any]) -> dict[str, Any]:
try:
with urllib.request.urlopen(url, timeout=5) as response: body=json.loads(response.read().decode()); status=response.status
except (OSError, ValueError, urllib.error.URLError): return {"status":"UNAVAILABLE"}
exact=(status==200 and body.get("version")==binding["version"] and body.get("commit_oid")==binding["target_oid"] and body.get("artifact_sha256")==binding["artifact_sha256"] 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 _extract(artifact: Path, destination: Path) -> None:
import tarfile
with tarfile.open(artifact, "r:*") as archive:
for member in archive.getmembers():
name=Path(member.name)
if name.is_absolute() or ".." in name.parts or member.issym() or member.islnk() or not member.isfile() or not member.name.startswith("payload/"):
raise AdapterBlocked("unsafe_artifact_member")
rel=name.relative_to("payload"); target=destination/rel; target.parent.mkdir(parents=True,exist_ok=True)
source=archive.extractfile(member)
if source is None: raise AdapterBlocked("artifact_member_unreadable")
target.write_bytes(source.read()); os.chmod(target,0o755 if rel.parts and rel.parts[0]=="scripts" else 0o644)
def probe(value: Mapping[str, Any]) -> dict[str, Any]:
binding=_binding(value); p=_paths(binding); active=_run("systemctl","is-active","--quiet",str(binding["production_service"]),check=False).returncode==0
return _health(str(p["health_url"]),binding) if active else {"status":"NOT_APPLIED","version":binding["version"],"target_oid":binding["target_oid"],"artifact_sha256":binding["artifact_sha256"],"production_deployed":False,"deployment_state":"ABSENT"}
def apply(value: Mapping[str, Any]) -> dict[str, Any]:
binding=_binding(value); p=_paths(binding); p["release_dir"].parent.mkdir(parents=True, exist_ok=True); artifact=Path(str(binding["artifact_path"]))
if artifact.stat().st_size != int(binding["artifact_size"]) or _sha(artifact)!=binding["artifact_sha256"]: raise AdapterBlocked("artifact_binding_mismatch")
marker={"schema":"aios.trusted-runtime-release-binding.v1",**binding}
marker_path=p["release_dir"]/"DEPLOYMENT-BINDING.json"
if p["release_dir"].exists():
if not marker_path.is_file() or json.loads(marker_path.read_text())!=marker: raise AdapterBlocked("existing_release_binding_conflict")
else:
temp=Path(tempfile.mkdtemp(prefix=p["release_dir"].name+".",dir=str(p["release_dir"].parent)))
try:
_extract(artifact,temp); (temp/"DEPLOYMENT-BINDING.json").write_text(json.dumps(marker,sort_keys=True,separators=(",",":"))+"\n"); os.rename(temp,p["release_dir"])
except Exception:
shutil.rmtree(temp,ignore_errors=True); raise
version_file=p["release_dir"] / "VERSION"
if not version_file.is_file() or version_file.read_text().strip()!=binding["version"]: raise AdapterBlocked("release_version_mismatch")
unit_source=p["release_dir"] / "deploy" / "aios-v2" / "systemd" / str(binding["production_service"])
if not unit_source.is_file(): raise AdapterBlocked("release_unit_missing")
p["unit_file"].parent.mkdir(parents=True,exist_ok=True); shutil.copyfile(unit_source,p["unit_file"])
p["current_link"].parent.mkdir(parents=True,exist_ok=True)
if p["current_link"].exists() and not p["current_link"].is_symlink(): raise AdapterBlocked("current_release_path_conflict")
if not p["current_link"].is_symlink(): p["current_link"].symlink_to(p["release_dir"])
p["state_file"].parent.mkdir(parents=True,exist_ok=True); p["state_file"].write_text(json.dumps({"version":binding["version"],"commit_oid":binding["target_oid"],"artifact_sha256":binding["artifact_sha256"],"candidate_id":binding["candidate_id"],"release_plan_sha256":binding["release_plan_sha256"],"deployment_state":"ACTIVE","production_deployed":True},sort_keys=True)+"\n")
env=(f"RELEASE_VERSION={binding['version']}\n" f"TARGET_OID={binding['target_oid']}\n"
f"ARTIFACT_SHA256={binding['artifact_sha256']}\n" f"CANDIDATE_ID={binding['candidate_id']}\n"
f"RELEASE_PLAN_SHA256={binding['release_plan_sha256']}\n" f"DEPLOYMENT_STATE=ACTIVE\n"
f"DEPLOYMENT_STATE_FILE={p['state_file']}\n" f"AIOS_V2R2C2_PORT={binding['production_port']}\n"
"AIOS_GITOPS_WRITES_ENABLED=false\n")
p["env_file"].parent.mkdir(parents=True,exist_ok=True); p["env_file"].write_text(env)
_run("systemctl","daemon-reload"); _run("systemctl","enable",str(binding["production_service"])); _run("systemctl","restart",str(binding["production_service"]))
result=_health(str(p["health_url"]),binding)
if result["status"]!="ACTIVE_VERIFIED": raise AdapterBlocked("production_health_binding_mismatch")
return result
def rollback(value: Mapping[str, Any]) -> dict[str, Any]:
binding=_binding(value); _run("systemctl","stop",str(binding["production_service"]),check=False); _run("systemctl","disable",str(binding["production_service"]),check=False)
p=_paths(binding); p["state_file"].parent.mkdir(parents=True,exist_ok=True); p["state_file"].write_text(json.dumps({"version":binding["version"],"commit_oid":binding["target_oid"],"artifact_sha256":binding["artifact_sha256"],"deployment_state":"ROLLED_BACK","production_deployed":False},sort_keys=True)+"\n")
return {"status":"ROLLED_BACK","version":binding["version"],"target_oid":binding["target_oid"],"artifact_sha256":binding["artifact_sha256"],"production_deployed":False,"deployment_state":"ROLLED_BACK"}
def main() -> int:
value=json.loads(sys.stdin.read()); operation=value.get("operation")
result=probe(value) if operation=="probe" else apply(value) if operation=="apply" else rollback(value) if operation=="rollback" else (_ for _ in ()).throw(AdapterBlocked("unsupported_operation"))
print(json.dumps(result,sort_keys=True)); return 0
if __name__=="__main__":
try: raise SystemExit(main())
except Exception as exc: print(json.dumps({"status":"FAIL_CLOSED","error":type(exc).__name__,"reason":str(exc)},sort_keys=True)); raise SystemExit(2)
@@ -1,101 +0,0 @@
#!/usr/bin/env python3
"""Canonical, fail-closed projection for trusted release execution plans.
A release plan is the only authority for candidate, source, artifact, policy,
rollback, and release identity. Callers may not provide executable paths or
replace individual fields after the plan has been accepted.
"""
from __future__ import annotations
import hashlib, json, re, stat
from dataclasses import dataclass
from pathlib import Path
from typing import Any, Mapping
OID_RE = re.compile(r"^[0-9a-f]{40}$")
SHA_RE = re.compile(r"^[0-9a-f]{64}$")
PLAN_ID_RE = re.compile(r"^sha256:[0-9a-f]{64}$")
IDENTIFIER_RE = re.compile(r"^[A-Za-z0-9][A-Za-z0-9_.:-]{2,255}$")
TOP_LEVEL_KEYS = frozenset({"schema","version","candidate_id","source_commit","tree_oid","artifact_sha256","manifest_sha256","package_closure_sha256","deployment_plan_sha256","policy_sha256","release_plan_id","approval_plan_id","evidence_closure_oid","campaign_authorization_sha256","campaign_generation","artifact","rollback","baseline","adapter_policy","release_plan_sha256"})
FORBIDDEN_KEYS = frozenset({"command","commands","executable","exec","shell","argv","adapter_command","tag_command","deploy_command","registry_command","baseline_command","sudo","force_push","direct_main_push"})
class PlanError(ValueError):
"""A release plan is malformed, inconsistent, or unsafe."""
def canonical_json(value: object) -> bytes:
return (json.dumps(value, sort_keys=True, separators=(",", ":")) + "\n").encode("utf-8")
def _sha(value: Any, label: str) -> str:
if not isinstance(value, str) or not SHA_RE.fullmatch(value): raise PlanError(f"{label}_invalid")
return value
def _oid(value: Any, label: str) -> str:
if not isinstance(value, str) or not OID_RE.fullmatch(value): raise PlanError(f"{label}_invalid")
return value
def _walk_forbidden(value: Any) -> None:
if isinstance(value, Mapping):
for key, item in value.items():
if str(key).lower() in FORBIDDEN_KEYS: raise PlanError(f"forbidden_plan_key:{key}")
_walk_forbidden(item)
elif isinstance(value, list):
for item in value: _walk_forbidden(item)
def _exact_mapping(value: Any, required: set[str], label: str) -> Mapping[str, Any]:
if not isinstance(value, Mapping) or set(value) != required: raise PlanError(f"{label}_fields_invalid")
return value
def _secure_json(path: Path) -> dict[str, Any]:
try: st = path.lstat()
except OSError as exc: raise PlanError(f"plan_unreadable:{path}") from exc
if path.is_symlink() or not stat.S_ISREG(st.st_mode): raise PlanError("plan_must_be_regular_file")
if st.st_uid != 0 or st.st_mode & 0o022: raise PlanError("plan_ownership_or_mode_invalid")
try: value = json.loads(path.read_text(encoding="utf-8"))
except (OSError, ValueError) as exc: raise PlanError("plan_json_invalid") from exc
if not isinstance(value, dict): raise PlanError("plan_object_required")
return value
@dataclass(frozen=True)
class TrustedReleasePlan:
value: Mapping[str, Any]
release_plan_sha256: str
@classmethod
def from_mapping(cls, raw: Mapping[str, Any]) -> "TrustedReleasePlan":
if not isinstance(raw, Mapping) or set(raw) - TOP_LEVEL_KEYS: raise PlanError("release_plan_fields_invalid")
required = TOP_LEVEL_KEYS - {"release_plan_sha256"}
if set(raw) not in (required, TOP_LEVEL_KEYS): raise PlanError("release_plan_fields_incomplete")
_walk_forbidden(raw)
if raw.get("schema") != "aios.trusted-release-execution-plan.v1": raise PlanError("release_plan_schema_invalid")
version = raw.get("version")
if not isinstance(version, str) or not re.fullmatch(r"V[0-9]+R[0-9]+C[0-9]+", version): raise PlanError("version_invalid")
if not isinstance(raw.get("candidate_id"), str) or not IDENTIFIER_RE.fullmatch(raw["candidate_id"]): raise PlanError("candidate_id_invalid")
for key in ("source_commit","tree_oid","evidence_closure_oid"): _oid(raw.get(key), key)
for key in ("artifact_sha256","manifest_sha256","package_closure_sha256","deployment_plan_sha256","policy_sha256","campaign_authorization_sha256"): _sha(raw.get(key), key)
for key in ("release_plan_id","approval_plan_id"):
if not isinstance(raw.get(key), str) or not PLAN_ID_RE.fullmatch(raw[key]): raise PlanError(f"{key}_invalid")
if not isinstance(raw.get("campaign_generation"), int) or raw["campaign_generation"] < 1: raise PlanError("campaign_generation_invalid")
artifact = _exact_mapping(raw.get("artifact"), {"path","size"}, "artifact")
if not isinstance(artifact["path"], str) or not artifact["path"].startswith("/") or "\\" in artifact["path"]: raise PlanError("artifact_path_invalid")
if not isinstance(artifact["size"], int) or artifact["size"] <= 0: raise PlanError("artifact_size_invalid")
rollback = _exact_mapping(raw.get("rollback"), {"version","target_oid","artifact_sha256"}, "rollback")
if not isinstance(rollback["version"], str) or not rollback["version"] or rollback["version"] == version: raise PlanError("rollback_version_invalid")
_oid(rollback["target_oid"], "rollback_target_oid"); _sha(rollback["artifact_sha256"], "rollback_artifact_sha256")
baseline = _exact_mapping(raw.get("baseline"), {"version","target_oid"}, "baseline")
if baseline["version"] != rollback["version"]: raise PlanError("baseline_rollback_version_mismatch")
_oid(baseline["target_oid"], "baseline_target_oid")
adapter = _exact_mapping(raw.get("adapter_policy"), {"policy_id","service","port"}, "adapter_policy")
if adapter["policy_id"] != "aios.production-adapter.allowlisted.v1": raise PlanError("adapter_policy_invalid")
if not isinstance(adapter["service"], str) or not IDENTIFIER_RE.fullmatch(adapter["service"]): raise PlanError("adapter_service_invalid")
if not isinstance(adapter["port"], int) or not 1024 <= adapter["port"] <= 65535: raise PlanError("adapter_port_invalid")
payload = {key: raw[key] for key in TOP_LEVEL_KEYS if key != "release_plan_sha256"}
digest = hashlib.sha256(canonical_json(payload)).hexdigest()
if raw.get("release_plan_sha256") is not None and raw["release_plan_sha256"] != digest: raise PlanError("release_plan_sha256_mismatch")
return cls(value=dict(payload), release_plan_sha256=digest)
@classmethod
def load(cls, path: Path) -> "TrustedReleasePlan": return cls.from_mapping(_secure_json(path))
@property
def candidate_binding_sha256(self) -> str:
value = {key: self.value[key] for key in ("candidate_id","source_commit","tree_oid","artifact_sha256","manifest_sha256")}
return hashlib.sha256(canonical_json(value)).hexdigest()
def as_dict(self) -> dict[str, Any]: return {**dict(self.value),"release_plan_sha256":self.release_plan_sha256,"candidate_binding_sha256":self.candidate_binding_sha256}
def assert_binding(self, binding: Mapping[str, Any]) -> None:
expected={"version":self.value["version"],"target_oid":self.value["source_commit"],"evidence_closure_oid":self.value["evidence_closure_oid"],"artifact_sha256":self.value["artifact_sha256"],"artifact_size":self.value["artifact"]["size"],"approval_plan_id":self.value["approval_plan_id"]}
for key,value in expected.items():
if binding.get(key) != value: raise PlanError(f"binding_plan_mismatch:{key}")
if binding.get("candidate_id") is not None and binding.get("candidate_id") != self.value["candidate_id"]:
raise PlanError("binding_plan_mismatch:candidate_id")
def assert_artifact(self, artifact: Mapping[str, Any]) -> None:
expected=self.value["artifact"]
if artifact.get("sha256") != self.value["artifact_sha256"] or artifact.get("size") != expected["size"]: raise PlanError("artifact_plan_mismatch")
if artifact.get("manifest_sha256") != self.value["manifest_sha256"]: raise PlanError("manifest_plan_mismatch")
if artifact.get("package_closure_sha256") != self.value["package_closure_sha256"]: raise PlanError("package_closure_plan_mismatch")
@@ -1,5 +0,0 @@
"""V2R2C2 production health entry point; all identity is plan supplied."""
from v2r1c6_privacy_tools import main
if __name__ == "__main__":
raise SystemExit(main())
@@ -1,57 +0,0 @@
{
"allowed_peer_cgroups": [
"/system.slice/aios-gitops-model-api-v2r2c1-staging.service"
],
"allowed_peer_executable_prefix": "/usr/bin/python3",
"allowed_peer_runtime_prefix": "/opt/aios/gitops-model-api/",
"allowed_peer_runtime_suffix": "/runtime/app/aios_model_api_staging.py",
"allowed_peer_uid": 1000,
"evidence_layout": {
"artifact": "{version}/artifact.tar.gz",
"canary": "{version}/canary.json",
"gitea": "{version}/gitea.json",
"manifest": "{version}/manifest.json",
"package_closure": "{version}/checksums.sha256",
"policy": "{version}/trusted-release-policy.json",
"rollback": "{version}/rollback.json",
"secret_scan": "{version}/secret-scan.json",
"tests": "{version}/tests.json"
},
"governed_recipes": {},
"maximum_request_age_seconds": 60,
"operation": "BuildTrustedCandidateV1",
"readiness_operation": "ProbeTrustedCandidateBuilderV1",
"recipes": {
"V2R2C2": {
"base_oid": "d2b5a8339a34a8f958a177f8a3b9e8296a57a7a6",
"candidate_id": "rc-v2r2c2-d2b5a8339a34",
"mode": "fixed",
"purpose": "AIOS_TRUSTED_RELEASE_BUILDER",
"source_commit": "d2b5a8339a34a8f958a177f8a3b9e8296a57a7a6",
"task_id": "v12-durable-trusted-release-controller-activation-20260829",
"version": "V2R2C2"
},
"V2R2C3": {
"derives_from": "V2R2C2",
"mode": "governed-derived-only",
"purpose": "AIOS_TRUSTED_RELEASE_BUILDER",
"version": "V2R2C3"
}
},
"repository": "aios/aios",
"request_schema": "aios.trusted-candidate-builder.request.v1",
"request_timeout_seconds": 30,
"required_checks": [
"authoritative_git",
"artifact",
"canary",
"gitea",
"manifest",
"package_closure",
"rollback",
"secret_scan",
"tests"
],
"response_schema": "aios.trusted-candidate-builder.response.v1",
"schema": "aios.trusted-candidate-builder-policy.v1"
}
-20
View File
@@ -1,20 +0,0 @@
{
"authoritative_git_dirs": [
"/data/aios-gitops/repo.git"
],
"cutover_links": [
"/opt/aios/v2r2c2"
],
"dropin_paths": [
"/var/lib/aios-gitops/release-only/v2r2c2-d2b5a8339a34/payload/deploy/aios-v2/systemd/aios-v2r2c2-production.service"
],
"release_roots": [
"/var/lib/aios-gitops/release-only"
],
"repository_identities": [
"aios/aios"
],
"services": [
"aios-v2r2c2-production.service"
]
}
@@ -0,0 +1,18 @@
# Non-secret staging configuration for the OpenClaw engineering backend.
#
# Discover the exact binary with `command -v openclaw`.
# Do not point this at the chat/WeChat Gateway HTTP endpoint.
AIOS_AGENT_RUNTIME_BACKEND_ARGV_JSON='["/usr/bin/python3","/usr/local/lib/aios-agent-runtime-broker/openclaw_engineering_backend.py"]'
# Required: exact absolute path discovered on hw.imruyi.top.
AIOS_OPENCLAW_BIN=/REPLACE/WITH/ABSOLUTE/OPENCLAW/PATH
# Required: provider/model verified on the installed OpenClaw build.
AIOS_OPENCLAW_MODEL=REPLACE_WITH_VERIFIED_PROVIDER_MODEL
# Non-secret map: provider environment variable -> systemd credential file name.
# Example after separately projecting an encrypted credential:
# AIOS_OPENCLAW_PROVIDER_CREDENTIAL_MAP_JSON='{"OPENAI_API_KEY":"openai_api_key"}'
AIOS_OPENCLAW_PROVIDER_CREDENTIAL_MAP_JSON='{}'
AIOS_OPENCLAW_EXEC_TIMEOUT_SECONDS=1800
@@ -1,28 +0,0 @@
[Unit]
Description=AIOS V2R2C2 governed trusted release health service
After=network.target
[Service]
Type=simple
User=aios-v2r1c7
Group=aios-v2r1c7
WorkingDirectory=/opt/aios/v2r2c2
EnvironmentFile=/etc/aios/v2r2c2.env
ExecStart=/usr/bin/python3 /opt/aios/v2r2c2/active-runtime/app/v2r2c2_health_service.py --serve --host 127.0.0.1 --port ${AIOS_V2R2C2_PORT}
Restart=on-failure
RestartSec=5
NoNewPrivileges=true
PrivateTmp=true
ProtectHome=true
ProtectSystem=strict
ProtectKernelLogs=true
ProtectKernelModules=true
ProtectKernelTunables=true
ProtectControlGroups=true
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6
IPAddressDeny=any
IPAddressAllow=localhost
UMask=0077
[Install]
WantedBy=multi-user.target
@@ -0,0 +1,66 @@
#!/bin/sh
set -eu
if [ "$(id -u)" -ne 0 ]; then
echo "must run as root" >&2
exit 20
fi
SRC_ROOT="${1:-.}"
WORKER_SRC="$SRC_ROOT/services/aios-autonomous-agent-worker"
BROKER_SRC="$SRC_ROOT/services/aios-agent-runtime-broker"
UNIT_SRC="$SRC_ROOT/deploy/systemd"
grep -Fq 'AIOS_GOVERNED_WRITE_API_URL=http://127.0.0.1:19998' \
"$UNIT_SRC/aios-autonomous-agent-worker.service" || {
echo "staging bridge binding missing" >&2
exit 21
}
getent group aios-agent-runtime >/dev/null 2>&1 || groupadd --system aios-agent-runtime
id aios-agent-runtime >/dev/null 2>&1 || \
useradd --system --gid aios-agent-runtime --home-dir /nonexistent --shell /usr/sbin/nologin aios-agent-runtime
id aios-autonomous-agent-worker >/dev/null 2>&1 || \
useradd --system --home-dir /nonexistent --shell /usr/sbin/nologin aios-autonomous-agent-worker
usermod -a -G aios-agent-runtime aios-autonomous-agent-worker
install -d -m 0755 /usr/local/lib/aios-autonomous-agent-worker
install -d -m 0755 /usr/local/lib/aios-agent-runtime-broker
install -d -m 0750 -o aios-autonomous-agent-worker -g aios-autonomous-agent-worker \
/var/lib/aios-autonomous-agent-worker
install -d -m 0770 -o aios-autonomous-agent-worker -g aios-agent-runtime \
/var/lib/aios-autonomous-agent-worker/workspaces
for file in autonomous_worker.py intent_runtime.py runtime_client.py register.py runtime_selftest.py; do
install -m 0755 "$WORKER_SRC/$file" "/usr/local/lib/aios-autonomous-agent-worker/$file"
done
install -m 0755 "$BROKER_SRC/broker.py" /usr/local/lib/aios-agent-runtime-broker/broker.py
install -m 0755 "$BROKER_SRC/openclaw_engineering_backend.py" /usr/local/lib/aios-agent-runtime-broker/openclaw_engineering_backend.py
install -m 0644 "$BROKER_SRC/RUNTIME_CONTRACT.md" /usr/local/lib/aios-agent-runtime-broker/RUNTIME_CONTRACT.md
install -m 0644 "$UNIT_SRC/aios-autonomous-agent-worker.service" /etc/systemd/system/
install -m 0644 "$UNIT_SRC/aios-autonomous-agent-register.service" /etc/systemd/system/
install -m 0644 "$UNIT_SRC/aios-agent-runtime-broker@.service" /etc/systemd/system/
systemctl daemon-reload
cat <<'EOF'
Files installed. Services were NOT started or enabled.
Required before activation:
1. Discover `openclaw` with `command -v openclaw`.
2. Verify `openclaw agent exec --help` exposes the required headless coding
options on the installed version.
3. Create /etc/aios/agent-runtime-openclaw.conf from the non-secret example.
4. Project provider/model credentials into the OpenClaw broker service with a
separate systemd encrypted credential. Never reuse the Online Agent Task
token or writer lease.
5. Start only `aios-agent-runtime-broker@openclaw.service`.
6. Run runtime_selftest.py and require a real worktree mutation with unchanged
Git HEAD.
7. Only after self-test PASS register the autonomous identity and enable
continuous polling.
The autonomous worker is staging-only and rejects any Governed Bridge other
than http://127.0.0.1:19998.
EOF
@@ -1,9 +0,0 @@
#!/usr/bin/python3
"""Installed entry point for the governed trusted-builder provisioner."""
from __future__ import annotations
import runpy
import sys
sys.path.insert(0, "/opt/aios/trusted-candidate-builder/current/app")
runpy.run_module("trusted_builder_signer_provisioning", run_name="__main__")
@@ -0,0 +1,42 @@
[Unit]
Description=AIOS isolated %i engineering runtime broker
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=aios-agent-runtime
Group=aios-agent-runtime
Environment=PYTHONUNBUFFERED=1
Environment=AIOS_AGENT_RUNTIME_ADAPTER=%i
Environment=AIOS_AGENT_RUNTIME_SOCKET=/run/aios-agent-runtime/%i.sock
Environment=AIOS_AGENT_RUNTIME_WORKSPACE_ROOT=/var/lib/aios-autonomous-agent-worker/workspaces
Environment=AIOS_AGENT_RUNTIME_ALLOWED_PEER_USER=aios-autonomous-agent-worker
Environment=AIOS_AGENT_RUNTIME_CONNECTION_TIMEOUT_SECONDS=15
Environment=AIOS_AGENT_RUNTIME_BACKEND_TIMEOUT_SECONDS=3600
EnvironmentFile=/etc/aios/agent-runtime-%i.conf
ExecStart=/usr/bin/python3 -B /usr/local/lib/aios-agent-runtime-broker/broker.py
Restart=always
RestartSec=5
RuntimeDirectory=aios-agent-runtime
RuntimeDirectoryMode=0770
UMask=0007
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
ProtectKernelTunables=true
ProtectKernelModules=true
ProtectControlGroups=true
LockPersonality=true
RestrictSUIDSGID=true
RestrictRealtime=true
RestrictNamespaces=true
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6
ReadWritePaths=/var/lib/aios-autonomous-agent-worker/workspaces /run/aios-agent-runtime
InaccessiblePaths=-/data/aios-gitops/repo.git -/etc/credstore.encrypted/aios-autonomous-agent-worker
CapabilityBoundingSet=
AmbientCapabilities=
[Install]
WantedBy=multi-user.target
@@ -0,0 +1,5 @@
# Example only. Choose the credential name/provider used by the verified model.
# The encrypted credential file contains secret material and must never be committed.
[Service]
# Example:
# LoadCredentialEncrypted=openai_api_key:/etc/credstore.encrypted/aios-agent-runtime-openclaw/openai_api_key
@@ -1,37 +0,0 @@
[Unit]
Description=AIOS owner-controlled Task initial execution projection API
After=aios-agent-task-api.service aios-chatgpt-bridge-v2-staging.service
Wants=aios-agent-task-api.service aios-chatgpt-bridge-v2-staging.service
[Service]
Type=simple
User=aios-agent-task
Group=aios-agent-task
LoadCredential=agent_task_control_token:/etc/aios-gitops/agent-task-control.token
Environment=AIOS_AGENT_TASK_DB=/var/lib/aios-agent-task/state.sqlite3
Environment=AIOS_AGENT_TASK_CONTROL_TOKEN_FILE=%d/agent_task_control_token
Environment=AIOS_GOVERNED_WRITE_API_URL=http://127.0.0.1:18892
Environment=AIOS_AGENT_TASK_INITIAL_EXECUTION_PROJECTION_AUTHORIZATION_ID=owner-initial-execution-projection-recovery-20260820
Environment=AIOS_TASK_INITIAL_EXECUTION_PROJECTION_HOST=127.0.0.1
Environment=AIOS_TASK_INITIAL_EXECUTION_PROJECTION_PORT=18943
ExecStart=/usr/bin/python3 -B /opt/aios/current/active-runtime/app/aios_agent_task_initial_execution_projection.py
Restart=on-failure
RestartSec=2
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
ProtectKernelTunables=true
ProtectKernelModules=true
ProtectControlGroups=true
LockPersonality=true
RestrictSUIDSGID=true
RestrictRealtime=true
RestrictNamespaces=true
MemoryDenyWriteExecute=true
RestrictAddressFamilies=AF_UNIX AF_INET
ReadWritePaths=/var/lib/aios-agent-task
UMask=0077
[Install]
WantedBy=multi-user.target
@@ -15,9 +15,17 @@ ExecStart=/usr/bin/python3 /usr/libexec/aios-approved-release-executor/current/a
--authorization ${AIOS_RELEASE_AUTHORIZATION} \
--repository ${AIOS_GITOPS_AUTHORITATIVE_GIT_DIR} \
--state-root ${AIOS_RELEASE_EXECUTION_STATE_ROOT} \
--release-plan ${AIOS_RELEASE_PLAN} \
--artifact ${AIOS_RELEASE_ARTIFACT} \
--artifact-size ${AIOS_RELEASE_ARTIFACT_SIZE} \
--version ${AIOS_RELEASE_VERSION} \
--target-oid ${AIOS_RELEASE_TARGET_OID} \
--evidence-closure-oid ${AIOS_RELEASE_EVIDENCE_CLOSURE_OID} \
--artifact-sha256 ${AIOS_RELEASE_ARTIFACT_SHA256} \
--approval-plan-id ${AIOS_RELEASE_APPROVAL_PLAN_ID} \
--production-service aios-v2r1c7-production.service \
--production-port 18927 \
--tag-adapter /usr/bin/python3 /usr/libexec/aios-approved-release-executor/current/active-runtime/app/governed_tag_adapter.py \
--deploy-adapter /usr/bin/python3 /usr/libexec/aios-approved-release-executor/current/active-runtime/app/trusted_production_adapter.py \
--deploy-adapter /usr/bin/python3 /usr/libexec/aios-approved-release-executor/current/active-runtime/app/v2r1c7_production_adapter.py \
--registry-adapter /usr/bin/env AIOS_RELEASE_METADATA_MODE=registry /usr/bin/python3 /usr/libexec/aios-approved-release-executor/current/active-runtime/app/release_metadata_adapter.py \
--baseline-adapter /usr/bin/env AIOS_RELEASE_METADATA_MODE=baseline /usr/bin/python3 /usr/libexec/aios-approved-release-executor/current/active-runtime/app/release_metadata_adapter.py
NoNewPrivileges=true
@@ -0,0 +1,38 @@
[Unit]
Description=AIOS Autonomous Intent Agent register/recover
After=network-online.target aios-agent-task-api.service aios-agent-runtime-broker@openclaw.service
Wants=network-online.target
Requires=aios-agent-runtime-broker@openclaw.service
Before=aios-autonomous-agent-worker.service
[Service]
Type=oneshot
RemainAfterExit=yes
User=root
Group=root
LoadCredential=bridge_control_token:/run/credentials/aios-gitops-model-api.service/agent_task_control_token
Environment=AIOS_AGENT_TASK_API_URL=http://127.0.0.1:18940
Environment=AIOS_AGENT_ID=online-engineering-worker-cn-network-mkt-autonomous-20260815
Environment=AIOS_AGENT_VERSION=2026-08-15.4-autonomous-intent
Environment=AIOS_WORKER_REGISTRATION_STATE=/var/lib/aios-autonomous-agent-worker/registration
Environment=AIOS_WORKER_ENCRYPTED_CREDENTIAL=/etc/credstore.encrypted/aios-autonomous-agent-worker/agent_token
Environment=AIOS_HERMES_BROKER_SOCKET=/run/aios-agent-runtime/hermes.sock
Environment=AIOS_OPENCLAW_BROKER_SOCKET=/run/aios-agent-runtime/openclaw.sock
ExecStart=/usr/bin/python3 -B /usr/local/lib/aios-autonomous-agent-worker/register.py
TimeoutStartSec=120
UMask=0077
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
ProtectKernelTunables=true
ProtectKernelModules=true
ProtectControlGroups=true
LockPersonality=true
RestrictSUIDSGID=true
RestrictRealtime=true
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6
ReadWritePaths=/etc/credstore.encrypted /var/lib/aios-autonomous-agent-worker
[Install]
WantedBy=multi-user.target
@@ -0,0 +1,56 @@
[Unit]
Description=AIOS Autonomous Intent Engineering Worker (staging)
After=network-online.target aios-agent-task-api.service aios-agent-runtime-broker@openclaw.service aios-autonomous-agent-register.service
Wants=network-online.target aios-agent-runtime-broker@openclaw.service
Requires=aios-autonomous-agent-register.service
[Service]
Type=simple
User=aios-autonomous-agent-worker
Group=aios-autonomous-agent-worker
SupplementaryGroups=aios-agent-runtime gitops
LoadCredentialEncrypted=agent_token:/etc/credstore.encrypted/aios-autonomous-agent-worker/agent_token
Environment=PYTHONUNBUFFERED=1
Environment=AIOS_AGENT_TASK_API_URL=http://127.0.0.1:18940
Environment=AIOS_GOVERNED_WRITE_API_URL=http://127.0.0.1:19998
Environment=AIOS_AGENT_ID=online-engineering-worker-cn-network-mkt-autonomous-20260815
Environment=AIOS_AGENT_VERSION=2026-08-15.4-autonomous-intent
Environment=AIOS_AUTONOMOUS_REPOSITORY=aios/aios
Environment=AIOS_AUTONOMOUS_BASE_REF=refs/heads/main
Environment=AIOS_AUTHORITY_GIT_DIR=/data/aios-gitops/repo.git
Environment=AIOS_AUTONOMOUS_STATE_ROOT=/var/lib/aios-autonomous-agent-worker
Environment=AIOS_AUTONOMOUS_WORKSPACE_ROOT=/var/lib/aios-autonomous-agent-worker/workspaces
Environment=AIOS_AUTONOMOUS_POLL_SECONDS=5
Environment=AIOS_AUTONOMOUS_HEARTBEAT_SECONDS=10
Environment=AIOS_AUTONOMOUS_LEASE_SECONDS=300
Environment=AIOS_AUTONOMOUS_RUNTIME_TIMEOUT_SECONDS=3600
Environment=AIOS_AUTONOMOUS_DEFAULT_ADAPTER=openclaw
Environment=AIOS_AGENT_RUNTIME_SHARED_GROUP=aios-agent-runtime
Environment=AIOS_HERMES_BROKER_SOCKET=/run/aios-agent-runtime/hermes.sock
Environment=AIOS_OPENCLAW_BROKER_SOCKET=/run/aios-agent-runtime/openclaw.sock
ExecStart=/usr/bin/python3 -B /usr/local/lib/aios-autonomous-agent-worker/autonomous_worker.py
Restart=always
RestartSec=5
TimeoutStopSec=30
UMask=0027
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
ProtectKernelTunables=true
ProtectKernelModules=true
ProtectControlGroups=true
LockPersonality=true
RestrictSUIDSGID=true
RestrictRealtime=true
RestrictNamespaces=true
MemoryDenyWriteExecute=true
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6
ReadOnlyPaths=/data/aios-gitops/repo.git
ReadWritePaths=/var/lib/aios-autonomous-agent-worker
InaccessiblePaths=-/etc/credstore.encrypted
CapabilityBoundingSet=
AmbientCapabilities=
[Install]
WantedBy=multi-user.target
@@ -4,32 +4,19 @@ After=network-online.target
[Service]
Type=simple
User=aiman
Group=gitops
PermissionsStartOnly=true
WorkingDirectory=@RUNTIME_ROOT@
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_GITOPS_WRITES_ENABLED=false
Environment=AIOS_GITEA_API_BASE=https://g.cnnetmkt.top/git/api/v1
Environment=AIOS_GITEA_TOKEN_FILE=/run/credentials/aios-chatgpt-bridge-v2-staging.service/gitea_token
Environment=AIOS_GITEA_REMOTE=gitea-http
Environment=AIOS_GITOPS_BASELINE_AUTHORITY=/var/lib/aios-gitops/model-api-staging/baseline/current.json
Environment=AIOS_CONTROL_PLANE_TARGET_VERSION=V2R2C1
Environment=AIOS_CONTROL_PLANE_TARGET_OID=@SOURCE_OID@
Environment=AIOS_CONTROL_PLANE_ARTIFACT_SHA256=@ARTIFACT_SHA256@
Environment=PYTHONPATH=@RUNTIME_ROOT@:@RUNTIME_ROOT@/app:@RUNTIME_ROOT@/runtime-agent-os
Environment=AIOS_DEPLOYMENT_STATE=STAGING
LoadCredential=gitea_token:/run/credentials/aios-governance-executor.service/gitea-governance-executor-token
ExecStartPre=/usr/bin/install -d -o aiman -g gitops -m 0750 /var/lib/aios-gitops/bridge-v2-staging
ExecStartPre=/usr/bin/setfacl -m u:aiman:--x /usr/local/libexec
ExecStart=/usr/bin/python3 -B @RUNTIME_ROOT@/app/aios_chatgpt_bridge_v2_server.py
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 /data/aios-gitops/repo.git /data/aios-gitops/repo.git/worktrees
ReadWritePaths=/var/lib/aios-gitops/bridge-v2-staging
Restart=on-failure
[Install]
@@ -1,14 +0,0 @@
# Non-secret activation template for a verified immutable model-api package.
# Install only through the governed runtime activation procedure.
[Service]
Environment=AIOS_GITOPS_WORKSPACE=/opt/aios/gitops-model-api/<VERIFIED_PACKAGE_OID>/runtime
Environment=AIOS_TRUSTED_CANDIDATE_BUILDER_ENABLED=true
Environment=AIOS_TRUSTED_CANDIDATE_BUILDER_SOCKET=/run/aios-trusted-candidate-builder.sock
Environment=AIOS_TRUSTED_CANDIDATE_BUILDER_POLICY=/opt/aios/gitops-model-api/<VERIFIED_PACKAGE_OID>/runtime/config/gitops/trusted-candidate-builder-policy.json
Environment=AIOS_TRUSTED_CANDIDATE_BUILDER_PUBLIC_KEYRING=/etc/aios-gitops/trusted-builder/public-keyring.json
Environment=AIOS_TRUSTED_CANDIDATE_BUILDER_CREDENTIALS_DIRECTORY=%d
Environment=AIOS_SILENT_RELEASE_TRUSTED_POLICY=/opt/aios/gitops-model-api/<VERIFIED_PACKAGE_OID>/runtime/config/gitops/trusted-release-policy.json
LoadCredential=builder_hmac:/etc/aios-gitops/credential-references/trusted-candidate-builder-hmac
# The credential source above is a reference only; no secret value belongs here.
# Keep execution shadow-disabled and do not add Owner/deploy/tag/baseline switches.
Environment=AIOS_SILENT_RELEASE_EXECUTION_ENABLED=false
@@ -0,0 +1,46 @@
[Unit]
Description=AIOS generic governed Online Agent Worker (2026-08-15)
After=aios-agent-task-api.service aios-online-agent-worker-register-20260815.service
Wants=aios-online-agent-worker-register-20260815.service
[Service]
Type=simple
User=aios-engineering-worker
Group=aios-engineering-worker
SupplementaryGroups=gitops
LoadCredentialEncrypted=agent_token:/etc/credstore.encrypted/aios-online-agent-worker-20260815/agent_token
Environment=PYTHONUNBUFFERED=1
Environment=AIOS_AGENT_TASK_API_URL=http://127.0.0.1:18940
Environment=AIOS_GOVERNED_WRITE_API_URL=http://127.0.0.1:18892
Environment=AIOS_AGENT_ID=online-engineering-worker-cn-network-mkt-generic-20260815
Environment=AIOS_AGENT_TYPE=trusted-online-engineering-worker
Environment=AIOS_AGENT_VERSION=2026-08-15.3-generic
Environment=AIOS_AUTHORITY_GIT_DIR=/data/aios-gitops/repo.git
Environment=AIOS_WORKER_STATE_ROOT=/var/lib/aios-online-agent-worker-20260815
Environment=AIOS_WORKER_WORKSPACE_ROOT=/var/lib/aios-online-agent-worker-20260815/workspaces
Environment=AIOS_WORKER_POLL_SECONDS=5
Environment=AIOS_WORKER_HEARTBEAT_SECONDS=10
Environment=AIOS_WORKER_LEASE_SECONDS=300
ExecStart=/usr/bin/python3 -B /usr/local/lib/aios-online-agent-worker-20260815/online_agent_worker.py
Restart=always
RestartSec=5
TimeoutStopSec=15
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
ProtectKernelTunables=true
ProtectKernelModules=true
ProtectControlGroups=true
LockPersonality=true
RestrictSUIDSGID=true
RestrictRealtime=true
RestrictNamespaces=true
MemoryDenyWriteExecute=true
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6
ReadOnlyPaths=/data/aios-gitops/repo.git
ReadWritePaths=/var/lib/aios-online-agent-worker-20260815
UMask=0027
[Install]
WantedBy=multi-user.target
@@ -0,0 +1,36 @@
[Unit]
Description=AIOS generic Online Agent register-or-recover (2026-08-15)
After=network-online.target aios-gitops-model-api.service aios-agent-task-api.service
Wants=network-online.target
Before=aios-online-agent-worker-20260815.service
[Service]
Type=oneshot
RemainAfterExit=yes
User=root
Group=root
LoadCredential=bridge_control_token:/run/credentials/aios-gitops-model-api.service/agent_task_control_token
Environment=AIOS_AGENT_TASK_API_URL=http://127.0.0.1:18940
Environment=AIOS_AGENT_ID=online-engineering-worker-cn-network-mkt-generic-20260815
Environment=AIOS_AGENT_TYPE=trusted-online-engineering-worker
Environment=AIOS_AGENT_VERSION=2026-08-15.3-generic
Environment=AIOS_WORKER_ENCRYPTED_CREDENTIAL=/etc/credstore.encrypted/aios-online-agent-worker-20260815/agent_token
Environment=AIOS_WORKER_REGISTRATION_STATE=/var/lib/aios-online-agent-worker-20260815/registration
ExecStart=/usr/bin/python3 -B /usr/local/lib/aios-online-agent-worker-20260815/register.py
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
ProtectKernelTunables=true
ProtectKernelModules=true
ProtectControlGroups=true
LockPersonality=true
RestrictSUIDSGID=true
RestrictRealtime=true
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6
ReadWritePaths=/etc/credstore.encrypted /var/lib/aios-online-agent-worker-20260815
UMask=0077
TimeoutStartSec=120
[Install]
WantedBy=multi-user.target
@@ -1,44 +0,0 @@
[Unit]
Description=AIOS trusted candidate builder
Requires=aios-trusted-candidate-builder.socket
After=aios-trusted-candidate-builder.socket
[Service]
Type=simple
User=root
Group=root
UMask=0077
LoadCredentialEncrypted=signing_private_key:/etc/credstore.encrypted/aios-trusted-candidate-builder/signing_private_key
LoadCredentialEncrypted=builder_hmac:/etc/credstore.encrypted/aios-trusted-candidate-builder/builder_hmac
Environment=AIOS_TRUSTED_BUILDER_POLICY=/opt/aios/trusted-candidate-builder/current/config/gitops/trusted-candidate-builder-policy.json
Environment=AIOS_TRUSTED_BUILDER_GIT_DIR=/data/aios-gitops/repo.git
Environment=AIOS_TRUSTED_BUILDER_EVIDENCE_ROOT=/var/lib/aios-trusted-candidate-builder/evidence
Environment=AIOS_TRUSTED_BUILDER_NONCE_LEDGER=/var/lib/aios-trusted-candidate-builder/nonce-ledger.json
Environment=AIOS_TRUSTED_BUILDER_KEY_ID=builder-attestation-ed25519-g1
Environment=AIOS_TRUSTED_BUILDER_KEY_GENERATION=1
Environment=PYTHONDONTWRITEBYTECODE=1
ExecStart=/usr/bin/python3 -B /opt/aios/trusted-candidate-builder/current/app/aios_trusted_candidate_builder.py --socket-fd 3 --policy ${AIOS_TRUSTED_BUILDER_POLICY}
Restart=on-failure
RestartSec=2
StateDirectory=aios-trusted-candidate-builder
StateDirectoryMode=0700
NoNewPrivileges=true
PrivateTmp=true
PrivateNetwork=true
ProtectSystem=strict
ProtectHome=true
ProtectControlGroups=true
ProtectKernelTunables=true
ProtectKernelModules=true
ProtectKernelLogs=true
RestrictAddressFamilies=AF_UNIX
LockPersonality=true
MemoryDenyWriteExecute=true
RestrictSUIDSGID=true
SystemCallArchitectures=native
CapabilityBoundingSet=
ReadOnlyPaths=/opt/aios/trusted-candidate-builder /data/aios-gitops/repo.git /var/lib/aios-trusted-candidate-builder/evidence
ReadWritePaths=/var/lib/aios-trusted-candidate-builder
[Install]
WantedBy=multi-user.target
@@ -1,13 +0,0 @@
[Unit]
Description=AIOS trusted candidate builder AF_UNIX socket
[Socket]
ListenStream=/run/aios-trusted-candidate-builder.sock
SocketUser=root
SocketGroup=gitops
SocketMode=0660
RemoveOnStop=yes
Accept=no
[Install]
WantedBy=sockets.target
@@ -1,57 +0,0 @@
{
"activation": {
"credential_installation": false,
"daemon_reload": true,
"enable_units": [
"aios-trusted-candidate-builder.socket"
],
"service_start": "socket-activation-only",
"signer_invocation": false
},
"files": [
{
"install_mode": "0644",
"source_mode": "0644",
"source_path": "active-runtime/app/trusted_builder_signer_provisioning.py"
},
{
"install_mode": "0644",
"source_mode": "0644",
"source_path": "active-runtime/app/aios_trusted_candidate_builder.py"
},
{
"install_mode": "0644",
"source_mode": "0644",
"source_path": "config/gitops/trusted-candidate-builder-policy.json"
},
{
"install_mode": "0644",
"source_mode": "0644",
"source_path": "deploy/systemd/aios-trusted-candidate-builder.service"
},
{
"install_mode": "0644",
"source_mode": "0644",
"source_path": "deploy/systemd/aios-trusted-candidate-builder.socket"
},
{
"install_mode": "0644",
"source_mode": "0644",
"source_path": "deploy/systemd/aios-gitops-model-api-trusted-builder.conf.example"
},
{
"install_mode": "0755",
"source_mode": "0644",
"source_path": "deploy/libexec/aios-trusted-builder-signer-provisioner"
},
{
"install_mode": "0644",
"source_mode": "0644",
"source_path": "deploy/trusted-builder/aios-trusted-builder-package-manifest.json"
}
],
"package_name": "aios-trusted-candidate-builder",
"schema": "aios.trusted-builder-package.v1",
"source_root": "/opt/aios/trusted-candidate-builder/current",
"version": 1
}
+110
View File
@@ -0,0 +1,110 @@
# Autonomous Intent Worker v2
## Goal
A Custom GPT or other trusted producer creates a prose engineering task with:
- `goal`
- `instructions`
- `success_criteria`
- `required_capabilities`
A long-running autonomous worker claims only tasks explicitly marked with:
- `intent.task`
- `engineering.execute`
and only when the requested engineering runtime capability is actually
available, e.g. `agent.runtime.hermes`.
The runtime edits a disposable worktree. The worker independently inspects the
diff, scans for secrets, reruns verification, and persists through staging
Governed GitOps.
## Security domains
The Online Agent worker owns task/lease/governed-write credentials.
The runtime broker is a separate service identity. It authorizes the worker
over Unix `SO_PEERCRED`, opens the worktree using `O_PATH|O_NOFOLLOW`, and
passes a pinned `/proc/self/fd/<fd>` path to the engineering backend. The
backend never receives the Agent Task token or writer lease.
The worker rejects:
- a runtime-created Git commit (`HEAD != base_oid`);
- direct authoritative ref mutation/push;
- sensitive credential/key paths;
- obvious secret material;
- non-staging Governed Bridge endpoints.
## Staging boundary
`autonomous_worker.py` intentionally rejects any Governed Write API base other
than:
`http://127.0.0.1:19998`
Production/main/tag/baseline/force-push writes remain outside this worker.
## Migration protection
The worker does not infer intent from prose. Historical tasks without
`intent.task` are skipped before claim, so they do not lose an attempt.
The bootstrap task
`implement-autonomous-intent-worker-v2-20260815` currently lacks the intent
marker and is therefore intentionally not claimable by this worker. That is a
control-plane/bootstrap concern, not a reason to weaken the runtime rule.
## Runtime requirement
A chat/WeChat Hermes endpoint is not enough. A compliant engineering backend
must:
1. accept `aios.online-agent.runtime-request.v1`;
2. inspect and mutate the supplied disposable worktree;
3. leave Git HEAD unchanged;
4. return `aios.online-agent.runtime-result.v1`.
The exact installed Hermes/Codex interface is host-specific and must be
discovered before configuring the broker. No CLI flags are invented here.
## Activation gate
Do not register or enable the autonomous consumer until:
1. a real engineering backend is found;
2. broker is configured with that backend;
3. `runtime_selftest.py` reports PASS;
4. backend has no Agent Task token or writer lease.
After activation, complete D/E/F/G:
- D: native intent task, no `metadata.execution`;
- E: goal-only engineering task, runtime finds files/root cause;
- F: unavailable runtime capability remains unclaimed;
- G: restart during real runtime execution, no duplicate commit/branch.
## OpenClaw backend candidate
A current OpenClaw installation can be used as an autonomous engineering
runtime without making OpenClaw itself an AIOS Task API client.
The bundled wrapper uses the official headless coding entry point:
```text
openclaw agent exec
--message-file -
--cwd <pinned-disposable-worktree>
--model <verified-provider/model>
--auth-env-only
--timeout <seconds>
--json
```
Reference used for implementation:
`https://docs.openclaw.ai/cli/agent`
The wrapper maps the stable OpenClaw JSON envelope into
`aios.online-agent.runtime-result.v1`. The AIOS worker remains responsible for
claim/heartbeat, independent diff verification and Governed GitOps.
OpenClaw is advertised as `agent.runtime.openclaw`, not as Hermes or Codex.
-44
View File
@@ -241,50 +241,6 @@ components:
properties:
candidate_id: {type: string}
paths:
/v2/commit-files:
post:
operationId: commitFilesGoverned
summary: Submit one atomic governed commit containing multiple files
requestBody:
required: true
content:
application/json:
schema:
type: object
additionalProperties: false
required:
- task_id
- repository
- expected_branch_oid
- target_branch
- generation
- idempotency_key
- files
- message
properties:
task_id: {type: string}
job_id: {type: string, description: External durable Job identity for exact completed-replay reconciliation}
source_lease_epoch: {type: integer, minimum: 1}
repository: {type: string, example: aios/aios}
expected_branch_oid: {type: string, pattern: '^[0-9a-f]{40}$'}
target_branch: {type: string, pattern: '^agent/[A-Za-z0-9._/-]+$'}
generation: {type: integer, minimum: 1}
idempotency_key: {type: string}
files:
type: array
minItems: 1
items:
type: object
additionalProperties: false
required: [path, content_base64]
properties:
path: {type: string, minLength: 1, maxLength: 512}
content_base64: {type: string}
message: {type: string, minLength: 1, maxLength: 200}
responses:
'200': {description: Governed commit job and verified branch result}
'400': {description: Contract or policy rejection}
'409': {description: Branch CAS or idempotency conflict}
/createReleaseIntent:
post:
operationId: createReleaseIntent
@@ -1,35 +0,0 @@
# Trusted-builder signer provisioning
`trusted_builder_signer_provisioning.py` provisions the purpose-bound
`AIOS_TRUSTED_RELEASE_BUILDER` signer only from an exact
`aios.trusted-builder-signer-provisioning.v2` payload and detached SSH signature
for `aios-task-grant-signer` in namespace `aios-owner-approval-v1`.
A fresh payload is bound to the final common Gitea, authority, and governed
`main` OID; the protected merge tree; the ordered two parents; and the selected
PR head as the merge's second parent. The provisioner rechecks those facts in
the authority repository and rejects ancestry or source drift. It does not
reuse PR69, PR70, or a release-candidate recipe binding.
The payload evidence is an exact allowlist: provisioner, candidate builder,
policy, service, socket, model-API config template, launcher, and the versioned
package manifest. Missing, extra, modified, or mode-incompatible evidence is
rejected. The package manifest is non-secret and declares daemon reload plus
socket activation, with no credential installation or signer invocation. It
requires the installed launcher target mode to be `0755`; source checkout mode
is treated separately from installed package mode. A controlled deterministic
worktree may hold ordinary source evidence at `0600`, `0640`, `0644`, or `0664`; the
installed package still enforces its declared target modes.
The provisioner generates the Ed25519 key and builder HMAC only on the host.
Plaintext exists only in its private temporary directory and is encrypted with
`systemd-creds --with-key=host`; the service receives encrypted credentials via
`LoadCredentialEncrypted`. The public keyring and service-principal binding
contain no private key.
Receipts append at
`/var/lib/aios-gitops/trusted-builder/provisioning-receipts.jsonl`. An exact
replay returns `IDEMPOTENT_REPLAY_PASS`; mismatched or unreceipted state fails
closed. Provisioning is configuration only: activation, restart persistence,
public-key verification and negative-capability tests are separate gates before
`TRUSTED_BUILDER_SIGNER_READINESS=PASS`.
@@ -1,80 +0,0 @@
#!/usr/bin/env bash
set -Eeuo pipefail
umask 027
ROOT="/data/aios-gitops"
NGINX_SITE="/etc/nginx/sites-enabled/aios-user-portal.conf"
STAMP="$(date -u +%Y%m%dT%H%M%SZ)"
BACKUP_DIR="$ROOT/backups/action-surface-final-$STAMP"
LOG="$ROOT/logs/action-surface-final-$STAMP.log"
mkdir -p "$BACKUP_DIR" "$ROOT/logs"
exec > >(tee -a "$LOG") 2>&1
sudo cp -a "$NGINX_SITE" "$BACKUP_DIR/aios-user-portal.conf"
SITE_BACKUP="$BACKUP_DIR/aios-user-portal.conf"
python3 - "$SITE_BACKUP" "$BACKUP_DIR/aios-user-portal.conf.next" <<'PY'
from pathlib import Path
import sys
source, output = map(Path, sys.argv[1:])
text = source.read_text(encoding="utf-8")
marker = " # AIOS external Action governed-read ownership"
if marker in text:
raise SystemExit("governed-read locations already present in backup")
block = r''' # AIOS external Action governed-read ownership
# These reads are owned by the authenticated 18890 facade, not the 18893 model API.
location = /api/governed-write-capabilities {
proxy_pass http://127.0.0.1:18890/api/governed-write-capabilities;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Authorization $http_authorization;
proxy_set_header X-AIOS-Token $http_x_aios_token;
}
location = /api/control-plane-snapshot {
proxy_pass http://127.0.0.1:18890/api/control-plane-snapshot;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Authorization $http_authorization;
proxy_set_header X-AIOS-Token $http_x_aios_token;
}
location = /api/online-agent-task {
proxy_pass http://127.0.0.1:18890/api/online-agent-task;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Authorization $http_authorization;
proxy_set_header X-AIOS-Token $http_x_aios_token;
}
location = /api/online-agent-task-events {
proxy_pass http://127.0.0.1:18890/api/online-agent-task-events;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Authorization $http_authorization;
proxy_set_header X-AIOS-Token $http_x_aios_token;
}
'''
needle = " location /api/ {"
if text.count(needle) != 1:
raise SystemExit("expected exactly one generic /api/ location")
text = text.replace(needle, block + "\n" + needle, 1)
output.write_text(text, encoding="utf-8")
PY
sudo cp "$BACKUP_DIR/aios-user-portal.conf.next" "$NGINX_SITE"
sudo nginx -t
sudo systemctl reload nginx
printf 'ACTION_SURFACE_FINAL_OK backup=%s log=%s\n' "$BACKUP_DIR" "$LOG"
@@ -1,121 +0,0 @@
{
"schema": "aios.canonical-task-projection-intent.v1",
"projection_id": "projection-v2r2c2-parent-recovery-20260826",
"projection_generation": 1,
"idempotency_key": "projection-v2r2c2-parent-recovery-20260826-v1",
"task": {
"task_id": "TASK-CONV-V2R2C2-RECONCILE-SNAPSHOT-20260826",
"goal": "Complete V2R2C2 staging-only control-plane convergence and repair the control-plane self-recovery defect exposed by the missing canonical Task projection.",
"instructions": "Resume the immutable runbook at runbooks/agent-v2r2c2-control-plane-convergence-20260825.md from branch agent/v2r2c2-convergence-integration-20260825. Preserve the parent task/job/generation/branch/OID. Complete canonical projection, worker claim, execution binding, heartbeat, WriterLease/CAS, required convergence code, tests, immutable staging build/deploy, first acceptance, restart-persistent second acceptance, and governed Evidence Closure. Recovery subgraphs must return to the parent goal. Never mutate main, V2R2C1 protected tag, production baseline, or production services.",
"success_criteria": [
{
"id": "control_plane_self_recovery",
"required": true,
"expected": "PASS"
},
{
"id": "canonical_task_projection",
"required": true,
"expected": "PASS"
},
{
"id": "worker_execution_path",
"required": true,
"expected": "PASS"
},
{
"id": "writer_path",
"required": true,
"expected": "PASS"
},
{
"id": "governed_mutation",
"required": true,
"expected": "PASS"
},
{
"id": "v2r2c2_code",
"required": true,
"expected": "PASS"
},
{
"id": "tests",
"required": true,
"expected": "PASS"
},
{
"id": "staging_deployment",
"required": true,
"expected": "PASS"
},
{
"id": "runtime_component_drift",
"required": true,
"expected": "PASS"
},
{
"id": "release_consistency",
"required": true,
"expected": "PASS"
},
{
"id": "tag_publication",
"required": true,
"expected": "PASS"
},
{
"id": "release_acceptance",
"required": true,
"expected": "PASS"
},
{
"id": "restart_persistence",
"required": true,
"expected": "PASS"
},
{
"id": "evidence_closure",
"required": true,
"expected": "PASS"
}
],
"constraints": {
"staging_only": true,
"production_write": false,
"main_direct_write": false,
"protected_tag_write": false,
"baseline_write": false,
"force_push": false,
"direct_task_db_write_outside_task_plane": false,
"secret_exposure": false,
"return_to_parent_after_recovery": true
},
"required_capabilities": [],
"metadata": {
"parent_goal": "V2R2C2 staging-only control-plane convergence",
"governed_task_id": "TASK-CONV-V2R2C2-RECONCILE-SNAPSHOT-20260826",
"bridge_job_id": "job-9eee1142f2fc8bb7b571d065c01f40e0",
"recovery_reason": "MISSING_DURABLE_CANONICAL_TASK_PROJECTION_RECOVERY",
"runbook_ref": "runbooks/agent-v2r2c2-control-plane-convergence-20260825.md",
"runbook_oid": "2afc2cf41fc9090cf01e3882d1b8db8d54cf1844",
"runbook_sha256": "a500504a76fb99b26541eaf4edd842a942992ae170fc0f3838530189ca672320"
},
"priority": 100,
"max_attempts": 20,
"deadline": null
},
"governed_binding": {
"governed_task_id": "TASK-CONV-V2R2C2-RECONCILE-SNAPSHOT-20260826",
"bridge_job_id": "job-9eee1142f2fc8bb7b571d065c01f40e0",
"repository": "aios/aios",
"branch": "agent/v2r2c2-convergence-integration-20260825",
"generation": 2,
"base_oid": "2afc2cf41fc9090cf01e3882d1b8db8d54cf1844",
"expected_branch_oid": "2afc2cf41fc9090cf01e3882d1b8db8d54cf1844"
},
"source_artifact": {
"ref": "git:2afc2cf41fc9090cf01e3882d1b8db8d54cf1844:runbooks/agent-v2r2c2-control-plane-convergence-20260825.md",
"sha256": "a500504a76fb99b26541eaf4edd842a942992ae170fc0f3838530189ca672320"
},
"task_payload_sha256": "dcc293ffcd9db36ea8dd35b79a15e77cca57ee4ad5d6f976e24ee454d46eb0c9"
}
-21
View File
@@ -1,21 +0,0 @@
"""Canonical AIOS release-version parser shared by runtime and release tools."""
from __future__ import annotations
import re
# The suffix preserves already-supported annotated release names while the
# canonical VnRnCn namespace remains accepted exactly.
RELEASE_VERSION_PATTERN = r"^V[0-9]+R[0-9]+C[0-9]+(?:-[A-Za-z0-9_.-]+)?$"
RELEASE_VERSION_RE = re.compile(RELEASE_VERSION_PATTERN)
def parse_release_version(version: object) -> str:
value = str(version or "").strip()
if not RELEASE_VERSION_RE.fullmatch(value):
raise ValueError("invalid release version")
return value
def validate_release_version(version: object) -> str:
return parse_release_version(version)
@@ -1,75 +0,0 @@
# Action Contract Regression Remediation — 2026-08-25
## Scope
Governed task: `fix-action-contracts-20260825-1407`
Base: `3244f0f17f8845592ac94d585f5ebd65693c9f29`
Target branch: `agent/fix-action-contracts-20260825-1407`
## Verified regressions
1. `getReleaseConsistency` and `runReleaseAcceptance` fail before evaluation with:
`Gitea API URL must be HTTP(S)`.
V2R2C1 release evidence at `a8b906d1bc24088c19647e98de6a679268f6d0a2`
proves Gitea API readback passed for target `998d30304787e31e5ae607c0bd86b5edb2b0e5ad`.
Therefore current staging/contract overlay configuration has regressed.
2. `getReleaseSaga` fails with:
`Permission denied: '/var/lib/aios-gitops/silent-release'`.
Runtime service identity must have read/traverse access to the durable release state root.
Fix ownership/group/ACL at deployment time; do not broaden to world-writable permissions.
3. `getTagPublicationStatus` rejects the authoritative approved version `V2R2C1`
as `invalid release version`, while `getGovernedWriteCapabilities` returns the same
version as the approved release scope. The two Action contracts are internally inconsistent.
Release-version validation must accept the canonical AIOS `^V[0-9]+R[0-9]+C[0-9]+$`
namespace (or, preferably, use one shared validator for all release Actions).
4. `commitFilesGoverned` is unusable from the published Action schema.
Runtime capability metadata declares required fields:
`task_id, repository, expected_branch_oid, target_branch, generation, idempotency_key, files, message`,
but the exposed tool contract has no `files` input. Regenerate/publish the Action schema from
the same operation contract source and add a contract-parity test.
5. `reconcileGitOpsJob` returned `DURABLE_BINDING_INVALID` for a completed governed
single-file job that was otherwise read back as `COMPLETED`.
Reconciliation of a valid terminal job should be idempotent/read-only and must not require
a stale or absent active lease. Add a regression case for terminal completed jobs.
6. `getDiff` returns `ResponseTooLargeError` / `GIT_OUTPUT_LIMIT_EXCEEDED` for large diffs.
Add server-side bounded output: path/stat summary plus truncation metadata, or pagination.
The Action must never fail merely because a valid repository diff is large.
## Required implementation changes
- Centralize release-version parsing and reuse it in tag status, candidate, consistency, and acceptance.
- Validate/normalize Gitea API URL at service startup and fail health checks with a configuration-specific diagnostic.
- Ensure the deployment unit projects a valid HTTP(S) Gitea API base URL into the staging overlay.
- Correct durable release-state directory ownership/ACL for the runtime service account.
- Generate the external Action schema from the runtime operation contract; parity-test required fields.
- Make terminal reconciliation idempotent and independent of expired active writer leases.
- Bound diff output and return `truncated=true` with deterministic metadata rather than transport failure.
## Acceptance gates
All must pass on the same deployed overlay:
- `getReleaseConsistency(V2R2C1, 998d303..., a8b906d..., approved_plan)` returns a structured result, not URL error.
- `runReleaseAcceptance(...)` reaches policy evaluation, not URL error.
- `getTagPublicationStatus("V2R2C1")` returns structured publication state.
- `getReleaseSaga(nonexistent_candidate)` returns NOT_FOUND/empty-state semantics, never filesystem permission failure.
- Published `commitFilesGoverned` schema exposes `files` and a two-file isolated branch commit succeeds.
- Reconcile of a completed governed job is idempotent.
- Large diff returns bounded/truncated data rather than transport exception.
- Existing protected-ref controls remain unchanged: no direct main/tag/baseline/production write.
## Proven stable reference
V2R2C1 evidence closure (`a8b906d1bc24088c19647e98de6a679268f6d0a2`) records:
- Gitea Git/API readback PASS
- staging runtime PASS
- restart recovery PASS
- 59 mandatory tests passed / 0 failed
This evidence should be used as the compatibility baseline when repairing the current
`contract-overlay-v2` / `scheme-a-323b615b...` staging layer.
@@ -1,80 +0,0 @@
# AIOS Action contract repair evidence — 2026-08-25
## Disposition
Final disposition: `NEEDS_REWORK`.
The contract repair is deployed to staging and the focused acceptance checks are green. The task is not eligible for `SUCCEEDED`: `runReleaseAcceptance` returns structured policy output with `accepted=false` because the existing publication state for protected tag `V2R2C1` is `REMOTE_PUSH_PENDING`, and the repository-wide suite still has the two pre-existing signer/event-bus failures recorded below. The online Task API row is still `QUEUED`; it was not changed by direct database manipulation or an unauthorized synthetic claim/result.
No production release, `main`, release tag, baseline registry, or production service was directly modified.
## Scope and pre-change facts
- Repository: `aios/aios`; governed branch: `agent/fix-action-contracts-20260825-1407`.
- Initial task branch HEAD: `35eec9afe1432e8466d60040b2fb1049bb41242a`.
- Stable reference: `V2R2C1`; source commit `998d30304787e31e5ae607c0bd86b5edb2b0e5ad`; evidence closure `a8b906d1bc24088c19647e98de6a679268f6d0a2`.
- Pre-change refs: authority `main=3244f0f17f8845592ac94d585f5ebd65693c9f29`, tag `V2R2C1=07fc2746b688f5f243f29b39de39f32edc3dd9d2`; these remained unchanged.
- Staging services run as `User=aiman`, `Group=gitops`. The actual staging Gitea API base is `https://g.cnnetmkt.top/git/api/v1`, supplied by systemd environment rather than business-code host constants.
- Pre-change full test baseline: `707 passed, 2 skipped, 154 subtests passed, 2 failed`. The failures were the stale `/tmp/aios-agent-events-test` event-bus fixture and the real signer self-test.
## Root causes and repairs
1. Gitea URL validation occurred at release-operation time while the staging overlay's API environment was not consistently projected into all runtime paths. The canonical HTTP(S) validator is now called during model and Bridge startup; `getReleaseConsistency` and `runReleaseAcceptance` use the actual staging environment URL.
2. `silent-release` was not consistently writable by the real service identity, and the credential helper parent directory blocked `aiman` traversal. The persisted staging systemd overlay creates the state directory as `aiman:gitops` mode `0750` and grants only `u:aiman:--x` on `/usr/local/libexec`; no world-writable mode was used. The staging bridge also has the minimum repository write path required by the governed AuthorityGitExecutor/CAS flow.
3. Release version parsing was split across callers and rejected `V2R2C1`. A shared canonical parser now accepts `^V[0-9]+R[0-9]+C[0-9]+$` plus the existing bounded suffix form, and is used by capabilities, release candidate, consistency, acceptance, tag publication, and release/tag queries.
4. The runtime operation contract required `files` but the published Action schema omitted it. `commitFilesGoverned` now has one contract source and published schema projection, with parity tests. A real governed two-file commit completed on isolated branch `agent/action-contract-integration-20260825-1407`; authority, Git remote, and Gitea API all read back OID `0345cd8c70967940bc8e9132dff7d6ea11efa304`.
5. `reconcileGitOpsJob` reconstructed active lease state before checking terminal state. Terminal `COMPLETED`, `FAILED`, and `CANCELLED` jobs now short-circuit to idempotent read-back; active jobs retain strict lease validation. The two-file job reconciled as `COMPLETED` and a second reconcile returned the same receipt.
6. `getDiff` returned unbounded patch data and could fail the transport. It now returns identities, changed paths, stats, total bytes, bounded patch output, patch limit, and `truncated`. The large smoke returned `total_size_bytes=350222`, `stdout_bytes=262144`, `patch_limit_bytes=262144`, `truncated=true`; no `ResponseTooLargeError` or `GIT_OUTPUT_LIMIT_EXCEEDED` escaped.
During live integration, two additional staging-only contract defects were found and repaired: CREATE_NEW branch normalization/CAS recovery, and release executor projection of the configured staging baseline plus immutable runtime `versions/V2R2C1.json`. The latter removed the missing publication-workspace baseline/version errors without writing production state.
## Source, test, and operational changes
Source commits on the governed branch include the six contract repairs, focused regression tests, `versions/V2R2C1.json`, governed CREATE_NEW/reconcile recovery, and final runtime projection commits. The final source before this evidence commit is `2ec49005bde44f6981b14351500847d594e0276c`.
The runbook update is in `runbooks/agent-fix-action-contracts-20260825.md`. This report is the required durable evidence artifact. Existing untracked `runtime/agent-observability/` and `runtime/task-center/` directories were preserved and were not staged.
Tests run on the final source:
```text
PYTHONPATH=active-runtime/app python3 -m pytest -q tests/test_silent_release_execution_readiness.py tests/test_silent_release_controller.py tests/test_silent_release_hardening.py tests/test_silent_release_production_contract.py tests/test_chatgpt_actions_tag.py tests/test_gitea_tag_publication.py tests/test_authoritative_git_context.py
264 passed, 28 subtests passed in 4.78s
PYTHONPATH=active-runtime/app python3 -m pytest -q tests
717 passed, 2 skipped, 154 subtests passed, 2 failed in 33.98s
```
The full-suite failures remain the known event-bus fixture failure and the real signer adapter self-test (`returncode=42`); no new mandatory-suite regression was observed. Focused contract/reconcile tests and the exact mandatory suite passed.
## Staging deployment identity
- Final deployed source OID at the last completed runtime smoke: `2ec49005bde44f6981b14351500847d594e0276c`.
- Final deployed artifact: `/opt/aios/gitops-model-api/2ec49005bde44f6981b14351500847d594e0276c-action-contract-repair/runtime`.
- Artifact SHA256: `f67aed6cc0cce1f08197485c3eea204d3ef4d3989f44dc98e8ea2691e5886cbf`.
- Deployment state: `STAGING`; production-deployed flag: `false`.
- A lexically highest staging-only systemd drop-in was installed for each affected service, followed by `daemon-reload` and restart. `systemctl is-active` returned `active` for both services, and `systemctl show` confirmed the final OID, artifact SHA, API URL, and baseline authority.
## Runtime smoke evidence
- Model health returned `status=ok`, `version=V2R2C1`, final commit/artifact identity, `deployment_state=STAGING`, and `production_deployed=false`.
- Bridge/API readback was available; the bridge's `/health` path is not exposed by this runtime and returned `404`, while its governed endpoints were healthy.
- `getReleaseConsistency("V2R2C1", ...)` returned structured `status=PASS`, with Gitea listing/peeled target and tag object matching the stable reference.
- `runReleaseAcceptance(...)` entered policy evaluation and returned structured `accepted=false`; it did not fail URL validation. The independent pre-existing tag publication state is `REMOTE_PUSH_PENDING`, with the remote rejection explicitly reporting protected tag `V2R2C1`.
- `getTagPublicationStatus("V2R2C1")` returned structured `REMOTE_PUSH_PENDING`, not `invalid release version`.
- `getReleaseSaga` for a nonexistent candidate returned HTTP `404` with `status=NOT_FOUND`, `state=NOT_FOUND`, `execution_enabled=false`, and no filesystem permission error.
- `commitFilesGoverned` published schema includes required `files`, with per-file `path` and `content_base64`.
- Two-file governed commit: branch `agent/action-contract-integration-20260825-1407`, OID `0345cd8c70967940bc8e9132dff7d6ea11efa304` at authority/Git remote/API; protected-ref flags remained false.
- Terminal reconcile: `COMPLETED` read-back was idempotent across two calls; focused tests cover `FAILED` and `CANCELLED` terminal semantics.
- Small diff returned bounded non-truncated output; empty diff returned zero paths and zero bytes; large diff returned bounded/truncated output as recorded above.
- Main, protected tag, baseline registry, and production OIDs were re-read after deployment and remained unchanged.
## Restart persistence and remaining risk
The final staging services were restarted after deployment and health/runtime identity were re-read. The post-restart `systemctl is-active` result was `active` for both services; model health again reported OID `2ec49005bde44f6981b14351500847d594e0276c`, artifact SHA `f67aed6cc0cce1f08197485c3eea204d3ef4d3989f44dc98e8ea2691e5886cbf`, staging mode, and `production_deployed=false`. Post-restart consistency remained `PASS`, acceptance remained structured `accepted=false` for the pre-existing protected-tag publication state, saga remained `NOT_FOUND` for the nonexistent candidate, and the large diff remained bounded/truncated. The task cannot be promoted to `SUCCEEDED` until the protected-tag publication state and the two known repository-wide failures are resolved through their respective governed owners. No automatic rollback was performed because the staging services stayed healthy, mandatory tests passed, Gitea readback passed, and protected-ref boundaries remained intact.
Task disposition is `NEEDS_REWORK`, not `SUCCEEDED`: the staging contract repair is materially complete, but the deployed acceptance result is not all-green and the task row has not been legitimately claimed/verified by the online task worker.
@@ -1,217 +0,0 @@
# Agent Execution Contract — Action Contract Regression Repair
## Objective
Fully repair the current AIOS Action runtime/contract overlay regressions on the single server, without modifying protected refs directly and without introducing new infrastructure. The task is not complete until the deployed staging/action overlay passes all acceptance gates.
## Authority and scope
- Repository: `aios/aios`
- Base at task creation: `3244f0f17f8845592ac94d585f5ebd65693c9f29`
- Governed task: `fix-action-contracts-20260825-1407`
- Target branch: `agent/fix-action-contracts-20260825-1407`
- Protected refs must not be directly written: `main`, release tags, baseline registry, production runtime.
- No new SaaS, database, agent framework, or monitoring platform may be introduced.
## Non-interactive rules
The Agent must run the whole flow unnattended:
1. Collect facts.
2. Determine root causes.
3. Make the smallest correct changes.
4. Run unit/integration/contract tests.
5. Deploy to staging only.
6. Restart only the affected non-production services.
7. Run the full Action smoke suite.
8. On failure, automatically collect logs, diagnose, fix, and retry.
9. If a change causes a new regression, automatically roll back the staging change to the previous known-good configuration and record the failure.
10. Stop only if an owner decision is required for a high-risk, irreversible, or production-boundary action.
## Fact collection — required before editing
Record all of the following in the final evidence bundle:
- `id`, `hostname`, `uname -a`, `date --iso-seconds`
- `git rev-parse --show HEAD` for authority worktree and running overlay source
- `git status --porcelaine=v2`
- `git remote -v`
- current systemd service unit(s), drop-ins, Environment/EnvironmentFile sources for GitOps/Bredge/release services
- `systemctl show <affected-service>` including `User=`, `Group=`, `Environment=`, `EnvironmentFile=`, `ExecStart=`
- resolved Gitea API base URL after env/config expansion; redact secrets but not scheme/host
- `stat -c, `namei -l`, `getfacl`, als -ld on `/var/lib/aios-gitops/silent-release` and its parents
- find Action OpenAPI/schema generator and runtime operation definitions for `commitFilesGoverned`
- find the release version validator(s) and all callers of `getTagPublicationStatus`
- find `reconcileGitOpsJob` implementation and durable binding validation logic
- find `getDiff implementation and transport output limits
Do not edit any file until the actual implementation and configuration sources have been located.
## Repair 1 — Gitea API URL
Symptoms:
- `getReleaseConsistency` → `Gitea API URL must be HTTP(S)`
- `runReleaseAcceptance` → `Gitea API URL must be HTTP(ÊX
Required behavior:
- Determine which env/config key is projected into the current contract overlay.
- Compare the deployed overlay with V2R2C1 known-good configuration/deployment manifests.
- Restore a valid `HTTP://` or `HTTPS://` Gitea API base URL.
- Add startup config validation so invalid schemes fail at health/startup with a config-specific message, not during a release check.
- Never hard-code a different Gitea host if the existing deployment already has a single authoritative source.
## Repair 2 — durable release state permissions
Symptom:
- `getReleaseSaga` → `Permission denied: '/var/lib/aios-gitops/silent-release'`
Required behavior:
- Identify the actual runtime service User/Group.
- Grant only the required traverse/read/write permissions for the durable state directories the service must use.
- Prefer ownership + group mode or a specific ACL; keep the directory non-world-writable.
- Persist the permission in deployment/install logic so restart/reboot does not regress.
- Verify create/read/update of a staging-only saga state file under the service identity.
## Repair 3 — release version contract
Symptom:
- `getTagPublicationStatus("V2R2C1")` → `invalid release version`
- The same runtime exposes `V2R2C1` as `approved_release_version`.
Required behavior:
- Locate all version validators; consolidate onto one canonical parser.
- Accept the AIOS canonical namespace `^V[0-9]+R[0-9]+C[0-9]+$`.
- Preserve any additional already-supported legacy formats unless they are security-sensitive.
- Add contract tests proving that the version accepted by capabilities is accepted by status, candidate, consistency, acceptance, and tag publication.
## Repair 4 — `commitFilesGoverned` schema parity
Symptom:
- Runtime capability metadata requires `files`.
- Published Action tool schema does not expose `files`.
- Call reaches Bridge and fails `BRIDGE_REQUEST_FIELDS_MISSING`.
Required behavior:
- Find the canonical runtime operation contract.
- Generate the external tool schema from the same source of truth; do not manually copy a parallel schema.
- `files` must be required and must represent a list of file objects with at least `path` and `content_base64`.
- Add a parity test that compares runtime required fields with published Action required fields.
- Add a real governed commit test that writes two test files to an isolated `agent/` ibranch and verifies remote OID.
## Repair 5 — terminal reconciliation
Symptom:
- A governed job read back as `COMPLETED' but `reconcileGitOpsJob` failed `DURABLE_BINDING_INVALID`.
Required behavior:
- Reconciling a terminal successful job must be idempotent.
- An expired writer lease must not invalidate a durable binding for a job that is already terminal.
- Active-job reconciliation may require a live lease; terminal read-back must not.
- Add cases for COMPLETED, FAILED, CANCELLED / equivalent terminal states.
## Repair 6 — bounded `getDiff` output
Symptom:
- Large valid diffs fail with `ResponseTooLargeError` or `GIT_OUTPUT_LIMIT_EXCEEDED`.
Required behavior:
- Do not load unbounded diff text into the Action response.
- Return bounded summary data: commit identities, changed paths/stats, total bytes, and `truncated: true` when the full patch exceeds the safe limit.
- If pagination already exists in the contract use it; otherwise prefer deterministic truncation over a breaking schema change.
- Add tests for small, empty, and oversize diffs.
## Staging deployment procedure
1. Run the existing mandatory test suite before modification; record baseline.
2. Apply code and config changes only on the governed branch/worktree.
3. Run focused regression tests.
4. Run the full mandatory suite.
5. Build the staging contract overlay using the repository's existing build/deploy path.
6. Deploy to staging/non-production only.
7. If systemd units are changed: `systemctl daemon-reload`, then restart only the affected staging services.
8. Wait for the existing health endpoint/runtime status to become healthy.
9. Run the acceptance suite below.
If any step fails:
- collect `journalctl` logs and relevant app logs;
- diagnose root cause;
- automatically repair and retry when non-destructive;
- if a new change breaks staging, restore the previous staging artifact/config and restart the affected service;
- never silently continue after a failed gate.
## Acceptance suite
All of the following must pass on the same deployed staging overlay:
- Runtime status is `active` and healthy after restart.
- Gitea Git/API readback works from the runtime service identity.
- `getReleaseConsistency(V2R2C1, 998d30304787e31e5ae607c0bd86b5edb2b0e5ad, a8b906d1bc24088c19647e98de6a679268f6d0a2, approved_plan) returns a structured result and reaches evaluation.
- `runReleaseAcceptance` for the same identities reaches policy evaluation, not URL validation error.
- `getTagPublicationStatus("V2R2C1")` returns structured state, not `invalid release version`.
- `getReleaseSaga` for a nonexistent candidate returns NOT_FOUND/empty-state semantics, not filesystem permission failure.
- `commitFilesGoverned` exposes its required `files` parameter in the published Action schema.
- A real two-file governed commit to an isolated `agent/` branch succeeds and remote branch OID matches.
- Resuming a COMPLETED job returns the existing `JOB_TERMINAL` semantics or idempotent success as designed.
- Reconciling a COMPLETED governed job is successful/idempotent and does not require an active lease.
- Small/empty `getDiff still works.
- Large `getDiff` returns bounded output with truncation metadata instead of a transport exception.
- Existing protected-ref controls remain unchange: no direct main/tag/baseline/production write.
## Restart / reboot persistence
After the first full pass:
- Restart the staging Action/Bridge/release services one more time without changing files.
- Rerun the acceptance suite.
- Permissions, Gitea URL, and schema must still be correct after restart.
- If the repository already has a safe staging reboot test, run it as well.
## Evidence and completion
Create a single durable report in the repository, e.g. `reports/action-contract-repair-evidence-20260825.md`, containing:
- pre-change facts
- root cause for each regression
- files changed and why
- config/permission changes
- test commands and exact results
- staging deployment identity (commit/artifact/config digest)
- restart-persistence results
- final Action smoke results
- any remaining risks
Commit all source, test, deployment config, and runbook changes to the governed task branch. Do not mark the task SUCCEEDED until all acceptance gates pass on the actually deployed staging overlay.
## Automatic rollback conditions
Automatically roll back the staging deployment if any of the following occur:
- service fails to become healthy after restart
- protected-ref write boundary is broken
- Gitea readback that worked before the change stops working
- durable saga state cannot be read or recovered
- mandatory test suite regresses below the pre-change baseline
Do not automatically roll back an existing production release. Rollback here refers only to the staging overlay/service configuration changes made by this task.
## 2026-08-25 execution record
The repair branch was deployed through the existing immutable staging builder. The final source code before the evidence commit was `2ec49005bde44f6981b14351500847d594e0276c`; the deployed artifact was built from that source with SHA256 `f67aed6cc0cce1f08197485c3eea204d3ef4d3989f44dc98e8ea2691e5886cbf`. The final staging drop-ins use a lexically highest `zzzz...-999.conf` layer so they override earlier diagnostic overlays.
The runtime configuration is staging-only: `User=aiman`, `Group=gitops`, API base `https://g.cnnetmkt.top/git/api/v1`, baseline authority `/var/lib/aios-gitops/model-api-staging/baseline/current.json`, and read/write access to the staging state plus the authoritative bare repository required by the governed Writer/CAS path. No `main`, protected tag, baseline registry, or production write is enabled.
The release executor now binds both the configured staging baseline and the immutable runtime package's `versions/V2R2C1.json` projection. The bridge passes `AIOS_GITOPS_BASELINE_AUTHORITY` into `RuntimeConfig`; this prevents release consistency and acceptance from falling back to missing publication-workspace files.
The final acceptance result is recorded in `reports/action-contract-repair-evidence-20260825.md`. Contract smoke is green, and release consistency is `PASS`; release acceptance is structured policy output but remains `accepted=false` because the pre-existing publication state is `REMOTE_PUSH_PENDING` for the protected `V2R2C1` tag. The task must therefore remain `NEEDS_REWORK` until an authorized owner resolves that independent protected-tag state.
@@ -1,311 +0,0 @@
# Agent Execution Contract — V2R2C2 Control-Plane Convergence
## Objective
Complete the AIOS Phase -1 authority/runtime/contract convergence on the existing
single server with no interactive prompts. The source integration branch already
contains the staging-validated Action contract repair lineage plus the new minimal
convergence contracts. The Agent's job is to verify, finish the one remaining
release-publication reconciliation defect if it is still reproducible, build and
deploy **staging only**, prove the same-source runtime, and return machine evidence.
This is not authorization to modify production, protected tags, production baseline,
or `main` directly.
## Canonical identities
- Repository: `aios/aios`
- Authoritative main observed at task start:
`3244f0f17f8845592ac94d585f5ebd65693c9f29`
- Production baseline: `V2R2C1`
- Production target:
`998d30304787e31e5ae607c0bd86b5edb2b0e5ad`
- Production evidence closure:
`a8b906d1bc24088c19647e98de6a679268f6d0a2`
- Approval plan:
`sha256:c0df52008ae7f64cec5ee2d28947aa7b892066c670ab8295e4549140b1a7500e`
- Staging repair source lineage:
`174e64dd0218cef6d49dd13d58bbd90c78fecadd`
- Integration branch:
`agent/v2r2c2-convergence-integration-20260825`
- Integration branch OID at handoff:
`7526fef619edd8170c03c31a87e32cd1ea52a4ec`
## Code already completed
The integration branch includes the complete governed staging repair lineage rooted
at the authoritative main revision, including:
- canonical release-version validation;
- startup-safe Gitea API URL validation;
- operation contract metadata and `commitFilesGoverned` schema parity;
- bounded `getDiff`;
- idempotent terminal job reconciliation;
- CREATE_NEW branch CAS recovery;
- release baseline/version projection;
- external governed-read routing;
- canonical release consistency routing;
- durable NOT_FOUND release-saga semantics.
It also adds:
- `active-runtime/app/aios_convergence_contracts.py`
- `tests/test_convergence_contracts.py`
The new convergence contracts deliberately have no external side effects. They add:
- fail-closed `AcceptanceVector` / `AcceptanceResult`;
- domain-preserving final-writer ownership validation;
- `DesiredState` / `ObservedState`;
- pure reconciliation planning with stable idempotency identity;
- `SideEffectReceipt`;
- `ContinuationManifest`.
Local isolated validation before handoff: `8 passed`.
## Current observed runtime facts
At handoff:
- model API staging service is active at source
`174e64dd0218cef6d49dd13d58bbd90c78fecadd`;
- external `getControlPlaneSnapshot` is reachable;
- external `getGovernedWriteCapabilities` is reachable;
- `getReleaseConsistency(V2R2C1, ...)` returns `status=PASS` and all checks true;
- remote/local tag object is
`07fc2746b688f5f243f29b39de39f32edc3dd9d2`;
- peeled target is
`998d30304787e31e5ae607c0bd86b5edb2b0e5ad`;
- production health returns HTTP 200 and the same target/artifact identity;
- `runReleaseAcceptance` still returns `accepted=false` despite consistency PASS;
- `getTagPublicationStatus(V2R2C1)` currently returns `UNKNOWN`;
- `getControlPlaneSnapshot.runtime.runtime_release_commit` reports
`2ec49005bde44f6981b14351500847d594e0276c`, while the model API runtime
reports `174e64dd0218cef6d49dd13d58bbd90c78fecadd`.
Those last two facts are the remaining convergence defects.
## Non-interactive execution policy
Run continuously without asking the Owner to type "continue".
Automatically:
1. collect facts;
2. run tests;
3. diagnose failures;
4. make the smallest correct repair;
5. rebuild immutable staging artifacts;
6. deploy/restart only affected staging services;
7. observe external truth;
8. retry recoverable failures;
9. roll back newly introduced staging regressions;
10. produce final evidence.
Stop only for:
- authority/approval that cannot be satisfied by existing bindings;
- irreversible production change;
- protected-ref mutation;
- security-boundary change;
- genuine goal ambiguity;
- no-progress after bounded diagnosis/repair attempts.
## Phase A — pre-edit facts
Before any further edit, persist:
- current authoritative HEAD;
- integration branch OID and remote readback;
- `git merge-base --is-ancestor 3244f0f... 7526fef...`;
- staging service source/artifact identity for both model API and Bridge;
- systemd unit + drop-in ordering for both services;
- exact environment sources for baseline, Gitea API, state roots and PYTHONPATH;
- current release consistency, acceptance and tag-publication status;
- current control-plane snapshot;
- current governed-write capabilities operation contracts.
Do not trust a report if a direct runtime observation is available.
## Phase B — mandatory tests
Run at minimum:
```text
PYTHONPATH=active-runtime/app python3 -m pytest -q tests/test_convergence_contracts.py tests/test_action_contract_regressions.py tests/test_bridge_v2_governed_http_bootstrap.py
PYTHONPATH=active-runtime/app python3 -m pytest -q tests/test_silent_release_execution_readiness.py tests/test_silent_release_controller.py tests/test_silent_release_hardening.py tests/test_silent_release_production_contract.py tests/test_chatgpt_actions_tag.py tests/test_gitea_tag_publication.py tests/test_authoritative_git_context.py
```
Then run the repository-wide suite. Compare failures to the persisted pre-existing
baseline. No new regression is allowed.
## Phase C — release-publication truth reconciliation
Only execute this code repair if the following remains simultaneously true on the
same staging runtime:
```text
getReleaseConsistency.status == PASS
runReleaseAcceptance.accepted == false
getTagPublicationStatus(V2R2C1) in {UNKNOWN, REMOTE_PUSH_PENDING}
```
### Required semantics
Do **not** hard-code `accepted=true` and do not reinterpret UNKNOWN as PASS.
The publication controller must reconcile durable publication state from observed
external truth:
1. Resolve the approved version binding.
2. Read authoritative local tag object and peeled commit.
3. Read Gitea tag object and peeled commit.
4. Read registry/baseline approved tag object and target.
5. Compare all identities to the approved release binding.
6. If every required observation is present and exactly equal:
- persist the existing terminal-success publication state used by the codebase
(`PUBLISHED`/equivalent);
- mark the transition as observation-only/reconciled;
- record the observed tag object, peeled target, repository identity and evidence;
- perform **no tag create/push**.
7. If any identity differs:
- fail closed as drift/conflict;
- never mutate the protected tag.
8. If any required observation is unavailable:
- remain `UNKNOWN`/reconciling;
- do not mark accepted.
9. Replaying reconciliation must be idempotent and must not create a second
side effect.
`runReleaseAcceptance` may return accepted only after both:
```text
release consistency == PASS
AND reconciled publication state == terminal success
```
### Mandatory regression tests
Add focused tests for:
- missing local publication journal + exact observed remote tag -> terminal success;
- stale `REMOTE_PUSH_PENDING` + exact observed remote tag -> terminal success;
- remote tag object mismatch -> fail closed;
- peeled target mismatch -> fail closed;
- Gitea observation unavailable -> UNKNOWN, not accepted;
- replay after reconciled success -> identical durable receipt, no tag write/push;
- existing protected tag is never force-updated or deleted.
Use the existing `TagPublisher`/publication store and existing reconciliation/state
model. Do not create a second release database or a second release controller.
## Phase D — same-source staging convergence
Build from integration branch OID after tests. Deploy the same immutable artifact
lineage to every staging component participating in these Actions.
Acceptance requires the externally visible snapshot to stop presenting mixed
source identity. At minimum model API and Bridge must report the same intended
source/build identity or explicitly report component-level identities without
claiming a single aggregate runtime commit.
Do not hide component drift by overwriting the snapshot value.
Preferred fix if the snapshot currently has only one `runtime_release_commit`:
extent it to expose component identities, for example:
```text
runtime.components.model_api.source_oid
runtime.components.bridge.source_oid
runtime.aggregate_drift_status
```
Aggregate PASS is legal only if the required components are on the approved
compatible source set.
## Phase E — staging smoke
On the deployed staging artifact run:
- runtime status;
- control-plane snapshot;
- governed-write capabilities;
- release consistency;
- release acceptance;
- tag publication status;
- nonexistent release saga;
- bounded large diff;
- completed-job reconcile idempotency;
- isolated two-file governed commit on a throwaway `agent/` branch.
Required end state:
```text
release_consistency = PASS
release_acceptance.accepted = true
tag_publication = terminal success
control_plane_snapshot = reachable
governed_capabilities = reachable
runtime_component_drift = PASS
legacy_writes = false
force_push = false
main_write = false
tag_write = false
baseline_write = false
production_write = false
```
## Phase F — restart persistence
Restart only affected staging services, without changing files.
Rerun Phase E. All identities and outcomes must remain stable.
## Governance and branch handling
Do not directly modify `main`, `V2R2C1`, the production baseline registry or
production services.
The current Bridge writer binding has a known limitation: after a successful
single-file CAS commit, reacquiring the same task lease can return the original
expected branch OID, and rebinding the same task/generation can conflict.
Do not work around that with direct Git writes.
For source changes use the existing governed task/lease/commit flow. If the platform
requires one governed task binding per branch advance, create an explicitly named
follow-up governed task on the **same branch**, with:
- `base_oid` = previously verified branch OID;
- `expected_branch_oid` = same previously verified branch OID;
- same repository and branch;
- no protected-ref write.
Record this as `TASK_BINDING_EXPECTED_OID_NOT_ADVANCING` evidence.
## Final evidence
Commit a durable report:
`reports/v2r2c2-control-plane-convergence-evidence-20260825.md`
It must contain:
- pre-change facts;
- exact commits and changed paths;
- all test commands/results;
- release-publication reconciliation proof;
- proof that no protected tag write occurred during observation reconciliation;
- build artifact SHA;
- staging component source identities;
- pre/post restart smoke;
- branch authority/Git remote/API readback;
- remaining known risks;
- final disposition.
Do not declare `SUCCEEDED` unless every required end-state line in Phase E is
proven from the actually deployed staging runtime.
Production promotion is a separate Owner-approved release step and is **not** part
of this execution contract.
@@ -1,90 +0,0 @@
#!/usr/bin/env python3
"""Build a deterministic plan-bound production payload from one clean source tree."""
from __future__ import annotations
import argparse, hashlib, json, shutil, tarfile, tempfile
from pathlib import Path
SOURCE_FILES = (
"active-runtime/app/deployment_state.py",
"active-runtime/app/v2r1c6_privacy_tools.py",
"active-runtime/app/v2r2c2_health_service.py",
"deploy/aios-v2/systemd/aios-v2r2c2-production.service",
)
def canon(value: object) -> bytes:
return (json.dumps(value, sort_keys=True, separators=(",", ":")) + "\n").encode()
def digest(path: Path) -> str:
h = hashlib.sha256()
with path.open("rb") as f:
for block in iter(lambda: f.read(1024 * 1024), b""): h.update(block)
return h.hexdigest()
def require_oid(value: str, label: str) -> str:
if len(value) != 40 or any(c not in "0123456789abcdef" for c in value): raise SystemExit(label + "_invalid")
return value
def require_sha(value: str, label: str) -> str:
if len(value) != 64 or any(c not in "0123456789abcdef" for c in value): raise SystemExit(label + "_invalid")
return value
def build(source: Path, output: Path, *, version: str, source_oid: str, tree_oid: str,
rollback_version: str, rollback_target_oid: str, rollback_artifact_sha256: str,
service: str, port: int) -> dict[str, object]:
if not source.is_dir() or source.is_symlink(): raise SystemExit("source_directory_invalid")
require_oid(source_oid, "source_oid"); require_oid(tree_oid, "tree_oid")
require_oid(rollback_target_oid, "rollback_target_oid"); require_sha(rollback_artifact_sha256, "rollback_artifact_sha256")
if version != "V2R2C2" or rollback_version == version: raise SystemExit("release_version_binding_invalid")
if service != "aios-v2r2c2-production.service" or port != 18928: raise SystemExit("adapter_policy_binding_invalid")
if output.exists(): raise SystemExit("output_exists")
output.parent.mkdir(parents=True, exist_ok=True)
with tempfile.TemporaryDirectory(prefix="trusted-release-build-") as td:
payload = Path(td) / "payload"; payload.mkdir()
for rel in SOURCE_FILES:
source_file = source / rel
if not source_file.is_file() or source_file.is_symlink(): raise SystemExit("required_source_missing:" + rel)
dest = payload / rel; dest.parent.mkdir(parents=True, exist_ok=True); shutil.copyfile(source_file, dest)
(payload / "VERSION").write_text(version + "\n", encoding="utf-8")
build_meta = {"schema": "aios.trusted-release-artifact-build.v1", "version": version,
"source_commit": source_oid, "tree_oid": tree_oid, "production_deployed": False,
"tag_created": False, "baseline_changed": False, "adapter_service": service, "adapter_port": port}
(payload / "BUILD-METADATA.json").write_bytes(canon(build_meta))
rollback = {"schema": "aios.trusted-release-rollback.v1", "version": rollback_version,
"target_oid": rollback_target_oid, "artifact_sha256": rollback_artifact_sha256,
"automatic_rollback": False}
(payload / "ROLLBACK-METADATA.json").write_bytes(canon(rollback))
files = []
for item in sorted(p for p in payload.rglob("*") if p.is_file()):
if item.name != "ARTIFACT-MANIFEST.json":
files.append({"path": "payload/" + item.relative_to(payload).as_posix(), "sha256": digest(item), "size": item.stat().st_size})
manifest = {"schema": "aios.trusted-release-artifact-manifest.v1", "version": version,
"source_commit": source_oid, "tree_oid": tree_oid, "files": files,
"deployable": True, "production_deployed": False}
(payload / "ARTIFACT-MANIFEST.json").write_bytes(canon(manifest))
files.append({"path": "payload/ARTIFACT-MANIFEST.json", "sha256": digest(payload / "ARTIFACT-MANIFEST.json"), "size": (payload / "ARTIFACT-MANIFEST.json").stat().st_size})
tar_path = Path(td) / "artifact.tar"
with tarfile.open(tar_path, "w") as archive:
for item in sorted(p for p in payload.rglob("*") if p.is_file()):
info = archive.gettarinfo(str(item), arcname="payload/" + item.relative_to(payload).as_posix())
info.uid = 0; info.gid = 0; info.uname = "root"; info.gname = "root"; info.mtime = 0; info.mode = 0o644
with item.open("rb") as stream: archive.addfile(info, stream)
shutil.copyfile(tar_path, output)
receipt = {"schema": "aios.trusted-release-artifact-receipt.v1", "version": version,
"source_commit": source_oid, "tree_oid": tree_oid, "artifact_sha256": digest(output),
"artifact_size": output.stat().st_size, "manifest_sha256": hashlib.sha256(canon(manifest)).hexdigest(),
"package_closure_sha256": hashlib.sha256(canon({"source_commit": source_oid, "tree_oid": tree_oid, "files": files})).hexdigest(),
"production_deployed": False, "tag_created": False, "baseline_changed": False,
"service": service, "port": port}
output.with_suffix(".manifest.json").write_bytes(canon(receipt))
return receipt
def main() -> int:
p = argparse.ArgumentParser(); p.add_argument("--source", type=Path, required=True); p.add_argument("--output", type=Path, required=True)
p.add_argument("--version", required=True); p.add_argument("--source-oid", required=True); p.add_argument("--tree-oid", required=True)
p.add_argument("--rollback-version", required=True); p.add_argument("--rollback-target-oid", required=True); p.add_argument("--rollback-artifact-sha256", required=True)
p.add_argument("--service", required=True); p.add_argument("--port", type=int, required=True); a = p.parse_args()
result = build(a.source, a.output, version=a.version, source_oid=a.source_oid, tree_oid=a.tree_oid,
rollback_version=a.rollback_version, rollback_target_oid=a.rollback_target_oid,
rollback_artifact_sha256=a.rollback_artifact_sha256, service=a.service, port=a.port)
print(json.dumps(result, sort_keys=True)); return 0
if __name__ == "__main__": raise SystemExit(main())
-22
View File
@@ -5,7 +5,6 @@ import hashlib
import json
import os
from pathlib import Path
import sys
import tarfile
import tempfile
@@ -19,18 +18,6 @@ REQUIRED_APP = (
'silent_release_controller.py',
'silent_release_hardening.py',
'aios_gitops_job_controller.py',
'aios_chatgpt_bridge_v2.py',
'aios_chatgpt_bridge_v2_server.py',
'governed_production_release.py',
'v2r2c1_production_adapter.py',
'v2r1c7_production_adapter.py',
'aios_governed_task_bootstrap.py',
'aios_continuation_executor.py',
'aios_continuation_lease.py',
'aios_authority_git_executor.py',
'aios_gitops_reconciliation_core.py',
'authorized_task_writer_provisioning.py',
'signer_contract_compatibility.py',
)
OPTIONAL_APP = (
'aios_chatgpt_actions_api.py',
@@ -69,18 +56,11 @@ def main() -> int:
source = args.source_root.resolve(strict=True)
output = args.output_root.resolve()
app = source / 'active-runtime' / 'app'
sys.path.insert(0, str(app))
from aios_chatgpt_bridge_v2 import render_openapi_contract_fragment
runtime = source / 'runtime' / 'agent-os'
docs = source / 'docs' / 'gitops'
if not app.is_dir():
raise SystemExit('active-runtime/app missing')
paths = []
root_version = source / 'release_version.py'
if not root_version.is_file():
raise SystemExit('required source dependency missing: release_version.py')
paths.append((Path('release_version.py'), root_version))
for name in REQUIRED_APP:
path = app / name
if not path.is_file():
@@ -97,8 +77,6 @@ def main() -> int:
paths.append((Path('runtime-agent-os') / rel, path))
openapi = docs / 'openapi-chatgpt-actions.yaml'
if openapi.is_file():
if ' /v2/commit-files:' not in openapi.read_text(encoding='utf-8'):
raise SystemExit('published OpenAPI is missing generated commitFilesGoverned contract')
paths.append((Path('docs') / 'gitops' / 'openapi-chatgpt-actions.yaml', openapi))
for folder in ('config/model_registry', 'config/model_priority', 'patches', 'versions'):
root = source / folder
+4 -10
View File
@@ -9,16 +9,10 @@ WORKSPACE="${AIOS_GITOPS_WORKSPACE:-/data/aios-gitops/workspace}"
TAG_REMOTE="${AIOS_GITOPS_GITEA_REMOTE:-gitea}"
BRANCH_REMOTE="${AIOS_GITOPS_BRANCH_REMOTE:-gitea}"
SCRIPT_ROOT="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)"
if ! PYTHONPATH="$SCRIPT_ROOT:${PYTHONPATH:-}" python3 - "$VERSION" <<'PY'
import sys
from release_version import validate_release_version
validate_release_version(sys.argv[1])
PY
then
echo "ERROR: version must match VxRxCx" >&2
exit 64
fi
case "$VERSION" in
V[0-9]*R[0-9]*C[0-9]*) ;;
*) echo "ERROR: version must match VxRxCx" >&2; exit 64 ;;
esac
for value in "$TARGET_OID" "$EVIDENCE_CLOSURE_OID"; do
[[ "$value" =~ ^[0-9a-f]{40}$ ]] || {
@@ -0,0 +1,93 @@
# Autonomous Engineering Runtime Contract v1
The Online Agent Worker and the Hermes/Codex engineering runtime are separate
security domains.
The worker owns:
- Agent Task polling, atomic claim, task lease and heartbeat;
- intent resolution and restart state;
- authoritative repository/base selection;
- independent diff inspection and secret checks;
- verification command allowlisting and re-execution;
- Governed GitOps task/lease/commit;
- final result submission.
The runtime broker and Hermes/Codex backend do **not** receive:
- Agent Task API token;
- task lease token;
- governed writer lease or Bridge credential;
- authority repository write access.
## stdin request
The backend configured behind the broker receives one JSON object:
`schema = aios.online-agent.runtime-request.v1`
It contains task goal/instructions/success criteria and a pinned disposable
worktree path. The backend may inspect and modify that worktree. It must not
commit or push.
## stdout response
The backend returns one JSON object:
```json
{
"schema": "aios.online-agent.runtime-result.v1",
"status": "COMPLETED",
"summary": "root cause and implementation summary",
"verification": [
{
"label": "focused test",
"argv": ["/usr/bin/python3", "-m", "pytest", "-q", "tests/test_x.py"],
"cwd": ".",
"timeout_seconds": 600
}
],
"evidence": {
"root_cause": "..."
}
}
```
`status` may also be `BLOCKED` or `FAILED`.
The worker ignores any backend claim about changed files and independently reads
Git state. It also reruns verification requests through its local command
allowlist.
## Real runtime requirement
A chat/router/WeChat Hermes service is not an engineering backend. A compliant
backend must be able to read and mutate the disposable worktree and emit the
runtime result schema above.
The exact Hermes/Codex CLI/API must be discovered from the installed runtime.
Do not invent flags. `/etc/aios/agent-runtime-hermes.conf` contains only
non-secret backend command configuration. Any model/runtime credential must be
delivered to the broker/backend through its own systemd credential projection,
never through the Online Agent worker token.
## OpenClaw headless engineering backend
For hosts that have a current OpenClaw CLI, use the bundled
`openclaw_engineering_backend.py`.
The wrapper invokes the documented headless coding entry point:
`openclaw agent exec --message-file - --cwd <pinned-worktree> --model <model> --auth-env-only --timeout <seconds> --json`
It deliberately does **not** use the Gateway/WeChat/chat route. `agent exec`
runs an embedded, one-shot agent turn and scopes its workspace/tool working
directory to `--cwd`.
Provider credentials, if required, are projected into the broker service under
separate systemd credentials and mapped to provider environment variables only
for the OpenClaw child. The Online Agent Task token and writer lease remain
absent.
Before enabling the autonomous worker, run a disposable broker self-test that
proves OpenClaw changes the requested worktree while Git HEAD remains at the
base OID.
@@ -0,0 +1,297 @@
#!/usr/bin/env python3
from __future__ import annotations
import array
import json
import os
from pathlib import Path
import pwd
import re
import socket
import stat
import subprocess
from typing import Any, Mapping
MAX_BYTES = 1024 * 1024
ADAPTER = os.environ.get("AIOS_AGENT_RUNTIME_ADAPTER", "").strip()
SOCKET_PATH = Path(os.environ.get("AIOS_AGENT_RUNTIME_SOCKET", ""))
WORKSPACE_ROOT = Path(os.environ.get("AIOS_AGENT_RUNTIME_WORKSPACE_ROOT", ""))
BACKEND_ARGV_JSON = os.environ.get("AIOS_AGENT_RUNTIME_BACKEND_ARGV_JSON", "")
ALLOWED_PEER_UID = int(os.environ.get("AIOS_AGENT_RUNTIME_ALLOWED_PEER_UID", "-1"))
ALLOWED_PEER_USER = os.environ.get("AIOS_AGENT_RUNTIME_ALLOWED_PEER_USER", "").strip()
CONNECTION_TIMEOUT = float(os.environ.get("AIOS_AGENT_RUNTIME_CONNECTION_TIMEOUT_SECONDS", "15"))
BACKEND_TIMEOUT = int(os.environ.get("AIOS_AGENT_RUNTIME_BACKEND_TIMEOUT_SECONDS", "3600"))
BACKEND_ENV_ALLOWLIST = (
"AIOS_OPENCLAW_BIN",
"AIOS_OPENCLAW_MODEL",
"AIOS_OPENCLAW_PROVIDER_CREDENTIAL_MAP_JSON",
"AIOS_OPENCLAW_EXEC_TIMEOUT_SECONDS",
)
SENSITIVE_RE = re.compile(r"(?i)(authorization\s*:\s*bearer\s+\S+|(?:password|passwd|secret|token|api[_-]?key)\s*[:=]\s*\S+)")
class BrokerError(RuntimeError):
def __init__(self, code: str, *, details: Mapping[str, Any] | None = None) -> None:
super().__init__(code)
self.code = code
self.details = dict(details or {})
def peer_uid(connection: socket.socket) -> int:
if not hasattr(socket, "SO_PEERCRED"):
raise BrokerError("SO_PEERCRED_UNAVAILABLE")
raw = connection.getsockopt(socket.SOL_SOCKET, socket.SO_PEERCRED, 12)
pid, uid, gid = array.array("i", raw[:12])
return int(uid)
def configured_peer_uid() -> int:
if ALLOWED_PEER_UID >= 0:
return ALLOWED_PEER_UID
if not ALLOWED_PEER_USER:
raise BrokerError("ALLOWED_PEER_UID_NOT_CONFIGURED")
try:
return int(pwd.getpwnam(ALLOWED_PEER_USER).pw_uid)
except KeyError as exc:
raise BrokerError("ALLOWED_PEER_USER_NOT_FOUND") from exc
def authorize_peer(connection: socket.socket) -> None:
uid = peer_uid(connection)
expected = configured_peer_uid()
if uid != expected:
raise BrokerError("PEER_UID_REJECTED", details={"peer_uid": uid})
def read_request(connection: socket.socket) -> dict[str, Any]:
connection.settimeout(CONNECTION_TIMEOUT)
total = 0
chunks: list[bytes] = []
while True:
try:
chunk = connection.recv(65536)
except socket.timeout as exc:
raise BrokerError("REQUEST_TIMEOUT") from exc
if not chunk:
break
total += len(chunk)
if total > MAX_BYTES:
raise BrokerError("REQUEST_TOO_LARGE")
chunks.append(chunk)
try:
value = json.loads(b"".join(chunks).decode("utf-8"))
except (UnicodeDecodeError, json.JSONDecodeError) as exc:
raise BrokerError("REQUEST_INVALID_JSON") from exc
if not isinstance(value, Mapping):
raise BrokerError("REQUEST_OBJECT_REQUIRED")
return dict(value)
def backend_argv() -> list[str]:
try:
value = json.loads(BACKEND_ARGV_JSON)
except json.JSONDecodeError as exc:
raise BrokerError("BACKEND_ARGV_JSON_INVALID") from exc
if not isinstance(value, list) or not value or not all(isinstance(item, str) and item for item in value):
raise BrokerError("BACKEND_ARGV_INVALID")
executable = Path(value[0])
if not executable.is_absolute():
raise BrokerError("BACKEND_EXECUTABLE_INVALID")
try:
resolved = executable.resolve(strict=True)
except OSError as exc:
raise BrokerError("BACKEND_EXECUTABLE_INVALID") from exc
if not resolved.is_file():
raise BrokerError("BACKEND_EXECUTABLE_INVALID")
return list(value)
def validate_request(value: Mapping[str, Any]) -> tuple[dict[str, Any], int]:
if value.get("schema") != "aios.online-agent.runtime-request.v1":
raise BrokerError("REQUEST_SCHEMA_INVALID")
if value.get("adapter") != ADAPTER or ADAPTER not in {"hermes", "codex", "openclaw"}:
raise BrokerError("REQUEST_ADAPTER_INVALID")
context = value.get("execution_context")
policy = value.get("policy")
if not isinstance(context, Mapping) or not isinstance(policy, Mapping):
raise BrokerError("REQUEST_CONTEXT_INVALID")
worktree_raw = context.get("worktree")
if not isinstance(worktree_raw, str):
raise BrokerError("REQUEST_WORKTREE_INVALID")
for field in (
"authoritative_repository_write",
"direct_git_ref_mutation",
"direct_git_push",
"direct_git_commit",
"main_write",
"force_push",
"production_write",
"tag_write",
"baseline_write",
"task_api_credential_visible",
"writer_lease_visible",
):
if policy.get(field) is not False:
raise BrokerError("REQUEST_POLICY_REJECTED", details={"field": field})
root = WORKSPACE_ROOT.resolve()
candidate = Path(worktree_raw)
if not candidate.is_absolute():
raise BrokerError("REQUEST_WORKTREE_INVALID")
parent = candidate.parent.resolve()
if root == parent or root not in parent.parents:
raise BrokerError("REQUEST_WORKTREE_OUT_OF_SCOPE")
flags = getattr(os, "O_PATH", os.O_RDONLY) | getattr(os, "O_NOFOLLOW", 0) | os.O_DIRECTORY
try:
fd = os.open(str(candidate), flags)
except OSError as exc:
raise BrokerError("REQUEST_WORKTREE_OPEN_FAILED") from exc
st = os.fstat(fd)
if not stat.S_ISDIR(st.st_mode):
os.close(fd)
raise BrokerError("REQUEST_WORKTREE_NOT_DIRECTORY")
pinned = Path(f"/proc/self/fd/{fd}")
try:
resolved = pinned.resolve(strict=True)
except OSError:
os.close(fd)
raise BrokerError("REQUEST_WORKTREE_PIN_FAILED")
if root not in resolved.parents:
os.close(fd)
raise BrokerError("REQUEST_WORKTREE_OUT_OF_SCOPE")
return dict(value), fd
def sanitize(value: Any) -> Any:
if isinstance(value, Mapping):
cleaned: dict[str, Any] = {}
for key, inner in value.items():
lowered = str(key).lower()
if any(marker in lowered for marker in ("token", "secret", "credential", "authorization", "password", "api_key", "apikey", "access_key")):
cleaned[str(key)] = "[REDACTED]"
else:
cleaned[str(key)] = sanitize(inner)
return cleaned
if isinstance(value, list):
return [sanitize(item) for item in value]
if isinstance(value, str):
return SENSITIVE_RE.sub("[REDACTED]", value)
return value
def sanitized_failure(code: str, details: Mapping[str, Any] | None = None) -> dict[str, Any]:
safe: dict[str, Any] = {}
for key, value in dict(details or {}).items():
lowered = str(key).lower()
if any(marker in lowered for marker in ("token", "secret", "credential", "authorization", "password", "api_key", "apikey", "access_key")):
safe[str(key)] = "[REDACTED]"
else:
safe[str(key)] = value
return {
"schema": "aios.online-agent.runtime-result.v1",
"status": "FAILED",
"summary": code,
"verification": [],
"evidence": safe,
}
def execute_backend(request: Mapping[str, Any], worktree_fd: int) -> dict[str, Any]:
argv = backend_argv()
request = dict(request)
context = dict(request["execution_context"])
# Pass a pinned inode-backed path, not the original user-visible pathname.
context["worktree"] = f"/proc/self/fd/{worktree_fd}"
request["execution_context"] = context
payload = json.dumps(request, ensure_ascii=False, sort_keys=True, separators=(",", ":")).encode("utf-8")
environment = {
"PATH": "/usr/local/bin:/usr/bin:/bin",
"LC_ALL": "C.UTF-8",
"AIOS_AGENT_RUNTIME_ADAPTER": ADAPTER,
}
for name in BACKEND_ENV_ALLOWLIST:
value = os.environ.get(name)
if value is not None:
environment[name] = value
credentials_dir = os.environ.get("CREDENTIALS_DIRECTORY")
if credentials_dir:
environment["CREDENTIALS_DIRECTORY"] = credentials_dir
try:
completed = subprocess.run(
argv,
input=payload,
cwd=f"/proc/self/fd/{worktree_fd}",
pass_fds=(worktree_fd,),
capture_output=True,
check=False,
timeout=BACKEND_TIMEOUT,
env=environment,
)
except subprocess.TimeoutExpired:
return sanitized_failure("AGENT_BACKEND_TIMEOUT")
except OSError as exc:
return sanitized_failure("AGENT_BACKEND_EXEC_FAILED", {"error": type(exc).__name__})
if completed.returncode != 0:
return sanitized_failure("AGENT_BACKEND_FAILED", {"exit_code": completed.returncode})
if len(completed.stdout) > MAX_BYTES:
return sanitized_failure("AGENT_BACKEND_RESULT_TOO_LARGE")
try:
value = json.loads(completed.stdout.decode("utf-8"))
except (UnicodeDecodeError, json.JSONDecodeError):
return sanitized_failure("AGENT_BACKEND_RESULT_INVALID")
if not isinstance(value, Mapping) or value.get("schema") != "aios.online-agent.runtime-result.v1":
return sanitized_failure("AGENT_BACKEND_RESULT_SCHEMA_INVALID")
sanitized = sanitize(dict(value))
return sanitized if isinstance(sanitized, dict) else sanitized_failure("AGENT_BACKEND_RESULT_INVALID")
def serve_once(listener: socket.socket) -> None:
connection, _ = listener.accept()
worktree_fd: int | None = None
try:
authorize_peer(connection)
request = read_request(connection)
request, worktree_fd = validate_request(request)
response = execute_backend(request, worktree_fd)
except BrokerError as exc:
response = sanitized_failure(exc.code, exc.details)
except Exception as exc:
response = sanitized_failure("BROKER_INTERNAL_ERROR", {"error": type(exc).__name__})
finally:
if worktree_fd is not None:
os.close(worktree_fd)
raw = json.dumps(response, ensure_ascii=False, sort_keys=True, separators=(",", ":")).encode("utf-8") + b"\n"
try:
connection.settimeout(CONNECTION_TIMEOUT)
connection.sendall(raw[:MAX_BYTES])
finally:
connection.close()
def main() -> int:
if ADAPTER not in {"hermes", "codex", "openclaw"}:
raise BrokerError("BROKER_ADAPTER_INVALID")
if not SOCKET_PATH.is_absolute() or not WORKSPACE_ROOT.is_absolute():
raise BrokerError("BROKER_PATH_INVALID")
SOCKET_PATH.parent.mkdir(parents=True, mode=0o750, exist_ok=True)
if SOCKET_PATH.exists() or SOCKET_PATH.is_symlink():
SOCKET_PATH.unlink()
listener = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
listener.bind(str(SOCKET_PATH))
os.chmod(SOCKET_PATH, 0o660)
listener.listen(8)
try:
while True:
serve_once(listener)
finally:
listener.close()
if SOCKET_PATH.exists():
SOCKET_PATH.unlink()
return 0
if __name__ == "__main__":
raise SystemExit(main())
@@ -0,0 +1,369 @@
#!/usr/bin/env python3
"""OpenClaw engineering backend for the AIOS autonomous runtime broker.
This process is intentionally *not* an Online Agent Task worker. It receives
one secret-free `aios.online-agent.runtime-request.v1` object on stdin, invokes
OpenClaw's headless `agent exec` against the pinned disposable worktree, and
returns one `aios.online-agent.runtime-result.v1` object on stdout.
Online Task claim/heartbeat/result and Governed GitOps remain owned by the
AIOS autonomous worker.
"""
from __future__ import annotations
import json
import os
from pathlib import Path
import re
import subprocess
import sys
from typing import Any, Mapping
REQUEST_SCHEMA = "aios.online-agent.runtime-request.v1"
RESULT_SCHEMA = "aios.online-agent.runtime-result.v1"
MAX_INPUT_BYTES = 1024 * 1024
MAX_OUTPUT_BYTES = 1024 * 1024
DEFAULT_TIMEOUT = 1800
ALLOWED_ADAPTERS = frozenset({"hermes", "codex", "openclaw"})
SECRET_PATTERNS = (
re.compile(r"\bsk-[A-Za-z0-9_-]{20,}\b"),
re.compile(r"\bghp_[A-Za-z0-9]{20,}\b"),
re.compile(r"\bAKIA[0-9A-Z]{16}\b"),
re.compile(r"-----BEGIN (?:RSA |EC |OPENSSH )?PRIVATE KEY-----"),
)
class BackendError(RuntimeError):
def __init__(self, code: str, *, details: Mapping[str, Any] | None = None) -> None:
super().__init__(code)
self.code = code
self.details = dict(details or {})
def redact_text(value: str, limit: int = 8192) -> str:
value = value[:limit]
for pattern in SECRET_PATTERNS:
value = pattern.sub("[REDACTED]", value)
return value
def result(
status: str,
summary: str,
*,
verification: list[dict[str, Any]] | None = None,
evidence: Mapping[str, Any] | None = None,
) -> dict[str, Any]:
return {
"schema": RESULT_SCHEMA,
"status": status,
"summary": redact_text(summary) or status,
"verification": verification or [],
"evidence": dict(evidence or {}),
}
def emit(value: Mapping[str, Any]) -> int:
raw = json.dumps(dict(value), ensure_ascii=False, sort_keys=True, separators=(",", ":"))
sys.stdout.write(raw)
sys.stdout.write("\n")
sys.stdout.flush()
return 0
def read_request() -> dict[str, Any]:
raw = sys.stdin.buffer.read(MAX_INPUT_BYTES + 1)
if len(raw) > MAX_INPUT_BYTES:
raise BackendError("REQUEST_TOO_LARGE")
try:
value = json.loads(raw.decode("utf-8"))
except (UnicodeDecodeError, json.JSONDecodeError) as exc:
raise BackendError("REQUEST_INVALID_JSON") from exc
if not isinstance(value, Mapping):
raise BackendError("REQUEST_OBJECT_REQUIRED")
if value.get("schema") != REQUEST_SCHEMA:
raise BackendError("REQUEST_SCHEMA_INVALID")
return dict(value)
def nonempty(value: Any, field: str, limit: int) -> str:
if not isinstance(value, str):
raise BackendError(f"{field.upper()}_INVALID")
value = value.strip()
if not value or len(value) > limit:
raise BackendError(f"{field.upper()}_INVALID")
return value
def string_list(value: Any, field: str) -> list[str]:
if not isinstance(value, list) or not value or len(value) > 64:
raise BackendError(f"{field.upper()}_INVALID")
result_value: list[str] = []
for item in value:
result_value.append(nonempty(item, field, 4096))
return result_value
def validate_request(value: Mapping[str, Any]) -> tuple[dict[str, Any], Path]:
adapter = str(value.get("adapter") or "")
if adapter not in ALLOWED_ADAPTERS:
raise BackendError("ADAPTER_INVALID")
task = value.get("task")
context = value.get("execution_context")
policy = value.get("policy")
if not isinstance(task, Mapping) or not isinstance(context, Mapping) or not isinstance(policy, Mapping):
raise BackendError("REQUEST_CONTEXT_INVALID")
task_id = nonempty(task.get("task_id"), "task_id", 180)
goal = nonempty(task.get("goal"), "goal", 16384)
instructions = nonempty(task.get("instructions"), "instructions", 65536)
success = string_list(task.get("success_criteria"), "success_criteria")
worktree_raw = context.get("worktree")
if not isinstance(worktree_raw, str) or not worktree_raw.startswith("/proc/self/fd/"):
raise BackendError("PINNED_WORKTREE_REQUIRED")
worktree = Path(worktree_raw)
try:
resolved = worktree.resolve(strict=True)
except OSError as exc:
raise BackendError("PINNED_WORKTREE_UNAVAILABLE") from exc
if not resolved.is_dir():
raise BackendError("PINNED_WORKTREE_INVALID")
for field in (
"authoritative_repository_write",
"direct_git_ref_mutation",
"direct_git_push",
"direct_git_commit",
"main_write",
"force_push",
"production_write",
"tag_write",
"baseline_write",
"task_api_credential_visible",
"writer_lease_visible",
):
if policy.get(field) is not False:
raise BackendError("REQUEST_POLICY_REJECTED", details={"field": field})
return {
"adapter": adapter,
"task_id": task_id,
"goal": goal,
"instructions": instructions,
"success_criteria": success,
"repository": str(context.get("repository") or ""),
"base_oid": str(context.get("base_oid") or ""),
"target_branch": str(context.get("target_branch") or ""),
}, worktree
def openclaw_binary() -> Path:
configured = os.environ.get("AIOS_OPENCLAW_BIN", "").strip()
if not configured:
raise BackendError("OPENCLAW_BINARY_NOT_CONFIGURED")
path = Path(configured)
if not path.is_absolute():
raise BackendError("OPENCLAW_BINARY_INVALID")
try:
resolved = path.resolve(strict=True)
except OSError as exc:
raise BackendError("OPENCLAW_BINARY_UNAVAILABLE") from exc
if not resolved.is_file() or not os.access(resolved, os.X_OK):
raise BackendError("OPENCLAW_BINARY_UNAVAILABLE")
return resolved
def provider_environment() -> dict[str, str]:
"""Build an environment containing only explicitly projected provider auth.
`--auth-env-only` tells OpenClaw not to load ambient config/auth stores.
The mapping is non-secret; values are read from systemd credential files.
"""
raw = os.environ.get("AIOS_OPENCLAW_PROVIDER_CREDENTIAL_MAP_JSON", "{}")
try:
mapping = json.loads(raw)
except json.JSONDecodeError as exc:
raise BackendError("PROVIDER_CREDENTIAL_MAP_INVALID") from exc
if not isinstance(mapping, Mapping):
raise BackendError("PROVIDER_CREDENTIAL_MAP_INVALID")
credentials_directory = os.environ.get("CREDENTIALS_DIRECTORY", "").strip()
environment = {
"PATH": "/usr/local/bin:/usr/bin:/bin",
"LC_ALL": "C.UTF-8",
"NO_COLOR": "1",
# Prevent optional login-shell env snapshot import into exec runs.
"OPENCLAW_EXEC_SHELL_SNAPSHOT": "0",
}
if mapping:
if not credentials_directory:
raise BackendError("PROVIDER_CREDENTIAL_DIRECTORY_UNAVAILABLE")
credential_root = Path(credentials_directory)
for env_name, credential_name in mapping.items():
if (
not isinstance(env_name, str)
or not re.fullmatch(r"[A-Z][A-Z0-9_]{2,63}", env_name)
or not isinstance(credential_name, str)
or not re.fullmatch(r"[A-Za-z0-9._-]{1,128}", credential_name)
):
raise BackendError("PROVIDER_CREDENTIAL_MAP_INVALID")
path = credential_root / credential_name
if not path.is_file() or path.is_symlink():
raise BackendError("PROVIDER_CREDENTIAL_UNAVAILABLE", details={"name": credential_name})
secret = path.read_text(encoding="utf-8").strip()
if not secret:
raise BackendError("PROVIDER_CREDENTIAL_EMPTY", details={"name": credential_name})
environment[env_name] = secret
return environment
def build_prompt(task: Mapping[str, Any]) -> str:
criteria = "\n".join(f"- {item}" for item in task["success_criteria"])
return f"""You are the autonomous engineering runtime for AIOS task {task['task_id']}.
Repository: {task['repository']}
Base OID: {task['base_oid']}
Target branch (owned by AIOS Governed GitOps, not by you): {task['target_branch']}
Goal:
{task['goal']}
Instructions:
{task['instructions']}
Success criteria:
{criteria}
Execution boundary:
- Work only inside the current disposable worktree.
- Inspect the repository yourself and determine the root cause and files to change.
- Implement the smallest correct fix.
- Run focused tests and repair failures when feasible.
- Do NOT run `git commit`, `git push`, `git update-ref`, create branches/tags, or modify authoritative refs.
- Do NOT attempt to access AIOS Agent Task credentials, writer leases, or Governed GitOps credentials.
- Leave Git HEAD unchanged; AIOS independently inspects the diff, reruns verification, and performs the governed commit.
- In your final answer, concisely state root cause, files changed, and tests you ran.
"""
def openclaw_timeout() -> int:
raw = os.environ.get("AIOS_OPENCLAW_EXEC_TIMEOUT_SECONDS", str(DEFAULT_TIMEOUT))
try:
value = int(raw)
except ValueError as exc:
raise BackendError("OPENCLAW_TIMEOUT_INVALID") from exc
if not 60 <= value <= 7200:
raise BackendError("OPENCLAW_TIMEOUT_INVALID")
return value
def invoke_openclaw(task: Mapping[str, Any], worktree: Path) -> dict[str, Any]:
binary = openclaw_binary()
model = nonempty(os.environ.get("AIOS_OPENCLAW_MODEL"), "openclaw_model", 256)
timeout_seconds = openclaw_timeout()
prompt = build_prompt(task)
argv = [
str(binary),
"agent",
"exec",
"--message-file",
"-",
"--cwd",
str(worktree),
"--model",
model,
"--auth-env-only",
"--timeout",
str(timeout_seconds),
"--json",
]
try:
pinned_fd = int(worktree.name)
except ValueError as exc:
raise BackendError("PINNED_WORKTREE_FD_INVALID") from exc
try:
completed = subprocess.run(
argv,
input=prompt.encode("utf-8"),
cwd=str(worktree),
pass_fds=(pinned_fd,),
capture_output=True,
check=False,
timeout=timeout_seconds + 90,
env=provider_environment(),
)
except subprocess.TimeoutExpired:
return result(
"FAILED",
"OPENCLAW_HARD_TIMEOUT",
evidence={"backend": "openclaw-agent-exec", "timeout_seconds": timeout_seconds},
)
except OSError as exc:
return result(
"FAILED",
"OPENCLAW_EXEC_FAILED",
evidence={"backend": "openclaw-agent-exec", "error": type(exc).__name__},
)
stdout = completed.stdout[:MAX_OUTPUT_BYTES]
try:
envelope = json.loads(stdout.decode("utf-8"))
except (UnicodeDecodeError, json.JSONDecodeError):
return result(
"FAILED",
"OPENCLAW_RESULT_INVALID",
evidence={
"backend": "openclaw-agent-exec",
"exit_code": completed.returncode,
"stderr_sha256": __import__("hashlib").sha256(completed.stderr).hexdigest(),
},
)
if not isinstance(envelope, Mapping):
return result("FAILED", "OPENCLAW_RESULT_INVALID", evidence={"backend": "openclaw-agent-exec"})
ok = envelope.get("ok") is True and envelope.get("status") == "ok" and completed.returncode == 0
final_text = str(envelope.get("final") or "").strip()
evidence = {
"backend": "openclaw-agent-exec",
"openclaw_status": envelope.get("status"),
"exit_code": completed.returncode,
"model": envelope.get("model"),
"provider": envelope.get("provider"),
"session_id_present": bool(envelope.get("sessionId")),
"code_mode_engaged": envelope.get("codeModeEngaged"),
"assistant_turns": envelope.get("assistantTurns"),
"tool_summary": envelope.get("toolSummary") if isinstance(envelope.get("toolSummary"), Mapping) else {},
}
if not ok:
error = envelope.get("error")
if isinstance(error, Mapping):
evidence["error_kind"] = error.get("kind")
summary = "OPENCLAW_" + str(envelope.get("status") or "ERROR").upper()
return result("FAILED", summary, evidence=evidence)
summary = final_text or "OpenClaw completed the engineering turn."
return result("COMPLETED", summary, evidence=evidence)
def main() -> int:
try:
request = read_request()
task, worktree = validate_request(request)
return emit(invoke_openclaw(task, worktree))
except BackendError as exc:
return emit(result("BLOCKED", exc.code, evidence=exc.details))
except Exception as exc:
return emit(result("FAILED", "OPENCLAW_BACKEND_INTERNAL_ERROR", evidence={"error": type(exc).__name__}))
if __name__ == "__main__":
raise SystemExit(main())
@@ -0,0 +1,822 @@
#!/usr/bin/env python3
from __future__ import annotations
import base64
from datetime import datetime, timezone
import grp
import hashlib
import json
import os
from pathlib import Path
import re
import shutil
import subprocess
import tempfile
import threading
import time
import urllib.error
import urllib.request
from typing import Any, Mapping, Sequence
from intent_runtime import IntentError, Resolution, is_claimable_intent, resolve
from runtime_client import RuntimeErrorV2, collect_changes, invoke, request_payload, scan_secrets
API_BASE = os.environ.get("AIOS_AGENT_TASK_API_URL", "http://127.0.0.1:18940").rstrip("/")
BRIDGE_BASE = os.environ.get("AIOS_GOVERNED_WRITE_API_URL", "http://127.0.0.1:19998").rstrip("/")
AGENT_ID = os.environ.get("AIOS_AGENT_ID", "online-engineering-worker-cn-network-mkt-autonomous-20260815")
AGENT_VERSION = os.environ.get("AIOS_AGENT_VERSION", "2026-08-15.4-autonomous-intent")
REPOSITORY = os.environ.get("AIOS_AUTONOMOUS_REPOSITORY", "aios/aios")
BASE_REF = os.environ.get("AIOS_AUTONOMOUS_BASE_REF", "refs/heads/main")
AUTHORITY_GIT_DIR = Path(os.environ.get("AIOS_AUTHORITY_GIT_DIR", "/data/aios-gitops/repo.git"))
STATE_ROOT = Path(os.environ.get("AIOS_AUTONOMOUS_STATE_ROOT", "/var/lib/aios-autonomous-agent-worker"))
WORKSPACE_ROOT = Path(os.environ.get("AIOS_AUTONOMOUS_WORKSPACE_ROOT", str(STATE_ROOT / "workspaces")))
SHARED_GROUP = os.environ.get("AIOS_AGENT_RUNTIME_SHARED_GROUP", "aios-agent-runtime")
DEFAULT_ADAPTER = os.environ.get("AIOS_AUTONOMOUS_DEFAULT_ADAPTER", "hermes")
HERMES_SOCKET = Path(os.environ.get("AIOS_HERMES_BROKER_SOCKET", "/run/aios-agent-runtime/hermes.sock"))
CODEX_SOCKET = Path(os.environ.get("AIOS_CODEX_BROKER_SOCKET", "/run/aios-agent-runtime/codex.sock"))
OPENCLAW_SOCKET = Path(os.environ.get("AIOS_OPENCLAW_BROKER_SOCKET", "/run/aios-agent-runtime/openclaw.sock"))
POLL_SECONDS = int(os.environ.get("AIOS_AUTONOMOUS_POLL_SECONDS", "5"))
HEARTBEAT_SECONDS = int(os.environ.get("AIOS_AUTONOMOUS_HEARTBEAT_SECONDS", "10"))
LEASE_SECONDS = int(os.environ.get("AIOS_AUTONOMOUS_LEASE_SECONDS", "300"))
RUNTIME_TIMEOUT_SECONDS = int(os.environ.get("AIOS_AUTONOMOUS_RUNTIME_TIMEOUT_SECONDS", "3600"))
MAX_OUTPUT_BYTES = 64 * 1024
OID_RE = re.compile(r"^[0-9a-f]{40}$")
SENSITIVE_RE = re.compile(
r"(?i)(authorization\s*:\s*bearer\s+\S+|(?:password|passwd|secret|token|api[_-]?key)\s*[:=]\s*\S+)"
)
class WorkerError(RuntimeError):
def __init__(self, code: str, *, phase: str, details: Mapping[str, Any] | None = None) -> None:
super().__init__(code)
self.code = code
self.phase = phase
self.details = dict(details or {})
def now() -> str:
return datetime.now(timezone.utc).isoformat()
def sha256_bytes(value: bytes) -> str:
return hashlib.sha256(value).hexdigest()
def sha256_json(value: Any) -> str:
return sha256_bytes(json.dumps(value, ensure_ascii=False, sort_keys=True, separators=(",", ":")).encode("utf-8"))
def sanitize(value: Any) -> Any:
if isinstance(value, Mapping):
result: dict[str, Any] = {}
for key, inner in value.items():
lowered = str(key).lower()
if any(word in lowered for word in ("token", "secret", "credential", "authorization", "password", "api_key", "apikey", "access_key")):
result[str(key)] = "[REDACTED]"
else:
result[str(key)] = sanitize(inner)
return result
if isinstance(value, list):
return [sanitize(item) for item in value]
if isinstance(value, tuple):
return [sanitize(item) for item in value]
if isinstance(value, str):
return SENSITIVE_RE.sub("[REDACTED]", value)
return value
def audit(event_type: str, **fields: Any) -> None:
STATE_ROOT.mkdir(parents=True, mode=0o750, exist_ok=True)
path = STATE_ROOT / "events.jsonl"
record = {
"event_type": event_type,
"created_at": now(),
"agent_id": AGENT_ID,
"agent_version": AGENT_VERSION,
"credential_material_exposed": False,
"writer_lease_material_exposed": False,
**sanitize(fields),
}
with path.open("a", encoding="utf-8") as handle:
handle.write(json.dumps(record, ensure_ascii=False, sort_keys=True) + "\n")
os.chmod(path, 0o640)
print(json.dumps(record, ensure_ascii=False, sort_keys=True), flush=True)
def atomic_json(path: Path, value: Mapping[str, Any]) -> None:
path.parent.mkdir(parents=True, mode=0o750, exist_ok=True)
if path.parent.is_symlink() or path.is_symlink():
raise WorkerError("STATE_PATH_SYMLINK_REJECTED", phase="state")
fd, temporary = tempfile.mkstemp(prefix=f".{path.name}.", dir=str(path.parent))
try:
os.fchmod(fd, 0o640)
with os.fdopen(fd, "w", encoding="utf-8") as handle:
json.dump(sanitize(dict(value)), handle, ensure_ascii=False, sort_keys=True)
handle.write("\n")
handle.flush()
os.fsync(handle.fileno())
os.replace(temporary, path)
finally:
if os.path.exists(temporary):
os.unlink(temporary)
def state_path(task_id: str) -> Path:
safe = re.sub(r"[^A-Za-z0-9._:-]+", "-", task_id)[:180]
return STATE_ROOT / "tasks" / f"{safe}.json"
def load_state(task_id: str) -> dict[str, Any] | None:
path = state_path(task_id)
if not path.is_file() or path.is_symlink():
return None
try:
value = json.loads(path.read_text(encoding="utf-8"))
except Exception:
return None
return value if isinstance(value, dict) else None
def save_state(task_id: str, value: Mapping[str, Any]) -> None:
atomic_json(state_path(task_id), value)
def api_call(method: str, route: str, token: str, body: Mapping[str, Any] | None = None) -> tuple[int, dict[str, Any]]:
payload = None if body is None else json.dumps(dict(body), ensure_ascii=False, sort_keys=True, separators=(",", ":")).encode("utf-8")
headers = {"Authorization": "Bearer " + token, "Accept": "application/json"}
if payload is not None:
headers["Content-Type"] = "application/json"
request = urllib.request.Request(API_BASE + route, data=payload, headers=headers, method=method)
try:
with urllib.request.urlopen(request, timeout=20) as response:
value = json.loads(response.read().decode("utf-8"))
return response.status, value if isinstance(value, dict) else {}
except urllib.error.HTTPError as exc:
try:
value = json.loads(exc.read(4096).decode("utf-8", "replace"))
except Exception:
value = {"error": "HTTP_ERROR"}
return exc.code, value if isinstance(value, dict) else {"error": "HTTP_ERROR"}
except (urllib.error.URLError, TimeoutError):
return 599, {"error": "AGENT_TASK_API_UNAVAILABLE"}
def bridge_call(route: str, body: Mapping[str, Any]) -> tuple[int, dict[str, Any]]:
if BRIDGE_BASE != "http://127.0.0.1:19998":
raise WorkerError(
"NON_STAGING_BRIDGE_REJECTED",
phase="startup",
details={"bridge_base": BRIDGE_BASE},
)
payload = json.dumps(dict(body), ensure_ascii=False, sort_keys=True, separators=(",", ":")).encode("utf-8")
request = urllib.request.Request(
BRIDGE_BASE + route,
data=payload,
headers={"Accept": "application/json", "Content-Type": "application/json"},
method="POST",
)
try:
with urllib.request.urlopen(request, timeout=60) as response:
value = json.loads(response.read().decode("utf-8"))
return response.status, value if isinstance(value, dict) else {}
except urllib.error.HTTPError as exc:
try:
value = json.loads(exc.read(4096).decode("utf-8", "replace"))
except Exception:
value = {"error": "HTTP_ERROR"}
return exc.code, value if isinstance(value, dict) else {"error": "HTTP_ERROR"}
except (urllib.error.URLError, TimeoutError):
return 599, {"error": "GOVERNED_WRITE_API_UNAVAILABLE"}
def load_agent_token() -> str:
directory = Path(os.environ.get("CREDENTIALS_DIRECTORY", ""))
path = directory / "agent_token"
if not path.is_file():
raise WorkerError("AGENT_RUNTIME_CREDENTIAL_UNAVAILABLE", phase="startup")
token = path.read_text(encoding="utf-8").strip()
if len(token) < 24:
raise WorkerError("AGENT_RUNTIME_CREDENTIAL_INVALID", phase="startup")
return token
def available_adapters() -> tuple[str, ...]:
values: list[str] = []
for name, path in (("hermes", HERMES_SOCKET), ("codex", CODEX_SOCKET), ("openclaw", OPENCLAW_SOCKET)):
try:
if path.is_socket():
values.append(name)
except OSError:
pass
return tuple(values)
def capabilities() -> tuple[str, ...]:
values = {
"online_agent_task_api", "task.poll", "task.claim", "task.heartbeat", "task.result",
"intent.task", "engineering.consumer", "engineering.execute", "engineering.autonomous",
"workspace.worktree", "git.read", "git.diff", "git.test",
"governed.write.branch", "governed.write.file",
}
values.update(f"agent.runtime.{name}" for name in available_adapters())
return tuple(sorted(values))
class LeaseHeartbeat:
def __init__(self, task_id: str, agent_token: str, lease_token: str) -> None:
self.task_id = task_id
self.agent_token = agent_token
self.lease_token = lease_token
self.stage = "claimed"
self.failed: str | None = None
self.stop = threading.Event()
self.thread = threading.Thread(target=self._run, daemon=True, name=f"heartbeat-{task_id}")
def set_stage(self, stage: str) -> None:
self.stage = stage
audit("TASK_PROGRESS_STAGE", task_id=self.task_id, stage=stage)
def send(self) -> None:
status, response = api_call(
"POST",
f"/v1/tasks/{self.task_id}/heartbeat",
self.agent_token,
{
"lease_token": self.lease_token,
"lease_seconds": LEASE_SECONDS,
"progress": {
"stage": self.stage,
"event_type": "TASK_STARTED" if self.stage == "started" else "TASK_PROGRESS",
"execution_mode": "autonomous-intent-v2",
"production_side_effects": False,
"main_write": False,
},
},
)
if 200 <= status < 300:
audit(
"TASK_HEARTBEAT",
task_id=self.task_id,
stage=self.stage,
api_status=status,
task_status=response.get("status"),
lease_expires_at=response.get("lease_expires_at"),
)
else:
self.failed = str(response.get("error") or status)
audit("TASK_HEARTBEAT_FAILED", task_id=self.task_id, stage=self.stage, error=self.failed)
def _run(self) -> None:
while not self.stop.wait(HEARTBEAT_SECONDS):
try:
self.send()
except Exception as exc:
self.failed = type(exc).__name__
audit("TASK_HEARTBEAT_FAILED", task_id=self.task_id, error=self.failed)
def start(self) -> None:
self.thread.start()
def close(self) -> None:
self.stop.set()
self.thread.join(timeout=3)
def ensure_roots() -> None:
if WORKSPACE_ROOT.is_symlink() or STATE_ROOT.is_symlink() or AUTHORITY_GIT_DIR.is_symlink():
raise WorkerError("ROOT_SYMLINK_REJECTED", phase="startup")
if not AUTHORITY_GIT_DIR.is_dir():
raise WorkerError("AUTHORITY_GIT_DIR_UNAVAILABLE", phase="startup")
STATE_ROOT.mkdir(parents=True, mode=0o750, exist_ok=True)
WORKSPACE_ROOT.mkdir(parents=True, mode=0o750, exist_ok=True)
def run_git(args: Sequence[str], *, cwd: Path, timeout: int = 180) -> subprocess.CompletedProcess[bytes]:
completed = subprocess.run(
["/usr/bin/git", *args],
cwd=str(cwd),
capture_output=True,
check=False,
timeout=timeout,
env={"PATH": "/usr/bin:/bin", "LC_ALL": "C.UTF-8"},
)
if completed.returncode != 0:
raise WorkerError(
"GIT_COMMAND_FAILED",
phase="workspace",
details={"operation": args[0] if args else "", "exit_code": completed.returncode},
)
return completed
def prepare_worktree(task_id: str, base_oid: str) -> tuple[Path, Path]:
ensure_roots()
run_root = Path(tempfile.mkdtemp(prefix=f"{task_id[:48]}-", dir=str(WORKSPACE_ROOT)))
os.chmod(run_root, 0o750)
clone = run_root / "repository"
worktree = run_root / "worktree"
run_git(["clone", "--no-local", "--no-hardlinks", str(AUTHORITY_GIT_DIR), str(clone)], cwd=run_root, timeout=300)
run_git(["-C", str(clone), "rev-parse", "--verify", f"{base_oid}^{{commit}}"], cwd=run_root)
run_git(["-C", str(clone), "worktree", "add", "--detach", str(worktree), base_oid], cwd=run_root, timeout=300)
return run_root, worktree
def share_run_root(run_root: Path) -> None:
try:
gid = grp.getgrnam(SHARED_GROUP).gr_gid
except KeyError as exc:
raise WorkerError("RUNTIME_SHARED_GROUP_UNAVAILABLE", phase="workspace") from exc
root = run_root.resolve()
if not root.is_dir() or run_root.is_symlink():
raise WorkerError("RUNTIME_RUN_ROOT_INVALID", phase="workspace")
for current, dirs, files in os.walk(root, topdown=True, followlinks=False):
current_path = Path(current)
for name in dirs + files:
candidate = current_path / name
if candidate.is_symlink():
raise WorkerError(
"RUNTIME_WORKSPACE_SYMLINK_REJECTED",
phase="workspace",
details={"path": str(candidate.relative_to(root))},
)
for candidate in [current_path, *(current_path / name for name in dirs), *(current_path / name for name in files)]:
st = candidate.stat()
os.chown(candidate, -1, gid)
mode = st.st_mode & 0o777
os.chmod(candidate, mode | (0o070 if candidate.is_dir() else 0o060))
def cleanup_run_root(run_root: Path) -> None:
root = WORKSPACE_ROOT.resolve()
candidate = run_root.resolve(strict=False)
if candidate == root or root not in candidate.parents:
raise WorkerError("CLEANUP_SCOPE_INVALID", phase="cleanup")
if candidate.exists():
shutil.rmtree(candidate)
def allowed_programs() -> set[str]:
raw = os.environ.get("AIOS_AUTONOMOUS_ALLOWED_PROGRAMS_JSON", '["/usr/bin/git","/usr/bin/python3"]')
try:
value = json.loads(raw)
except json.JSONDecodeError as exc:
raise WorkerError("ALLOWED_PROGRAMS_CONFIG_INVALID", phase="startup") from exc
if not isinstance(value, list) or not all(isinstance(item, str) and item.startswith("/") for item in value):
raise WorkerError("ALLOWED_PROGRAMS_CONFIG_INVALID", phase="startup")
return set(value)
def validate_verification_argv(value: Any) -> list[str]:
if not isinstance(value, list) or not value or not all(isinstance(item, str) and item for item in value):
raise WorkerError("VERIFICATION_ARGV_INVALID", phase="verification")
argv = list(value)
if argv[0] not in allowed_programs():
raise WorkerError("VERIFICATION_PROGRAM_NOT_ALLOWED", phase="verification", details={"program": Path(argv[0]).name})
if argv[0].endswith("/git") and any(item in {"push", "update-ref", "receive-pack", "send-pack", "commit", "reset", "clean"} for item in argv[1:]):
raise WorkerError("VERIFICATION_GIT_MUTATION_REJECTED", phase="verification")
if argv[0].endswith("/python3") and "-c" in argv:
raise WorkerError("VERIFICATION_INLINE_PYTHON_REJECTED", phase="verification")
return argv
def run_verification(label: str, argv: Sequence[str], *, cwd: Path, timeout: int) -> dict[str, Any]:
started = time.monotonic()
completed = subprocess.run(
list(argv),
cwd=str(cwd),
capture_output=True,
check=False,
timeout=timeout,
env={"PATH": "/usr/local/bin:/usr/bin:/bin", "LC_ALL": "C.UTF-8"},
)
result = {
"label": label[:120],
"argv": [Path(item).name if idx == 0 else item for idx, item in enumerate(argv)],
"exit_code": completed.returncode,
"stdout_sha256": sha256_bytes(completed.stdout),
"stderr_sha256": sha256_bytes(completed.stderr),
"duration_ms": int((time.monotonic() - started) * 1000),
}
audit("VERIFICATION_RESULT", **result)
if completed.returncode != 0:
raise WorkerError(
"VERIFICATION_FAILED",
phase="verification",
details={"label": label[:120], "exit_code": completed.returncode},
)
return result
def independent_verification(
worktree: Path,
changed_files: Sequence[str],
runtime_verification: Sequence[Mapping[str, Any]],
) -> list[dict[str, Any]]:
results = [
run_verification(
"git_diff_check",
["/usr/bin/git", "-C", str(worktree), "diff", "--check"],
cwd=worktree,
timeout=120,
)
]
py_files = [item for item in changed_files if item.endswith(".py")]
if py_files:
results.append(
run_verification(
"changed_python_compile",
["/usr/bin/python3", "-m", "py_compile", *py_files],
cwd=worktree,
timeout=300,
)
)
test_files = [
item for item in py_files
if item.startswith("tests/") or Path(item).name.startswith("test_")
]
if test_files:
results.append(
run_verification(
"changed_pytest",
["/usr/bin/python3", "-m", "pytest", "-q", *test_files],
cwd=worktree,
timeout=900,
)
)
seen: set[str] = set()
for item in runtime_verification:
argv = validate_verification_argv(item.get("argv"))
cwd_value = item.get("cwd", ".")
if cwd_value in {"", "."}:
cwd = worktree
else:
if not isinstance(cwd_value, str) or cwd_value.startswith(("/", "~")) or ".." in Path(cwd_value).parts:
raise WorkerError("VERIFICATION_CWD_INVALID", phase="verification")
cwd = (worktree / cwd_value).resolve()
if worktree.resolve() not in cwd.parents and cwd != worktree.resolve():
raise WorkerError("VERIFICATION_CWD_ESCAPE", phase="verification")
timeout = item.get("timeout_seconds", 300)
if not isinstance(timeout, int) or isinstance(timeout, bool) or not 1 <= timeout <= 1800:
raise WorkerError("VERIFICATION_TIMEOUT_INVALID", phase="verification")
key = sha256_json({"argv": argv, "cwd": str(cwd), "timeout": timeout})
if key in seen:
continue
seen.add(key)
results.append(
run_verification(str(item.get("label") or "agent_requested_verification"), argv, cwd=cwd, timeout=timeout)
)
return results
def governed_commit(
task_id: str,
resolution: Resolution,
worktree: Path,
changed_files: Sequence[str],
) -> dict[str, Any]:
files: dict[str, str] = {}
for relative in changed_files:
path = (worktree / relative).resolve()
if worktree.resolve() not in path.parents or not path.is_file():
raise WorkerError("COMMIT_FILE_INVALID", phase="committing", details={"path": relative})
files[relative] = base64.b64encode(path.read_bytes()).decode("ascii")
governed_task_id = f"{task_id}-writer"
create_key = f"{task_id}-governed-v2"
commit_key = f"{task_id}-commit-v2"
status, created = bridge_call(
"/v2/governed-task",
{
"task_id": governed_task_id,
"repository": resolution.repository,
"base_oid": resolution.base_oid,
"target_branch": resolution.target_branch,
"risk": "engineering",
"description": f"Autonomous intent task {task_id}",
"idempotency_key": create_key,
},
)
if not 200 <= status < 300:
raise WorkerError(
"GOVERNED_TASK_CREATE_FAILED",
phase="committing",
details={"api_status": status, "error": created.get("error")},
)
generation = int(created.get("generation") or 1)
expected = str(created.get("expected_branch_oid") or resolution.base_oid)
branch_mode = str(created.get("branch_mode") or "CREATE_NEW")
status, lease = bridge_call(
"/v2/writer-lease",
{
"schema": "aios.task-writer-lease.v2",
"task_id": governed_task_id,
"repository": resolution.repository,
"branch_mode": branch_mode,
"base_oid": resolution.base_oid,
"expected_branch_oid": expected,
"target_branch": resolution.target_branch,
"generation": generation,
"lease_seconds": 900,
},
)
if not 200 <= status < 300:
raise WorkerError(
"WRITER_LEASE_ACQUIRE_FAILED",
phase="committing",
details={"api_status": status, "error": lease.get("error")},
)
status, committed = bridge_call(
"/v2/commit-files",
{
"task_id": governed_task_id,
"repository": resolution.repository,
"expected_branch_oid": expected,
"target_branch": resolution.target_branch,
"generation": generation,
"idempotency_key": commit_key,
"files": files,
"message": f"chore(agent): execute online intent task {task_id}"[:240],
},
)
if not 200 <= status < 300:
raise WorkerError(
"GOVERNED_COMMIT_FAILED",
phase="committing",
details={"api_status": status, "error": committed.get("error")},
)
commit_oid = committed.get("commit_oid")
if not isinstance(commit_oid, str) or not OID_RE.fullmatch(commit_oid):
raise WorkerError("GOVERNED_COMMIT_OID_INVALID", phase="committing")
return {
"governed_task_id": governed_task_id,
"generation": generation,
"branch_mode": branch_mode,
"commit_oid": commit_oid,
"commit_visible": committed.get("commit_visible"),
"target_branch": resolution.target_branch,
"create_idempotency_key": create_key,
"commit_idempotency_key": commit_key,
}
def socket_for(adapter: str) -> Path:
if adapter == "hermes":
return HERMES_SOCKET
if adapter == "codex":
return CODEX_SOCKET
if adapter == "openclaw":
return OPENCLAW_SOCKET
raise WorkerError("ADAPTER_INVALID", phase="runtime")
def resolve_task(task: Mapping[str, Any], prior: Mapping[str, Any] | None = None) -> Resolution:
try:
return resolve(
task,
authority_git_dir=AUTHORITY_GIT_DIR,
repository=REPOSITORY,
base_ref=BASE_REF,
available_adapters=available_adapters(),
default_adapter=DEFAULT_ADAPTER,
prior=prior,
)
except IntentError as exc:
raise WorkerError(exc.code, phase="intent_resolution", details=exc.details) from exc
def execute_claim(task: Mapping[str, Any], claim: Mapping[str, Any], token: str) -> int:
task_id = str(claim.get("task_id") or task.get("task_id") or "")
lease_token = claim.get("lease_token")
if not isinstance(lease_token, str) or len(lease_token) < 24:
raise WorkerError("LEASE_TOKEN_NOT_RETURNED", phase="claim")
attempt = int(claim.get("attempt") or 0)
state = load_state(task_id) or {"task_id": task_id, "phase": "claimed"}
prior = state.get("resolution") if isinstance(state.get("resolution"), Mapping) else None
resolution = resolve_task(task, prior=prior)
state["resolution"] = resolution.to_json()
state["attempt"] = attempt
save_state(task_id, state)
heartbeat = LeaseHeartbeat(task_id, token, lease_token)
heartbeat.start()
run_root: Path | None = None
try:
heartbeat.set_stage("started")
heartbeat.send()
audit("TASK_STARTED", task_id=task_id, attempt=attempt, adapter=resolution.adapter)
existing_commit = state.get("governed")
if isinstance(existing_commit, Mapping) and isinstance(existing_commit.get("commit_oid"), str):
result = {
"status": "SUCCEEDED",
"verification_state": "VERIFIED_BY_WORKER_READBACK_PENDING_SERVER",
"task_id": task_id,
"execution_mode": "autonomous-intent-v2",
"adapter": resolution.adapter,
"resolution_digest": resolution.resolution_digest,
"governed": dict(existing_commit),
"recovered_after_commit": True,
"secret_material_exposed": False,
"direct_main_write": False,
"force_push": False,
"production_write": False,
}
else:
heartbeat.set_stage("workspace_preparing")
run_root, worktree = prepare_worktree(task_id, resolution.base_oid)
share_run_root(run_root)
state["phase"] = "workspace_prepared"
save_state(task_id, state)
heartbeat.set_stage("agent_executing")
payload = request_payload(
adapter=resolution.adapter,
task_id=task_id,
goal=resolution.goal,
instructions=resolution.instructions,
success_criteria=resolution.success_criteria,
repository=resolution.repository,
base_oid=resolution.base_oid,
target_branch=resolution.target_branch,
worktree=worktree,
)
runtime = invoke(
socket_for(resolution.adapter),
payload,
timeout_seconds=RUNTIME_TIMEOUT_SECONDS,
)
heartbeat.set_stage("validating")
changed_files = collect_changes(worktree, resolution.base_oid)
scan_secrets(worktree, changed_files)
verification = independent_verification(worktree, changed_files, runtime.verification)
heartbeat.set_stage("committing")
governed = governed_commit(task_id, resolution, worktree, changed_files)
state.update({"phase": "commit_done", "governed": governed, "changed_files": list(changed_files)})
save_state(task_id, state)
result = {
"status": "SUCCEEDED",
"verification_state": "VERIFIED_BY_WORKER_READBACK_PENDING_SERVER",
"task_id": task_id,
"execution_mode": "autonomous-intent-v2",
"adapter": resolution.adapter,
"resolution_digest": resolution.resolution_digest,
"base_oid": resolution.base_oid,
"target_branch": resolution.target_branch,
"changed_files": list(changed_files),
"tests": verification,
"agent_summary": runtime.summary,
"agent_evidence": sanitize(runtime.evidence),
"governed": governed,
"secret_material_exposed": False,
"direct_main_write": False,
"force_push": False,
"production_write": False,
}
if heartbeat.failed:
raise WorkerError("HEARTBEAT_UNHEALTHY", phase="result_submitting", details={"error": heartbeat.failed})
heartbeat.set_stage("result_submitting")
result_key = f"{task_id}-result-v2"
status, response = api_call(
"POST",
f"/v1/tasks/{task_id}/result",
token,
{
"lease_token": lease_token,
"idempotency_key": result_key,
"result": result,
},
)
if not 200 <= status < 300:
raise WorkerError(
"TASK_RESULT_SUBMIT_FAILED",
phase="result_submitting",
details={"api_status": status, "error": response.get("error")},
)
audit("RESULT_SUBMITTED", task_id=task_id, task_status=response.get("status"), result_idempotency_key=result_key)
state["phase"] = "result_submitted"
save_state(task_id, state)
return 0
except (WorkerError, RuntimeErrorV2) as exc:
code = exc.code if hasattr(exc, "code") else type(exc).__name__
phase = exc.phase if isinstance(exc, WorkerError) else "runtime"
details = exc.details if hasattr(exc, "details") else {}
audit("AUTONOMOUS_EXECUTION_FAILED", task_id=task_id, error=code, phase=phase, details=details)
failure_status = "BLOCKED" if code in {
"AGENT_RUNTIME_UNAVAILABLE", "RUNTIME_UNAVAILABLE", "RUNTIME_BLOCKED",
"GOVERNED_WRITE_API_UNAVAILABLE", "NON_STAGING_BRIDGE_REJECTED",
} else "FAILED_RETRYABLE"
failure = {
"status": failure_status,
"verification_state": "UNVERIFIED",
"task_id": task_id,
"execution_mode": "autonomous-intent-v2",
"error": code,
"phase": phase,
"details": sanitize(details),
"secret_material_exposed": False,
"direct_main_write": False,
"force_push": False,
"production_write": False,
}
if not heartbeat.failed:
status, response = api_call(
"POST",
f"/v1/tasks/{task_id}/result",
token,
{
"lease_token": lease_token,
"idempotency_key": f"{task_id}-attempt-{attempt}-failure-v2",
"result": failure,
},
)
audit("FAILURE_RESULT_SUBMITTED", task_id=task_id, api_status=status, task_status=response.get("status"))
return 20
finally:
heartbeat.close()
if run_root is not None:
try:
cleanup_run_root(run_root)
audit("WORKSPACE_CLEANED", task_id=task_id, cleanup="PASS")
except Exception as exc:
audit("WORKSPACE_CLEANUP_FAILED", task_id=task_id, error=type(exc).__name__)
def worker_once(token: str) -> int:
status, poll = api_call("POST", "/v1/tasks/poll", token, {"limit": 100})
if status != 200:
audit("TASK_POLL_FAILED", api_status=status, error=poll.get("error"))
return 10
worker_caps = capabilities()
candidates: list[Mapping[str, Any]] = []
for item in poll.get("tasks", []):
if not isinstance(item, Mapping):
continue
if not is_claimable_intent(item, worker_caps):
continue
try:
# Read-only preflight. Do not consume an attempt if runtime/base is unavailable.
resolve_task(item, prior=(load_state(str(item.get("task_id") or "")) or {}).get("resolution"))
except WorkerError as exc:
audit("TASK_SKIPPED_PREFLIGHT", task_id=item.get("task_id"), error=exc.code)
continue
candidates.append(item)
if not candidates:
return 0
# Server priority/order remains authoritative; take the first visible compatible task.
task = candidates[0]
task_id = str(task.get("task_id") or "")
status, claim = api_call("POST", f"/v1/tasks/{task_id}/claim", token, {"lease_seconds": LEASE_SECONDS})
if status != 200:
audit("TASK_CLAIM_FAILED", task_id=task_id, api_status=status, error=claim.get("error"))
return 11
audit(
"TASK_CLAIMED",
task_id=task_id,
attempt=claim.get("attempt"),
revision=claim.get("revision"),
claim_mode="server_atomic",
execution_mode="autonomous-intent-v2",
)
merged = dict(task)
merged.update({key: value for key, value in claim.items() if key not in {"lease_token"}})
merged["task_id"] = task_id
return execute_claim(merged, claim, token)
def main() -> int:
ensure_roots()
if BRIDGE_BASE != "http://127.0.0.1:19998":
raise WorkerError("NON_STAGING_BRIDGE_REJECTED", phase="startup", details={"bridge_base": BRIDGE_BASE})
token = load_agent_token()
audit(
"AUTONOMOUS_WORKER_READY",
poll_mode="continuous",
claim_mode="server_atomic",
task_scope="intent-only",
bridge_base=BRIDGE_BASE,
available_adapters=list(available_adapters()),
capabilities=list(capabilities()),
main_write=False,
production_write=False,
force_push=False,
)
while True:
try:
result = worker_once(token)
except Exception as exc:
audit("WORKER_LOOP_ERROR", error=type(exc).__name__)
result = 20
time.sleep(POLL_SECONDS if result == 0 else max(POLL_SECONDS, 5))
if __name__ == "__main__":
raise SystemExit(main())
@@ -0,0 +1,273 @@
from __future__ import annotations
from dataclasses import asdict, dataclass
import hashlib
import json
from pathlib import Path
import re
import subprocess
from typing import Any, Mapping, Sequence
OID_RE = re.compile(r"^[0-9a-f]{40}$")
TASK_ID_RE = re.compile(r"^[A-Za-z0-9][A-Za-z0-9._:-]{2,179}$")
REPOSITORY_RE = re.compile(r"^[A-Za-z0-9][A-Za-z0-9._/-]{2,199}$")
ALLOWED_ADAPTERS = frozenset({"hermes", "codex", "openclaw"})
class IntentError(RuntimeError):
def __init__(self, code: str, *, details: Mapping[str, Any] | None = None) -> None:
super().__init__(code)
self.code = code
self.details = dict(details or {})
def stable_digest(value: Any) -> str:
raw = json.dumps(value, ensure_ascii=False, sort_keys=True, separators=(",", ":")).encode("utf-8")
return hashlib.sha256(raw).hexdigest()
def _text(value: Any, field: str, limit: int) -> str:
if not isinstance(value, str):
raise IntentError(f"{field.upper()}_INVALID")
value = value.strip()
if not value or len(value) > limit:
raise IntentError(f"{field.upper()}_INVALID")
return value
def _string_list(value: Any, field: str, *, required: bool) -> tuple[str, ...]:
if value is None and not required:
return ()
if not isinstance(value, list) or len(value) > 64 or (required and not value):
raise IntentError(f"{field.upper()}_INVALID")
return tuple(_text(item, field, 4096) for item in value)
@dataclass(frozen=True)
class IntentTask:
task_id: str
goal: str
instructions: str
success_criteria: tuple[str, ...]
required_capabilities: tuple[str, ...]
digest: str
@classmethod
def parse(cls, task: Mapping[str, Any]) -> "IntentTask":
task_id = str(task.get("task_id") or "")
if not TASK_ID_RE.fullmatch(task_id):
raise IntentError("TASK_ID_INVALID")
metadata = task.get("metadata")
if isinstance(metadata, Mapping) and isinstance(metadata.get("execution"), Mapping):
raise IntentError("STRUCTURED_TASK_NOT_INTENT")
goal = _text(task.get("goal"), "goal", 16384)
instructions = _text(
task.get("instructions") or "Inspect the repository, implement the goal, and satisfy the success criteria.",
"instructions",
65536,
)
success = _string_list(task.get("success_criteria"), "success_criteria", required=True)
capabilities = _string_list(task.get("required_capabilities") or [], "required_capabilities", required=False)
required_markers = {"intent.task", "engineering.execute"}
missing = sorted(required_markers - set(capabilities))
if missing:
raise IntentError("INTENT_CAPABILITY_MARKER_REQUIRED", details={"missing": missing})
canonical = {
"task_id": task_id,
"goal": goal,
"instructions": instructions,
"success_criteria": success,
"required_capabilities": sorted(capabilities),
}
return cls(
task_id=task_id,
goal=goal,
instructions=instructions,
success_criteria=success,
required_capabilities=capabilities,
digest=stable_digest(canonical),
)
@dataclass(frozen=True)
class Resolution:
schema: str
task_id: str
repository: str
base_ref: str
base_oid: str
target_branch: str
adapter: str
intent_digest: str
goal: str
instructions: str
success_criteria: tuple[str, ...]
required_capabilities: tuple[str, ...]
resolution_digest: str
def to_json(self) -> dict[str, Any]:
value = asdict(self)
value["success_criteria"] = list(self.success_criteria)
value["required_capabilities"] = list(self.required_capabilities)
return value
@classmethod
def from_json(cls, value: Mapping[str, Any], *, intent: IntentTask) -> "Resolution":
if value.get("schema") != "aios.autonomous-intent-resolution.v2":
raise IntentError("RESOLUTION_SCHEMA_INVALID")
if value.get("task_id") != intent.task_id or value.get("intent_digest") != intent.digest:
raise IntentError("RESOLUTION_TASK_DRIFT")
result = cls(
schema=str(value["schema"]),
task_id=str(value["task_id"]),
repository=str(value["repository"]),
base_ref=str(value["base_ref"]),
base_oid=str(value["base_oid"]),
target_branch=str(value["target_branch"]),
adapter=str(value["adapter"]),
intent_digest=str(value["intent_digest"]),
goal=str(value["goal"]),
instructions=str(value["instructions"]),
success_criteria=tuple(str(item) for item in value.get("success_criteria") or []),
required_capabilities=tuple(str(item) for item in value.get("required_capabilities") or []),
resolution_digest=str(value["resolution_digest"]),
)
validate_resolution(result)
if result.resolution_digest != resolution_digest(result):
raise IntentError("RESOLUTION_DIGEST_INVALID")
return result
def read_base_oid(authority_git_dir: Path, base_ref: str) -> str:
if not base_ref.startswith("refs/heads/"):
raise IntentError("BASE_REF_INVALID")
if authority_git_dir.is_symlink() or not authority_git_dir.is_dir():
raise IntentError("AUTHORITY_GIT_DIR_INVALID")
completed = subprocess.run(
["/usr/bin/git", "--git-dir", str(authority_git_dir), "rev-parse", "--verify", f"{base_ref}^{{commit}}"],
capture_output=True,
text=True,
check=False,
timeout=30,
env={"PATH": "/usr/bin:/bin", "LC_ALL": "C.UTF-8"},
)
oid = completed.stdout.strip()
if completed.returncode != 0 or not OID_RE.fullmatch(oid):
raise IntentError("BASE_OID_UNAVAILABLE")
return oid
def derive_branch(task_id: str, digest: str) -> str:
slug = re.sub(r"[^A-Za-z0-9._-]+", "-", task_id).strip(".-").lower() or "task"
slug = slug[:100]
branch = f"agent/intent/{slug}-{digest[:12]}"
if len(branch) > 180:
branch = branch[:167] + "-" + digest[:12]
if ".." in branch or branch.endswith(("/", ".", ".lock")):
raise IntentError("TARGET_BRANCH_INVALID")
return branch
def select_adapter(capabilities: Sequence[str], available: Sequence[str], default_adapter: str) -> str:
advertised = set(available)
if not advertised.issubset(ALLOWED_ADAPTERS):
raise IntentError("ADAPTER_CONFIGURATION_INVALID")
requested = {
item.split(".", 2)[-1]
for item in capabilities
if item.startswith("agent.runtime.")
}
requested.discard("")
if len(requested) > 1:
raise IntentError("MULTIPLE_AGENT_RUNTIMES_REQUIRED")
adapter = next(iter(requested)) if requested else default_adapter
if adapter not in ALLOWED_ADAPTERS:
raise IntentError("AGENT_RUNTIME_INVALID")
if adapter not in advertised:
raise IntentError("AGENT_RUNTIME_UNAVAILABLE", details={"adapter": adapter})
return adapter
def resolution_digest(value: Resolution) -> str:
return stable_digest({
"schema": value.schema,
"task_id": value.task_id,
"repository": value.repository,
"base_ref": value.base_ref,
"base_oid": value.base_oid,
"target_branch": value.target_branch,
"adapter": value.adapter,
"intent_digest": value.intent_digest,
"goal": value.goal,
"instructions": value.instructions,
"success_criteria": value.success_criteria,
"required_capabilities": sorted(value.required_capabilities),
})
def validate_resolution(value: Resolution) -> None:
if not TASK_ID_RE.fullmatch(value.task_id):
raise IntentError("TASK_ID_INVALID")
if not REPOSITORY_RE.fullmatch(value.repository) or value.repository.startswith(("/", ".")):
raise IntentError("REPOSITORY_INVALID")
if not value.base_ref.startswith("refs/heads/"):
raise IntentError("BASE_REF_INVALID")
if not OID_RE.fullmatch(value.base_oid):
raise IntentError("BASE_OID_INVALID")
if not value.target_branch.startswith("agent/intent/"):
raise IntentError("TARGET_BRANCH_POLICY_INVALID")
if value.adapter not in ALLOWED_ADAPTERS:
raise IntentError("AGENT_RUNTIME_INVALID")
def resolve(
task: Mapping[str, Any],
*,
authority_git_dir: Path,
repository: str,
base_ref: str,
available_adapters: Sequence[str],
default_adapter: str,
prior: Mapping[str, Any] | None = None,
) -> Resolution:
intent = IntentTask.parse(task)
if prior is not None:
return Resolution.from_json(prior, intent=intent)
if not REPOSITORY_RE.fullmatch(repository) or repository.startswith(("/", ".")):
raise IntentError("REPOSITORY_INVALID")
base_oid = read_base_oid(authority_git_dir, base_ref)
adapter = select_adapter(intent.required_capabilities, available_adapters, default_adapter)
provisional = Resolution(
schema="aios.autonomous-intent-resolution.v2",
task_id=intent.task_id,
repository=repository,
base_ref=base_ref,
base_oid=base_oid,
target_branch=derive_branch(intent.task_id, intent.digest),
adapter=adapter,
intent_digest=intent.digest,
goal=intent.goal,
instructions=intent.instructions,
success_criteria=intent.success_criteria,
required_capabilities=intent.required_capabilities,
resolution_digest="",
)
result = Resolution(
**{**asdict(provisional), "resolution_digest": resolution_digest(provisional)}
)
validate_resolution(result)
return result
def is_claimable_intent(task: Mapping[str, Any], worker_capabilities: Sequence[str]) -> bool:
try:
intent = IntentTask.parse(task)
except IntentError:
return False
required = set(intent.required_capabilities)
return required.issubset(set(worker_capabilities))
@@ -0,0 +1,197 @@
#!/usr/bin/env python3
from __future__ import annotations
import json
import os
from pathlib import Path
import subprocess
import tempfile
import urllib.error
import urllib.request
from datetime import datetime, timezone
from typing import Any
BASE = os.environ.get("AIOS_AGENT_TASK_API_URL", "http://127.0.0.1:18940").rstrip("/")
AGENT_ID = os.environ.get("AIOS_AGENT_ID", "online-engineering-worker-cn-network-mkt-autonomous-20260815")
AGENT_TYPE = os.environ.get("AIOS_AGENT_TYPE", "trusted-online-engineering-worker")
AGENT_VERSION = os.environ.get("AIOS_AGENT_VERSION", "2026-08-15.4-autonomous-intent")
STATE_DIR = Path(os.environ.get("AIOS_WORKER_REGISTRATION_STATE", "/var/lib/aios-autonomous-agent-worker/registration"))
CREDENTIAL_PATH = Path(os.environ.get("AIOS_WORKER_ENCRYPTED_CREDENTIAL", "/etc/credstore.encrypted/aios-autonomous-agent-worker/agent_token"))
IDENTITY_PATH = STATE_DIR / "identity.json"
PROJECTION_PROVIDER = "BridgeV2RuntimeCredentialProjection"
def now() -> str:
return datetime.now(timezone.utc).isoformat()
def runtime_capabilities() -> list[str]:
values = [
"online_agent_task_api", "task.poll", "task.claim", "task.heartbeat", "task.result",
"intent.task", "engineering.consumer", "engineering.execute", "engineering.autonomous",
"workspace.worktree", "git.read", "git.diff", "git.test",
"governed.write.branch", "governed.write.file",
]
hermes = os.environ.get("AIOS_HERMES_BROKER_SOCKET", "")
codex = os.environ.get("AIOS_CODEX_BROKER_SOCKET", "")
openclaw = os.environ.get("AIOS_OPENCLAW_BROKER_SOCKET", "")
if hermes and Path(hermes).is_socket():
values.append("agent.runtime.hermes")
if codex and Path(codex).is_socket():
values.append("agent.runtime.codex")
if openclaw and Path(openclaw).is_socket():
values.append("agent.runtime.openclaw")
return values
def api_call(method: str, route: str, token: str, body: dict[str, Any] | None = None) -> tuple[int, dict[str, Any]]:
payload = None if body is None else json.dumps(body, sort_keys=True, separators=(",", ":")).encode("utf-8")
headers = {"Authorization": "Bearer " + token, "Accept": "application/json"}
if payload is not None:
headers["Content-Type"] = "application/json"
request = urllib.request.Request(BASE + route, data=payload, headers=headers, method=method)
try:
with urllib.request.urlopen(request, timeout=15) as response:
value = json.loads(response.read().decode("utf-8"))
return response.status, value if isinstance(value, dict) else {}
except urllib.error.HTTPError as exc:
try:
value = json.loads(exc.read(2048).decode("utf-8"))
except Exception:
value = {"error": "HTTP_ERROR"}
return exc.code, value if isinstance(value, dict) else {"error": "HTTP_ERROR"}
except (urllib.error.URLError, TimeoutError):
return 599, {"error": "AGENT_TASK_API_UNAVAILABLE"}
def encrypted_credential_is_valid() -> bool:
if not CREDENTIAL_PATH.is_file() or CREDENTIAL_PATH.is_symlink():
return False
result = subprocess.run(
["systemd-creds", "decrypt", "--with-key=host", str(CREDENTIAL_PATH), "/dev/null"],
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, check=False, timeout=15,
)
return result.returncode == 0
def load_identity() -> dict[str, Any] | None:
if not IDENTITY_PATH.is_file() or IDENTITY_PATH.is_symlink():
return None
try:
value = json.loads(IDENTITY_PATH.read_text(encoding="utf-8"))
except Exception:
return None
return value if isinstance(value, dict) else None
def identity_matches(value: dict[str, Any] | None) -> bool:
return bool(
value
and value.get("agent_id") == AGENT_ID
and value.get("agent_type") == AGENT_TYPE
and value.get("agent_version") == AGENT_VERSION
and value.get("task_scope") == "intent-only"
and value.get("credential_projection_provider") == PROJECTION_PROVIDER
)
def encrypt_agent_token(token: str) -> None:
CREDENTIAL_PATH.parent.mkdir(parents=True, mode=0o700, exist_ok=True)
fd, temporary = tempfile.mkstemp(prefix=".agent-token.", dir=str(CREDENTIAL_PATH.parent))
os.close(fd)
os.unlink(temporary)
try:
result = subprocess.run(
["systemd-creds", "encrypt", "--with-key=host", "--name=agent_token", "/dev/stdin", temporary],
input=(token + "\n").encode("utf-8"),
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, check=False, timeout=20,
)
if result.returncode != 0:
raise RuntimeError("AGENT_CREDENTIAL_ENCRYPT_FAILED")
os.chmod(temporary, 0o600)
os.replace(temporary, CREDENTIAL_PATH)
finally:
if os.path.exists(temporary):
os.unlink(temporary)
def write_identity() -> None:
value = {
"agent_id": AGENT_ID,
"agent_type": AGENT_TYPE,
"agent_version": AGENT_VERSION,
"task_scope": "intent-only",
"credential_projection_provider": PROJECTION_PROVIDER,
"credential_delivery": "systemd-LoadCredentialEncrypted",
"task_api_url": BASE,
"task_api_schema": "aios.agent-task.v1",
"execution_backend": "autonomous-intent-worker-v2",
"runtime_only_secret_delivery": True,
"main_write": False,
"production_write": False,
"force_push": False,
"tag_write": False,
"baseline_write": False,
"capabilities": runtime_capabilities(),
"created_at": now(),
}
STATE_DIR.mkdir(parents=True, mode=0o750, exist_ok=True)
fd, temporary = tempfile.mkstemp(prefix="identity.", dir=str(STATE_DIR))
try:
with os.fdopen(fd, "w", encoding="utf-8") as handle:
json.dump(value, handle, ensure_ascii=False, sort_keys=True)
handle.write("\n")
os.chmod(temporary, 0o640)
os.replace(temporary, IDENTITY_PATH)
finally:
if os.path.exists(temporary):
os.unlink(temporary)
def register_or_recover() -> str:
STATE_DIR.mkdir(parents=True, mode=0o750, exist_ok=True)
existing = load_identity()
if CREDENTIAL_PATH.exists():
if not identity_matches(existing):
raise RuntimeError("AGENT_IDENTITY_BINDING_CONFLICT")
if not encrypted_credential_is_valid():
raise RuntimeError("AGENT_ENCRYPTED_CREDENTIAL_INVALID")
return "RECOVERED"
if existing is not None:
raise RuntimeError("AGENT_CREDENTIAL_MISSING_AFTER_PRIOR_REGISTRATION")
credential_directory = Path(os.environ.get("CREDENTIALS_DIRECTORY", ""))
control_path = credential_directory / "bridge_control_token"
if not control_path.is_file():
raise RuntimeError("BRIDGE_RUNTIME_CONTROL_CREDENTIAL_UNAVAILABLE")
control_token = control_path.read_text(encoding="utf-8").strip()
status, response = api_call(
"POST", "/v1/agents/register", control_token,
{
"agent_id": AGENT_ID,
"agent_type": AGENT_TYPE,
"agent_version": AGENT_VERSION,
"capabilities": runtime_capabilities(),
"metadata": {
"task_scope": "intent-only",
"credential_projection_provider": PROJECTION_PROVIDER,
"runtime_only_secret_delivery": True,
"execution_backend": "autonomous-intent-worker-v2",
"main_write": False,
"production_write": False,
"force_push": False,
},
},
)
if status != 201:
raise RuntimeError("AGENT_REGISTER_FAILED_" + str(response.get("error") or status))
token = response.get("agent_token")
if not isinstance(token, str) or len(token) < 24:
raise RuntimeError("AGENT_TOKEN_NOT_RETURNED")
encrypt_agent_token(token)
write_identity()
return "REGISTERED"
if __name__ == "__main__":
raise SystemExit(0 if register_or_recover() in {"REGISTERED", "RECOVERED"} else 20)
@@ -0,0 +1,224 @@
from __future__ import annotations
from dataclasses import dataclass
import json
from pathlib import Path
import re
import socket
import subprocess
from typing import Any, Mapping, Sequence
OID_RE = re.compile(r"^[0-9a-f]{40}$")
MAX_BYTES = 1024 * 1024
MAX_CHANGED_FILES = 256
MAX_FILE_BYTES = 4 * 1024 * 1024
SENSITIVE_NAMES = frozenset({
".env", ".env.local", ".env.production", "id_rsa", "id_ed25519",
"credentials", "credentials.json",
})
SENSITIVE_SUFFIXES = (".key", ".p12", ".pfx")
SECRET_PATTERNS = (
re.compile(rb"-----BEGIN (?:RSA |EC |OPENSSH )?PRIVATE KEY-----"),
re.compile(rb"\bAKIA[0-9A-Z]{16}\b"),
re.compile(rb"\bghp_[A-Za-z0-9]{30,}\b"),
re.compile(rb"\bsk-[A-Za-z0-9_-]{24,}\b"),
)
class RuntimeErrorV2(RuntimeError):
def __init__(self, code: str, *, details: Mapping[str, Any] | None = None) -> None:
super().__init__(code)
self.code = code
self.details = dict(details or {})
def safe_relative_path(value: str) -> str:
if (
not value
or value.startswith(("/", "~"))
or "\\" in value
or "\x00" in value
or any(part in {"", ".", ".."} for part in value.split("/"))
):
raise RuntimeErrorV2("CHANGED_PATH_INVALID", details={"path": value[:160]})
lowered = value.split("/")[-1].lower()
if value.startswith(".git/") or value in {".git", ".gitmodules"}:
raise RuntimeErrorV2("GIT_METADATA_CHANGE_REJECTED", details={"path": value})
if lowered in SENSITIVE_NAMES or lowered.endswith(SENSITIVE_SUFFIXES):
raise RuntimeErrorV2("SENSITIVE_PATH_REJECTED", details={"path": value})
return value
def git(worktree: Path, args: Sequence[str], timeout: int = 60) -> bytes:
completed = subprocess.run(
["/usr/bin/git", "-C", str(worktree), *args],
capture_output=True,
check=False,
timeout=timeout,
env={"PATH": "/usr/bin:/bin", "LC_ALL": "C.UTF-8"},
)
if completed.returncode != 0:
raise RuntimeErrorV2("WORKTREE_GIT_FAILED", details={"operation": args[0] if args else ""})
return completed.stdout
def _zpaths(raw: bytes) -> list[str]:
result: list[str] = []
for item in raw.split(b"\0"):
if not item:
continue
try:
result.append(item.decode("utf-8"))
except UnicodeDecodeError as exc:
raise RuntimeErrorV2("WORKTREE_PATH_ENCODING_INVALID") from exc
return result
def collect_changes(worktree: Path, base_oid: str) -> list[str]:
if not OID_RE.fullmatch(base_oid):
raise RuntimeErrorV2("BASE_OID_INVALID")
head = git(worktree, ["rev-parse", "HEAD"]).decode("ascii", "strict").strip()
if head != base_oid:
raise RuntimeErrorV2("WORKTREE_HEAD_MUTATED", details={"expected": base_oid, "actual": head})
tracked = _zpaths(git(worktree, ["diff", "--name-only", "--no-renames", "-z", base_oid, "--"]))
untracked = _zpaths(git(worktree, ["ls-files", "--others", "--exclude-standard", "-z"]))
changed = sorted({safe_relative_path(item) for item in tracked + untracked})
if not changed:
raise RuntimeErrorV2("AGENT_PRODUCED_NO_CHANGES")
if len(changed) > MAX_CHANGED_FILES:
raise RuntimeErrorV2("CHANGED_FILE_LIMIT_EXCEEDED")
root = worktree.resolve()
for relative in changed:
path = (root / relative).resolve(strict=False)
if root not in path.parents or not path.is_file() or path.is_symlink():
raise RuntimeErrorV2("CHANGED_FILE_UNSUPPORTED", details={"path": relative})
if path.stat().st_size > MAX_FILE_BYTES:
raise RuntimeErrorV2("CHANGED_FILE_TOO_LARGE", details={"path": relative})
return changed
def scan_secrets(worktree: Path, changed: Sequence[str]) -> None:
root = worktree.resolve()
for relative in changed:
path = (root / safe_relative_path(relative)).resolve()
raw = path.read_bytes()
for pattern in SECRET_PATTERNS:
if pattern.search(raw):
raise RuntimeErrorV2("SECRET_MATERIAL_REJECTED", details={"path": relative})
@dataclass(frozen=True)
class RuntimeResult:
status: str
summary: str
verification: tuple[dict[str, Any], ...]
evidence: dict[str, Any]
@classmethod
def parse(cls, value: Mapping[str, Any]) -> "RuntimeResult":
if value.get("schema") != "aios.online-agent.runtime-result.v1":
raise RuntimeErrorV2("RUNTIME_RESULT_SCHEMA_INVALID")
status = str(value.get("status") or "")
if status not in {"COMPLETED", "BLOCKED", "FAILED"}:
raise RuntimeErrorV2("RUNTIME_RESULT_STATUS_INVALID")
summary = str(value.get("summary") or "").strip()
if not summary or len(summary) > 8192:
raise RuntimeErrorV2("RUNTIME_RESULT_SUMMARY_INVALID")
verification = value.get("verification") or []
evidence = value.get("evidence") or {}
if not isinstance(verification, list) or len(verification) > 64:
raise RuntimeErrorV2("RUNTIME_VERIFICATION_INVALID")
if not isinstance(evidence, Mapping):
raise RuntimeErrorV2("RUNTIME_EVIDENCE_INVALID")
return cls(status, summary, tuple(dict(item) for item in verification if isinstance(item, Mapping)), dict(evidence))
def request_payload(
*,
adapter: str,
task_id: str,
goal: str,
instructions: str,
success_criteria: Sequence[str],
repository: str,
base_oid: str,
target_branch: str,
worktree: Path,
) -> dict[str, Any]:
if adapter not in {"hermes", "codex", "openclaw"}:
raise RuntimeErrorV2("ADAPTER_INVALID")
if not OID_RE.fullmatch(base_oid):
raise RuntimeErrorV2("BASE_OID_INVALID")
return {
"schema": "aios.online-agent.runtime-request.v1",
"adapter": adapter,
"task": {
"task_id": task_id,
"goal": goal,
"instructions": instructions,
"success_criteria": list(success_criteria),
},
"execution_context": {
"repository": repository,
"base_oid": base_oid,
"target_branch": target_branch,
"worktree": str(worktree),
},
"policy": {
"authoritative_repository_write": False,
"direct_git_ref_mutation": False,
"direct_git_push": False,
"direct_git_commit": False,
"main_write": False,
"force_push": False,
"production_write": False,
"tag_write": False,
"baseline_write": False,
"task_api_credential_visible": False,
"writer_lease_visible": False,
},
}
def invoke(socket_path: Path, payload: Mapping[str, Any], timeout_seconds: int) -> RuntimeResult:
if not socket_path.is_absolute():
raise RuntimeErrorV2("RUNTIME_SOCKET_NOT_ABSOLUTE")
if not 1 <= timeout_seconds <= 7200:
raise RuntimeErrorV2("RUNTIME_TIMEOUT_INVALID")
raw = json.dumps(dict(payload), ensure_ascii=False, sort_keys=True, separators=(",", ":")).encode("utf-8") + b"\n"
if len(raw) > MAX_BYTES:
raise RuntimeErrorV2("RUNTIME_REQUEST_TOO_LARGE")
client = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
client.settimeout(timeout_seconds)
try:
client.connect(str(socket_path))
client.sendall(raw)
client.shutdown(socket.SHUT_WR)
chunks: list[bytes] = []
total = 0
while True:
chunk = client.recv(65536)
if not chunk:
break
total += len(chunk)
if total > MAX_BYTES:
raise RuntimeErrorV2("RUNTIME_RESPONSE_TOO_LARGE")
chunks.append(chunk)
except (OSError, socket.timeout) as exc:
raise RuntimeErrorV2("RUNTIME_UNAVAILABLE", details={"socket": str(socket_path)}) from exc
finally:
client.close()
try:
value = json.loads(b"".join(chunks).decode("utf-8"))
except (UnicodeDecodeError, json.JSONDecodeError) as exc:
raise RuntimeErrorV2("RUNTIME_RESPONSE_INVALID") from exc
if not isinstance(value, Mapping):
raise RuntimeErrorV2("RUNTIME_RESPONSE_INVALID")
result = RuntimeResult.parse(value)
if result.status == "BLOCKED":
raise RuntimeErrorV2("RUNTIME_BLOCKED", details={"summary": result.summary})
if result.status == "FAILED":
raise RuntimeErrorV2("RUNTIME_FAILED", details={"summary": result.summary})
return result
@@ -0,0 +1,101 @@
#!/usr/bin/env python3
from __future__ import annotations
import json
import os
from pathlib import Path
import shutil
import subprocess
import tempfile
from runtime_client import collect_changes, invoke, request_payload, scan_secrets
WORKSPACE_ROOT = Path(os.environ.get(
"AIOS_AUTONOMOUS_WORKSPACE_ROOT",
"/var/lib/aios-autonomous-agent-worker/workspaces",
))
ADAPTER = os.environ.get("AIOS_AUTONOMOUS_SELFTEST_ADAPTER", "openclaw")
SOCKET = Path(os.environ.get(
"AIOS_AUTONOMOUS_SELFTEST_BROKER_SOCKET",
f"/run/aios-agent-runtime/{ADAPTER}.sock",
))
def run(argv: list[str], cwd: Path) -> str:
completed = subprocess.run(
argv,
cwd=str(cwd),
text=True,
capture_output=True,
check=False,
timeout=60,
env={"PATH": "/usr/bin:/bin", "LC_ALL": "C.UTF-8"},
)
if completed.returncode != 0:
raise RuntimeError(f"SELFTEST_COMMAND_FAILED:{argv[0]}:{completed.returncode}")
return completed.stdout.strip()
def main() -> int:
if not SOCKET.is_socket():
raise RuntimeError("AUTONOMOUS_ENGINEERING_BACKEND_UNAVAILABLE")
WORKSPACE_ROOT.mkdir(parents=True, mode=0o750, exist_ok=True)
run_root = Path(tempfile.mkdtemp(prefix="runtime-selftest-", dir=str(WORKSPACE_ROOT)))
repo = run_root / "worktree"
repo.mkdir()
try:
run(["/usr/bin/git", "init", str(repo)], run_root)
run(["/usr/bin/git", "-C", str(repo), "config", "user.email", "selftest@example.invalid"], run_root)
run(["/usr/bin/git", "-C", str(repo), "config", "user.name", "AIOS Runtime Selftest"], run_root)
marker = repo / "agent_runtime_selftest.txt"
marker.write_text("VALUE=1\n", encoding="utf-8")
run(["/usr/bin/git", "-C", str(repo), "add", "agent_runtime_selftest.txt"], run_root)
run(["/usr/bin/git", "-C", str(repo), "commit", "-m", "selftest base"], run_root)
base_oid = run(["/usr/bin/git", "-C", str(repo), "rev-parse", "HEAD"], run_root)
payload = request_payload(
adapter=ADAPTER,
task_id="autonomous-runtime-selftest",
goal="Change agent_runtime_selftest.txt so it contains exactly VALUE=2 followed by a newline.",
instructions=(
"Modify only agent_runtime_selftest.txt in the provided disposable worktree. "
"Do not commit, push, change Git refs, or access task/governed-write credentials."
),
success_criteria=[
"agent_runtime_selftest.txt contains VALUE=2",
"Git HEAD remains unchanged",
],
repository="selftest/disposable",
base_oid=base_oid,
target_branch="agent/intent/runtime-selftest",
worktree=repo,
)
result = invoke(SOCKET, payload, timeout_seconds=300)
changed = collect_changes(repo, base_oid)
scan_secrets(repo, changed)
content = marker.read_text(encoding="utf-8")
head = run(["/usr/bin/git", "-C", str(repo), "rev-parse", "HEAD"], run_root)
if content != "VALUE=2\n":
raise RuntimeError("RUNTIME_SELFTEST_CONTENT_MISMATCH")
if head != base_oid:
raise RuntimeError("RUNTIME_SELFTEST_HEAD_MUTATED")
if changed != ["agent_runtime_selftest.txt"]:
raise RuntimeError("RUNTIME_SELFTEST_UNEXPECTED_DIFF")
print(json.dumps({
"status": "PASS",
"adapter": ADAPTER,
"base_oid": base_oid,
"changed_files": changed,
"head_unchanged": True,
"runtime_summary": result.summary,
"task_api_token_visible_to_runtime": False,
"writer_lease_visible_to_runtime": False,
}, sort_keys=True))
return 0
finally:
shutil.rmtree(run_root, ignore_errors=True)
if __name__ == "__main__":
raise SystemExit(main())
@@ -0,0 +1,54 @@
# Generic Governed Online Agent Worker
This worker is a pull-based, restartable consumer for `aios.agent-task.v1`.
The service configuration contains only stable runtime identity, capability
names, API endpoints, heartbeat timing, and the credential projection. It does
not contain a task id, repository base OID, target branch, or plan-file path.
## Dynamic task contract
Engineering tasks carry `metadata.execution` with schema
`aios.online-agent.execution.v2`. The payload supplies the repository, exact
base OID, target branch, detached-worktree policy, adapter name, explicit file
change set, verification commands, and governed commit idempotency keys.
Task prose is descriptive only and is never passed to a shell.
The worker accepts only structured operations:
- `deterministic`: allow-listed file changes and test commands;
- `codex`: fail-closed until a task supplies an explicit Codex file/command
policy;
- `hermes`: fail-closed because the current Hermes runtime is a user-chat
service, not an engineering executor.
All command execution uses `shell=False`, fixed program allowlists, relative
paths beneath an ephemeral detached worktree, and output redaction. Direct
`git branch`, `update-ref`, `push`, `commit`, `reset`, `clean`, main,
production, tag, and baseline mutations are rejected by the worker.
## Governed write path
The worker calls the Bridge in this order:
1. `POST /v2/governed-task`;
2. `POST /v2/writer-lease`;
3. `POST /v2/commit-files`;
4. read-only authority-ref visibility verification.
CREATE_NEW is represented by the task payload and is normalized by the Bridge
lease authority. The worker never creates an authoritative ref itself.
## Lifecycle and recovery
The runtime registers with `register_or_recover`, receives its agent token
once, and stores only the encrypted systemd credential. The consumer loops over
poll, atomically claims one task, sends the first heartbeat, emits
`TASK_STARTED`, and continues heartbeats while executing.
State is atomically stored per task without agent or lease token material. If
the process stops after a governed commit but before result submission, the
next lease attempt reuses the stable governed idempotency key and submits the
stored commit evidence without issuing a second commit.
The control plane must independently verify submitted results; worker-local
success is not production or mainline authorization.
@@ -0,0 +1,3 @@
"""Generic governed Online Agent Worker runtime."""
__all__ = ["worker"]
@@ -0,0 +1,937 @@
#!/usr/bin/env python3
"""Generic, restartable Online Agent Worker.
The worker is deliberately task-agnostic. A task's signed/durable payload is
the only source of repository, branch, change, verification, and governance
parameters. There is no task id, base OID, branch name, or plan file in the
runtime configuration.
The worker performs local work only in an ephemeral detached worktree. All
authoritative ref updates go through the governed Bridge API; direct branch,
update-ref, push, main, production, tag, and baseline mutations are rejected.
"""
from __future__ import annotations
import base64
from dataclasses import dataclass
from datetime import datetime, timezone
from abc import ABC, abstractmethod
import hashlib
import json
import os
from pathlib import Path
import re
import shutil
import subprocess
import tempfile
import threading
import time
import urllib.error
import urllib.request
from typing import Any, Mapping
from urllib.parse import unquote
API_BASE = os.environ.get("AIOS_AGENT_TASK_API_URL", "http://127.0.0.1:18940").rstrip("/")
BRIDGE_BASE = os.environ.get("AIOS_GOVERNED_WRITE_API_URL", "http://127.0.0.1:18892").rstrip("/")
AGENT_ID = os.environ.get(
"AIOS_AGENT_ID", "online-engineering-worker-cn-network-mkt-generic-20260815"
)
AGENT_TYPE = os.environ.get("AIOS_AGENT_TYPE", "trusted-online-engineering-worker")
AGENT_VERSION = os.environ.get("AIOS_AGENT_VERSION", "2026-08-15.3-generic")
AUTHORITY_GIT_DIR = Path(
os.environ.get("AIOS_AUTHORITY_GIT_DIR", "/data/aios-gitops/repo.git")
)
STATE_ROOT = Path(
os.environ.get("AIOS_WORKER_STATE_ROOT", "/var/lib/aios-online-agent-worker-20260815")
)
WORKSPACE_ROOT = Path(
os.environ.get(
"AIOS_WORKER_WORKSPACE_ROOT", "/var/lib/aios-online-agent-worker-20260815/workspaces"
)
)
GIT_CONFIG_GLOBAL = STATE_ROOT / "gitconfig"
POLL_SECONDS = int(os.environ.get("AIOS_WORKER_POLL_SECONDS", "5"))
HEARTBEAT_SECONDS = int(os.environ.get("AIOS_WORKER_HEARTBEAT_SECONDS", "20"))
LEASE_SECONDS = int(os.environ.get("AIOS_WORKER_LEASE_SECONDS", "300"))
MAX_TASK_BYTES = 512 * 1024
MAX_FILE_BYTES = 2 * 1024 * 1024
OID_RE = re.compile(r"^[0-9a-f]{40}$")
SHA256_RE = re.compile(r"^[0-9a-f]{64}$")
ID_RE = re.compile(r"^[A-Za-z0-9][A-Za-z0-9._:-]{2,179}$")
BRANCH_RE = re.compile(r"^[A-Za-z0-9][A-Za-z0-9._/-]*$")
PATH_RE = re.compile(r"^[^\\\x00-\x1f\x7f]+$")
SENSITIVE_RE = re.compile(
r"(?i)(authorization\s*:\s*bearer\s+\S+|(?:password|passwd|secret|token|api[_-]?key)\s*[:=]\s*\S+)"
)
class WorkerError(RuntimeError):
def __init__(self, code: str, *, phase: str, details: Mapping[str, Any] | None = None) -> None:
super().__init__(code)
self.code = code
self.phase = phase
self.details = dict(details or {})
def now() -> str:
return datetime.now(timezone.utc).isoformat()
def digest_bytes(value: bytes) -> str:
return hashlib.sha256(value).hexdigest()
def digest_json(value: Any) -> str:
return digest_bytes(
json.dumps(value, ensure_ascii=False, sort_keys=True, separators=(",", ":")).encode("utf-8")
)
def sanitize(value: Any) -> Any:
if isinstance(value, Mapping):
cleaned: dict[str, Any] = {}
for key, inner in value.items():
lowered = str(key).lower()
if any(marker in lowered for marker in ("token", "secret", "credential", "password", "authorization")):
cleaned[str(key)] = "[REDACTED]"
else:
cleaned[str(key)] = sanitize(inner)
return cleaned
if isinstance(value, list):
return [sanitize(item) for item in value]
if isinstance(value, tuple):
return [sanitize(item) for item in value]
if isinstance(value, str):
return SENSITIVE_RE.sub("[REDACTED]", value)
return value
def excerpt(value: str, limit: int = 800) -> str:
value = str(sanitize(value))
return value if len(value) <= limit else value[: limit // 2] + "...[TRUNCATED]..." + value[-limit // 2 :]
def audit(event_type: str, **fields: Any) -> None:
STATE_ROOT.mkdir(mode=0o750, parents=True, exist_ok=True)
path = STATE_ROOT / "events.jsonl"
record = {
"event_type": event_type,
"created_at": now(),
"agent_id": AGENT_ID,
"credential_material_exposed": False,
"lease_material_exposed": False,
**sanitize(fields),
}
with path.open("a", encoding="utf-8") as handle:
handle.write(json.dumps(record, ensure_ascii=False, sort_keys=True) + "\n")
os.chmod(path, 0o640)
print(json.dumps(record, ensure_ascii=False, sort_keys=True), flush=True)
def atomic_json(path: Path, value: Mapping[str, Any], mode: int = 0o640) -> None:
path.parent.mkdir(mode=0o750, parents=True, exist_ok=True)
if path.parent.is_symlink() or path.is_symlink():
raise WorkerError("STATE_PATH_SYMLINK_REJECTED", phase="state")
fd, temporary = tempfile.mkstemp(prefix=f".{path.name}.", dir=str(path.parent))
try:
os.fchmod(fd, mode)
with os.fdopen(fd, "w", encoding="utf-8") as handle:
json.dump(sanitize(dict(value)), handle, ensure_ascii=False, sort_keys=True)
handle.write("\n")
handle.flush()
os.fsync(handle.fileno())
os.replace(temporary, path)
os.chmod(path, mode)
finally:
if os.path.exists(temporary):
os.unlink(temporary)
def safe_relative_path(value: Any, *, field: str = "path") -> str:
if not isinstance(value, str) or not value or len(value) > 240:
raise WorkerError(f"{field.upper()}_INVALID", phase="validation")
if unquote(value) != value or not PATH_RE.fullmatch(value):
raise WorkerError(f"{field.upper()}_INVALID", phase="validation")
if value.startswith(("/", "~")) or re.match(r"^[A-Za-z]:", value):
raise WorkerError(f"{field.upper()}_ABSOLUTE", phase="validation")
parts = value.replace("\\", "/").split("/")
if any(part in {"", ".", ".."} for part in parts):
raise WorkerError(f"{field.upper()}_TRAVERSAL", phase="validation")
return "/".join(parts)
def resolve_under(root: Path, relative: str, *, write: bool = False) -> Path:
relative = safe_relative_path(relative)
root = root.resolve()
candidate = (root / relative).resolve(strict=False)
if candidate != root and root not in candidate.parents:
raise WorkerError("WORKSPACE_ESCAPE", phase="validation")
current = root
for part in Path(relative).parts:
current /= part
if current.exists() and current.is_symlink():
raise WorkerError("WORKSPACE_SYMLINK_REJECTED", phase="validation")
if write and candidate.exists() and candidate.is_symlink():
raise WorkerError("WRITE_SYMLINK_REJECTED", phase="validation")
return candidate
def validate_oid(value: Any, field: str = "oid") -> str:
if not isinstance(value, str) or not OID_RE.fullmatch(value):
raise WorkerError(f"{field.upper()}_INVALID", phase="validation")
return value
def validate_sha(value: Any, field: str = "sha256") -> str:
if not isinstance(value, str) or not SHA256_RE.fullmatch(value):
raise WorkerError(f"{field.upper()}_INVALID", phase="validation")
return value
def validate_branch(value: Any) -> str:
if not isinstance(value, str) or not 3 <= len(value) <= 180:
raise WorkerError("TARGET_BRANCH_INVALID", phase="validation")
if value in {"main", "master", "HEAD"} or value.startswith(
("main/", "master/", "refs/", "tags/", "tag/", "release/", "releases/", "baseline/", "production/")
):
raise WorkerError("PROTECTED_BRANCH_REJECTED", phase="validation")
if (
value.startswith("/")
or value.endswith(("/", ".", ".lock"))
or ".." in value
or "//" in value
or "@{" in value
or any(ch in value for ch in " ~^:?*[\\")
or any(part in {"", ".", ".."} for part in value.split("/"))
or not BRANCH_RE.fullmatch(value)
):
raise WorkerError("TARGET_BRANCH_INVALID", phase="validation")
return value
def validate_program(argv: Any, *, internal_git: bool = False) -> list[str]:
if not isinstance(argv, list) or not argv or not all(isinstance(item, str) and item for item in argv):
raise WorkerError("STRUCTURED_ARGV_REQUIRED", phase="validation")
command = argv[0]
if command not in {"/usr/bin/git", "/usr/bin/python3"}:
raise WorkerError("PROGRAM_NOT_ALLOWLISTED", phase="validation", details={"program": Path(command).name})
if command.endswith("python3"):
if any(item in {"-c", "-", "--command"} for item in argv):
raise WorkerError("PYTHON_INLINE_CODE_REJECTED", phase="validation")
if "-m" in argv:
index = argv.index("-m")
if index + 1 >= len(argv) or argv[index + 1] not in {"pytest", "py_compile"}:
raise WorkerError("PYTHON_MODULE_NOT_ALLOWLISTED", phase="validation")
if command.endswith("git") and not internal_git:
rejected = {"push", "update-ref", "receive-pack", "send-pack", "reset", "clean", "branch", "commit", "merge", "rebase"}
if any(item in rejected for item in argv[1:]):
raise WorkerError("GIT_MUTATION_NOT_ALLOWLISTED", phase="validation")
return list(argv)
def run_command(
label: str,
argv: list[str],
*,
cwd: Path,
timeout: int,
internal_git: bool = False,
allowed_returncodes: tuple[int, ...] = (0,),
) -> dict[str, Any]:
validate_program(argv, internal_git=internal_git)
if not 1 <= timeout <= 3600:
raise WorkerError("COMMAND_TIMEOUT_INVALID", phase=label)
started = time.monotonic()
try:
completed = subprocess.run(
argv,
cwd=str(cwd),
text=True,
capture_output=True,
check=False,
shell=False,
timeout=timeout,
env={"PATH": "/usr/bin:/bin", "LC_ALL": "C.UTF-8", "GIT_CONFIG_GLOBAL": str(GIT_CONFIG_GLOBAL)},
)
except subprocess.TimeoutExpired as exc:
raise WorkerError("COMMAND_TIMEOUT", phase=label, details={"timeout_seconds": timeout}) from exc
result = {
"label": label,
"argv": [Path(item).name if index == 0 else item for index, item in enumerate(argv)],
"exit_code": completed.returncode,
"stdout_sha256": digest_bytes(completed.stdout.encode("utf-8", "replace")),
"stderr_sha256": digest_bytes(completed.stderr.encode("utf-8", "replace")),
"stdout_excerpt": excerpt(completed.stdout),
"stderr_excerpt": excerpt(completed.stderr),
"duration_ms": int((time.monotonic() - started) * 1000),
}
audit("COMMAND_RESULT", **result)
if completed.returncode not in allowed_returncodes:
raise WorkerError("COMMAND_FAILED", phase=label, details={"exit_code": completed.returncode, "stderr": excerpt(completed.stderr, 400)})
return result
@dataclass(frozen=True)
class ExecutionEnvelope:
task_id: str
repository: str
base_oid: str
target_branch: str
adapter: str
change_set: tuple[dict[str, Any], ...]
verification: tuple[dict[str, Any], ...]
governance: dict[str, Any]
workspace: dict[str, Any]
digest: str
@classmethod
def from_task(cls, task: Mapping[str, Any]) -> "ExecutionEnvelope":
task_id = str(task.get("task_id") or "")
if not ID_RE.fullmatch(task_id):
raise WorkerError("TASK_ID_INVALID", phase="validation")
metadata = task.get("metadata")
if not isinstance(metadata, Mapping):
raise WorkerError("TASK_METADATA_REQUIRED", phase="validation")
execution = metadata.get("execution")
if not isinstance(execution, Mapping):
raise WorkerError("DYNAMIC_EXECUTION_PAYLOAD_REQUIRED", phase="validation")
if execution.get("schema") != "aios.online-agent.execution.v2":
raise WorkerError("EXECUTION_SCHEMA_INVALID", phase="validation")
repository = execution.get("repository")
if not isinstance(repository, str) or not repository:
raise WorkerError("REPOSITORY_INVALID", phase="validation")
base_oid = validate_oid(execution.get("base_oid"), "base_oid")
target_branch = validate_branch(execution.get("target_branch"))
workspace = execution.get("workspace")
if not isinstance(workspace, Mapping):
raise WorkerError("WORKSPACE_POLICY_REQUIRED", phase="validation")
workspace_value = dict(workspace)
if workspace_value.get("authority_git_dir") != str(AUTHORITY_GIT_DIR):
raise WorkerError("AUTHORITY_ROOT_INVALID", phase="validation")
if workspace_value.get("read_only_repository") is not True:
raise WorkerError("REPOSITORY_WRITE_POLICY_INVALID", phase="validation")
if workspace_value.get("cleanup_policy") not in {"always", "cleanup_on_success"}:
raise WorkerError("WORKSPACE_CLEANUP_POLICY_INVALID", phase="validation")
adapter_value = execution.get("adapter")
if isinstance(adapter_value, Mapping):
adapter = str(adapter_value.get("name") or "")
else:
adapter = str(adapter_value or "")
if adapter not in {"deterministic", "codex", "hermes"}:
raise WorkerError("RUNTIME_ADAPTER_UNSUPPORTED", phase="validation")
change_set = execution.get("change_set")
if not isinstance(change_set, list) or not change_set or len(change_set) > 128:
raise WorkerError("CHANGE_SET_INVALID", phase="validation")
normalized_changes: list[dict[str, Any]] = []
for item in change_set:
if not isinstance(item, Mapping):
raise WorkerError("CHANGE_OPERATION_INVALID", phase="validation")
operation = str(item.get("operation") or "")
relative = safe_relative_path(item.get("path"))
normalized = dict(item)
normalized["operation"] = operation
normalized["path"] = relative
if operation == "write_file":
content = item.get("content")
if not isinstance(content, str) or len(content.encode("utf-8")) > MAX_FILE_BYTES:
raise WorkerError("WRITE_CONTENT_INVALID", phase="validation")
elif operation == "replace_text":
if not isinstance(item.get("old"), str) or not item.get("old") or not isinstance(item.get("new"), str):
raise WorkerError("REPLACE_TEXT_INVALID", phase="validation")
if len(str(item["old"]).encode("utf-8")) > MAX_FILE_BYTES:
raise WorkerError("REPLACE_TEXT_INVALID", phase="validation")
elif operation == "delete_file":
pass
else:
raise WorkerError("CHANGE_OPERATION_UNSUPPORTED", phase="validation", details={"operation": operation})
expected = item.get("expected_sha256")
if expected is not None:
validate_sha(expected, "expected_sha256")
normalized_changes.append(normalized)
verification = execution.get("verification") or []
if not isinstance(verification, list) or len(verification) > 128:
raise WorkerError("VERIFICATION_INVALID", phase="validation")
normalized_verification: list[dict[str, Any]] = []
for item in verification:
if not isinstance(item, Mapping):
raise WorkerError("VERIFICATION_COMMAND_INVALID", phase="validation")
label = str(item.get("label") or "verification")
argv = validate_program(item.get("argv"))
cwd_value = item.get("cwd")
cwd = "." if cwd_value in {None, "", "."} else safe_relative_path(cwd_value, field="cwd")
timeout = item.get("timeout_seconds", 120)
if not isinstance(timeout, int) or isinstance(timeout, bool) or not 1 <= timeout <= 3600:
raise WorkerError("VERIFICATION_TIMEOUT_INVALID", phase="validation")
normalized_verification.append({"label": label, "argv": argv, "cwd": cwd, "timeout_seconds": timeout})
governance = execution.get("governance")
if not isinstance(governance, Mapping):
raise WorkerError("GOVERNANCE_POLICY_REQUIRED", phase="validation")
governance_value = dict(governance)
governed_task_id = str(governance_value.get("task_id") or f"{task_id}-writer")
if not ID_RE.fullmatch(governed_task_id):
raise WorkerError("GOVERNED_TASK_ID_INVALID", phase="validation")
idempotency_key = str(governance_value.get("idempotency_key") or f"{task_id}-governed-v1")
commit_key = str(governance_value.get("commit_idempotency_key") or f"{task_id}-commit-v1")
for field, value in (("idempotency_key", idempotency_key), ("commit_idempotency_key", commit_key)):
if not re.fullmatch(r"[A-Za-z0-9][A-Za-z0-9._:-]{7,159}", value):
raise WorkerError(f"{field.upper()}_INVALID", phase="validation")
governance_value.update(
{
"task_id": governed_task_id,
"idempotency_key": idempotency_key,
"commit_idempotency_key": commit_key,
"message": str(governance_value.get("message") or f"chore(gitops): execute task {task_id}"),
}
)
for field in ("main_write", "force_push", "production_write", "tag_write", "baseline_write", "protected_ref_bypass"):
if governance_value.get(field, False) is not False:
raise WorkerError("GOVERNANCE_POLICY_REJECTED", phase="validation", details={"field": field})
allowed_files = governance_value.get("allowed_files")
if allowed_files is not None:
if not isinstance(allowed_files, list):
raise WorkerError("ALLOWED_FILES_INVALID", phase="validation")
allowed = {safe_relative_path(item, field="allowed_file") for item in allowed_files}
if not {item["path"] for item in normalized_changes}.issubset(allowed):
raise WorkerError("CHANGE_OUTSIDE_ALLOWED_FILES", phase="validation")
governance_value["allowed_files"] = sorted(allowed)
return cls(
task_id=task_id,
repository=repository,
base_oid=base_oid,
target_branch=target_branch,
adapter=adapter,
change_set=tuple(normalized_changes),
verification=tuple(normalized_verification),
governance=governance_value,
workspace=workspace_value,
digest=digest_json(execution),
)
class RuntimeAdapter(ABC):
name: str
@abstractmethod
def execute(self, envelope: ExecutionEnvelope, worktree: Path) -> dict[str, Any]:
raise NotImplementedError
class DeterministicExecutor(RuntimeAdapter):
name = "deterministic"
@staticmethod
def _write_atomic(path: Path, content: str) -> None:
path.parent.mkdir(mode=0o750, parents=True, exist_ok=True)
fd, temporary = tempfile.mkstemp(prefix=f".{path.name}.", dir=str(path.parent))
try:
with os.fdopen(fd, "w", encoding="utf-8") as handle:
handle.write(content)
handle.flush()
os.fsync(handle.fileno())
os.replace(temporary, path)
finally:
if os.path.exists(temporary):
os.unlink(temporary)
def execute(self, envelope: ExecutionEnvelope, worktree: Path) -> dict[str, Any]:
changed: set[str] = set()
for change in envelope.change_set:
relative = str(change["path"])
path = resolve_under(worktree, relative, write=True)
expected = change.get("expected_sha256")
current = path.read_bytes() if path.is_file() else None
if expected is not None and (current is None or digest_bytes(current) != expected):
raise WorkerError("WORKTREE_SOURCE_DRIFT", phase="implementing", details={"path": relative})
operation = change["operation"]
if operation == "write_file":
self._write_atomic(path, str(change["content"]))
elif operation == "replace_text":
if current is None:
raise WorkerError("REPLACE_SOURCE_MISSING", phase="implementing", details={"path": relative})
original = current.decode("utf-8")
old = str(change["old"])
count = original.count(old)
expected_count = int(change.get("count", 1))
if count != expected_count:
raise WorkerError("REPLACE_TEXT_MATCH_COUNT_INVALID", phase="implementing", details={"path": relative, "count": count})
self._write_atomic(path, original.replace(old, str(change["new"])))
elif operation == "delete_file":
if path.exists():
path.unlink()
changed.add(relative)
audit("REPOSITORY_CHANGE_APPLIED", operation=operation, path=relative, sha256=digest_bytes(path.read_bytes()) if path.exists() else None)
commands: list[dict[str, Any]] = []
if changed:
commands.append(run_command("git_intent_to_add", ["/usr/bin/git", "-C", str(worktree), "add", "-N", "--", *sorted(changed)], cwd=worktree, timeout=120, internal_git=True))
commands.append(run_command("git_diff_check", ["/usr/bin/git", "-C", str(worktree), "diff", "--check"], cwd=worktree, timeout=120))
commands.append(run_command("git_diff_stat", ["/usr/bin/git", "-C", str(worktree), "diff", "--no-ext-diff", "--stat"], cwd=worktree, timeout=120))
tests: list[dict[str, Any]] = []
for item in envelope.verification:
cwd = worktree if item["cwd"] == "." else resolve_under(worktree, item["cwd"])
tests.append(run_command(item["label"], item["argv"], cwd=cwd, timeout=item["timeout_seconds"]))
return {
"changed_files": sorted(changed),
"commands": commands,
"tests": tests,
"adapter": self.name,
}
class CodexExecutor(RuntimeAdapter):
name = "codex"
def execute(self, envelope: ExecutionEnvelope, worktree: Path) -> dict[str, Any]:
raise WorkerError(
"CODEX_ADAPTER_REQUIRES_EXPLICIT_POLICY",
phase="adapter",
details={"reason": "Codex is not allowed to mutate a governed worktree without an explicit file/command policy"},
)
class HermesExecutor(RuntimeAdapter):
name = "hermes"
def execute(self, envelope: ExecutionEnvelope, worktree: Path) -> dict[str, Any]:
raise WorkerError(
"HERMES_ENGINEERING_ADAPTER_UNAVAILABLE",
phase="adapter",
details={"reason": "The live Hermes service is a user-chat runtime, not an engineering executor"},
)
def adapter_for(name: str) -> RuntimeAdapter:
return {"deterministic": DeterministicExecutor, "codex": CodexExecutor, "hermes": HermesExecutor}[name]()
class HttpClient:
@staticmethod
def call(base: str, method: str, route: str, token: str | None, body: Mapping[str, Any] | None = None, timeout: int = 30) -> tuple[int, dict[str, Any]]:
payload = None if body is None else json.dumps(body, ensure_ascii=False, sort_keys=True, separators=(",", ":")).encode("utf-8")
headers = {"Accept": "application/json"}
if token:
headers["Authorization"] = "Bearer " + token
if payload is not None:
headers["Content-Type"] = "application/json"
request = urllib.request.Request(base + route, data=payload, headers=headers, method=method)
try:
with urllib.request.urlopen(request, timeout=timeout) as response:
value = json.loads(response.read().decode("utf-8"))
return response.status, value if isinstance(value, dict) else {}
except urllib.error.HTTPError as exc:
raw = exc.read(8192).decode("utf-8", "replace")
try:
value = json.loads(raw)
except json.JSONDecodeError:
value = {"error": "HTTP_ERROR"}
return exc.code, value if isinstance(value, dict) else {"error": "HTTP_ERROR"}
except (urllib.error.URLError, TimeoutError):
return 599, {"error": "SERVICE_UNAVAILABLE"}
class AgentTaskClient:
def __init__(self, token: str) -> None:
self.token = token
def poll(self) -> list[dict[str, Any]]:
status, response = HttpClient.call(API_BASE, "POST", "/v1/tasks/poll", self.token, {"limit": 100})
if status != 200:
raise WorkerError("TASK_POLL_FAILED", phase="poll", details={"api_status": status, "error": response.get("error")})
tasks = response.get("tasks")
return [dict(item) for item in tasks if isinstance(item, Mapping)] if isinstance(tasks, list) else []
def claim(self, task_id: str) -> tuple[int, dict[str, Any]]:
return HttpClient.call(API_BASE, "POST", f"/v1/tasks/{task_id}/claim", self.token, {"lease_seconds": LEASE_SECONDS})
def heartbeat(self, task_id: str, lease_token: str, stage: str) -> tuple[int, dict[str, Any]]:
return HttpClient.call(
API_BASE,
"POST",
f"/v1/tasks/{task_id}/heartbeat",
self.token,
{"lease_token": lease_token, "lease_seconds": LEASE_SECONDS, "progress": {"stage": stage, "worker_version": AGENT_VERSION}},
)
def result(self, task_id: str, lease_token: str, idempotency_key: str, result: Mapping[str, Any]) -> tuple[int, dict[str, Any]]:
return HttpClient.call(
API_BASE,
"POST",
f"/v1/tasks/{task_id}/result",
self.token,
{"lease_token": lease_token, "idempotency_key": idempotency_key, "result": dict(result)},
)
class BridgeClient:
def call(self, route: str, body: Mapping[str, Any]) -> tuple[int, dict[str, Any]]:
return HttpClient.call(BRIDGE_BASE, "POST", route, None, body, timeout=60)
class LeaseHeartbeat:
def __init__(self, client: AgentTaskClient, task_id: str, lease_token: str) -> None:
self.client = client
self.task_id = task_id
self.lease_token = lease_token
self.stage = "claimed"
self.failed: str | None = None
self.stop_event = threading.Event()
self.lock = threading.Lock()
self.thread = threading.Thread(target=self._run, name="online-agent-heartbeat", daemon=True)
def first(self) -> None:
status, response = self.client.heartbeat(self.task_id, self.lease_token, self.stage)
if not 200 <= status < 300:
self.failed = str(response.get("error") or status)
raise WorkerError("TASK_HEARTBEAT_FAILED", phase="heartbeat", details={"api_status": status, "error": self.failed})
audit("TASK_HEARTBEAT", task_id=self.task_id, stage=self.stage, api_status=status)
def start(self) -> None:
self.thread.start()
def set_stage(self, stage: str) -> None:
with self.lock:
self.stage = stage
def _run(self) -> None:
while not self.stop_event.wait(HEARTBEAT_SECONDS):
with self.lock:
stage = self.stage
try:
status, response = self.client.heartbeat(self.task_id, self.lease_token, stage)
if not 200 <= status < 300:
self.failed = str(response.get("error") or status)
audit("TASK_HEARTBEAT_FAILED", task_id=self.task_id, stage=stage, api_status=status, error=self.failed)
else:
audit("TASK_HEARTBEAT", task_id=self.task_id, stage=stage, api_status=status)
except Exception as exc: # pragma: no cover - defensive runtime guard
self.failed = type(exc).__name__
audit("TASK_HEARTBEAT_FAILED", task_id=self.task_id, stage=stage, error=self.failed)
def ensure(self) -> None:
if self.failed:
raise WorkerError("TASK_LEASE_LOST", phase="heartbeat", details={"error": self.failed})
def close(self) -> None:
self.stop_event.set()
if self.thread.ident is not None:
self.thread.join(timeout=2)
def load_agent_token() -> str:
directory = Path(os.environ.get("CREDENTIALS_DIRECTORY", ""))
path = directory / "agent_token"
if not path.is_file() or path.is_symlink():
raise WorkerError("AGENT_RUNTIME_CREDENTIAL_UNAVAILABLE", phase="startup")
value = path.read_text(encoding="utf-8").strip()
if len(value) < 24:
raise WorkerError("AGENT_RUNTIME_CREDENTIAL_INVALID", phase="startup")
return value
def ensure_roots() -> None:
if AUTHORITY_GIT_DIR.is_symlink() or STATE_ROOT.is_symlink() or WORKSPACE_ROOT.is_symlink():
raise WorkerError("ROOT_SYMLINK_REJECTED", phase="startup")
if not AUTHORITY_GIT_DIR.is_dir():
raise WorkerError("AUTHORITY_GIT_DIR_UNAVAILABLE", phase="startup")
STATE_ROOT.mkdir(mode=0o750, parents=True, exist_ok=True)
WORKSPACE_ROOT.mkdir(mode=0o750, parents=True, exist_ok=True)
if GIT_CONFIG_GLOBAL.is_symlink():
raise WorkerError("GIT_CONFIG_SYMLINK_REJECTED", phase="startup")
expected = f"[safe]\n\tdirectory = {AUTHORITY_GIT_DIR}\n"
if GIT_CONFIG_GLOBAL.exists() and GIT_CONFIG_GLOBAL.read_text(encoding="utf-8") != expected:
raise WorkerError("GIT_CONFIG_BINDING_CONFLICT", phase="startup")
if not GIT_CONFIG_GLOBAL.exists():
GIT_CONFIG_GLOBAL.write_text(expected, encoding="utf-8")
os.chmod(GIT_CONFIG_GLOBAL, 0o640)
def task_state_path(task_id: str) -> Path:
if not ID_RE.fullmatch(task_id):
raise WorkerError("TASK_ID_INVALID", phase="state")
return STATE_ROOT / "tasks" / f"{task_id}.json"
def load_task_state(task_id: str, execution_digest: str) -> dict[str, Any] | None:
path = task_state_path(task_id)
if not path.is_file() or path.is_symlink():
return None
try:
value = json.loads(path.read_text(encoding="utf-8"))
except Exception:
raise WorkerError("TASK_STATE_INVALID", phase="state")
if not isinstance(value, dict):
raise WorkerError("TASK_STATE_INVALID", phase="state")
if value.get("execution_digest") not in {None, execution_digest}:
raise WorkerError("TASK_PAYLOAD_DRIFT", phase="recovery")
return value
def save_task_state(task_id: str, value: Mapping[str, Any]) -> None:
atomic_json(task_state_path(task_id), value)
def new_run_root() -> Path:
ensure_roots()
root = Path(tempfile.mkdtemp(prefix="task-", dir=str(WORKSPACE_ROOT)))
os.chmod(root, 0o750)
return root
def cleanup_run_root(path: Path) -> None:
root = WORKSPACE_ROOT.resolve()
candidate = path.resolve(strict=False)
if candidate == root or root not in candidate.parents:
raise WorkerError("CLEANUP_SCOPE_INVALID", phase="cleanup")
if candidate.exists() or candidate.is_symlink():
shutil.rmtree(candidate)
def prepare_worktree(run_root: Path, base_oid: str) -> Path:
clone = run_root / "repository"
worktree = run_root / "worktree"
run_command("workspace_clone", ["/usr/bin/git", "clone", "--no-local", "--no-hardlinks", str(AUTHORITY_GIT_DIR), str(clone)], cwd=run_root, timeout=300, internal_git=True)
run_command("workspace_base_probe", ["/usr/bin/git", "-C", str(clone), "rev-parse", "--verify", base_oid + "^{commit}"], cwd=run_root, timeout=60)
run_command("workspace_worktree_create", ["/usr/bin/git", "-C", str(clone), "worktree", "add", "--detach", str(worktree), base_oid], cwd=run_root, timeout=300, internal_git=True)
return worktree
def governed_commit(envelope: ExecutionEnvelope, worktree: Path, bridge: BridgeClient, heartbeat: LeaseHeartbeat) -> dict[str, Any]:
governance = envelope.governance
create_body = {
"task_id": governance["task_id"],
"repository": envelope.repository,
"base_oid": envelope.base_oid,
"target_branch": envelope.target_branch,
"risk": str(governance.get("risk") or "engineering"),
"description": str(governance.get("description") or f"Governed execution for {envelope.task_id}"),
"idempotency_key": governance["idempotency_key"],
}
status, response = bridge.call("/v2/governed-task", create_body)
if not 200 <= status < 300:
raise WorkerError("GOVERNED_TASK_CREATE_FAILED", phase="committing", details={"api_status": status, "error": response.get("error")})
generation = int(response.get("generation") or 0)
expected_branch_oid = str(response.get("expected_branch_oid") or envelope.base_oid)
branch_mode = str(response.get("branch_mode") or "CREATE_NEW")
lease_body = {
"schema": "aios.task-writer-lease.v2",
"task_id": governance["task_id"],
"repository": envelope.repository,
"branch_mode": branch_mode,
"base_oid": envelope.base_oid,
"expected_branch_oid": expected_branch_oid,
"target_branch": envelope.target_branch,
"generation": generation,
"lease_seconds": int(governance.get("lease_seconds") or 900),
}
status, lease_response = bridge.call("/v2/writer-lease", lease_body)
if not 200 <= status < 300:
raise WorkerError("WRITER_LEASE_ACQUIRE_FAILED", phase="committing", details={"api_status": status, "error": lease_response.get("error")})
heartbeat.ensure()
files: dict[str, str] = {}
paths = governance.get("allowed_files") or [item["path"] for item in envelope.change_set]
for relative in sorted({safe_relative_path(item, field="allowed_file") for item in paths}):
path = resolve_under(worktree, relative)
if not path.is_file():
raise WorkerError("COMMIT_FILE_MISSING", phase="committing", details={"path": relative})
files[relative] = base64.b64encode(path.read_bytes()).decode("ascii")
commit_body = {
"task_id": governance["task_id"],
"repository": envelope.repository,
"expected_branch_oid": expected_branch_oid,
"target_branch": envelope.target_branch,
"generation": generation,
"idempotency_key": governance["commit_idempotency_key"],
"files": files,
"message": governance["message"],
}
status, commit_response = bridge.call("/v2/commit-files", commit_body)
if not 200 <= status < 300:
raise WorkerError("GOVERNED_COMMIT_FAILED", phase="committing", details={"api_status": status, "error": commit_response.get("error")})
commit_oid = commit_response.get("commit_oid")
if not isinstance(commit_oid, str) or not OID_RE.fullmatch(commit_oid):
raise WorkerError("GOVERNED_COMMIT_OID_INVALID", phase="committing")
visible_oid: str | None = None
for _ in range(5):
probe = subprocess.run(
["/usr/bin/git", "--git-dir", str(AUTHORITY_GIT_DIR), "rev-parse", "--verify", f"refs/heads/{envelope.target_branch}"],
text=True,
capture_output=True,
check=False,
env={"PATH": "/usr/bin:/bin", "LC_ALL": "C.UTF-8", "GIT_CONFIG_GLOBAL": str(GIT_CONFIG_GLOBAL)},
timeout=30,
)
if probe.returncode == 0:
visible_oid = probe.stdout.strip()
if visible_oid == commit_oid:
break
time.sleep(1)
if visible_oid != commit_oid:
raise WorkerError("GIT_VISIBILITY_MISMATCH", phase="verification", details={"commit_oid": commit_oid, "visible_oid": visible_oid})
return {
"governed_task_id": governance["task_id"],
"branch_mode": branch_mode,
"generation": generation,
"target_branch": envelope.target_branch,
"commit_oid": commit_oid,
"visible_oid": visible_oid,
"commit_idempotency_key": governance["commit_idempotency_key"],
"api_commit_visible": commit_response.get("commit_visible"),
"direct_main_write": False,
"force_push": False,
"production_write": False,
"protected_ref_bypass": False,
}
def build_result(task_id: str, envelope: ExecutionEnvelope, execution: Mapping[str, Any], governed: Mapping[str, Any]) -> dict[str, Any]:
return {
"status": "SUCCEEDED",
"verification_state": "VERIFIED_BY_WORKER_PENDING_SERVER_REVIEW",
"task_id": task_id,
"execution_backend": "generic-online-agent-worker-v2",
"adapter": envelope.adapter,
"execution_digest": envelope.digest,
"repository": envelope.repository,
"base_oid": envelope.base_oid,
"target_branch": envelope.target_branch,
"changed_files": execution.get("changed_files", []),
"commands": [{k: item.get(k) for k in ("label", "exit_code", "stdout_sha256", "stderr_sha256", "duration_ms")} for item in execution.get("commands", [])],
"tests": [{k: item.get(k) for k in ("label", "exit_code", "stdout_sha256", "stderr_sha256", "duration_ms")} for item in execution.get("tests", [])],
"governed": dict(governed),
"workspace_cleanup": "PASS",
"secret_material_exposed": False,
"direct_main_write": False,
"force_push": False,
"production_write": False,
"protected_ref_bypass": False,
}
class WorkerSupervisor:
def __init__(self, token: str) -> None:
self.agent = AgentTaskClient(token)
self.bridge = BridgeClient()
def process_claim(self, task: Mapping[str, Any], claim: Mapping[str, Any]) -> None:
task_id = str(claim.get("task_id") or task.get("task_id") or "")
lease_token = claim.get("lease_token")
if not isinstance(lease_token, str) or len(lease_token) < 24:
raise WorkerError("LEASE_TOKEN_NOT_RETURNED", phase="claim")
audit("TASK_CLAIMED", task_id=task_id, attempt=claim.get("attempt"), revision=claim.get("revision"))
heartbeat = LeaseHeartbeat(self.agent, task_id, lease_token)
try:
heartbeat.first()
heartbeat.set_stage("started")
heartbeat.start()
audit("TASK_STARTED", task_id=task_id, attempt=claim.get("attempt"))
envelope = ExecutionEnvelope.from_task(task)
state = load_task_state(task_id, envelope.digest) or {"task_id": task_id, "execution_digest": envelope.digest, "phase": "claimed"}
governed = state.get("governed") if isinstance(state.get("governed"), Mapping) else None
if isinstance(governed, Mapping) and isinstance(governed.get("commit_oid"), str) and OID_RE.fullmatch(governed["commit_oid"]):
result = state.get("result")
if not isinstance(result, Mapping):
result = {"status": "SUCCEEDED", "verification_state": "VERIFIED_BY_WORKER_PENDING_SERVER_REVIEW", "task_id": task_id, "execution_digest": envelope.digest, "governed": dict(governed), "recovered_without_duplicate_commit": True, "direct_main_write": False, "production_write": False, "force_push": False, "protected_ref_bypass": False}
audit("TASK_RECOVERED_COMMIT", task_id=task_id, commit_oid=governed["commit_oid"], duplicate_commit=False)
else:
run_root = new_run_root()
try:
worktree = prepare_worktree(run_root, envelope.base_oid)
state.update({"phase": "workspace_prepared", "execution_digest": envelope.digest, "attempt": claim.get("attempt")})
save_task_state(task_id, state)
adapter = adapter_for(envelope.adapter)
execution = adapter.execute(envelope, worktree)
state.update({"phase": "verification_passed", "execution": execution})
save_task_state(task_id, state)
heartbeat.ensure()
heartbeat.set_stage("committing")
governed = governed_commit(envelope, worktree, self.bridge, heartbeat)
result = build_result(task_id, envelope, execution, governed)
state.update({"phase": "commit_done", "governed": governed, "result": result})
save_task_state(task_id, state)
finally:
cleanup_run_root(run_root)
audit("WORKSPACE_CLEANED", task_id=task_id, cleanup="PASS")
heartbeat.ensure()
heartbeat.set_stage("result")
result_key = str(result.get("governed", {}).get("commit_idempotency_key") or f"{task_id}-result-{envelope.digest[:16]}")
status, response = self.agent.result(task_id, lease_token, result_key, result)
if not 200 <= status < 300:
raise WorkerError("TASK_RESULT_SUBMIT_FAILED", phase="result", details={"api_status": status, "error": response.get("error")})
audit("TASK_RESULT_SUBMITTED", task_id=task_id, api_status=status, result_idempotency_key=result_key, commit_oid=result.get("governed", {}).get("commit_oid"))
except WorkerError as exc:
audit("TASK_FAILED", task_id=task_id, error=exc.code, phase=exc.phase, details=exc.details)
failure_key = f"{task_id}-attempt-{claim.get('attempt', 0)}-failure"
failure = {
"status": "FAILED",
"verification_state": "UNVERIFIED",
"task_id": task_id,
"execution_backend": "generic-online-agent-worker-v2",
"error": exc.code,
"phase": exc.phase,
"details": exc.details,
"direct_main_write": False,
"force_push": False,
"production_write": False,
"protected_ref_bypass": False,
"secret_material_exposed": False,
}
try:
status, response = self.agent.result(task_id, lease_token, failure_key, failure)
audit("TASK_FAILURE_RESULT_SUBMITTED", task_id=task_id, api_status=status, error=response.get("error"))
except Exception as submit_exc: # pragma: no cover - defensive runtime guard
audit("TASK_FAILURE_RESULT_SUBMIT_FAILED", task_id=task_id, error=type(submit_exc).__name__)
finally:
heartbeat.close()
def once(self) -> int:
try:
tasks = self.agent.poll()
audit("TASK_POLL", visible_count=len(tasks))
except WorkerError as exc:
audit("TASK_POLL_FAILED", error=exc.code, phase=exc.phase, details=exc.details)
return 10
dynamic_tasks: list[dict[str, Any]] = []
for task in tasks:
metadata = task.get("metadata")
execution = metadata.get("execution") if isinstance(metadata, Mapping) else None
if not isinstance(execution, Mapping) or execution.get("schema") != "aios.online-agent.execution.v2":
audit("TASK_SKIPPED_NON_DYNAMIC", task_id=task.get("task_id"), reason="DYNAMIC_EXECUTION_PAYLOAD_REQUIRED")
continue
dynamic_tasks.append(task)
for task in dynamic_tasks:
task_id = str(task.get("task_id") or "")
status, claim = self.agent.claim(task_id)
if status == 409:
audit("TASK_CLAIM_SKIPPED", task_id=task_id, api_status=status, error=claim.get("error"))
continue
if status != 200:
audit("TASK_CLAIM_FAILED", task_id=task_id, api_status=status, error=claim.get("error"))
continue
self.process_claim(task, claim)
return 0
return 0
def run_forever(self) -> int:
ensure_roots()
audit("WORKER_STARTED", task_agnostic=True, api_base=API_BASE, bridge_base=BRIDGE_BASE, adapter_names=["deterministic", "codex", "hermes"], runtime_only_credentials=True)
while True:
self.once()
time.sleep(POLL_SECONDS)
def main() -> int:
try:
token = load_agent_token()
supervisor = WorkerSupervisor(token)
if os.environ.get("AIOS_WORKER_ONCE") == "1":
ensure_roots()
return supervisor.once()
return supervisor.run_forever()
except WorkerError as exc:
audit("WORKER_START_FAILED", error=exc.code, phase=exc.phase, details=exc.details)
return 20
if __name__ == "__main__":
raise SystemExit(main())
@@ -0,0 +1,157 @@
#!/usr/bin/env python3
"""Register or recover the generic Online Agent using runtime-only secrets."""
from __future__ import annotations
import json
import os
from pathlib import Path
import subprocess
import tempfile
import urllib.error
import urllib.request
from datetime import datetime, timezone
from typing import Any
BASE = os.environ.get("AIOS_AGENT_TASK_API_URL", "http://127.0.0.1:18940").rstrip("/")
AGENT_ID = os.environ.get("AIOS_AGENT_ID", "online-engineering-worker-cn-network-mkt-generic-20260815")
AGENT_TYPE = os.environ.get("AIOS_AGENT_TYPE", "trusted-online-engineering-worker")
AGENT_VERSION = os.environ.get("AIOS_AGENT_VERSION", "2026-08-15.3-generic")
PROJECTION_PROVIDER = "BridgeV2RuntimeCredentialProjection"
STATE_DIR = Path(os.environ.get("AIOS_WORKER_REGISTRATION_STATE", "/var/lib/aios-online-agent-worker-20260815/registration"))
CREDENTIAL_PATH = Path(os.environ.get("AIOS_WORKER_ENCRYPTED_CREDENTIAL", "/etc/credstore.encrypted/aios-online-agent-worker-20260815/agent_token"))
IDENTITY_PATH = STATE_DIR / "identity.json"
AUDIT_PATH = STATE_DIR / "events.jsonl"
CAPABILITIES = [
"online_agent_task_api", "task.poll", "task.claim", "task.heartbeat", "task.result",
"engineering.consumer", "engineering.execute", "workspace.worktree", "git.read", "git.diff",
"git.test", "governed.write.branch", "governed.write.file",
]
def now() -> str:
return datetime.now(timezone.utc).isoformat()
def audit(event_type: str, **fields: Any) -> None:
STATE_DIR.mkdir(mode=0o750, parents=True, exist_ok=True)
record = {"event_type": event_type, "created_at": now(), "agent_id": AGENT_ID, "credential_material_exposed": False, **fields}
with AUDIT_PATH.open("a", encoding="utf-8") as handle:
handle.write(json.dumps(record, ensure_ascii=False, sort_keys=True) + "\n")
os.chmod(AUDIT_PATH, 0o640)
print(json.dumps(record, ensure_ascii=False, sort_keys=True), flush=True)
def api_call(method: str, route: str, token: str, body: dict[str, Any] | None = None) -> tuple[int, dict[str, Any]]:
payload = None if body is None else json.dumps(body, ensure_ascii=False, sort_keys=True, separators=(",", ":")).encode("utf-8")
headers = {"Authorization": "Bearer " + token, "Accept": "application/json"}
if payload is not None:
headers["Content-Type"] = "application/json"
request = urllib.request.Request(BASE + route, data=payload, headers=headers, method=method)
try:
with urllib.request.urlopen(request, timeout=15) as response:
value = json.loads(response.read().decode("utf-8"))
return response.status, value if isinstance(value, dict) else {}
except urllib.error.HTTPError as exc:
try:
value = json.loads(exc.read(2048).decode("utf-8"))
except Exception:
value = {"error": "HTTP_ERROR"}
return exc.code, value if isinstance(value, dict) else {"error": "HTTP_ERROR"}
except (urllib.error.URLError, TimeoutError):
return 599, {"error": "AGENT_TASK_API_UNAVAILABLE"}
def read_identity() -> dict[str, Any] | None:
if not IDENTITY_PATH.is_file() or IDENTITY_PATH.is_symlink():
return None
try:
value = json.loads(IDENTITY_PATH.read_text(encoding="utf-8"))
except Exception:
return None
return value if isinstance(value, dict) else None
def identity_matches(value: dict[str, Any] | None) -> bool:
return bool(value and value.get("agent_id") == AGENT_ID and value.get("agent_type") == AGENT_TYPE and value.get("agent_version") == AGENT_VERSION and value.get("credential_projection_provider") == PROJECTION_PROVIDER and value.get("task_scope") == "dynamic")
def encrypted_credential_is_valid() -> bool:
if not CREDENTIAL_PATH.is_file() or CREDENTIAL_PATH.is_symlink():
return False
result = subprocess.run(["systemd-creds", "decrypt", "--with-key=host", str(CREDENTIAL_PATH), "/dev/null"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, check=False, timeout=15)
return result.returncode == 0
def write_identity() -> None:
value = {"agent_id": AGENT_ID, "agent_type": AGENT_TYPE, "agent_version": AGENT_VERSION, "task_scope": "dynamic", "credential_projection_provider": PROJECTION_PROVIDER, "credential_delivery": "systemd-LoadCredentialEncrypted", "task_api_url": BASE, "task_api_schema": "aios.agent-task.v1", "execution_backend": "generic-online-agent-worker-v2", "runtime_only_secret_delivery": True, "production_write": False, "main_write": False, "force_push": False, "tag_write": False, "baseline_write": False, "writer_lease": True, "created_at": now()}
STATE_DIR.mkdir(mode=0o750, parents=True, exist_ok=True)
fd, temporary = tempfile.mkstemp(prefix="identity.", dir=str(STATE_DIR))
try:
with os.fdopen(fd, "w", encoding="utf-8") as handle:
json.dump(value, handle, ensure_ascii=False, sort_keys=True)
handle.write("\n")
os.chmod(temporary, 0o640)
os.replace(temporary, IDENTITY_PATH)
finally:
if os.path.exists(temporary):
os.unlink(temporary)
def encrypt_agent_token(agent_token: str) -> None:
CREDENTIAL_PATH.parent.mkdir(mode=0o700, parents=True, exist_ok=True)
fd, temporary = tempfile.mkstemp(prefix=".online-agent-token.", dir=str(CREDENTIAL_PATH.parent))
os.close(fd)
os.unlink(temporary)
try:
result = subprocess.run(["systemd-creds", "encrypt", "--with-key=host", "--name=agent_token", "/dev/stdin", temporary], input=(agent_token + "\n").encode("utf-8"), stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, check=False, timeout=20)
if result.returncode != 0:
raise RuntimeError("AGENT_CREDENTIAL_ENCRYPT_FAILED")
os.chmod(temporary, 0o600)
os.replace(temporary, CREDENTIAL_PATH)
os.chmod(CREDENTIAL_PATH, 0o600)
finally:
if os.path.exists(temporary):
os.unlink(temporary)
def register_or_recover() -> str:
STATE_DIR.mkdir(mode=0o750, parents=True, exist_ok=True)
existing = read_identity()
if CREDENTIAL_PATH.exists():
if not identity_matches(existing):
raise RuntimeError("AGENT_IDENTITY_BINDING_CONFLICT")
if not encrypted_credential_is_valid():
raise RuntimeError("AGENT_ENCRYPTED_CREDENTIAL_INVALID")
audit("AGENT_RECOVERED", credential_delivery="systemd-LoadCredentialEncrypted", credential_projection_provider=PROJECTION_PROVIDER, register_or_recover=True)
return "RECOVERED"
if existing is not None:
raise RuntimeError("AGENT_CREDENTIAL_MISSING_AFTER_PRIOR_REGISTRATION")
credential_directory = Path(os.environ.get("CREDENTIALS_DIRECTORY", ""))
control_path = credential_directory / "bridge_control_token"
if not control_path.is_file():
raise RuntimeError("BRIDGE_RUNTIME_CONTROL_CREDENTIAL_UNAVAILABLE")
control_token = control_path.read_text(encoding="utf-8").strip()
if len(control_token) < 32:
raise RuntimeError("BRIDGE_RUNTIME_CONTROL_CREDENTIAL_INVALID")
status, response = api_call("POST", "/v1/agents/register", control_token, {"agent_id": AGENT_ID, "agent_type": AGENT_TYPE, "agent_version": AGENT_VERSION, "capabilities": CAPABILITIES, "metadata": {"task_scope": "dynamic", "credential_projection_provider": PROJECTION_PROVIDER, "runtime_only_secret_delivery": True, "task_api_schema": "aios.agent-task.v1", "execution_backend": "generic-online-agent-worker-v2", "production_write": False, "main_write": False, "force_push": False, "tag_write": False, "baseline_write": False, "writer_lease": True}})
if status == 409 and response.get("error") == "AGENT_ALREADY_EXISTS":
raise RuntimeError("AGENT_ALREADY_EXISTS_RECOVERY_CREDENTIAL_UNAVAILABLE")
if status != 201:
raise RuntimeError("AGENT_REGISTER_FAILED_" + str(response.get("error") or status))
agent_token = response.get("agent_token")
if not isinstance(agent_token, str) or len(agent_token) < 24:
raise RuntimeError("AGENT_TOKEN_NOT_RETURNED")
encrypt_agent_token(agent_token)
write_identity()
audit("AGENT_REGISTERED", credential_delivery="systemd-LoadCredentialEncrypted", credential_projection_provider=PROJECTION_PROVIDER, register_or_recover=True)
return "REGISTERED"
if __name__ == "__main__":
try:
raise SystemExit(0 if register_or_recover() in {"REGISTERED", "RECOVERED"} else 20)
except Exception as exc:
audit("AGENT_REGISTRATION_FAILED", error=type(exc).__name__)
raise SystemExit(20)
@@ -0,0 +1,79 @@
from __future__ import annotations
import os
from pathlib import Path
from aios_online_agent_worker.worker import ExecutionEnvelope, WorkerError, sanitize # noqa: E402
BASE = "a" * 40
def payload(task_id: str, branch: str, *, base_oid: str = BASE) -> dict[str, object]:
return {
"task_id": task_id,
"metadata": {
"execution": {
"schema": "aios.online-agent.execution.v2",
"repository": "aios/aios",
"base_oid": base_oid,
"target_branch": branch,
"adapter": "deterministic",
"workspace": {
"authority_git_dir": str(Path(os.environ.get("AIOS_AUTHORITY_GIT_DIR", "/data/aios-gitops/repo.git"))),
"read_only_repository": True,
"cleanup_policy": "always",
},
"change_set": [
{
"operation": "write_file",
"path": "tests/generated_marker.py",
"content": "def test_marker():\n assert True\n",
"expected_sha256": None,
}
],
"verification": [
{"label": "compile", "argv": ["/usr/bin/python3", "-m", "py_compile", "tests/generated_marker.py"], "cwd": ".", "timeout_seconds": 60}
],
"governance": {
"task_id": f"{task_id}-writer",
"idempotency_key": f"{task_id}-governed-v1",
"commit_idempotency_key": f"{task_id}-commit-v1",
"allowed_files": ["tests/generated_marker.py"],
"main_write": False,
"force_push": False,
"production_write": False,
"tag_write": False,
"baseline_write": False,
"protected_ref_bypass": False,
},
}
},
}
def test_dynamic_payload_is_not_bound_to_task_or_base() -> None:
first = ExecutionEnvelope.from_task(payload("task-alpha-20260815", "agent/alpha-20260815"))
second = ExecutionEnvelope.from_task(payload("task-beta-20260815", "agent/beta-20260815", base_oid="b" * 40))
assert first.task_id != second.task_id
assert first.base_oid != second.base_oid
assert first.target_branch != second.target_branch
def test_protected_branch_is_rejected() -> None:
try:
ExecutionEnvelope.from_task(payload("task-main-20260815", "main"))
except WorkerError as exc:
assert exc.code == "PROTECTED_BRANCH_REJECTED"
else:
raise AssertionError("protected branch accepted")
def test_task_payload_does_not_persist_secret_values() -> None:
value = sanitize({"agent_token": "agt-secret", "nested": {"authorization": "Bearer secret"}})
assert value == {"agent_token": "[REDACTED]", "nested": {"authorization": "[REDACTED]"}}
def test_execution_digest_is_stable() -> None:
value = payload("task-digest-20260815", "agent/digest-20260815")
assert ExecutionEnvelope.from_task(value).digest == ExecutionEnvelope.from_task(value).digest
@@ -1,9 +1,8 @@
from __future__ import annotations
import hashlib,json,re,tarfile
from release_version import RELEASE_VERSION_RE
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=RELEASE_VERSION_RE
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):
-165
View File
@@ -1,165 +0,0 @@
from __future__ import annotations
import inspect
import json
import sys
from pathlib import Path
import pytest
ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(ROOT))
sys.path.insert(0, str(ROOT / "runtime/agent-os"))
from aios_chatgpt_bridge_v2 import ( # noqa: E402
OPERATION_REQUIRED_FIELDS,
operation_contracts,
render_openapi_contract_fragment,
)
from aios_chatgpt_bridge_v2_server import GovernedRuntime # noqa: E402
from aios_gitops_job_controller import JobState # noqa: E402
from gitea_tag_publication import validate_gitea_api_url # noqa: E402
from silent_release_hardening import ReleaseSagaStore, SilentReleaseActionService # noqa: E402
from release_version import validate_release_version # noqa: E402
import aios_model_api_staging as model_api # noqa: E402
def test_canonical_release_version_is_shared_and_accepts_v2r2c1():
assert validate_release_version("V2R2C1") == "V2R2C1"
assert validate_release_version("V2R2C1-repair") == "V2R2C1-repair"
with pytest.raises(ValueError):
validate_release_version("v2r2c1")
def test_gitea_url_validation_is_startup_safe():
assert validate_gitea_api_url("https://g.cnnetmkt.top/git/api/v1") == (
"https://g.cnnetmkt.top/git/api/v1"
)
with pytest.raises(ValueError, match="HTTP"):
validate_gitea_api_url("g.cnnetmkt.top/git/api/v1")
def test_public_release_reads_use_the_bridge_canonical_evaluator():
query = {
"version": ["V2R2C1"],
"target_oid": ["998d30304787e31e5ae607c0bd86b5edb2b0e5ad"],
"evidence_closure_oid": ["a8b906d1bc24088c19647e98de6a679268f6d0a2"],
"approval_plan_id": ["sha256:plan"],
}
assert model_api.governed_release_query_payload(query) == {
"version": "V2R2C1",
"target_oid": "998d30304787e31e5ae607c0bd86b5edb2b0e5ad",
"evidence_closure_oid": "a8b906d1bc24088c19647e98de6a679268f6d0a2",
"approval_plan_id": "sha256:plan",
}
source = inspect.getsource(model_api.H.do_GET)
assert "publication_publisher()" in source
assert "get_release_consistency" in source
assert "run_release_acceptance" in source
def test_governed_read_routes_are_bound_to_authenticated_facade():
route_script = (
ROOT / "ops/gitops-migration/bind_external_action_surface_final_staging.sh"
).read_text()
for path in (
"/api/governed-write-capabilities",
"/api/control-plane-snapshot",
"/api/online-agent-task",
"/api/online-agent-task-events",
):
assert f"location = {path}" in route_script
assert f"proxy_pass http://127.0.0.1:18890{path};" in route_script
assert "location /api/ {" in route_script
assert "Authorization $http_authorization" in route_script
def test_commit_files_runtime_and_published_schema_have_same_required_fields():
runtime = operation_contracts()["commitFilesGoverned"]["required_fields"]
assert runtime == list(OPERATION_REQUIRED_FIELDS["commitFilesGoverned"])
fragment = render_openapi_contract_fragment()
published = (ROOT / "docs/gitops/openapi-chatgpt-actions.yaml").read_text()
assert fragment in published
for field in runtime:
assert f" - {field}" in fragment
assert "required: [path, content_base64]" in fragment
class _Store:
def __init__(self, row):
self.row = row
self.receipt_calls = 0
def read(self, _job_id):
return self.row
def _receipt(self, row, duplicate=False):
self.receipt_calls += 1
return type("Receipt", (), {"as_dict": lambda _self: {
"job_id": row["job_id"],
"state": row["state"],
"duplicate": duplicate,
}})()
class _Controller:
def __init__(self, row):
self.store = _Store(row)
def _runtime_for(row):
runtime = object.__new__(GovernedRuntime)
controller = _Controller(row)
runtime._controller_for_job = lambda _job_id: controller
runtime._executor_from_row = lambda *_args: (_ for _ in ()).throw(
AssertionError("terminal reconcile must not reconstruct an executor")
)
return runtime, controller
@pytest.mark.parametrize(
"state",
[
JobState.COMPLETED.value,
JobState.FAILED.value,
JobState.CANCELLED.value,
JobState.MANUAL_OWNER_REVIEW_REQUIRED.value,
],
)
def test_terminal_reconcile_is_idempotent_without_durable_binding(state):
row = {
"job_id": "job-terminal",
"state": state,
"request_json": json.dumps({}),
}
runtime, controller = _runtime_for(row)
result = GovernedRuntime.job(runtime, {"job_id": row["job_id"]}, "reconcile")
assert result["duplicate"] is True
assert controller.store.receipt_calls == 1
def test_active_reconcile_still_uses_executor():
row = {
"job_id": "job-active",
"state": JobState.RECONCILIATION_REQUIRED.value,
"request_json": json.dumps({}),
}
runtime, controller = _runtime_for(row)
class Executor:
def reconcile(self, job_id):
return {"job_id": job_id, "state": "COMPLETED"}
runtime._executor_from_row = lambda *_args: Executor()
assert GovernedRuntime.job(runtime, {"job_id": row["job_id"]}, "reconcile") == {
"job_id": "job-active",
"state": "COMPLETED",
}
def test_missing_release_saga_is_a_not_found_empty_state(tmp_path):
service = object.__new__(SilentReleaseActionService)
service.sagas = ReleaseSagaStore(tmp_path)
service.execution_enabled = False
result = service.get_release_saga("never-created-action-contract")
assert result["status"] == "NOT_FOUND"
assert result["state"] == "NOT_FOUND"
assert not list(tmp_path.rglob("*"))
@@ -1,452 +0,0 @@
from __future__ import annotations
from pathlib import Path
import hashlib
import json
import sqlite3
import sys
import pytest
ROOT=Path(__file__).resolve().parents[1]
sys.path.insert(0,str(ROOT/"active-runtime/app"))
from aios_agent_task_initial_execution_projection import (
InitialExecutionProjectionError,
InitialExecutionProjectionRequest,
InitialExecutionProjectionStore,
SourceCaptureProjectionRecoveryRequest,
)
from aios_online_agent_execution import (
GovernedExecutionBinding,
project_execution_envelope,
)
AUTHORIZATION="owner-initial-execution-projection-recovery-20260820"
TASK_ID="task-initial-projection-a"
JOB_ID="job-initial-projection-a"
BASE="a"*40
BRANCH="agent/task-initial-projection-a"
IDEMPOTENCY="task-initial-projection-binding-v1"
CAPABILITY="agent.runtime.openclaw"
@pytest.fixture(autouse=True)
def _auth(monkeypatch):
monkeypatch.setenv(
"AIOS_AGENT_TASK_INITIAL_EXECUTION_PROJECTION_AUTHORIZATION_ID",
AUTHORIZATION,
)
def canon(value):
return hashlib.sha256(
json.dumps(value,sort_keys=True,separators=(",",":")).encode()
).hexdigest()
def binding():
return GovernedExecutionBinding.from_mapping({
"task_id":TASK_ID,
"job_id":JOB_ID,
"repository":"aios/aios",
"base_oid":BASE,
"expected_branch_oid":BASE,
"target_branch":BRANCH,
"branch_mode":"CREATE_NEW",
"generation":1,
"idempotency_key":IDEMPOTENCY,
})
def runtime_input():
return {
"goal":"Supervise release preparation",
"instructions":"Observe and resume only the same lineage.",
"success_criteria":[{"id":"heartbeat","description":"Heartbeat observed"}],
"constraints":{"production_write":False},
"scope":{"scope_type":"legacy","tenant_id":None,"project_id":None},
"trace_id":TASK_ID,
}
def envelope(*, job_id=JOB_ID, action="agent_runtime", capability=CAPABILITY):
bound=binding()
if job_id != JOB_ID:
bound=GovernedExecutionBinding.from_mapping({**bound.public(),"job_id":job_id})
return project_execution_envelope(
bound,
{
"action":action,
"adapter":"openclaw" if action=="agent_runtime" else "source_capture",
"capability_scope":[capability],
"runtime_input":runtime_input() if action=="agent_runtime" else None,
},
)
def make_db(tmp_path, *, claimed_by=None, lease=False, metadata=None, caps=None, event="TASK_CREATED"):
path=tmp_path/"state.sqlite3"
con=sqlite3.connect(path)
con.executescript("""
CREATE TABLE tasks(
task_id TEXT PRIMARY KEY,
goal TEXT NOT NULL,
instructions TEXT NOT NULL,
success_criteria_json TEXT NOT NULL,
required_capabilities_json TEXT NOT NULL,
constraints_json TEXT NOT NULL,
metadata_json TEXT NOT NULL,
deadline TEXT,
priority INTEGER NOT NULL,
max_attempts INTEGER NOT NULL,
attempt INTEGER NOT NULL DEFAULT 0,
status TEXT NOT NULL,
claimed_by TEXT,
lease_token_hash TEXT,
lease_expires_at TEXT,
result_json TEXT,
result_digest TEXT,
result_idempotency_key TEXT,
verification_json TEXT,
revision INTEGER NOT NULL DEFAULT 1,
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL
);
CREATE TABLE events(
event_id INTEGER PRIMARY KEY AUTOINCREMENT,
task_id TEXT,
agent_id TEXT,
event_type TEXT NOT NULL,
payload_json TEXT NOT NULL,
created_at TEXT NOT NULL
);
""")
metadata={} if metadata is None else metadata
caps=[] if caps is None else caps
con.execute(
"INSERT INTO tasks VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)",
(
TASK_ID,"Supervise release preparation",
"Observe and resume only the same lineage.",
json.dumps([{"id":"heartbeat","description":"Heartbeat observed"}]),
json.dumps(caps),json.dumps({"production_write":False}),json.dumps(metadata),
None,100,20,0,"QUEUED",claimed_by,
"lease-hash" if lease else None,
"2099-01-01T00:00:00Z" if lease else None,
None,None,None,None,1,"2026-08-20T00:00:00Z","2026-08-20T00:00:00Z",
),
)
con.execute(
"INSERT INTO events(task_id,agent_id,event_type,payload_json,created_at) VALUES (?,?,?,?,?)",
(TASK_ID,None,event,"{}","2026-08-20T00:00:00Z"),
)
con.commit();con.close()
return path
def request(*, digest=None, key="initial-projection-a-v1"):
return InitialExecutionProjectionRequest.from_mapping({
"expected_status":"QUEUED",
"expected_revision":1,
"expected_attempt":0,
"expected_metadata_sha256":canon({}),
"expected_required_capabilities_sha256":canon([]),
"expected_job_id":JOB_ID,
"expected_execution_digest":digest or envelope()["execution_digest"],
"required_capability":CAPABILITY,
"adapter":"openclaw",
"authorization_id":AUTHORIZATION,
"idempotency_key":key,
"reason":"initial governed supervisor runtime projection",
})
def resolver(_task_id,_job_id,_capability,_adapter,received_runtime_input):
assert received_runtime_input==runtime_input()
return envelope()
def source_capture_result():
old=envelope(action="source_capture",capability="git.read")
return {
"task_id":TASK_ID,
"job_id":JOB_ID,
"repository":"aios/aios",
"base_oid":BASE,
"expected_branch_oid":BASE,
"target_branch":BRANCH,
"generation":1,
"status":"SUCCEEDED",
"classification":"NEEDS_REWORK_SOURCE_CAPTURE",
"execution_mode":"source_capture",
"execution_digest":old["execution_digest"],
"changed_files":[],
"writer_lease":"NONE",
"verification_state":"NEEDS_REWORK",
"direct_main_write":False,
"production_write":False,
"force_push":False,
"protected_ref_bypass":False,
"secret_material_exposed":False,
"source_snapshot":{"schema":"test.snapshot.v1","payload_sha256":"d"*64},
}
def make_source_capture_rework_db(tmp_path, *, mutate=None, events=None):
path=make_db(tmp_path)
result=source_capture_result()
verification={
"decision":"NEEDS_REWORK",
"reason":"SOURCE_CAPTURE_COMPLETE_RUNTIME_PROJECTION_REQUIRED",
}
if mutate:
mutate(result,verification)
event_types=events or [
"TASK_CLAIMED","TASK_HEARTBEAT","RESULT_SUBMITTED","TASK_VERIFIED"
]
con=sqlite3.connect(path)
con.execute(
"UPDATE tasks SET attempt=1,status='QUEUED',claimed_by=NULL,revision=5,"
"result_json=?,result_digest=?,result_idempotency_key=?,verification_json=?",
(
json.dumps(result,sort_keys=True,separators=(",",":")),
canon(result),"source-capture-result-v1",
json.dumps(verification,sort_keys=True,separators=(",",":")),
),
)
for event_type in event_types:
payload={}
if event_type=="TASK_VERIFIED":
payload={"decision":"NEEDS_REWORK","next_state":"QUEUED"}
con.execute(
"INSERT INTO events(task_id,agent_id,event_type,payload_json,created_at) "
"VALUES (?,?,?,?,?)",
(TASK_ID,"generic-worker",event_type,json.dumps(payload),"2026-08-20T00:01:00Z"),
)
con.commit();con.close()
return path,result,verification
def recovery_request(result,verification,*,digest=None,key="source-capture-recovery-a-v1"):
return SourceCaptureProjectionRecoveryRequest.from_mapping({
"expected_status":"QUEUED",
"expected_revision":5,
"expected_attempt":1,
"expected_metadata_sha256":canon({}),
"expected_required_capabilities_sha256":canon([]),
"previous_verification_sha256":canon(verification),
"expected_source_capture_result_sha256":canon(result),
"expected_source_capture_execution_digest":result["execution_digest"],
"expected_job_id":JOB_ID,
"expected_execution_digest":digest or envelope()["execution_digest"],
"required_capability":CAPABILITY,
"adapter":"openclaw",
"authorization_id":AUTHORIZATION,
"idempotency_key":key,
"reason":"first durable runtime projection after governed source capture",
})
def test_initial_projection_cas_persists_envelope_capability_and_event(tmp_path):
path=make_db(tmp_path)
result=InitialExecutionProjectionStore(path,resolver).project(TASK_ID,request())
assert result["revision"]==2
assert result["attempt"]==0
assert result["idempotent"] is False
con=sqlite3.connect(path)
row=con.execute(
"SELECT status,attempt,revision,claimed_by,metadata_json,required_capabilities_json FROM tasks"
).fetchone()
assert row[:4]==("QUEUED",0,2,None)
metadata=json.loads(row[4])
assert metadata["execution"]["action"]=="agent_runtime"
assert metadata["execution"]["adapter"]=="openclaw"
assert metadata["execution"]["governance"]["writer_lease"] is False
assert json.loads(row[5])==[CAPABILITY]
events=con.execute("SELECT event_type,payload_json FROM events ORDER BY event_id").fetchall()
assert [event[0] for event in events]==[
"TASK_CREATED","TASK_INITIAL_EXECUTION_PROJECTION_AUTHORIZED"
]
payload=json.loads(events[-1][1])
assert payload["job_id"]==JOB_ID
assert payload["main_write"] is False
assert payload["production_write"] is False
def test_initial_projection_idempotent_exact_replay(tmp_path):
path=make_db(tmp_path)
store=InitialExecutionProjectionStore(path,resolver)
first=store.project(TASK_ID,request())
second=store.project(TASK_ID,request())
assert first["idempotent"] is False
assert second["idempotent"] is True
assert second["execution_digest"]==first["execution_digest"]
con=sqlite3.connect(path)
assert con.execute("SELECT COUNT(*) FROM task_initial_execution_projections").fetchone()[0]==1
assert con.execute("SELECT COUNT(*) FROM events").fetchone()[0]==2
def test_source_capture_recovery_projects_first_runtime_envelope(tmp_path):
path,result,verification=make_source_capture_rework_db(tmp_path)
response=InitialExecutionProjectionStore(path,resolver).project_from_source_capture(
TASK_ID,recovery_request(result,verification)
)
assert response["revision"]==6
assert response["attempt"]==1
assert response["recovery_from_source_capture"] is True
assert response["idempotent"] is False
con=sqlite3.connect(path)
row=con.execute(
"SELECT status,attempt,revision,claimed_by,metadata_json,"
"required_capabilities_json,result_json,verification_json FROM tasks"
).fetchone()
assert row[:4]==("QUEUED",1,6,None)
metadata=json.loads(row[4])
assert metadata["execution"]["action"]=="agent_runtime"
assert metadata["execution"]["adapter"]=="openclaw"
assert metadata["execution"]["governance"]["writer_lease"] is False
assert json.loads(row[5])==[CAPABILITY]
assert json.loads(row[6])==result
assert json.loads(row[7])==verification
events=con.execute("SELECT event_type,payload_json FROM events ORDER BY event_id").fetchall()
assert [event[0] for event in events]==[
"TASK_CREATED","TASK_CLAIMED","TASK_HEARTBEAT","RESULT_SUBMITTED",
"TASK_VERIFIED","TASK_INITIAL_EXECUTION_PROJECTION_AUTHORIZED",
]
payload=json.loads(events[-1][1])
assert payload["recovery_from_source_capture"] is True
assert payload["source_capture_execution_digest"]==result["execution_digest"]
assert payload["main_write"] is False
assert payload["production_write"] is False
def test_source_capture_recovery_idempotent_exact_replay(tmp_path):
path,result,verification=make_source_capture_rework_db(tmp_path)
store=InitialExecutionProjectionStore(path,resolver)
first=store.project_from_source_capture(TASK_ID,recovery_request(result,verification))
second=store.project_from_source_capture(TASK_ID,recovery_request(result,verification))
assert first["idempotent"] is False
assert second["idempotent"] is True
assert second["execution_digest"]==first["execution_digest"]
con=sqlite3.connect(path)
assert con.execute(
"SELECT COUNT(*) FROM task_source_capture_projection_recoveries"
).fetchone()[0]==1
assert con.execute("SELECT COUNT(*) FROM events").fetchone()[0]==6
@pytest.mark.parametrize(
"mutate,error",
[
(lambda result,verification: result.__setitem__("changed_files",["x"]),
"SOURCE_CAPTURE_RESULT_CONTRACT_INVALID"),
(lambda result,verification: result.__setitem__("job_id","job-other"),
"SOURCE_CAPTURE_RESULT_CONTRACT_INVALID"),
(lambda result,verification: verification.__setitem__("decision","SUCCEEDED"),
"SOURCE_CAPTURE_NEEDS_REWORK_VERIFICATION_REQUIRED"),
],
)
def test_source_capture_recovery_rejects_unsafe_result_or_verification(
tmp_path,mutate,error
):
path,result,verification=make_source_capture_rework_db(tmp_path,mutate=mutate)
with pytest.raises(InitialExecutionProjectionError,match=error):
InitialExecutionProjectionStore(path,resolver).project_from_source_capture(
TASK_ID,recovery_request(result,verification)
)
@pytest.mark.parametrize(
"events",
[
["TASK_CLAIMED","RESULT_SUBMITTED","TASK_VERIFIED"],
["TASK_CLAIMED","TASK_HEARTBEAT","RESULT_SUBMITTED","TASK_VERIFIED","TASK_CLAIMED"],
["TASK_CLAIMED","TASK_HEARTBEAT","TASK_VERIFIED","RESULT_SUBMITTED"],
],
)
def test_source_capture_recovery_rejects_invalid_event_chain(tmp_path,events):
path,result,verification=make_source_capture_rework_db(tmp_path,events=events)
with pytest.raises(
InitialExecutionProjectionError,match="SOURCE_CAPTURE_RECOVERY_EVENT_CHAIN_INVALID"
):
InitialExecutionProjectionStore(path,resolver).project_from_source_capture(
TASK_ID,recovery_request(result,verification)
)
def test_source_capture_recovery_rejects_execution_digest_drift(tmp_path):
path,result,verification=make_source_capture_rework_db(tmp_path)
with pytest.raises(InitialExecutionProjectionError,match="EXECUTION_DIGEST_DRIFT"):
InitialExecutionProjectionStore(path,resolver).project_from_source_capture(
TASK_ID,recovery_request(result,verification,digest="f"*64)
)
def test_source_capture_recovery_request_is_strict(tmp_path):
_path,result,verification=make_source_capture_rework_db(tmp_path)
data=recovery_request(result,verification).public()
data["metadata_patch"]={"execution":"forbidden"}
with pytest.raises(InitialExecutionProjectionError,match="FIELDS_INVALID"):
SourceCaptureProjectionRecoveryRequest.from_mapping(data)
@pytest.mark.parametrize(
"kwargs,error",
[
({"claimed_by":"worker-a"},"TASK_ALREADY_CLAIMED"),
({"lease":True},"ACTIVE_LEASE_DENY"),
({"metadata":{"execution":{}}},"TASK_EXECUTION_ALREADY_PROJECTED"),
({"caps":["git.read"]},"TASK_INITIAL_CAPABILITIES_MUST_BE_EMPTY"),
({"event":"TASK_CLAIMED"},"TASK_INITIAL_EVENT_CHAIN_REQUIRED"),
],
)
def test_initial_projection_rejects_noninitial_state(tmp_path,kwargs,error):
path=make_db(tmp_path,**kwargs)
with pytest.raises(InitialExecutionProjectionError,match=error):
InitialExecutionProjectionStore(path,resolver).project(TASK_ID,request())
def test_initial_projection_rejects_binding_drift(tmp_path):
path=make_db(tmp_path)
def wrong(*_args):
return envelope(job_id="job-other")
with pytest.raises(InitialExecutionProjectionError,match="GOVERNED_BINDING_CONFLICT"):
InitialExecutionProjectionStore(path,wrong).project(TASK_ID,request())
def test_initial_projection_rejects_nonruntime_envelope(tmp_path):
path=make_db(tmp_path)
def wrong(*_args):
return envelope(action="source_capture",capability="git.read")
with pytest.raises(InitialExecutionProjectionError):
InitialExecutionProjectionStore(path,wrong).project(TASK_ID,request())
def test_initial_projection_rejects_execution_digest_drift(tmp_path):
path=make_db(tmp_path)
with pytest.raises(InitialExecutionProjectionError,match="EXECUTION_DIGEST_DRIFT"):
InitialExecutionProjectionStore(path,resolver).project(
TASK_ID,request(digest="f"*64)
)
def test_request_is_narrow_and_initial_state_bound():
data=request().public()
data["arbitrary_metadata"]={"x":1}
with pytest.raises(InitialExecutionProjectionError,match="FIELDS_INVALID"):
InitialExecutionProjectionRequest.from_mapping(data)
data=request().public();data["expected_attempt"]=1
with pytest.raises(InitialExecutionProjectionError,match="INITIAL_STATE_REQUIRED"):
InitialExecutionProjectionRequest.from_mapping(data)
def test_initial_projection_unit_is_unprivileged_and_owner_credential_bound():
unit=(ROOT/"deploy/systemd/aios-agent-task-initial-execution-projection.service").read_text()
assert "User=aios-agent-task" in unit
assert "Group=aios-agent-task" in unit
assert "LoadCredential=agent_task_control_token:" in unit
assert "Environment=AIOS_GOVERNED_WRITE_API_URL=http://127.0.0.1:18892" in unit
assert "RestrictAddressFamilies=AF_UNIX AF_INET" in unit
assert "ReadWritePaths=/var/lib/aios-agent-task" in unit
assert "User=root" not in unit
+201
View File
@@ -0,0 +1,201 @@
from __future__ import annotations
import importlib.util
import json
import os
from pathlib import Path
import socket
import subprocess
import sys
import time
import pytest
BROKER_PATH = Path(__file__).parents[1] / "services" / "aios-agent-runtime-broker" / "broker.py"
def load_broker():
spec = importlib.util.spec_from_file_location("broker_under_test", BROKER_PATH)
module = importlib.util.module_from_spec(spec)
assert spec.loader is not None
spec.loader.exec_module(module)
return module
def test_peer_credentials_accept_configured_uid(monkeypatch) -> None:
broker = load_broker()
left, right = socket.socketpair()
try:
monkeypatch.setattr(broker, "ALLOWED_PEER_UID", os.getuid())
monkeypatch.setattr(broker, "ALLOWED_PEER_USER", "")
broker.authorize_peer(left)
finally:
left.close()
right.close()
def test_peer_credentials_reject_other_uid(monkeypatch) -> None:
broker = load_broker()
left, right = socket.socketpair()
try:
monkeypatch.setattr(broker, "ALLOWED_PEER_UID", os.getuid() + 100000)
monkeypatch.setattr(broker, "ALLOWED_PEER_USER", "")
with pytest.raises(broker.BrokerError) as caught:
broker.authorize_peer(left)
assert caught.value.code == "PEER_UID_REJECTED"
finally:
left.close()
right.close()
def test_request_read_timeout(monkeypatch) -> None:
broker = load_broker()
left, right = socket.socketpair()
try:
monkeypatch.setattr(broker, "CONNECTION_TIMEOUT", 0.05)
with pytest.raises(broker.BrokerError) as caught:
broker.read_request(left)
assert caught.value.code == "REQUEST_TIMEOUT"
finally:
left.close()
right.close()
def test_worktree_fd_remains_pinned_after_path_replacement(tmp_path: Path, monkeypatch) -> None:
broker = load_broker()
root = tmp_path / "workspaces"
run_root = root / "run"
original = run_root / "worktree"
original.mkdir(parents=True)
(original / "marker.txt").write_text("original\n", encoding="utf-8")
request = {
"schema": "aios.online-agent.runtime-request.v1",
"adapter": "hermes",
"execution_context": {
"repository": "aios/aios",
"base_oid": "a" * 40,
"target_branch": "agent/intent/test",
"worktree": str(original),
},
"policy": {
"authoritative_repository_write": False,
"direct_git_ref_mutation": False,
"direct_git_push": False,
"direct_git_commit": False,
"main_write": False,
"force_push": False,
"production_write": False,
"tag_write": False,
"baseline_write": False,
"task_api_credential_visible": False,
"writer_lease_visible": False,
},
}
monkeypatch.setattr(broker, "ADAPTER", "hermes")
monkeypatch.setattr(broker, "WORKSPACE_ROOT", root)
_, fd = broker.validate_request(request)
try:
moved = run_root / "original-moved"
original.rename(moved)
original.mkdir()
(original / "marker.txt").write_text("replacement\n", encoding="utf-8")
pinned = Path(f"/proc/self/fd/{fd}")
(pinned / "through-fd.txt").write_text("pinned\n", encoding="utf-8")
assert (moved / "through-fd.txt").read_text(encoding="utf-8") == "pinned\n"
assert not (original / "through-fd.txt").exists()
finally:
os.close(fd)
def test_backend_receives_no_task_api_token(tmp_path: Path, monkeypatch) -> None:
broker = load_broker()
worktree = tmp_path / "worktree"
worktree.mkdir()
backend = tmp_path / "backend.py"
backend.write_text(
"""#!/usr/bin/env python3
import json, os, pathlib, sys
request = json.load(sys.stdin)
assert 'AIOS_AGENT_TASK_TOKEN' not in os.environ
worktree = pathlib.Path(request['execution_context']['worktree'])
(worktree / 'changed.txt').write_text('ok\\n', encoding='utf-8')
json.dump({
'schema':'aios.online-agent.runtime-result.v1',
'status':'COMPLETED',
'summary':'done',
'verification':[],
'evidence':{'token_visible':False, 'api_key':'runtime-secret-value'}
}, sys.stdout)
""",
encoding="utf-8",
)
backend.chmod(0o755)
monkeypatch.setattr(broker, "ADAPTER", "hermes")
monkeypatch.setattr(broker, "BACKEND_ARGV_JSON", json.dumps(["/usr/bin/python3", str(backend)]))
monkeypatch.setattr(broker, "BACKEND_TIMEOUT", 5)
monkeypatch.delenv("CREDENTIALS_DIRECTORY", raising=False)
fd = os.open(str(worktree), getattr(os, "O_PATH", os.O_RDONLY) | os.O_DIRECTORY)
try:
request = {
"schema": "aios.online-agent.runtime-request.v1",
"adapter": "hermes",
"execution_context": {"worktree": str(worktree)},
}
result = broker.execute_backend(request, fd)
finally:
os.close(fd)
assert result["status"] == "COMPLETED"
assert result["evidence"]["api_key"] == "[REDACTED]"
assert (worktree / "changed.txt").read_text(encoding="utf-8") == "ok\n"
def test_broker_backend_env_does_not_inherit_task_token(tmp_path: Path, monkeypatch) -> None:
broker = load_broker()
worktree = tmp_path / "worktree-env"
worktree.mkdir()
backend = tmp_path / "backend-env.py"
backend.write_text(
"""#!/usr/bin/env python3
import json, os, sys
assert 'AIOS_AGENT_TASK_TOKEN' not in os.environ
assert os.environ.get('AIOS_OPENCLAW_MODEL') == 'test/model'
assert os.environ.get('AIOS_OPENCLAW_BIN') == '/test/openclaw'
json.dump({
'schema':'aios.online-agent.runtime-result.v1',
'status':'COMPLETED',
'summary':'environment isolated',
'verification':[],
'evidence':{'task_token_visible':False}
}, sys.stdout)
""",
encoding="utf-8",
)
backend.chmod(0o755)
monkeypatch.setattr(broker, "ADAPTER", "openclaw")
monkeypatch.setattr(
broker,
"BACKEND_ARGV_JSON",
json.dumps(["/usr/bin/python3", str(backend)]),
)
monkeypatch.setenv("AIOS_AGENT_TASK_TOKEN", "must-not-cross")
monkeypatch.setenv("AIOS_OPENCLAW_MODEL", "test/model")
monkeypatch.setenv("AIOS_OPENCLAW_BIN", "/test/openclaw")
monkeypatch.delenv("CREDENTIALS_DIRECTORY", raising=False)
fd = os.open(str(worktree), getattr(os, "O_PATH", os.O_RDONLY) | os.O_DIRECTORY)
try:
request = {
"schema": "aios.online-agent.runtime-request.v1",
"adapter": "openclaw",
"execution_context": {"worktree": str(worktree)},
}
result = broker.execute_backend(request, fd)
finally:
os.close(fd)
assert result["status"] == "COMPLETED"
+143
View File
@@ -0,0 +1,143 @@
from __future__ import annotations
from pathlib import Path
import subprocess
import pytest
from intent_runtime import IntentError, IntentTask, is_claimable_intent, resolve
def make_bare(tmp_path: Path) -> tuple[Path, str]:
work = tmp_path / "work"
bare = tmp_path / "repo.git"
subprocess.run(["git", "init", str(work)], check=True, capture_output=True)
subprocess.run(["git", "-C", str(work), "config", "user.email", "test@example.invalid"], check=True)
subprocess.run(["git", "-C", str(work), "config", "user.name", "AIOS Test"], check=True)
(work / "README.md").write_text("base\n", encoding="utf-8")
subprocess.run(["git", "-C", str(work), "add", "README.md"], check=True)
subprocess.run(["git", "-C", str(work), "commit", "-m", "base"], check=True, capture_output=True)
subprocess.run(["git", "-C", str(work), "branch", "-M", "main"], check=True)
oid = subprocess.check_output(["git", "-C", str(work), "rev-parse", "HEAD"], text=True).strip()
subprocess.run(["git", "clone", "--bare", str(work), str(bare)], check=True, capture_output=True)
return bare, oid
def intent_task(task_id: str = "custom-gpt-intent-20260815") -> dict[str, object]:
return {
"task_id": task_id,
"goal": "Fix the repository behavior described by the task.",
"instructions": "Inspect the code, find the root cause, implement and test the smallest correct fix.",
"success_criteria": ["Relevant tests pass", "git diff --check passes"],
"required_capabilities": [
"intent.task",
"engineering.execute",
"engineering.autonomous",
"git.read",
"git.diff",
"git.test",
"governed.write.file",
"governed.write.branch",
"workspace.worktree",
"agent.runtime.hermes",
],
"metadata": {},
}
def fixed_implementation_task() -> dict[str, object]:
return {
"task_id": "implement-autonomous-intent-worker-v2-20260815",
"goal": "Implement autonomous intent worker v2.",
"instructions": "Implement the worker.",
"success_criteria": ["Implementation succeeds"],
"required_capabilities": [
"engineering.execute",
"git.diff",
"git.read",
"git.test",
"governed.write.branch",
"governed.write.file",
"workspace.worktree",
],
"metadata": {},
}
def test_fixed_bootstrap_task_is_not_claimable_by_intent_worker() -> None:
worker_caps = intent_task()["required_capabilities"]
assert is_claimable_intent(fixed_implementation_task(), worker_caps) is False
with pytest.raises(IntentError) as caught:
IntentTask.parse(fixed_implementation_task())
assert caught.value.code == "INTENT_CAPABILITY_MARKER_REQUIRED"
def test_native_custom_gpt_intent_task_is_claimable() -> None:
task = intent_task()
assert is_claimable_intent(task, task["required_capabilities"]) is True
def test_missing_runtime_capability_fails_closed() -> None:
task = intent_task()
caps = [item for item in task["required_capabilities"] if item != "agent.runtime.hermes"]
assert is_claimable_intent(task, caps) is False
def test_resolution_is_stable_across_restart(tmp_path: Path) -> None:
bare, oid = make_bare(tmp_path)
task = intent_task()
first = resolve(
task,
authority_git_dir=bare,
repository="aios/aios",
base_ref="refs/heads/main",
available_adapters=["hermes"],
default_adapter="hermes",
)
recovered = resolve(
task,
authority_git_dir=bare,
repository="aios/aios",
base_ref="refs/heads/main",
available_adapters=["hermes"],
default_adapter="hermes",
prior=first.to_json(),
)
assert first == recovered
assert recovered.base_oid == oid
assert recovered.target_branch.startswith("agent/intent/")
def test_requested_codex_is_rejected_when_unavailable(tmp_path: Path) -> None:
bare, _ = make_bare(tmp_path)
task = intent_task("codex-only-20260815")
task["required_capabilities"] = [
item for item in task["required_capabilities"] if item != "agent.runtime.hermes"
] + ["agent.runtime.codex"]
with pytest.raises(IntentError) as caught:
resolve(
task,
authority_git_dir=bare,
repository="aios/aios",
base_ref="refs/heads/main",
available_adapters=["hermes"],
default_adapter="hermes",
)
assert caught.value.code == "AGENT_RUNTIME_UNAVAILABLE"
def test_openclaw_is_a_first_class_runtime_adapter(tmp_path: Path) -> None:
bare, _ = make_bare(tmp_path)
task = intent_task("openclaw-intent-20260815")
task["required_capabilities"] = [
item for item in task["required_capabilities"] if item != "agent.runtime.hermes"
] + ["agent.runtime.openclaw"]
resolved = resolve(
task,
authority_git_dir=bare,
repository="aios/aios",
base_ref="refs/heads/main",
available_adapters=["openclaw"],
default_adapter="openclaw",
)
assert resolved.adapter == "openclaw"
+102
View File
@@ -0,0 +1,102 @@
from __future__ import annotations
import json
from pathlib import Path
import socket
import subprocess
import threading
import time
import pytest
from runtime_client import RuntimeErrorV2, collect_changes, invoke, request_payload, scan_secrets
def repo(tmp_path: Path) -> tuple[Path, str]:
work = tmp_path / "repo"
subprocess.run(["git", "init", str(work)], check=True, capture_output=True)
subprocess.run(["git", "-C", str(work), "config", "user.email", "test@example.invalid"], check=True)
subprocess.run(["git", "-C", str(work), "config", "user.name", "AIOS Test"], check=True)
(work / "a.py").write_text("VALUE = 1\n", encoding="utf-8")
subprocess.run(["git", "-C", str(work), "add", "a.py"], check=True)
subprocess.run(["git", "-C", str(work), "commit", "-m", "base"], check=True, capture_output=True)
oid = subprocess.check_output(["git", "-C", str(work), "rev-parse", "HEAD"], text=True).strip()
return work, oid
def test_collect_changes_reads_actual_git_state(tmp_path: Path) -> None:
work, oid = repo(tmp_path)
(work / "a.py").write_text("VALUE = 2\n", encoding="utf-8")
(work / "new.py").write_text("NEW = True\n", encoding="utf-8")
assert collect_changes(work, oid) == ["a.py", "new.py"]
def test_agent_commit_is_rejected(tmp_path: Path) -> None:
work, oid = repo(tmp_path)
(work / "a.py").write_text("VALUE = 2\n", encoding="utf-8")
subprocess.run(["git", "-C", str(work), "add", "a.py"], check=True)
subprocess.run(["git", "-C", str(work), "commit", "-m", "agent commit"], check=True, capture_output=True)
with pytest.raises(RuntimeErrorV2) as caught:
collect_changes(work, oid)
assert caught.value.code == "WORKTREE_HEAD_MUTATED"
def test_secret_material_is_rejected(tmp_path: Path) -> None:
work, oid = repo(tmp_path)
private_key_marker = "-----BEGIN PRIVATE " + "KEY-----"
(work / "new.txt").write_text(
private_key_marker + "\nabc\n", encoding="utf-8")
changed = collect_changes(work, oid)
with pytest.raises(RuntimeErrorV2) as caught:
scan_secrets(work, changed)
assert caught.value.code == "SECRET_MATERIAL_REJECTED"
def test_runtime_protocol_round_trip(tmp_path: Path) -> None:
sock = tmp_path / "runtime.sock"
response = {
"schema": "aios.online-agent.runtime-result.v1",
"status": "COMPLETED",
"summary": "Implemented the requested change.",
"verification": [],
"evidence": {"runtime": "fake-hermes"},
}
def server() -> None:
listener = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
listener.bind(str(sock))
listener.listen(1)
conn, _ = listener.accept()
raw = b""
while True:
chunk = conn.recv(65536)
if not chunk:
break
raw += chunk
value = json.loads(raw.decode("utf-8"))
assert value["schema"] == "aios.online-agent.runtime-request.v1"
conn.sendall(json.dumps(response).encode("utf-8"))
conn.close()
listener.close()
thread = threading.Thread(target=server)
thread.start()
for _ in range(100):
if sock.exists():
break
time.sleep(0.01)
payload = request_payload(
adapter="hermes",
task_id="intent-e2e-20260815",
goal="Change VALUE",
instructions="Inspect and implement",
success_criteria=["Tests pass"],
repository="aios/aios",
base_oid="a" * 40,
target_branch="agent/intent/example",
worktree=tmp_path,
)
result = invoke(sock, payload, 5)
thread.join(timeout=2)
assert result.status == "COMPLETED"
assert result.evidence["runtime"] == "fake-hermes"
@@ -1,5 +1,4 @@
import base64
import subprocess
from pathlib import Path
from aios_authority_git_executor import AuthorityGitExecutor
@@ -25,22 +24,6 @@ class Probe:
return True
def test_authority_create_task_branch_is_agent_scoped_and_idempotent(tmp_path):
bare = tmp_path / "repo.git"
subprocess.run(["git", "init", "--bare", str(bare)], check=True, capture_output=True)
source = tmp_path / "source"
subprocess.run(["git", "init", str(source)], check=True, capture_output=True)
(source / "README.md").write_text("fixture\n", encoding="utf-8")
subprocess.run(["git", "-C", str(source), "add", "README.md"], check=True)
subprocess.run(["git", "-C", str(source), "-c", "user.name=test", "-c", "user.email=test@example.invalid", "commit", "-m", "fixture"], check=True, capture_output=True)
base = subprocess.check_output(["git", "-C", str(source), "rev-parse", "HEAD"], text=True).strip()
executor = AuthorityGitExecutor(bare, tmp_path / "worktrees")
subprocess.run(["git", "-C", str(source), "push", str(bare), "HEAD:refs/heads/main"], check=True, capture_output=True)
assert executor.create_task_branch("refs/heads/agent/contract-test", base) == base
assert executor.create_task_branch("refs/heads/agent/contract-test", base) == base
assert executor.read_ref("refs/heads/agent/contract-test") == base
def test_create_new_lease_is_exact_base_and_absent():
request = WriterLeaseRequestV2(
task_id="TASK-P0-BRIDGE-V21-DURABLE-RECONCILIATION-20260809",
@@ -92,7 +75,6 @@ def test_slash_branch_uses_paginated_collection():
def test_http_route_contract_is_complete():
expected = {
"/v2/runtime", "/v2/capabilities", "/v2/governed-task",
"/v2/execution-envelope",
"/v2/writer-lease", "/v2/commit-file", "/v2/commit-files",
"/v2/job", "/v2/job/resume", "/v2/job/reconcile",
"/v2/tag/approval-request",
@@ -1,260 +0,0 @@
from __future__ import annotations
import json
from pathlib import Path
import sqlite3
import sys
import pytest
ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(ROOT / "active-runtime" / "app"))
from aios_agent_task_projection_recovery import (
CanonicalTaskProjectionIntent,
CanonicalTaskProjectionStore,
ProjectionRecoveryError,
ProjectionState,
sha256_json,
)
from aios_agent_task_projection_recovery_http import recover_from_json
TASK_ID = "TASK-CONV-V2R2C2-RECONCILE-SNAPSHOT-20260826"
JOB_ID = "job-9eee1142f2fc8bb7b571d065c01f40e0"
OID = "2afc2cf41fc9090cf01e3882d1b8db8d54cf1844"
RUNBOOK_SHA = "a500504a76fb99b26541eaf4edd842a942992ae170fc0f3838530189ca672320"
def make_db(tmp_path: Path) -> Path:
path = tmp_path / "state.sqlite3"
con = sqlite3.connect(path)
con.executescript(
"""
CREATE TABLE tasks(
task_id TEXT PRIMARY KEY,
goal TEXT NOT NULL,
instructions TEXT NOT NULL,
success_criteria_json TEXT NOT NULL,
required_capabilities_json TEXT NOT NULL,
constraints_json TEXT NOT NULL,
metadata_json TEXT NOT NULL,
deadline TEXT,
priority INTEGER NOT NULL,
max_attempts INTEGER NOT NULL,
attempt INTEGER NOT NULL DEFAULT 0,
status TEXT NOT NULL,
claimed_by TEXT,
lease_token_hash TEXT,
lease_expires_at TEXT,
result_json TEXT,
result_digest TEXT,
result_idempotency_key TEXT,
verification_json TEXT,
revision INTEGER NOT NULL DEFAULT 1,
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL
);
CREATE TABLE events(
event_id INTEGER PRIMARY KEY AUTOINCREMENT,
task_id TEXT,
agent_id TEXT,
event_type TEXT NOT NULL,
payload_json TEXT NOT NULL,
created_at TEXT NOT NULL
);
"""
)
con.commit()
con.close()
return path
def manifest() -> dict:
task = {
"task_id": TASK_ID,
"goal": "Complete V2R2C2 staging convergence",
"instructions": "Resume the immutable runbook and close the parent goal.",
"success_criteria": [{"id": "closure", "required": True, "expected": "PASS"}],
"constraints": {
"staging_only": True,
"production_write": False,
"main_direct_write": False,
},
"required_capabilities": [],
"metadata": {
"governed_task_id": TASK_ID,
"bridge_job_id": JOB_ID,
"runbook_sha256": RUNBOOK_SHA,
},
"priority": 100,
"max_attempts": 20,
"deadline": None,
}
return {
"schema": "aios.canonical-task-projection-intent.v1",
"projection_id": "projection-v2r2c2-parent-recovery-20260826",
"projection_generation": 1,
"idempotency_key": "projection-v2r2c2-parent-recovery-20260826-v1",
"task": task,
"governed_binding": {
"governed_task_id": TASK_ID,
"bridge_job_id": JOB_ID,
"repository": "aios/aios",
"branch": "agent/v2r2c2-convergence-integration-20260825",
"generation": 2,
"base_oid": OID,
"expected_branch_oid": OID,
},
"source_artifact": {
"ref": f"git:{OID}:runbooks/agent-v2r2c2-control-plane-convergence-20260825.md",
"sha256": RUNBOOK_SHA,
},
"task_payload_sha256": sha256_json(task),
}
def test_missing_task_is_recovered_with_same_identity(tmp_path):
db = make_db(tmp_path)
intent = CanonicalTaskProjectionIntent.from_mapping(manifest())
result = CanonicalTaskProjectionStore(db).reconcile(intent)
assert result["state"] == ProjectionState.CONVERGED.value
assert result["canonical_task_id"] == TASK_ID
assert result["bridge_job_id"] == JOB_ID
assert result["created_new_row"] is True
con = sqlite3.connect(db)
row = con.execute(
"SELECT task_id,status,attempt,revision,claimed_by,"
"required_capabilities_json,metadata_json FROM tasks WHERE task_id=?",
(TASK_ID,),
).fetchone()
assert row[:5] == (TASK_ID, "QUEUED", 0, 1, None)
assert json.loads(row[5]) == []
metadata = json.loads(row[6])
assert "execution" not in metadata
assert "execution_projection" not in metadata
events = con.execute(
"SELECT event_type FROM events WHERE task_id=? ORDER BY event_id",
(TASK_ID,),
).fetchall()
assert events == [("TASK_CREATED",)]
def test_replay_is_idempotent_and_creates_no_duplicate_event(tmp_path):
db = make_db(tmp_path)
intent = CanonicalTaskProjectionIntent.from_mapping(manifest())
store = CanonicalTaskProjectionStore(db)
first = store.reconcile(intent)
second = store.reconcile(intent)
assert first["idempotent"] is False
assert second["idempotent"] is True
con = sqlite3.connect(db)
assert con.execute("SELECT COUNT(*) FROM tasks").fetchone()[0] == 1
assert con.execute("SELECT COUNT(*) FROM events").fetchone()[0] == 1
assert con.execute(
"SELECT COUNT(*) FROM task_projection_receipts"
).fetchone()[0] == 1
def test_existing_matching_row_converges_without_second_task_created_event(tmp_path):
db = make_db(tmp_path)
intent = CanonicalTaskProjectionIntent.from_mapping(manifest())
store = CanonicalTaskProjectionStore(db)
store.reconcile(intent)
con = sqlite3.connect(db)
con.execute("DELETE FROM task_projection_receipts")
con.execute("DELETE FROM task_projection_intents")
con.commit()
con.close()
result = store.reconcile(intent)
assert result["created_new_row"] is False
con = sqlite3.connect(db)
assert con.execute("SELECT COUNT(*) FROM tasks").fetchone()[0] == 1
assert con.execute("SELECT COUNT(*) FROM events").fetchone()[0] == 1
def test_existing_different_task_payload_fails_closed(tmp_path):
db = make_db(tmp_path)
intent = CanonicalTaskProjectionIntent.from_mapping(manifest())
store = CanonicalTaskProjectionStore(db)
store.reconcile(intent)
con = sqlite3.connect(db)
con.execute("DELETE FROM task_projection_receipts")
con.execute("DELETE FROM task_projection_intents")
con.execute("UPDATE tasks SET goal='drifted'")
con.commit()
con.close()
with pytest.raises(
ProjectionRecoveryError,
match="CANONICAL_TASK_PROJECTION_IDENTITY_CONFLICT",
):
store.reconcile(intent)
def test_intent_replay_with_same_generation_but_different_request_conflicts(tmp_path):
db = make_db(tmp_path)
first = manifest()
intent = CanonicalTaskProjectionIntent.from_mapping(first)
store = CanonicalTaskProjectionStore(db)
store.persist_intent(intent)
other = manifest()
other["task"]["instructions"] = "different"
other["task_payload_sha256"] = sha256_json(other["task"])
other_intent = CanonicalTaskProjectionIntent.from_mapping(other)
with pytest.raises(
ProjectionRecoveryError,
match="PROJECTION_INTENT_IDEMPOTENCY_CONFLICT",
):
store.persist_intent(other_intent)
def test_recovery_refuses_non_pristine_execution_metadata(tmp_path):
value = manifest()
value["task"]["metadata"]["execution"] = {"action": "agent_runtime"}
value["task_payload_sha256"] = sha256_json(value["task"])
with pytest.raises(
ProjectionRecoveryError,
match="TASK_RECOVERY_REQUIRES_PRISTINE_METADATA",
):
CanonicalTaskProjectionIntent.from_mapping(value)
def test_recovery_refuses_runtime_capability_in_pristine_row(tmp_path):
value = manifest()
value["task"]["required_capabilities"] = ["agent.runtime.openclaw"]
value["task_payload_sha256"] = sha256_json(value["task"])
with pytest.raises(
ProjectionRecoveryError,
match="TASK_RECOVERY_REQUIRES_PRISTINE_CAPABILITIES",
):
CanonicalTaskProjectionIntent.from_mapping(value)
def test_http_adapter_rejects_path_body_task_mismatch(tmp_path):
db = make_db(tmp_path)
status, body = recover_from_json(db, "TASK-other-123", manifest())
assert status == 409
assert body["error"] == "PROJECTION_TASK_IDENTITY_MISMATCH"
def test_projection_readback_contains_durable_mapping_and_receipt(tmp_path):
db = make_db(tmp_path)
intent = CanonicalTaskProjectionIntent.from_mapping(manifest())
store = CanonicalTaskProjectionStore(db)
store.reconcile(intent)
projection = store.get_projection(TASK_ID)
assert projection is not None
assert projection["task_id"] == TASK_ID
assert projection["bridge_job_id"] == JOB_ID
assert projection["state"] == "CONVERGED"
assert projection["receipt"]["canonical_task_id"] == TASK_ID
@@ -1,333 +0,0 @@
from __future__ import annotations
import json
import sqlite3
from concurrent.futures import ThreadPoolExecutor
from pathlib import Path
from types import SimpleNamespace
import pytest
from aios_gitops_job_controller import BindingConflict, GitOpsJobController, JobError, JobState, JobStore, sha256_json
TASK = "TASK-CONV-V2R2C2-RECONCILE-SNAPSHOT-20260826"
EXTERNAL = "job-external-v7"
BRANCH = "agent/v2r2c2-convergence-integration-20260825"
BASE = "e" * 40
COMMIT = "1" * 40
def make_store(tmp_path: Path) -> tuple[GitOpsJobController, str, dict]:
store = JobStore(tmp_path / "job-root")
controller = GitOpsJobController(store)
request = {
"idempotency_key": "v7-replay",
"campaign_id": TASK,
"task_id": TASK,
"external_job_id": EXTERNAL,
"repository": "aios/aios",
"operation": "CONTINUATION_COMMIT_FILES",
"branch": BRANCH,
"target_branch": BRANCH,
"branch_mode": "CONTINUE_EXISTING",
"base_oid": BASE,
"lineage_anchor_oid": BASE,
"expected_branch_oid": BASE,
"generation": 2,
"payload_digest": "a" * 64,
"files_digest": "b" * 64,
"message_digest": "c" * 64,
"worker_principal": "aios-autonomy-agent",
"worker_lineage": "governed-continuation",
}
receipt = controller.submit(request)
controller.advance(receipt.job_id, JobState.AUTHORIZED, side_effect_state="AUTHORIZED")
row = store.read(receipt.job_id)
controller.lease_job(
receipt.job_id,
lease_id="lease-v7-epoch-3",
lineage_anchor_oid=BASE,
current_head_oid=BASE,
generation=2,
lease_expires_at="2099-01-01T00:00:00Z",
expected_revision=row["revision"],
)
with store._connect() as connection:
connection.execute("ALTER TABLE branch_leases ADD COLUMN epoch INTEGER")
connection.execute("UPDATE branch_leases SET epoch=3, state='USED' WHERE job_id=?", (receipt.job_id,))
connection.execute("CREATE TABLE branch_lease_events (lease_id TEXT NOT NULL, state TEXT NOT NULL, current_head_oid TEXT NOT NULL, generation INTEGER NOT NULL)")
connection.execute("INSERT INTO branch_lease_events VALUES (?, 'USED', ?, 2)", ("lease-v7-epoch-3", BASE))
controller.advance(receipt.job_id, JobState.WORKTREE_READY, side_effect_state="WORKTREE_READY")
controller.advance(receipt.job_id, JobState.EXECUTING, side_effect_state="IN_PROGRESS")
completed = {
"commit_oid": COMMIT,
"authority_oid": COMMIT,
"remote_verified": True,
"worker_principal": request["worker_principal"],
"worker_lineage": request["worker_lineage"],
}
controller.advance(receipt.job_id, JobState.REMOTE_VERIFIED, side_effect_state="REMOTE_VERIFIED", receipt=completed)
final = controller.advance(receipt.job_id, JobState.COMPLETED, side_effect_state="REMOTE_VERIFIED", receipt=completed)
row = store.read(receipt.job_id)
envelope = {
"task_id": TASK,
"external_job_id": EXTERNAL,
"internal_job_id": receipt.job_id,
"repository": "aios/aios",
"target_branch": BRANCH,
"generation": 2,
"branch_mode": "CONTINUE_EXISTING",
"payload_digest": request["payload_digest"],
"files_digest": request["files_digest"],
"message_digest": request["message_digest"],
"commit_oid": COMMIT,
"authority_head_oid": COMMIT,
"worker_principal": request["worker_principal"],
"worker_lineage": request["worker_lineage"],
"source_lease_epoch": 3,
"historical_lease_head_oid": BASE,
"durable_revision": row["revision"],
"completion_identity": sha256_json({"job_id": receipt.job_id, "revision": row["revision"], "receipt": completed}),
"reason": "COMPLETED_REPLAY_LEASE_EVIDENCE_RECONCILIATION",
"provenance": {"source": "v7-test", "authority_readback": True},
}
assert final.state is JobState.COMPLETED
return controller, receipt.job_id, envelope
def test_completed_replay_is_append_only_and_exactly_idempotent(tmp_path: Path):
controller, job_id, envelope = make_store(tmp_path)
with controller.store._connect() as connection:
before = dict(connection.execute("SELECT * FROM branch_leases WHERE job_id=?", (job_id,)).fetchone())
before_events = [dict(row) for row in connection.execute("SELECT * FROM branch_lease_events WHERE lease_id=?", ("lease-v7-epoch-3",)).fetchall()]
old_receipt = connection.execute("SELECT receipt_json FROM jobs WHERE job_id=?", (job_id,)).fetchone()[0]
first = controller.store.append_completed_replay_reconciliation(job_id, envelope, authority_head_oid=COMMIT)
second = controller.store.append_completed_replay_reconciliation(job_id, envelope, authority_head_oid=COMMIT)
restarted = GitOpsJobController(JobStore(controller.store.root))
third = restarted.store.append_completed_replay_reconciliation(job_id, envelope, authority_head_oid=COMMIT)
assert first["reconciliation_id"] == second["reconciliation_id"] == third["reconciliation_id"]
assert first["current_head_oid"] == COMMIT
with controller.store._connect() as connection:
after = dict(connection.execute("SELECT * FROM branch_leases WHERE job_id=?", (job_id,)).fetchone())
after_events = [dict(row) for row in connection.execute("SELECT * FROM branch_lease_events WHERE lease_id=?", ("lease-v7-epoch-3",)).fetchall()]
new_receipt = connection.execute("SELECT receipt_json FROM jobs WHERE job_id=?", (job_id,)).fetchone()[0]
count = connection.execute("SELECT COUNT(*) FROM completed_replay_reconciliations WHERE job_id=?", (job_id,)).fetchone()[0]
assert before == after
assert before_events == after_events
assert old_receipt == new_receipt
assert count == 1
with pytest.raises(sqlite3.IntegrityError, match="COMPLETED_REPLAY_APPEND_ONLY"):
with controller.store._connect() as connection:
connection.execute(
"UPDATE completed_replay_reconciliations SET reason='tampered' WHERE job_id=?",
(job_id,),
)
with pytest.raises(sqlite3.IntegrityError, match="COMPLETED_REPLAY_APPEND_ONLY"):
with controller.store._connect() as connection:
connection.execute(
"DELETE FROM completed_replay_reconciliations WHERE job_id=?",
(job_id,),
)
def test_completed_replay_authority_conflict_fails_closed(tmp_path: Path):
controller, job_id, envelope = make_store(tmp_path)
with pytest.raises(BindingConflict, match="AUTHORITY_CONFLICT"):
controller.store.append_completed_replay_reconciliation(job_id, envelope, authority_head_oid="2" * 40)
def test_completed_replay_binding_conflict_fails_closed(tmp_path: Path):
controller, job_id, envelope = make_store(tmp_path)
conflicting = {**envelope, "files_digest": "d" * 64}
with pytest.raises(BindingConflict, match="BINDING_CONFLICT"):
controller.store.append_completed_replay_reconciliation(job_id, conflicting, authority_head_oid=COMMIT)
def test_identical_reconciliation_converges_under_contention(tmp_path: Path):
controller, job_id, envelope = make_store(tmp_path)
def run(_: int) -> str:
return controller.store.append_completed_replay_reconciliation(
job_id, envelope, authority_head_oid=COMMIT
)["reconciliation_id"]
with ThreadPoolExecutor(max_workers=8) as pool:
values = list(pool.map(run, range(16)))
assert set(values) == {values[0]}
assert controller.store.completed_replay_evidence(job_id)["commit_oid"] == COMMIT
def test_public_job_projection_exposes_external_reconciled_head(tmp_path: Path):
from aios_chatgpt_bridge_v2_server import GovernedRuntime
controller, job_id, envelope = make_store(tmp_path)
reconciliation = controller.store.append_completed_replay_reconciliation(
job_id, envelope, authority_head_oid=COMMIT
)
class Authority:
def read_ref(self, ref: str):
assert ref == "refs/heads/" + BRANCH
return COMMIT
runtime = object.__new__(GovernedRuntime)
runtime.authority = Authority()
runtime._controller_for_job = lambda _job_id: controller
result = GovernedRuntime.job(runtime, {"job_id": job_id}, "get")
assert result["job_id"] == EXTERNAL
assert result["current_head_oid"] == COMMIT
assert result["completed_replay_reconciliation_id"] == reconciliation["reconciliation_id"]
assert result["receipt"]["commit_oid"] == result["receipt"]["authority_oid"] == COMMIT
def test_writer_lease_admits_exact_completed_replay_without_new_lease(tmp_path: Path):
from aios_chatgpt_bridge_v2_server import GovernedRuntime
controller, job_id, envelope = make_store(tmp_path)
controller.store.append_completed_replay_reconciliation(
job_id, envelope, authority_head_oid=COMMIT
)
class Authority:
def read_ref(self, ref: str):
assert ref == "refs/heads/" + BRANCH
return COMMIT
runtime = object.__new__(GovernedRuntime)
runtime.config = SimpleNamespace(state_root=tmp_path)
runtime.controllers = {"job": controller}
runtime.authority = Authority()
body = {
"task_id": TASK,
"job_id": EXTERNAL,
"repository": "aios/aios",
"branch_mode": "CONTINUE_EXISTING",
"base_oid": BASE,
"expected_branch_oid": BASE,
"target_branch": BRANCH,
"generation": 2,
"lease_seconds": 900,
}
result = GovernedRuntime.acquire_lease(runtime, body)
assert result["completed_replay"] is True
assert result["lease_created"] is False
assert result["expected_branch_oid"] == BASE
assert result["current_head_oid"] == COMMIT
assert result["completed_replay_reconciliation_id"].startswith("reconcile-")
with controller.store._connect() as connection:
assert connection.execute(
"SELECT COUNT(*) FROM branch_leases WHERE job_id=?", (job_id,)
).fetchone()[0] == 1
def test_conflicting_reconciliation_has_no_ambiguous_winner(tmp_path: Path):
controller, job_id, envelope = make_store(tmp_path)
conflicting = {**envelope, "reason": "CONFLICTING_PROVENANCE"}
values = [envelope, conflicting] * 8
def run(candidate: dict) -> str:
try:
result = controller.store.append_completed_replay_reconciliation(
job_id, candidate, authority_head_oid=COMMIT
)
return "ok:" + result["identity_digest"]
except BindingConflict:
return "conflict"
with ThreadPoolExecutor(max_workers=8) as pool:
outcomes = list(pool.map(run, values))
winners = {item for item in outcomes if item.startswith("ok:")}
assert len(winners) == 1
assert "conflict" in outcomes
with controller.store._connect() as connection:
assert connection.execute(
"SELECT COUNT(*) FROM completed_replay_reconciliations WHERE job_id=?", (job_id,)
).fetchone()[0] == 1
def test_reconciliation_rejects_head_alias_before_append(tmp_path: Path):
controller, job_id, envelope = make_store(tmp_path)
conflicting = {**envelope, "historical_lease_head_oid": COMMIT}
with pytest.raises(BindingConflict, match="HISTORICAL_LEASE_CONFLICT"):
controller.store.append_completed_replay_reconciliation(
job_id, conflicting, authority_head_oid=COMMIT
)
with controller.store._connect() as connection:
assert connection.execute(
"SELECT COUNT(*) FROM completed_replay_reconciliations WHERE job_id=?",
(job_id,),
).fetchone()[0] == 0
def test_http_commit_allowlist_carries_replay_identity(tmp_path: Path):
from aios_chatgpt_bridge_v2_server import BridgeHandler
handler = object.__new__(BridgeHandler)
body = {
"task_id": TASK,
"job_id": EXTERNAL,
"source_lease_epoch": 3,
"repository": "aios/aios",
"expected_branch_oid": BASE,
"target_branch": BRANCH,
"generation": 2,
"idempotency_key": "v7-replay",
"files": [{"path": "x.txt", "content_base64": "eA=="}],
"message": "replay",
}
handler._allowed("commitFilesGoverned", body)
def test_cold_append_requires_existing_database(tmp_path: Path):
missing = tmp_path / "missing-job-root"
with pytest.raises(JobError, match="JOB_ROOT_NOT_FOUND"):
JobStore.for_existing_append(missing)
assert not missing.exists()
def test_real_http_post_carries_external_replay_identity(tmp_path: Path):
from http.client import HTTPConnection
from http.server import HTTPServer
from threading import Thread
from aios_chatgpt_bridge_v2_server import BridgeHandler
observed = {}
class Adapter:
def commitFilesGoverned(self, body):
observed.update(body)
return {"job_id": body["job_id"], "current_head_oid": COMMIT}
server = HTTPServer(("127.0.0.1", 0), BridgeHandler)
BridgeHandler.adapter = Adapter()
thread = Thread(target=server.serve_forever, daemon=True)
thread.start()
body = {
"task_id": TASK,
"job_id": EXTERNAL,
"source_lease_epoch": 3,
"repository": "aios/aios",
"expected_branch_oid": BASE,
"target_branch": BRANCH,
"generation": 2,
"idempotency_key": "v7-replay",
"files": [{"path": "x.txt", "content_base64": "eA=="}],
"message": "replay",
}
try:
connection = HTTPConnection("127.0.0.1", server.server_port, timeout=5)
connection.request("POST", "/v2/commit-files", body=json.dumps(body), headers={"Content-Type": "application/json"})
response = connection.getresponse()
payload = json.loads(response.read().decode())
connection.close()
finally:
server.shutdown()
server.server_close()
thread.join(timeout=5)
BridgeHandler.adapter = None
assert response.status == 200
assert observed["job_id"] == EXTERNAL
assert observed["source_lease_epoch"] == 3
assert payload["job_id"] == EXTERNAL
assert payload["current_head_oid"] == COMMIT
-157
View File
@@ -1,157 +0,0 @@
from __future__ import annotations
import sys
from pathlib import Path
import pytest
ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(ROOT / "active-runtime" / "app"))
from aios_convergence_contracts import (
AcceptanceCriterion,
AcceptanceDecision,
AcceptanceInvariant,
AcceptanceVector,
ContinuationManifest,
ContinuationStopReason,
ConvergenceContractError,
DesiredState,
EvidenceState,
ObservedHealth,
ObservedState,
ReconciliationState,
StateOwnership,
evaluate_acceptance,
ownership_index,
plan_reconciliation,
validate_state_ownership,
)
def test_acceptance_never_promotes_required_unknown():
vector = AcceptanceVector(
subject_type="goal",
subject_id="goal-1",
subject_revision=1,
criteria=(
AcceptanceCriterion(
criterion_id="runtime",
required=True,
state=EvidenceState.UNKNOWN,
verifier_id="runtime-proof",
),
),
)
result = evaluate_acceptance(vector)
assert result.decision == AcceptanceDecision.UNKNOWN
assert result.reasons == ("criterion_unknown:runtime",)
def test_acceptance_rejects_failed_invariant():
vector = AcceptanceVector(
subject_type="release",
subject_id="V2R2C2",
subject_revision=1,
criteria=(
AcceptanceCriterion("tests", True, EvidenceState.PASS, "pytest"),
),
invariants=(
AcceptanceInvariant("protected-ref", EvidenceState.FAIL),
),
)
assert evaluate_acceptance(vector).decision == AcceptanceDecision.REJECT
def test_acceptance_blocks_on_unresolved_blocker():
vector = AcceptanceVector(
subject_type="goal",
subject_id="goal-1",
subject_revision=1,
criteria=(AcceptanceCriterion("code", True, EvidenceState.PASS, "ci"),),
unresolved_blockers=("blocker-1",),
)
assert evaluate_acceptance(vector).decision == AcceptanceDecision.BLOCKED
def test_state_ownership_is_unique_and_final_writer_is_enforced():
custom = (
StateOwnership(
object_type="goal",
authoritative_service="goal-kernel",
authoritative_store="goal-store",
final_writer="goal-kernel",
),
)
validate_state_ownership("goal", "goal-kernel", custom, final_state=True)
with pytest.raises(ConvergenceContractError, match="STATE_OWNERSHIP_VIOLATION"):
validate_state_ownership("goal", "worker", custom, final_state=True)
with pytest.raises(ConvergenceContractError, match="duplicate_state_owner"):
ownership_index(custom + custom)
def test_missing_or_unknown_observation_never_converges():
desired = DesiredState("contract:gpt", "external_projection", 2, {"hash": "abc"})
missing = plan_reconciliation(desired, None)
assert missing.state == ReconciliationState.OBSERVING
assert missing.observation_required is True
unknown = ObservedState(
resource_id="contract:gpt",
observed_generation=None,
observed_spec={},
health=ObservedHealth.UNKNOWN,
source_identity="gpt-action",
observed_at="2026-08-25T00:00:00Z",
)
result = plan_reconciliation(desired, unknown)
assert result.state == ReconciliationState.OBSERVING
def test_equal_healthy_observation_converges_and_key_is_stable():
desired = DesiredState("worker:a", "worker_binding", 3, {"build": "x"})
observed = ObservedState(
resource_id="worker:a",
observed_generation=3,
observed_spec={"build": "x"},
health=ObservedHealth.HEALTHY,
source_identity="worker-registry",
observed_at="2026-08-25T00:00:00Z",
)
one = plan_reconciliation(desired, observed)
two = plan_reconciliation(desired, observed)
assert one.state == ReconciliationState.CONVERGED
assert one.idempotency_key == two.idempotency_key
def test_drift_is_diff_found_not_auto_applied():
desired = DesiredState("projection:openapi", "external_projection", 4, {"hash": "new"})
observed = ObservedState(
resource_id="projection:openapi",
observed_generation=3,
observed_spec={"hash": "old"},
health=ObservedHealth.HEALTHY,
source_identity="published-openapi",
observed_at="2026-08-25T00:00:00Z",
)
result = plan_reconciliation(desired, observed)
assert result.state == ReconciliationState.DIFF_FOUND
assert result.observation_required is False
def test_continuation_manifest_is_durable_and_hashable():
manifest = ContinuationManifest(
trace_id="trace-1",
goal_id="goal-1",
goal_revision=1,
task_id="task-1",
run_id="run-1",
current_node_id="node-4",
acceptance_vector_ref="acceptance:goal-1:1",
next_action="resume node-4",
stop_reason=ContinuationStopReason.CONTEXT_BOUNDARY,
checkpoint_ref="checkpoint-9",
unresolved_blocker_refs=("blocker-a",),
)
assert manifest.as_dict()["stop_reason"] == "context_boundary"
assert manifest.content_hash.startswith("sha256:")
@@ -1,506 +0,0 @@
from __future__ import annotations
import base64
import dataclasses
import hashlib
import importlib
import importlib.util
import json
import socket
import sys
import threading
import urllib.error
import urllib.request
from datetime import datetime, timedelta, timezone
from pathlib import Path
from types import SimpleNamespace
import pytest
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
APP = Path(__file__).resolve().parents[1] / "active-runtime" / "app"
AGENT_OS = Path(__file__).resolve().parents[1] / "runtime" / "agent-os"
for import_root in (APP, AGENT_OS):
if str(import_root) not in sys.path:
sys.path.insert(0, str(import_root))
from silent_release_hardening import ( # noqa: E402
BuilderAttestation,
BuilderAttestationKeyRing,
BuilderAttestationVerifier,
CapabilityAuthorizationError,
SilentReleaseActionService,
TrustedCandidateBuilderUnavailable,
)
import silent_release_hardening as hardening # noqa: E402
COMMIT = "a" * 40
TREE = "b" * 40
SHA = "c" * 64
GITEA = {"consistent": True}
def _af_inet_available() -> bool:
try:
probe = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
except OSError:
return False
probe.close()
return True
def _canonical_digest(value: dict) -> str:
return hashlib.sha256(
json.dumps(value, sort_keys=True, separators=(",", ":")).encode()
).hexdigest()
def _instant(delta: timedelta = timedelta()) -> str:
return (datetime.now(timezone.utc) + delta).isoformat().replace(
"+00:00", "Z"
)
def _keyring_payload(private: Ed25519PrivateKey, *, purpose: str) -> dict:
public = private.public_key().public_bytes(
serialization.Encoding.Raw, serialization.PublicFormat.Raw
)
record = {
"algorithm": "Ed25519",
"purpose": purpose,
"key_id": "builder-key-001",
"generation": 1,
"not_before": _instant(timedelta(minutes=-1)),
"expires_at": _instant(timedelta(days=1)),
"status": "ACTIVE",
"public_key": base64.b64encode(public).decode("ascii"),
"public_key_sha256": hashlib.sha256(public).hexdigest(),
}
payload = {
"algorithm": "Ed25519",
"purpose": "AIOS_TRUSTED_RELEASE_BUILDER",
"keys": [record],
}
payload["keyring_sha256"] = hashlib.sha256(
json.dumps(payload, sort_keys=True, separators=(",", ":")).encode("utf-8")
).hexdigest()
return payload
def _refresh_keyring_digest(payload: dict) -> dict:
canonical = {
"algorithm": payload["algorithm"],
"purpose": payload["purpose"],
"keys": payload["keys"],
}
payload["keyring_sha256"] = hashlib.sha256(
json.dumps(canonical, sort_keys=True, separators=(",", ":")).encode("utf-8")
).hexdigest()
return payload
def _attestation(private: Ed25519PrivateKey) -> BuilderAttestation:
unsigned = BuilderAttestation(
builder_attestation_id="builder-attestation-001",
builder_key_id="builder-key-001",
source_commit=COMMIT,
tree_oid=TREE,
artifact_sha256=SHA,
manifest_sha256=SHA,
package_closure_sha256=SHA,
test_evidence_sha256=SHA,
canary_evidence_sha256=SHA,
issued_at=_instant(),
signature="",
builder_key_generation=1,
signature_algorithm="Ed25519",
signature_purpose="AIOS_TRUSTED_RELEASE_BUILDER",
gitea_evidence_sha256=_canonical_digest(GITEA),
rollback_evidence_sha256=_canonical_digest({
"ready": True,
"rollback_sha256": SHA,
"source_commit": COMMIT,
}),
policy_evidence_sha256=SHA,
secret_scan_evidence_sha256=SHA,
)
return BuilderAttestation(
**{
**unsigned.canonical_payload,
"signature": base64.b64encode(
private.sign(unsigned.canonical_bytes())
).decode("ascii"),
}
)
def test_builder_verifier_rejects_owner_key_record_cross_purpose(tmp_path):
private = Ed25519PrivateKey.generate()
payload = _keyring_payload(private, purpose="owner_approval")
path = tmp_path / "builder-keyring.json"
path.write_text(json.dumps(payload), encoding="utf-8")
with pytest.raises(ValueError, match="purpose"):
BuilderAttestationKeyRing.from_file(path)
def test_builder_verifier_checks_domain_key_window_digest_and_signature(tmp_path):
private = Ed25519PrivateKey.generate()
path = tmp_path / "builder-keyring.json"
path.write_text(
json.dumps(_keyring_payload(private, purpose="AIOS_TRUSTED_RELEASE_BUILDER")),
encoding="utf-8",
)
verifier = BuilderAttestationVerifier(
BuilderAttestationKeyRing.from_file(path)
)
candidate = SimpleNamespace(
source_commit=COMMIT,
tree_oid=TREE,
artifact_sha256=SHA,
manifest_sha256=SHA,
gitea_consistency=GITEA,
rollback_ready=True,
)
plan = SimpleNamespace(
deployment=SimpleNamespace(
package_closure_sha256=SHA,
rollback_sha256=SHA,
),
policy_sha256=SHA,
)
assert verifier.verify(_attestation(private), candidate, plan).builder_key_id == "builder-key-001"
@pytest.mark.parametrize("state", ["REVOKED", "EXPIRED", "FUTURE"])
def test_builder_verifier_rejects_unavailable_key_lifecycle_states(tmp_path, state):
private = Ed25519PrivateKey.generate()
payload = _keyring_payload(private, purpose="AIOS_TRUSTED_RELEASE_BUILDER")
record = payload["keys"][0]
if state == "REVOKED":
record["status"] = "REVOKED"
elif state == "EXPIRED":
record["not_before"] = _instant(timedelta(days=-2))
record["expires_at"] = _instant(timedelta(days=-1))
else:
record["not_before"] = _instant(timedelta(days=1))
record["expires_at"] = _instant(timedelta(days=2))
path = tmp_path / "builder-keyring.json"
path.write_text(json.dumps(_refresh_keyring_digest(payload)), encoding="utf-8")
verifier = BuilderAttestationVerifier(BuilderAttestationKeyRing.from_file(path))
candidate = SimpleNamespace(
source_commit=COMMIT, tree_oid=TREE,
artifact_sha256=SHA, manifest_sha256=SHA,
gitea_consistency=GITEA, rollback_ready=True,
)
plan = SimpleNamespace(deployment=SimpleNamespace(package_closure_sha256=SHA, rollback_sha256=SHA), policy_sha256=SHA)
with pytest.raises(CapabilityAuthorizationError, match="validity|revoked|unavailable"):
verifier.verify(_attestation(private), candidate, plan)
def test_builder_verifier_rejects_unknown_key_invalid_signature_and_keyring_digest(tmp_path):
private = Ed25519PrivateKey.generate()
payload = _keyring_payload(private, purpose="AIOS_TRUSTED_RELEASE_BUILDER")
path = tmp_path / "builder-keyring.json"
path.write_text(json.dumps(payload), encoding="utf-8")
verifier = BuilderAttestationVerifier(BuilderAttestationKeyRing.from_file(path))
candidate = SimpleNamespace(
source_commit=COMMIT, tree_oid=TREE,
artifact_sha256=SHA, manifest_sha256=SHA,
gitea_consistency=GITEA, rollback_ready=True,
)
plan = SimpleNamespace(deployment=SimpleNamespace(package_closure_sha256=SHA, rollback_sha256=SHA), policy_sha256=SHA)
with pytest.raises(CapabilityAuthorizationError, match="not trusted"):
verifier.verify(
dataclasses.replace(_attestation(private), builder_key_id="unknown-builder-key"),
candidate,
plan,
)
with pytest.raises(CapabilityAuthorizationError, match="signature"):
verifier.verify(
dataclasses.replace(_attestation(private), signature=base64.b64encode(b"x" * 64).decode()),
candidate,
plan,
)
payload["keyring_sha256"] = "0" * 64
path.write_text(json.dumps(payload), encoding="utf-8")
with pytest.raises(ValueError, match="keyring digest"):
BuilderAttestationKeyRing.from_file(path)
def test_builder_attestation_rejects_tampered_digest_and_signing_metadata():
private = Ed25519PrivateKey.generate()
raw = _attestation(private).to_dict()
raw["fingerprint"] = "0" * 64
with pytest.raises(CapabilityAuthorizationError, match="digest is invalid"):
BuilderAttestation.from_dict(raw, require_fingerprint=True)
@pytest.mark.parametrize(
"field,value",
[
("builder_key_generation", 2),
("signature_algorithm", "RSA"),
("signature_purpose", "owner_approval"),
],
)
def test_builder_keyring_rejects_attestation_metadata_mismatch(tmp_path, field, value):
private = Ed25519PrivateKey.generate()
path = tmp_path / "builder-keyring.json"
path.write_text(
json.dumps(_keyring_payload(private, purpose="AIOS_TRUSTED_RELEASE_BUILDER")),
encoding="utf-8",
)
verifier = BuilderAttestationVerifier(
BuilderAttestationKeyRing.from_file(path)
)
candidate = SimpleNamespace(
source_commit=COMMIT, tree_oid=TREE,
artifact_sha256=SHA, manifest_sha256=SHA,
gitea_consistency=GITEA, rollback_ready=True,
)
plan = SimpleNamespace(deployment=SimpleNamespace(package_closure_sha256=SHA, rollback_sha256=SHA), policy_sha256=SHA)
with pytest.raises(CapabilityAuthorizationError, match="metadata"):
verifier.verify(
dataclasses.replace(_attestation(private), **{field: value}),
candidate,
plan,
)
raw = _attestation(private).to_dict()
raw["builder_key_generation"] = 2
with pytest.raises(CapabilityAuthorizationError, match="digest is invalid"):
BuilderAttestation.from_dict(raw, require_fingerprint=True)
def test_builder_unavailable_intent_is_bounded_retryable_and_recovers_without_secret(tmp_path, monkeypatch):
class UnavailableBuilder:
def build(self, intent):
raise TrustedCandidateBuilderUnavailable("socket token=should-not-leak")
monkeypatch.setattr(hardening, "_fsync_directory", lambda _path: None)
service = SilentReleaseActionService(
tmp_path,
candidate_builder=UnavailableBuilder(),
max_builder_attempts=2,
)
payload = {"event_id": "event-builder-001", "source_commit": COMMIT}
first = service.create_release_intent(payload)
second = service.create_release_intent(payload)
assert first["status"] == "BUILDER_RETRYABLE_FAILURE"
assert first["retryable"] is True
assert first["attempt_count"] == 1
assert "should-not-leak" not in json.dumps(first)
assert second["status"] == "BUILDER_TERMINAL_FAILURE"
assert second["retryable"] is False
assert second["attempt_count"] == 2
def test_stale_building_intent_recovers_after_restart_without_double_running_active_attempt(tmp_path, monkeypatch):
class CountingUnavailableBuilder:
calls = 0
def build(self, intent):
self.calls += 1
raise TrustedCandidateBuilderUnavailable("nonsecret")
monkeypatch.setattr(hardening, "_fsync_directory", lambda _path: None)
builder = CountingUnavailableBuilder()
service = SilentReleaseActionService(
tmp_path,
candidate_builder=builder,
max_builder_attempts=3,
builder_attempt_timeout_seconds=1,
)
payload = {"event_id": "event-builder-crash-001", "source_commit": COMMIT}
intent_id = "intent-" + hashlib.sha256(
f"{payload['event_id']}\0{COMMIT}".encode()
).hexdigest()[:24]
intent_path = tmp_path / "intents" / f"{intent_id}.json"
intent_path.parent.mkdir(parents=True)
active_record = {
"attempt_count": 1,
"attempt_started_at": _instant(),
"created_at": _instant(timedelta(minutes=-1)),
"event_id": payload["event_id"],
"intent_id": intent_id,
"source_commit": COMMIT,
"status": "BUILDING",
}
intent_path.write_text(json.dumps(active_record), encoding="utf-8")
active = service.create_release_intent(payload)
assert active["status"] == "BUILDING"
assert active["idempotent"] is True
assert builder.calls == 0
active_record["attempt_started_at"] = _instant(timedelta(minutes=-5))
intent_path.write_text(json.dumps(active_record), encoding="utf-8")
recovered = service.create_release_intent(payload)
assert recovered["status"] == "BUILDER_RETRYABLE_FAILURE"
assert recovered["attempt_count"] == 2
assert recovered["failure_code"] == "TRUSTED_BUILDER_UNAVAILABLE"
assert recovered["last_recovery_code"] == "TRUSTED_BUILDER_CRASH_RECOVERED"
assert recovered["recovery_count"] == 1
assert builder.calls == 1
@pytest.mark.skipif(sys.platform == "win32", reason="model API runtime requires POSIX fcntl")
@pytest.mark.skipif(
not _af_inet_available(),
reason="isolated executor permits AF_UNIX only; live HTTP probe runs at activation",
)
def test_model_api_keeps_process_liveness_separate_from_builder_readiness(monkeypatch):
api = importlib.import_module("aios_model_api_staging")
class FakeService:
ready = False
def get_builder_readiness(self):
return {"status": "READY" if self.ready else "UNAVAILABLE"}
def create_release_candidate(self, payload):
if not payload:
raise ValueError("event_id is required")
return {"status": "READY_FOR_APPROVAL"}
service = FakeService()
monkeypatch.setattr(api, "silent_release_service", lambda: service)
monkeypatch.setattr(
api,
"authenticate",
lambda token: (
{"account": "test", "role": "owner", "scopes": ["release:candidate"]}
if token == "test-token"
else None
),
)
server = api.ThreadingHTTPServer(("127.0.0.1", 0), api.H)
thread = threading.Thread(target=server.serve_forever, daemon=True)
thread.start()
base = f"http://127.0.0.1:{server.server_port}"
def request(path, body=None):
data = None if body is None else json.dumps(body).encode("utf-8")
value = urllib.request.Request(
base + path,
data=data,
headers={"Authorization": "Bearer test-token"},
method="POST" if body is not None else "GET",
)
try:
with urllib.request.urlopen(value, timeout=5) as response:
return response.status, json.load(response)
except urllib.error.HTTPError as error:
return error.code, json.load(error)
try:
health_code, health = request("/health")
unavailable_code, unavailable = request("/createReleaseCandidate", {})
service.ready = True
invalid_code, invalid = request("/createReleaseCandidate", {})
finally:
server.shutdown()
server.server_close()
thread.join(timeout=5)
assert health_code == 200
assert health["process_liveness"] == "PASS"
assert health["builder_readiness"]["status"] == "UNAVAILABLE"
assert unavailable_code == 503
assert unavailable["error"] == "TRUSTED_BUILDER_UNAVAILABLE"
assert invalid_code == 400
assert invalid["error"] == "event_id is required"
@pytest.mark.skipif(sys.platform == "win32", reason="model API runtime requires POSIX fcntl")
def test_host_adapter_accepts_a_real_builder_bundle_response():
spec = importlib.util.spec_from_file_location(
"trusted_builder_public_seam",
APP.parents[1] / "tests" / "test_trusted_candidate_builder.py",
)
assert spec and spec.loader
backend_seam = importlib.util.module_from_spec(spec)
spec.loader.exec_module(backend_seam)
server, client, _ = backend_seam.make_seam()
candidate_id = "rc-v2r2c2-d2b5a8339a34"
source_commit = "d2b5a8339a34a8f958a177f8a3b9e8296a57a7a6"
response = server.handle_request(
client.create_request(
"V2R2C2", candidate_id, source_commit, nonce="z" * 32
),
connection=object(),
)
class ResponseClient:
def build(self, version, supplied_candidate_id, supplied_source, *, nonce):
assert version == "V2R2C2"
assert supplied_candidate_id == candidate_id
assert supplied_source == source_commit
assert len(nonce) >= 32
return response
api = importlib.import_module("aios_model_api_staging")
bundle = api._HostTrustedCandidateBuilderAdapter(ResponseClient()).build(
{
"candidate_id": candidate_id,
"source_commit": source_commit,
"version": "V2R2C2",
}
)
assert bundle.validate().candidate.source_commit == response["base_oid"]
assert bundle.attestation.builder_key_id == response["attestation"]["builder_key_id"]
@pytest.mark.skipif(sys.platform == "win32", reason="model API runtime requires POSIX fcntl")
def test_host_adapter_forwards_v2r2c3_to_the_policy_and_fails_closed_when_unavailable():
from aios_trusted_candidate_builder import BuilderError
api = importlib.import_module("aios_model_api_staging")
calls = []
class DerivedOnlyClient:
def build(self, version, candidate_id, source_commit, *, nonce):
calls.append((version, candidate_id, source_commit, nonce))
raise BuilderError("GOVERNED_V2R2C3_DERIVATION_REQUIRED")
with pytest.raises(TrustedCandidateBuilderUnavailable):
api._HostTrustedCandidateBuilderAdapter(DerivedOnlyClient()).build(
{
"candidate_id": "r42-release-candidate-v2r2c3-20260820",
"source_commit": COMMIT,
"version": "V2R2C3",
}
)
assert calls[0][:3] == (
"V2R2C3",
"r42-release-candidate-v2r2c3-20260820",
COMMIT,
)
def test_host_adapter_accepts_intent_only_without_caller_candidate_id():
api = importlib.import_module('aios_model_api_staging')
calls = []
class IntentClient:
def build(self, version, candidate_id, source_commit, *, nonce):
calls.append((version, candidate_id, source_commit, nonce))
raise RuntimeError('intent-only probe')
with pytest.raises(RuntimeError, match='intent-only probe'):
api._HostTrustedCandidateBuilderAdapter(IntentClient()).build({
'event_id': 'event-intent-only',
'intent_id': 'intent-intent-only',
'source_commit': COMMIT,
'version': 'V2R2C2',
})
assert calls[0][0] == 'V2R2C2'
assert calls[0][1] == ''
assert calls[0][2] == COMMIT
assert len(calls[0][3]) >= 32
@@ -0,0 +1,156 @@
from __future__ import annotations
import importlib.util
import json
import os
from pathlib import Path
import subprocess
import sys
import pytest
BACKEND_PATH = Path(__file__).parents[1] / "services" / "aios-agent-runtime-broker" / "openclaw_engineering_backend.py"
def load_backend():
spec = importlib.util.spec_from_file_location("openclaw_backend_under_test", BACKEND_PATH)
module = importlib.util.module_from_spec(spec)
assert spec.loader is not None
spec.loader.exec_module(module)
return module
def fake_openclaw(tmp_path: Path) -> Path:
path = tmp_path / "openclaw"
path.write_text(
"""#!/usr/bin/env python3
import json, os, pathlib, sys
args = sys.argv[1:]
assert args[:2] == ['agent', 'exec']
def value(flag):
return args[args.index(flag) + 1]
assert value('--message-file') == '-'
cwd = pathlib.Path(value('--cwd'))
assert cwd.resolve() == pathlib.Path.cwd().resolve()
assert '--auth-env-only' in args
assert '--json' in args
assert os.environ.get('OPENCLAW_EXEC_SHELL_SNAPSHOT') == '0'
assert 'AIOS_AGENT_TASK_TOKEN' not in os.environ
prompt = sys.stdin.read()
assert 'Do NOT run `git commit`' in prompt
(cwd / 'changed_by_openclaw.txt').write_text('changed\\n', encoding='utf-8')
json.dump({
'ok': True,
'status': 'ok',
'final': 'Changed the requested file and verified the result.',
'payloads': [{'text':'done'}],
'model': value('--model'),
'provider': 'test-provider',
'sessionId': 'test-session',
'assistantTurns': 2,
'codeModeEngaged': True,
'toolSummary': {'calls': 3, 'tools': ['read','write','exec'], 'totalToolTimeMs': 10}
}, sys.stdout)
""",
encoding="utf-8",
)
path.chmod(0o755)
return path
def request(worktree: Path) -> dict[str, object]:
return {
"schema": "aios.online-agent.runtime-request.v1",
"adapter": "hermes",
"task": {
"task_id": "openclaw-backend-selftest",
"goal": "Make the requested change.",
"instructions": "Inspect, implement, and test.",
"success_criteria": ["The change is correct"],
},
"execution_context": {
"repository": "aios/aios",
"base_oid": "a" * 40,
"target_branch": "agent/intent/openclaw-backend-selftest",
"worktree": str(worktree),
},
"policy": {
"authoritative_repository_write": False,
"direct_git_ref_mutation": False,
"direct_git_push": False,
"direct_git_commit": False,
"main_write": False,
"force_push": False,
"production_write": False,
"tag_write": False,
"baseline_write": False,
"task_api_credential_visible": False,
"writer_lease_visible": False,
},
}
def test_openclaw_agent_exec_contract(tmp_path: Path, monkeypatch) -> None:
backend = load_backend()
worktree = tmp_path / "worktree"
worktree.mkdir()
fake = fake_openclaw(tmp_path)
monkeypatch.setenv("AIOS_OPENCLAW_BIN", str(fake))
monkeypatch.setenv("AIOS_OPENCLAW_MODEL", "test/model")
monkeypatch.setenv("AIOS_OPENCLAW_PROVIDER_CREDENTIAL_MAP_JSON", "{}")
fd = os.open(str(worktree), getattr(os, "O_PATH", os.O_RDONLY) | os.O_DIRECTORY)
try:
value = request(worktree)
value["execution_context"]["worktree"] = f"/proc/self/fd/{fd}"
task, pinned = backend.validate_request(value)
result = backend.invoke_openclaw(task, pinned)
finally:
os.close(fd)
assert result["status"] == "COMPLETED"
assert result["evidence"]["backend"] == "openclaw-agent-exec"
assert result["evidence"]["model"] == "test/model"
assert (worktree / "changed_by_openclaw.txt").read_text(encoding="utf-8") == "changed\n"
def test_provider_credential_is_projected_only_to_openclaw_child(tmp_path: Path, monkeypatch) -> None:
backend = load_backend()
credentials = tmp_path / "credentials"
credentials.mkdir()
(credentials / "openai_api_key").write_text("provider-secret\n", encoding="utf-8")
monkeypatch.setenv("CREDENTIALS_DIRECTORY", str(credentials))
monkeypatch.setenv(
"AIOS_OPENCLAW_PROVIDER_CREDENTIAL_MAP_JSON",
json.dumps({"OPENAI_API_KEY": "openai_api_key"}),
)
env = backend.provider_environment()
assert env["OPENAI_API_KEY"] == "provider-secret"
assert "AIOS_AGENT_TASK_TOKEN" not in env
assert env["OPENCLAW_EXEC_SHELL_SNAPSHOT"] == "0"
def test_missing_model_fails_closed(tmp_path: Path, monkeypatch) -> None:
backend = load_backend()
worktree = tmp_path / "worktree"
worktree.mkdir()
monkeypatch.setenv("AIOS_OPENCLAW_BIN", str(fake_openclaw(tmp_path)))
monkeypatch.delenv("AIOS_OPENCLAW_MODEL", raising=False)
fd = os.open(str(worktree), getattr(os, "O_PATH", os.O_RDONLY) | os.O_DIRECTORY)
try:
value = request(worktree)
value["execution_context"]["worktree"] = f"/proc/self/fd/{fd}"
task, pinned = backend.validate_request(value)
with pytest.raises(backend.BackendError) as caught:
backend.invoke_openclaw(task, pinned)
finally:
os.close(fd)
assert caught.value.code == "OPENCLAW_MODEL_INVALID"
def test_pinned_worktree_is_required(tmp_path: Path) -> None:
backend = load_backend()
value = request(tmp_path)
value["execution_context"]["worktree"] = str(tmp_path)
with pytest.raises(backend.BackendError) as caught:
backend.validate_request(value)
assert caught.value.code == "PINNED_WORKTREE_REQUIRED"
-46
View File
@@ -1,46 +0,0 @@
from __future__ import annotations
from pathlib import Path
import subprocess
import sys
FIXTURE_SOURCE = '"""x"""\nimport re\nfrom aios_online_agent_execution import (\n GovernedExecutionBinding,\n validate_execution_envelope,\n)\n\nclass InitialExecutionProjectionHandler:\n store = None\n def _authorized(self):\n return True\n def _json(self, value, status):\n pass\n def do_GET(self):\n if self.path == "/health":\n self._json(\n {"status": "ok", "service": "aios-agent-task-initial-execution-projection"},\n 200,\n )\n else:\n self._json({"error": "NOT_FOUND"}, 404)\n\n def do_POST(self):\n initial_match = re.fullmatch(\n r"/v1/tasks/([^/]+)/initial-execution-projection", self.path\n )\n recovery_match = re.fullmatch(\n r"/v1/tasks/([^/]+)/source-capture-projection-recovery", self.path\n )\n if initial_match is None and recovery_match is None:\n self._json({"error": "NOT_FOUND"}, 404)\n return\n if not self._authorized():\n self._json({"error": "UNAUTHORIZED"}, 401)\n return\n try:\n value = {}\n if self.store is None:\n raise RuntimeError("RUNTIME_UNAVAILABLE")\n if initial_match is not None:\n request = InitialExecutionProjectionRequest.from_mapping(value)\n result = self.store.project(initial_match.group(1), request)\n else:\n recovery_request = SourceCaptureProjectionRecoveryRequest.from_mapping(value)\n result = self.store.project_from_source_capture(\n recovery_match.group(1), recovery_request\n )\n self._json(result, 200 if result["idempotent"] else 201)\n except Exception:\n pass\n'
def test_patcher_is_strict_and_idempotent(tmp_path):
source = tmp_path / "aios_agent_task_initial_execution_projection.py"
source.write_text(FIXTURE_SOURCE, encoding="utf-8")
patcher = Path(__file__).resolve().parents[1] / "tools" / "integrate_projection_recovery.py"
first = subprocess.run(
[sys.executable, str(patcher), str(source)],
capture_output=True,
text=True,
)
assert first.returncode == 0, first.stderr
assert first.stdout.strip() == "CHANGED"
text = source.read_text()
assert "/canonical-projection-recovery" in text
assert "/canonical-projection" in text
assert "recover_canonical_projection" in text
second = subprocess.run(
[sys.executable, str(patcher), str(source)],
capture_output=True,
text=True,
)
assert second.returncode == 0
assert second.stdout.strip() == "ALREADY_APPLIED"
def test_patcher_fails_closed_on_unexpected_source(tmp_path):
source = tmp_path / "wrong.py"
source.write_text("print('different')\n")
patcher = Path(__file__).resolve().parents[1] / "tools" / "integrate_projection_recovery.py"
result = subprocess.run(
[sys.executable, str(patcher), str(source)],
capture_output=True,
text=True,
)
assert result.returncode == 2
assert "ANCHOR_COUNT" in result.stderr
@@ -1,32 +0,0 @@
from pathlib import Path
import ast
ROOT = Path(__file__).resolve().parents[1]
def test_r7_amendment_module_is_valid_and_fail_closed():
text = (
ROOT
/ "active-runtime/app/aios_task_deterministic_execution_amendment.py"
).read_text()
ast.parse(text)
assert "validate_execution_envelope" in text
assert "SIGNED_EXECUTION_AMENDMENT_REQUIRED" in text
assert "BEFORE UPDATE OF status ON tasks" in text
assert "APPLY_STATES" in text
assert '"FAILED"' in text and '"QUEUED"' in text
assert "AMENDMENT_EXECUTION_OPERATIONS_REQUIRED" in text
assert "AMENDMENT_PROTECTED_WRITE_FORBIDDEN" in text
def test_bridge_unit_keeps_strict_sandbox_and_required_projection():
text = (
ROOT
/ "deploy/systemd/aios-chatgpt-bridge-v2-staging.service"
).read_text()
assert "ProtectSystem=strict" in text
assert "ProtectHome=true" in text
assert (
"LoadCredential=gitea_token:"
) in text
assert "/data/aios-gitops/repo.git/worktrees" in text
@@ -110,8 +110,6 @@ class SignerContractCompatibilityTests(unittest.TestCase):
signer = "/usr/local/libexec/aios-grant-signer-adapter"
if not Path(signer).is_file():
self.skipTest("deployed signer is not present")
if os.geteuid() != 0:
self.skipTest("deployed signer self-test requires root")
help_result = subprocess.run([signer, "sign", "--help"], capture_output=True, text=True, check=False)
self.assertEqual(help_result.returncode, 0)
self.assertTrue(deployed_signer_help(help_result.stdout, help_result.stderr))
@@ -1,114 +0,0 @@
from __future__ import annotations
import json
import os
from pathlib import Path
import sys
import pytest
APP = Path(__file__).resolve().parents[1] / "active-runtime" / "app"
sys.path.insert(0, str(APP))
import trusted_builder_signer_provisioning as provisioning # noqa: E402
def _source(tmp_path: Path) -> tuple[Path, list[str]]:
root = tmp_path / "source"
for relative in provisioning.SOURCE_EVIDENCE_PATHS:
path = root / relative
path.parent.mkdir(parents=True, exist_ok=True)
if relative != provisioning.PACKAGE_MANIFEST_PATH:
path.write_text("source:" + relative + "\n", encoding="utf-8")
os.chmod(path, 0o644)
manifest = root / provisioning.PACKAGE_MANIFEST_PATH
manifest.write_bytes(provisioning.pretty_json(provisioning.package_contract_document()))
os.chmod(manifest, 0o644)
return root, list(provisioning.SOURCE_EVIDENCE_PATHS)
def _payload(tmp_path: Path):
source, paths = _source(tmp_path)
output = tmp_path / "payload.json"
value = provisioning.build_payload(
source, output, main_oid="c" * 40, tree_oid="d" * 40,
pr_number=70, pr_head_oid="e" * 40,
merge_parent_oids=["a" * 40, "e" * 40], source_files=paths,
nonce="test-provisioning-nonce-001",
)
return source, output, value
def test_fresh_payload_is_bound_to_final_common_main_and_ordered_pr_parent(tmp_path: Path):
_source_root, output, payload = _payload(tmp_path)
assert output.read_bytes().startswith(b"{")
provisioning._validate_payload(payload)
assert payload["source"]["gitea_main_oid"] == payload["source"]["governed_main_oid"]
assert payload["source"]["merge_parent_oids"][1] == payload["source"]["pr_head_oid"]
assert set(payload["source"]["source_evidence_sha256"]) == set(provisioning.SOURCE_EVIDENCE_PATHS)
assert set(payload["allowed_capabilities"]) == provisioning.ALLOWED_CAPABILITIES
assert set(payload["denied_capabilities"]) == provisioning.DENIED_CAPABILITIES
@pytest.mark.parametrize("field", ["allowed_capabilities", "denied_capabilities"])
def test_payload_rejects_capability_scope_tampering(tmp_path: Path, field: str):
_source_root, _output, payload = _payload(tmp_path)
payload[field] = ["owner_release_approval"]
with pytest.raises(provisioning.ProvisioningError, match="CAPABILITY_SCOPE_INVALID"):
provisioning._validate_payload(payload)
def test_payload_rejects_main_divergence_and_wrong_second_parent(tmp_path: Path):
_source_root, _output, payload = _payload(tmp_path)
payload["source"]["governed_main_oid"] = "f" * 40
with pytest.raises(provisioning.ProvisioningError, match="SOURCE_MAIN_NOT_CONVERGED"):
provisioning._validate_payload(payload)
_source_root, _output, payload = _payload(tmp_path / "second")
payload["source"]["merge_parent_oids"][1] = "a" * 40
with pytest.raises(provisioning.ProvisioningError, match="SOURCE_PR_HEAD_SECOND_PARENT_INVALID"):
provisioning._validate_payload(payload)
def test_payload_rejects_missing_or_extra_source_evidence(tmp_path: Path):
_source_root, _output, payload = _payload(tmp_path)
payload["source"]["source_evidence_sha256"].pop(next(iter(payload["source"]["source_evidence_sha256"])))
with pytest.raises(provisioning.ProvisioningError, match="SOURCE_EVIDENCE_SET_INVALID"):
provisioning._validate_payload(payload)
_source_root, _output, payload = _payload(tmp_path / "second")
payload["source"]["source_evidence_sha256"]["extra.txt"] = "0" * 64
with pytest.raises(provisioning.ProvisioningError, match="SOURCE_EVIDENCE_SET_INVALID"):
provisioning._validate_payload(payload)
def test_package_contract_rejects_incompatible_source_mode_and_tampering(tmp_path: Path):
source, _paths = _source(tmp_path)
service = source / "deploy/systemd/aios-trusted-candidate-builder.service"
os.chmod(service, 0o601)
with pytest.raises(provisioning.ProvisioningError, match="SOURCE_EVIDENCE_MODE_INVALID"):
provisioning.verify_package_contract(source)
os.chmod(service, 0o644)
manifest = source / provisioning.PACKAGE_MANIFEST_PATH
value = json.loads(manifest.read_text(encoding="utf-8"))
value["activation"]["credential_installation"] = True
manifest.write_text(json.dumps(value), encoding="utf-8")
with pytest.raises(provisioning.ProvisioningError, match="PACKAGE_CONTRACT_INVALID"):
provisioning.verify_package_contract(source)
def test_package_contract_declares_socket_activation_no_credentials_and_0755_launcher(tmp_path: Path):
source, _paths = _source(tmp_path)
provisioning.verify_package_contract(source)
value = json.loads((source / provisioning.PACKAGE_MANIFEST_PATH).read_text(encoding="utf-8"))
launcher = next(item for item in value["files"] if item["source_path"] == "deploy/libexec/aios-trusted-builder-signer-provisioner")
assert launcher["install_mode"] == "0755"
assert value["activation"] == {
"daemon_reload": True,
"enable_units": ["aios-trusted-candidate-builder.socket"],
"service_start": "socket-activation-only",
"credential_installation": False,
"signer_invocation": False,
}
def test_provisioner_self_test_has_no_mutation(capsys):
assert provisioning.main(["--self-test"]) == 0
assert capsys.readouterr().out.strip() == "PASS"
-592
View File
@@ -1,592 +0,0 @@
from __future__ import annotations
import dataclasses
import hashlib
import hmac
import json
import sys
from pathlib import Path
from types import SimpleNamespace
import pytest
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(ROOT / "active-runtime" / "app"))
import aios_trusted_candidate_builder as builder_runtime # noqa: E402
from aios_trusted_candidate_builder import ( # noqa: E402
BuilderError,
BuilderKeyring,
DeterministicEvidenceCollector,
PeerIdentity,
ServerOwnedEvidenceSource,
TrustedCandidateBuilder,
TrustedCandidateBuilderConfig,
UnixTrustedCandidateBuilderClient,
load_builder_policy,
)
from silent_release_controller import ReleaseCandidate, ReleaseState # noqa: E402
from silent_release_hardening import ( # noqa: E402
BuilderAttestation,
BuilderAttestationKeyRecord,
BuilderAttestationKeyRing,
BuilderAttestationVerifier,
DeploymentPlan,
FinalizationPlan,
ReleasePlan,
TagPlan,
TrustedCandidateBundle,
)
POLICY_PATH = ROOT / "config" / "gitops" / "trusted-candidate-builder-policy.json"
NOW = 1_725_000_000
class RecordingDomainSigner:
key_id = "test-builder-key"
key_generation = 1
def __init__(self) -> None:
self.calls: list[tuple[str, bytes]] = []
def sign_for_purpose(self, purpose: str, payload: bytes) -> bytes:
self.calls.append((purpose, payload))
return hashlib.sha256(b"test-signature\0" + payload).digest()
def make_seam(*, peer: PeerIdentity | None = None, cgroup: str = "/system.slice/aios-gitops-model-api-v2r2c1-staging.service", process=("/usr/bin/python3.12", ("/usr/bin/python3", "/opt/aios/gitops-model-api/test/runtime/app/aios_model_api_staging.py")), server_clock=lambda: NOW, client_clock=lambda: NOW, collector=None, nonce_ledger_path=None, signer_override=None) -> tuple[TrustedCandidateBuilder, UnixTrustedCandidateBuilderClient, RecordingDomainSigner]:
policy = load_builder_policy(POLICY_PATH)
config = TrustedCandidateBuilderConfig.from_policy(policy)
keyring = BuilderKeyring(builder_hmac=b"h" * 32)
signer = signer_override or RecordingDomainSigner()
server = TrustedCandidateBuilder(
config,
keyring,
collector=collector or DeterministicEvidenceCollector(config, evidence_source=passing_bundle),
signer=signer,
peer_identity_resolver=lambda _connection: peer or PeerIdentity(pid=77, uid=1000, gid=1000),
cgroup_resolver=lambda _pid: cgroup,
process_identity_resolver=lambda _pid: process,
clock=server_clock,
nonce_ledger_path=nonce_ledger_path,
)
return server, UnixTrustedCandidateBuilderClient(config, keyring, clock=client_clock), signer
def passing_bundle(recipe_name, recipe):
base_oid = recipe["base_oid"]
digest = lambda label: hashlib.sha256(f"{label}\0{base_oid}".encode()).hexdigest()
candidate = ReleaseCandidate(
candidate_id=recipe["candidate_id"], source_commit=base_oid,
tree_oid=digest("tree")[:40], artifact_sha256=digest("artifact"),
manifest_sha256=digest("manifest"), tests={"passed": True, "receipt": digest("tests")},
canary={"passed": True, "receipt": digest("canary")}, rollback_ready=True,
git_consistency={"consistent": True}, gitea_consistency={"consistent": True},
state=ReleaseState.READY_FOR_APPROVAL,
)
policy_sha = digest("policy")
plan = ReleasePlan(
deployment=DeploymentPlan(
service="aios-silent-release-controller", release_path="/srv/release/r1",
release_root="/srv/release", cutover_link="/srv/release/current",
dropin_path="/srv/release/r1/deploy/systemd/controller.service",
dropin_sha256=digest("dropin"), systemd_unit_sha256=digest("unit"),
rollback_path="/srv/release/rollback-r1", rollback_sha256=digest("rollback"),
source_commit=base_oid, tree_oid=candidate.tree_oid,
artifact_sha256=candidate.artifact_sha256, manifest_sha256=candidate.manifest_sha256,
package_closure_sha256=digest("closure"), authoritative_git_dir="/data/aios-gitops/repo.git",
repository_identity="aios/aios", baseline_before_sha256=digest("baseline"),
registry_before_sha256=digest("registry"), production_tag_refset_before_sha256=digest("tagset"),
health_contract_version="trusted-builder/v1", timeout_policy={"seconds": 10},
retry_policy={"attempts": 1}, policy_sha256=policy_sha, health_contract={"checked": True},
),
tag=TagPlan(
version="trusted-v2r2c2-001", tag_type="annotated", expected_tag_oid=digest("tag")[:40],
expected_target_oid=base_oid, evidence_closure_oid=digest("closure-oid")[:40],
approval_plan_id="owner-plan-trusted-v2r2c2", repository_identity="aios/aios",
gitea_owner="aios", gitea_repository="aios", current_remote_tag_oid=None,
remote_absent_evidence_sha256=digest("tag-absence"), verification_contract="trusted-builder/v1",
),
finalization=FinalizationPlan(
version="trusted-v2r2c2-001", expected_tag_oid=digest("tag")[:40], expected_target_oid=base_oid,
expected_previous_baseline_sha256=digest("baseline"), registry_expected_record={"base": base_oid},
baseline_expected_record={"base": base_oid}, final_consistency_contract="trusted-builder/v1",
), policy_sha256=policy_sha,
)
checks = {name: True for name in [
"authoritative_git", "artifact", "canary", "gitea", "manifest",
"package_closure", "rollback", "secret_scan", "tests",
]}
TrustedCandidateBundle(candidate, plan, checks).validate()
canonical_digest = lambda value: hashlib.sha256(
json.dumps(value, sort_keys=True, separators=(",", ":")).encode()
).hexdigest()
evidence_digests = {
"artifact": candidate.artifact_sha256,
"manifest": candidate.manifest_sha256,
"package_closure": plan.deployment.package_closure_sha256,
"tests": canonical_digest(candidate.tests),
"canary": canonical_digest(candidate.canary),
"gitea": canonical_digest(candidate.gitea_consistency),
"rollback": canonical_digest({
"ready": True,
"rollback_sha256": plan.deployment.rollback_sha256,
"source_commit": candidate.source_commit,
}),
"policy": plan.policy_sha256,
"secret_scan": digest("secret-scan"),
}
return {
"candidate": candidate.to_dict(),
"release_plan": plan.to_dict(),
"checks": checks,
"evidence_digests": evidence_digests,
}
def rehmac(request: dict) -> None:
unsigned = {key: value for key, value in request.items() if key != "hmac"}
request["hmac"] = hmac.new(
b"h" * 32,
json.dumps(unsigned, sort_keys=True, separators=(",", ":")).encode(),
hashlib.sha256,
).hexdigest()
def test_public_build_seam_accepts_only_the_fixed_v2r2c2_recipe_and_returns_server_owned_evidence() -> None:
server, client, signer = make_seam()
request = client.create_request("V2R2C2", "rc-v2r2c2-d2b5a8339a34", "d2b5a8339a34a8f958a177f8a3b9e8296a57a7a6", nonce="a" * 32)
result = server.handle_request(request, connection=object())
assert result["schema"] == "aios.trusted-candidate-builder.response.v1"
assert result["status"] == "BUILT"
assert result["recipe"] == "V2R2C2"
assert result["repository"] == "aios/aios"
assert result["task_id"] == "v12-durable-trusted-release-controller-activation-20260829"
assert result["silent_release_bundle"]["candidate"]["candidate_id"] == "rc-v2r2c2-d2b5a8339a34"
assert result["base_oid"] == "d2b5a8339a34a8f958a177f8a3b9e8296a57a7a6"
assert set(result["checks"]) == set(server.config.required_checks)
assert all(result["checks"].values())
assert result["attestation"]["trust_domain"] == "aios.trusted-builder.attestation.v1"
assert signer.calls and signer.calls[0][0] == "AIOS_TRUSTED_RELEASE_BUILDER"
assert result["attestation"]["fingerprint"] == hashlib.sha256(signer.calls[0][1]).hexdigest()
assert BuilderAttestation.from_dict(result["attestation"]).canonical_bytes() == signer.calls[0][1]
silent_bundle = result["silent_release_bundle"]
round_trip = TrustedCandidateBundle(
ReleaseCandidate.from_dict(silent_bundle["candidate"]),
ReleasePlan.from_dict(silent_bundle["release_plan"]),
silent_bundle["checks"],
)
assert round_trip.validate().candidate.candidate_id == result["evidence"]["candidate_id"]
@pytest.mark.parametrize(
"mutate, error",
[
(lambda request: request.update({"base_oid": "f" * 40}), "REQUEST_HMAC_INVALID"),
(lambda request: request.update({"artifact_sha256": "0" * 64}), "REQUEST_SCHEMA_INVALID"),
],
)
def test_public_build_seam_rejects_tampering_and_caller_owned_evidence(mutate, error) -> None:
server, client, _ = make_seam()
request = client.create_request("V2R2C2", "rc-v2r2c2-d2b5a8339a34", "d2b5a8339a34a8f958a177f8a3b9e8296a57a7a6", nonce="b" * 32)
mutate(request)
with pytest.raises(BuilderError, match=error):
server.handle_request(request, connection=object())
@pytest.mark.parametrize("field,value", [
("repository", "other/repository"),
("task_id", "other-task"),
("base_oid", "f" * 40),
("purpose", "owner_approval"),
])
def test_public_build_seam_rejects_reauthenticated_scope_rebinding(field, value) -> None:
server, client, _ = make_seam()
request = client.create_request(
"V2R2C2",
"rc-v2r2c2-d2b5a8339a34",
"d2b5a8339a34a8f958a177f8a3b9e8296a57a7a6",
nonce="s" * 32,
)
request[field] = value
rehmac(request)
with pytest.raises(BuilderError, match="REQUEST_SCOPE_INVALID"):
server.handle_request(request, connection=object())
def test_public_build_seam_rejects_wrong_hmac() -> None:
server, client, _ = make_seam()
request = client.create_request(
"V2R2C2",
"rc-v2r2c2-d2b5a8339a34",
"d2b5a8339a34a8f958a177f8a3b9e8296a57a7a6",
nonce="w" * 32,
)
request["hmac"] = "0" * 64
with pytest.raises(BuilderError, match="REQUEST_HMAC_INVALID"):
server.handle_request(request, connection=object())
def test_public_build_seam_returns_the_original_result_for_exact_nonce_replay_and_rejects_conflicts() -> None:
server, client, _ = make_seam()
request = client.create_request("V2R2C2", "rc-v2r2c2-d2b5a8339a34", "d2b5a8339a34a8f958a177f8a3b9e8296a57a7a6", nonce="c" * 32)
first = server.handle_request(request, connection=object())
assert server.handle_request(request, connection=object()) == first
conflict = client.create_request("V2R2C2", "rc-v2r2c2-d2b5a8339a34", "d2b5a8339a34a8f958a177f8a3b9e8296a57a7a6", nonce="c" * 32)
conflict["issued_at"] += 1
unsigned = {key: value for key, value in conflict.items() if key != "hmac"}
conflict["hmac"] = hmac.new(b"h" * 32, json.dumps(unsigned, sort_keys=True, separators=(",", ":")).encode(), hashlib.sha256).hexdigest()
with pytest.raises(BuilderError, match="REPLAY_CONFLICT"):
server.handle_request(conflict, connection=object())
def test_exact_replay_survives_restart_and_the_original_freshness_window(tmp_path) -> None:
ledger = tmp_path / "nonce-ledger.json"
server, client, _ = make_seam(nonce_ledger_path=ledger)
request = client.create_request(
"V2R2C2",
"rc-v2r2c2-d2b5a8339a34",
"d2b5a8339a34a8f958a177f8a3b9e8296a57a7a6",
nonce="r" * 32,
)
first = server.handle_request(request, connection=object())
restarted, _, _ = make_seam(
server_clock=lambda: NOW + 3600,
nonce_ledger_path=ledger,
)
assert restarted.handle_request(request, connection=object()) == first
def test_readiness_probe_checks_collector_and_signer_without_signing() -> None:
server, client, signer = make_seam()
request = client.create_request(
"V2R2C2",
"rc-v2r2c2-d2b5a8339a34",
"d2b5a8339a34a8f958a177f8a3b9e8296a57a7a6",
nonce="q" * 32,
operation=server.config.readiness_operation,
)
result = server.handle_request(request, connection=object())
assert result["status"] == "READY"
assert result["collector_ready"] is True
assert result["signer_ready"] is True
assert result["builder_key_id"] == signer.key_id
assert result["policy_sha256"] == server.config.policy_sha256
assert "signature" not in result
assert "silent_release_bundle" not in result
assert signer.calls == []
def test_readiness_fails_closed_when_signer_is_unavailable() -> None:
class UnavailableSigner:
@property
def key_id(self):
raise BuilderError("SIGNER_UNAVAILABLE")
@property
def key_generation(self):
return 1
def sign_for_purpose(self, purpose, payload):
raise AssertionError("readiness must not attempt signing")
server, client, _ = make_seam(signer_override=UnavailableSigner())
request = client.create_request(
"V2R2C2",
"rc-v2r2c2-d2b5a8339a34",
"d2b5a8339a34a8f958a177f8a3b9e8296a57a7a6",
nonce="u" * 32,
operation=server.config.readiness_operation,
)
with pytest.raises(BuilderError, match="SIGNER_UNAVAILABLE"):
server.handle_request(request, connection=object())
def test_unix_client_applies_bounded_timeout_and_fails_closed(monkeypatch) -> None:
observed = {}
class TimeoutSocket:
def __enter__(self):
return self
def __exit__(self, *_args):
return False
def settimeout(self, seconds):
observed["timeout"] = seconds
def connect(self, _path):
raise TimeoutError("bounded test timeout")
monkeypatch.setattr(builder_runtime.socket, "socket", lambda *_args: TimeoutSocket())
monkeypatch.setattr(builder_runtime.socket, "AF_UNIX", 1, raising=False)
monkeypatch.setattr(builder_runtime.socket, "SOCK_STREAM", 1, raising=False)
config = TrustedCandidateBuilderConfig.from_policy(load_builder_policy(POLICY_PATH))
client = UnixTrustedCandidateBuilderClient(
config,
BuilderKeyring(builder_hmac=b"h" * 32),
socket_path=Path("/run/aios-trusted-candidate-builder.sock"),
clock=lambda: NOW,
)
with pytest.raises(BuilderError, match="BUILDER_TRANSPORT_UNAVAILABLE"):
client.build(
"V2R2C2",
"rc-v2r2c2-d2b5a8339a34",
"d2b5a8339a34a8f958a177f8a3b9e8296a57a7a6",
nonce="t" * 32,
)
assert observed["timeout"] == config.request_timeout_seconds
def test_backend_attestation_round_trip_verifies_with_production_keyring() -> None:
private = Ed25519PrivateKey.generate()
class Ed25519Signer:
key_id = "builder-key-001"
key_generation = 1
def sign_for_purpose(self, purpose, payload):
assert purpose == "AIOS_TRUSTED_RELEASE_BUILDER"
return private.sign(payload)
server, client, _ = make_seam(signer_override=Ed25519Signer())
response = server.handle_request(
client.create_request(
"V2R2C2",
"rc-v2r2c2-d2b5a8339a34",
"d2b5a8339a34a8f958a177f8a3b9e8296a57a7a6",
nonce="v" * 32,
),
connection=object(),
)
public_key = private.public_key().public_bytes(
serialization.Encoding.Raw,
serialization.PublicFormat.Raw,
)
verifier = BuilderAttestationVerifier(
BuilderAttestationKeyRing(
[BuilderAttestationKeyRecord(
key_id="builder-key-001",
public_key=public_key,
algorithm="Ed25519",
purpose="AIOS_TRUSTED_RELEASE_BUILDER",
generation=1,
not_before="2020-01-01T00:00:00Z",
expires_at="2099-01-01T00:00:00Z",
status="ACTIVE",
)],
require_attestation_metadata=True,
)
)
raw = response["silent_release_bundle"]
bundle = TrustedCandidateBundle(
ReleaseCandidate.from_dict(raw["candidate"]),
ReleasePlan.from_dict(raw["release_plan"]),
raw["checks"],
BuilderAttestation.from_dict(raw["attestation"], require_fingerprint=True),
)
assert bundle.validate(verifier).candidate.candidate_id == response["evidence"]["candidate_id"]
def test_public_build_seam_rejects_direct_unbound_v2r2c3_and_authentication_boundaries() -> None:
server, client, _ = make_seam()
with pytest.raises(BuilderError, match="GOVERNED_V2R2C3_DERIVATION_REQUIRED"):
client.create_request("V2R2C3", "unknown", "f" * 40, nonce="d" * 32)
denied_peer, denied_client, _ = make_seam(peer=PeerIdentity(pid=77, uid=0, gid=0))
with pytest.raises(BuilderError, match="PEER_IDENTITY_DENIED"):
denied_peer.handle_request(denied_client.create_request("V2R2C2", "rc-v2r2c2-d2b5a8339a34", "d2b5a8339a34a8f958a177f8a3b9e8296a57a7a6", nonce="e" * 32), connection=object())
denied_cgroup, denied_client, _ = make_seam(cgroup="/system.slice/untrusted.service")
with pytest.raises(BuilderError, match="PEER_CGROUP_DENIED"):
denied_cgroup.handle_request(denied_client.create_request("V2R2C2", "rc-v2r2c2-d2b5a8339a34", "d2b5a8339a34a8f958a177f8a3b9e8296a57a7a6", nonce="f" * 32), connection=object())
denied_process, denied_client, _ = make_seam(
process=("/usr/bin/python3.12", ("/usr/bin/python3", "/tmp/aios_model_api_staging.py"))
)
with pytest.raises(BuilderError, match="PEER_PROCESS_DENIED"):
denied_process.handle_request(denied_client.create_request("V2R2C2", "rc-v2r2c2-d2b5a8339a34", "d2b5a8339a34a8f958a177f8a3b9e8296a57a7a6", nonce="p" * 32), connection=object())
stale_server, stale_client, _ = make_seam(client_clock=lambda: NOW - 61)
with pytest.raises(BuilderError, match="REQUEST_STALE"):
stale_server.handle_request(stale_client.create_request("V2R2C2", "rc-v2r2c2-d2b5a8339a34", "d2b5a8339a34a8f958a177f8a3b9e8296a57a7a6", nonce="g" * 32), connection=object())
def test_public_build_seam_fails_closed_when_server_owned_collector_is_incomplete() -> None:
def incomplete_evidence(recipe_name, recipe):
value = passing_bundle(recipe_name, recipe)
value["checks"]["canary"] = False
return value
policy = load_builder_policy(POLICY_PATH)
config = TrustedCandidateBuilderConfig.from_policy(policy)
server, client, _ = make_seam(
collector=DeterministicEvidenceCollector(config, evidence_source=incomplete_evidence)
)
with pytest.raises(BuilderError, match="COLLECTOR_CHECKS_INCOMPLETE"):
server.handle_request(client.create_request("V2R2C2", "rc-v2r2c2-d2b5a8339a34", "d2b5a8339a34a8f958a177f8a3b9e8296a57a7a6", nonce="h" * 32), connection=object())
def test_server_owned_source_rehashes_every_fixed_evidence_file_and_rejects_tampering(tmp_path) -> None:
config = TrustedCandidateBuilderConfig.from_policy(load_builder_policy(POLICY_PATH))
recipe = config.fixed_recipe("V2R2C2")
raw = passing_bundle("V2R2C2", recipe)
candidate = ReleaseCandidate.from_dict(raw["candidate"])
plan = ReleasePlan.from_dict(raw["release_plan"])
evidence_root = tmp_path / "evidence"
version_root = evidence_root / "V2R2C2"
version_root.mkdir(parents=True)
def write_bytes(name: str, value: bytes) -> str:
path = version_root / name
path.write_bytes(value)
path.chmod(0o600)
return hashlib.sha256(value).hexdigest()
def canonical(value: dict) -> bytes:
return json.dumps(value, sort_keys=True, separators=(",", ":")).encode()
tests = {"passed": True, "receipt": "tests-fixed"}
canary = {"passed": True, "receipt": "canary-fixed"}
gitea = {
"consistent": True,
"remote_ref_oid": recipe["source_commit"],
"repository": "aios/aios",
"source_commit": recipe["source_commit"],
"tree_oid": candidate.tree_oid,
}
git_consistency = {
"consistent": True,
"repository": "aios/aios",
"source_commit": recipe["source_commit"],
"tree_oid": candidate.tree_oid,
}
rollback = {
"ready": True,
"rollback_sha256": plan.deployment.rollback_sha256,
"source_commit": recipe["source_commit"],
}
artifact_bytes = b"immutable-artifact"
manifest_bytes = b'{"manifest":"fixed"}\n'
closure_bytes = b"closure artifact.tar.gz\n"
policy_bytes = b'{"policy":"fixed"}\n'
artifact_sha = write_bytes("artifact.tar.gz", artifact_bytes)
manifest_sha = write_bytes("manifest.json", manifest_bytes)
closure_sha = write_bytes("checksums.sha256", closure_bytes)
tests_sha = write_bytes("tests.json", canonical(tests))
canary_sha = write_bytes("canary.json", canonical(canary))
gitea_sha = write_bytes("gitea.json", canonical(gitea))
rollback_sha = write_bytes("rollback.json", canonical(rollback))
policy_sha = write_bytes("trusted-release-policy.json", policy_bytes)
secret_scan = {"artifact_sha256": artifact_sha, "passed": True}
secret_sha = write_bytes("secret-scan.json", canonical(secret_scan))
candidate.artifact_sha256 = artifact_sha
candidate.manifest_sha256 = manifest_sha
candidate.tests = tests
candidate.canary = canary
candidate.git_consistency = git_consistency
candidate.gitea_consistency = gitea
deployment = dataclasses.replace(
plan.deployment,
artifact_sha256=artifact_sha,
manifest_sha256=manifest_sha,
package_closure_sha256=closure_sha,
policy_sha256=policy_sha,
)
plan = dataclasses.replace(plan, deployment=deployment, policy_sha256=policy_sha)
receipt = {
"schema": "aios.trusted-candidate-builder.evidence.v1",
"version": "V2R2C2",
"candidate": candidate.to_dict(),
"release_plan": plan.to_dict(),
}
write_bytes("receipt.json", canonical(receipt))
git_state = {"tree": candidate.tree_oid, "ancestry": 0}
def git_runner(argv, **_kwargs):
if "rev-parse" in argv:
return SimpleNamespace(stdout=git_state["tree"] + "\n", returncode=0)
return SimpleNamespace(stdout="", returncode=git_state["ancestry"])
source = ServerOwnedEvidenceSource(
config,
tmp_path / "repo.git",
evidence_root,
expected_owner_uid=None,
git_runner=git_runner,
)
collected = source("V2R2C2", recipe)
assert collected["candidate"]["artifact_sha256"] == artifact_sha
assert collected["evidence_digests"] == {
"artifact": artifact_sha,
"manifest": manifest_sha,
"package_closure": closure_sha,
"tests": tests_sha,
"canary": canary_sha,
"gitea": gitea_sha,
"rollback": rollback_sha,
"policy": policy_sha,
"secret_scan": secret_sha,
}
(version_root / "manifest.json").write_bytes(b"tampered")
with pytest.raises(BuilderError, match="COLLECTOR_DIGEST_MISMATCH"):
source("V2R2C2", recipe)
(version_root / "manifest.json").write_bytes(manifest_bytes)
for name, tampered, expected in (
("artifact.tar.gz", b"tampered", "COLLECTOR_DIGEST_MISMATCH"),
("checksums.sha256", b"tampered", "COLLECTOR_DIGEST_MISMATCH"),
("trusted-release-policy.json", b"tampered", "COLLECTOR_DIGEST_MISMATCH"),
("tests.json", canonical({"passed": True, "receipt": "wrong"}), "COLLECTOR_TESTS_MISMATCH"),
("canary.json", canonical({"passed": True, "receipt": "wrong"}), "COLLECTOR_CANARY_MISMATCH"),
("gitea.json", canonical({**gitea, "consistent": False}), "COLLECTOR_GITEA_MISMATCH"),
("rollback.json", canonical({**rollback, "ready": False}), "COLLECTOR_ROLLBACK_MISMATCH"),
("secret-scan.json", canonical({**secret_scan, "passed": False}), "COLLECTOR_SECRET_SCAN_MISMATCH"),
):
path = version_root / name
original = path.read_bytes()
path.write_bytes(tampered)
with pytest.raises(BuilderError, match=expected):
source("V2R2C2", recipe)
path.write_bytes(original)
git_state["tree"] = "f" * 40
with pytest.raises(BuilderError, match="COLLECTOR_GIT_MISMATCH"):
source("V2R2C2", recipe)
git_state["tree"] = candidate.tree_oid
git_state["ancestry"] = 1
with pytest.raises(BuilderError, match="COLLECTOR_GIT_MISMATCH"):
source("V2R2C2", recipe)
def test_systemd_socket_and_service_are_af_unix_hardened_and_credential_bound() -> None:
socket_unit = (ROOT / "deploy/systemd/aios-trusted-candidate-builder.socket").read_text()
service_unit = (ROOT / "deploy/systemd/aios-trusted-candidate-builder.service").read_text()
assert "ListenStream=/run/aios-trusted-candidate-builder.sock" in socket_unit
assert "SocketMode=0660" in socket_unit
assert "LoadCredentialEncrypted=signing_private_key:" in service_unit
assert "LoadCredentialEncrypted=builder_hmac:" in service_unit
assert "NoNewPrivileges=true" in service_unit
assert "ProtectSystem=strict" in service_unit
assert "PrivateNetwork=true" in service_unit
assert "RestrictAddressFamilies=AF_UNIX" in service_unit
assert "StateDirectory=aios-trusted-candidate-builder" in service_unit
assert "AIOS_TRUSTED_BUILDER_KEY_GENERATION=1" in service_unit
assert "aios-approved-release-executor" not in service_unit
assert "--socket-fd 3" in service_unit
policy = load_builder_policy(POLICY_PATH)
assert policy["allowed_peer_uid"] == 1000
assert policy["allowed_peer_cgroups"] == [
"/system.slice/aios-gitops-model-api-v2r2c1-staging.service"
]
-60
View File
@@ -1,60 +0,0 @@
from __future__ import annotations
from pathlib import Path
from types import SimpleNamespace
import sys
import pytest
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "active-runtime" / "app"))
from governed_production_release import ReleaseBlocked, TrustedPlanReleaseExecutor, build_executor_config
from trusted_production_adapter import AdapterBlocked, _binding
def config(tmp_path: Path, version: str = "V2R2C2") -> SimpleNamespace:
return SimpleNamespace(
state_root=tmp_path,
target_version=version,
release_plan_path=None,
authoritative_git_dir=tmp_path,
baseline_path=tmp_path / "baseline.json",
registry_root=tmp_path / "registry",
api_base="http://127.0.0.1:3000/api/v1",
token_path=tmp_path / "token",
remote_name="gitea-http",
)
def test_non_legacy_runtime_selects_trusted_plan_handler(tmp_path: Path):
handler = build_executor_config(config(tmp_path))
assert isinstance(handler, TrustedPlanReleaseExecutor)
def test_trusted_handler_requires_server_side_plan(tmp_path: Path):
handler = build_executor_config(config(tmp_path))
with pytest.raises(ReleaseBlocked, match="TRUSTED_RELEASE_PLAN_REQUIRED"):
handler.consistency({"version": "V2R2C2"})
def test_legacy_runtime_keeps_compatibility_path(tmp_path: Path):
handler = build_executor_config(config(tmp_path, "V2R2C1"))
assert handler.__class__.__name__ == "GovernedProductionReleaseExecutor"
def test_generic_adapter_requires_candidate_and_plan_binding():
value = {
"version": "V2R2C2",
"target_oid": "1" * 40,
"artifact_sha256": "2" * 64,
"artifact_path": "/opt/aios/releases/V2R2C2.tar",
"artifact_size": 1,
"production_service": "aios-v2r2c2-production.service",
"production_port": 18928,
"candidate_id": "rc-v2r2c2-001",
"release_plan_sha256": "sha256:" + "3" * 64,
}
assert _binding(value)["candidate_id"] == "rc-v2r2c2-001"
with pytest.raises(AdapterBlocked, match="legacy_or_unbound_release_rejected"):
_binding({**value, "version": "V2R1C7"})
with pytest.raises(AdapterBlocked, match="trusted_binding_incomplete"):
_binding({key: item for key, item in value.items() if key != "release_plan_sha256"})
-23
View File
@@ -1,23 +0,0 @@
from pathlib import Path
import json, subprocess, sys
def test_trusted_builder_emits_plan_bound_payload(tmp_path):
source = tmp_path / "source"; source.mkdir()
files = {
"active-runtime/app/deployment_state.py": "x\n",
"active-runtime/app/v2r1c6_privacy_tools.py": "x\n",
"active-runtime/app/v2r2c2_health_service.py": "x\n",
"deploy/aios-v2/systemd/aios-v2r2c2-production.service": "x\n",
}
for rel, text in files.items():
path = source / rel; path.parent.mkdir(parents=True, exist_ok=True); path.write_text(text)
output = tmp_path / "release.tar"
script = Path(__file__).resolve().parents[1] / "scripts/autonomous-release/build_trusted_release_artifact.py"
result = subprocess.run([sys.executable, str(script), "--source", str(source), "--output", str(output),
"--version", "V2R2C2", "--source-oid", "1" * 40, "--tree-oid", "2" * 40,
"--rollback-version", "V2R2C1", "--rollback-target-oid", "3" * 40,
"--rollback-artifact-sha256", "4" * 64, "--service", "aios-v2r2c2-production.service", "--port", "18928"],
check=True, capture_output=True, text=True)
receipt = json.loads(result.stdout)
assert receipt["source_commit"] == "1" * 40
assert output.is_file() and receipt["artifact_size"] == output.stat().st_size
@@ -1,61 +0,0 @@
from __future__ import annotations
import hashlib
import sys
from pathlib import Path
import pytest
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "active-runtime" / "app"))
from trusted_release_execution_plan import PlanError, TrustedReleasePlan, canonical_json
def plan():
return {
"schema": "aios.trusted-release-execution-plan.v1",
"version": "V2R2C2",
"candidate_id": "rc-v2r2c2-contract-001",
"source_commit": "1" * 40,
"tree_oid": "2" * 40,
"artifact_sha256": "3" * 64,
"manifest_sha256": "4" * 64,
"package_closure_sha256": "5" * 64,
"deployment_plan_sha256": "6" * 64,
"policy_sha256": "7" * 64,
"release_plan_id": "sha256:" + "8" * 64,
"approval_plan_id": "sha256:" + "9" * 64,
"evidence_closure_oid": "a" * 40,
"campaign_authorization_sha256": "b" * 64,
"campaign_generation": 4,
"artifact": {"path": "/var/lib/aios/releases/V2R2C2/artifact.tar.zst", "size": 123},
"rollback": {"version": "V2R2C1", "target_oid": "c" * 40, "artifact_sha256": "d" * 64},
"baseline": {"version": "V2R2C1", "target_oid": "c" * 40},
"adapter_policy": {"policy_id": "aios.production-adapter.allowlisted.v1", "service": "aios-gitops-model-api", "port": 18927},
}
def test_plan_digest_is_recomputed_and_binding_is_exact():
value = plan()
parsed = TrustedReleasePlan.from_mapping(value)
assert parsed.release_plan_sha256 == hashlib.sha256(canonical_json(value)).hexdigest()
assert parsed.candidate_binding_sha256 == hashlib.sha256(canonical_json({k: value[k] for k in ("candidate_id", "source_commit", "tree_oid", "artifact_sha256", "manifest_sha256")})).hexdigest()
parsed.assert_binding({"version": "V2R2C2", "target_oid": "1" * 40, "evidence_closure_oid": "a" * 40, "artifact_sha256": "3" * 64, "artifact_size": 123, "approval_plan_id": "sha256:" + "9" * 64, "candidate_id": "rc-v2r2c2-contract-001"})
def test_claimed_digest_or_candidate_tamper_fails_closed():
value = plan()
value["release_plan_sha256"] = "f" * 64
with pytest.raises(PlanError, match="release_plan_sha256_mismatch"):
TrustedReleasePlan.from_mapping(value)
value = plan()
value["candidate_id"] = "different-candidate"
parsed = TrustedReleasePlan.from_mapping(value)
with pytest.raises(PlanError, match="binding_plan_mismatch"):
parsed.assert_binding({"version": "V2R2C2", "target_oid": "1" * 40, "evidence_closure_oid": "a" * 40, "artifact_sha256": "3" * 64, "artifact_size": 123, "approval_plan_id": "sha256:" + "9" * 64, "candidate_id": "rc-v2r2c2-contract-001"})
def test_plan_cannot_supply_executable_or_shell_path():
value = plan()
value["adapter_policy"] = {"policy_id": "aios.production-adapter.allowlisted.v1", "service": "aios-gitops-model-api", "port": 18927, "executable": "/tmp/evil"}
with pytest.raises(PlanError, match="release_plan_fields_invalid|forbidden_plan_key"):
TrustedReleasePlan.from_mapping(value)
-137
View File
@@ -1,137 +0,0 @@
#!/usr/bin/env python3
from __future__ import annotations
import argparse
from pathlib import Path
import sys
IMPORT_LINE = (
"from aios_agent_task_projection_recovery_http import "
"get_projection as get_canonical_projection, "
"recover_from_json as recover_canonical_projection\n"
)
IMPORT_ANCHOR = '''from aios_online_agent_execution import (
GovernedExecutionBinding,
validate_execution_envelope,
)
'''
GET_OLD = ''' if self.path == "/health":
self._json(
{"status": "ok", "service": "aios-agent-task-initial-execution-projection"},
200,
)
else:
self._json({"error": "NOT_FOUND"}, 404)
'''
GET_NEW = ''' if self.path == "/health":
self._json(
{"status": "ok", "service": "aios-agent-task-initial-execution-projection"},
200,
)
else:
canonical_match = re.fullmatch(
r"/v1/tasks/([^/]+)/canonical-projection", self.path
)
if canonical_match is None:
self._json({"error": "NOT_FOUND"}, 404)
return
if not self._authorized():
self._json({"error": "UNAUTHORIZED"}, 401)
return
if self.store is None:
self._json({"error": "RUNTIME_UNAVAILABLE"}, 503)
return
status, result = get_canonical_projection(
self.store.db_path, canonical_match.group(1)
)
self._json(result, status)
'''
POST_MATCH_ANCHOR = ''' initial_match = re.fullmatch(
r"/v1/tasks/([^/]+)/initial-execution-projection", self.path
)
'''
POST_MATCH_REPLACEMENT = POST_MATCH_ANCHOR + ''' canonical_match = re.fullmatch(
r"/v1/tasks/([^/]+)/canonical-projection-recovery", self.path
)
'''
POST_VALIDATION_OLD = ''' if initial_match is None and recovery_match is None:
self._json({"error": "NOT_FOUND"}, 404)
return
'''
POST_VALIDATION_NEW = ''' if (
initial_match is None
and recovery_match is None
and canonical_match is None
):
self._json({"error": "NOT_FOUND"}, 404)
return
'''
VALUE_HANDLER_OLD = ''' if initial_match is not None:
request = InitialExecutionProjectionRequest.from_mapping(value)
'''
VALUE_HANDLER_NEW = ''' if canonical_match is not None:
status, result = recover_canonical_projection(
self.store.db_path, canonical_match.group(1), value
)
self._json(result, status)
return
if initial_match is not None:
request = InitialExecutionProjectionRequest.from_mapping(value)
'''
class PatchError(RuntimeError):
pass
def replace_once(text: str, old: str, new: str, label: str) -> str:
count = text.count(old)
if count != 1:
raise PatchError(f"{label}_ANCHOR_COUNT_{count}")
return text.replace(old, new, 1)
def apply(path: Path) -> bool:
text = path.read_text(encoding="utf-8")
if "canonical-projection-recovery" in text and IMPORT_LINE.strip() in text:
return False
text = replace_once(text, IMPORT_ANCHOR, IMPORT_ANCHOR + IMPORT_LINE, "IMPORT")
text = replace_once(text, GET_OLD, GET_NEW, "GET")
text = replace_once(
text, POST_MATCH_ANCHOR, POST_MATCH_REPLACEMENT, "POST_MATCH"
)
text = replace_once(
text, POST_VALIDATION_OLD, POST_VALIDATION_NEW, "POST_VALIDATION"
)
text = replace_once(
text, VALUE_HANDLER_OLD, VALUE_HANDLER_NEW, "VALUE_HANDLER"
)
path.write_text(text, encoding="utf-8")
return True
def main() -> int:
parser = argparse.ArgumentParser()
parser.add_argument("path", type=Path)
args = parser.parse_args()
try:
changed = apply(args.path)
except (OSError, PatchError) as exc:
print(f"ERROR:{exc}", file=sys.stderr)
return 2
print("CHANGED" if changed else "ALREADY_APPLIED")
return 0
if __name__ == "__main__":
raise SystemExit(main())
-21
View File
@@ -1,21 +0,0 @@
{
"approval_plan_id": "sha256:c0df52008ae7f64cec5ee2d28947aa7b892066c670ab8295e4549140b1a7500e",
"artifact_sha256": "ab4ae257346c82863f05abf5e309381a1c86612957608c3a4f4ab5cc9233f052",
"deployment_state": "ACTIVE",
"enabled": true,
"evidence_closure_oid": "a8b906d1bc24088c19647e98de6a679268f6d0a2",
"peeled_target_oid": "998d30304787e31e5ae607c0bd86b5edb2b0e5ad",
"production_deployed": true,
"production_port": 18927,
"production_service": "aios-v2r1c7-production.service",
"registered": true,
"repository": "aios/aios",
"schema": "aios.production-registry.v2",
"status": "PRODUCTION_DEPLOYED",
"tag": "V2R2C1",
"tag_object": "07fc2746b688f5f243f29b39de39f32edc3dd9d2",
"tag_oid": "07fc2746b688f5f243f29b39de39f32edc3dd9d2",
"target_oid": "998d30304787e31e5ae607c0bd86b5edb2b0e5ad",
"updated_at": "2026-08-09T09:13:13Z",
"version": "V2R2C1"
}