AUDIT-WP-0018-T04: founder-run seed of the nk-bounded-executor bearer; T05 live activation waits
All checks were successful
CI Smoke / host-smoke (push) Successful in 1s
CI Smoke / container-smoke (push) Successful in 2s

- scripts/seed-nk-bounded-executor-sender.py (+ silent attended wrapper):
  one attended process mints once, writes CCR-2026-0063 custody
  (platform/workloads/net-kingdom/bounded-executor/audit-sender, cas 0) and
  appends the sender to the audit-core registry (cas), syncs
  audit-core-senders and compares by hash, restarts the receiver, probes the
  live scope without writing an event, and checks sso/nk-bind-executor-audit
  by hash when it exists. Receipt only; no value printed or stored.
- Refuses unless CCR-2026-0063 is approved by its three owners, cluster is
  railiance01, receiver image and live scope ConfigMap match, the ingress
  policy exists and the coding-agent boundary covers the custody path.
- tests/test_seed_nk_bounded_executor_sender.py: 16 synthetic tests,
  including the probe run against the real receiver over HTTP.
- docs/bounded-executor-receipts.md: 2026-11-04 live state and founder
  runbook (single-object ingress apply, preflight, seed, verify).
- Read-only check: ingress policy not live; other nine policies match.

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
This commit is contained in:
tegwick 2026-11-04 01:39:36 +01:00
parent 06d0029f1e
commit 876c3f6647
6 changed files with 855 additions and 1 deletions

View file

@ -115,6 +115,8 @@
| task | AUDIT-WP-0018-T01 | done | — | workplans/AUDIT-WP-0018-bounded-executor-receipt-custody.md |
| task | AUDIT-WP-0018-T02 | done | — | workplans/AUDIT-WP-0018-bounded-executor-receipt-custody.md |
| task | AUDIT-WP-0018-T03 | done | — | workplans/AUDIT-WP-0018-bounded-executor-receipt-custody.md |
| task | AUDIT-WP-0018-T04 | done | — | workplans/AUDIT-WP-0018-bounded-executor-receipt-custody.md |
| task | AUDIT-WP-0018-T05 | wait | — | workplans/AUDIT-WP-0018-bounded-executor-receipt-custody.md |
| intake | AUDIT-IN-0001 | closed | — | intakes/intakes.md |
| intake | AUDIT-IN-0002 | closed | — | intakes/intakes.md |
| intake | AUDIT-IN-0003 | closed | — | intakes/intakes.md |

View file

