Add platform-supplier-credentials pointer and review the OIDC RP follow-through
All checks were successful
CI Smoke / host-smoke (push) Successful in 1s
CI Smoke / container-smoke (push) Successful in 2s

WARDEN-WP-0043-T01: pointer lane and playbook for
platform/suppliers/<supplier>/<account>. 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 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 327285@bnt-lap001
Assistant-Session: d6532cd1-7cd5-417b-baac-b841c9103a6b
This commit is contained in:
tegwick 2026-10-08 23:25:22 +02:00
parent 139f77dd55
commit d470f2e5a7
8 changed files with 174 additions and 12 deletions

View file

@ -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: <catalog-id>` 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.

View file

@ -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"

View file

@ -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

View file

@ -1453,3 +1453,40 @@ entries:
exec_command: "ops-mason/scripts/plan-survey.py --kv-path <PATH> --policy <POLICY> --approle <ROLE> --consumer <REPO> --keyword <WORD>"
exec_capable: false
lane: ceremony
- id: platform-supplier-credentials
title: Supplier account credentials under platform/suppliers/<supplier>/<account>
# Pointer only (WARDEN-WP-0043-T01). Founder decision 2026-10-08: credentials
# we hold with a supplier live on the platform mount under
# suppliers/<supplier>/<account>. 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/<supplier>/<account>"
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/<supplier>/<account> (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."

View file

@ -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",

View file

@ -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/<supplier>/<account>` 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/<supplier>/<account>` (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 <path>`, `bao token capabilities <path>`.
## 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.

View file

@ -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

View file

@ -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