From 720277bd39cbc20815ce50068daf556c16af86e1 Mon Sep 17 00:00:00 2001 From: codex Date: Wed, 7 Oct 2026 09:16:58 +0200 Subject: [PATCH] Silence applier-apply under --quiet and drop the retired bao callback default The warden attended wrapper refuses any output, so --quiet now captures bao's own messages and skips the [OK] lines; a failure reports only the step and exit code. New OIDC scaffolds default to the operator-tunnel UI callback rather than bao.coulomb.social, which was retired on 2026-09-15. Existing CCRs keep their lists until amended. The CCR-2026-0033 test pins move to its approved/applied state. Co-Authored-By: Claude Opus 5.5 Assistant: claude-code Assistant-Model: opus Assistant-Process: 132360@bnt-lap001 Assistant-Session: 769b1afd-4330-4c86-96b2-e510e26db42b --- .../keycape/founder-runbook.md | 8 ++--- scripts/credential-change.py | 22 +++++++++---- tests/test_credential_change.py | 33 +++++++++++++++++-- 3 files changed, 50 insertions(+), 13 deletions(-) diff --git a/docs/provisioning-windows/2026-10-04-1000/keycape/founder-runbook.md b/docs/provisioning-windows/2026-10-04-1000/keycape/founder-runbook.md index 8e07473..89ed2ec 100644 --- a/docs/provisioning-windows/2026-10-04-1000/keycape/founder-runbook.md +++ b/docs/provisioning-windows/2026-10-04-1000/keycape/founder-runbook.md @@ -117,14 +117,14 @@ python3 scripts/credential-change.py applier-apply credential-change-requests/CC # apply (attended platform-admin; one phrase per CCR) warden access openbao-platform-admin-login --exec -- \ python3 scripts/credential-change.py applier-apply credential-change-requests/CCR-2026-0033.yaml \ - --confirm "DELEGATED APPLY CCR-2026-0033" --actor founder --record-state-hub + --confirm "DELEGATED APPLY CCR-2026-0033" --actor founder --record-state-hub --quiet warden access openbao-platform-admin-login --exec -- \ python3 scripts/credential-change.py applier-apply credential-change-requests/CCR-2026-0034.yaml \ - --confirm "DELEGATED APPLY CCR-2026-0034" --actor founder --record-state-hub + --confirm "DELEGATED APPLY CCR-2026-0034" --actor founder --record-state-hub --quiet ``` -Expected: `[OK] CCR-2026-0033.yaml delegated metadata apply recorded`, the same for -0034, and a value-free evidence entry in each CCR. +Expected: exit 0 with no output (`--quiet`, which the warden wrapper requires) and a +value-free `delegated_metadata_apply` evidence entry in each CCR. ESO: `eso.draft.yaml` in this directory carries `railiance.io/admission: pending-review`. Promote it to `approved` in a reviewed commit after A2 first. Then: diff --git a/scripts/credential-change.py b/scripts/credential-change.py index 1fe9c85..8595123 100755 --- a/scripts/credential-change.py +++ b/scripts/credential-change.py @@ -1182,7 +1182,7 @@ def render_runbook(payload: dict[str, Any]) -> str: return "\n".join(lines) -def run_bao_metadata_apply(ccr: dict[str, Any], bao_bin: str) -> None: +def run_bao_metadata_apply(ccr: dict[str, Any], bao_bin: str, quiet: bool = False) -> None: openbao = ccr["openbao"] auth = openbao["auth"] auth_path = f"auth/{auth['mount']}/role/{auth['role']}" @@ -1196,7 +1196,14 @@ def run_bao_metadata_apply(ccr: dict[str, Any], bao_bin: str) -> None: [bao_bin, "write", auth_path, f"@{role_path}"], ] for command in commands: - subprocess.run(command, cwd=REPO_DIR, check=True) + if not quiet: + subprocess.run(command, cwd=REPO_DIR, check=True) + continue + # Silent mode for attended wrappers that refuse any output. bao's own + # messages are discarded; a failure reports only the step and exit code. + result = subprocess.run(command, cwd=REPO_DIR, capture_output=True) + if result.returncode != 0: + fail(f"bao {command[1]} failed with exit code {result.returncode}") finally: try: role_path.unlink() @@ -1494,7 +1501,8 @@ def inventory_ccr_from_args(args: argparse.Namespace) -> dict[str, Any]: allowed_redirect_uris = list(getattr(args, "redirect_uri", None) or []) if args.auth_method == "oidc" and not allowed_redirect_uris: allowed_redirect_uris = [ - "https://bao.coulomb.social/ui/vault/auth/netkingdom/oidc/callback", + # bao.coulomb.social was retired on 2026-09-15; the UI goes through the operator tunnel. + "http://127.0.0.1:18200/ui/vault/auth/netkingdom/oidc/callback", "http://localhost:8250/oidc/callback", "http://127.0.0.1:8250/oidc/callback", ] @@ -2053,7 +2061,7 @@ def command_applier_apply(args: argparse.Namespace) -> int: phrase = args.confirm or input("Type delegated apply confirmation phrase: ") if phrase != expected: fail(f"confirmation phrase mismatch; expected {expected!r}") - run_bao_metadata_apply(ccr, args.bao_bin) + run_bao_metadata_apply(ccr, args.bao_bin, quiet=args.quiet) set_status = "applied" if ccr.get("status") == "approved" else None details = delegated_apply_details(ccr, args.actor) ccr = append_evidence( @@ -2064,7 +2072,8 @@ def command_applier_apply(args: argparse.Namespace) -> int: details, set_status=set_status, ) - print(f"[OK] {path.name} delegated metadata apply recorded") + if not args.quiet: + print(f"[OK] {path.name} delegated metadata apply recorded") if args.record_state_hub: event = record_evidence_state_hub( ccr, @@ -2074,7 +2083,8 @@ def command_applier_apply(args: argparse.Namespace) -> int: "passed", details, ) - print(f"[OK] State Hub progress event {event.get('id', '')}") + if not args.quiet: + print(f"[OK] State Hub progress event {event.get('id', '')}") return 0 diff --git a/tests/test_credential_change.py b/tests/test_credential_change.py index 00fe480..f111667 100644 --- a/tests/test_credential_change.py +++ b/tests/test_credential_change.py @@ -436,6 +436,32 @@ class CredentialChangeTests(unittest.TestCase): self.assertEqual(payload["bound_service_account_namespaces"], ["external-secrets"]) self.assertNotIn("bound_claims", payload) + def test_quiet_metadata_apply_captures_bao_output(self) -> None: + ccr, errors, _warnings = credential_change.validate_ccr(self.sample) + self.assertEqual(errors, []) + seen = [] + + class Done: + returncode = 0 + + def fake_run(command, **kwargs): + seen.append(kwargs.get("capture_output")) + return Done() + + original = credential_change.subprocess.run + credential_change.subprocess.run = fake_run + try: + credential_change.run_bao_metadata_apply(ccr, "bao", quiet=True) + finally: + credential_change.subprocess.run = original + self.assertEqual(seen, [True, True]) + + def test_scaffold_default_redirects_exclude_retired_public_host(self) -> None: + source = (REPO_DIR / "scripts/credential-change.py").read_text() + default = source.split('if args.auth_method == "oidc" and not allowed_redirect_uris:', 1)[1].split("]", 1)[0] + self.assertNotIn('"https://bao.coulomb.social', default) + self.assertIn("http://127.0.0.1:18200/ui/vault/auth/netkingdom/oidc/callback", default) + def test_oidc_auth_payload_includes_redirect_uris(self) -> None: ccr, errors, _warnings = credential_change.validate_ccr(self.sample) self.assertEqual(errors, []) @@ -505,12 +531,13 @@ class CredentialChangeTests(unittest.TestCase): self.assertEqual(errors, []) self.assertNotIn("consumer_copy", ccr) self.assertEqual(ccr["target"]["domain"], "infotech") - self.assertEqual(ccr["status"], "proposed") + # Approved under 2ab717e1 and applied on 2026-10-07. + self.assertIn(ccr["status"], credential_change.POST_APPLY_STATUSES) meta = ccr["openbao"]["kv_metadata"] self.assertEqual((meta["cas_required"], meta["max_versions"], meta["delete_version_after"]), (True, 2, "0s")) self.assertEqual(meta["custom_metadata"]["readers"], "eso-sso-issuer,oidc-secrets-engine-operator") - self.assertIs(ccr["openbao"]["auth"]["bound_claims_confirmed"], False) + self.assertIs(ccr["openbao"]["auth"]["bound_claims_confirmed"], True) def test_apply_plan_refuses_unapproved_ccr(self) -> None: with self.assertRaises(SystemExit): @@ -795,7 +822,7 @@ class CredentialChangeTests(unittest.TestCase): original_apply = credential_change.run_bao_metadata_apply original_post = credential_change.state_hub_post_json try: - credential_change.run_bao_metadata_apply = lambda ccr, bao_bin: calls.append((ccr["id"], bao_bin)) + credential_change.run_bao_metadata_apply = lambda ccr, bao_bin, quiet=False: calls.append((ccr["id"], bao_bin)) credential_change.state_hub_post_json = ( lambda _base_url, _path, payload: events.append(payload) or {"id": "event-1"} )