From e7ee6d06949807a4a04e7ccafd1855bb674a59d5 Mon Sep 17 00:00:00 2001 From: tegwick Date: Sat, 3 Oct 2026 00:45:57 +0200 Subject: [PATCH] Seed tenant-engine sender token into its dedicated KV path (MASON-WP-0007 row 7) Attended, silent, check-and-set. Evidence 2026-10-02: created, version 1, no values. Co-Authored-By: Claude Sonnet 5.5 Assistant: claude-code Assistant-Model: sonnet Assistant-Process: 292700@bnt-lap001 Assistant-Session: 42de6b79-9019-4cfd-b49a-6db3bbfe9341 --- ...-tenant-engine-audit-core-sender-seed.json | 12 ++ ...ed-seed-tenant-engine-audit-core-sender.sh | 13 ++ .../seed-tenant-engine-audit-core-sender.py | 180 ++++++++++++++++++ ...st_seed_tenant_engine_audit_core_sender.py | 103 ++++++++++ 4 files changed, 308 insertions(+) create mode 100644 docs/evidence/2026-10-02-tenant-engine-audit-core-sender-seed.json create mode 100755 scripts/attended-seed-tenant-engine-audit-core-sender.sh create mode 100755 scripts/seed-tenant-engine-audit-core-sender.py create mode 100644 tests/test_seed_tenant_engine_audit_core_sender.py diff --git a/docs/evidence/2026-10-02-tenant-engine-audit-core-sender-seed.json b/docs/evidence/2026-10-02-tenant-engine-audit-core-sender-seed.json new file mode 100644 index 0000000..1995143 --- /dev/null +++ b/docs/evidence/2026-10-02-tenant-engine-audit-core-sender-seed.json @@ -0,0 +1,12 @@ +{ + "step": "done", + "observed_at": "2026-10-02T22:44:39+00:00", + "sender": "tenant-engine", + "source": "platform/workloads/audit-core/senders", + "destination": "platform/workloads/tenant-engine/audit-core-sender", + "credential_values_emitted": false, + "registry_version": 11, + "destination_version": 1, + "action": "created", + "field": "token" +} diff --git a/scripts/attended-seed-tenant-engine-audit-core-sender.sh b/scripts/attended-seed-tenant-engine-audit-core-sender.sh new file mode 100755 index 0000000..4ae07ce --- /dev/null +++ b/scripts/attended-seed-tenant-engine-audit-core-sender.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash +# Parent-env wrapper for the attended seed of the tenant-engine sender token +# into platform/workloads/tenant-engine/audit-core-sender (field token). +# MASON-WP-0007 row 7 / TEN-IN-0005. Founder-run; no agent executes this. +# +# Operator login is the named tunnel openbao-ui-railiance01 -> +# http://127.0.0.1:18200. railiance-platform's openbao-attended-exec.py pins +# that address for both OIDC and the child. Run `bridge status` first. +# Read the line warden prints, not the exit code. +set -euo pipefail +cd "$(dirname "$0")/.." +exec python3 /home/worsch/railiance-platform/scripts/openbao-attended-exec.py -- \ + "$PWD/scripts/seed-tenant-engine-audit-core-sender.py" diff --git a/scripts/seed-tenant-engine-audit-core-sender.py b/scripts/seed-tenant-engine-audit-core-sender.py new file mode 100755 index 0000000..4a37907 --- /dev/null +++ b/scripts/seed-tenant-engine-audit-core-sender.py @@ -0,0 +1,180 @@ +#!/usr/bin/env python3 +"""Attended seed of the tenant-engine sender token into its dedicated KV path. + +ops-mason MASON-WP-0007 / tenant-engine TEN-IN-0005, plan row 7. The registry +document holds every sender, and OpenBao policies scope by path, not by field, +so External Secrets reads a one-field copy instead: + + source: platform/data/workloads/audit-core/senders (sender "tenant-engine") + destination: platform/data/workloads/tenant-engine/audit-core-sender (field "token") + +Read and write happen in this one process. The token is never printed, never +passed on a command line, never written to evidence; evidence holds versions, +booleans and counts only. Silent: warden access --exec fails closed on any +child stdout/stderr. + + ./scripts/attended-seed-tenant-engine-audit-core-sender.sh + +Re-running is safe. Equal value: no write. Different value (rotation re-seed): +one check-and-set write against the version just read. No destination: cas 0. +When the registry holds several tokens for the sender (rotation overlap), the +last one is seeded, which is the newest appended. +""" +from __future__ import annotations + +import json +import os +import subprocess +import sys +from datetime import datetime, timezone +from pathlib import Path + +REGISTRY = "platform/data/workloads/audit-core/senders" +SENDER = "tenant-engine" +DEST_DATA = "platform/data/workloads/tenant-engine/audit-core-sender" +DEST_META = "platform/metadata/workloads/tenant-engine/audit-core-sender" +FIELD = "token" +EVIDENCE_DIR = Path("/home/worsch/audit-core/docs/evidence") + + +class NotFound(Exception): + """The path holds no value. Distinct from a failed call.""" + + +def _now() -> str: + return datetime.now(timezone.utc).replace(microsecond=0).isoformat() + + +def evidence_path() -> Path: + return EVIDENCE_DIR / f"{_now()[:10]}-tenant-engine-audit-core-sender-seed.json" + + +def _evidence(step: str, extra: dict | None = None) -> None: + doc = { + "step": step, + "observed_at": _now(), + "sender": SENDER, + "source": "platform/workloads/audit-core/senders", + "destination": "platform/workloads/tenant-engine/audit-core-sender", + "credential_values_emitted": False, + } + if extra: + doc.update(extra) + path = evidence_path() + path.parent.mkdir(parents=True, exist_ok=True) + path.write_text(json.dumps(doc, indent=2) + "\n") + + +def _fail(reason: str, extra: dict | None = None) -> int: + payload = {"reason": reason} + if extra: + payload.update(extra) + _evidence("failed", payload) + return 1 + + +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(), + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + env=env, + check=False, + ) + if proc.returncode != 0: + # stderr is inspected, never emitted. + err = proc.stderr.decode(errors="replace").lower() + if "no value found" in err or "404" in err: + raise NotFound() + raise RuntimeError("bao_failed") + return json.loads(proc.stdout.decode() or "{}") + + +def pick_token(rows: list) -> str: + """The sender's newest token. Raises ValueError with a reason, never a value.""" + row = next((r for r in rows if isinstance(r, dict) and r.get("name") == SENDER), None) + if row is None: + raise ValueError("sender_not_in_registry") + tokens = [t for t in (row.get("tokens") or []) if isinstance(t, str) and t] + if not tokens: + raise ValueError("sender_has_no_tokens") + return tokens[-1] + + +def seed(token: str, call=bao) -> dict: + """Write token to the destination if needed. Returns metadata only.""" + try: + current = call(["read", "-format=json", DEST_DATA])["data"] + version = current["metadata"]["version"] + existing = (current.get("data") or {}).get(FIELD) + except NotFound: + version, existing = 0, None + if existing == token: + return {"action": "already_seeded", "version": version} + call( + ["write", "-format=json", DEST_DATA, "-"], + payload={"options": {"cas": version}, "data": {FIELD: token}}, + ) + after = call(["read", "-format=json", DEST_DATA])["data"] + if (after.get("data") or {}).get(FIELD) != token: + raise RuntimeError("readback_mismatch") + return { + "action": "created" if version == 0 else "reseeded", + "version": after["metadata"]["version"], + } + + +def main() -> int: + _evidence("started") + if Path.home().parent.name != ".warden-attended-login": + return _fail("attended_warden_envelope_required") + if os.environ.get("BAO_TOKEN") or os.environ.get("VAULT_TOKEN"): + return _fail("token_env_must_be_unset") + try: + identity = bao(["token", "lookup", "-format=json"])["data"] + except Exception: + return _fail("token_lookup_failed") + policies = identity.get("policies") or [] + if "platform-admin" not in policies or "root" in policies: + return _fail("attended_platform_admin_required") + + try: + raw = bao(["read", "-format=json", REGISTRY])["data"] + registry_version = raw["metadata"]["version"] + rows = json.loads(raw["data"]["senders.json"]) + except Exception: + return _fail("registry_read_failed") + try: + token = pick_token(rows) + except ValueError as exc: + return _fail(str(exc), {"registry_version": registry_version}) + _evidence("registry_read", {"registry_version": registry_version}) + + try: + result = seed(token) + except Exception as exc: + return _fail("seed_failed", {"error_class": exc.__class__.__name__}) + _evidence( + "done", + { + "registry_version": registry_version, + "destination_version": result["version"], + "action": result["action"], + "field": FIELD, + }, + ) + return 0 + + +if __name__ == "__main__": + try: + raise SystemExit(main()) + except SystemExit: + raise + except Exception: + _fail("contained_operation_failed") + raise SystemExit(1) diff --git a/tests/test_seed_tenant_engine_audit_core_sender.py b/tests/test_seed_tenant_engine_audit_core_sender.py new file mode 100644 index 0000000..0874fe0 --- /dev/null +++ b/tests/test_seed_tenant_engine_audit_core_sender.py @@ -0,0 +1,103 @@ +"""Seed script for the tenant-engine sender token (MASON-WP-0007 row 7). + +Uses a fake OpenBao and obviously fake token strings. No real credential. +""" +import importlib.util +import json +import sys +from pathlib import Path + +import pytest + +SCRIPT = Path(__file__).parents[1] / "scripts" / "seed-tenant-engine-audit-core-sender.py" +SPEC = importlib.util.spec_from_file_location("seed_tenant_engine_sender", SCRIPT) +SEED = importlib.util.module_from_spec(SPEC) +sys.modules[SPEC.name] = SEED +SPEC.loader.exec_module(SEED) + +FAKE_OLD = "fake-token-old-not-a-secret" +FAKE_NEW = "fake-token-new-not-a-secret" + + +class FakeBao: + """Minimal KV v2 behaviour: cas enforcement, versions, NotFound.""" + + def __init__(self, existing=None): + self.version = 0 + self.value = None + self.writes = [] + if existing is not None: + self.version, self.value = 1, existing + + def __call__(self, args, *, payload=None): + path = args[-2] if args[0] == "write" else args[-1] + assert path == SEED.DEST_DATA + if args[0] == "read": + if self.version == 0: + raise SEED.NotFound() + return {"data": {"metadata": {"version": self.version}, "data": {"token": self.value}}} + assert args[0] == "write" + assert payload["options"]["cas"] == self.version, "cas must match the version read" + self.writes.append(payload) + self.version += 1 + self.value = payload["data"]["token"] + return {} + + +def test_pick_token_takes_newest_and_rejects_missing(): + rows = [ + {"name": "other", "tokens": ["x"]}, + {"name": "tenant-engine", "tokens": [FAKE_OLD, FAKE_NEW]}, + ] + assert SEED.pick_token(rows) == FAKE_NEW + with pytest.raises(ValueError, match="sender_not_in_registry"): + SEED.pick_token([{"name": "other", "tokens": ["x"]}]) + with pytest.raises(ValueError, match="sender_has_no_tokens"): + SEED.pick_token([{"name": "tenant-engine", "tokens": []}]) + + +def test_creates_with_cas_zero(): + fake = FakeBao() + result = SEED.seed(FAKE_NEW, call=fake) + assert result == {"action": "created", "version": 1} + assert fake.writes[0]["options"]["cas"] == 0 + assert fake.writes[0]["data"] == {"token": FAKE_NEW} + + +def test_rerun_with_same_value_does_not_write(): + fake = FakeBao(existing=FAKE_NEW) + assert SEED.seed(FAKE_NEW, call=fake) == {"action": "already_seeded", "version": 1} + assert fake.writes == [] + + +def test_rotation_reseeds_against_current_version(): + fake = FakeBao(existing=FAKE_OLD) + assert SEED.seed(FAKE_NEW, call=fake) == {"action": "reseeded", "version": 2} + assert fake.value == FAKE_NEW + + +def test_readback_mismatch_fails(): + class Lossy(FakeBao): + def __call__(self, args, *, payload=None): + out = super().__call__(args, payload=payload) + if args[0] == "read" and self.writes: + out["data"]["data"]["token"] = "something-else" + return out + + with pytest.raises(RuntimeError, match="readback_mismatch"): + SEED.seed(FAKE_NEW, call=Lossy()) + + +def test_evidence_never_contains_a_token(tmp_path, monkeypatch): + monkeypatch.setattr(SEED, "EVIDENCE_DIR", tmp_path) + SEED._evidence("done", {"destination_version": 1, "action": "created", "field": "token"}) + SEED._fail("seed_failed", {"error_class": "RuntimeError"}) + text = "".join(p.read_text() for p in tmp_path.iterdir()) + assert FAKE_NEW not in text and FAKE_OLD not in text + assert json.loads(next(tmp_path.iterdir()).read_text())["credential_values_emitted"] is False + + +def test_source_never_prints_or_passes_the_token_on_a_command_line(): + src = SCRIPT.read_text() + assert "print(" not in src + assert "sys.stdout" not in src and "sys.stderr" not in src