#!/usr/bin/env python3 """Prepare the live pilot admission (NK-WP-0056-T06) from the pilot example. The executor checks every pinned file against the copy inside its image (source_root /app), so the pins are taken from the commit the pinned image was built from (`--commit`, read with `git show`), not from the working tree. The window opens at `--not-before` and lasts `--hours`. The budget, plan, checks and scope ceiling are the example's, so the admission stays inside the executor's declared grants (make bounded-executor-test). python3 bounded-executor/prepare_admission.py --id BEA-2026-0002 --commit 9bd7bf0 \\ --not-before 2026-11-05T09:00:00Z --hours 2 --out admissions/BEA-2026-0002.yaml Prints the admission and plan digests. Writes nothing else and contacts nothing. """ from __future__ import annotations import argparse import datetime as dt import hashlib import importlib.util import json import pathlib import subprocess import sys import yaml HERE = pathlib.Path(__file__).resolve().parent REPO = HERE.parent EXAMPLE = REPO / "examples/bounded-execution-admission/bind-rotation-svc-authelia.yaml" _spec = importlib.util.spec_from_file_location("bea_validate_prepare", REPO / "tools/bounded-execution-admission/validate.py") bea = importlib.util.module_from_spec(_spec) _spec.loader.exec_module(bea) def _git(*args: str) -> bytes: return subprocess.run(["git", "-C", str(REPO), *args], capture_output=True, check=True).stdout def _z(t: dt.datetime) -> str: return t.astimezone(dt.UTC).isoformat(timespec="seconds").replace("+00:00", "Z") def prepare(admission_id: str, commit: str, not_before: dt.datetime, hours: float) -> dict: full = _git("rev-parse", "--verify", commit + "^{commit}").decode().strip() doc = bea.load(EXAMPLE) spec = doc["spec"] spec.update(id=admission_id, work_ref="NK-WP-0056-T06", title="Rotate the svc-authelia directory bind (live pilot, one consumer)") spec["window"] = {"not_before": _z(not_before), "not_after": _z(not_before + dt.timedelta(hours=hours))} files = [{"path": f["path"], "sha256": hashlib.sha256(_git("show", f"{full}:{f['path']}")).hexdigest()} for f in spec["plan"]["source"]["files"]] spec["plan"]["source"].update(commit=full[:12], files=files) pins = {f["path"]: f["sha256"] for f in files} for check in spec["checks"]: check["sha256"] = pins[check["ref"]] spec["plan_digest"] = bea.plan_digest(doc) report = bea.evaluate(doc, json.loads(bea.SCHEMA.read_text()), as_of=not_before) if not report["valid"]: raise ValueError("admission invalid: " + ",".join(sorted({f["rule"] for f in report["findings"]}))) return doc def main(argv=None) -> int: parser = argparse.ArgumentParser(description=__doc__.split("\n", 1)[0]) parser.add_argument("--id", required=True) parser.add_argument("--commit", required=True, help="the commit the pinned executor image was built from") parser.add_argument("--not-before", required=True, help="window start, RFC 3339 with offset") parser.add_argument("--hours", type=float, default=2.0) parser.add_argument("--out", required=True, type=pathlib.Path) args = parser.parse_args(argv) if args.out.exists(): raise SystemExit("output exists; admissions are immutable, choose a new id") if not 0 < args.hours <= 2: raise SystemExit("--hours must be in (0, 2]: audit-core's declared bound for a run without receipt is 2 h") try: doc = prepare(args.id, args.commit, dt.datetime.fromisoformat(args.not_before.replace("Z", "+00:00")), args.hours) except (ValueError, subprocess.CalledProcessError) as error: print(f"refused: {error}" if isinstance(error, ValueError) else "refused: unknown commit or path", file=sys.stderr) return 4 args.out.parent.mkdir(parents=True, exist_ok=True) args.out.write_text("# NK-WP-0056-T06 live pilot admission. Immutable once requested: a change is a new id.\n" + yaml.safe_dump(doc, sort_keys=False, width=120)) print(json.dumps({"admission": args.id, "admission_digest": bea.admission_digest(doc), "plan_digest": doc["spec"]["plan_digest"], "window": doc["spec"]["window"], "pinned_commit": doc["spec"]["plan"]["source"]["commit"]}, indent=2)) return 0 if __name__ == "__main__": raise SystemExit(main())