id: CCR-2026-0020 kind: credential-change-request schema_version: 1 request_type: workload-kv-read title: Client-side read of the approval-engine-operator client secret status: cancelled created: '2026-09-09' updated: '2026-09-10' cancellation: owner: approval-engine reason: >- Requesting owner withdrew the combined lifecycle operator client: no presenter exists and no client-side reader is wanted. Human approval belongs to informed-decision; any future service presenter needs a narrow new request. Completed verifier custody CCR-2026-0018 remains unchanged. source_ref: >- approval-engine@849c75bb094613ff6ac1a1d4cda56a745520c5a1:docs/keycape-service-registrations.md#approval-engine-operator recorded_at: '2026-09-10' # Historical omissions retained so cancellation does not invent an auth binding. in_flight: missing_fields: - openbao.auth blocking_reason: >- The presenting actor is not identified by any owner source. approval-engine verifies this client's tokens but never presents them, and no other repo claims the approver surface that would. Live inspection on 2026-09-09 found no approval-engine namespace, workload or Service in the cluster; the owner deploy manifest targets namespace approval-engine and remains unapplied. An auth method cannot be chosen before it is known whether the presenter is an attended operator, an in-cluster component, or an agent surface, and the three imply different roles, policies and delivery. owner: railiance-platform requester: agent: claude reason: >- RPF-WP-0035-T06 residual from the completed verifier custody (CCR-2026-0018). The verifier copy lets KeyCape authenticate the approval-engine-operator client; something must also present it. This request holds the client-side reader open with the determined parts recorded and the undetermined parts named, rather than guessing a reader for the widest scope set in the pair. It reuses existing version-1 custody; no reseed, no rotation. review: required: true required_approvers: - platform-operator - approval-engine-owner comments: - at: '2026-09-09' reviewer: railiance-platform (claude) decision: actor_not_determined comment: >- Determined from owner source: the approval-engine-operator registration holds approval:create, read, approve, revoke, supersede, observe and emit, and explicitly not consume; approval-engine itself only verifies presented tokens (approval_engine/api.py identity check) and does not hold this client. approval-engine's own records note the engine does not restrict /entries by principal type, so this client can supply approver evidence today, and whether it should is approval doctrine owned by gate-house. The reader therefore cannot be named here without an owner decision, and naming one anyway would hand the widest approval scope to a guessed identity. Held in_flight deliberately. - at: '2026-09-09' reviewer: key-cape (issuer view, not a decision) decision: presenting_actor_shapes_offered comment: >- KeyCape notes the registration carries both approval:create and approval:approve, so a single presenter can create an entry and then approve it. That is a separation-of-duties property of whoever holds the credential, not a defect in the token: KeyCape issues exactly the grants approval-engine requested. Two shapes are supportable -- two registrations with disjoint create and approve grants, enforced at issuance and available today; or one registration with the holder constrained by custody, which is where it sits now. Which is right is approval-engine's call and the doctrine question is gate-house's. Recorded in key-cape docs/approval-engine-provisioning-request.yaml under presenting_actor_note. This does not name an actor and does not unblock this request. target: domain: financials tenant: platform workload: approval-engine environment: production purpose: >- Establish the separate exact read by which the eventual approval-engine operator surface obtains its client secret, once that surface is named by its owner. openbao: mount: platform kv_path: platform/workloads/approval-engine/operator-client fields: - CLIENT_SECRET policy_name: workload-kv-read-approval-engine-operator-client policy_file: openbao/policies/workload-kv-read-approval-engine-operator-client.hcl access_frontdoor: type: ops-warden catalog_id: approval-engine-operator-client selector: approval-engine operator client secret resolvable: false readiness: disabled delivery: surface: none target: >- Cancelled on owner withdrawal; the following alternatives were never admitted. Not determined. The delivery surface follows the actor: an attended operator implies a protected workstation file, an in-cluster component implies an ExternalSecret into the eventual approval-engine namespace, and an agent surface implies neither. No surface is admitted until the owner names one. risk: classification: high notes: - >- This is the widest scope set in the pair: create, approve, revoke and supersede. A holder can forge approval lifecycle actions, and because approval-engine records principal_type rather than restricting it, a non-human holder can supply approver evidence. That is why the actor is left unnamed rather than defaulted. - >- approval:consume is absent by design and stays absent. Adding it is a new lane decision, not an edit to this request. - >- The exact-path read policy is written and committed so the eventual reader inherits a bounded grant rather than a broad one drafted under time pressure. Writing the policy is not admitting a reader. - >- No reseed and no rotation; custody is the existing version 1 from the verifier activation. verification: positive: - >- Once the actor is named, the bound identity reads CLIENT_SECRET from the exact path into its admitted delivery surface with no value in chat, argv, shell history or logs. - >- The named surface obtains a token with subject service:approval-engine-operator, audience approval-engine and tenant tenant:platform, verified against live JWKS. negative: - >- An identity outside the confirmed binding cannot authenticate through the eventual role. - >- The role cannot read the secrets-engine approval-client path, cannot list any parent, and cannot write or patch the custody path. - The reader is denied approval:consume, and denial is observed rather than assumed. - >- The KeyCape verifier role cannot be substituted for this reader, and this reader cannot read the verifier's delivery Secret. activation_conditions: - >- The approval-engine owner names the presenting actor and its placement, or records that no client-side reader is wanted and this request is cancelled. - >- Given that actor, the auth method, mount, role, bound claims and delivery surface are completed and reviewed as a concrete request. - >- Policy and role applied under attended authority (openbao-platform-admin-login, founder_required) with metadata-only receipts. - Positive and negative results recorded with non-secret request ids. evidence: [] lifecycle: deactivate: >- Detach the policy from the eventual role and disable the ops-warden catalog entry. The KeyCape verifier lane and its delivery are unaffected. rotate: >- Shared with CCR-2026-0018: one client secret, minted by KeyCape and rewritten once under attended authority. No independent rotation for this lane alone. compromised: >- KeyCape disables the approval-engine-operator registration first, then the value is rotated; emitted approval actions are referred to approval-engine for audit review, since rotation does not retract them. state_hub: workplan_id: RPF-WP-0035 task_id: RPF-WP-0035-T06 related_request: CCR-2026-0018