oidc-service-client 0.1 owner review; draft CCR-2026-0060/0061 (proposed)
All checks were successful
CCR intake gate / gate (push) Successful in 1m12s
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s

Review of net-kingdom's proposed SecurityCaseType oidc-service-client 0.1
(hub message 4ec57765): mint grant covers the keycape/<client_id> family but
the broker does not admit the type yet (R1); consumer role shape consistent
(R2); ESO bodies, consumer role name and rotation text need a type r2
(R3-R5, R7); per-case CCRs required (R6), drafted for nk-bind-executor-approval.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 5502@bnt-lap001
Assistant-Session: 68b3b02f-5d0f-4e71-8421-796f62e50196
This commit is contained in:
codex 2026-10-31 23:03:46 +01:00
parent 7bdf1996f0
commit 6858bef858
5 changed files with 417 additions and 0 deletions

View file

@ -0,0 +1,98 @@
id: CCR-2026-0060
kind: credential-change-request
schema_version: 1
request_type: workload-kv-read
title: 'nk-bind-executor-approval: keycape-reader-policy and eso-role (oidc-service-client@0.1)'
status: proposed
created: '2026-10-31'
updated: '2026-10-31'
requester:
agent: railiance-platform
reason: 'NK-WP-0056-T14 first case of SecurityCaseType oidc-service-client 0.1 (net-kingdom
5d5f15d, proposed; hub decision f8044f81; memo nk-wp-0056-t14-oidc-service-client-r1).
Drafted by the platform owner in its review (docs/credential-lane-designs/oidc-service-client-review.md,
R6). KeyCape reads the client secret of nk-bind-executor-approval only through ESO, as
CCR-2026-0041 for the browser type. Not approved.'
review:
required: true
required_approvers:
- platform-owner
comments: []
target:
domain: infotech
tenant: platform
workload: keycape
environment: production
purpose: KeyCape reads the nk-bind-executor-approval client secret only through ESO. No metadata,
list, parent or sibling access.
openbao:
mount: platform
kv_path: platform/workloads/keycape/nk-bind-executor-approval
fields:
- CLIENT_SECRET
policy_name: workload-kv-read-keycape-nk-bind-executor-approval-eso
policy_file: openbao/policies/workload-kv-read-keycape-nk-bind-executor-approval-eso.hcl
metadata_read: false
token_self_lifecycle: true
auth:
method: kubernetes
mount: kubernetes
role: external-secrets-keycape-nk-bind-executor-approval
audience: openbao
ttl: 15m
token_max_ttl: 15m
token_explicit_max_ttl: 15m
token_no_default_policy: true
policies:
- workload-kv-read-keycape-nk-bind-executor-approval-eso
bound_claims:
service_account_names:
- external-secrets
service_account_namespaces:
- external-secrets
bound_claims_confirmed: false
access_frontdoor:
type: external-secrets
catalog_id: bounded-executor-keycape-nk-bind-executor-approval
readiness: pending-review
resolvable: false
risk:
classification: high
notes:
- Rendered from a proposed SecurityCaseType; the type is not yet acknowledged and the case
is not yet accepted.
- Exact read on one KV path; no metadata, list, parent or sibling access.
- The value lets its holder obtain approval-engine tokens as service:nk-bind-executor
(approval:create/read/consume, tenant:platform).
- Only ExternalSecrets in namespace sso may use the store; sso namespace writers are
within this boundary (as CCR-2026-0033/0041).
verification:
positive:
- policy workload-kv-read-keycape-nk-bind-executor-approval-eso grants read on
platform/data/workloads/keycape/nk-bind-executor-approval only (capabilities check)
- ExternalSecret sso/keycape-nk-bind-executor-approval-client reports Ready=True (status
only)
negative:
- a default-only identity has no read capability on the path
- sibling GET, parent LIST and metadata read are denied
- wrong ServiceAccount and wrong audience logins are denied
activation_conditions:
- oidc-service-client 0.1 acknowledged by the founder (informed-decision memo, hub decision
f8044f81).
- SecurityCase nk-bind-executor-approval accepted by owner and founder.
- Custody entry minted at version 1 by keycape-client-secret-generate, after the broker
admits the type (review R1).
- platform-owner approval of this exact request and bound_claims_confirmed set to true.
lifecycle:
deactivate: delete ExternalSecret sso/keycape-nk-bind-executor-approval-client, then role
external-secrets-keycape-nk-bind-executor-approval, then the policy (case rollback)
rotate: no rotation lane exists yet (RPF-WP-0055 residual); until then a rotation is an
attended platform-admin write with CAS=current, an ESO refresh and a KeyCape restart
compromised: owner incident process; delete the role first; no automatic read, reseed or
cleanup
security_case:
case: nk-bind-executor-approval
type: oidc-service-client@0.1
type_status: proposed
type_source: net-kingdom security-case-types/oidc-service-client/type.yaml (5d5f15d)
acceptance_record: pending

