ouroboros/scripts/build_execd_bundle.py
Andrei Kaznacheev 03b6a81bee feat(remote): execd, the daemon on the target, and its attested bundle
The daemon that runs on the remote host over OpenSSH stdio. It carries NO Home
policy authority — no models, no memory, no safety layer, no review — and an
import-closure gate proves it: the bundle's modules may not reach a Home
authority module, forward or reverse, and the gate fails the build rather than
the run.

`execd_state.py` is the durable authority for what the target owns: process
custody, service leases, and the lease ceiling that makes Panic binding across a
partition. `execd_spool.py` is the process-log spool state machine, bounded so a
chatty process cannot size a Home record. `execd_task_files.py` is the task-file
contract, the one door for bytes moving either way.

The bundle is built and attested rather than copied: `build_execd_bundle.py`
produces it from a locked dependency set, `assemble_execd_stage.py` stages it,
and `smoke_execd_stage.py` refuses a stage whose identity does not match what was
built.

Rejected: shipping the whole package and letting the target import what it needs.
The closure gate only means something if the closure is the deployment unit.
2026-08-15 18:52:13 +04:00

344 lines
14 KiB
Python

#!/usr/bin/env python3
"""Build deterministic, manifest-allowlisted Linux execd release archives.
Architecture-native CI prepares each stage (including its PBS runtime and
binary wheels); this packager refuses links/special files and normalizes every
tar attribute. It performs no network access and never reads credential files.
"""
from __future__ import annotations
import argparse
import gzip
import hashlib
import json
import os
import pathlib
import tarfile
import tempfile
from typing import Any
PBS_RELEASE = "20260718"
PYTHON_VERSION = "3.12.13"
RIPGREP_VERSION = "15.2.0"
GLIBC_MIN = "2.17"
ARCHITECTURES = {
"x86_64": {
"loader": "/lib64/ld-linux-x86-64.so.2",
"pbs_sha256": "5854aa6ec71cad00334d5065633c210b2e7feb40956767a59a91791cadcf0b79",
"ripgrep_sha256": "33e15bcf1624b25cdd2a55813a47a2f95dbe126268203e76aa6a585d1e7b149c",
},
"aarch64": {
"loader": "/lib/ld-linux-aarch64.so.1",
"pbs_sha256": "f226576b91491ffa5739aa85726521e9031f4d87f80627d64ed348ac77cb31e9",
"ripgrep_sha256": "800b1e7206afe799dfb5a6901f23147cfaabe0e52210538100f61e86e1740915",
},
}
MAX_FILES = 20_000
MAX_UNPACKED_BYTES = 1_500_000_000
# The stage puts importable code under `lib/`, which is the ONLY namespace a Home
# module can travel in. A hand-written list of repo-relative spellings
# (`ouroboros/config.py`) lived here and could never match a stage path
# (`lib/ouroboros/config.py`), so the guard had never refused anything since it was
# written. The prohibition is now derived, not restated: staged module names are
# read out of the stage's own layout and judged against
# `FORBIDDEN_REMOTE_IMPORT_PREFIXES` — the same source of truth the import-closure
# gate uses — so the packager cannot fall out of step with it a third time.
LIBRARY_ROOT = "lib"
def _sha256(path: pathlib.Path) -> str:
digest = hashlib.sha256()
with path.open("rb") as stream:
for chunk in iter(lambda: stream.read(1024 * 1024), b""):
digest.update(chunk)
return digest.hexdigest()
def _stage_files(
root: pathlib.Path,
declared_modules: frozenset[str],
) -> list[pathlib.Path]:
from ouroboros.tool_capabilities import FORBIDDEN_REMOTE_IMPORT_PREFIXES
forbidden = tuple(
prefix
for prefixes in FORBIDDEN_REMOTE_IMPORT_PREFIXES.values()
for prefix in prefixes
)
if not root.is_dir():
raise ValueError(f"stage is not a directory: {root}")
files: list[pathlib.Path] = []
total = 0
for path in sorted(root.rglob("*"), key=lambda item: item.as_posix()):
relative = path.relative_to(root).as_posix()
if path.is_symlink():
raise ValueError(f"stage links are forbidden: {relative}")
if path.is_dir():
continue
if not path.is_file():
raise ValueError(f"stage special file is forbidden: {relative}")
parts = pathlib.PurePosixPath(relative).parts
if parts[0] == LIBRARY_ROOT:
# Judged as the MODULE the interpreter would import from this path,
# because that is the name the prohibition is written in.
if not relative.endswith(".py"):
raise ValueError(f"execd stage library holds a non-module: {relative}")
module = ".".join(parts[1:])[: -len(".py")].removesuffix(".__init__")
if any(
module == name or module.startswith(name + ".")
for name in forbidden
):
raise ValueError(
f"Home-only module leaked into execd stage: {relative} ({module})"
)
if module not in declared_modules:
raise ValueError(
f"execd stage carries an undeclared module: {relative} ({module})"
)
total += path.stat().st_size
files.append(path)
if len(files) > MAX_FILES or total > MAX_UNPACKED_BYTES:
raise ValueError("execd stage exceeds deterministic bundle limits")
required = {"bin/ouroboros-execd", "bin/rg"}
observed = {path.relative_to(root).as_posix() for path in files}
missing = sorted(required - observed)
if missing:
raise ValueError(f"execd stage is missing required files: {missing}")
return files
def _validate_dependency_lock(lock: dict[str, Any]) -> None:
if lock.get("schema_version") != 1:
raise ValueError("unsupported execd dependency lock")
try:
python = lock["python_build_standalone"]
ripgrep = lock["ripgrep"]
video = lock["video_helper"]
wheels = lock["python_wheels"]
if (
python["release"] != PBS_RELEASE
or python["version"] != PYTHON_VERSION
or ripgrep["version"] != RIPGREP_VERSION
):
raise ValueError("execd dependency lock version drift")
for architecture, config in ARCHITECTURES.items():
if (
python["architectures"][architecture]["sha256"]
!= config["pbs_sha256"]
or ripgrep["architectures"][architecture]["sha256"]
!= config["ripgrep_sha256"]
or len(video["architectures"][architecture]["sha256"]) != 64
or not wheels[architecture]
):
raise ValueError("execd dependency lock architecture drift")
except (KeyError, TypeError) as exc:
raise ValueError("execd dependency lock is incomplete") from exc
def _validate_stage_provenance(
root: pathlib.Path,
architecture: str,
lock: dict[str, Any],
) -> dict[str, Any]:
try:
payload = json.loads(
(root / "stage-provenance.json").read_text(encoding="utf-8")
)
except (OSError, json.JSONDecodeError) as exc:
raise ValueError("execd stage provenance is missing or invalid") from exc
expected = {
"python_build_standalone": lock["python_build_standalone"]["architectures"][
architecture
],
"ripgrep": lock["ripgrep"]["architectures"][architecture],
"video_helper": lock["video_helper"]["architectures"][architecture],
"python_wheels": lock["python_wheels"][architecture],
}
if (
not isinstance(payload, dict)
or payload.get("architecture") != architecture
or any(payload.get(key) != value for key, value in expected.items())
):
raise ValueError("execd stage provenance differs from dependency lock")
# The module allowlist `_stage_files` judges `lib/` against. A stage that does
# not say which modules it carries cannot be admitted, because then every file
# under `lib/` would be undeclared and unjudgeable rather than merely refused.
modules = payload.get("kernel_modules")
if not isinstance(modules, list) or not all(
isinstance(module, str) and module for module in modules
):
raise ValueError("execd stage provenance declares no kernel module set")
return payload
def _write_archive(
root: pathlib.Path,
files: list[pathlib.Path],
target: pathlib.Path,
) -> None:
target.parent.mkdir(parents=True, exist_ok=True)
temporary = target.with_name(f".{target.name}.tmp.{os.getpid()}")
with temporary.open("wb") as raw:
with gzip.GzipFile(filename="", mode="wb", fileobj=raw, mtime=0) as compressed:
with tarfile.open(
fileobj=compressed,
mode="w",
format=tarfile.USTAR_FORMAT,
) as archive:
directories: set[pathlib.PurePosixPath] = set()
for path in files:
parent = pathlib.PurePosixPath(
path.relative_to(root).as_posix()
).parent
while parent.as_posix() != ".":
directories.add(parent)
parent = parent.parent
for directory in sorted(directories, key=lambda item: item.as_posix()):
info = tarfile.TarInfo(directory.as_posix())
info.type = tarfile.DIRTYPE
info.mode = 0o755
info.uid = info.gid = 0
info.uname = info.gname = ""
info.mtime = 0
archive.addfile(info)
for path in files:
relative = path.relative_to(root).as_posix()
info = tarfile.TarInfo(relative)
info.size = path.stat().st_size
info.mode = 0o755 if os.access(path, os.X_OK) else 0o644
info.uid = info.gid = 0
info.uname = info.gname = ""
info.mtime = 0
with path.open("rb") as stream:
archive.addfile(info, stream)
raw.flush()
os.fsync(raw.fileno())
os.replace(temporary, target)
def build(
*,
version: str,
stages: dict[str, pathlib.Path],
output_dir: pathlib.Path,
dependency_lock: dict[str, Any],
) -> dict[str, Any]:
if set(stages) != set(ARCHITECTURES):
raise ValueError("both x86_64 and aarch64 stages are required")
_validate_dependency_lock(dependency_lock)
output_dir.mkdir(parents=True, exist_ok=True)
assets: dict[str, Any] = {}
contract_sets: set[Any] = set()
for architecture in sorted(stages):
root = stages[architecture].resolve(strict=True)
provenance = _validate_stage_provenance(root, architecture, dependency_lock)
contract_sets.add(provenance.get("contract_set_version"))
files = _stage_files(root, frozenset(provenance["kernel_modules"]))
filename = (
f"ouroboros-execd-{version}-linux-gnu-{architecture}.tar.gz"
)
archive = output_dir / filename
_write_archive(root, files, archive)
config = ARCHITECTURES[architecture]
assets[f"linux-{architecture}"] = {
"archive": filename,
"sha256": _sha256(archive),
"size": archive.stat().st_size,
"loader": config["loader"],
"glibc_min": GLIBC_MIN,
"files": [
{
"path": path.relative_to(root).as_posix(),
"sha256": _sha256(path),
"size": path.stat().st_size,
"mode": 0o755 if os.access(path, os.X_OK) else 0o644,
}
for path in files
],
}
# One bundle, ONE contract set. Two stages assembled from different trees would
# produce an artifact whose two architectures make different promises, and the
# manifest carries a single number that Home admits against — so the packager
# refuses rather than publishing whichever value it happened to see last.
if len(contract_sets) != 1 or not isinstance(next(iter(contract_sets)), int):
raise ValueError(
"execd stages declare no single Home↔execd contract set: "
f"{sorted(map(repr, contract_sets))}"
)
manifest = {
"schema_version": 1,
"build": version,
# The Home↔execd contract set this artifact implements, carried from the
# stages' own provenance. It is NOT the release version: a Home release that
# touches no shared contract ships a bundle with the same value, which is
# exactly why the contract set is the carrier of compatibility and the
# release id is not. Home refuses to SELECT a bundle whose value is not its
# own, so a build shipping a stale execd artifact cannot open a remote
# session and discover the disagreement later, inside a tool call.
"contract_set_version": contract_sets.pop(),
"python_build_standalone": {
"release": PBS_RELEASE,
"python": PYTHON_VERSION,
"sha256": {
architecture: config["pbs_sha256"]
for architecture, config in ARCHITECTURES.items()
},
},
"ripgrep": {
"version": RIPGREP_VERSION,
"sha256": {
architecture: config["ripgrep_sha256"]
for architecture, config in ARCHITECTURES.items()
},
},
"dependency_lock": dependency_lock,
"assets": assets,
}
encoded = (
json.dumps(
manifest,
ensure_ascii=False,
allow_nan=False,
sort_keys=True,
separators=(",", ":"),
)
+ "\n"
).encode("utf-8")
for name in ("manifest.json", f"ouroboros-execd-{version}-manifest.json"):
target = output_dir / name
with tempfile.NamedTemporaryFile(
dir=output_dir,
prefix=f".{name}.",
delete=False,
) as stream:
stream.write(encoded)
stream.flush()
os.fsync(stream.fileno())
temporary = pathlib.Path(stream.name)
os.replace(temporary, target)
return manifest
def _main() -> int:
parser = argparse.ArgumentParser()
parser.add_argument("--version", required=True)
parser.add_argument("--x86-stage", type=pathlib.Path, required=True)
parser.add_argument("--aarch64-stage", type=pathlib.Path, required=True)
parser.add_argument("--dependency-lock", type=pathlib.Path, required=True)
parser.add_argument("--output-dir", type=pathlib.Path, required=True)
args = parser.parse_args()
lock = json.loads(args.dependency_lock.read_text(encoding="utf-8"))
if not isinstance(lock, dict):
raise SystemExit("dependency lock must be a JSON object")
build(
version=args.version,
stages={"x86_64": args.x86_stage, "aarch64": args.aarch64_stage},
output_dir=args.output_dir,
dependency_lock=lock,
)
return 0
if __name__ == "__main__":
raise SystemExit(_main())