2026-09-05 01:08:58 +02:00
|
|
|
# Proposed non-secret admission packet. This is not executable authorization.
|
Align approval registrations to the tenant:platform decision
Operator decision 5ed3fb35-eca9-413a-82b9-95171ba85bf6 accepts tenant:platform
as the platform management tenant for the Glas approval chain, requiring exact
spelling across the approval store, the service-client JWT claim and the
lifecycle CheckRequest.
Changes the tenant field on secrets-engine-approval and approval-engine-operator
only, in the registration fixture and the provisioning packet. Unrelated clients
and the human directory default keep tenant:coulomb, and no audience, scope,
subject, role, lifetime or MFA grant changes.
Adds issuance evidence that the approval shape emits tenant:platform exactly and
never an alias the caller requests, that the OpenBao client gains no
cross-tenant reach, and a fixture guard pinning every reviewed client's tenant.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NV9oijZukGyGbRQGGKnK4P
Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 713576@bnt-lap001
Assistant-Session: 384c511d-9bce-4cb8-a676-2aef6c0c8df6
2026-09-06 22:30:32 +02:00
|
|
|
# Tenant for both requests is tenant:platform per decision
|
|
|
|
|
# 5ed3fb35-eca9-413a-82b9-95171ba85bf6 (landlord zone). Exact spelling required
|
|
|
|
|
# across the approval store, these JWT claims and the lifecycle CheckRequest;
|
|
|
|
|
# no alias to platform or tenant:coulomb.
|
2026-09-05 01:08:58 +02:00
|
|
|
status: awaiting-custody-admission
|
|
|
|
|
owner: key-cape
|
|
|
|
|
resource_audience: approval-engine
|
|
|
|
|
issuer: https://kc.coulomb.social
|
|
|
|
|
registration_source: config/service-clients.example.yaml
|
|
|
|
|
requests:
|
|
|
|
|
- client_id: secrets-engine-approval
|
|
|
|
|
subject: service:secrets-engine
|
Align approval registrations to the tenant:platform decision
Operator decision 5ed3fb35-eca9-413a-82b9-95171ba85bf6 accepts tenant:platform
as the platform management tenant for the Glas approval chain, requiring exact
spelling across the approval store, the service-client JWT claim and the
lifecycle CheckRequest.
Changes the tenant field on secrets-engine-approval and approval-engine-operator
only, in the registration fixture and the provisioning packet. Unrelated clients
and the human directory default keep tenant:coulomb, and no audience, scope,
subject, role, lifetime or MFA grant changes.
Adds issuance evidence that the approval shape emits tenant:platform exactly and
never an alias the caller requests, that the OpenBao client gains no
cross-tenant reach, and a fixture guard pinning every reviewed client's tenant.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NV9oijZukGyGbRQGGKnK4P
Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 713576@bnt-lap001
Assistant-Session: 384c511d-9bce-4cb8-a676-2aef6c0c8df6
2026-09-06 22:30:32 +02:00
|
|
|
tenant: tenant:platform
|
2026-09-05 01:08:58 +02:00
|
|
|
scopes: [approval:read, approval:consume]
|
|
|
|
|
lifetime: 15m
|
|
|
|
|
proposed_openbao_path: platform/workloads/secrets-engine/approval-client
|
2026-09-08 16:46:02 +02:00
|
|
|
field: CLIENT_SECRET
|
2026-09-05 01:08:58 +02:00
|
|
|
proposed_kubernetes_secret: sso/keycape-secrets-engine-approval-client
|
|
|
|
|
kubernetes_key: client-secret
|
|
|
|
|
keycape_environment: KEYCAPE_SECRETS_ENGINE_APPROVAL_CLIENT_SECRET
|
|
|
|
|
custody_owner: railiance-platform
|
|
|
|
|
consumer: secrets-engine
|
|
|
|
|
- client_id: approval-engine-operator
|
|
|
|
|
subject: service:approval-engine-operator
|
Align approval registrations to the tenant:platform decision
Operator decision 5ed3fb35-eca9-413a-82b9-95171ba85bf6 accepts tenant:platform
as the platform management tenant for the Glas approval chain, requiring exact
spelling across the approval store, the service-client JWT claim and the
lifecycle CheckRequest.
Changes the tenant field on secrets-engine-approval and approval-engine-operator
only, in the registration fixture and the provisioning packet. Unrelated clients
and the human directory default keep tenant:coulomb, and no audience, scope,
subject, role, lifetime or MFA grant changes.
Adds issuance evidence that the approval shape emits tenant:platform exactly and
never an alias the caller requests, that the OpenBao client gains no
cross-tenant reach, and a fixture guard pinning every reviewed client's tenant.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NV9oijZukGyGbRQGGKnK4P
Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 713576@bnt-lap001
Assistant-Session: 384c511d-9bce-4cb8-a676-2aef6c0c8df6
2026-09-06 22:30:32 +02:00
|
|
|
tenant: tenant:platform
|
2026-09-05 01:08:58 +02:00
|
|
|
scopes: [approval:create, approval:read, approval:approve, approval:revoke, approval:supersede, approval:observe, approval:emit]
|
|
|
|
|
lifetime: 15m
|
|
|
|
|
proposed_openbao_path: platform/workloads/approval-engine/operator-client
|
2026-09-08 16:46:02 +02:00
|
|
|
field: CLIENT_SECRET
|
2026-09-05 01:08:58 +02:00
|
|
|
proposed_kubernetes_secret: sso/keycape-approval-engine-operator-client
|
|
|
|
|
kubernetes_key: client-secret
|
|
|
|
|
keycape_environment: KEYCAPE_APPROVAL_ENGINE_OPERATOR_CLIENT_SECRET
|
|
|
|
|
custody_owner: railiance-platform
|
|
|
|
|
consumer: approval-engine-operator
|
|
|
|
|
human_registration:
|
|
|
|
|
status: awaiting-exact-callback
|
2026-09-08 16:46:02 +02:00
|
|
|
blocks_service_client_rollout: false
|
Answer the approver-client questions, and fix what checking them turned up
informed-decision and approval-engine both asked to hear problems with the human
approver registration now rather than at handover. Checking their requested shape
against the source rather than agreeing it on paper turned up three things.
Scope gap, accepted: [openid, approval:approve] cannot render a decision, since
GET /v1/approvals/{id} and /claim both need approval:read -- the surface could
submit an entry it was never able to display. Published
[openid, approval:read, approval:approve]. Reading through a service identity was
the alternative and is worse: it weakens the evidence-of-what-this-person-saw
claim the component exists to make. approval:consume stays excluded.
Assurance shape, published and a defect fixed. Both asked for a documented shape
and KeyCape already emitted one, so it is written down rather than renegotiated.
Writing it down surfaced that `at` was the token mint time rather than the
authentication time. Those differ by hours whenever a browser session is reused,
and approval-engine persists this object verbatim as the only downstream record
that MFA happened -- so a stored approval could have evidenced MFA at a moment
the person proved nothing. PKCESession.AuthTime now carries the original login
instant through session reuse, with mint time as the fallback.
Blocker found before anyone built on it: a human token cannot carry
tenant:platform. effectiveTenant resolves the human tenant from the directory
user, no adapter populates User.Tenant, and the per-client tenant field is read
only on the client_credentials path -- so every human token defaults to
tenant:coulomb, which approval-engine refuses by exact string equality. It would
have presented as a failed approval rather than a registration defect. Two
resolutions sent to the owners and neither implemented here: the choice decides
whether a human's tenant is a property of the person or of the registration, and
that is not KeyCape's alone to make.
Also recorded ops-warden's answers to KEY-WP-0014-T04, including their finding
that `warden plan` returns `autonomous` for a need containing generate and
CAS-write, because it has no read-versus-mutate intent. Their standing
instruction -- treat a warden plan verdict on any write, rotate or provision need
as unreliable until WARDEN-WP-0038 lands -- is recorded in the workplan rather
than left in an inbox.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016uV8zoCKpA1WRAxsKRYbdH
Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 1182213@bnt-lap001
Assistant-Session: 966597b9-ae61-46a4-8b9e-1594ab3ec4ad
2026-09-09 14:25:38 +02:00
|
|
|
# Owner identified 2026-09-09: informed-decision (hub repo cf4c7da8). It
|
|
|
|
|
# supplies client_id and callback URI from INFD-WP-0001-T07 once it has a
|
|
|
|
|
# deployed origin. approval-engine is a bearer-only resource server and never
|
|
|
|
|
# owned those strings.
|
|
|
|
|
owner: informed-decision
|
|
|
|
|
# approval:read added 2026-09-09 on approval-engine's finding: the surface
|
|
|
|
|
# cannot render a decision without GET /v1/approvals/{id} and /claim, so the
|
|
|
|
|
# earlier scope set let an approver submit what they could not display.
|
|
|
|
|
scopes: [openid, approval:read, approval:approve]
|
2026-09-05 01:08:58 +02:00
|
|
|
mfa_required: true
|
|
|
|
|
client_type: public
|
Answer the approver-client questions, and fix what checking them turned up
informed-decision and approval-engine both asked to hear problems with the human
approver registration now rather than at handover. Checking their requested shape
against the source rather than agreeing it on paper turned up three things.
Scope gap, accepted: [openid, approval:approve] cannot render a decision, since
GET /v1/approvals/{id} and /claim both need approval:read -- the surface could
submit an entry it was never able to display. Published
[openid, approval:read, approval:approve]. Reading through a service identity was
the alternative and is worse: it weakens the evidence-of-what-this-person-saw
claim the component exists to make. approval:consume stays excluded.
Assurance shape, published and a defect fixed. Both asked for a documented shape
and KeyCape already emitted one, so it is written down rather than renegotiated.
Writing it down surfaced that `at` was the token mint time rather than the
authentication time. Those differ by hours whenever a browser session is reused,
and approval-engine persists this object verbatim as the only downstream record
that MFA happened -- so a stored approval could have evidenced MFA at a moment
the person proved nothing. PKCESession.AuthTime now carries the original login
instant through session reuse, with mint time as the fallback.
Blocker found before anyone built on it: a human token cannot carry
tenant:platform. effectiveTenant resolves the human tenant from the directory
user, no adapter populates User.Tenant, and the per-client tenant field is read
only on the client_credentials path -- so every human token defaults to
tenant:coulomb, which approval-engine refuses by exact string equality. It would
have presented as a failed approval rather than a registration defect. Two
resolutions sent to the owners and neither implemented here: the choice decides
whether a human's tenant is a property of the person or of the registration, and
that is not KeyCape's alone to make.
Also recorded ops-warden's answers to KEY-WP-0014-T04, including their finding
that `warden plan` returns `autonomous` for a need containing generate and
CAS-write, because it has no read-versus-mutate intent. Their standing
instruction -- treat a warden plan verdict on any write, rotate or provision need
as unreliable until WARDEN-WP-0038 lands -- is recorded in the workplan rather
than left in an inbox.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016uV8zoCKpA1WRAxsKRYbdH
Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 1182213@bnt-lap001
Assistant-Session: 966597b9-ae61-46a4-8b9e-1594ab3ec4ad
2026-09-09 14:25:38 +02:00
|
|
|
grant: authorization_code + S256 PKCE
|
|
|
|
|
never: [approval:consume]
|
2026-09-09 14:40:36 +02:00
|
|
|
# Required. Without it the token carries tenant:coulomb from the directory
|
|
|
|
|
# default and approval-engine refuses it. See docs/tenant-claim-contract.md.
|
|
|
|
|
tenant: tenant:platform
|
|
|
|
|
keycape_side_resolved: |
|
|
|
|
|
2026-09-09: a human token can now carry tenant:platform. A client
|
|
|
|
|
registration may declare a tenant; humanTenant() supplies it when the
|
|
|
|
|
directory has not placed the user, requires agreement when it has, and
|
|
|
|
|
refuses issuance on conflict rather than relabelling. Safe only because
|
|
|
|
|
registrations are static and deployment-owned. Still outstanding from
|
|
|
|
|
elsewhere: the exact client_id and callback URI from informed-decision
|
|
|
|
|
(INFD-WP-0001-T07), and whether the owners prefer the directory-sourced
|
|
|
|
|
resolution instead, which this implementation degrades into cleanly.
|
2026-09-05 01:08:58 +02:00
|
|
|
verification:
|
Ship the live-registration check both blocked tasks depend on
KEY-WP-0013-T02 and KEY-WP-0014-T04 stay blocked on custody and on ops-warden,
but each contains a KeyCape-owned piece that had been left as prose. T02 requires
proving "live JWKS verification and denied excess scopes without logging values";
T04 step 4 requires verifying a rotated secret, refusing its predecessor and
refusing excess scope. Both were describable and neither was runnable, so the
proof would have been improvised by hand against production at the moment custody
lands -- the worst possible time for it.
keycape verify-client does it in one command. Per registration it checks
discovery origin, the client_credentials exchange and its RS256 signature against
the deployed JWKS, exact sub/tenant/roles/principal_type, that every -deny-scope
is refused, and that the token carries no scope that was not requested. That last
check is a real gap: the caller commands prove every requested scope was granted,
never that nothing extra came back. -previous-secret-env additionally requires
the predecessor to be refused, and treats an unchanged secret as a rotation that
did not happen.
Nothing is written to disk and no value is printed. Failures name the claim, not
the observed value, so running this against production cannot turn a verification
into a disclosure. Every check runs before it reports, so one failure does not
hide the rest.
The exact invocation for each approval client is recorded in the verification
block of the provisioning packet, so custody admission hands back a command
rather than a description.
Tests cover the passing case, an over-broad registration, a live predecessor, an
identical rotation and output non-disclosure; neutering mustFail makes the suite
fail, so the checks have teeth.
Also recorded in KEY-WP-0014: read from ops-warden's catalog rather than waiting
for a reply, key-cape-oidc-login is asked-and-waiting on us since 2026-08-28 and
is a pointer lane with no programmatic consumers, and the rapp-qonto-keycape-client
blocker citing an absent native exchange command went stale when service-token
shipped on 2026-09-05.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016uV8zoCKpA1WRAxsKRYbdH
Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 1182213@bnt-lap001
Assistant-Session: 966597b9-ae61-46a4-8b9e-1594ab3ec4ad
2026-09-08 14:30:43 +02:00
|
|
|
# The first two lines are now one runnable command per client; see
|
|
|
|
|
# docs/native-authentication.md, "Verifying a live registration". It writes
|
|
|
|
|
# nothing and prints no value, so it is safe to run against production.
|
|
|
|
|
- command: |
|
|
|
|
|
keycape verify-client -issuer https://kc.coulomb.social
|
|
|
|
|
-client-id secrets-engine-approval -audience approval-engine
|
|
|
|
|
-scope "approval:read approval:consume"
|
|
|
|
|
-secret-env KEYCAPE_SECRETS_ENGINE_APPROVAL_CLIENT_SECRET
|
|
|
|
|
-expect-subject service:secrets-engine -expect-tenant tenant:platform
|
|
|
|
|
-expect-roles secrets-engine
|
|
|
|
|
-deny-scope "approval:approve approval:revoke approval:supersede"
|
|
|
|
|
- command: |
|
|
|
|
|
keycape verify-client -issuer https://kc.coulomb.social
|
|
|
|
|
-client-id approval-engine-operator -audience approval-engine
|
|
|
|
|
-scope "approval:create approval:read approval:approve approval:revoke approval:supersede approval:observe approval:emit"
|
|
|
|
|
-secret-env KEYCAPE_APPROVAL_ENGINE_OPERATOR_CLIENT_SECRET
|
|
|
|
|
-expect-subject service:approval-engine-operator -expect-tenant tenant:platform
|
|
|
|
|
-expect-roles approval-operator
|
|
|
|
|
-deny-scope "approval:consume"
|
2026-09-05 01:08:58 +02:00
|
|
|
- Check KeyCape and consumer readiness without emitting secrets or tokens.
|
|
|
|
|
- Preserve existing registrations and signing key; record versions and image digest.
|
Ship the live-registration check both blocked tasks depend on
KEY-WP-0013-T02 and KEY-WP-0014-T04 stay blocked on custody and on ops-warden,
but each contains a KeyCape-owned piece that had been left as prose. T02 requires
proving "live JWKS verification and denied excess scopes without logging values";
T04 step 4 requires verifying a rotated secret, refusing its predecessor and
refusing excess scope. Both were describable and neither was runnable, so the
proof would have been improvised by hand against production at the moment custody
lands -- the worst possible time for it.
keycape verify-client does it in one command. Per registration it checks
discovery origin, the client_credentials exchange and its RS256 signature against
the deployed JWKS, exact sub/tenant/roles/principal_type, that every -deny-scope
is refused, and that the token carries no scope that was not requested. That last
check is a real gap: the caller commands prove every requested scope was granted,
never that nothing extra came back. -previous-secret-env additionally requires
the predecessor to be refused, and treats an unchanged secret as a rotation that
did not happen.
Nothing is written to disk and no value is printed. Failures name the claim, not
the observed value, so running this against production cannot turn a verification
into a disclosure. Every check runs before it reports, so one failure does not
hide the rest.
The exact invocation for each approval client is recorded in the verification
block of the provisioning packet, so custody admission hands back a command
rather than a description.
Tests cover the passing case, an over-broad registration, a live predecessor, an
identical rotation and output non-disclosure; neutering mustFail makes the suite
fail, so the checks have teeth.
Also recorded in KEY-WP-0014: read from ops-warden's catalog rather than waiting
for a reply, key-cape-oidc-login is asked-and-waiting on us since 2026-08-28 and
is a pointer lane with no programmatic consumers, and the rapp-qonto-keycape-client
blocker citing an absent native exchange command went stale when service-token
shipped on 2026-09-05.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016uV8zoCKpA1WRAxsKRYbdH
Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 1182213@bnt-lap001
Assistant-Session: 966597b9-ae61-46a4-8b9e-1594ab3ec4ad
2026-09-08 14:30:43 +02:00
|
|
|
- Human consume denial is approval-engine's to verify at its resource; KeyCape
|
|
|
|
|
proves only that the human client is never issued a consume grant.
|
Record the custody owner's confirmations and guard a receipt against misreading
railiance-platform replied on all three open threads. Recording what they add
rather than what we already knew.
T02: they reached the same reading of the verifier receipt independently and add
human_client_consume_denied: true for both clients, confirm the verifier ran
twice (activation and post-rollout) at generation 38 with the signing key
unchanged, and state T02 can close against that receipt. They also correct our
framing, rightly: the packet says client-side retrieval is unadmitted, which
stays true, but the attended operator path is not a client-side read and never
required one. This task had been treating those as the same constraint.
T04: the live approval clients mean this repo now holds a committed receipt that
looks like rotation evidence and is not. Both owners independently state the same
two gaps -- no real predecessor rotation, no observed wall-clock expiry. Recorded
in T04 rather than only in T02, because verify-client's predecessor rejection is
now implemented and unproven, which is a different state from missing or done,
and T04 is where that distinction belongs.
Answered their open question on CCR-2026-0020, which has no named presenting
actor. As issuer: the registration carries both approval:create and
approval:approve, so one presenter can create an entry and approve it. That is a
separation-of-duties property of the holder, not a defect in the token -- KeyCape
issues the grants approval-engine asked for. Recorded both shapes KeyCape can
support, and that who holds it is approval-engine's decision and the doctrine
question gate-house's, not ours.
Also narrowed the operations note: the deployed config has not been written since
the activation, so the inspected state is the state that will boot.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016uV8zoCKpA1WRAxsKRYbdH
Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 1182213@bnt-lap001
Assistant-Session: 966597b9-ae61-46a4-8b9e-1594ab3ec4ad
2026-09-09 20:05:40 +02:00
|
|
|
# Asked by railiance-platform 2026-09-09 (CCR-2026-0020 has no named presenting
|
|
|
|
|
# actor in any owner source, and they declined to guess one). This is KeyCape's
|
|
|
|
|
# view as the issuer of the identity, not a decision: who may hold the
|
|
|
|
|
# credential is approval-engine's call and the doctrine question is
|
|
|
|
|
# gate-house's.
|
|
|
|
|
presenting_actor_note:
|
|
|
|
|
client_id: approval-engine-operator
|
Record the operator withdrawal and the gate-house tenant conditions
approval-engine answered the presenting-actor question we put to them and the
answer withdrew the client: nobody presents approval-engine-operator. Nothing in
their repository obtains an OAuth token, and scope by scope the bundle never
described a single actor -- approve belongs to the human client, emit is
redundant, and no requester identity was ever settled for create. Annotated as
withdrawn in the example config and the packet rather than deleted, because they
asked for it to stay authenticable under CCR-2026-0018 if a presenter appears.
The separation-of-duties observation is kept because it constrains the
re-request: approve must not travel with the operational scopes.
Raised rather than filed: "do not provision" cannot undo a provision. The client
went live in the 2026-09-09 attended rollout, so the estate now holds a
confidential credential that can create and approve approvals, that nobody
presents, and that no consumer awaits. Standing capability with no counterparty
is a worse resting state than either provisioning it for a named presenter or
disabling it. Disablement is KeyCape's to execute and not KeyCape's to decide
alone, so it goes to the owners rather than into a commit.
Also records GH-DEC-2026-013 conditions (a) and (b) in the tenant contract, both
of which strengthen what we had written about ourselves. Row four is normative,
and in the direction we would not have guessed: a future change preferring the
DIRECTORY is also void, because "the directory won" is still a winner and picking
any winner turns a refusal into a silent cross-tenant assertion. And lifting the
dynamic-registration exclusion now VOIDS the registration-bound shape that day
rather than reopening it for review -- our "must be revisited" was too weak.
The shape is recorded as a declared bounded gap, not the terminal state.
Verification note: the Go suite could not be run green for this commit because a
peer session is mid-edit on token.go and human_tenant_test.go for the §5
provenance work. Every file in this commit is YAML or Markdown; both YAML files
were parsed and the example config's scope set and tenant were asserted unchanged.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016uV8zoCKpA1WRAxsKRYbdH
Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 1182213@bnt-lap001
Assistant-Session: 966597b9-ae61-46a4-8b9e-1594ab3ec4ad
2026-09-09 23:30:09 +02:00
|
|
|
# ANSWERED 2026-09-09 by approval-engine: the presenter is nobody, and they have
|
|
|
|
|
# withdrawn the request. Nothing in their repository obtains an OAuth token; the
|
|
|
|
|
# engine only verifies them. They asked railiance-platform to cancel the
|
|
|
|
|
# client-side reader CCR-2026-0020 as an owner decision. Verifier custody
|
|
|
|
|
# CCR-2026-0018 stands. The separation-of-duties observation below is what
|
|
|
|
|
# prompted the question and is retained because it constrains the re-request:
|
|
|
|
|
# approval:approve must not travel with the operational scopes.
|
|
|
|
|
status: withdrawn-by-requesting-owner
|
|
|
|
|
already_live: true # provisioned in the 2026-09-09 attended rollout; withdrawal does not unprovision it
|
Record the custody owner's confirmations and guard a receipt against misreading
railiance-platform replied on all three open threads. Recording what they add
rather than what we already knew.
T02: they reached the same reading of the verifier receipt independently and add
human_client_consume_denied: true for both clients, confirm the verifier ran
twice (activation and post-rollout) at generation 38 with the signing key
unchanged, and state T02 can close against that receipt. They also correct our
framing, rightly: the packet says client-side retrieval is unadmitted, which
stays true, but the attended operator path is not a client-side read and never
required one. This task had been treating those as the same constraint.
T04: the live approval clients mean this repo now holds a committed receipt that
looks like rotation evidence and is not. Both owners independently state the same
two gaps -- no real predecessor rotation, no observed wall-clock expiry. Recorded
in T04 rather than only in T02, because verify-client's predecessor rejection is
now implemented and unproven, which is a different state from missing or done,
and T04 is where that distinction belongs.
Answered their open question on CCR-2026-0020, which has no named presenting
actor. As issuer: the registration carries both approval:create and
approval:approve, so one presenter can create an entry and approve it. That is a
separation-of-duties property of the holder, not a defect in the token -- KeyCape
issues the grants approval-engine asked for. Recorded both shapes KeyCape can
support, and that who holds it is approval-engine's decision and the doctrine
question gate-house's, not ours.
Also narrowed the operations note: the deployed config has not been written since
the activation, so the inspected state is the state that will boot.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016uV8zoCKpA1WRAxsKRYbdH
Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 1182213@bnt-lap001
Assistant-Session: 966597b9-ae61-46a4-8b9e-1594ab3ec4ad
2026-09-09 20:05:40 +02:00
|
|
|
observation: |
|
|
|
|
|
The registration as issued 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 the credential holder, not a defect in the
|
|
|
|
|
token -- KeyCape issues exactly the grants approval-engine requested, and
|
|
|
|
|
splitting them would be a change to that request rather than to issuance.
|
|
|
|
|
options_keycape_can_support:
|
|
|
|
|
- Two registrations with disjoint grants, one creating and one approving, if
|
|
|
|
|
approval-engine wants the split enforced at issuance. KeyCape can do this
|
|
|
|
|
today; it needs approval-engine to say so, since it changes their contract.
|
|
|
|
|
- Keep one registration and constrain the holder by custody instead, which is
|
|
|
|
|
where it sits now.
|
|
|
|
|
keycape_does_not_decide: |
|
|
|
|
|
Who presents this credential, and whether self-approval is acceptable for the
|
|
|
|
|
lifecycle operator, are not KeyCape's to settle. Recorded here so the question
|
|
|
|
|
is answered from the registration rather than reconstructed from it.
|
|
|
|
|
|
2026-09-05 01:08:58 +02:00
|
|
|
blockers:
|
|
|
|
|
- Admit exact custody paths, field delivery, consumer identities and lifecycle authority.
|
|
|
|
|
- Resolve attended first-provision authority through the custody owner.
|
2026-09-08 16:46:02 +02:00
|
|
|
- Verify the actual upstream ID-token issuer before production image rollout.
|
|
|
|
|
custody_return:
|
|
|
|
|
owner_record: railiance-platform/workplans/RPF-WP-0035-credential-lane-implementation.md#Admit-KeyCape-approval-engine-client-custody-and-delivery
|
|
|
|
|
requests: [CCR-2026-0017, CCR-2026-0018]
|
|
|
|
|
status: proposed-awaiting-owner-approval
|
|
|
|
|
field_correction: CLIENT_SECRET
|
|
|
|
|
scope: KeyCape verifier-side copies only; client-side retrieval is not admitted.
|
|
|
|
|
human_registration_gate:
|
|
|
|
|
- The approver UI owner must supply its real client ID and exact callback.
|
|
|
|
|
- This gate blocks human approval entry; it does not block the two independent client_credentials registrations or service startup.
|