507 lines
26 KiB
Python
507 lines
26 KiB
Python
#!/usr/bin/env python3
|
|
"""Root-side executor for the single Athena Operator MCP.
|
|
|
|
The daemon exposes structured platform operations over a local Unix socket.
|
|
It deliberately has no arbitrary-command endpoint. Every mutation is first
|
|
materialised as an expiring, content-bound proposal and requires its exact
|
|
confirmation string in a later call.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import difflib
|
|
import hashlib
|
|
import json
|
|
import os
|
|
import re
|
|
import shutil
|
|
import socketserver
|
|
import subprocess
|
|
import tempfile
|
|
import threading
|
|
import time
|
|
import urllib.parse
|
|
import urllib.request
|
|
import uuid
|
|
from pathlib import Path
|
|
from typing import Any
|
|
|
|
|
|
VERSION = "1.0.0"
|
|
STACK = Path(os.environ.get("ATHENA_OPERATOR_STACK", "/opt/mike-ai/stack")).resolve()
|
|
REPOSITORY = Path(os.environ.get("ATHENA_OPERATOR_REPOSITORY", "/data/mike-ai-operator/repository")).resolve()
|
|
STATE = Path(os.environ.get("ATHENA_OPERATOR_STATE", "/data/mike-ai-operator/state")).resolve()
|
|
SOCKET = Path(os.environ.get("ATHENA_OPERATOR_SOCKET", "/run/mike-ai-operator/operator.sock"))
|
|
MODELS = Path(os.environ.get("ATHENA_OPERATOR_MODELS", "/data/models")).resolve()
|
|
TICKET_TTL = 1800
|
|
MAX_FILE_BYTES = 1_000_000
|
|
MAX_FILES = 24
|
|
MAX_OUTPUT = 30_000
|
|
LOCK = threading.RLock()
|
|
|
|
SAFE_PATH = re.compile(r"^[A-Za-z0-9_.+/-]{1,240}$")
|
|
SAFE_NAME = re.compile(r"^[A-Za-z0-9_.-]{1,100}$")
|
|
SAFE_COMMIT = re.compile(r"^[A-Za-z0-9ÄÖÜäöüß _.,:;()+/\-]{5,120}$")
|
|
BLOCKED_PATH_PARTS = {".git", ".ssh", "secrets", "credentials", "authorized_keys"}
|
|
PROTECTED_CONTAINERS = {
|
|
"mike-ai-wireguard-gateway",
|
|
}
|
|
ALLOWED_OPERATIONS = {
|
|
"file_update", "run_checks", "compose_deploy", "container_action",
|
|
"openwebui_sync", "git_publish", "model_download", "benchmark", "recovery",
|
|
}
|
|
ALLOWED_CHECKS = {
|
|
"operator-tests": ["python3", "dev/test_athena_operator.py"],
|
|
"openwebui-filter-tests": ["python3", "dev/test_openwebui_filters.py"],
|
|
"platform-verify": ["bash", "platform/checks/verify-platform.sh"],
|
|
"compose-main": ["docker", "compose", "-f", "compose.yaml", "config", "-q"],
|
|
"compose-mcp": ["docker", "compose", "-f", "platform/mcp/compose.yaml", "config", "-q"],
|
|
}
|
|
|
|
|
|
def now() -> int:
|
|
return int(time.time())
|
|
|
|
|
|
def compact(text: str) -> str:
|
|
if len(text) <= MAX_OUTPUT:
|
|
return text
|
|
return text[:22_000] + f"\n...[truncated from {len(text)} chars]...\n" + text[-7_000:]
|
|
|
|
|
|
def run(argv: list[str], *, cwd: Path = STACK, timeout: int = 900, check: bool = False) -> dict[str, Any]:
|
|
completed = subprocess.run(
|
|
argv, cwd=cwd, env={"PATH": "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", "LANG": "C.UTF-8", "HOME": "/root"},
|
|
stdin=subprocess.DEVNULL, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
|
|
text=True, errors="replace", timeout=timeout, check=False,
|
|
)
|
|
result = {"argv": argv, "exit_code": completed.returncode, "output": compact(completed.stdout)}
|
|
if check and completed.returncode != 0:
|
|
raise RuntimeError(json.dumps(result, ensure_ascii=False))
|
|
return result
|
|
|
|
|
|
def safe_relative(value: str) -> Path:
|
|
if not SAFE_PATH.fullmatch(value or "") or value.startswith("/"):
|
|
raise ValueError("invalid repository-relative path")
|
|
path = Path(value)
|
|
if ".." in path.parts or any(part.casefold() in BLOCKED_PATH_PARTS for part in path.parts):
|
|
raise PermissionError("protected path")
|
|
resolved = (REPOSITORY / path).resolve(strict=False)
|
|
resolved.relative_to(REPOSITORY)
|
|
return path
|
|
|
|
|
|
def source_file(root: Path, relative: Path) -> Path:
|
|
candidate = (root / relative).resolve(strict=False)
|
|
candidate.relative_to(root)
|
|
return candidate
|
|
|
|
|
|
def sha(data: bytes) -> str:
|
|
return hashlib.sha256(data).hexdigest()
|
|
|
|
|
|
def json_write(path: Path, value: Any) -> None:
|
|
path.parent.mkdir(parents=True, exist_ok=True)
|
|
temporary = path.with_suffix(path.suffix + ".tmp")
|
|
temporary.write_text(json.dumps(value, ensure_ascii=False, indent=2), encoding="utf-8")
|
|
os.replace(temporary, path)
|
|
|
|
|
|
def audit(event: str, **fields: Any) -> None:
|
|
record = {"time": now(), "event": event, **fields}
|
|
path = STATE / "audit.jsonl"
|
|
path.parent.mkdir(parents=True, exist_ok=True)
|
|
with path.open("a", encoding="utf-8") as handle:
|
|
handle.write(json.dumps(record, ensure_ascii=False, separators=(",", ":")) + "\n")
|
|
|
|
|
|
def ensure_repository() -> None:
|
|
if not (REPOSITORY / ".git").is_dir():
|
|
bundle = Path("/data/mike-ai-recovery-kit/source.git.bundle")
|
|
if not bundle.is_file():
|
|
raise RuntimeError("operator repository missing and no recovery Git bundle is available")
|
|
REPOSITORY.parent.mkdir(parents=True, exist_ok=True)
|
|
run(["git", "clone", str(bundle), str(REPOSITORY)], cwd=Path("/data"), check=True)
|
|
current_file = STACK / ".mike-ai-source-commit"
|
|
current = current_file.read_text().strip() if current_file.is_file() else ""
|
|
if re.fullmatch(r"[0-9a-f]{40}", current):
|
|
run(["git", "switch", "-C", "main", current], cwd=REPOSITORY, check=True)
|
|
remote = os.environ.get("ATHENA_OPERATOR_GIT_REMOTE", "").strip()
|
|
if remote:
|
|
run(["git", "remote", "set-url", "origin", remote], cwd=REPOSITORY, check=True)
|
|
run(["git", "config", "user.name", os.environ.get("ATHENA_OPERATOR_GIT_NAME", "Athena Operator")], cwd=REPOSITORY, check=True)
|
|
run(["git", "config", "user.email", os.environ.get("ATHENA_OPERATOR_GIT_EMAIL", "athena-operator@localhost")], cwd=REPOSITORY, check=True)
|
|
|
|
|
|
def inspect(subject: str, arguments: dict[str, Any]) -> dict[str, Any]:
|
|
ensure_repository()
|
|
if subject == "overview":
|
|
return {
|
|
"version": VERSION,
|
|
"source_commit": (STACK / ".mike-ai-source-commit").read_text().strip(),
|
|
"git": run(["git", "status", "--short", "--branch"], cwd=REPOSITORY),
|
|
"containers": run(["docker", "ps", "--format", "{{.Names}}\t{{.Status}}\t{{.Image}}"]),
|
|
"storage": run(["df", "-h", "/", "/data", str(MODELS)]),
|
|
"gpus": run(["nvidia-smi", "--query-gpu=name,memory.total,memory.used,utilization.gpu", "--format=csv,noheader"]),
|
|
"boundary": "Athena AI-platform operator; no arbitrary shell, shutdown, reboot, SSH/network/firewall/kernel/driver/partition operations",
|
|
}
|
|
if subject == "git_status":
|
|
return {"status": run(["git", "status", "--short", "--branch"], cwd=REPOSITORY), "diff": run(["git", "diff", "--stat"], cwd=REPOSITORY)}
|
|
if subject == "containers":
|
|
return {"containers": run(["docker", "ps", "-a", "--format", "{{.Names}}\t{{.Status}}\t{{.Image}}"])}
|
|
if subject == "models":
|
|
entries = []
|
|
if MODELS.is_dir():
|
|
for path in sorted(MODELS.rglob("*.gguf")):
|
|
entries.append({"path": str(path.relative_to(MODELS)), "bytes": path.stat().st_size})
|
|
return {"models": entries[:500], "count": len(entries)}
|
|
if subject == "jobs":
|
|
jobs = []
|
|
for path in sorted((STATE / "jobs").glob("*.json"), reverse=True)[:30]:
|
|
jobs.append(json.loads(path.read_text()))
|
|
return {"jobs": jobs}
|
|
raise ValueError("unsupported inspection subject")
|
|
|
|
|
|
def read_source(arguments: dict[str, Any]) -> dict[str, Any]:
|
|
ensure_repository()
|
|
relative = safe_relative(str(arguments.get("path", "")))
|
|
target = source_file(REPOSITORY, relative)
|
|
if not target.is_file():
|
|
raise FileNotFoundError("source file not found")
|
|
text = target.read_text(encoding="utf-8", errors="replace")
|
|
start = max(1, int(arguments.get("start_line", 1)))
|
|
count = min(1000, max(1, int(arguments.get("line_count", 300))))
|
|
lines = text.splitlines()
|
|
return {"path": str(relative), "sha256": sha(target.read_bytes()), "start_line": start, "content": "\n".join(lines[start - 1:start - 1 + count]), "total_lines": len(lines)}
|
|
|
|
|
|
def search_source(arguments: dict[str, Any]) -> dict[str, Any]:
|
|
ensure_repository()
|
|
query = str(arguments.get("query", ""))
|
|
if not query or len(query) > 200 or any(x in query for x in ("\x00", "\n", "\r")):
|
|
raise ValueError("invalid query")
|
|
result = run(["rg", "-n", "--hidden", "--glob", "!.git/**", "--", query, "."], cwd=REPOSITORY, timeout=20)
|
|
return {"query": query, "matches": result["output"], "exit_code": result["exit_code"]}
|
|
|
|
|
|
def normalise_operation(operation: str, payload: dict[str, Any]) -> tuple[dict[str, Any], str]:
|
|
if operation not in ALLOWED_OPERATIONS:
|
|
raise ValueError("unsupported operation")
|
|
if not isinstance(payload, dict):
|
|
raise ValueError("payload must be an object")
|
|
if operation == "file_update":
|
|
files = payload.get("files")
|
|
if not isinstance(files, list) or not 1 <= len(files) <= MAX_FILES:
|
|
raise ValueError("files must contain 1..24 entries")
|
|
normal = []
|
|
previews = []
|
|
for item in files:
|
|
relative = safe_relative(str(item.get("path", "")))
|
|
content = str(item.get("content", ""))
|
|
raw = content.encode()
|
|
if len(raw) > MAX_FILE_BYTES:
|
|
raise ValueError("file content exceeds limit")
|
|
target = source_file(REPOSITORY, relative)
|
|
before = target.read_text(encoding="utf-8", errors="replace") if target.is_file() else ""
|
|
expected = str(item.get("expected_sha256", ""))
|
|
before_sha = sha(before.encode())
|
|
if expected and expected != before_sha:
|
|
raise RuntimeError(f"source drift for {relative}")
|
|
diff = "".join(difflib.unified_diff(before.splitlines(True), content.splitlines(True), fromfile=f"a/{relative}", tofile=f"b/{relative}"))
|
|
normal.append({"path": str(relative), "content": content, "before_sha256": before_sha, "after_sha256": sha(raw)})
|
|
previews.append(compact(diff))
|
|
return {"files": normal}, "\n".join(previews)
|
|
if operation == "run_checks":
|
|
checks = payload.get("checks") or []
|
|
if not isinstance(checks, list) or not checks or any(name not in ALLOWED_CHECKS for name in checks):
|
|
raise ValueError("unknown check suite")
|
|
return {"checks": checks}, "Will run: " + ", ".join(checks)
|
|
if operation == "compose_deploy":
|
|
compose_file = str(payload.get("compose_file", ""))
|
|
if compose_file not in {"compose.yaml", "platform/mcp/compose.yaml"}:
|
|
raise ValueError("unsupported compose file")
|
|
services = payload.get("services") or []
|
|
if not isinstance(services, list) or not 1 <= len(services) <= 12 or any(not SAFE_NAME.fullmatch(str(x)) for x in services):
|
|
raise ValueError("invalid compose service list")
|
|
return {"compose_file": compose_file, "services": services, "build": bool(payload.get("build", True))}, f"docker compose -f {compose_file} up -d {'--build ' if payload.get('build', True) else ''}{' '.join(services)}"
|
|
if operation == "container_action":
|
|
action = str(payload.get("action", ""))
|
|
containers = payload.get("containers") or []
|
|
if action not in {"start", "stop", "restart"}:
|
|
raise ValueError("invalid container action")
|
|
if not isinstance(containers, list) or not 1 <= len(containers) <= 12:
|
|
raise ValueError("invalid container list")
|
|
for name in containers:
|
|
if not SAFE_NAME.fullmatch(str(name)) or not str(name).startswith("mike-ai-") or name in PROTECTED_CONTAINERS:
|
|
raise PermissionError(f"container is outside operator boundary: {name}")
|
|
return {"action": action, "containers": containers}, f"docker {action} {' '.join(containers)}"
|
|
if operation == "openwebui_sync":
|
|
return {}, (
|
|
"Synchronise the versioned Open WebUI model profiles, filters, tool connections "
|
|
"and system prompt with the running Open WebUI instance."
|
|
)
|
|
if operation == "git_publish":
|
|
message = str(payload.get("message", ""))
|
|
if not SAFE_COMMIT.fullmatch(message):
|
|
raise ValueError("invalid commit message")
|
|
status = run(["git", "status", "--short"], cwd=REPOSITORY, check=True)["output"].strip()
|
|
if not status:
|
|
raise RuntimeError("repository has no changes to publish")
|
|
diff = run(["git", "diff", "--stat"], cwd=REPOSITORY, check=True)["output"]
|
|
preview = f"Commit message: {message}\n\nChanged and untracked files:\n{status}\n\nDiff summary:\n{diff}"
|
|
return {"message": message, "reviewed_status": status}, preview
|
|
if operation == "model_download":
|
|
url = str(payload.get("url", ""))
|
|
parsed = urllib.parse.urlparse(url)
|
|
if parsed.scheme != "https" or parsed.hostname not in {"huggingface.co", "cdn-lfs.huggingface.co", "hf.co"}:
|
|
raise PermissionError("only HTTPS Hugging Face downloads are allowed")
|
|
destination = safe_relative(str(payload.get("destination", "")))
|
|
expected = str(payload.get("sha256", ""))
|
|
if expected and not re.fullmatch(r"[0-9a-f]{64}", expected):
|
|
raise ValueError("invalid sha256")
|
|
return {"url": url, "destination": str(destination), "sha256": expected}, f"Download {url} to models/{destination}"
|
|
if operation == "benchmark":
|
|
script = safe_relative(str(payload.get("script", "")))
|
|
if not (str(script).startswith("dev/") or str(script).startswith("platform/bench")) or script.suffix not in {".py", ".sh"}:
|
|
raise PermissionError("benchmark script must be versioned below dev/ or platform/bench*")
|
|
args = payload.get("arguments") or []
|
|
if not isinstance(args, list) or len(args) > 20 or any(not isinstance(x, str) or len(x) > 200 or re.search(r"[\x00\n\r]", x) for x in args):
|
|
raise ValueError("invalid benchmark arguments")
|
|
return {"script": str(script), "arguments": args}, f"Run versioned benchmark {script} with {args!r}"
|
|
if operation == "recovery":
|
|
label = str(payload.get("label", time.strftime("%Y%m%d")))
|
|
if not SAFE_NAME.fullmatch(label):
|
|
raise ValueError("invalid recovery label")
|
|
return {"label": label}, f"Create encrypted recovery bundle and self-contained data kit: {label}"
|
|
raise AssertionError(operation)
|
|
|
|
|
|
def prepare(arguments: dict[str, Any]) -> dict[str, Any]:
|
|
ensure_repository()
|
|
operation = str(arguments.get("operation", ""))
|
|
payload, preview = normalise_operation(operation, arguments.get("payload") or {})
|
|
ticket_id = uuid.uuid4().hex
|
|
record = {"id": ticket_id, "operation": operation, "payload": payload, "preview": preview, "created": now(), "expires": now() + TICKET_TTL, "status": "pending"}
|
|
record["binding"] = sha(json.dumps({"operation": operation, "payload": payload}, sort_keys=True, ensure_ascii=False).encode())
|
|
json_write(STATE / "pending" / f"{ticket_id}.json", record)
|
|
audit("prepared", ticket=ticket_id, operation=operation, binding=record["binding"])
|
|
return {"ticket": ticket_id, "operation": operation, "binding": record["binding"], "preview": preview, "expires_in_seconds": TICKET_TTL, "required_confirmation": f"EXECUTE {ticket_id}", "instruction": "Show the full preview to the user and wait for explicit confirmation in a later message."}
|
|
|
|
|
|
def sync_file(relative: Path, content: str, backup_root: Path) -> None:
|
|
for root in (REPOSITORY, STACK):
|
|
target = source_file(root, relative)
|
|
if target.exists():
|
|
backup = backup_root / root.name / relative
|
|
backup.parent.mkdir(parents=True, exist_ok=True)
|
|
shutil.copy2(target, backup)
|
|
target.parent.mkdir(parents=True, exist_ok=True)
|
|
fd, temp_name = tempfile.mkstemp(prefix=f".{target.name}.", dir=target.parent)
|
|
with os.fdopen(fd, "w", encoding="utf-8") as handle:
|
|
handle.write(content)
|
|
os.replace(temp_name, target)
|
|
|
|
|
|
def start_job(ticket: str, operation: str, worker) -> dict[str, Any]:
|
|
job_id = uuid.uuid4().hex
|
|
job_path = STATE / "jobs" / f"{job_id}.json"
|
|
json_write(job_path, {"id": job_id, "ticket": ticket, "operation": operation, "status": "running", "started": now()})
|
|
def wrapped():
|
|
try:
|
|
result = worker()
|
|
json_write(job_path, {"id": job_id, "ticket": ticket, "operation": operation, "status": "completed", "started": json.loads(job_path.read_text())["started"], "finished": now(), "result": result})
|
|
audit("job_completed", job=job_id, operation=operation)
|
|
except Exception as exc:
|
|
json_write(job_path, {"id": job_id, "ticket": ticket, "operation": operation, "status": "failed", "finished": now(), "error": compact(str(exc))})
|
|
audit("job_failed", job=job_id, operation=operation)
|
|
threading.Thread(target=wrapped, daemon=True).start()
|
|
return {"job_id": job_id, "status": "running", "instruction": "Poll athena_operator_job until completed or failed."}
|
|
|
|
|
|
def execute_operation(ticket: str, operation: str, payload: dict[str, Any]) -> dict[str, Any]:
|
|
if operation == "file_update":
|
|
backup = STATE / "backups" / f"{now()}-{ticket}"
|
|
for item in payload["files"]:
|
|
relative = safe_relative(item["path"])
|
|
current = source_file(REPOSITORY, relative)
|
|
current_sha = sha(current.read_bytes()) if current.is_file() else sha(b"")
|
|
if current_sha != item["before_sha256"]:
|
|
raise RuntimeError(f"source drift after preview: {relative}")
|
|
for item in payload["files"]:
|
|
sync_file(safe_relative(item["path"]), item["content"], backup)
|
|
return {"changed": [item["path"] for item in payload["files"]], "backup": str(backup), "git_diff": run(["git", "diff", "--stat"], cwd=REPOSITORY)}
|
|
if operation == "run_checks":
|
|
return {"checks": [{"name": name, **run(ALLOWED_CHECKS[name], cwd=REPOSITORY, timeout=1200)} for name in payload["checks"]]}
|
|
if operation == "compose_deploy":
|
|
compose = payload["compose_file"]
|
|
run(["docker", "compose", "-f", compose, "config", "-q"], cwd=STACK, check=True)
|
|
argv = ["docker", "compose", "-f", compose, "up", "-d"]
|
|
if payload["build"]:
|
|
argv.append("--build")
|
|
argv.extend(payload["services"])
|
|
return {"deploy": run(argv, cwd=STACK, timeout=3600, check=True), "containers": run(["docker", "ps", "--format", "{{.Names}}\t{{.Status}}"])}
|
|
if operation == "container_action":
|
|
return {"action": run(["docker", payload["action"], *payload["containers"]], timeout=300, check=True)}
|
|
if operation == "openwebui_sync":
|
|
installer = STACK / "platform/openwebui/install-filters.sh"
|
|
if not installer.is_file():
|
|
raise FileNotFoundError("versioned Open WebUI synchronisation script is missing")
|
|
return {"sync": run(["bash", str(installer)], cwd=STACK, timeout=1800, check=True)}
|
|
if operation == "git_publish":
|
|
current_status = run(["git", "status", "--short"], cwd=REPOSITORY, check=True)["output"].strip()
|
|
if current_status != payload["reviewed_status"]:
|
|
raise RuntimeError("repository changed after the Git publish preview")
|
|
run(["git", "add", "--all"], cwd=REPOSITORY, check=True)
|
|
run(["git", "diff", "--cached", "--check"], cwd=REPOSITORY, check=True)
|
|
commit = run(["git", "commit", "-m", payload["message"]], cwd=REPOSITORY, check=True)
|
|
pushed = run(["git", "push", "origin", "HEAD:main"], cwd=REPOSITORY, timeout=300, check=True)
|
|
head = run(["git", "rev-parse", "HEAD"], cwd=REPOSITORY, check=True)["output"].strip()
|
|
(STACK / ".mike-ai-source-commit").write_text(head + "\n")
|
|
return {"commit": head, "commit_output": commit, "push_output": pushed}
|
|
if operation == "model_download":
|
|
def download():
|
|
destination = source_file(MODELS, Path(payload["destination"]))
|
|
destination.parent.mkdir(parents=True, exist_ok=True)
|
|
partial = destination.with_suffix(destination.suffix + ".partial")
|
|
request = urllib.request.Request(payload["url"], headers={"User-Agent": "Athena-Operator/1"})
|
|
digest = hashlib.sha256()
|
|
total = 0
|
|
with urllib.request.urlopen(request, timeout=60) as response, partial.open("wb") as output:
|
|
while chunk := response.read(8 * 1024 * 1024):
|
|
output.write(chunk); digest.update(chunk); total += len(chunk)
|
|
actual = digest.hexdigest()
|
|
if payload["sha256"] and actual != payload["sha256"]:
|
|
partial.unlink(missing_ok=True); raise RuntimeError("model checksum mismatch")
|
|
os.replace(partial, destination)
|
|
return {"destination": str(destination), "bytes": total, "sha256": actual}
|
|
return start_job(ticket, operation, download)
|
|
if operation == "benchmark":
|
|
def benchmark():
|
|
script = source_file(REPOSITORY, safe_relative(payload["script"]))
|
|
interpreter = "python3" if script.suffix == ".py" else "bash"
|
|
return run([interpreter, str(script), *payload["arguments"]], cwd=REPOSITORY, timeout=86400)
|
|
return start_job(ticket, operation, benchmark)
|
|
if operation == "recovery":
|
|
def recovery():
|
|
label = payload["label"]
|
|
dirty = run(["git", "status", "--porcelain"], cwd=REPOSITORY, check=True)["output"].strip()
|
|
if dirty:
|
|
raise RuntimeError("publish repository changes before creating a recovery kit")
|
|
head = run(["git", "rev-parse", "HEAD"], cwd=REPOSITORY, check=True)["output"].strip()
|
|
marker = (STACK / ".mike-ai-source-commit").read_text().strip()
|
|
if marker != head:
|
|
raise RuntimeError("deployed source marker and operator repository HEAD differ")
|
|
encrypted = Path(f"/data/athena-recovery-{label}.tar.age")
|
|
source_bundle = Path(f"/data/athena-source-{label}.git.bundle")
|
|
release = Path(f"/data/mike-ai-recovery-kit-{label}")
|
|
for target in (encrypted, source_bundle, release):
|
|
if target.exists():
|
|
raise FileExistsError(target)
|
|
git_bundle = run(["git", "bundle", "create", str(source_bundle), "--all"], cwd=REPOSITORY, timeout=1800, check=True)
|
|
encrypted_result = run([str(STACK / "platform/recovery/create-recovery-bundle.sh"), str(encrypted)], timeout=7200, check=True)
|
|
identity = Path("/data/mike-ai-recovery-kit/recovery.agekey")
|
|
if not identity.is_file():
|
|
raise RuntimeError("existing recovery identity is unavailable")
|
|
kit_result = run([
|
|
str(STACK / "platform/recovery/create-self-contained-data-kit.sh"),
|
|
str(encrypted), str(identity), str(source_bundle), str(release),
|
|
], timeout=7200, check=True)
|
|
verify = run(["sha256sum", "-c", "SHA256SUMS"], cwd=release, timeout=1800, check=True)
|
|
return {
|
|
"commit": head,
|
|
"recovery_bundle": str(encrypted),
|
|
"source_bundle": str(source_bundle),
|
|
"self_contained_kit": str(release),
|
|
"git_bundle": git_bundle,
|
|
"encrypted_bundle": encrypted_result,
|
|
"kit": kit_result,
|
|
"verification": verify,
|
|
}
|
|
return start_job(ticket, operation, recovery)
|
|
raise AssertionError(operation)
|
|
|
|
|
|
def execute(arguments: dict[str, Any]) -> dict[str, Any]:
|
|
ticket = str(arguments.get("ticket", ""))
|
|
confirmation = str(arguments.get("confirmation", ""))
|
|
if not re.fullmatch(r"[0-9a-f]{32}", ticket) or confirmation != f"EXECUTE {ticket}":
|
|
raise PermissionError("exact ticket confirmation required")
|
|
path = STATE / "pending" / f"{ticket}.json"
|
|
if not path.is_file():
|
|
raise ValueError("ticket not found or already consumed")
|
|
with LOCK:
|
|
record = json.loads(path.read_text())
|
|
if record["status"] != "pending" or now() > record["expires"]:
|
|
raise PermissionError("ticket expired or unavailable")
|
|
expected = sha(json.dumps({"operation": record["operation"], "payload": record["payload"]}, sort_keys=True, ensure_ascii=False).encode())
|
|
if expected != record["binding"]:
|
|
raise RuntimeError("ticket binding mismatch")
|
|
record["status"] = "executing"
|
|
json_write(path, record)
|
|
try:
|
|
result = execute_operation(ticket, record["operation"], record["payload"])
|
|
record["status"] = "executed"
|
|
record["executed"] = now()
|
|
record["result_summary"] = compact(json.dumps(result, ensure_ascii=False))
|
|
completed = STATE / "completed" / path.name
|
|
json_write(completed, record)
|
|
path.unlink()
|
|
audit("executed", ticket=ticket, operation=record["operation"], binding=record["binding"])
|
|
return {"ticket": ticket, "operation": record["operation"], "result": result, "instruction": "Verify health and Git/recovery state before declaring the work complete."}
|
|
except Exception:
|
|
record["status"] = "failed"
|
|
json_write(path, record)
|
|
raise
|
|
|
|
|
|
def job(arguments: dict[str, Any]) -> dict[str, Any]:
|
|
job_id = str(arguments.get("job_id", ""))
|
|
if not re.fullmatch(r"[0-9a-f]{32}", job_id):
|
|
raise ValueError("invalid job id")
|
|
path = STATE / "jobs" / f"{job_id}.json"
|
|
if not path.is_file():
|
|
raise FileNotFoundError("job not found")
|
|
return json.loads(path.read_text())
|
|
|
|
|
|
def dispatch(request: dict[str, Any]) -> dict[str, Any]:
|
|
action = request.get("action")
|
|
arguments = request.get("arguments") or {}
|
|
if action == "inspect": return inspect(str(arguments.get("subject", "overview")), arguments)
|
|
if action == "read_source": return read_source(arguments)
|
|
if action == "search_source": return search_source(arguments)
|
|
if action == "prepare": return prepare(arguments)
|
|
if action == "execute": return execute(arguments)
|
|
if action == "job": return job(arguments)
|
|
raise ValueError("unsupported operator action")
|
|
|
|
|
|
class Handler(socketserver.StreamRequestHandler):
|
|
def handle(self) -> None:
|
|
line = self.rfile.readline(2_000_000)
|
|
try:
|
|
request = json.loads(line)
|
|
result = dispatch(request)
|
|
response = {"ok": True, "result": result}
|
|
except Exception as exc:
|
|
response = {"ok": False, "error": compact(str(exc))}
|
|
self.wfile.write((json.dumps(response, ensure_ascii=False, separators=(",", ":")) + "\n").encode())
|
|
|
|
|
|
def main() -> None:
|
|
STATE.mkdir(parents=True, exist_ok=True)
|
|
SOCKET.parent.mkdir(parents=True, exist_ok=True)
|
|
SOCKET.unlink(missing_ok=True)
|
|
server = socketserver.ThreadingUnixStreamServer(str(SOCKET), Handler)
|
|
os.chmod(SOCKET, 0o660)
|
|
os.chown(SOCKET, 0, int(os.environ.get("ATHENA_OPERATOR_GID", "10003")))
|
|
audit("daemon_started", version=VERSION)
|
|
server.serve_forever()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|