The Qonto rotation blocker is narrowed from an open question to a schedulable act: railiance-platform named the executor, transport and authority, and will prepare the CCR if KeyCape asks. Not asked -- requesting a production credential rotation with a restart window is the operator's decision, so it was put to him. Established first that this rotation has no incident driver. The 2026-08-23 exposure covered credentials embedded in config.yaml and the signing key; the rapp-qonto secret is an env: secretRef in a separate Secret and was not in that payload. Lifecycle hygiene, not remediation -- which changes what a reasonable window looks like, and means nobody should be carrying it as leftover incident work. 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
17 KiB
| id | type | title | domain | repo | status | owner | topic_slug | created | updated | state_hub_workstream_id |
|---|---|---|---|---|---|---|---|---|---|---|
| KEY-WP-0014 | workplan | Review native login and client credential lane handoffs | infotech | key-cape | blocked | codex | native-credential-lane-handoff | 2026-09-05 | 2026-09-08 | 0d003df3-f7d3-5063-8ca0-e1e33f7df74a |
Source: ops-warden inbox message 0dd9c7bd-0ecd-42d1-806f-7cc4ba9730ed. The native service exchange and public PKCE login commands are now implemented. Cross-owner rotation and consumer-specific route cutover remain outstanding.
Design owner command and custody boundaries
id: KEY-WP-0014-T01
status: done
priority: medium
state_hub_task_id: "0c0a0b61-c19e-5631-9cda-8b2dc0f47d8f"
Review ops-warden's existing key-cape-oidc-login proxy and rapp-qonto-keycape-client route contracts. Specify the native interactive login and bounded exchange commands, token delivery, renewal and custody-mediated rotation before implementation. Keep secret custody with OpenBao and avoid retiring the proxy until replacement commands have equivalent verification.
Verify handoff delivery evidence
id: KEY-WP-0014-T02
status: done
priority: low
state_hub_task_id: "d9a5de97-b7d5-5599-98c2-eaab32f51495"
Ops-warden reports KEY-WP-0009-T04's claimed reply did not arrive. Verify prior receipts for all four named recipients before claiming successful notification. No outbound coordination messages were sent during the 2026-09-05 repo review.
Implement and verify native caller commands
id: KEY-WP-0014-T03
status: done
priority: high
state_hub_task_id: "4e46f474-a15e-594f-ae07-ad37a9667d89"
Implemented keycape service-token and login with HTTPS discovery, RS256/JWKS verification, exact audience bindings, PKCE/state/nonce, private out-of-Git file delivery and redacted errors. Service Basic authentication now form-decodes credentials. Integration tests exercise the real token/JWKS handlers and browser callback, including forged state, tampering, excess scope and unsafe output. See docs/native-authentication.md for invocation and ownership boundaries.
2026-09-10. The authority question is answered and the blocker is narrowed to a
schedulable act. railiance-platform named the executor (platform operator,
attended founder session; explicitly not ops-warden, not secrets-engine
autonomously, and no unattended agent), the transport (the governed
openbao-platform-admin-login lane via warden access ... --exec), and the
authority (founder_required attended OIDC, role=platform-admin). What is missing
is a rotation CCR, which they will prepare if KeyCape asks — a two-custodian
CAS rotation with a service restart being a distinct version-guarded operation
rather than an implementation detail of an existing lane.
Not asked, deliberately. Requesting a production credential rotation with a restart window is the operator's decision, not this repository's, so it was put to him rather than answered here (ops-warden msg 10fcc8af).
Established before recommending anything either way: this rotation has no
incident driver. KEY-WP-0011 recovered a real exposure on 2026-08-23, and the
coordinated rotation that followed replaced the RS256 signing key, the LLDAP bind
credential, the Authelia client credential and the privacyIDEA admin token. The
rapp-qonto client secret was not in that payload — it is an env: secretRef
resolved from a separate Kubernetes Secret, never inline in config.yaml, which
config validation enforces. So this is lifecycle hygiene, not remediation, and
nobody should be carrying it as leftover incident work. Hygiene can wait for a
chosen window; remediation could not.
Also recorded: warden route/warden plan previously returned a generic match
that read as authorization, and offered read transports against a write need.
This repository declined to act on it; railiance-platform independently called it
a defect; fixed under WARDEN-WP-0038.
Admit rotation and verify consumer handoff
id: KEY-WP-0014-T04
status: wait
priority: high
state_hub_task_id: "e7632c71-9676-5837-a925-7f905e3015c5"
Qonto rotation requires admitted custody/provider transaction and rollback semantics; see the exact review sequence in docs/native-authentication.md. The current login proxy yields an OpenBao token, so it cannot be replaced by a JWT-writing command without adapting its consumer contract. Verify that split with the platform owner and ops-warden before route changes. No route was changed.
2026-09-05 receipt audit: GET /messages/?from_agent=key-cape returned one 2026-03 image-publication message and no KEY-WP-0009 handoff receipts for NetKingdom, secrets-engine, ops-warden or railiance-platform. Corrected the unsupported statement in KEY-WP-0009-T04. This proves only that no receipts are present in the current Hub response, not that no message ever existed. The 2026-09-05 audit's finding stands as recorded; the replacement outbound messages were sent on 2026-09-08 (below).
2026-09-08: 0a6c9d88-cf50-45d8-acdc-99a40747683e -> ops-warden, readable back
via GET /messages/?from_agent=key-cape. It puts both open questions to the
route and custody owner in one place:
- The
key-cape-oidc-loginproxy yields an OpenBao token andkeycape loginyields an issuer JWT — different credential types, different verifiers, different enforcement owners, so not interchangeable. Two admissible options are offered: keep the proxy and runkeycape loginalongside it for JWT consumers, or adapt the route's consumer contract to a JWT and then cut over. Option (a) is recommended until every consumer of the route is enumerated. - Qonto rotation for
rapp-qonto-keycape-client, quoting the five-step reviewed sequence fromdocs/native-authentication.mdand asking who executes the successor generation and CAS update, in which transport, under what authority. It records thatwarden route/warden planreturned no exact admitted lane and that a generic match is not being treated as authorization.
The KEY-WP-0009 handoff ops-warden reported missing has also now been delivered
(5bc58847-eed5-4164-bb0f-a0d8455e75b8), closing the KEY-WP-0014-T02 correction.
Task remains wait — no route was changed and no rotation command ships until
ops-warden answers (1) and an execution/rollback authority is admitted for (2).
2026-09-08, second pass — read ops-warden's own catalog rather than waiting for a reply, and it changes the shape of both questions.
registry/routing/catalog.yaml, lane key-cape-oidc-login: intended_owner was
already corrected to key-cape, and verified: asked-and-waiting since
2026-08-28. ops-warden is waiting on us, not the other way round. The lane is
also warden_executes: false with a wiki_ref into CredentialRouting.md — a
pointer, per their ADR-0001, not a code path with programmatic consumers. So the
consumer-contract risk behind question (1) is smaller than assumed: adding
keycape login alongside retires nothing, and the acceptance we owe them is a
statement of ownership, not a cutover.
Lane rapp-qonto-keycape-client: rotation.owner: key-cape,
automatable: true, and blocked_on reads "still no key-cape-native
exchange/rotation command", verified: source-read 2026-08-28. That blocker is
stale. keycape service-token (T03) shipped 2026-09-05 and is exactly the
native client_secret_basic exchange they record as absent. It does not clear
the lane, and should be narrowed rather than closed.
Shipped here: keycape verify-client, which is rotation step 4 and the denial
checks step 5 rests on — new exchange verified against deployed JWKS, predecessor
refused, excess scope refused, no value printed or written. See
docs/native-authentication.md, "Verifying a live registration". The identical
predecessor case is treated as a failure, because an unchanged secret is a
rotation that did not happen.
Steps 1-3 remain custody's and are deliberately not automated. Task stays wait
on exactly two answers: which option ops-warden takes for the login proxy, and
who executes the successor generation and CAS update under what authority. No
route changed.
Both catalog corrections were sent, receipts readable via
GET /messages/?from_agent=key-cape:
12f1bdfa-b240-414b-aa49-128a4d2acf20— accepts ownership of lanekey-cape-oidc-login, answering the WARDEN-WP-0033-T04 follow-up they have carried asasked-and-waitingsince 2026-08-28. States the boundary the lane'sfetch_commandcrosses: key-cape owns browser authentication and the identity token; thenetkingdomauth mount,role=<domain>mapping, token store and enforcement of the resulting OpenBao token are not ours. Supplies the source paths they need to move the lane toverified: source-read, and records our revised, lower estimate of the consumer risk — a pointer lane under ADR-0001 retires nothing whenkeycape loginruns alongside it — while asking them to correct us if the catalog understates its consumers.08d42f47-8e19-4976-98f5-dd259e7b1225— reports lanerapp-qonto-keycape-client's blocker as stale against its 2026-08-28 source-read, sincekeycape service-tokenis the nativeclient_secret_basicexchange it records as absent andkeycape verify-clientis its rotation step 3 verbatim. Asks them to narrow it to steps 1-2 rather than clear it, with proposed replacement text, and notesrotation.automatable: trueis now half-true and should say so precisely rather than be flipped either way.
Neither message asks for a route change and neither was one.
2026-09-08/09 — ops-warden answered both, and the second answer matters more than the question did.
Item 1 answered: option (a). Keep the proxy as-is and run keycape login
alongside it, "for your reason rather than out of caution -- they are different
credential types with different verifiers, so this is not a cutover deferred, it
is two things that were never one." They verified our pointer-layer reading
rather than accepting it, and added a fact we did not have: warden access
already excludes is_login lanes from raw-value streaming
(WARDEN-WP-0032-T05). Lane key-cape-oidc-login moved to owner-confirmed with
our boundary written in verbatim (commit c133004). It stays interim on purpose:
per their ADR-0003 a lane retires when a front door exists, not when ownership is
agreed, and by our own analysis keycape login is not that front door. The entry
now names the retirement condition instead of implying we owe one.
Item 2 answered: nobody owns it, admittedly. Steps 1-2 mutate custody on
platform workload paths, which is railiance-platform's, with secrets-engine exec
as the admitted transport. ops-warden declined to take the act on the grounds
that fronting the read lane is not authority to mutate — their ADR-0002 and
ADR-0005 — and routed it themselves rather than describing the route and leaving
it to us. Lane rapp-qonto-keycape-client narrowed exactly as asked and moved
source-read -> owner-confirmed, crediting the correction to us rather than to
a re-read they did not perform. Rotation step 3 now points at
keycape verify-client instead of restating its assertions.
They found a defect in their own front door because of our caution, and told
us. Running our need through warden plan returns verdict: autonomous,
lane_id: rapp-qonto-keycape-client, answered with three read transports — a
need containing generate and CAS-write inherits the verdict of the lane that
reads the same path, because warden plan has no read-versus-mutate intent at
all. autonomous is documented as the signal to proceed without the founder.
Raised as WARDEN-WP-0038. Their stated conclusion, which we should not soften: "A
counterparty declining to trust our output is not a control, and we are not going
to record it as one."
Standing instruction from them until WP-0038 lands, recorded here so it is not
lost in an inbox: treat a warden plan verdict on any need containing a write,
rotate or provision act as unreliable. That applies to this repository whenever
it plans a rotation.
On automatable, they did not do what we suggested and said so. We asked
them not to flip the boolean and to state the half-truth precisely; the schema has
one boolean whose only consumer is a future executable driver, and a driver told
true would attempt the custody steps. So it is false, failing safe, with the
precision moved into per-step notes. They offered to add a per-step field if we
would rather. We would not: a lane-level flag that fails safe plus per-step notes
is the right shape, and asking them to change a schema for our convenience would
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.
2026-09-10: the authority is answered. What remains is an artifact and a
decision. railiance-platform answered the routed question (7c7228ac, relayed
by ops-warden b14655b3):
- Who: the platform operator, attended, as a founder-attended session. Not ops-warden, not secrets-engine autonomously, and no unattended agent — railiance-platform named themselves in that exclusion too.
- Transport: the governed
openbao-platform-admin-loginlane, invoked only throughwarden access openbao-platform-admin-login --exec -- <command>with a unique metadata receipt path. The owner command is never run directly. - Authority:
founder_requiredattended OIDC via netkingdomrole=platform-admin. Nothing else in that repo carries write authority againstplatform/workloads/.
Missing: a rotation CCR. They will not raise one speculatively, and their
reason is sound — a two-custodian CAS rotation with a service restart is a
distinct version-guarded operation, not an implementation detail of an existing
lane. It must name the CAS precondition and expected version on both
custodians, sibling-field preservation on the KV write, the restart window,
bounded predecessor retention for rollback and denial checks, and the
reconcile-to-same-version step on failure. That last is step 5 of the sequence in
docs/native-authentication.md, so the reviewed sequence is what the CCR encodes.
Blocked on a decision that is not KeyCape's to take unilaterally. Their offer: "if key-cape wants the rotation scheduled, say so and I will prepare it under RPF-WP-0035 with both custodians in one window." ops-warden deliberately did not ask on our behalf — correctly, per their ADR-0003, since implying an owner request from a routing errand is quiet ownership. So T04 now waits on the repository owner deciding whether the Qonto rotation should happen, and when. Nothing indicates compromise; this is hygiene plus the first real exercise of rotation step 4, which is why it is a judgement call rather than an obvious yes.
Precedent offered, and it is the right one. The identical two-custodian shape
was exercised 2026-09-09 for the approval clients: attended founder session,
OpenBao KV custody plus a Kubernetes-delivered consumer copy, compatible pinned
rollout, protected recovery retained, and rollback that restored configuration and
image and detached delivery before a clean resume. Their
scripts/keycape_approval_custody.py (activate / resume-activate / verify) is the
shape a rapp-qonto rotation should follow rather than a bare bao kv patch — for
exactly the provider/consumer consistency reason this task gave when it declined
to ship a wrapper around a raw KV write. They endorsed that refusal unprompted.
And the warden plan defect is fixed (WARDEN-WP-0038, 2026-09-10). The Qonto
need now returns founder_required naming the write owner, and read transports
are no longer offered against a write need. ops-warden's closing line is worth
keeping: "Your caution was load-bearing and it should not have had to be."
If the rotation is scheduled, it is also the first opportunity to close the
real_predecessor_rotation_tested: false gap recorded above — verify-client
with -previous-secret-env is exactly step 4, and the predecessor would finally
be genuinely distinct.