View file

@ -0,0 +1,127 @@
id: CCR-2026-0061
kind: credential-change-request
schema_version: 1
request_type: workload-kv-read
title: 'nk-bind-executor-approval: consumer reader for sso/nk-bind-executor (oidc-service-client@0.1)'
status: proposed
created: '2026-10-31'
updated: '2026-10-31'
requester:
agent: railiance-platform
reason: 'NK-WP-0056-T14 first case of SecurityCaseType oidc-service-client 0.1 (net-kingdom
5d5f15d, proposed; hub decision f8044f81). The bounded executor service:nk-bind-executor
reads its approval-engine client secret natively per call to file, read and consume its
own approvals. Drafted by the platform owner in its review
(docs/credential-lane-designs/oidc-service-client-review.md, R2-R6). Not approved.'
review:
required: true
required_approvers:
- platform-owner
- net-kingdom-owner
comments: []
target:
domain: infotech
tenant: platform
workload: net-kingdom-bind-executor
rapp: rapp-bounded-executor
environment: production
purpose: Let only the executor ServiceAccount sso/nk-bind-executor read the
nk-bind-executor-approval CLIENT_SECRET in memory for its client_credentials exchange;
no write, metadata, list, sibling path, Secret copy or other identity
admission:
standard: net-kingdom canon/standards/bounded-execution-admission_v0.1.md (proposed)
executor_identity: service:nk-bind-executor
note: The role does not evaluate the admission. It bounds what the ServiceAccount can read
to one client secret whose tokens carry approval:create/read/consume at approval-engine.
openbao:
mount: platform
kv_path: platform/workloads/keycape/nk-bind-executor-approval
fields:
- CLIENT_SECRET
policy_name: workload-kv-read-keycape-nk-bind-executor-approval-bounded-executor
policy_file: openbao/policies/workload-kv-read-keycape-nk-bind-executor-approval-bounded-executor.hcl
metadata_read: false
token_self_lifecycle: true
auth:
method: kubernetes
mount: kubernetes
# Review R4: role name ends with -workload-kv-read so the delegated applier admits it;
# the type r1 template (role name = policy name) would be refused.
role: keycape-nk-bind-executor-approval-bounded-executor-workload-kv-read
audience: openbao
ttl: 15m
token_max_ttl: 15m
token_explicit_max_ttl: 15m
token_no_default_policy: true
policies:
- workload-kv-read-keycape-nk-bind-executor-approval-bounded-executor
bound_claims:
service_account_names:
- nk-bind-executor
service_account_namespaces:
- sso
bound_claims_confirmed: false
access_frontdoor:
type: platform-owner
catalog_id: bounded-executor-keycape-nk-bind-executor-approval
readiness: pending-review
resolvable: false
agent_fence:
data_and_metadata_denied: true
note: Coding agents are denied data and metadata of platform/workloads/keycape/nk-bind-executor-approval;
setup checks the deny before the role is created.
lifecycle:
role_window: Standing, as CCR-2026-0059. The executor exchanges a fresh token per call and
keeps neither the secret nor a token beyond the call.
deactivate: Delete the role, then the policy. The custody entry and the ESO reader stay
under CCR-2026-0060 until the case rollback.
rotate: No rotation lane exists yet (RPF-WP-0055 residual). The reader needs no change on
rotation because the path stays the same.
compromised: Remove client nk-bind-executor-approval from KeyCape (register-client rollback,
attended), delete this role, then rotate attended. Approvals already created by the
client still need a human entry under human_control.
delivery:
surface: native-workload-login
namespace: sso
service_account: nk-bind-executor
reader: Executor runtime (net-kingdom bounded-executor/, deployed by rapp-bounded-executor);
logs in with its projected ServiceAccount token, audience openbao, separately from the
CCR-2026-0058 and CCR-2026-0059 roles; the value stays in memory and never reaches a
Secret, log or receipt
risk:
classification: high
notes:
- Reachable value is one confidential client secret; its tokens act as service:nk-bind-executor
at approval-engine (create approvals naming itself, read and consume approvals of
tenant:platform). Approvals still need a human entry under human_control.
- The role is separate from the CCR-2026-0058 write role, the CCR-2026-0059 journey reader
and the CCR-2026-0060 ESO reader; policies are never combined in one role.
- No Secret copy in namespace sso beyond KeyCape's own ESO target; other workloads in sso
without the ServiceAccount cannot log in.
- Node root, cluster admins able to mint tokens for sso/nk-bind-executor and platform-admin
remain inside this credential boundary.
verification:
positive:
- Exact policy and role read back; login as sso/nk-bind-executor (audience openbao) gets
only this policy with a lease of at most 15m
- The executor reports the claims of one client_credentials exchange per scope (sub
service:nk-bind-executor, aud approval-engine, tenant:platform, lifetime <= 15m), value-free
negative:
- Write, metadata read, list, delete and sibling paths (including keycape/secrets-engine-openbao
and directory-binds) are refused
- Wrong ServiceAccount, wrong namespace and wrong audience logins are denied
- Coding-agent boundary denies data and metadata of the path
activation_conditions:
- oidc-service-client acknowledged by the founder and case nk-bind-executor-approval accepted.
- Custody entry minted (CCR-2026-0040 lane, after broker admission R1).
- Explicit platform-owner and net-kingdom-owner approval of this exact request.
- bound_claims_confirmed set to true by the platform-owner (ServiceAccount sso/nk-bind-executor
already exists, uid bd15569c, CCR-2026-0058/0059).
- Attended platform-admin (aal2) session or the delegated applier applies policy and role
from a reviewed commit; positive and negative results recorded in value-free receipts.
security_case:
case: nk-bind-executor-approval
type: oidc-service-client@0.1
type_status: proposed
type_source: net-kingdom security-case-types/oidc-service-client/type.yaml (5d5f15d)
acceptance_record: pending

