net-kingdom/bounded-executor/prepare_admission.py
tegwick c43afd46a7 NK-WP-0056-T06 prep: admission helper, write-role window-open/close, service-client case; T15 evidence
- bounded-executor/prepare_admission.py: the live admission from the pilot example, pins read
  from the image's commit, window <= 2 h (audit-core's declared bound), validated.
- executor-lanes.py window-open/window-close: CCR-2026-0058's window-scoped write role, created
  for one admitted open window under attended platform-admin and deleted afterwards (idempotent).
- security-cases/nk-bind-executor-approval.yaml: the case of record, proposed (founder acceptance).
- docs/evidence/2026-11-04-nk56-t15-audit-sender: 8 checks against audit-core 876c3f6.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 523928@bnt-lap001
Assistant-Session: 6c737afb-9c14-4152-9f02-477214b9c350
2026-11-04 01:49:40 +01:00

92 lines
4.4 KiB
Python

#!/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())