Files
aios/active-runtime/app/aios_durable_owner_authorization.py
T

1636 lines
63 KiB
Python

#!/usr/bin/env python3
"""Durable Owner Authorization broker for unattended AIOS preparation work.
This module deliberately separates durable Owner intent authorization from
cryptographic release approval. A durable grant may authorize preparation
operations such as governed-task projection and release-candidate creation,
but it can never authorize deployment, tag publication, baseline finalization
or runtime switching.
"""
from __future__ import annotations
from dataclasses import dataclass
from datetime import datetime, timezone, timedelta
import argparse
import hashlib
import grp
import hmac
from http.server import BaseHTTPRequestHandler
import json
import os
import pwd
from pathlib import Path
import re
import secrets
import sqlite3
import socket
import socketserver
import stat
import tempfile
import struct
import threading
import urllib.error
import urllib.request
from typing import Any, Mapping
POLICY_SCHEMA = "aios.owner-durable-authorization.v1"
RECEIPT_SCHEMA = "aios.owner-durable-authorization-receipt.v1"
DECISION_SCHEMA = "aios.owner-durable-authorization-decision.v1"
PROJECTION_SCHEMA = "aios.external-capability-projection.request.v1"
PROJECTION_LIST_SCHEMA = "aios.external-capability-projection.list.v1"
CALLER_PROJECTION_SCHEMA = "aios.caller-admission-projection.v1"
CALLER_PROJECTION_TTL_DEFAULT = 300
CALLER_PROJECTION_SCOPES = frozenset({
"governed-task:project",
"release:candidate",
})
GATE_AMENDMENT_SCHEMA = "aios.release-preparation.gate-amendment.v1"
GATE_AMENDMENT_CLASSIFICATION = "SUPERVISOR_GLOBAL_EXTERNAL_ACK_GATE_REMEDIATED"
ALLOWED_PERSISTENT_CAPABILITIES = frozenset({
"governed-task:project",
"release:candidate",
})
SAFE_PROJECTED_TASK_CAPABILITIES = frozenset({
"git.read",
"git.test",
"governed.write.branch",
"governed.write.file",
})
FORBIDDEN_PERSISTENT_CAPABILITIES = frozenset({
"submitOwnerApproval",
"startDeployment",
"tag_publish",
"baseline_registry_finalize",
"runtime_switch",
"main_write",
"production_deploy",
"force_push",
"credential_admin",
})
ACCOUNT_RE = re.compile(r"^[A-Za-z0-9][A-Za-z0-9._:@/-]{3,255}$")
GRANT_RE = re.compile(r"^[A-Za-z0-9][A-Za-z0-9._:@/-]{7,255}$")
OID_RE = re.compile(r"^[0-9a-f]{40}$")
VERSION_RE = re.compile(r"^V[0-9]+R[0-9]+C[0-9]+$")
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}$")
JOB_RE = re.compile(r"^job-[0-9a-f]{32}$")
MAX_BODY = 512 * 1024
SO_PEERCRED_SIZE = struct.calcsize("3i")
def _peer_process_status(pid: int) -> tuple[int, int, set[int]]:
status_path = Path(f"/proc/{pid}/status")
try:
lines = status_path.read_text(encoding="utf-8").splitlines()
except (OSError, UnicodeError) as exc:
raise AuthorizationError("SOCKPEER_CREDENTIALS_INVALID", 403) from exc
fields: dict[str, str] = {}
for line in lines:
if ":" in line:
key, value = line.split(":", 1)
if key in {"Uid", "Gid", "Groups"}:
fields[key] = value.strip()
try:
uid = int(fields["Uid"].split()[0])
gid = int(fields["Gid"].split()[0])
groups = {int(item) for item in fields["Groups"].split()}
except (KeyError, IndexError, ValueError) as exc:
raise AuthorizationError("SOCKPEER_CREDENTIALS_INVALID", 403) from exc
return uid, gid, groups
def _peer_process_groups(pid: int) -> set[int]:
return _peer_process_status(pid)[2]
def peer_credentials_from_connection(connection: object) -> tuple[int, int, int, set[int]]:
if not hasattr(socket, "SO_PEERCRED"):
raise AuthorizationError("SO_PEERCRED_UNAVAILABLE", 500)
try:
raw = connection.getsockopt(socket.SOL_SOCKET, socket.SO_PEERCRED, SO_PEERCRED_SIZE)
except OSError as exc:
raise AuthorizationError("SO_PEERCRED_UNAVAILABLE", 403) from exc
if len(raw) != SO_PEERCRED_SIZE:
raise AuthorizationError("SOCKPEER_CREDENTIALS_INVALID", 403)
try:
pid, uid, gid = struct.unpack("3i", raw)
except struct.error as exc:
raise AuthorizationError("SOCKPEER_CREDENTIALS_INVALID", 403) from exc
if pid <= 1 or uid < 0 or gid < 0:
raise AuthorizationError("SOCKPEER_CREDENTIALS_INVALID", 403)
process_uid, process_gid, groups = _peer_process_status(pid)
if process_uid != uid or process_gid != gid:
raise AuthorizationError("SOCKPEER_CREDENTIALS_INVALID", 403)
return pid, uid, gid, groups | {gid}
def peer_user_from_connection(connection: object) -> str:
_pid, uid, _gid, _groups = peer_credentials_from_connection(connection)
try:
return pwd.getpwuid(uid).pw_name
except (KeyError, OSError) as exc:
raise AuthorizationError("SO_PEERCRED_UID_UNKNOWN", 403) from exc
CANONICAL_BROKER_GROUP = os.environ.get(
"AIOS_OWNER_AUTH_BROKER_ALLOWED_GROUP",
"aios-autonomous-agent-worker",
).strip()
def authorize_broker_peer(connection: object) -> bool:
_pid, _uid, _gid, groups = peer_credentials_from_connection(connection)
if not CANONICAL_BROKER_GROUP:
raise AuthorizationError("SOCKGROUP_CONFIGURATION_INVALID", 503)
try:
canonical_gid = grp.getgrnam(CANONICAL_BROKER_GROUP).gr_gid
except KeyError as exc:
raise AuthorizationError("SOCKGROUP_CONFIGURATION_INVALID", 503) from exc
if canonical_gid not in groups:
raise AuthorizationError("SOCKGROUP_NOT_AUTHORIZED", 403)
return True
class AuthorizationError(RuntimeError):
def __init__(self, code: str, status: int = 400) -> None:
super().__init__(code)
self.code = code
self.status = status
def canonical(value: object) -> bytes:
return json.dumps(
value,
ensure_ascii=False,
sort_keys=True,
separators=(",", ":"),
).encode("utf-8")
def sha256(value: bytes) -> str:
return hashlib.sha256(value).hexdigest()
def parse_time(value: object, *, field: str) -> datetime:
if not isinstance(value, str) or not value.strip():
raise AuthorizationError("POLICY_" + field.upper() + "_INVALID")
text = value.strip()
if text.endswith("Z"):
text = text[:-1] + "+00:00"
try:
result = datetime.fromisoformat(text)
except ValueError as exc:
raise AuthorizationError("POLICY_" + field.upper() + "_INVALID") from exc
if result.tzinfo is None:
raise AuthorizationError("POLICY_" + field.upper() + "_INVALID")
return result.astimezone(timezone.utc)
def validate_root_file(path: Path, *, max_size: int = 256 * 1024) -> Path:
if path.is_symlink() or not path.is_file():
raise AuthorizationError("POLICY_FILE_INVALID", 503)
info = path.stat(follow_symlinks=False)
if info.st_uid != 0 or info.st_gid != 0:
raise AuthorizationError("POLICY_FILE_OWNERSHIP_INVALID", 503)
if stat.S_IMODE(info.st_mode) != 0o600:
raise AuthorizationError("POLICY_FILE_MODE_INVALID", 503)
if info.st_size < 16 or info.st_size > max_size:
raise AuthorizationError("POLICY_FILE_SIZE_INVALID", 503)
return path
@dataclass(frozen=True)
class Constraints:
repository: str
account: str
allowed_target_prefix: str
release_version_prefix: str
max_task_risk: str
@dataclass(frozen=True)
class DurablePolicy:
grant_id: str
owner_identity: str
account: str
capabilities: frozenset[str]
issued_at: datetime
not_before: datetime
persistent_until_revoked: bool
constraints: Constraints
policy_sha256: str
expires_at: datetime | None
@classmethod
def load(
cls,
policy_path: Path,
revocation_path: Path | None = None,
*,
now: datetime | None = None,
) -> "DurablePolicy":
validate_root_file(policy_path)
try:
value = json.loads(policy_path.read_text(encoding="utf-8"))
except Exception as exc:
raise AuthorizationError("POLICY_JSON_INVALID", 503) from exc
if not isinstance(value, Mapping):
raise AuthorizationError("POLICY_JSON_INVALID", 503)
allowed_fields = {
"schema",
"grant_id",
"owner_identity",
"account",
"capabilities",
"issued_at",
"not_before",
"expires_at",
"persistent_until_revoked",
"constraints",
}
if (
not set(value).issubset(allowed_fields)
or value.get("schema") != POLICY_SCHEMA
):
raise AuthorizationError("POLICY_FIELDS_INVALID", 503)
grant_id = str(value.get("grant_id") or "")
owner_identity = str(value.get("owner_identity") or "")
account = str(value.get("account") or "")
if GRANT_RE.fullmatch(grant_id) is None:
raise AuthorizationError("POLICY_GRANT_ID_INVALID", 503)
if ACCOUNT_RE.fullmatch(owner_identity) is None:
raise AuthorizationError("POLICY_OWNER_IDENTITY_INVALID", 503)
if ACCOUNT_RE.fullmatch(account) is None:
raise AuthorizationError("POLICY_ACCOUNT_INVALID", 503)
raw_caps = value.get("capabilities")
if not isinstance(raw_caps, list) or not raw_caps:
raise AuthorizationError("POLICY_CAPABILITIES_INVALID", 503)
capabilities = frozenset(str(item) for item in raw_caps)
if len(capabilities) != len(raw_caps):
raise AuthorizationError("POLICY_CAPABILITIES_DUPLICATE", 503)
if not capabilities.issubset(ALLOWED_PERSISTENT_CAPABILITIES):
raise AuthorizationError("POLICY_CAPABILITY_NOT_PERSISTABLE", 403)
if capabilities & FORBIDDEN_PERSISTENT_CAPABILITIES:
raise AuthorizationError("POLICY_FORBIDDEN_CAPABILITY", 403)
issued_at = parse_time(value.get("issued_at"), field="issued_at")
not_before = parse_time(value.get("not_before"), field="not_before")
current = now or datetime.now(timezone.utc)
if issued_at > not_before or current < not_before:
raise AuthorizationError("POLICY_NOT_ACTIVE", 403)
expires_at = None
if value.get("expires_at") is not None:
expires_at = parse_time(value.get("expires_at"), field="expires_at")
if expires_at <= not_before:
raise AuthorizationError("POLICY_EXPIRY_INVALID", 503)
if current >= expires_at:
raise AuthorizationError("POLICY_EXPIRED", 403)
if value.get("persistent_until_revoked") is not True:
raise AuthorizationError("POLICY_PERSISTENCE_INVALID", 503)
raw_constraints = value.get("constraints")
if not isinstance(raw_constraints, Mapping) or set(raw_constraints) != {
"repository",
"allowed_target_prefix",
"release_version_prefix",
"max_task_risk",
}:
raise AuthorizationError("POLICY_CONSTRAINTS_INVALID", 503)
if raw_constraints.get("repository") != "aios/aios":
raise AuthorizationError("POLICY_REPOSITORY_INVALID", 503)
prefix = str(raw_constraints.get("allowed_target_prefix") or "")
if prefix != "agent/":
raise AuthorizationError("POLICY_TARGET_PREFIX_INVALID", 503)
version_prefix = str(raw_constraints.get("release_version_prefix") or "")
if version_prefix != "V2R2C":
raise AuthorizationError("POLICY_RELEASE_VERSION_PREFIX_INVALID", 503)
max_risk = str(raw_constraints.get("max_task_risk") or "")
if max_risk not in {"low", "engineering"}:
raise AuthorizationError("POLICY_MAX_RISK_INVALID", 503)
if revocation_path is not None and revocation_path.exists():
validate_root_file(revocation_path, max_size=64 * 1024)
try:
revoked = json.loads(revocation_path.read_text(encoding="utf-8"))
except Exception as exc:
raise AuthorizationError("REVOCATION_JSON_INVALID", 503) from exc
if not isinstance(revoked, Mapping):
raise AuthorizationError("REVOCATION_JSON_INVALID", 503)
grants = revoked.get("revoked_grant_ids")
if not isinstance(grants, list) or not all(isinstance(x, str) for x in grants):
raise AuthorizationError("REVOCATION_FIELDS_INVALID", 503)
if grant_id in grants:
raise AuthorizationError("OWNER_GRANT_REVOKED", 403)
return cls(
grant_id=grant_id,
owner_identity=owner_identity,
account=account,
capabilities=capabilities,
issued_at=issued_at,
not_before=not_before,
persistent_until_revoked=True,
constraints=Constraints(
repository="aios/aios",
account=account,
allowed_target_prefix=prefix,
release_version_prefix=version_prefix,
max_task_risk=max_risk,
),
policy_sha256=sha256(policy_path.read_bytes()),
expires_at=expires_at,
)
def validate_caller_scope(
self,
caller: str,
scope: str,
request: Mapping[str, Any],
) -> dict[str, Any]:
if caller != self.account:
raise AuthorizationError("CALLER_MISMATCH", 403)
if scope not in CALLER_PROJECTION_SCOPES:
raise AuthorizationError("SCOPE_MISMATCH", 403)
if scope not in self.capabilities:
raise AuthorizationError("CAPABILITY_NOT_AUTHORIZED", 403)
if not isinstance(request, Mapping):
raise AuthorizationError("CALLER_PROJECTION_REQUEST_INVALID")
if scope == "release:candidate":
if set(request) != {"source_commit", "version"}:
raise AuthorizationError("CALLER_PROJECTION_FIELDS_INVALID")
source_commit = str(request.get("source_commit") or "")
version = str(request.get("version") or "")
if OID_RE.fullmatch(source_commit) is None:
raise AuthorizationError("RELEASE_SOURCE_COMMIT_INVALID")
if VERSION_RE.fullmatch(version) is None or not version.startswith(
self.constraints.release_version_prefix
):
raise AuthorizationError("RELEASE_VERSION_NOT_AUTHORIZED", 403)
elif request:
raise AuthorizationError("CALLER_PROJECTION_FIELDS_INVALID")
return {
"schema": CALLER_PROJECTION_SCHEMA,
"caller": caller,
"scope": scope,
"request_sha256": sha256(canonical(request)),
}
def authorize(self, capability: str, request: Mapping[str, Any]) -> dict[str, Any]:
if capability not in self.capabilities:
raise AuthorizationError("CAPABILITY_NOT_AUTHORIZED", 403)
if capability in FORBIDDEN_PERSISTENT_CAPABILITIES:
raise AuthorizationError("CAPABILITY_REQUIRES_SEPARATE_OWNER_APPROVAL", 403)
if capability == "governed-task:project":
required = {
"task_id",
"repository",
"base_oid",
"expected_branch_oid",
"target_branch",
"generation",
"risk",
"description",
"idempotency_key",
"priority",
"max_attempts",
"require_online_task_projection",
"online_task",
"execution",
}
optional = {"branch_mode"}
if set(request) - (required | optional) or not required.issubset(request):
raise AuthorizationError("GOVERNED_TASK_FIELDS_INVALID")
if request["repository"] != self.constraints.repository:
raise AuthorizationError("REPOSITORY_NOT_AUTHORIZED", 403)
if not TASK_RE.fullmatch(str(request["task_id"])):
raise AuthorizationError("TASK_ID_INVALID")
base = str(request["base_oid"])
expected = str(request["expected_branch_oid"])
if not OID_RE.fullmatch(base) or expected != base:
raise AuthorizationError("BASE_BINDING_INVALID")
branch = str(request["target_branch"])
if BRANCH_RE.fullmatch(branch) is None or not branch.startswith(
self.constraints.allowed_target_prefix
):
raise AuthorizationError("TARGET_BRANCH_NOT_AUTHORIZED", 403)
if int(request["generation"]) != 1:
raise AuthorizationError("GENERATION_NOT_AUTHORIZED", 403)
risk = str(request["risk"])
allowed_risks = {"low"} if self.constraints.max_task_risk == "low" else {
"low",
"engineering",
}
if risk not in allowed_risks:
raise AuthorizationError("TASK_RISK_NOT_AUTHORIZED", 403)
if request.get("branch_mode", "CREATE_NEW") != "CREATE_NEW":
raise AuthorizationError("BRANCH_MODE_NOT_AUTHORIZED", 403)
priority = request["priority"]
if (
not isinstance(priority, int)
or isinstance(priority, bool)
or not -100 <= priority <= 100
):
raise AuthorizationError("TASK_PRIORITY_INVALID")
max_attempts = request["max_attempts"]
if (
not isinstance(max_attempts, int)
or isinstance(max_attempts, bool)
or not 1 <= max_attempts <= 3
):
raise AuthorizationError("TASK_MAX_ATTEMPTS_INVALID")
if request["require_online_task_projection"] is not True:
raise AuthorizationError("ONLINE_TASK_PROJECTION_REQUIRED", 403)
online = request["online_task"]
execution = request["execution"]
if not isinstance(online, Mapping) or not isinstance(execution, Mapping):
raise AuthorizationError("ONLINE_TASK_ENVELOPE_INVALID")
online_allowed = {
"goal",
"instructions",
"success_criteria",
"constraints",
"required_capabilities",
"priority",
"max_attempts",
}
if set(online) != online_allowed:
raise AuthorizationError("ONLINE_TASK_FIELDS_INVALID")
if (
not isinstance(online["goal"], str)
or not online["goal"].strip()
or len(online["goal"]) > 12_000
or not isinstance(online["instructions"], str)
or len(online["instructions"]) > 24_000
or not isinstance(online["success_criteria"], list)
or len(online["success_criteria"]) > 32
or not isinstance(online["constraints"], Mapping)
):
raise AuthorizationError("ONLINE_TASK_CONTENT_INVALID")
projected_caps = online["required_capabilities"]
if (
not isinstance(projected_caps, list)
or not projected_caps
or len(projected_caps) != len(set(projected_caps))
or not frozenset(projected_caps).issubset(
SAFE_PROJECTED_TASK_CAPABILITIES
)
):
raise AuthorizationError("ONLINE_TASK_CAPABILITIES_NOT_AUTHORIZED", 403)
if online["priority"] != priority or online["max_attempts"] != max_attempts:
raise AuthorizationError("ONLINE_TASK_LIMITS_MISMATCH")
execution_allowed = {
"action",
"adapter",
"capability_scope",
"source_signals",
}
if set(execution) != execution_allowed:
raise AuthorizationError("EXECUTION_FIELDS_INVALID")
if execution.get("action") != "source_capture" or execution.get(
"adapter"
) != "source_capture":
raise AuthorizationError("INITIAL_EXECUTION_ACTION_NOT_AUTHORIZED", 403)
scope = execution.get("capability_scope")
if (
not isinstance(scope, list)
or sorted(scope) != sorted(projected_caps)
):
raise AuthorizationError("EXECUTION_CAPABILITY_SCOPE_MISMATCH")
signals = execution.get("source_signals")
if (
not isinstance(signals, list)
or len(signals) > 32
or not all(isinstance(item, str) and len(item) <= 1_000 for item in signals)
):
raise AuthorizationError("SOURCE_SIGNALS_INVALID")
elif capability == "release:candidate":
required = {"candidate_id", "source_commit", "version"}
if set(request) != required:
raise AuthorizationError("RELEASE_CANDIDATE_FIELDS_INVALID")
if not GRANT_RE.fullmatch(str(request["candidate_id"])):
raise AuthorizationError("RELEASE_CANDIDATE_ID_INVALID")
if OID_RE.fullmatch(str(request["source_commit"])) is None:
raise AuthorizationError("RELEASE_SOURCE_COMMIT_INVALID")
version = str(request["version"])
if VERSION_RE.fullmatch(version) is None or not version.startswith(
self.constraints.release_version_prefix
):
raise AuthorizationError("RELEASE_VERSION_NOT_AUTHORIZED", 403)
else:
raise AuthorizationError("CAPABILITY_NOT_SUPPORTED", 403)
return {
"schema": DECISION_SCHEMA,
"decision": "ALLOW",
"grant_id": self.grant_id,
"owner_identity": self.owner_identity,
"account": self.account,
"capability": capability,
"policy_sha256": self.policy_sha256,
"request_sha256": sha256(canonical(request)),
"persistent_until_revoked": True,
}
class ReceiptStore:
def __init__(self, path: Path) -> None:
self.path = path
self.path.parent.mkdir(parents=True, exist_ok=True)
with sqlite3.connect(self.path) as con:
con.execute(
"""
CREATE TABLE IF NOT EXISTS receipts(
idempotency_key TEXT PRIMARY KEY,
capability TEXT NOT NULL,
request_sha256 TEXT NOT NULL,
response_sha256 TEXT NOT NULL,
response_json TEXT NOT NULL,
created_at TEXT NOT NULL
)
"""
)
con.execute(
"""
CREATE TABLE IF NOT EXISTS projection_outbox(
projection_id TEXT PRIMARY KEY,
idempotency_key TEXT NOT NULL UNIQUE,
grant_id TEXT NOT NULL,
account TEXT NOT NULL,
capability TEXT NOT NULL,
policy_sha256 TEXT NOT NULL,
request_sha256 TEXT NOT NULL,
payload_sha256 TEXT NOT NULL,
payload_json TEXT NOT NULL,
state TEXT NOT NULL,
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL
)
"""
)
con.execute(
"""
CREATE INDEX IF NOT EXISTS idx_projection_outbox_state
ON projection_outbox(state, capability, created_at)
"""
)
con.execute(
"""
CREATE TABLE IF NOT EXISTS caller_projections(
projection_id TEXT PRIMARY KEY,
caller TEXT NOT NULL,
scope TEXT NOT NULL,
grant_id TEXT NOT NULL,
policy_sha256 TEXT NOT NULL,
projection_state TEXT NOT NULL,
credential_state TEXT NOT NULL,
rehydration_state TEXT NOT NULL,
issued_at TEXT NOT NULL,
expires_at TEXT NOT NULL,
updated_at TEXT NOT NULL,
UNIQUE(caller, scope)
)
"""
)
con.execute(
"""
CREATE TABLE IF NOT EXISTS gate_amendments(
idempotency_key TEXT PRIMARY KEY,
amendment_sha256 TEXT NOT NULL,
amendment_json TEXT NOT NULL,
created_at TEXT NOT NULL
)
"""
)
con.commit()
self._lock = threading.RLock()
@staticmethod
def caller_projection_id(caller: str, scope: str) -> str:
digest = sha256(canonical({"caller": caller, "scope": scope}))
return "caller-projection-" + digest[:32]
def upsert_caller_projection(
self,
*,
caller: str,
scope: str,
grant_id: str,
policy_sha256: str,
ttl_seconds: int,
effective_scopes: Any,
) -> dict[str, Any]:
if not 30 <= ttl_seconds <= 900:
raise AuthorizationError("CALLER_PROJECTION_TTL_INVALID", 503)
normalized_scopes = sorted({str(item) for item in effective_scopes})
if (
not normalized_scopes
or scope not in normalized_scopes
or not set(normalized_scopes).issubset(CALLER_PROJECTION_SCOPES)
):
raise AuthorizationError("CALLER_PROJECTION_SCOPES_INVALID", 503)
requested_projection_id = self.caller_projection_id(caller, scope)
issued = datetime.now(timezone.utc)
issued_at = issued.isoformat()
expires_at = (issued + timedelta(seconds=ttl_seconds)).isoformat()
requested_recovery = False
with self._lock, sqlite3.connect(self.path) as con:
con.row_factory = sqlite3.Row
con.execute("BEGIN IMMEDIATE")
for projected_scope in normalized_scopes:
projection_id = self.caller_projection_id(caller, projected_scope)
row = con.execute(
"SELECT * FROM caller_projections WHERE caller=? AND scope=?",
(caller, projected_scope),
).fetchone()
if row is not None and row["projection_state"] not in {"ACKED", "PENDING"}:
con.rollback()
raise AuthorizationError("PROJECTION_STATE_INVALID", 503)
recovery = row is not None
if projected_scope == scope:
requested_recovery = recovery
if row is None:
con.execute(
"""
INSERT INTO caller_projections(
projection_id,caller,scope,grant_id,policy_sha256,
projection_state,credential_state,rehydration_state,
issued_at,expires_at,updated_at
) VALUES(?,?,?,?,?,?,?,?,?,?,?)
""",
(
projection_id,
caller,
projected_scope,
grant_id,
policy_sha256,
"PENDING",
"GENERATING",
"RECOVERING" if recovery else "GENERATING",
issued_at,
expires_at,
issued_at,
),
)
else:
con.execute(
"""
UPDATE caller_projections
SET projection_id=?,grant_id=?,policy_sha256=?,
projection_state='PENDING',
credential_state='GENERATING',
rehydration_state=?,
issued_at=?,expires_at=?,updated_at=?
WHERE caller=? AND scope=?
""",
(
projection_id,
grant_id,
policy_sha256,
"RECOVERING",
issued_at,
expires_at,
issued_at,
caller,
projected_scope,
),
)
con.execute(
"""
UPDATE caller_projections
SET projection_state='ACKED',
credential_state='REHYDRATED',
rehydration_state=?,
updated_at=?
WHERE caller=? AND scope=?
""",
(
"RECOVERED" if recovery else "GENERATED",
issued_at,
caller,
projected_scope,
),
)
con.commit()
return {
"schema": CALLER_PROJECTION_SCHEMA,
"projection_id": requested_projection_id,
"caller": caller,
"scope": scope,
"grant_id": grant_id,
"policy_sha256": policy_sha256,
"projection_state": "ACKED",
"credential_state": "REHYDRATED",
"rehydration_state": "RECOVERED" if requested_recovery else "GENERATED",
"effective_scopes": normalized_scopes,
"issued_at": issued_at,
"expires_at": expires_at,
"secret_exposed": False,
}
def get(self, key: str) -> dict[str, Any] | None:
with sqlite3.connect(self.path) as con:
row = con.execute(
"SELECT request_sha256,response_json FROM receipts WHERE idempotency_key=?",
(key,),
).fetchone()
if row is None:
return None
return {"request_sha256": row[0], "response": json.loads(row[1])}
def put(
self,
key: str,
capability: str,
request_sha256: str,
response: Mapping[str, Any],
) -> None:
encoded = json.dumps(response, sort_keys=True, separators=(",", ":"))
with self._lock, sqlite3.connect(self.path) as con:
con.execute(
"""
INSERT INTO receipts(
idempotency_key,capability,request_sha256,response_sha256,
response_json,created_at
) VALUES(?,?,?,?,?,?)
""",
(
key,
capability,
request_sha256,
sha256(encoded.encode()),
encoded,
datetime.now(timezone.utc).isoformat(),
),
)
con.commit()
def update_response(
self,
key: str,
request_sha256: str,
response: Mapping[str, Any],
) -> None:
encoded = json.dumps(response, sort_keys=True, separators=(",", ":"))
with self._lock, sqlite3.connect(self.path) as con:
con.execute("BEGIN IMMEDIATE")
row = con.execute(
"""
SELECT request_sha256 FROM receipts
WHERE idempotency_key=?
""",
(key,),
).fetchone()
if row is None:
con.rollback()
raise AuthorizationError("RECEIPT_NOT_FOUND", 409)
if row[0] != request_sha256:
con.rollback()
raise AuthorizationError("IDEMPOTENCY_CONFLICT", 409)
con.execute(
"""
UPDATE receipts
SET response_sha256=?,response_json=?
WHERE idempotency_key=?
""",
(sha256(encoded.encode()), encoded, key),
)
con.commit()
def put_gate_amendment(
self,
idempotency_key: str,
amendment: Mapping[str, Any],
) -> dict[str, Any]:
encoded = json.dumps(amendment, sort_keys=True, separators=(",", ":"))
lowered = encoded.lower()
if any(marker in lowered for marker in (
"bearer ", "access_token", "refresh_token", "private_key", "credential_file"
)):
raise AuthorizationError("GATE_AMENDMENT_SECRET_MATERIAL_DENY", 403)
digest = sha256(encoded.encode())
with self._lock, sqlite3.connect(self.path) as con:
con.execute("BEGIN IMMEDIATE")
row = con.execute(
"SELECT amendment_sha256,amendment_json FROM gate_amendments WHERE idempotency_key=?",
(idempotency_key,),
).fetchone()
if row is not None:
if row[0] != digest or row[1] != encoded:
con.rollback()
raise AuthorizationError("GATE_AMENDMENT_IDEMPOTENCY_CONFLICT", 409)
con.commit()
return {"amendment_sha256": digest, "idempotent": True}
con.execute(
"INSERT INTO gate_amendments(idempotency_key,amendment_sha256,amendment_json,created_at) VALUES(?,?,?,?)",
(idempotency_key, digest, encoded, datetime.now(timezone.utc).isoformat()),
)
con.commit()
return {"amendment_sha256": digest, "idempotent": False}
@staticmethod
def projection_id(
grant_id: str,
account: str,
capability: str,
request_sha256: str,
) -> str:
digest = sha256(
canonical(
{
"grant_id": grant_id,
"account": account,
"capability": capability,
"request_sha256": request_sha256,
}
)
)
return "projection-" + digest[:32]
def enqueue_projection(
self,
*,
idempotency_key: str,
grant_id: str,
account: str,
capability: str,
policy_sha256: str,
request_sha256: str,
request: Mapping[str, Any],
) -> dict[str, Any]:
projection_id = self.projection_id(
grant_id,
account,
capability,
request_sha256,
)
now = datetime.now(timezone.utc).isoformat()
payload = {
"schema": PROJECTION_SCHEMA,
"projection_id": projection_id,
"state": "PENDING_EXTERNAL_CONNECTOR",
"grant_id": grant_id,
"account": account,
"capability": capability,
"policy_sha256": policy_sha256,
"request_sha256": request_sha256,
"request": dict(request),
"created_at": now,
"separate_owner_release_approval_still_required": True,
}
encoded = json.dumps(payload, sort_keys=True, separators=(",", ":"))
payload_sha = sha256(encoded.encode())
with self._lock, sqlite3.connect(self.path) as con:
con.row_factory = sqlite3.Row
con.execute("BEGIN IMMEDIATE")
existing = con.execute(
"""
SELECT * FROM projection_outbox
WHERE idempotency_key=?
""",
(idempotency_key,),
).fetchone()
if existing is not None:
if (
existing["projection_id"] != projection_id
or existing["request_sha256"] != request_sha256
or existing["grant_id"] != grant_id
or existing["account"] != account
or existing["capability"] != capability
or existing["policy_sha256"] != policy_sha256
):
con.rollback()
raise AuthorizationError(
"PROJECTION_IDEMPOTENCY_CONFLICT",
409,
)
value = json.loads(existing["payload_json"])
con.commit()
return value
con.execute(
"""
INSERT INTO projection_outbox(
projection_id,idempotency_key,grant_id,account,capability,
policy_sha256,request_sha256,payload_sha256,payload_json,
state,created_at,updated_at
) VALUES(?,?,?,?,?,?,?,?,?,?,?,?)
""",
(
projection_id,
idempotency_key,
grant_id,
account,
capability,
policy_sha256,
request_sha256,
payload_sha,
encoded,
"PENDING_EXTERNAL_CONNECTOR",
now,
now,
),
)
con.commit()
return payload
def get_projection(self, projection_id: str) -> dict[str, Any] | None:
if not re.fullmatch(r"projection-[0-9a-f]{32}", projection_id):
raise AuthorizationError("PROJECTION_ID_INVALID")
with sqlite3.connect(self.path) as con:
row = con.execute(
"""
SELECT payload_json FROM projection_outbox
WHERE projection_id=?
""",
(projection_id,),
).fetchone()
if row is None:
return None
return json.loads(row[0])
def list_projections(
self,
*,
grant_id: str,
account: str,
capability: str = "release:candidate",
limit: int = 100,
) -> list[dict[str, Any]]:
if not 1 <= limit <= 500:
raise AuthorizationError("PROJECTION_LIMIT_INVALID")
with sqlite3.connect(self.path) as con:
rows = con.execute(
"""
SELECT payload_json
FROM projection_outbox
WHERE grant_id=? AND account=? AND capability=?
ORDER BY created_at,idempotency_key
LIMIT ?
""",
(grant_id, account, capability, limit),
).fetchall()
return [json.loads(row[0]) for row in rows]
@dataclass(frozen=True)
class Upstream:
bridge_url: str
bridge_token: str
def post_bridge(
self,
path: str,
body: Mapping[str, Any],
) -> tuple[int, dict[str, Any]]:
req = urllib.request.Request(
self.bridge_url.rstrip("/") + path,
method="POST",
data=canonical(body),
headers={
"Authorization": "Bearer " + self.bridge_token,
"Content-Type": "application/json",
"Accept": "application/json",
},
)
try:
with urllib.request.urlopen(req, timeout=30) as response:
raw, status = response.read(), response.status
except urllib.error.HTTPError as exc:
raw, status = exc.read(), exc.code
except Exception as exc:
raise AuthorizationError("UPSTREAM_TRANSPORT_FAILED", 503) from exc
try:
value = json.loads(raw.decode())
except Exception as exc:
raise AuthorizationError("UPSTREAM_RESPONSE_INVALID", 502) from exc
if not isinstance(value, Mapping):
raise AuthorizationError("UPSTREAM_RESPONSE_INVALID", 502)
return status, dict(value)
def project_governed_task(
self,
body: Mapping[str, Any],
) -> tuple[int, dict[str, Any]]:
return self.post_bridge("/v2/governed-task", body)
class Broker:
def __init__(
self,
*,
policy_path: Path,
revocation_path: Path,
store: ReceiptStore,
upstream: Upstream,
expected_account: str,
expected_peer_user: str,
allowed_peer_users: frozenset[str] | None = None,
) -> None:
self.policy_path = policy_path
self.revocation_path = revocation_path
self.store = store
self.upstream = upstream
self.expected_account = expected_account
self.expected_peer_user = expected_peer_user
self.allowed_peer_users = allowed_peer_users or frozenset({expected_peer_user})
@staticmethod
def validate_caller_projection(
value: Mapping[str, Any],
*,
expected_scopes: Any = None,
) -> dict[str, Any]:
expected = sorted(
set(expected_scopes) if expected_scopes is not None else CALLER_PROJECTION_SCOPES
)
if value.get("caller") != "chatgpt-governed-implementer-singlehost":
raise AuthorizationError("CALLER_MISMATCH", 503)
if value.get("scope") not in expected:
raise AuthorizationError("SCOPE_MISMATCH", 503)
if value.get("projection_state") != "ACKED":
raise AuthorizationError("PROJECTION_STATE_INVALID", 503)
if value.get("credential_state") != "REHYDRATED":
raise AuthorizationError("CREDENTIAL_REHYDRATION_INVALID", 503)
if value.get("rehydration_state") not in {"GENERATED", "RECOVERED"}:
raise AuthorizationError("REHYDRATION_STATE_INVALID", 503)
if value.get("effective_scopes") != expected:
raise AuthorizationError("PROJECTION_SCOPE_INVALID", 503)
if value.get("secret_exposed") is not False:
raise AuthorizationError("PROJECTION_SECRET_EXPOSURE", 503)
return dict(value)
def project_caller(
self,
caller: str,
scope: str,
request: Mapping[str, Any],
*,
idempotency_key: str,
) -> dict[str, Any]:
if not GRANT_RE.fullmatch(idempotency_key):
raise AuthorizationError("IDEMPOTENCY_KEY_INVALID")
if caller != self.expected_account:
raise AuthorizationError("CALLER_MISMATCH", 403)
if scope not in CALLER_PROJECTION_SCOPES:
raise AuthorizationError("SCOPE_MISMATCH", 403)
policy = self.policy()
policy.validate_caller_scope(caller, scope, request)
effective_scopes = sorted(policy.capabilities & CALLER_PROJECTION_SCOPES)
try:
ttl_seconds = int(
os.environ.get(
"AIOS_OWNER_AUTH_CALLER_PROJECTION_TTL_SECONDS",
str(CALLER_PROJECTION_TTL_DEFAULT),
)
)
except ValueError as exc:
raise AuthorizationError("CALLER_PROJECTION_TTL_INVALID", 503) from exc
credential = secrets.token_urlsafe(32)
value = self.store.upsert_caller_projection(
caller=caller,
scope=scope,
grant_id=policy.grant_id,
policy_sha256=policy.policy_sha256,
ttl_seconds=ttl_seconds,
effective_scopes=effective_scopes,
)
value = self.validate_caller_projection(
value,
expected_scopes=effective_scopes,
)
value["_credential"] = credential
return value
def policy(self) -> DurablePolicy:
policy = DurablePolicy.load(self.policy_path, self.revocation_path)
if policy.account != self.expected_account:
raise AuthorizationError("POLICY_ACCOUNT_MISMATCH", 403)
return policy
def projection(self, projection_id: str) -> dict[str, Any]:
policy = self.policy()
value = self.store.get_projection(projection_id)
if value is None:
raise AuthorizationError("PROJECTION_NOT_FOUND", 404)
if value.get("grant_id") != policy.grant_id:
raise AuthorizationError("PROJECTION_GRANT_MISMATCH", 403)
if value.get("account") != policy.account:
raise AuthorizationError("PROJECTION_ACCOUNT_MISMATCH", 403)
return value
def projections(self, *, limit: int = 100) -> dict[str, Any]:
policy = self.policy()
items = self.store.list_projections(
grant_id=policy.grant_id,
account=policy.account,
limit=limit,
)
return {
"schema": PROJECTION_LIST_SCHEMA,
"grant_id": policy.grant_id,
"account": policy.account,
"capability": "release:candidate",
"count": len(items),
"items": items,
"external_connector_ack_supported": False,
"separate_owner_release_approval_still_required": True,
}
@staticmethod
def validate_gate_amendment(
amendment: Mapping[str, Any],
*,
policy: DurablePolicy,
request: Mapping[str, Any],
) -> dict[str, Any]:
expected = {
"schema": GATE_AMENDMENT_SCHEMA,
"classification": GATE_AMENDMENT_CLASSIFICATION,
"supervisor_task_id": "r42-release-preparation-autonomous-supervisor-20260820",
"supervisor_execution_digest": "491457595667a95809f6f3cd19e9e7c9e74181070fd8038b86f30eb41bdfc231",
"actor_type": "credentialed_host_coordinator",
"operation": "createGovernedTask",
"repository": "aios/aios",
"original_external_request_sha256": "08fc2f3d1b64a2ca11da9f22da48de834cff3442fa7fa2d3bc390f8dbf9b254a",
"external_connector_rule": "ACK_STILL_REQUIRED",
"host_coordinator_rule": "DURABLE_OWNER_GOVERNED_TASK_PROJECT",
"owner_grant_id": policy.grant_id,
"owner_policy_sha256": policy.policy_sha256,
"builder_task_id": "r42-trusted-candidate-builder-runtime-wiring-20260820",
"builder_base_oid": "d957a216a6e9bd4c2292ffc6e44876a1c08fa1e3",
"builder_expected_branch_oid": "d957a216a6e9bd4c2292ffc6e44876a1c08fa1e3",
"builder_target_branch": "agent/r42-trusted-candidate-builder-runtime-wiring-20260820",
"builder_generation": 1,
"builder_idempotency_key": "r42-trusted-candidate-builder-runtime-wiring-binding-v1",
}
value = dict(amendment)
if value != expected:
raise AuthorizationError("GATE_AMENDMENT_BINDING_INVALID", 403)
request_coordinates = {
"task_id": expected["builder_task_id"],
"base_oid": expected["builder_base_oid"],
"expected_branch_oid": expected["builder_expected_branch_oid"],
"target_branch": expected["builder_target_branch"],
"generation": expected["builder_generation"],
"idempotency_key": expected["builder_idempotency_key"],
"repository": expected["repository"],
}
if any(request.get(key) != item for key, item in request_coordinates.items()):
raise AuthorizationError("GATE_AMENDMENT_BUILDER_BINDING_INVALID", 403)
return value
def execute(
self,
capability: str,
request: Mapping[str, Any],
*,
idempotency_key: str,
amendment: Mapping[str, Any] | None = None,
) -> dict[str, Any]:
if not GRANT_RE.fullmatch(idempotency_key):
raise AuthorizationError("IDEMPOTENCY_KEY_INVALID")
policy = self.policy()
decision = policy.authorize(capability, request)
request_sha = decision["request_sha256"]
amendment_value: dict[str, Any] | None = None
if amendment is not None:
if capability != "governed-task:project":
raise AuthorizationError("GATE_AMENDMENT_CAPABILITY_INVALID", 403)
amendment_value = self.validate_gate_amendment(
amendment,
policy=policy,
request=request,
)
prior = self.store.get(idempotency_key)
if prior is not None:
if prior["request_sha256"] != request_sha:
raise AuthorizationError("IDEMPOTENCY_CONFLICT", 409)
response = prior["response"]
if amendment_value is not None:
amendment_result = self.store.put_gate_amendment(
idempotency_key,
amendment_value,
)
response = {
**response,
"gate_amendment": {
"classification": GATE_AMENDMENT_CLASSIFICATION,
"amendment_sha256": amendment_result["amendment_sha256"],
},
}
self.store.update_response(idempotency_key, request_sha, response)
if (
capability == "release:candidate"
and not response.get("projection_id")
):
projection = self.store.enqueue_projection(
idempotency_key=idempotency_key,
grant_id=policy.grant_id,
account=policy.account,
capability=capability,
policy_sha256=policy.policy_sha256,
request_sha256=request_sha,
request=request,
)
response = {
**response,
"projection_id": projection["projection_id"],
"projection_state": projection["state"],
"external_capability_projection_required": True,
"separate_owner_release_approval_still_required": True,
}
self.store.update_response(
idempotency_key,
request_sha,
response,
)
return response
if capability == "governed-task:project":
status, value = self.upstream.project_governed_task(request)
if status not in {200, 201, 202}:
raise AuthorizationError(
"GOVERNED_TASK_PROJECTION_FAILED",
409 if status == 409 else 503,
)
if value.get("task_id") != request["task_id"]:
raise AuthorizationError("GOVERNED_TASK_RESPONSE_INVALID", 502)
operation_state = "EXECUTED"
upstream_status: int | None = status
upstream_response: Mapping[str, Any] | None = value
elif capability == "release:candidate":
# Persist a machine-readable projection request. The connector
# remains the only component that can turn this request into a real
# account scope; this broker never self-grants or calls a fake
# release-builder endpoint.
projection = self.store.enqueue_projection(
idempotency_key=idempotency_key,
grant_id=policy.grant_id,
account=policy.account,
capability=capability,
policy_sha256=policy.policy_sha256,
request_sha256=request_sha,
request=request,
)
operation_state = "AUTHORIZED_PENDING_EXTERNAL_CAPABILITY_PROJECTION"
upstream_status = None
upstream_response = None
else:
raise AuthorizationError("CAPABILITY_NOT_SUPPORTED", 403)
response = {
"schema": RECEIPT_SCHEMA,
"grant_id": policy.grant_id,
"owner_identity": policy.owner_identity,
"account": policy.account,
"capability": capability,
"policy_sha256": policy.policy_sha256,
"request_sha256": request_sha,
"idempotency_key": idempotency_key,
"operation_state": operation_state,
"upstream_status": upstream_status,
"upstream_response": upstream_response,
"external_capability_projection_required": (
capability == "release:candidate"
),
"projection_id": (
projection["projection_id"]
if capability == "release:candidate"
else None
),
"projection_state": (
projection["state"]
if capability == "release:candidate"
else None
),
"separate_owner_release_approval_still_required": True,
}
if amendment_value is not None:
amendment_result = self.store.put_gate_amendment(
idempotency_key,
amendment_value,
)
response["gate_amendment"] = {
"classification": GATE_AMENDMENT_CLASSIFICATION,
"amendment_sha256": amendment_result["amendment_sha256"],
}
self.store.put(idempotency_key, capability, request_sha, response)
return response
def load_secret(path: Path, *, code: str) -> str:
if path.is_symlink() or not path.is_file():
raise RuntimeError(code + "_UNAVAILABLE")
value = path.read_text(encoding="utf-8").strip()
if len(value) < 32 or any(ch.isspace() for ch in value):
raise RuntimeError(code + "_INVALID")
return value
class Handler(BaseHTTPRequestHandler):
broker: Broker | None = None
def log_message(self, *_args: Any) -> None:
return
def send_json(
self,
value: Mapping[str, Any],
status: int,
*,
internal_credential: str | None = None,
) -> None:
raw = canonical(value)
self.send_response(status)
self.send_header("Content-Type", "application/json")
self.send_header("Cache-Control", "no-store")
if internal_credential:
self.send_header(
"X-AIOS-Caller-Projection-Credential",
internal_credential,
)
self.send_header("Content-Length", str(len(raw)))
self.end_headers()
self.wfile.write(raw)
def caller_account(self) -> str:
return self.headers.get("X-AIOS-Account", "").strip()
def peer_user(self) -> str:
if self.broker is None:
return ""
return peer_user_from_connection(self.connection)
def peer_authorized(self) -> bool:
if self.broker is None:
return False
return authorize_broker_peer(self.connection)
def account_authorized(self) -> bool:
if self.broker is None:
return False
return self.caller_account() == self.broker.expected_account
def body(self) -> dict[str, Any]:
try:
length = int(self.headers.get("Content-Length", "-1"))
except ValueError as exc:
raise AuthorizationError("CONTENT_LENGTH_INVALID") from exc
if length < 1 or length > MAX_BODY:
raise AuthorizationError("REQUEST_SIZE_INVALID", 413)
try:
value = json.loads(self.rfile.read(length).decode())
except Exception as exc:
raise AuthorizationError("REQUEST_JSON_INVALID") from exc
if not isinstance(value, Mapping):
raise AuthorizationError("REQUEST_OBJECT_REQUIRED")
return dict(value)
def do_GET(self) -> None:
if self.path == "/health":
self.send_json(
{
"status": "ok",
"service": "aios-owner-durable-authorization-broker",
"transport": "unix",
},
200,
)
return
try:
peer_ok = self.peer_authorized()
except AuthorizationError as exc:
self.send_json({"error": exc.code}, exc.status)
return
if not peer_ok:
self.send_json({"error": "SOCKGROUP_NOT_AUTHORIZED"}, 403)
return
if not self.account_authorized():
self.send_json({"error": "ACCOUNT_NOT_AUTHORIZED"}, 403)
return
if self.broker is None:
self.send_json({"error": "NOT_FOUND"}, 404)
return
try:
if self.path == "/v1/capabilities":
policy = self.broker.policy()
value = {
"schema": POLICY_SCHEMA,
"grant_id": policy.grant_id,
"account": policy.account,
"capabilities": sorted(policy.capabilities),
"policy_sha256": policy.policy_sha256,
"constraints": {
"repository": policy.constraints.repository,
"allowed_target_prefix": policy.constraints.allowed_target_prefix,
"max_task_risk": policy.constraints.max_task_risk,
},
"persistent_until_revoked": True,
"separate_owner_release_approval_still_required": True,
}
elif self.path == "/v1/projections":
value = self.broker.projections()
else:
match = re.fullmatch(
r"/v1/projections/(projection-[0-9a-f]{32})",
self.path,
)
if match is None:
self.send_json({"error": "NOT_FOUND"}, 404)
return
value = self.broker.projection(match.group(1))
self.send_json(value, 200)
except AuthorizationError as exc:
self.send_json({"error": exc.code}, exc.status)
def do_POST(self) -> None:
try:
peer_ok = self.peer_authorized()
except AuthorizationError as exc:
self.send_json({"error": exc.code}, exc.status)
return
if not peer_ok:
self.send_json({"error": "SOCKGROUP_NOT_AUTHORIZED"}, 403)
return
if not self.account_authorized():
self.send_json({"error": "ACCOUNT_NOT_AUTHORIZED"}, 403)
return
routes = {
"/v1/project-governed-task": "governed-task:project",
"/v1/authorize-release-candidate": "release:candidate",
"/v1/project-caller": "caller:projection",
}
capability = routes.get(self.path)
if capability is None or self.broker is None:
self.send_json({"error": "NOT_FOUND"}, 404)
return
try:
body = self.body()
if self.path == "/v1/project-caller":
if set(body) != {
"caller",
"scope",
"idempotency_key",
"request",
}:
raise AuthorizationError("CALLER_PROJECTION_FIELDS_INVALID")
if not isinstance(body["request"], Mapping):
raise AuthorizationError("CALLER_PROJECTION_REQUEST_INVALID")
value = self.broker.project_caller(
str(body["caller"]),
str(body["scope"]),
dict(body["request"]),
idempotency_key=str(body["idempotency_key"]),
)
credential = str(value.pop("_credential", ""))
if len(credential) < 32:
raise AuthorizationError("CREDENTIAL_REHYDRATION_MISSING", 503)
self.send_json(
value,
200,
internal_credential=credential,
)
return
if set(body) not in (
{"idempotency_key", "request"},
{"idempotency_key", "request", "amendment"},
):
raise AuthorizationError("BROKER_FIELDS_INVALID")
if not isinstance(body["request"], Mapping):
raise AuthorizationError("BROKER_REQUEST_INVALID")
if "amendment" in body and not isinstance(body["amendment"], Mapping):
raise AuthorizationError("GATE_AMENDMENT_INVALID")
value = self.broker.execute(
capability,
dict(body["request"]),
idempotency_key=str(body["idempotency_key"]),
amendment=(
dict(body["amendment"])
if isinstance(body.get("amendment"), Mapping)
else None
),
)
self.send_json(value, 200)
except AuthorizationError as exc:
self.send_json({"error": exc.code}, exc.status)
class UnixHTTPServer(socketserver.ThreadingUnixStreamServer):
daemon_threads = True
def serve() -> None:
credentials = Path(os.environ.get("CREDENTIALS_DIRECTORY", "/run/credentials"))
raw_allowed_users = os.environ.get(
"AIOS_OWNER_AUTH_BROKER_ALLOWED_USERS",
"",
).strip()
if raw_allowed_users:
allowed_peer_users = tuple(
dict.fromkeys(
item.strip()
for item in raw_allowed_users.split(",")
if item.strip()
)
)
else:
single_allowed_user = os.environ.get(
"AIOS_OWNER_AUTH_BROKER_ALLOWED_USER",
"aios-autonomous-agent-worker",
).strip()
allowed_peer_users = (single_allowed_user,) if single_allowed_user else ()
if not allowed_peer_users:
raise RuntimeError("BROKER_ALLOWED_USER_MISSING")
allowed_peer_user = allowed_peer_users[0]
for username in allowed_peer_users:
pwd.getpwnam(username)
allowed_peer_uid = pwd.getpwnam(allowed_peer_user).pw_uid
global CANONICAL_BROKER_GROUP
CANONICAL_BROKER_GROUP = os.environ.get(
"AIOS_OWNER_AUTH_BROKER_ALLOWED_GROUP",
"aios-autonomous-agent-worker",
).strip()
if not CANONICAL_BROKER_GROUP:
raise RuntimeError("BROKER_ALLOWED_GROUP_MISSING")
try:
allowed_peer_gid = grp.getgrnam(CANONICAL_BROKER_GROUP).gr_gid
except KeyError as exc:
raise RuntimeError("BROKER_ALLOWED_GROUP_INVALID") from exc
policy_path = Path(
os.environ.get(
"AIOS_OWNER_DURABLE_AUTHORIZATION_POLICY",
"/etc/aios-gitops/owner-durable-authorization.json",
)
)
revocation_path = Path(
os.environ.get(
"AIOS_OWNER_DURABLE_AUTHORIZATION_REVOCATIONS",
"/etc/aios-gitops/owner-durable-authorization-revocations.json",
)
)
bridge_token = load_secret(
Path(
os.environ.get(
"AIOS_AGENT_TASK_CONTROL_TOKEN_FILE",
str(credentials / "agent_task_control_token"),
)
),
code="BRIDGE_CONTROL_CREDENTIAL",
)
account = os.environ.get(
"AIOS_OWNER_AUTH_BROKER_ACCOUNT",
"chatgpt-governed-implementer-singlehost",
).strip()
state_root = Path(
os.environ.get(
"AIOS_OWNER_AUTH_BROKER_STATE_ROOT",
"/var/lib/aios-owner-authorization-broker",
)
)
Handler.broker = Broker(
policy_path=policy_path,
revocation_path=revocation_path,
store=ReceiptStore(state_root / "receipts.sqlite3"),
upstream=Upstream(
bridge_url="http://127.0.0.1:18892",
bridge_token=bridge_token,
),
expected_account=account,
expected_peer_user=allowed_peer_user,
allowed_peer_users=frozenset(allowed_peer_users),
)
socket_path = Path(
os.environ.get(
"AIOS_OWNER_AUTH_BROKER_SOCKET",
"/run/aios-owner-authorization-broker/broker.sock",
)
)
if not socket_path.is_absolute() or socket_path.is_symlink():
raise RuntimeError("BROKER_SOCKET_PATH_INVALID")
socket_path.parent.mkdir(parents=True, exist_ok=True)
if socket_path.exists():
if not stat.S_ISSOCK(socket_path.stat().st_mode):
raise RuntimeError("BROKER_SOCKET_CONFLICT")
socket_path.unlink()
server = UnixHTTPServer(str(socket_path), Handler)
os.chown(socket_path, allowed_peer_uid, allowed_peer_gid)
os.chmod(socket_path, 0o660)
try:
server.serve_forever()
finally:
server.server_close()
socket_path.unlink(missing_ok=True)
if __name__ == "__main__":
serve()