View file

@ -0,0 +1,162 @@
# oidc-service-client 0.1: custody lane and consumer role (owner review, railiance-platform)
Owner review from railiance-platform for net-kingdom's proposed SecurityCaseType `oidc-service-client` 0.1
(net-kingdom `security-case-types/oidc-service-client/`, commit 5d5f15d; hub decision `f8044f81`; memo
`nk-wp-0056-t14-oidc-service-client-r1`). Request: State Hub message `4ec57765`. Per canon
security-case-types_v0.1 section 8 this review comes before the founder acknowledges the type.
Everything here is source review and proposed drafts. No OpenBao role, policy, KV entry or Secret was
created, read or changed.
## Verdict
**Shape accepted with changes.** The custody path family, the mint grant and the consumer-role shape fit
railiance-platform's contracts. Four type-side corrections are needed (R3, R4, R5, R7), which makes this a
memo r2. One platform-side change is needed before the first mint (R1). Each case needs its own CCRs (R6).
For the first case they are drafted: CCR-2026-0060 (ESO reader) and CCR-2026-0061 (consumer reader), both
`proposed`.
## Findings
### R1. Mint lane: the OpenBao grant covers the path, the broker does not admit the type yet. Verdict: platform change required
- The grant (CCR-2026-0040, policy `keycape-client-secret-generate`) is create-only on
`platform/data/workloads/keycape/+`. It denies exactly the reserved `secrets-engine-openbao`.
`nk-bind-executor-approval` is one segment, matches the type's `client_id` pattern (the same pattern as
the browser type) and is not reserved. The issuer binding (CCR-2026-0043, net-kingdom-admins + aal2)
covers it too. **At the OpenBao layer no new grant, policy or CCR is needed** for service-client paths.
- The broker entry point `scripts/keycape_client_secret_generate.py` refuses the case today, for three
reasons:
- `ACKNOWLEDGED_TYPES` admits only `oidc-browser-relying-party@1.1`. The result is
`case_type_unacknowledged`.
- It requires `parameters.client_type == confidential`, but the service type has no `client_type`
parameter, because it is confidential by construction. The result is `case_client_not_confidential`.
- It requires case status `materialized`.
- Needed: an owner-reviewed source change that adds `oidc-service-client@<acknowledged version>` to
`ACKNOWLEDGED_TYPES`, with its type-byte and `PARAMETER_PINS` fingerprint. The change must make the
confidential check type-specific: the service type is implicitly confidential. Refusal tests must cover an
unacknowledged service type, a drifted source and a client_id mismatch. It follows the RPF-WP-0060-T01
rule: no speculative pin before genuine acknowledgment. It can therefore only land after the founder
acknowledges the type. Until then, `mint-secret` for this case refuses (fail closed).
- The KeyCape secret format (48 bytes, URL-safe, confirmed by key-cape for confidential clients) applies
unchanged to `client_credentials` clients.
### R2. Consumer-reader role shape. Verdict: consistent
The role shape matches the one-role-per-lane rule and the CCR-2026-0058/0059 shape:
- Kubernetes auth;
- one ServiceAccount and one namespace;
- audience `openbao`;
- `ttl`, `token_max_ttl` and `token_explicit_max_ttl` all 15m;
- `token_no_default_policy`;
- exactly one policy.
It is a separate role from the executor's CCR-2026-0058 write role, the CCR-2026-0059 journey reader and the
ESO reader. The policies are never combined. `scripts/credential-change.py` enforces this: the validator
requires `policies == [policy_name]`, and the applier requires "only openbao.policy_name".
The `auth/token/lookup-self` (read) and `auth/token/revoke-self` (update) stanzas are **correct and
expected**. They are exactly what `token_self_lifecycle: true` generates (`generated_policy_hcl`), and they
match CCR-2026-0033's amendment, CCR-2026-0041/0042, CCR-2026-0059 and the 0058 write policy. Together they let
the reader end its own token after each call. The browser type omitted them, and the platform had to add them
itself for CCR-2026-0042. The service type gets this right.
Minor wording: verification `custody-allowed-to-consumer` says the policy grants read on the path "and
nothing else". It should say "and nothing else besides its own token's lookup-self and revoke-self". (Type
text; fold into r2.)
### R3. ESO reader (keycape-reader-policy, eso-role) lags behind the platform shape. Verdict: change the type (r2)
The type inherited the browser type's bodies:
- the policy has no self-lifecycle stanzas;
- the role has no `audience: openbao` and no `token_explicit_max_ttl`.
The applied browser instance (CCR-2026-0041) and CCR-2026-0033 carry `token_self_lifecycle: true`,
`audience: openbao` and `token_explicit_max_ttl: 15m`. A policy rendered from the type as written would fail
the applier's "policy_file does not match generated CCR policy" check. Align both bodies with the consumer
reader. The same gap exists in `oidc-browser-relying-party`; that is a separate note for its owner, not a
blocker here.
### R4. Consumer role name: the delegated applier refuses the template. Verdict: change the type (r2)
The type names the role the same as the policy: `workload-kv-read-keycape-{client_id}-{consuming_service}`.
The delegated applier (`applier_policy_violations`) admits Kubernetes roles only if they end with
`-workload-kv-read` or `-secrets-read`, or start with `external-secrets-`. As written, every case could only
be applied by an attended out-of-band tool. CCR-2026-0059's role was applied that way: its name fails the same
check, and it was applied through net-kingdom `executor-lanes.py`.
Proposed template: `keycape-{client_id}-{consuming_service}-workload-kv-read`. The policy name stays as it
is, because it satisfies the required `workload-kv-read-` prefix. The `hand-over` step, the
`consumer-login-bound` verification and the rollback text must use the role name. CCR-2026-0061 is drafted
with the proposed name, and `applier_policy_violations` returns no violations for it.
### R5. Rotation is not mint-secret. Verdict: change the type text (r2)
The README ("Rotation is mint-secret, then the consumer's next exchange") and the catalog entry ("rotation:
mint-secret then verification") are wrong for this lane. The grant is create-only (CAS 0). It refuses an
existing entry with exit 3 and can never write a second version. No rotation lane exists:
- RPF-WP-0054-T07 was cancelled as transferred;
- the follow-up is the residual RPF-WP-0055, which needs its own CAS/update authority and ESO and KeyCape
coordination.
State it plainly: until that lane exists, rotation is an attended platform-admin write with CAS=current,
followed by an ESO refresh and a KeyCape restart. The consumer needs no change, because the path stays the
same.
### R6. Each case needs its own CCRs; the type does not stand in. Verdict: per-case CCRs required (drafted for the first case)
This is the same as for browser cases. The forgejo-netkingdom-oidc case got its own CCR-2026-0041 (ESO
reader) and CCR-2026-0042 (consumer reader), rendered by ops-mason and carrying a `security_case` block.
What the acknowledged type replaces:
- the shape review: the platform does not re-argue the role and policy form;
- a fresh executive summary: the case acceptance is cited instead.
What it does not replace is the per-case platform-owner approval of the exact names, paths and bound claims,
nor `binding_confirmed`, nor the per-case apply and verification receipts. Only the mint needs no per-case
CCR: the standing CCR-2026-0040 and CCR-2026-0043 grant covers the family, gated by the broker's case
admission (R1).
For nk-bind-executor-approval, drafted `proposed` (both pass `scripts/credential-change.py validate` and the
delegated-applier checks; `bound_claims_confirmed: false`, so apply stays blocked):
- `credential-change-requests/CCR-2026-0060-nk-bind-executor-approval-keycape-eso-reader.yaml`
- role `external-secrets-keycape-nk-bind-executor-approval`
- policy `openbao/policies/workload-kv-read-keycape-nk-bind-executor-approval-eso.hcl`
- approver: platform-owner
- `credential-change-requests/CCR-2026-0061-nk-bind-executor-approval-consumer-reader.yaml`
- role `keycape-nk-bind-executor-approval-bounded-executor-workload-kv-read`
- policy `openbao/policies/workload-kv-read-keycape-nk-bind-executor-approval-bounded-executor.hcl`
- bound to `sso/nk-bind-executor`, audience openbao, 15m
- approvers: platform-owner and net-kingdom-owner (an executor lane, as CCR-2026-0058/0059)
Activation conditions for both:
- the type is acknowledged;
- the case is accepted;
- the custody entry is minted (after R1);
- the CCR is approved and the binding confirmed.
If ops-mason later renders the case, these drafts are what the render must reconcile to; they are not
duplicates to be added beside it. The full-repository validate still reports the known, unrelated
CCR-2026-0035 failure.
### R7. KeyCape gets a Secret copy; the consumer does not. Verdict: accepted, with a note
KeyCape's ESO target `sso/keycape-<client_id>-client` is a Secret in namespace `sso`, the same namespace as
the consumer. "No Secret copy in the consumer namespace" holds for the consumer's own delivery, but not for
the namespace. The README's control text should say "no copy for the consumer" and name the KeyCape Secret
as the one at-rest copy, as CCR-2026-0033's risk note does ("one value, two copies"). This is a wording
change for r2; the boundary is unchanged (sso namespace writers are already inside it, CCR-2026-0033).
## Open items
1. net-kingdom: memo r2 with R3, R4, R5 and R7, plus the R2 wording.
2. Founder: acknowledge the type (r2), then accept the case.
3. railiance-platform: broker admission for `oidc-service-client@<version>` after acknowledgment (R1). This
needs a workplan task here.
4. Founder: approve CCR-2026-0060 and CCR-2026-0061 and confirm their bindings (R6).

View file

@ -0,0 +1,15 @@
# workload-kv-read-keycape-nk-bind-executor-approval-bounded-executor: CCR-2026-0061 (proposed),
# SecurityCase nk-bind-executor-approval (oidc-service-client@0.1, proposed), consumer reader for
# sso/nk-bind-executor. Platform-generated from the CCR. Source only; not applied.
path "platform/data/workloads/keycape/nk-bind-executor-approval" {
capabilities = ["read"]
}
path "auth/token/lookup-self" {
capabilities = ["read"]
}
path "auth/token/revoke-self" {
capabilities = ["update"]
}

View file

@ -0,0 +1,15 @@
# workload-kv-read-keycape-nk-bind-executor-approval-eso: CCR-2026-0060 (proposed), SecurityCase
# nk-bind-executor-approval (oidc-service-client@0.1, proposed). Platform-generated from the CCR
# (token_self_lifecycle true, as CCR-2026-0041). Source only; not applied.
path "platform/data/workloads/keycape/nk-bind-executor-approval" {
capabilities = ["read"]
}
path "auth/token/lookup-self" {
capabilities = ["read"]
}
path "auth/token/revoke-self" {
capabilities = ["update"]
}