@ -229,3 +229,114 @@ Released 2026-11-03: receiver `sha256:5f3d01c8…` (built from 2b8c909) and
the scope ConfigMap carrying `closing_classes` are live on railiance01. The
detection is inert until the `nk-bounded-executor` token is seeded and the
executor emits.
## Status 2026-11-04 — what is still needed live (NK-WP-0056-T15/T16)
Checked read-only on 2026-11-04 with the default (limited) identity:
- **Ingress: not live.** `audit-core-nk-bounded-executor-ingress` is the only
object in `deploy/networkpolicies.yaml` that is absent on railiance01. The
nine other policies in that file match the live specs exactly. The
executor's own egress (`sso/bounded-executor`, to `audit-core` pods on 8080) is
already live.
- **Scope ConfigMap:** the limited identity cannot read ConfigMaps in
`audit-core`. The release on 2026-11-03 is recorded in 06d0029. The seed
script below refuses unless the live entry equals `deploy/senders-scope.json`.
- **Bearer: not minted.** The registry is OpenBao
`platform/workloads/audit-core/senders` (field `senders.json`). ExternalSecret
`audit-core/audit-core-senders` projects it into the receiver. The same
pattern served tenant-engine and the CCR-2026-0021/0022 senders.
How the token reaches both sides. One founder-run process,
`scripts/seed-nk-bounded-executor-sender.py`, does the following:
1. It mints one bearer.
2. It writes the bearer with cas 0 to
`platform/workloads/net-kingdom/bounded-executor/audit-sender` (`AUDIT_TOKEN`,
`CUSTODY_REQUEST=CCR-2026-0063`).
3. It appends the `nk-bounded-executor` entry to the registry. The entry is the
declared scope plus the token, written with cas on the version read. Every
other sender stays byte-equal.
4. It force-syncs `audit-core-senders` and compares the token by SHA-256 only.
5. It restarts the receiver.
6. It proves the bearer without writing an event. Inside the receiver pod, with
the bearer on stdin:
- a read gets 403 (write-only);
- reconciliation for its own source and tenant `platform` gets 200;
- tenant `tenant:platform` gets 403;
- another source gets 403.
An unknown bearer gets 401.
7. It force-syncs `sso/nk-bind-executor-audit` and compares `audit-token` by
hash, but only if that ExternalSecret exists.
The script is silent. The receipt holds versions, booleans and a 12-hex
fingerprint. A rerun reuses its own custody and writes nothing new. It refuses
foreign or drifted custody, a sender registered without custody, a token
collision and a concurrent registry write. Tests:
`tests/test_seed_nk_bounded_executor_sender.py`.
The script refuses before any write unless all of the following hold:
- CCR-2026-0063 is `approved` or later, with all three owner reviews;
- the cluster is railiance01 (kube-system UID);
- the receiver runs the pinned image;
- the live scope ConfigMap matches;
- the ingress policy exists;
- the coding-agent boundary already denies the custody data and metadata paths.
It does not create the reader policy, the role or the boundary
(railiance-platform). It also does not create the `sso` ClusterSecretStore and
ExternalSecret, which belong to the rapp-bounded-executor package (CCR-2026-0063
`delivery.target`).
## Founder runbook — nk-bounded-executor sender
Run these in order. Steps 1 and 2 can run now. Step 3 depends on CCR-2026-0063:
railiance-platform must have it approved by platform-owner, audit-core-owner and
net-kingdom-owner, and must have applied its reader policy, role and
coding-agent boundary entries.
**1. Ingress (NK-WP-0056-T16).** This applies exactly one object. Run the server
dry-run first, then the apply:
```bash
cd /home/worsch/audit-core && python3 -c 'import sys,yaml; d=[x for x in yaml.safe_load_all(open("deploy/networkpolicies.yaml")) if x and x["metadata"]["name"]=="audit-core-nk-bounded-executor-ingress"]; assert len(d)==1; yaml.safe_dump(d[0],sys.stdout)' | KUBECONFIG=/home/worsch/.kube/config-railiance01 kubectl apply --dry-run=server -f -
cd /home/worsch/audit-core && python3 -c 'import sys,yaml; d=[x for x in yaml.safe_load_all(open("deploy/networkpolicies.yaml")) if x and x["metadata"]["name"]=="audit-core-nk-bounded-executor-ingress"]; assert len(d)==1; yaml.safe_dump(d[0],sys.stdout)' | KUBECONFIG=/home/worsch/.kube/config-railiance01 kubectl apply -f -
```
**2. Read-only preflight** (prints no secrets):
```bash
KUBECONFIG=/home/worsch/.kube/config-railiance01 kubectl -n audit-core get configmap audit-core-senders-scope -o jsonpath='{.data.senders-scope\.json}' | python3 -c 'import sys,json; print([e for e in json.load(sys.stdin) if e["name"]=="nk-bounded-executor"])'
KUBECONFIG=/home/worsch/.kube/config-railiance01 kubectl -n audit-core get deploy audit-core -o jsonpath='{.spec.template.spec.containers[0].image}{"\n"}'
bridge status
```
The image must end in
`sha256:5f3d01c8a1f90411a1429451b46c66a9ac9262fc7fce68790c16e22eeb7871a6`.
The `openbao-ui-railiance01` tunnel must be up.
**3. Seed and reload (NK-WP-0056-T15). Needs CCR-2026-0063 approved and applied.**
This is one silent command. Read the result from the receipt, not from the
terminal:
```bash
BAO_ADDR=http://127.0.0.1:18200 VAULT_ADDR=http://127.0.0.1:18200 warden access openbao-platform-admin-login --exec -- /home/worsch/audit-core/scripts/attended-seed-nk-bounded-executor-sender.sh /home/worsch/audit-core/docs/evidence/nk-bounded-executor-sender-seed-r1.json
```
The receipt is created exclusively. If you rerun, use `-r2.json`. The receiver
restart is part of this step. No separate rollout is needed.
**4. Verify** (read-only):
```bash
python3 -c 'import json; r=json.load(open("/home/worsch/audit-core/docs/evidence/nk-bounded-executor-sender-seed-r1.json")); print(r["status"], r.get("error"), r.get("receiver_probe"), r.get("registry_secret_hash_match"), r.get("sso_secret_hash_match"), r["credential_values_emitted"])'
KUBECONFIG=/home/worsch/.kube/config-railiance01 kubectl -n audit-core get networkpolicy audit-core-nk-bounded-executor-ingress
```
The expected status is `seeded_pending_sso_delivery`, or `seeded_and_delivered`
once the rapp package's ExternalSecret exists. The expected probe is
`{"read": 403, "own_tenant": 200, "other_tenant": 403, "other_source": 403}`.
After the rapp package applies `sso/nk-bind-executor-audit`, rerun step 3 with
`-r2.json`. It writes no custody or registry and confirms the `sso` copy by
hash.

View file

@ -0,0 +1,12 @@
#!/usr/bin/env bash
# Silent child for the attended nk-bounded-executor sender seed (AUDIT-WP-0018-T04,
# NK-WP-0056-T15, CCR-2026-0063). Founder-run under
# warden access openbao-platform-admin-login --exec -- <this script> <receipt>
# Results go to the receipt only; any output would fail the warden envelope closed.
# Inside the lane HOME is the warden envelope, so the kubeconfig default is absolute.
set -euo pipefail
receipt="${1:?receipt path required}"
exec python3 "$(dirname "$0")/seed-nk-bounded-executor-sender.py" \
--kubeconfig "${RAILIANCE01_KUBECONFIG:-/home/worsch/.kube/config-railiance01}" \
--confirm 'SEED nk-bounded-executor CCR-2026-0063' \
--receipt "$receipt" >/dev/null 2>&1

View file

