railiance-platform/credential-change-requests/CCR-2026-0020-approval-engine-operator-client-read.yaml

174 lines
7.9 KiB
YAML
Raw Normal View History

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.
Declare the security layer, and repair the placement admission check Layer declaration (gate-house). INTENT.md now carries the declaration in its own voice with layer.yaml as the machine-readable form, adapted from ops-warden's reference. railiance-platform is Staff: operating OpenBao is not a claim to the Tooling layer, because §4 is explicit that no operator-of-third-party-Tooling shape exists and that someone running it stays a declared gap. Six direct Tooling contacts are mapped by capability rather than by file — one §5.2 conduit, one §5.1 diagnostic, four §5.3 gaps with intended owners and review dates — and the uncatalogued contacts are listed so the check is total. We are PEP-shaped and the unreachable-engine stance map is NOT published; that is recorded as an open obligation to build against v0.8, not left silent. Placement admission. canned-prompts was added as a PostgresConsumer on platform-pg-2 in rapp-postgres 1b68b4c without a placement owner here, which is exactly the cross-repo drift the assurance check exists to catch; the check had been failing on it. Registered with its real boundary evidence, corrected the stale test expectation that pinned the overflow cell at one consumer, and updated the SCOPE occupancy line to 2/4. Also records owner input received today: key-cape's issuer view on CCR-2026-0020's presenting actor, and their confirmation that codex-railiance-platform correctly stays tenant:coulomb, so the flagged T02 discrepancy is closed as not-a-defect. The whynot-design npm field is NOT changed. Two dated live receipts here name NPM_AUTH_TOKEN as the field, including an attended founder fetch; that is recorded against the counterparty claim rather than either side being flipped before the session settles it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WLUjpv3ssxNRAEPPgLFnEB Assistant: claude-code Assistant-Model: opus Assistant-Process: 1275505@bnt-lap001 Assistant-Session: 97265baa-f08f-4032-b290-a1e2965a69c5
2026-09-09 23:27:14 +02:00
- 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