From d470f2e5a7443db04119eb4d369687abb922e777 Mon Sep 17 00:00:00 2001 From: tegwick Date: Thu, 8 Oct 2026 23:25:22 +0200 Subject: [PATCH] Add platform-supplier-credentials pointer and review the OIDC RP follow-through WARDEN-WP-0043-T01: pointer lane and playbook for platform/suppliers//. railiance-platform owns the write; warden_executes false. WARDEN-WP-0044-T01: review of net-kingdom's oidc-browser-relying-party 1.0 follow_through section. Requires a per-step lane, a narrow warden_executes, and value_handling. Co-Authored-By: Claude Opus 5.5 Assistant: claude-code Assistant-Model: opus Assistant-Process: 327285@bnt-lap001 Assistant-Session: d6532cd1-7cd5-417b-baac-b841c9103a6b --- ...026-10-08-oidc-rp-follow-through-review.md | 75 +++++++++++++++++++ pep-stance.yaml | 6 +- registry/generated/high-risk-data-paths.yaml | 9 ++- registry/routing/catalog.yaml | 37 +++++++++ tests/test_workload_join.py | 6 +- .../platform-supplier-credentials.md | 37 +++++++++ ...N-WP-0043-platform-supplier-credentials.md | 5 +- ...EN-WP-0044-security-case-follow-through.md | 11 ++- 8 files changed, 174 insertions(+), 12 deletions(-) create mode 100644 history/2026-10-08-oidc-rp-follow-through-review.md create mode 100644 wiki/playbooks/platform-supplier-credentials.md diff --git a/history/2026-10-08-oidc-rp-follow-through-review.md b/history/2026-10-08-oidc-rp-follow-through-review.md new file mode 100644 index 0000000..e27218e --- /dev/null +++ b/history/2026-10-08-oidc-rp-follow-through-review.md @@ -0,0 +1,75 @@ +# Review — `oidc-browser-relying-party` 1.0 `follow_through` (WARDEN-WP-0044-T01) + +Reviewed: net-kingdom `security-case-types/oidc-browser-relying-party/type.yaml` +(1.0, proposed), against canon `security-case-types_v0.1` and ops-warden +`ADR-0001`, `ADR-0002`, `ADR-0004`, `ADR-0005`, `ADR-0008`, and `ADR-0010`. +Reviewer: ops-warden, 2026-10-08. Feeds net-kingdom `NK-WP-0048-T05`. + +**Verdict:** the step order, the attended grouping, and the rollback are sound. +Before ops-warden can plan a run of this type (`WARDEN-WP-0044-T02`), the schema +needs the changes R1–R3. R4–R7 are refinements. + +## Required + +**R1 — Bind each step to a catalog lane.** Add a `lane: ` field to +every follow-through step. `warden case plan` resolves each step through the +catalog, and it refuses a step whose lane is missing or not reviewed. That is +the T02 acceptance test. Today the steps name their auth only in prose, so +ops-warden cannot tell which lane applies. Two lanes do not exist yet: + +| Step | Lane needed | Owner | +| --- | --- | --- | +| `ensure-groups` | identity-provisioner caller credential for `POST /v1/groups/ensure` / `revoke` | net-kingdom | +| `mint-secret` | broker grant that **generates a value in custody** at `{secret_ref}` | railiance-platform | + +`key-cape-oidc-login` already covers the attended operator session. + +**R2 — Define `warden_executes: true` narrowly.** It should mean: ops-warden +invokes the owner's declared entry point, through the existing caller proxy and +under the caller's identity. It must not mean that ops-warden holds a credential. +For `ensure-groups`, the "identity-provisioner service token … via the ops-warden +proxy" has to be a token the caller obtains through its own lane. If ops-warden +held a standing service token, it would be a broker, which `ADR-0002` rules out. +Please write this definition into the canon field description, not only into +this type. + +**R3 — Generated secrets must never pass through the run.** `mint-secret` has to +generate the value inside the broker and custody. It must never be generated in +the run and written to custody afterwards. Add `value_handling: none | +generated_in_custody` per step, and refuse any step that would put a value on +ops-warden's stdout, argv, or receipt (`ADR-0004`, `ADR-0008`). +`custody-allowed-to-consumer` remains a capabilities-only check. That is correct. + +## Refinements + +**R4 — Per-case catalog entries.** The `catalog-entry` materialization item +(`oidc-client-{client_id}`) creates one route per case. `WARDEN-WP-0044-T04` and +`ADR-0001` point the other way: the type contributes one reviewed, parameterized +entry, and each case adds values, not routes. Proposal: drop `catalog-entry` +from `materialization`, and let the type declare a single `oidc-client` lane +whose path template uses `{client_id}`. The existing hand-built +`rapp-qonto-keycape-client` entry would later become a case of the type. + +**R5 — Make ESO delivery a gate.** `bind-env` needs the delivered Secret to +exist, and KeyCape fails closed at start when it does not. Add a per-step +`ready_check` (or a verification item) after `deliver-secret`: ExternalSecret +`sso/keycape-{client_id}-client` reports `Ready=True`. This checks status only +and never reads the value. Without it, the attended session can start before +the Secret has been delivered. + +**R6 — Membership comes only from the accepted record.** In `ensure-groups`, the +case controls the members (`initial_members`), and it runs unattended. The run +must pass exactly the memberships in the accepted case record and refuse +anything else. Group membership is an access grant. + +**R7 — Missing verifications.** Add `mfa-pinned` (when `mfa_required == "true"`, +the KeyCape registration carries the MFA pin, checked through the non-secret +client view) and `secret-delivered` (R5). Every item keeps +`reads_secret_value: false`. + +## Not in scope for ops-warden + +`bind-env` and `register-client` are attended, and their owner is net-kingdom. +The justification is accepted: the live registry is a Kubernetes Secret. +ops-warden groups those steps into one `key-cape-oidc-login` session and does +not execute them. `hand-over` belongs to the consumer. diff --git a/pep-stance.yaml b/pep-stance.yaml index fec6920..6d43550 100644 --- a/pep-stance.yaml +++ b/pep-stance.yaml @@ -95,8 +95,10 @@ on_apply: # re-measure, and this one was stale by eight lanes before the test caught it. # 2026-10-05: inbound-library-imap-approle added as an applicable lane without a # published workload declaration, so routing unknown rose 20 -> 21; resolved unchanged. +# 2026-10-08: platform-supplier-credentials added as a not-applicable path pattern, +# so routing not_applicable rose 16 -> 17. classification_coverage: - measured: "2026-10-05" + measured: "2026-10-08" attribution: self-measured signing_targets: resolved: 0 @@ -105,7 +107,7 @@ classification_coverage: routing_lanes: resolved: 4 unknown: 21 - not_applicable: 16 + not_applicable: 17 verdict_caching: none input_claim_caching: "registry/flex-auth/production_registry_snapshot.json, rebuilt by scripts/build_flex_auth_registry.py" diff --git a/registry/generated/high-risk-data-paths.yaml b/registry/generated/high-risk-data-paths.yaml index 64fe13d..afabb89 100644 --- a/registry/generated/high-risk-data-paths.yaml +++ b/registry/generated/high-risk-data-paths.yaml @@ -10,12 +10,12 @@ # declares it, and is null where the field set has not been established -- # null means unknown, never 'one field'. -generated_at: "2026-10-07T22:52:56Z" +generated_at: "2026-10-08T21:24:31Z" source: ops-warden/registry/routing/catalog.yaml -catalog_revision: "9084581461545e07d26fc29783c9fd79446e54c6" -catalog_revision_date: "2026-10-05T16:12:48+02:00" +catalog_revision: "139f77dd557ca4139cd4a8ec4d0ef90e7ca607ee" +catalog_revision_date: "2026-10-08T23:21:46+02:00" catalog_dirty: true -high_risk_lane_count: 32 +high_risk_lane_count: 33 concrete_path_count: 17 # Graded high but not a single KV address -- a routing pattern, a broker @@ -33,6 +33,7 @@ no_concrete_path: - openbao-shamir-recovery-ceremony - openbao-structure-survey - ops-warden-warden-sign-token + - platform-supplier-credentials - secrets-engine-approval-client-login - secrets-engine-requester-login - state-hub-forge-derivation-read diff --git a/registry/routing/catalog.yaml b/registry/routing/catalog.yaml index a851b60..709005a 100644 --- a/registry/routing/catalog.yaml +++ b/registry/routing/catalog.yaml @@ -1453,3 +1453,40 @@ entries: exec_command: "ops-mason/scripts/plan-survey.py --kv-path --policy --approle --consumer --keyword " exec_capable: false lane: ceremony + + - id: platform-supplier-credentials + title: Supplier account credentials under platform/suppliers// + # Pointer only (WARDEN-WP-0043-T01). Founder decision 2026-10-08: credentials + # we hold with a supplier live on the platform mount under + # suppliers//. railiance-platform owns the KV write and the + # credential-change allowlist; ops-warden routes the shape and holds nothing. + # Workload and rein paths with a live reader stay where they are until each + # reader is retargeted (see the playbook table). + risk: high + workload_ref: + applicability: not-applicable + reason: "Generic supplier-account path pattern; concrete consumers keep their own workload lanes." + need_keywords: [supplier, suppliers, supplier account, supplier credential, vendor, vendor account, platform/suppliers, account credential] + owner_repo: railiance-platform + subsystem: OpenBao platform mount (suppliers/) + warden_executes: false + wiki_ref: wiki/playbooks/platform-supplier-credentials.md#worker-checklist + canon_ref: railiance-platform/scripts/credential-change.py + reviewed: "2026-10-08" + status: active + delegation: + mode: native + intended_owner: railiance-platform + reviewed: "2026-10-08" + verified: source-read + auth_method: "railiance-platform delegated credential change (CCR); founder-attended provision from a mode-0600 file" + path_template: "platform/suppliers//" + exec_capable: false + rotation: + method: rotate + owner: railiance-platform + automatable: false + steps: + - "Re-mint the credential at the supplier, written to a mode-0600 file and never printed." + - "Write it through a railiance-platform delegated credential change to platform/suppliers// (value from the file, never on argv); revoke the predecessor at the supplier." + - "Verify with capabilities and metadata only (`bao token capabilities`, `bao kv metadata get`), then tell readers to re-fetch." diff --git a/tests/test_workload_join.py b/tests/test_workload_join.py index 40e97ae..24cd918 100644 --- a/tests/test_workload_join.py +++ b/tests/test_workload_join.py @@ -21,8 +21,10 @@ def test_repo_catalog_uses_only_explicit_workload_references(): # and pep-stance.yaml's classification_coverage measure the same populations # and must be updated together. 16 since 2026-10-05: the # openbao-structure-survey pointer (WARDEN-WP-0042) is a builder survey - # under a founder-minted grant, not a workload credential. - assert len(report["not_applicable"]) == 16 + # under a founder-minted grant, not a workload credential. 17 since + # 2026-10-08: platform-supplier-credentials (WARDEN-WP-0043) is a generic + # supplier-account path pattern; concrete consumers keep their own lanes. + assert len(report["not_applicable"]) == 17 assert {row["lane"] for row in report["resolved"]} == { "ops-warden-warden-sign-token", "issue-core-ingestion-api-key", diff --git a/wiki/playbooks/platform-supplier-credentials.md b/wiki/playbooks/platform-supplier-credentials.md new file mode 100644 index 0000000..3a09567 --- /dev/null +++ b/wiki/playbooks/platform-supplier-credentials.md @@ -0,0 +1,37 @@ +# Supplier account credentials (`platform/suppliers/`) + +Catalog: `platform-supplier-credentials` (status `active`, pointer only) +Owner: `railiance-platform`, which owns the KV write and the `credential-change.py` allowlist +Routing record: `WARDEN-WP-0043` + +Founder decision, 2026-10-08: credentials we hold with a supplier live at +`platform/suppliers//` on the existing `platform` mount. +Tenant commercial secrets stay on `tenants/`. ops-warden routes this shape. It +does not store values and does not apply OpenBao. + +## Worker checklist + +1. Pick the path `platform/suppliers//` (two levels, lowercase). +2. To create or change the path, open a delegated credential change with + railiance-platform. The `credential-change.py` allowlist does not yet + accept `platform/suppliers/` (WARDEN-WP-0043-T02). +3. The founder provisions values on a trusted terminal from a mode-0600 + file. Never put a value in git, State Hub, a workplan, a log, or chat. +4. Verify with metadata and capabilities only: + `bao kv metadata get `, `bao token capabilities `. + +## Existing accounts (not moved yet) + +A path with a live reader stays where it is until that reader is retargeted. + +| Account | Path today | Future home | +| --- | --- | --- | +| OpenRouter, llm-connect | `platform/workloads/activity-core/llm-connect/llm-connect-provider-secrets` | `platform/suppliers/openrouter/llm-connect` | +| OpenRouter, rein-openweights | `reins/rein-openweights/openrouter` | `platform/suppliers/openrouter/rein-openweights` | +| Scaleway bootstrap | `platform/workloads/railiance/scaleway/bootstrap` | `platform/suppliers/scaleway/bootstrap` | +| Scaleway backup space | `platform/workloads/railiance/backup/object-storage` | `platform/suppliers/scaleway/backup` | +| IONOS transactional mailbox | `platform/workloads/email-connect/transactional` | `platform/suppliers/ionos/email-connect-smtp` | +| IONOS library mailbox | `platform/workloads/inbound-library/imap` | `platform/suppliers/ionos/library-imap` | + +llm-connect already tries the supplier path first and falls back to the +workload path. diff --git a/workplans/WARDEN-WP-0043-platform-supplier-credentials.md b/workplans/WARDEN-WP-0043-platform-supplier-credentials.md index 914353a..e1d3d49 100644 --- a/workplans/WARDEN-WP-0043-platform-supplier-credentials.md +++ b/workplans/WARDEN-WP-0043-platform-supplier-credentials.md @@ -49,7 +49,7 @@ and then the live workload path before `OPENROUTER_API_KEY`. ```task id: WARDEN-WP-0043-T01 -status: todo +status: done priority: high state_hub_task_id: "7a86d91a-fcbd-5dc8-b3cf-4c28ad963474" ``` @@ -59,6 +59,9 @@ Add a pointer catalog entry and a short playbook for owner of the KV write is railiance-platform. No secret values, no fetch of a live field, and no new `warden` verb. +Done 2026-10-08: catalog `platform-supplier-credentials` plus +`wiki/playbooks/platform-supplier-credentials.md`. + ## Allow the path in credential changes ```task diff --git a/workplans/WARDEN-WP-0044-security-case-follow-through.md b/workplans/WARDEN-WP-0044-security-case-follow-through.md index 833ed1f..95ae058 100644 --- a/workplans/WARDEN-WP-0044-security-case-follow-through.md +++ b/workplans/WARDEN-WP-0044-security-case-follow-through.md @@ -4,7 +4,7 @@ type: workplan title: "Follow through on KnownSecurityCases along their type's route" domain: infotech repo: ops-warden -status: ready +status: active flavor: implementation owner: claude-code topic_slug: netkingdom @@ -41,9 +41,8 @@ caller's identity. ```task id: WARDEN-WP-0044-T01 -status: wait +status: progress depends_on: [NK-WP-0048-T03] -blocking_reason: "The follow-through section follows the type schema and template." priority: high state_hub_task_id: "ef0920fc-418e-52cb-acff-9a5c0c728bf5" ``` @@ -57,6 +56,12 @@ the type's justification), and the verification command. Write it for the first type, `oidc-browser-relying-party`, covering key-cape client registration, custody minting and LLDAP group membership. +2026-10-08: NK-WP-0048-T03/T04 landed with a drafted section. Review is in +`history/2026-10-08-oidc-rp-follow-through-review.md`. It requires three +changes before T02: a per-step `lane`, a narrow `warden_executes`, and +`value_handling`. It also lists four refinements. This task is done when +net-kingdom acknowledges the review (NK-WP-0048-T05). + ```task id: WARDEN-WP-0044-T02 status: wait