Assistant: codex Assistant-Model: gpt-5.6-luna Assistant-Session: 01a07ff8-19d0-7820-b4d0-1353833cb7fc
194 lines
10 KiB
Markdown
194 lines
10 KiB
Markdown
# KeyCape approval-engine client custody admission
|
|
|
|
**2026-09-09 accepted:** verifier custody and the compatible image/config are live;
|
|
both service verifiers and a fresh existing-human OpenBao login passed. Both CCRs
|
|
are verified. [Live evidence](../evidence/2026-09-09-keycape-verifier-admission.json) supersedes the preparation
|
|
status below. Client-side reads and factory operating grants remain separate.
|
|
|
|
Answer to KEY-WP-0013-T02 (State Hub message
|
|
`278a3ebe-b529-49f6-bd1a-e3ebcf318260`, KeyCape packet
|
|
`key-cape: docs/approval-engine-provisioning-request.yaml`). Tracked here as
|
|
RPF-WP-0035-T05. Requests: [CCR-2026-0017](../../credential-change-requests/CCR-2026-0017-keycape-secrets-engine-approval-client.yaml),
|
|
[CCR-2026-0018](../../credential-change-requests/CCR-2026-0018-keycape-approval-engine-operator-client.yaml).
|
|
|
|
Both CCRs are **verified** following explicit user approval in both reviewer
|
|
roles. Version-1 custody, ESO delivery, the compatible KeyCape rollout and the
|
|
fresh existing-human login all passed. The contract below defines the completed
|
|
verifier-side scope; client-side reads remain a separate admission.
|
|
|
|
## a) Custody paths and field names
|
|
|
|
| Item | Proposed by KeyCape | Admitted | Change |
|
|
| --- | --- | --- | --- |
|
|
| Client 1 path | `platform/workloads/secrets-engine/approval-client` | same | confirmed |
|
|
| Client 2 path | `platform/workloads/approval-engine/operator-client` | same | confirmed |
|
|
| Field | `client_secret` | `CLIENT_SECRET` | **corrected** |
|
|
|
|
Both paths are correct as proposed. They match the
|
|
`platform/workloads/<workload>/<lane>` shape that `scripts/credential-change.py`
|
|
enforces for the `platform` mount and that every admitted platform lane uses
|
|
(`platform/workloads/core-hub/runtime`, `platform/workloads/user-engine/runtime`,
|
|
`platform/workloads/telemetry/grafana-admin`). Naming each path under the client's
|
|
own workload prefix rather than under KeyCape is also right: the credential
|
|
belongs to the client identity; KeyCape is its verifier, not its owner.
|
|
|
|
The field name is the one correction. KV field names on this mount are uppercase
|
|
(`PROVISIONER_TOKEN`, `CORE_HUB_API_TOKEN`, `NPM_AUTH_TOKEN`), and the CCR
|
|
validator rejects anything that does not match `^[A-Z][A-Z0-9_]*$`. So the
|
|
OpenBao field is `CLIENT_SECRET`. This does not change the Kubernetes Secret key
|
|
or the environment name — see below.
|
|
|
|
## b) Kubernetes delivery references
|
|
|
|
Confirmed as proposed, with the mapping made explicit:
|
|
|
|
| | Client 1 | Client 2 |
|
|
| --- | --- | --- |
|
|
| Secret | `sso/keycape-secrets-engine-approval-client` | `sso/keycape-approval-engine-operator-client` |
|
|
| Secret key | `client-secret` | `client-secret` |
|
|
| KeyCape env | `KEYCAPE_SECRETS_ENGINE_APPROVAL_CLIENT_SECRET` | `KEYCAPE_APPROVAL_ENGINE_OPERATOR_CLIENT_SECRET` |
|
|
| ClusterSecretStore | `openbao-keycape-secrets-engine-approval` | `openbao-keycape-approval-engine-operator` |
|
|
| OpenBao policy | `workload-kv-read-keycape-secrets-engine-approval` | `workload-kv-read-keycape-approval-engine-operator` |
|
|
| Kubernetes auth role | `external-secrets-keycape-secrets-engine-approval` | `external-secrets-keycape-approval-engine-operator` |
|
|
|
|
`OpenBao CLIENT_SECRET` → `ExternalSecret secretKey client-secret` →
|
|
`secretKeyRef` → env. That shape is the one already running in `sso`: the live
|
|
`keycape` pod on `main-153258b` resolves
|
|
`KEYCAPE_RAPP_QONTO_CLIENT_SECRET` from `secretKeyRef{name:
|
|
keycape-rapp-qonto-client, key: client-secret}`. Read-only observation
|
|
2026-09-08, and it confirms KeyCape's own statement that the image carries only
|
|
that one client reference.
|
|
|
|
Both delivery manifests are applied and live-verified:
|
|
`argocd/platform-addons/openbao-secretstore/openbao-keycape-approval-clients.clustersecretstore.yaml`
|
|
and `keycape-approval-clients.externalsecrets.yaml`.
|
|
|
|
Three deliberate choices, so they are not read as accidents:
|
|
|
|
- **Two stores, two roles, two policies.** The scope sets differ — the operator
|
|
client can create, approve, revoke and supersede; the secrets-engine client can
|
|
read and consume. Separate identities mean either can be revoked without
|
|
touching the other. A shared store would erase that boundary.
|
|
- **Kubernetes auth, not AppRole.** The neighbouring `sso` store
|
|
(`openbao-sso-user-engine-runtime`) and `openbao-rapp-qonto` use AppRole for
|
|
cutover reasons. New same-cluster lanes use Kubernetes auth against
|
|
`external-secrets/external-secrets`, as `openbao-core-hub-runtime` does, so
|
|
there is no AppRole secret to hold in custody.
|
|
- **Verifier side only.** This admits the copy KeyCape needs to verify presented
|
|
credentials. It does **not** admit the client-side lane by which secrets-engine
|
|
or the approval-engine operator would read its own copy. That is a separate
|
|
request with its own consumer gate, and no part of it is implied here.
|
|
|
|
`creationPolicy: Owner` means the ExternalSecret creates the Secret. Do not
|
|
pre-create either Secret by hand.
|
|
|
|
## c) Authority for the attended first provision
|
|
|
|
Your reading is right, and your refusal to act on the `warden plan` result was
|
|
also right: a generic database-lane match is not authorization for this path.
|
|
|
|
The authority is the governed **`openbao-platform-admin-login`** lane —
|
|
`founder_required`, attended OIDC through `bao login -method=oidc -path=netkingdom
|
|
role=platform-admin`, invoked as:
|
|
|
|
```bash
|
|
warden access openbao-platform-admin-login --exec -- <command>
|
|
```
|
|
|
|
with a unique `--receipt` path. Nothing else in this repo carries write authority
|
|
against `platform/workloads/`. Two constraints that come with it:
|
|
|
|
- It is a **founder-attended** session. No agent, including this one, can execute
|
|
it unattended, and neither KeyCape nor railiance-platform can self-authorize it.
|
|
- The owner command is never run directly, only through the `warden access
|
|
--exec` envelope, so output containment applies (RPF-WP-0017). No value in chat,
|
|
command arguments, shell history or logs; receipts carry metadata only.
|
|
|
|
Custody writes are seeded with compare-and-set zero on first version.
|
|
|
|
## d) Rollout window
|
|
|
|
Agreed in shape: **one attended session**, everything below in order, no partial
|
|
landing. Registrations and the reading build deploy together because a KeyCape
|
|
image that reads an absent env fails closed, and a delivered secret with no build
|
|
to read it is custody without a consumer.
|
|
|
|
Prerequisites before the window opens:
|
|
|
|
1. Both CCRs approved by platform-operator and the KeyCape owner.
|
|
2. KeyCape image that reads both environment names is **built and pinned, not
|
|
deployed**. `main-153258b` is not that image.
|
|
3. Founder available for the attended OpenBao session.
|
|
4. **The actual signed issuer is verified as `https://auth.coulomb.social`.**
|
|
The admitted probe passed signature/audience/time/nonce verification on
|
|
2026-09-08 at 21:44:44 UTC, exited 0 and removed every temporary resource.
|
|
[Receipt](../evidence/2026-09-08-keycape-upstream-issuer-proof.json).
|
|
NetKingdom completed the exact `authelia.issuer` pin on 2026-09-09,
|
|
with unchanged signing key and unrelated configuration; Secret revision
|
|
`58713343`. [Pin receipt](../evidence/2026-09-09-keycape-upstream-issuer-pin.json).
|
|
This prerequisite is closed. Keep the existing-human-login regression in
|
|
the separately admitted image/configuration rollout checks.
|
|
|
|
In-window order:
|
|
|
|
1. Apply the two policies and the two Kubernetes auth roles (attended, via the
|
|
authority in (c)).
|
|
2. Seed both KV paths, CAS=0, with the client secrets KeyCape mints. Record
|
|
metadata-only receipts.
|
|
3. Apply the two ClusterSecretStores; confirm both reach `Valid`.
|
|
4. Apply the two ExternalSecrets; confirm `SecretSynced` and that both Secrets
|
|
exist with key `client-secret`. No value is printed at any point.
|
|
5. Roll out the KeyCape image.
|
|
6. KeyCape runs its verification: live JWKS signature, exact claim bindings
|
|
(subject, tenant `tenant:platform`, audience `approval-engine`, 15m lifetime),
|
|
and denial of excess scopes — operator `consume` and human `consume` — without
|
|
logging any value or token.
|
|
7. Platform runs its negative checks: cross-path read denied, wrong service
|
|
account denied, out-of-namespace store use denied, parent listing denied.
|
|
8. Both sides record evidence into the CCRs and a receipt into the workplan.
|
|
|
|
Rollback: revert the KeyCape image to `main-153258b`, delete the two
|
|
ExternalSecrets, detach the policies from the roles. The KV versions are retained
|
|
until KeyCape confirms whether the registrations stay; if they are abandoned,
|
|
KeyCape disables the registrations first and platform then destroys the versions.
|
|
|
|
The attended window completed on **2026-09-09** under the user's explicit
|
|
approval. Failed checks restored the compatible config/image and detached ESO
|
|
delivery. The final resume preserved both initial KV versions and passed all
|
|
service checks plus a fresh existing-human OpenBao login.
|
|
|
|
## What is not admitted
|
|
|
|
- No client-side (secrets-engine / approval-engine) read lane.
|
|
- No adoption of the hand-created `sso/keycape-rapp-qonto-client` Secret into ESO.
|
|
- No change to the separate `codex-railiance-platform` JWT issuance contract
|
|
(KEY-WP-0009-T04) or to RPF-WP-0035-T02's service-JWT lane.
|
|
- No approval for `approval:consume` on the operator client, now or by later
|
|
scope edit. Adding it is a new lane decision.
|
|
|
|
|
|
## Attended authority return — 2026-09-08
|
|
|
|
The metadata-only first-provision preflight is now observed on the exact six
|
|
policy/role/data paths: create/update capabilities are present. The successful
|
|
session self-revoked and its helper was removed. This settles the availability
|
|
of the named operator authority, not the CCR approvals or the upstream issuer
|
|
precondition. See
|
|
[the receipt](../evidence/2026-09-08-keycape-approval-custody-preflight.json).
|
|
|
|
For a repeat after a role/policy change, use the existing contained WSL wrapper:
|
|
|
|
```bash
|
|
python3 scripts/openbao-attended-exec.py -- /usr/bin/python3 \
|
|
scripts/keycape-approval-custody-preflight.py --receipt /tmp/<new-private-receipt>.json
|
|
```
|
|
|
|
Use an absolute path to the owner command if invoking from another directory.
|
|
It emits no output or values; inspect its allowlisted capability metadata only
|
|
after Warden has completed revocation and cleanup. No custody activation or
|
|
approval is performed by this preflight.
|
|
|
|
|
|
2026-09-09: the live issuer pin is complete. The next review is captured in
|
|
[keycape-approval-clients-review.md](keycape-approval-clients-review.md), with
|
|
the resolved Hub decisions, both recorded reviewer roles and completed live verification.
|