@ -0,0 +1,427 @@
#!/usr/bin/env python3
"""Attended seed of the nk-bounded-executor sender bearer (AUDIT-WP-0018-T04).
NetKingdom NK-WP-0056-T15. railiance-platform CCR-2026-0063 holds the custody
contract. One process mints one bearer and writes it to the two places that need it:
custody: platform/data/workloads/net-kingdom/bounded-executor/audit-sender
fields AUDIT_TOKEN, CUSTODY_REQUEST=CCR-2026-0063 (cas 0)
registry: platform/data/workloads/audit-core/senders
one appended entry "nk-bounded-executor" (cas on the version read)
It then syncs ExternalSecret audit-core/audit-core-senders, restarts the receiver,
and proves without writing an event that the live receiver knows the bearer
with the exact registered scope. If railiance-platform's ExternalSecret
sso/nk-bind-executor-audit exists, the script syncs it and compares the token by hash.
This script is silent. The wrapper discards stdout and stderr, so `warden access --exec`
fails closed on any output. The token is never printed, never passed on a command line
and never written to the receipt. The receipt holds versions, booleans and a
12-hex SHA-256 fingerprint.
Re-running is safe. Existing custody that this request wrote (version 1,
CUSTODY_REQUEST matching) is reused. A registry entry that equals the desired
entry is not rewritten. Anything else refuses and changes nothing: a sender
without custody, a drifted entry, another request's custody, a token
collision or a concurrent registry write.
Founder-run only. The exact command is in docs/bounded-executor-receipts.md, section
"Founder runbook". The script does not create OpenBao policies or roles, the coding-agent
boundary, the ClusterSecretStore or the ExternalSecret in sso. Those belong to
railiance-platform and the rapp-bounded-executor package.
"""
from __future__ import annotations
import argparse
import copy
import hashlib
import json
import os
import re
import secrets
import subprocess
import time
from datetime import datetime, timezone
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
SENDER = "nk-bounded-executor"
SOURCE = "net-kingdom.bounded-executor"
TENANT = "platform"
CCR_ID = "CCR-2026-0063"
CCR_FILE = Path("/home/worsch/railiance-platform/credential-change-requests/"
"CCR-2026-0063-nk-bind-executor-audit-sender.yaml")
CCR_APPROVERS = ("platform-owner", "audit-core-owner", "net-kingdom-owner")
CONFIRM = "SEED nk-bounded-executor CCR-2026-0063"
REGISTRY = "platform/data/workloads/audit-core/senders"
CUSTODY_KV = "platform/workloads/net-kingdom/bounded-executor/audit-sender"
CUSTODY_DATA = "platform/data/workloads/net-kingdom/bounded-executor/audit-sender"
CUSTODY_META = "platform/metadata/workloads/net-kingdom/bounded-executor/audit-sender"
BOUNDARY = "sys/policies/acl/agent-high-risk-boundary"
CLUSTER_UID = "a553c742-0115-43d4-99a4-a5ca56fe0786" # railiance01 kube-system
SSO_SECRET = "nk-bind-executor-audit"
SSO_KEY = "audit-token"
TOKEN_SHAPE = re.compile(r"[A-Za-z0-9_-]{64}")
# Runs inside the receiver pod. The bearer arrives on stdin and leaves only as
# HTTP status codes. No request writes an event.
RECEIVER_PROBE = r'''import json,sys,urllib.request,urllib.error
tok=sys.stdin.read().strip()
def get(p,auth=True):
r=urllib.request.Request("http://127.0.0.1:8080"+p)
if auth: r.add_header("Authorization","Bearer "+tok)
try:
with urllib.request.urlopen(r,timeout=10) as x: return x.status
except urllib.error.HTTPError as e: return e.code
w="since=2026-01-01T00:00:00%2B00:00&until=2026-01-02T00:00:00%2B00:00"
print(json.dumps({
"read":get("/v1/events/probe-not-an-id"),
"own_tenant":get("/v1/reconciliation?source=net-kingdom.bounded-executor&tenant=platform&"+w),
"other_tenant":get("/v1/reconciliation?source=net-kingdom.bounded-executor&tenant=tenant:platform&"+w),
"other_source":get("/v1/reconciliation?source=tenant-engine&tenant=platform&"+w)}))
'''
PROBE_EXPECTED = {"read": 403, "own_tenant": 200, "other_tenant": 403, "other_source": 403}
class Refused(Exception):
"""A named refusal. The message is a reason code, never a value."""
class NotFound(Exception):
"""The OpenBao path holds no value."""
def require(condition: bool, code: str) -> None:
if not condition:
raise Refused(code)
def fingerprint(token: str) -> str:
return hashlib.sha256(token.encode()).hexdigest()[:12]
# --- contract ---------------------------------------------------------------
def scope_entry(path: Path = ROOT / "deploy" / "senders-scope.json") -> dict:
rows = json.loads(path.read_text())
entry = next((r for r in rows if r.get("name") == SENDER), None)
require(entry is not None, "scope_entry_missing")
require(entry.get("sources") == [SOURCE] and entry.get("tenants") == [TENANT]
and entry.get("may_write") is True and entry.get("may_read") is False
and entry.get("secret_policy") == "reject"
and entry.get("evidence_kind") == "load-bearing", "scope_entry_drift")
return entry
def desired(token: str, scope: dict) -> dict:
"""Registry entry: exactly the declared scope plus the bearer."""
entry = {"name": SENDER, "tokens": [token]}
entry.update({k: copy.deepcopy(v) for k, v in scope.items() if k != "name"})
return entry
def check_ccr(doc: dict) -> None:
require(doc.get("id") == CCR_ID, "ccr_identity_mismatch")
require(doc.get("status") in {"approved", "applied", "verified", "active"}, "approved_ccr_required")
comments = (doc.get("review") or {}).get("comments") or []
for owner in CCR_APPROVERS:
require(any(isinstance(c, dict) and c.get("decision") == "approved"
and owner in str(c.get("reviewer", "")) for c in comments),
"named_owner_reviews_required")
ob = doc.get("openbao") or {}
require(ob.get("kv_path") == CUSTODY_KV and ob.get("fields") == ["AUDIT_TOKEN", "CUSTODY_REQUEST"]
and ob.get("metadata_read") is False, "ccr_custody_contract_mismatch")
delivery = doc.get("delivery") or {}
require(delivery.get("namespace") == "sso", "ccr_delivery_mismatch")
def load_ccr(path: Path = CCR_FILE) -> tuple[dict, str]:
import yaml # system PyYAML; only needed for the CCR file
raw = path.read_bytes()
return yaml.safe_load(raw), hashlib.sha256(raw).hexdigest()
# --- process boundaries -----------------------------------------------------
def bao(args: list[str], *, payload: dict | None = None) -> dict:
env = os.environ.copy()
for name in ("BAO_TOKEN", "VAULT_TOKEN", "OPENBAO_TOKEN"):
env.pop(name, None)
proc = subprocess.run(["bao", *args],
input=None if payload is None else json.dumps(payload).encode(),
capture_output=True, env=env, check=False, timeout=60)
if proc.returncode != 0:
err = proc.stderr.decode(errors="replace").lower() # inspected, never emitted
if "no value found" in err or "404" in err:
raise NotFound()
raise Refused("bao_failed")
return json.loads(proc.stdout.decode() or "{}")
class Kube:
def __init__(self, kubeconfig: str):
self.base = ["kubectl", "--kubeconfig", kubeconfig, "--request-timeout=30s"]
def run(self, args: list[str], *, stdin: bytes | None = None) -> subprocess.CompletedProcess:
return subprocess.run([*self.base, *args], input=stdin, capture_output=True,
check=False, timeout=90)
def json(self, args: list[str], code: str = "kubectl_failed") -> dict:
proc = self.run([*args, "-o", "json"])
require(proc.returncode == 0, code)
return json.loads(proc.stdout.decode())
def exists(self, args: list[str]) -> bool:
return self.run([*args, "-o", "name"]).returncode == 0
# --- OpenBao ----------------------------------------------------------------
def snapshot(call=bao) -> tuple[int, dict, list, set]:
raw = call(["read", "-format=json", REGISTRY])["data"]
version = raw["metadata"]["version"]
require(type(version) is int and version > 0, "registry_version_missing")
body = raw["data"]
require(isinstance(body, dict) and isinstance(body.get("senders.json"), str), "registry_shape_invalid")
try:
rows = json.loads(body["senders.json"])
except ValueError:
raise Refused("registry_shape_invalid") from None
require(isinstance(rows, list) and bool(rows), "registry_must_preserve_existing_senders")
names: set = set()
tokens: set = set()
for row in rows:
require(isinstance(row, dict) and isinstance(row.get("name"), str) and row["name"]
and row["name"] not in names, "registry_identity_invalid_or_duplicate")
names.add(row["name"])
values = row.get("tokens") or ([row["token"]] if row.get("token") else [])
require(isinstance(values, list) and bool(values), "registry_tokens_invalid")
for value in values:
require(isinstance(value, str) and value and value not in tokens, "registry_token_collision")
tokens.add(value)
return version, body, rows, tokens
def read_custody(call=bao) -> dict | None:
try:
return call(["read", "-format=json", CUSTODY_DATA])["data"]
except NotFound:
return None
def seed(scope: dict, receipt: dict, save, call=bao) -> str:
"""Mint or reuse the bearer, then merge it into the registry. Returns the token."""
version, body, rows, used = snapshot(call)
originals = copy.deepcopy(rows)
found = next((r for r in rows if r["name"] == SENDER), None)
receipt.update(registry_version_before=version, other_senders=len(rows) - (found is not None))
existing = read_custody(call)
if existing is None:
require(found is None, "registry_sender_without_custody")
token = secrets.token_urlsafe(48)
require(TOKEN_SHAPE.fullmatch(token) is not None and token not in used, "sender_token_collision")
result = call(["write", "-format=json", CUSTODY_DATA, "-"],
payload={"options": {"cas": 0},
"data": {"AUDIT_TOKEN": token, "CUSTODY_REQUEST": CCR_ID}})
require((result.get("data") or {}).get("version") == 1, "initial_custody_version_mismatch")
receipt.update(custody="created", custody_version=1)
else:
data = existing.get("data") or {}
require(existing["metadata"]["version"] == 1 and set(data) == {"AUDIT_TOKEN", "CUSTODY_REQUEST"}
and data["CUSTODY_REQUEST"] == CCR_ID, "custody_provenance_mismatch")
token = data["AUDIT_TOKEN"]
require(isinstance(token, str) and TOKEN_SHAPE.fullmatch(token) is not None, "custody_token_shape")
if found is None:
require(token not in used, "sender_token_collision")
receipt.update(custody="reused", custody_version=1)
receipt["token_sha256_12"] = fingerprint(token)
want = desired(token, scope)
if found is not None:
require(found == want, "registered_sender_drift")
receipt["registry"] = "already_present"
else:
receipt["phase"] = "registry_cas"
save()
updated = [*rows, want]
payload = dict(body, **{"senders.json": json.dumps(updated, separators=(",", ":"))})
call(["write", "-format=json", REGISTRY, "-"], payload={"options": {"cas": version}, "data": payload})
receipt["registry"] = "appended"
after_version, after_body, after_rows, _ = snapshot(call)
require(all(r in after_rows for r in originals) and want in after_rows
and len(after_rows) == len(originals) + (found is None)
and all(after_body.get(k) == v for k, v in body.items() if k != "senders.json"),
"registry_readback_mismatch")
receipt.update(registry_version_after=after_version, unchanged_other_senders=True)
save()
return token
# --- Kubernetes -------------------------------------------------------------
def secret_entry_sha(kube: Kube) -> str | None:
import base64
secret = kube.json(["-n", "audit-core", "get", "secret", "audit-core-senders"])
raw = (secret.get("data") or {}).get("senders.json")
if not raw:
return None
rows = json.loads(base64.b64decode(raw))
row = next((r for r in rows if isinstance(r, dict) and r.get("name") == SENDER), None)
if not row or not row.get("tokens"):
return None
return hashlib.sha256(row["tokens"][-1].encode()).hexdigest()
def wait_for(check, timeout: int, interval: int = 3) -> bool:
deadline = time.time() + timeout
while time.time() < deadline:
try:
if check():
return True
except Exception:
pass
time.sleep(interval)
return False
def receiver_pod(kube: Kube, image: str) -> str | None:
dep = kube.json(["-n", "audit-core", "get", "deployment", "audit-core"])
if dep["spec"]["template"]["spec"]["containers"][0]["image"] != image:
return None
if dep.get("status", {}).get("readyReplicas") != 1 or \
dep["status"].get("observedGeneration", 0) < dep["metadata"]["generation"]:
return None
pods = kube.json(["-n", "audit-core", "get", "pods", "-l",
"app.kubernetes.io/name=audit-core,app.kubernetes.io/component=receiver"])["items"]
if len(pods) != 1 or pods[0]["metadata"].get("deletionTimestamp"):
return None
statuses = pods[0].get("status", {}).get("containerStatuses") or []
if not statuses or not all(s.get("ready") for s in statuses) \
or pods[0]["spec"]["containers"][0]["image"] != image:
return None
return pods[0]["metadata"]["name"]
def live_scope_matches(kube: Kube, scope: dict) -> bool:
cm = kube.json(["-n", "audit-core", "get", "configmap", "audit-core-senders-scope"], "scope_configmap_unreadable")
rows = json.loads((cm.get("data") or {}).get("senders-scope.json") or "[]")
return next((r for r in rows if r.get("name") == SENDER), None) == scope
def expected_image() -> str:
text = (ROOT / "deploy" / "audit-core.yaml").read_text()
match = re.search(r"image:\s*(forgejo\.coulomb\.social/coulomb/audit-core@sha256:[0-9a-f]{64})", text)
require(match is not None, "receiver_digest_required")
return match.group(1)
def probe(kube: Kube, pod: str, token: str) -> dict:
proc = kube.run(["-n", "audit-core", "exec", "-i", pod, "-c", "audit-core", "--",
"python", "-c", RECEIVER_PROBE], stdin=token.encode())
require(proc.returncode == 0, "receiver_probe_failed")
return json.loads(proc.stdout.decode())
def main(argv: list[str] | None = None) -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--kubeconfig", required=True)
parser.add_argument("--confirm", required=True)
parser.add_argument("--receipt", type=Path, required=True)
args = parser.parse_args(argv)
receipt: dict = {"schema": "audit-core.sender-seed.v1", "sender": SENDER, "ccr": CCR_ID,
"custody_path": CUSTODY_KV, "status": "refused",
"credential_values_emitted": False,
"started_at": datetime.now(timezone.utc).replace(microsecond=0).isoformat()}
fd = os.open(args.receipt, os.O_RDWR | os.O_CREAT | os.O_EXCL | os.O_NOFOLLOW, 0o600)
def save() -> None:
os.lseek(fd, 0, os.SEEK_SET)
os.ftruncate(fd, 0)
os.write(fd, (json.dumps(receipt, indent=2) + "\n").encode())
os.fsync(fd)
try:
save()
require(args.confirm == CONFIRM, "exact_confirmation_required")
scope = scope_entry()
ccr, ccr_sha = load_ccr()
check_ccr(ccr)
receipt["ccr_sha256"] = ccr_sha
require(Path.home().parent.name == ".warden-attended-login", "attended_warden_envelope_required")
require(not any(os.environ.get(n) for n in ("BAO_TOKEN", "VAULT_TOKEN", "OPENBAO_TOKEN")),
"token_env_must_be_unset")
identity = bao(["token", "lookup", "-format=json"])["data"]
policies = identity.get("policies") or []
require("platform-admin" in policies and "root" not in policies, "attended_platform_admin_required")
kube = Kube(args.kubeconfig)
ns = kube.json(["get", "namespace", "kube-system"], "not_railiance01")
require(ns.get("metadata", {}).get("uid") == CLUSTER_UID, "not_railiance01")
image = expected_image()
require(receiver_pod(kube, image) is not None, "receiver_not_current")
require(live_scope_matches(kube, scope), "live_scope_configmap_mismatch")
require(kube.exists(["-n", "audit-core", "get", "networkpolicy",
"audit-core-nk-bounded-executor-ingress"]), "ingress_policy_not_applied")
boundary = bao(["read", "-format=json", BOUNDARY])["data"]["policy"]
require(all(f'"{p}"' in boundary for p in (CUSTODY_DATA, CUSTODY_META)),
"coding_agent_boundary_missing")
receipt.update(phase="preflight_ok", receiver_image=image)
save()
token = seed(scope, receipt, save)
want = hashlib.sha256(token.encode()).hexdigest()
receipt["phase"] = "registry_sync"
save()
require(kube.run(["-n", "audit-core", "annotate", "externalsecret", "audit-core-senders",
f"force-sync={int(time.time())}", "--overwrite"]).returncode == 0,
"externalsecret_annotate_failed")
require(wait_for(lambda: secret_entry_sha(kube) == want, 120), "registry_secret_sync_timeout")
receipt["registry_secret_hash_match"] = True
receipt["phase"] = "receiver_reload"
save()
require(kube.run(["-n", "audit-core", "rollout", "restart", "deployment/audit-core"]).returncode == 0,
"receiver_restart_failed")
time.sleep(5)
holder: dict = {}
require(wait_for(lambda: holder.update(pod=receiver_pod(kube, image)) or holder["pod"], 240, 5),
"receiver_not_ready_after_restart")
result = probe(kube, holder["pod"], token)
receipt["receiver_probe"] = result
require(result == PROBE_EXPECTED, "receiver_probe_mismatch")
receipt["phase"] = "sso_delivery"
save()
if kube.exists(["-n", "sso", "get", "externalsecret", SSO_SECRET]):
kube.run(["-n", "sso", "annotate", "externalsecret", SSO_SECRET,
f"force-sync={int(time.time())}", "--overwrite"])
def sso_sha() -> bool:
import base64
secret = kube.json(["-n", "sso", "get", "secret", SSO_SECRET])
value = base64.b64decode((secret.get("data") or {})[SSO_KEY]).decode()
return hashlib.sha256(value.encode()).hexdigest() == want
require(wait_for(sso_sha, 120), "sso_secret_hash_mismatch")
receipt.update(sso_secret_hash_match=True, status="seeded_and_delivered")
else:
receipt.update(sso_secret_hash_match=None, status="seeded_pending_sso_delivery")
receipt["phase"] = "done"
receipt["finished_at"] = datetime.now(timezone.utc).replace(microsecond=0).isoformat()
save()
return 0
except Exception as exc:
receipt["status"] = "refused"
receipt["error"] = str(exc) if isinstance(exc, Refused) else "contained_operation_failed"
save()
return 1
finally:
os.close(fd)
if __name__ == "__main__":
raise SystemExit(main())

