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
This commit is contained in:
tegwick 2026-09-09 20:05:40 +02:00
parent 5ae80d341f
commit 7a73352368
4 changed files with 75 additions and 2 deletions

View file

@ -85,6 +85,30 @@ verification:
- Preserve existing registrations and signing key; record versions and image digest.
- 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.
# 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
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.
blockers:
- Admit exact custody paths, field delivery, consumer identities and lifecycle authority.
- Resolve attended first-provision authority through the custody owner.

View file

@ -139,8 +139,11 @@ grant is not `client_credentials`; those fields were silently ignored there, so
registration could look effective and do nothing. This is a **startup** error:
a stray field in the deployed config stops the process from booting rather than
degrading. The live `sso/keycape-config` was checked on 2026-09-09 and none of
its three browser clients carries either field, so it passes as deployed — but
re-check if that config changed since. `tenant` is deliberately not rejected;
its three browser clients carries either field, so it passes as deployed. The
custody owner confirmed independently that the config was last written during the
2026-09-09 activation (resource version 58747126) and has not been edited since,
so the inspected state is the state that will boot — and that they will re-check
the browser clients if anything writes that Secret before the window. `tenant` is deliberately not rejected;
see below.
**2. A conflicting tenant binding refuses issuance (`329e48f`, KEY-WP-0013-T05).**

View file

@ -280,6 +280,37 @@ anyone. Predecessor rejection is implemented and unit-tested in
`keycape verify-client`; it has not been run against a real rotation, which is
KEY-WP-0014-T04's territory and still has no admitted execution authority.
**Independently confirmed by the custody owner, 2026-09-09** (railiance-platform
`ae6ff4bc`, `570e6746`). They reached the same reading from their own side and
add one datum this record did not carry: `human_client_consume_denied: true` for
both clients, alongside the signature, exact-claim, lifetime, excess-scope and
wrong-secret results. They confirm the verifier ran twice — once during
activation and again after rollout — at deployment generation 38, single ready
replica, signing key unchanged, no credential values emitted, and state that T02
can be closed against that receipt rather than against a run KeyCape cannot make.
They also correct one thing in our framing, and they are right: the packet says
*client-side retrieval* is unadmitted, which remains true, but the attended
operator path is not a client-side read and never required one. The two are
distinct and this task had been treating them as the same constraint.
**Do not cite this receipt as rotation evidence.** Both owners now state the same
two gaps independently: `real_predecessor_rotation_tested: false` and
`observed_wall_clock_expiry: false`. Neither is a T02 acceptance criterion, and
neither is closed by anything recorded here — see KEY-WP-0014-T04.
**Client-side retrieval is prepared but still unadmitted.** CCR-2026-0019
(secrets-engine reader) and CCR-2026-0020 (approval-engine-operator reader) are
`in_flight` under RPF-WP-0035-T06. 0020 has no named presenting actor in any
owner source, and railiance-platform left it undetermined rather than guessing —
correctly. KeyCape's view, asked for as the issuer of the identity and recorded
in `docs/approval-engine-provisioning-request.yaml`, is that the registration as
issued carries both `approval:create` and `approval:approve`, so a single
presenter can create and then approve the same entry. That is a
separation-of-duties question about who may hold the credential, not a defect in
the token: KeyCape issues the grants approval-engine requested. The decision is
approval-engine's and the doctrine question is gate-house's.
## Assign and register the human approver browser client
```task

View file

@ -212,3 +212,18 @@ be worse than the imprecision. No reply needed on that point.
T04 stays `wait`: the rotation authority now has a named owner
(railiance-platform, transport `secrets-engine exec`) but does not yet exist, and
nothing here changed a route.
2026-09-09, guarding against a misreading that is now easy to make. The approval
clients went live and `docs/evidence/2026-09-09-keycape-verifier-admission.json`
records a passing verifier run, so there is now a committed receipt in this repo
that looks like rotation evidence and is not. It records
`real_predecessor_rotation_tested: false` and `observed_wall_clock_expiry: false`,
and under `limits`, `actual_predecessor_rotation_observed: false` and
`wall_clock_jwt_expiry_observed: false`. railiance-platform stated the same two
gaps unprompted from their side (`ae6ff4bc`): "do not cite this receipt as
rotation evidence."
So `keycape verify-client`'s predecessor rejection has been exercised only
against unit tests and a first provisioning where predecessor and successor were
never distinct. Rotation step 4 is implemented and unproven, which is a different
state from either "missing" or "done", and T04 is where that distinction lives.