From eb24e04b71a30449ffc3c21de5728a96760d719a Mon Sep 17 00:00:00 2001 From: tegwick Date: Sun, 28 Jun 2026 01:00:12 +0200 Subject: [PATCH] Correct whynot credential tenant path --- ...R-2026-0001-whynot-design-npm-publish.yaml | 22 +++++++++++------- docs/argocd-gitops.md | 8 +++++-- docs/credential-change-approval.md | 2 +- docs/openbao.md | 2 +- docs/workload-kv-access-lanes.md | 20 ++++++++-------- ...load-kv-read-whynot-design-npm-publish.hcl | 4 ++-- scripts/openbao-apply-workload-kv-lanes.sh | 4 ++-- tests/test_credential_change.py | 22 ++++++++++-------- ...ILIANCE-WP-0004-argocd-gitops-bootstrap.md | 8 +++++-- ...LIANCE-WP-0006-workload-kv-access-lanes.md | 23 ++++++++++--------- 10 files changed, 67 insertions(+), 48 deletions(-) diff --git a/credential-change-requests/CCR-2026-0001-whynot-design-npm-publish.yaml b/credential-change-requests/CCR-2026-0001-whynot-design-npm-publish.yaml index b1b476a..015cb10 100644 --- a/credential-change-requests/CCR-2026-0001-whynot-design-npm-publish.yaml +++ b/credential-change-requests/CCR-2026-0001-whynot-design-npm-publish.yaml @@ -3,9 +3,9 @@ kind: credential-change-request schema_version: 1 request_type: workload-kv-read title: whynot-design npm publish token lane -status: approved +status: proposed created: '2026-06-27' -updated: '2026-06-27' +updated: '2026-06-28' requester: agent: ops-warden message_id: fe5b1696-8956-4bd5-9d6f-dbde1901a076 @@ -26,15 +26,22 @@ review: decision: approved comment: 'State Hub decision 250669d0-8475-4527-9624-cd072249f9a9: APPROVE: scoped path and confirmed binding are acceptable' + - at: '2026-06-27T22:54:20+00:00' + reviewer: bernd.worsch + decision: scope_corrected_requires_review + comment: Corrected tenant from whynot-design to coulomb per operator clarification. + The previous approval covered platform/workloads/whynot-design/whynot-design/npm-publish + and must not be reused for the corrected platform/workloads/coulomb/whynot-design/npm-publish + scope. target: domain: financials - tenant: whynot-design + tenant: coulomb workload: whynot-design environment: production purpose: npm package publishing through ops-warden caller-scoped fetch/exec openbao: mount: platform - kv_path: platform/workloads/whynot-design/whynot-design/npm-publish + kv_path: platform/workloads/coulomb/whynot-design/npm-publish fields: - NPM_AUTH_TOKEN policy_name: workload-kv-read-whynot-design-npm-publish @@ -90,7 +97,6 @@ state_hub: related_workplan_id: RAILIANCE-WP-0006 ops_warden_reply_message_id: b175c561-7858-43f5-a309-949b0dede1b4 ops_warden_batch_message_id: fe5b1696-8956-4bd5-9d6f-dbde1901a076 - decision_id: 250669d0-8475-4527-9624-cd072249f9a9 - decision_api_url: http://127.0.0.1:8000/decisions/250669d0-8475-4527-9624-cd072249f9a9 - decision_dashboard_url: http://127.0.0.1:3000/decisions - decision_resolved_at: '2026-06-27T22:04:32.956077Z' + superseded_decision_id: 250669d0-8475-4527-9624-cd072249f9a9 + superseded_decision_resolved_at: '2026-06-27T22:04:32.956077Z' + superseded_decision_reason: tenant/workload scope corrected before secret provisioning diff --git a/docs/argocd-gitops.md b/docs/argocd-gitops.md index 88d6a9b..8e7f80c 100644 --- a/docs/argocd-gitops.md +++ b/docs/argocd-gitops.md @@ -137,12 +137,16 @@ Default pattern: workload namespace. 3. Reference that Kubernetes Secret from the Deployment, Job, or CronJob. -Path convention: +Path convention for workload credential custody: ```text -platform/workloads/// +platform/workloads/// ``` +Kubernetes namespace and service-account bounds belong in the OpenBao auth role +or External Secrets binding, not in the tenant segment unless the namespace is +itself the approved workload identity. + Use CSI-mounted files only for workloads that need file references, sharper mount boundaries, or refresh behavior that should not rewrite application manifests. Do not use the OpenBao injector in the current deployment. diff --git a/docs/credential-change-approval.md b/docs/credential-change-approval.md index dd534a7..342c072 100644 --- a/docs/credential-change-approval.md +++ b/docs/credential-change-approval.md @@ -103,7 +103,7 @@ A reviewer should see a concise rendered proposal: Request: whynot-design npm publish token lane Type: workload-kv-read Mount/path/field: - platform/workloads/whynot-design/whynot-design/npm-publish + platform/workloads/coulomb/whynot-design/npm-publish NPM_AUTH_TOKEN Policy: workload-kv-read-whynot-design-npm-publish diff --git a/docs/openbao.md b/docs/openbao.md index 27d53ce..b2e5ad1 100644 --- a/docs/openbao.md +++ b/docs/openbao.md @@ -395,7 +395,7 @@ tenant contract): Path convention: ```text -platform/workloads/// +platform/workloads/// platform/object-storage/ platform/databases/ platform/operators/ diff --git a/docs/workload-kv-access-lanes.md b/docs/workload-kv-access-lanes.md index 165ebb5..a60f13e 100644 --- a/docs/workload-kv-access-lanes.md +++ b/docs/workload-kv-access-lanes.md @@ -25,8 +25,10 @@ Ops-warden batch follow-up: | Item | Value | | --- | --- | | ops-warden catalog id | `whynot-design-npm-publish` | +| Tenant/org | `coulomb` | +| Workload/project | `whynot-design` | | KV mount | `platform` | -| OpenBao CLI path | `platform/workloads/whynot-design/whynot-design/npm-publish` | +| OpenBao CLI path | `platform/workloads/coulomb/whynot-design/npm-publish` | | Secret field | `NPM_AUTH_TOKEN` | | Front-door readiness | `template`, `resolvable=false` until CCR verification | | Read policy | `workload-kv-read-whynot-design-npm-publish` | @@ -45,7 +47,7 @@ bao login -method=oidc -path=netkingdom role=whynot-design-workload-kv-read Expected OpenBao fetch shape: ```bash -bao kv get -field=NPM_AUTH_TOKEN platform/workloads/whynot-design/whynot-design/npm-publish +bao kv get -field=NPM_AUTH_TOKEN platform/workloads/coulomb/whynot-design/npm-publish ``` Expected ops-warden exec shape after activation: @@ -63,8 +65,8 @@ logging it. The source policy grants only: ```text -read platform/data/workloads/whynot-design/whynot-design/npm-publish -read platform/metadata/workloads/whynot-design/whynot-design/npm-publish +read platform/data/workloads/coulomb/whynot-design/npm-publish +read platform/metadata/workloads/coulomb/whynot-design/npm-publish ``` It does not grant write, delete, patch, sudo, auth, sibling workload, or parent @@ -121,7 +123,7 @@ and service account. An approved operator must create or confirm the secret with: ```text -path: platform/workloads/whynot-design/whynot-design/npm-publish +path: platform/workloads/coulomb/whynot-design/npm-publish field: NPM_AUTH_TOKEN ``` @@ -129,14 +131,14 @@ In the OpenBao UI, open the `platform` KV engine and create or edit the secret at: ```text -workloads/whynot-design/whynot-design/npm-publish +workloads/coulomb/whynot-design/npm-publish ``` For policies and API checks, the same KV-v2 secret is addressed as: ```text -platform/data/workloads/whynot-design/whynot-design/npm-publish -platform/metadata/workloads/whynot-design/whynot-design/npm-publish +platform/data/workloads/coulomb/whynot-design/npm-publish +platform/metadata/workloads/coulomb/whynot-design/npm-publish ``` The OpenBao UI path does not include the `data/` or `metadata/` segment. Those @@ -169,7 +171,7 @@ Send ops-warden only these pointers: ```text catalog id: whynot-design-npm-publish mount: platform -path: platform/workloads/whynot-design/whynot-design/npm-publish +path: platform/workloads/coulomb/whynot-design/npm-publish field: NPM_AUTH_TOKEN oidc login: bao login -method=oidc -path=netkingdom role=whynot-design-workload-kv-read policy: workload-kv-read-whynot-design-npm-publish diff --git a/openbao/policies/workload-kv-read-whynot-design-npm-publish.hcl b/openbao/policies/workload-kv-read-whynot-design-npm-publish.hcl index 5a84376..dc0aa98 100644 --- a/openbao/policies/workload-kv-read-whynot-design-npm-publish.hcl +++ b/openbao/policies/workload-kv-read-whynot-design-npm-publish.hcl @@ -4,10 +4,10 @@ # path used by ops-warden's caller-scoped access lane. It does not grant list # access to sibling workloads or mutation capabilities. -path "platform/data/workloads/whynot-design/whynot-design/npm-publish" { +path "platform/data/workloads/coulomb/whynot-design/npm-publish" { capabilities = ["read"] } -path "platform/metadata/workloads/whynot-design/whynot-design/npm-publish" { +path "platform/metadata/workloads/coulomb/whynot-design/npm-publish" { capabilities = ["read"] } diff --git a/scripts/openbao-apply-workload-kv-lanes.sh b/scripts/openbao-apply-workload-kv-lanes.sh index 191f988..b3bed52 100755 --- a/scripts/openbao-apply-workload-kv-lanes.sh +++ b/scripts/openbao-apply-workload-kv-lanes.sh @@ -19,7 +19,7 @@ Applies source-owned OpenBao workload KV read-lane policies. Current lane: - policy: workload-kv-read-whynot-design-npm-publish - - path: platform/workloads/whynot-design/whynot-design/npm-publish + - path: platform/workloads/coulomb/whynot-design/npm-publish - field: NPM_AUTH_TOKEN The script reads an OpenBao operator token from OPENBAO_TOKEN_FILE or an @@ -131,7 +131,7 @@ Remaining live steps: 1. Confirm the whynot-design KeyCape/NetKingdom bound claim or service account. 2. Create auth/netkingdom/role/whynot-design-workload-kv-read with only the workload-kv-read-whynot-design-npm-publish policy. - 3. Provision platform/workloads/whynot-design/whynot-design/npm-publish with + 3. Provision platform/workloads/coulomb/whynot-design/npm-publish with field NPM_AUTH_TOKEN through approved OpenBao/operator custody. 4. Run positive and negative fetch verification without printing the token. NEXT diff --git a/tests/test_credential_change.py b/tests/test_credential_change.py index 28de451..383b403 100644 --- a/tests/test_credential_change.py +++ b/tests/test_credential_change.py @@ -50,7 +50,7 @@ class CredentialChangeTests(unittest.TestCase): ccr, _errors, warnings = credential_change.validate_ccr(self.sample) rendered = credential_change.render_summary(ccr, warnings) self.assertIn("whynot-design npm publish token lane", rendered) - self.assertIn("platform/workloads/whynot-design/whynot-design/npm-publish", rendered) + self.assertIn("platform/workloads/coulomb/whynot-design/npm-publish", rendered) self.assertIn("whynot-design-npm-publish", rendered) self.assertIn("readiness: template resolvable=False", rendered) self.assertIn("approve | deny | needs_changes", rendered) @@ -58,18 +58,15 @@ class CredentialChangeTests(unittest.TestCase): def test_status_payload_marks_template_not_resolvable(self) -> None: ccr, _errors, warnings = credential_change.validate_ccr(self.sample) payload = credential_change.status_payload(ccr, warnings) - self.assertTrue(payload["apply_allowed"]) + self.assertFalse(payload["apply_allowed"]) self.assertFalse(payload["frontdoor_resolvable"]) self.assertEqual(payload["access_frontdoor"]["readiness"], "template") self.assertEqual(payload["access_frontdoor"]["catalog_id"], "whynot-design-npm-publish") - self.assertEqual(payload["apply_blockers"], []) + self.assertEqual(payload["apply_blockers"], ["apply requires status approved, got proposed"]) self.assertEqual(payload["warnings"], []) - self.assertEqual( - payload["state_hub"]["decision_id"], - "250669d0-8475-4527-9624-cd072249f9a9", - ) + self.assertIsNone(payload["state_hub"]["decision_id"]) self.assertIn( - "front door requires CCR status active, got approved", + "front door requires CCR status active, got proposed", payload["frontdoor_blockers"], ) self.assertIn("front door is marked resolvable=false", payload["frontdoor_blockers"]) @@ -94,6 +91,11 @@ class CredentialChangeTests(unittest.TestCase): with tempfile.TemporaryDirectory() as tmp: copied = Path(tmp) / self.sample.name shutil.copy2(self.sample, copied) + copied_ccr = credential_change.load_yaml(copied) + copied_ccr.setdefault("state_hub", {})[ + "decision_id" + ] = "250669d0-8475-4527-9624-cd072249f9a9" + credential_change.dump_yaml(copied, copied_ccr) original = credential_change.state_hub_decision_status try: credential_change.state_hub_decision_status = lambda _ccr, _url: { @@ -144,7 +146,7 @@ class CredentialChangeTests(unittest.TestCase): rendered, ) self.assertIn( - "# bao kv put platform/workloads/whynot-design/whynot-design/npm-publish", + "# bao kv put platform/workloads/coulomb/whynot-design/npm-publish", rendered, ) self.assertIn("NPM_AUTH_TOKEN=", rendered) @@ -199,7 +201,7 @@ class CredentialChangeTests(unittest.TestCase): def test_generated_policy_is_narrow(self) -> None: ccr, _errors, _warnings = credential_change.validate_ccr(self.sample) policy = credential_change.generated_policy_hcl(ccr) - self.assertIn('path "platform/data/workloads/whynot-design/whynot-design/npm-publish"', policy) + self.assertIn('path "platform/data/workloads/coulomb/whynot-design/npm-publish"', policy) self.assertNotIn("*", policy) self.assertNotIn("delete", policy) diff --git a/workplans/RAILIANCE-WP-0004-argocd-gitops-bootstrap.md b/workplans/RAILIANCE-WP-0004-argocd-gitops-bootstrap.md index f85f405..8f03c89 100644 --- a/workplans/RAILIANCE-WP-0004-argocd-gitops-bootstrap.md +++ b/workplans/RAILIANCE-WP-0004-argocd-gitops-bootstrap.md @@ -123,12 +123,16 @@ Kubernetes workloads, use External Secrets Operator to materialize OpenBao values as Kubernetes Secrets. Do not use the OpenBao injector in the current deployment. -Runtime path convention: +Runtime path convention for workload credential custody: ```text -platform/workloads/// +platform/workloads/// ``` +Kubernetes namespace and service-account bounds belong in the auth role or +External Secrets binding unless the namespace is itself the approved workload +identity. + ArgoCD repository credentials are operator credentials, not workload secrets, and should live under: diff --git a/workplans/RAILIANCE-WP-0006-workload-kv-access-lanes.md b/workplans/RAILIANCE-WP-0006-workload-kv-access-lanes.md index c6fa682..7c5e1ad 100644 --- a/workplans/RAILIANCE-WP-0006-workload-kv-access-lanes.md +++ b/workplans/RAILIANCE-WP-0006-workload-kv-access-lanes.md @@ -52,11 +52,10 @@ whynot-design. ## Proposed Contract -Use the existing workload convention documented in `docs/openbao.md` and -`docs/argocd-gitops.md`: +Use the workload credential convention documented in `docs/openbao.md`: ```text -platform/workloads/// +platform/workloads/// ``` For this lane, the proposed non-secret contract is: @@ -64,9 +63,11 @@ For this lane, the proposed non-secret contract is: | Item | Proposed value | | --- | --- | | KV mount | `platform` | -| CLI path | `platform/workloads/whynot-design/whynot-design/npm-publish` | -| KV-v2 policy data path | `platform/data/workloads/whynot-design/whynot-design/npm-publish` | -| KV-v2 policy metadata path | `platform/metadata/workloads/whynot-design/whynot-design/npm-publish` | +| Tenant/org | `coulomb` | +| Workload/project | `whynot-design` | +| CLI path | `platform/workloads/coulomb/whynot-design/npm-publish` | +| KV-v2 policy data path | `platform/data/workloads/coulomb/whynot-design/npm-publish` | +| KV-v2 policy metadata path | `platform/metadata/workloads/coulomb/whynot-design/npm-publish` | | Secret field | `NPM_AUTH_TOKEN` | | OpenBao read policy | `workload-kv-read-whynot-design-npm-publish` | | OIDC auth mount | `netkingdom` unless KeyCape compatibility requires `keycape` | @@ -78,7 +79,7 @@ The expected caller-facing read shape is: ```bash bao login -method=oidc -path=netkingdom role=whynot-design-workload-kv-read -bao kv get -field=NPM_AUTH_TOKEN platform/workloads/whynot-design/whynot-design/npm-publish +bao kv get -field=NPM_AUTH_TOKEN platform/workloads/coulomb/whynot-design/npm-publish ``` The command shape is illustrative only. Verification must avoid printing the @@ -109,7 +110,7 @@ Acceptance: ops-warden signing smoke. **2026-06-27:** Reviewed the unread ops-warden request and existing -`platform/workloads///` convention. +`platform/workloads///` convention. Captured the proposed `whynot-design` npm publish lane above with no secret values. @@ -129,7 +130,7 @@ the selected `npm-publish` path. Acceptance: - A policy file under `openbao/policies/` defines read access to the exact - `platform/data/workloads/whynot-design/whynot-design/npm-publish` path. + `platform/data/workloads/coulomb/whynot-design/npm-publish` path. - Metadata/list capabilities are only as broad as needed for the caller and ops-warden fetch UX. - The policy grants no write, delete, patch, sudo, auth, or unrelated workload @@ -140,7 +141,7 @@ Acceptance: **2026-06-27:** Added the concrete policy artifact at `openbao/policies/workload-kv-read-whynot-design-npm-publish.hcl`. It grants only `read` on the exact KV-v2 data and metadata paths for -`platform/workloads/whynot-design/whynot-design/npm-publish`; it does not grant +`platform/workloads/coulomb/whynot-design/npm-publish`; it does not grant write/delete/list/sudo/auth or sibling workload access. Added `scripts/openbao-apply-workload-kv-lanes.sh`, `make openbao-workload-kv-lanes-dry-run`, and @@ -195,7 +196,7 @@ publish token. Acceptance: - The path exists at - `platform/workloads/whynot-design/whynot-design/npm-publish`. + `platform/workloads/coulomb/whynot-design/npm-publish`. - The field is named exactly `NPM_AUTH_TOKEN`. - The token value is entered through an approved operator/OpenBao path and is never written to Git, State Hub, chat, prompts, shell history, or workplan