View file

@ -0,0 +1,250 @@
"""AUDIT-WP-0018-T04. Seed script for the nk-bounded-executor bearer, on fixtures.
Uses a fake OpenBao and obviously fake token strings. No real credential, no
cluster.
"""
import copy
import importlib.util
import json
import sys
from pathlib import Path
import subprocess
import threading
from wsgiref.simple_server import WSGIRequestHandler, make_server
import pytest
from audit_core.ingestion import IngestionApplication
from audit_core.senders import SenderRegistry
from audit_core.sqlite_backend import SQLiteAuditBackend
ROOT = Path(__file__).resolve().parents[1]
SCRIPT = ROOT / "scripts" / "seed-nk-bounded-executor-sender.py"
SPEC = importlib.util.spec_from_file_location("seed_nk_bounded_executor_sender", SCRIPT)
SEED = importlib.util.module_from_spec(SPEC)
sys.modules[SPEC.name] = SEED
SPEC.loader.exec_module(SEED)
OTHER = [
{"name": "tenant-engine", "tokens": ["fake-tenant-engine-not-a-secret"],
"sources": ["tenant-engine"], "tenants": ["*"]},
{"name": "operator", "tokens": ["fake-operator-not-a-secret"], "sources": ["*"],
"tenants": ["*"], "may_write": False, "may_read": True},
]
FAKE = "f" * 64 # matches the minted shape, obviously not a secret
class FakeBao:
"""KV v2 with cas enforcement for the registry and the custody path."""
def __init__(self, rows=None, custody=None, extra_fields=None):
self.kv = {
SEED.REGISTRY: [1, dict({"senders.json": json.dumps(rows if rows is not None else OTHER)},
**(extra_fields or {}))],
}
if custody is not None:
self.kv[SEED.CUSTODY_DATA] = [1, custody]
self.writes = []
def __call__(self, args, *, payload=None):
if args[0] == "read":
path = args[-1]
if path not in self.kv:
raise SEED.NotFound()
version, data = self.kv[path]
return {"data": {"metadata": {"version": version}, "data": copy.deepcopy(data)}}
assert args[0] == "write"
path = args[-2]
current = self.kv.get(path, [0, None])[0]
assert payload["options"]["cas"] == current, "cas must match the version read"
self.writes.append(path)
self.kv[path] = [current + 1, copy.deepcopy(payload["data"])]
return {"data": {"version": current + 1}}
class _Quiet(WSGIRequestHandler):
def log_message(self, *args):
pass
def run_seed(fake):
receipt = {}
token = SEED.seed(SEED.scope_entry(), receipt, lambda: None, call=fake)
return token, receipt
def registry_rows(fake):
return json.loads(fake.kv[SEED.REGISTRY][1]["senders.json"])
def test_first_run_mints_once_into_custody_and_registry():
fake = FakeBao(extra_fields={"unrelated": "kept"})
token, receipt = run_seed(fake)
assert SEED.TOKEN_SHAPE.fullmatch(token)
assert fake.writes == [SEED.CUSTODY_DATA, SEED.REGISTRY]
assert fake.kv[SEED.CUSTODY_DATA][1] == {"AUDIT_TOKEN": token, "CUSTODY_REQUEST": "CCR-2026-0063"}
rows = registry_rows(fake)
assert rows[:2] == OTHER
assert rows[2] == SEED.desired(token, SEED.scope_entry())
assert fake.kv[SEED.REGISTRY][1]["unrelated"] == "kept"
assert receipt["custody"] == "created" and receipt["registry"] == "appended"
assert receipt["token_sha256_12"] == SEED.fingerprint(token)
assert token not in json.dumps(receipt)
def test_rerun_reuses_and_writes_nothing():
fake = FakeBao()
token, _ = run_seed(fake)
fake.writes.clear()
again, receipt = run_seed(fake)
assert again == token and fake.writes == []
assert receipt["custody"] == "reused" and receipt["registry"] == "already_present"
def test_resume_after_custody_written_but_registry_not():
fake = FakeBao(custody={"AUDIT_TOKEN": FAKE, "CUSTODY_REQUEST": "CCR-2026-0063"})
token, receipt = run_seed(fake)
assert token == FAKE and fake.writes == [SEED.REGISTRY]
assert receipt["custody"] == "reused" and receipt["registry"] == "appended"
@pytest.mark.parametrize("custody, code", [
({"AUDIT_TOKEN": FAKE, "CUSTODY_REQUEST": "CCR-2026-0099"}, "custody_provenance_mismatch"),
({"AUDIT_TOKEN": FAKE}, "custody_provenance_mismatch"),
({"AUDIT_TOKEN": "short", "CUSTODY_REQUEST": "CCR-2026-0063"}, "custody_token_shape"),
])
def test_foreign_or_malformed_custody_refuses(custody, code):
fake = FakeBao(custody=custody)
with pytest.raises(SEED.Refused, match=code):
run_seed(fake)
assert fake.writes == []
def test_registered_sender_without_custody_refuses():
rows = OTHER + [{"name": "nk-bounded-executor", "tokens": [FAKE]}]
fake = FakeBao(rows=rows)
with pytest.raises(SEED.Refused, match="registry_sender_without_custody"):
run_seed(fake)
assert fake.writes == []
def test_drifted_registry_entry_refuses():
entry = SEED.desired(FAKE, SEED.scope_entry())
entry["tenants"] = ["*"]
fake = FakeBao(rows=OTHER + [entry],
custody={"AUDIT_TOKEN": FAKE, "CUSTODY_REQUEST": "CCR-2026-0063"})
with pytest.raises(SEED.Refused, match="registered_sender_drift"):
run_seed(fake)
assert fake.writes == []
def test_custody_token_already_used_by_another_sender_refuses():
rows = OTHER + [{"name": "someone", "tokens": [FAKE]}]
fake = FakeBao(rows=rows, custody={"AUDIT_TOKEN": FAKE, "CUSTODY_REQUEST": "CCR-2026-0063"})
with pytest.raises(SEED.Refused, match="sender_token_collision"):
run_seed(fake)
def test_empty_or_duplicate_registry_refuses():
with pytest.raises(SEED.Refused, match="registry_must_preserve_existing_senders"):
run_seed(FakeBao(rows=[]))
with pytest.raises(SEED.Refused, match="registry_identity_invalid_or_duplicate"):
run_seed(FakeBao(rows=OTHER + [OTHER[0]]))
def test_concurrent_registry_removal_is_caught_on_readback():
class Lossy(FakeBao):
def __call__(self, args, *, payload=None):
result = super().__call__(args, payload=payload)
if args[0] == "write" and args[-2] == SEED.REGISTRY:
rows = registry_rows(self)[1:] # someone else dropped tenant-engine
self.kv[SEED.REGISTRY][1]["senders.json"] = json.dumps(rows)
return result
with pytest.raises(SEED.Refused, match="registry_readback_mismatch"):
run_seed(Lossy())
# --- contract checks ----------------------------------------------------------
def approved_ccr():
doc, _ = SEED.load_ccr()
doc = copy.deepcopy(doc)
doc["status"] = "approved"
doc["review"]["comments"] = [
{"reviewer": owner, "decision": "approved"} for owner in SEED.CCR_APPROVERS
]
return doc
@pytest.mark.skipif(not SEED.CCR_FILE.exists(), reason="railiance-platform checkout absent")
def test_ccr_contract_as_drafted_matches_and_refuses_until_approved():
pytest.importorskip("yaml")
SEED.check_ccr(approved_ccr())
proposed, _ = SEED.load_ccr()
with pytest.raises(SEED.Refused, match="approved_ccr_required"):
SEED.check_ccr(proposed)
partial = approved_ccr()
partial["review"]["comments"] = partial["review"]["comments"][:2]
with pytest.raises(SEED.Refused, match="named_owner_reviews_required"):
SEED.check_ccr(partial)
moved = approved_ccr()
moved["openbao"]["kv_path"] = "platform/workloads/net-kingdom/other"
with pytest.raises(SEED.Refused, match="ccr_custody_contract_mismatch"):
SEED.check_ccr(moved)
def test_wrong_confirmation_refuses_before_any_call(tmp_path, monkeypatch):
monkeypatch.setattr(SEED, "bao", lambda *a, **k: pytest.fail("bao called"))
receipt = tmp_path / "r.json"
rc = SEED.main(["--kubeconfig", "/nonexistent", "--confirm", "yes", "--receipt", str(receipt)])
assert rc == 1
doc = json.loads(receipt.read_text())
assert doc["error"] == "exact_confirmation_required" and doc["credential_values_emitted"] is False
# Receipt is never overwritten: a second run needs a fresh path.
with pytest.raises(FileExistsError):
SEED.main(["--kubeconfig", "/nonexistent", "--confirm", SEED.CONFIRM, "--receipt", str(receipt)])
def test_wrapper_is_silent_and_pins_the_confirmation():
text = (ROOT / "scripts" / "attended-seed-nk-bounded-executor-sender.sh").read_text()
assert ">/dev/null 2>&1" in text and SEED.CONFIRM in text
# --- the seeded entry and the live probe, against the real receiver -----------
def test_seeded_entry_yields_the_declared_identity_with_the_live_overlay():
entry = SEED.desired(FAKE, SEED.scope_entry())
reg = SenderRegistry.from_env({
"AUDIT_CORE_SENDERS": json.dumps(OTHER + [entry]),
"AUDIT_CORE_SENDERS_SCOPE_PATH": str(ROOT / "deploy" / "senders-scope.json"),
})
identity = reg.authenticate(f"Bearer {FAKE}")
assert identity.name == "nk-bounded-executor"
assert identity.sources == frozenset({"net-kingdom.bounded-executor"})
assert identity.tenants == frozenset({"platform"})
assert identity.may_write and not identity.may_read
assert identity.secret_policy == "reject" and identity.is_load_bearing
def test_probe_expectations_match_the_receiver(tmp_path):
"""Runs the probe source itself against the real receiver over HTTP."""
entry = SEED.desired(FAKE, SEED.scope_entry())
reg = SenderRegistry.from_env({"AUDIT_CORE_SENDERS": json.dumps(OTHER + [entry])})
app = IngestionApplication(SQLiteAuditBackend(str(tmp_path / "p.db")), reg)
server = make_server("127.0.0.1", 0, app, handler_class=_Quiet)
thread = threading.Thread(target=server.serve_forever, daemon=True)
thread.start()
try:
source = SEED.RECEIVER_PROBE.replace("127.0.0.1:8080", f"127.0.0.1:{server.server_port}")
def run(token):
proc = subprocess.run([sys.executable, "-c", source], input=token.encode(),
capture_output=True, check=True, timeout=30)
return json.loads(proc.stdout)
assert run(FAKE) == SEED.PROBE_EXPECTED
# An unknown bearer gets 401 everywhere, so the expected pattern proves registration.
assert set(run("fake-unknown").values()) == {401}
finally:
server.shutdown()

View file

@ -9,7 +9,7 @@ flavor: implementation
owner: audit-core
topic_slug: audit-evidence-dependencies
created: "2026-10-31"
updated: "2026-11-03"
updated: "2026-11-04"
related:
- NK-WP-0056
- NK-WP-0040
@ -129,6 +129,58 @@ Code: `audit_core/stream_findings.py` (`evaluate_closing`) and
completed. The finding stays inert until `nk-bounded-executor` has a seeded
token and emits.
## Prepare the founder-run seed of the sender bearer
```task
id: AUDIT-WP-0018-T04
status: done
priority: high
```
NK-WP-0056-T15 needs a bearer for `nk-bounded-executor`. Audit-core's registry
must hold the bearer, and so must railiance-platform's CCR-2026-0063 custody path
`platform/workloads/net-kingdom/bounded-executor/audit-sender` (`AUDIT_TOKEN`).
ESO projects that path to `sso/nk-bind-executor-audit:audit-token`.
Done 2026-11-04. The script
`scripts/seed-nk-bounded-executor-sender.py` has a silent wrapper,
`scripts/attended-seed-nk-bounded-executor-sender.sh`, and runs in one attended
process:
- mint once;
- custody written with cas 0;
- registry merged with cas;
- `audit-core-senders` synced and checked by hash;
- receiver restarted;
- a non-writing probe of the live scope;
- the `sso` copy checked by hash, when its ExternalSecret exists.
Tests: `tests/test_seed_nk_bounded_executor_sender.py` (16, synthetic). Founder
commands: `docs/bounded-executor-receipts.md`, "Founder runbook". A read-only
check on 2026-11-04 found the ingress policy not live. The other nine policies
in `deploy/networkpolicies.yaml` match live.
## Founder-run live activation of the sender and ingress
```task
id: AUDIT-WP-0018-T05
status: wait
priority: high
```
Run the runbook steps in order:
1. **Ingress apply (NK-WP-0056-T16):** runnable now.
2. **Read-only preflight.**
3. **Seed and reload (NK-WP-0056-T15):** CCR-2026-0063 must be approved by its
three owners. Railiance-platform must also have applied the reader policy,
the role and the coding-agent boundary entries.
4. **Verify from the receipt.**
`sso` delivery is complete only when rapp-bounded-executor carries the
ClusterSecretStore and the ExternalSecret `nk-bind-executor-audit`, and mounts
key `audit-token`. Today it mounts `bounded-executor-tokens:audit-core`.
## Founder decision 2026-10-31
Approved by the founder (`proposed` -> `ready`) after the NK-WP-0056-T02 owner answers (hub decision 19afd15e). Founder adopted the `execution.run.started` event plus one receipt per run with an admission-level target/action, `steps[]` detail and labelled `decision_refs`.