feat: verify live KeyCape custody and preserve versions on resume
Assistant: codex Assistant-Model: gpt-5.6-luna Assistant-Session: 01a07ff8-19d0-7820-b4d0-1353833cb7fc
This commit is contained in:
parent
b7861de20e
commit
c6dc4286e2
8 changed files with 382 additions and 57 deletions
|
|
@ -3,9 +3,9 @@ kind: credential-change-request
|
||||||
schema_version: 1
|
schema_version: 1
|
||||||
request_type: workload-kv-read
|
request_type: workload-kv-read
|
||||||
title: KeyCape verifier custody for the secrets-engine-approval confidential client
|
title: KeyCape verifier custody for the secrets-engine-approval confidential client
|
||||||
status: approved
|
status: verified
|
||||||
created: '2026-09-08'
|
created: '2026-09-08'
|
||||||
updated: '2026-09-08'
|
updated: '2026-09-09'
|
||||||
requester:
|
requester:
|
||||||
agent: claude
|
agent: claude
|
||||||
reason: "KEY-WP-0013-T02 (State Hub message 278a3ebe-b529-49f6-bd1a-e3ebcf318260)\
|
reason: "KEY-WP-0013-T02 (State Hub message 278a3ebe-b529-49f6-bd1a-e3ebcf318260)\
|
||||||
|
|
@ -140,7 +140,29 @@ verification:
|
||||||
- Policy, Kubernetes auth role and ClusterSecretStore applied before the ExternalSecret;
|
- Policy, Kubernetes auth role and ClusterSecretStore applied before the ExternalSecret;
|
||||||
sync confirmed before the KeyCape image is rolled out.
|
sync confirmed before the KeyCape image is rolled out.
|
||||||
- Positive and negative results recorded with non-secret request ids or timestamps.
|
- Positive and negative results recorded with non-secret request ids or timestamps.
|
||||||
evidence: []
|
evidence:
|
||||||
|
- at: '2026-09-09T00:14:27+00:00'
|
||||||
|
actor: the-custodian (codex)
|
||||||
|
kind: verifier_custody_and_rollout
|
||||||
|
result: passed
|
||||||
|
details:
|
||||||
|
- Named user approval recorded for platform-operator and key-cape-owner on 2026-09-09.
|
||||||
|
- Initial CAS=0 custody created version 1. Verified rollback/resume retained the
|
||||||
|
same values and versions.
|
||||||
|
- Both stores Valid and ExternalSecrets SecretSynced; exact native read, sibling/listing
|
||||||
|
and wrong service account/namespace/store-use denials passed; reader revocation
|
||||||
|
and coding-agent deny precedence passed.
|
||||||
|
- Pinned image 7ff54c54e63e has one ready replica; protected previous config/image
|
||||||
|
retained and signing key unchanged.
|
||||||
|
- Both clients passed live JWKS and exact audience/subject/tenant/role/scope/900-second
|
||||||
|
lifetime checks, excess-scope and wrong-secret denials; human client consume
|
||||||
|
denied.
|
||||||
|
- Native verifier from pinned image ran in the attended owner process with memory-only
|
||||||
|
credentials. Pod network policy remains unchanged. Future iat bound is the existing
|
||||||
|
30-second contract; expiry has zero leeway.
|
||||||
|
- Fresh post-rollout OpenBao OIDC login succeeded and self-revoked. No client-side
|
||||||
|
fetch front door or factory spending was admitted.
|
||||||
|
- 'Receipt: docs/evidence/2026-09-09-keycape-verifier-admission.json'
|
||||||
lifecycle:
|
lifecycle:
|
||||||
deactivate: KeyCape disables the secrets-engine-approval registration; platform
|
deactivate: KeyCape disables the secrets-engine-approval registration; platform
|
||||||
detaches the policy from role external-secrets-keycape-secrets-engine-approval
|
detaches the policy from role external-secrets-keycape-secrets-engine-approval
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,9 @@ kind: credential-change-request
|
||||||
schema_version: 1
|
schema_version: 1
|
||||||
request_type: workload-kv-read
|
request_type: workload-kv-read
|
||||||
title: KeyCape verifier custody for the approval-engine-operator confidential client
|
title: KeyCape verifier custody for the approval-engine-operator confidential client
|
||||||
status: approved
|
status: verified
|
||||||
created: '2026-09-08'
|
created: '2026-09-08'
|
||||||
updated: '2026-09-08'
|
updated: '2026-09-09'
|
||||||
requester:
|
requester:
|
||||||
agent: claude
|
agent: claude
|
||||||
reason: 'Second of the two registrations in KEY-WP-0013-T02 (State Hub message 278a3ebe-b529-49f6-bd1a-e3ebcf318260):
|
reason: 'Second of the two registrations in KEY-WP-0013-T02 (State Hub message 278a3ebe-b529-49f6-bd1a-e3ebcf318260):
|
||||||
|
|
@ -122,7 +122,29 @@ verification:
|
||||||
- Policy, Kubernetes auth role and ClusterSecretStore applied before the ExternalSecret;
|
- Policy, Kubernetes auth role and ClusterSecretStore applied before the ExternalSecret;
|
||||||
sync confirmed before the KeyCape rollout.
|
sync confirmed before the KeyCape rollout.
|
||||||
- Positive and negative results recorded with non-secret request ids or timestamps.
|
- Positive and negative results recorded with non-secret request ids or timestamps.
|
||||||
evidence: []
|
evidence:
|
||||||
|
- at: '2026-09-09T00:14:27+00:00'
|
||||||
|
actor: the-custodian (codex)
|
||||||
|
kind: verifier_custody_and_rollout
|
||||||
|
result: passed
|
||||||
|
details:
|
||||||
|
- Named user approval recorded for platform-operator and key-cape-owner on 2026-09-09.
|
||||||
|
- Initial CAS=0 custody created version 1. Verified rollback/resume retained the
|
||||||
|
same values and versions.
|
||||||
|
- Both stores Valid and ExternalSecrets SecretSynced; exact native read, sibling/listing
|
||||||
|
and wrong service account/namespace/store-use denials passed; reader revocation
|
||||||
|
and coding-agent deny precedence passed.
|
||||||
|
- Pinned image 7ff54c54e63e has one ready replica; protected previous config/image
|
||||||
|
retained and signing key unchanged.
|
||||||
|
- Both clients passed live JWKS and exact audience/subject/tenant/role/scope/900-second
|
||||||
|
lifetime checks, excess-scope and wrong-secret denials; human client consume
|
||||||
|
denied.
|
||||||
|
- Native verifier from pinned image ran in the attended owner process with memory-only
|
||||||
|
credentials. Pod network policy remains unchanged. Future iat bound is the existing
|
||||||
|
30-second contract; expiry has zero leeway.
|
||||||
|
- Fresh post-rollout OpenBao OIDC login succeeded and self-revoked. No client-side
|
||||||
|
fetch front door or factory spending was admitted.
|
||||||
|
- 'Receipt: docs/evidence/2026-09-09-keycape-verifier-admission.json'
|
||||||
lifecycle:
|
lifecycle:
|
||||||
deactivate: KeyCape disables the approval-engine-operator registration; platform
|
deactivate: KeyCape disables the approval-engine-operator registration; platform
|
||||||
detaches the policy from role external-secrets-keycape-approval-engine-operator
|
detaches the policy from role external-secrets-keycape-approval-engine-operator
|
||||||
|
|
|
||||||
|
|
@ -1,45 +1,28 @@
|
||||||
# KeyCape approval-client custody: review packet
|
# KeyCape approval-client custody: accepted verifier delivery
|
||||||
|
|
||||||
Prepared 2026-09-09 by the-custodian. Both requests are **approved** by the user as platform operator and KeyCape
|
Both CCR-2026-0017 and CCR-2026-0018 have the user's explicit approval as platform
|
||||||
owner, explicitly recorded on 2026-09-09. The admission receipt is
|
operator and KeyCape owner. Both are now **verified**. Their existing decision
|
||||||
[here](../evidence/2026-09-09-keycape-approval-admission.json). This is the review record for
|
IDs remain resolved; no renewed review is needed for this completed scope.
|
||||||
RPF-WP-0035-T05, consumed by HFACT-WP-0001-T03.
|
|
||||||
|
|
||||||
| Request | Secret path and field | Client authority | Resolved decision |
|
[Combined live receipt](../evidence/2026-09-09-keycape-verifier-admission.json) records version-1 custody, both ESO
|
||||||
| --- | --- | --- | --- |
|
stores and Secrets, exact native scope/auth/namespace denials, reader revocation,
|
||||||
| [CCR-2026-0017](../../credential-change-requests/CCR-2026-0017-keycape-secrets-engine-approval-client.yaml) | `platform/workloads/secrets-engine/approval-client`, `CLIENT_SECRET` | `approval:read`, `approval:consume` | `b533a271-b704-4c5c-98a2-9a5951aadfb6` |
|
compatible pinned KeyCape deployment and both service-client verification runs.
|
||||||
| [CCR-2026-0018](../../credential-change-requests/CCR-2026-0018-keycape-approval-engine-operator-client.yaml) | `platform/workloads/approval-engine/operator-client`, `CLIENT_SECRET` | create/read/approve/revoke/supersede/observe/emit; no consume | `efa90517-0cae-4eb6-a68d-5b0489c84d65` |
|
The existing human OpenBao login passed before and after the rollout.
|
||||||
|
|
||||||
Both named **platform-operator and key-cape-owner** reviews are approved. Each has its own
|
The two Warden fetch selectors remain unresolved: these CCRs authorize verifier
|
||||||
exact-path policy, Kubernetes auth role and ClusterSecretStore, bound to
|
copies only. Client-side reads, audit custody, natural JWT expiry, real predecessor
|
||||||
`external-secrets/external-secrets`, limited to `sso`. ESO owns the resulting
|
rotation and factory operating/spend admission remain with the existing owner
|
||||||
Secret; authentication tokens have a 15-minute TTL. Client secrets require
|
records. See [the owner sequence](keycape-approval-clients.md).
|
||||||
explicit rotation or registration disablement; token expiry does not revoke them.
|
|
||||||
|
|
||||||
The approved requests authorize verifier-side custody only. Client-side
|
The exercised procedure is `scripts/keycape_approval_custody.py`: `activate` for
|
||||||
retrieval, audit sender/receiver custody, operator `approval:consume`, adoption
|
first provision, `resume-activate` only with a completed rollback receipt, and
|
||||||
of the Qonto Secret and factory spending remain outside these two requests.
|
`verify` for existing custody. The selected kubeconfig must identify railiance01;
|
||||||
|
on this workstation it is `/home/worsch/.kube/config-railiance01`. Use the
|
||||||
|
Warden attended wrapper, a unique metadata receipt and protected recovery file.
|
||||||
|
The pinned native verifier is extracted from image digest `7ff54c54e63e...` and
|
||||||
|
hash-checked; its child environment carries credentials only during execution.
|
||||||
|
No Kubernetes egress policy changes are needed.
|
||||||
|
|
||||||
Technical review completed: both CCRs validate, their generated policies match
|
Validation: eight local OpenBao checks, ten config/rollback/clock checks, 53
|
||||||
the two source HCL files, and the delivery manifests map exactly to the declared
|
existing credential-change tests, plus a disposable pinned-image HTTPS exercise
|
||||||
paths and fields. The signed upstream issuer is verified; NetKingdom's exact live
|
that runs the exact native verifier with synthetic keys and client credentials.
|
||||||
pin is independently read back at Secret revision `58713343`. The compatible
|
|
||||||
KeyCape image and rollout patch are prepared. These checks are evidence for the
|
|
||||||
review, not substitutes for either named approval.
|
|
||||||
|
|
||||||
Execution still follows the [owner rollout sequence](keycape-approval-clients.md)
|
|
||||||
through the founder-attended Warden/OpenBao envelope: policy/roles, CAS=0 initial
|
|
||||||
custody, Valid stores, SecretSynced delivery, compatible config/image replacement,
|
|
||||||
then positive and negative acceptance and metadata-only receipts. The contained owner command is now `scripts/keycape_approval_custody.py activate`.
|
|
||||||
It preflights both config and deployment changes, seeds with CAS=0, verifies native
|
|
||||||
and namespace boundaries, then calls NetKingdom's contained compatible rollout.
|
|
||||||
A failure restores the prior config/image pair before detaching verifier delivery;
|
|
||||||
initial KV versions remain in custody. The four new exact data/metadata deny
|
|
||||||
stanzas extend the coding-agent boundary without changing any grant.
|
|
||||||
|
|
||||||
Validation: six checks against a disposable local OpenBao, eight config/rollback
|
|
||||||
tests, and real HTTP signature/claim/scope acceptance against the pinned KeyCape
|
|
||||||
image with synthetic keys. Production in-pod CLI and existing-human-login checks
|
|
||||||
remain part of the live window. The image exercise omits the in-pod CLI explicitly.
|
|
||||||
Use a unique private receipt and protected owner recovery path; a fresh attended
|
|
||||||
login after the rollout establishes the existing human path on the new build.
|
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,20 @@
|
||||||
# KeyCape approval-engine client custody admission
|
# 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
|
Answer to KEY-WP-0013-T02 (State Hub message
|
||||||
`278a3ebe-b529-49f6-bd1a-e3ebcf318260`, KeyCape packet
|
`278a3ebe-b529-49f6-bd1a-e3ebcf318260`, KeyCape packet
|
||||||
`key-cape: docs/approval-engine-provisioning-request.yaml`). Tracked here as
|
`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),
|
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).
|
[CCR-2026-0018](../../credential-change-requests/CCR-2026-0018-keycape-approval-engine-operator-client.yaml).
|
||||||
|
|
||||||
Nothing below is an activation. Both CCRs are `proposed`; no value has been
|
Both CCRs are **verified** following explicit user approval in both reviewer
|
||||||
generated, no KV version written, no manifest applied. Source preparation is not
|
roles. Version-1 custody, ESO delivery, the compatible KeyCape rollout and the
|
||||||
live completion.
|
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
|
## a) Custody paths and field names
|
||||||
|
|
||||||
|
|
@ -53,7 +59,7 @@ 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
|
2026-09-08, and it confirms KeyCape's own statement that the image carries only
|
||||||
that one client reference.
|
that one client reference.
|
||||||
|
|
||||||
Manifests are written and client-validated but unapplied:
|
Both delivery manifests are applied and live-verified:
|
||||||
`argocd/platform-addons/openbao-secretstore/openbao-keycape-approval-clients.clustersecretstore.yaml`
|
`argocd/platform-addons/openbao-secretstore/openbao-keycape-approval-clients.clustersecretstore.yaml`
|
||||||
and `keycape-approval-clients.externalsecrets.yaml`.
|
and `keycape-approval-clients.externalsecrets.yaml`.
|
||||||
|
|
||||||
|
|
@ -146,9 +152,10 @@ ExternalSecrets, detach the policies from the roles. The KV versions are retaine
|
||||||
until KeyCape confirms whether the registrations stay; if they are abandoned,
|
until KeyCape confirms whether the registrations stay; if they are abandoned,
|
||||||
KeyCape disables the registrations first and platform then destroys the versions.
|
KeyCape disables the registrations first and platform then destroys the versions.
|
||||||
|
|
||||||
**Date is not set here.** It depends on the founder's availability, which is not
|
The attended window completed on **2026-09-09** under the user's explicit
|
||||||
mine to schedule. Propose a slot from 2026-09-10 and I will confirm the
|
approval. Failed checks restored the compatible config/image and detached ESO
|
||||||
platform side; the window needs roughly 60–90 minutes with both owners present.
|
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
|
## What is not admitted
|
||||||
|
|
||||||
|
|
@ -184,4 +191,4 @@ approval is performed by this preflight.
|
||||||
|
|
||||||
2026-09-09: the live issuer pin is complete. The next review is captured in
|
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
|
[keycape-approval-clients-review.md](keycape-approval-clients-review.md), with
|
||||||
one pending Hub decision per existing CCR and both required reviewer roles.
|
the resolved Hub decisions, both recorded reviewer roles and completed live verification.
|
||||||
|
|
|
||||||
220
docs/evidence/2026-09-09-keycape-verifier-admission.json
Normal file
220
docs/evidence/2026-09-09-keycape-verifier-admission.json
Normal file
|
|
@ -0,0 +1,220 @@
|
||||||
|
{
|
||||||
|
"schema": "helixforge.keycape-verifier-admission.v1",
|
||||||
|
"recorded_at": "2026-09-09T00:14:27.155338+00:00",
|
||||||
|
"approval": {
|
||||||
|
"schema": "railiance.keycape-custody-user-approval.v1",
|
||||||
|
"recorded_at": "2026-09-08T23:05:19.886216+00:00",
|
||||||
|
"user_response": "I approve, go on.",
|
||||||
|
"approved_question": "Do you approve CCR-2026-0017 and CCR-2026-0018, as platform operator and KeyCape owner, for the verifier-side credential delivery described in the review packet?",
|
||||||
|
"review_packet_revision": "52b24eab9a8aff3396e71b2296d0240a44f3b0db",
|
||||||
|
"roles": [
|
||||||
|
"platform-operator",
|
||||||
|
"key-cape-owner"
|
||||||
|
],
|
||||||
|
"scope": "Two verifier-side KeyCape client credential custody requests, including their governed attended provisioning and compatible rollout sequence.",
|
||||||
|
"client_side_read_authorized": false,
|
||||||
|
"factory_spending_authorized": false,
|
||||||
|
"requests": [
|
||||||
|
{
|
||||||
|
"id": "CCR-2026-0017",
|
||||||
|
"decision_id": "b533a271-b704-4c5c-98a2-9a5951aadfb6",
|
||||||
|
"status": "approved",
|
||||||
|
"decision_status": "resolved",
|
||||||
|
"reviewed_roles": [
|
||||||
|
"platform-operator",
|
||||||
|
"key-cape-owner"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "CCR-2026-0018",
|
||||||
|
"decision_id": "efa90517-0cae-4eb6-a68d-5b0489c84d65",
|
||||||
|
"status": "approved",
|
||||||
|
"decision_status": "resolved",
|
||||||
|
"reviewed_roles": [
|
||||||
|
"platform-operator",
|
||||||
|
"key-cape-owner"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"activation": {
|
||||||
|
"schema": "platform.keycape-approval-custody.v1",
|
||||||
|
"status": "custody_and_service_acceptance_passed_pending_fresh_human_login",
|
||||||
|
"lanes": [
|
||||||
|
{
|
||||||
|
"ccr": "CCR-2026-0017",
|
||||||
|
"source_sha256": "dc2bbd86e213edecc2f2f91a99d0f51136e9a0853a90e55f65dda2b942305416",
|
||||||
|
"custody_seeded": false,
|
||||||
|
"existing_version_reused": true,
|
||||||
|
"kv_version": 1,
|
||||||
|
"initial_request_id": "94c98373-33fa-591d-c201-d36dddf9879b",
|
||||||
|
"policy_applied": false,
|
||||||
|
"role_applied": true,
|
||||||
|
"native_reader_verified": true,
|
||||||
|
"cross_path_denied": true,
|
||||||
|
"parent_listing_denied": true,
|
||||||
|
"auth_ttl": 900,
|
||||||
|
"reader_revocation_verified": true,
|
||||||
|
"wrong_service_account_denied": true,
|
||||||
|
"coding_agent_deny_wins": true,
|
||||||
|
"store_ready": true,
|
||||||
|
"external_secret_ready": true,
|
||||||
|
"delivery_matches": true,
|
||||||
|
"secret_uid": "eaa28bdf-04af-4e4e-a1fc-fe395c7689a7",
|
||||||
|
"secret_resource_version": "58747058",
|
||||||
|
"wrong_namespace_denied": true,
|
||||||
|
"outside_namespace_store_denied": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ccr": "CCR-2026-0018",
|
||||||
|
"source_sha256": "99148133ddf1eb15dce3c03862b51dc001818ca3121045035a0b0e5897c55dad",
|
||||||
|
"custody_seeded": false,
|
||||||
|
"existing_version_reused": true,
|
||||||
|
"kv_version": 1,
|
||||||
|
"initial_request_id": "a62ee5c5-b5ba-b7dc-0f51-e6fd8a6dde32",
|
||||||
|
"policy_applied": false,
|
||||||
|
"role_applied": true,
|
||||||
|
"native_reader_verified": true,
|
||||||
|
"cross_path_denied": true,
|
||||||
|
"parent_listing_denied": true,
|
||||||
|
"auth_ttl": 900,
|
||||||
|
"reader_revocation_verified": true,
|
||||||
|
"wrong_service_account_denied": true,
|
||||||
|
"coding_agent_deny_wins": true,
|
||||||
|
"store_ready": true,
|
||||||
|
"external_secret_ready": true,
|
||||||
|
"delivery_matches": true,
|
||||||
|
"secret_uid": "599a61a7-8244-4618-8c44-6473195bbe4e",
|
||||||
|
"secret_resource_version": "58747062",
|
||||||
|
"wrong_namespace_denied": true,
|
||||||
|
"outside_namespace_store_denied": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"started_at": "2026-09-09T00:09:28.852280+00:00",
|
||||||
|
"credential_values_emitted": false,
|
||||||
|
"client_side_read_admitted": false,
|
||||||
|
"keycape_rollout_completed": true,
|
||||||
|
"issuer_pin_revision": "58746187",
|
||||||
|
"phase": "awaiting_fresh_human_login",
|
||||||
|
"custody_versions_unchanged": true,
|
||||||
|
"namespace_probe_cleanup_requested": true,
|
||||||
|
"keycape": {
|
||||||
|
"existing_human_login_before": true,
|
||||||
|
"protected_recovery_retained": true,
|
||||||
|
"deployment_uid": "99ddd83c-cb3f-4847-bcf8-35f1aa87627f",
|
||||||
|
"generation": 38,
|
||||||
|
"pod_uid": "8d62e972-d466-413e-a2e4-08b9a08016bc",
|
||||||
|
"single_ready_replica": true,
|
||||||
|
"acceptance_phase": "passed",
|
||||||
|
"clients": [
|
||||||
|
{
|
||||||
|
"client_id": "secrets-engine-approval",
|
||||||
|
"live_jwks_signature_verified": true,
|
||||||
|
"exact_claims_verified": true,
|
||||||
|
"lifetime_seconds": 900,
|
||||||
|
"maximum_future_iat_seconds": 30,
|
||||||
|
"expiry_leeway_seconds": 0,
|
||||||
|
"excess_scope_denied": true,
|
||||||
|
"wrong_secret_denied": true,
|
||||||
|
"pinned_artifact_verifier_passed": true,
|
||||||
|
"verifier_location": "attended owner process",
|
||||||
|
"verifier_sha256": "4bf93bbe9afe0bf2e21d51c03373a7e2b4be6864586bcc2d472eeb9abc913d92",
|
||||||
|
"real_predecessor_rotation_tested": false,
|
||||||
|
"observed_wall_clock_expiry": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"client_id": "approval-engine-operator",
|
||||||
|
"live_jwks_signature_verified": true,
|
||||||
|
"exact_claims_verified": true,
|
||||||
|
"lifetime_seconds": 900,
|
||||||
|
"maximum_future_iat_seconds": 30,
|
||||||
|
"expiry_leeway_seconds": 0,
|
||||||
|
"excess_scope_denied": true,
|
||||||
|
"wrong_secret_denied": true,
|
||||||
|
"pinned_artifact_verifier_passed": true,
|
||||||
|
"verifier_location": "attended owner process",
|
||||||
|
"verifier_sha256": "4bf93bbe9afe0bf2e21d51c03373a7e2b4be6864586bcc2d472eeb9abc913d92",
|
||||||
|
"real_predecessor_rotation_tested": false,
|
||||||
|
"observed_wall_clock_expiry": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"acceptance_client": "approval-engine-operator",
|
||||||
|
"human_client_consume_denied": true,
|
||||||
|
"status": "service_acceptance_passed_pending_fresh_human_login",
|
||||||
|
"image": "forgejo.coulomb.social/coulomb/key-cape@sha256:7ff54c54e63ee172ae9e6e7fd2da96e427352f712343d74626ee6fe0f6f82611",
|
||||||
|
"config_resource_version": "58747126",
|
||||||
|
"signing_key_unchanged": true,
|
||||||
|
"unrelated_config_bytes_preserved": true,
|
||||||
|
"existing_human_login_after": false
|
||||||
|
},
|
||||||
|
"finished_at": "2026-09-09T00:10:09.099481+00:00"
|
||||||
|
},
|
||||||
|
"post_rollout_login": {
|
||||||
|
"schema": "netkingdom.keycape-approval-rollout.v1",
|
||||||
|
"status": "service_and_existing_human_login_acceptance_passed",
|
||||||
|
"values_emitted": false,
|
||||||
|
"deployment_uid": "99ddd83c-cb3f-4847-bcf8-35f1aa87627f",
|
||||||
|
"generation": 38,
|
||||||
|
"pod_uid": "8d62e972-d466-413e-a2e4-08b9a08016bc",
|
||||||
|
"single_ready_replica": true,
|
||||||
|
"acceptance_phase": "passed",
|
||||||
|
"clients": [
|
||||||
|
{
|
||||||
|
"client_id": "secrets-engine-approval",
|
||||||
|
"live_jwks_signature_verified": true,
|
||||||
|
"exact_claims_verified": true,
|
||||||
|
"lifetime_seconds": 900,
|
||||||
|
"maximum_future_iat_seconds": 30,
|
||||||
|
"expiry_leeway_seconds": 0,
|
||||||
|
"excess_scope_denied": true,
|
||||||
|
"wrong_secret_denied": true,
|
||||||
|
"pinned_artifact_verifier_passed": true,
|
||||||
|
"verifier_location": "attended owner process",
|
||||||
|
"verifier_sha256": "4bf93bbe9afe0bf2e21d51c03373a7e2b4be6864586bcc2d472eeb9abc913d92",
|
||||||
|
"real_predecessor_rotation_tested": false,
|
||||||
|
"observed_wall_clock_expiry": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"client_id": "approval-engine-operator",
|
||||||
|
"live_jwks_signature_verified": true,
|
||||||
|
"exact_claims_verified": true,
|
||||||
|
"lifetime_seconds": 900,
|
||||||
|
"maximum_future_iat_seconds": 30,
|
||||||
|
"expiry_leeway_seconds": 0,
|
||||||
|
"excess_scope_denied": true,
|
||||||
|
"wrong_secret_denied": true,
|
||||||
|
"pinned_artifact_verifier_passed": true,
|
||||||
|
"verifier_location": "attended owner process",
|
||||||
|
"verifier_sha256": "4bf93bbe9afe0bf2e21d51c03373a7e2b4be6864586bcc2d472eeb9abc913d92",
|
||||||
|
"real_predecessor_rotation_tested": false,
|
||||||
|
"observed_wall_clock_expiry": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"acceptance_client": "approval-engine-operator",
|
||||||
|
"human_client_consume_denied": true,
|
||||||
|
"existing_human_login_after": true,
|
||||||
|
"receipt_written_at": "2026-09-09T00:11:59.113549+00:00"
|
||||||
|
},
|
||||||
|
"attended_envelope": {
|
||||||
|
"all_attempted_sessions_revoked": true,
|
||||||
|
"successful_activation_exit_code": 0,
|
||||||
|
"post_rollout_login_exit_code": 0
|
||||||
|
},
|
||||||
|
"validation": {
|
||||||
|
"local_openbao_tests": 8,
|
||||||
|
"configuration_and_recovery_tests": 10,
|
||||||
|
"credential_change_tests": 53,
|
||||||
|
"pinned_image_synthetic_https_and_native_verifier": "passed"
|
||||||
|
},
|
||||||
|
"failed_attempt_receipts": [
|
||||||
|
"net-kingdom:docs/evidence/2026-09-09-keycape-activation-attempts.json"
|
||||||
|
],
|
||||||
|
"limits": {
|
||||||
|
"client_side_read_admitted": false,
|
||||||
|
"factory_spending_admitted": false,
|
||||||
|
"wall_clock_jwt_expiry_observed": false,
|
||||||
|
"actual_predecessor_rotation_observed": false
|
||||||
|
},
|
||||||
|
"temporary_probe_namespaces_remaining": 0,
|
||||||
|
"owner_manifest_api_defaults_match_live": true
|
||||||
|
}
|
||||||
|
|
@ -62,6 +62,36 @@ def contracts():
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def resume_provision(lanes, receipt, prior_path):
|
||||||
|
prior = json.loads(Path(prior_path).read_text())
|
||||||
|
require(prior.get('status') == 'failed' and prior.get('verifier_delivery_disabled_custody_versions_retained')
|
||||||
|
and prior.get('keycape', {}).get('compatible_pair_restored'), 'verified_partial_activation_receipt_required')
|
||||||
|
old_rows = prior['lanes']
|
||||||
|
require(len(old_rows) == len(lanes), 'partial_receipt_lane_mismatch')
|
||||||
|
for lane, old in zip(lanes, old_rows):
|
||||||
|
require(old['ccr'] == lane['ccr'] and (old.get('custody_seeded') or old.get('existing_version_reused')) and old.get('kv_version') == 1,
|
||||||
|
'initial_version_provenance_required')
|
||||||
|
metadata = read_optional(lane['metadata'])
|
||||||
|
require(metadata is not None and metadata['current_version'] == 1
|
||||||
|
and not metadata['versions']['1'].get('destroyed') and not metadata['versions']['1'].get('deletion_time'),
|
||||||
|
'initial_custody_version_changed')
|
||||||
|
role = read_optional('auth/kubernetes/role/' + lane['role'])
|
||||||
|
require(role is not None and role.get('token_policies') == []
|
||||||
|
and role_matches(dict(role, token_policies=[lane['policy']]), lane), 'disabled_role_drift')
|
||||||
|
require(read_optional('sys/policies/acl/' + lane['policy'])['policy'] == lane['hcl'], 'resume_policy_drift')
|
||||||
|
require(all(re.search(r'path\s+"' + re.escape(path) + r'"\s*\{\s*capabilities\s*=\s*\["deny"\]\s*\}', read_optional(BOUNDARY)['policy'])
|
||||||
|
for path in (lane['kv'], lane['metadata'])), 'resume_boundary_drift')
|
||||||
|
# Only reattach the same reviewed reader roles. No KV write, import or rotation.
|
||||||
|
for lane, old in zip(lanes, old_rows):
|
||||||
|
bao(['write', 'auth/kubernetes/role/' + lane['role'], '-'], payload=lane['role_payload'])
|
||||||
|
require(role_matches(read_optional('auth/kubernetes/role/' + lane['role']), lane), 'resumed_role_readback_failed')
|
||||||
|
receipt['lanes'].append({'ccr': lane['ccr'], 'source_sha256': lane['source_sha256'],
|
||||||
|
'custody_seeded': False, 'existing_version_reused': True, 'kv_version': 1,
|
||||||
|
'initial_request_id': old.get('initial_request_id', old.get('request_id')), 'policy_applied': False, 'role_applied': True})
|
||||||
|
receipt['custody_versions_unchanged'] = True
|
||||||
|
|
||||||
|
|
||||||
def read_optional(path):
|
def read_optional(path):
|
||||||
result = bao(['read', '-format=json', path], allow_failure=True)
|
result = bao(['read', '-format=json', path], allow_failure=True)
|
||||||
if result.returncode == 0:
|
if result.returncode == 0:
|
||||||
|
|
@ -264,8 +294,9 @@ def run(args, receipt):
|
||||||
check = data(command(['python3', '-B', '/home/worsch/net-kingdom/sso-mfa/k8s/keycape/openbao-client-config.py', 'issuer-check-live'], env=env))
|
check = data(command(['python3', '-B', '/home/worsch/net-kingdom/sso-mfa/k8s/keycape/openbao-client-config.py', 'issuer-check-live'], env=env))
|
||||||
require(check['issuer_matches'] and check['verified_issuer'] == 'https://auth.coulomb.social', 'verified_issuer_pin_required')
|
require(check['issuer_matches'] and check['verified_issuer'] == 'https://auth.coulomb.social', 'verified_issuer_pin_required')
|
||||||
receipt['issuer_pin_revision'] = check['before']['resource_version']
|
receipt['issuer_pin_revision'] = check['before']['resource_version']
|
||||||
rollout = load_rollout() if args.action == 'activate' else None
|
rollout = load_rollout() if args.action in {'activate', 'resume-activate'} else None
|
||||||
if rollout:
|
if rollout:
|
||||||
|
rollout.verify_artifact()
|
||||||
# Exercise config construction and API admission before any custody write.
|
# Exercise config construction and API admission before any custody write.
|
||||||
secret = rollout.get(kube, 'secret', 'keycape-config')
|
secret = rollout.get(kube, 'secret', 'keycape-config')
|
||||||
dep = rollout.get(kube, 'deployment', 'keycape')
|
dep = rollout.get(kube, 'deployment', 'keycape')
|
||||||
|
|
@ -282,6 +313,9 @@ def run(args, receipt):
|
||||||
receipt['phase'] = 'provision'
|
receipt['phase'] = 'provision'
|
||||||
if args.action in {'provision', 'activate'}:
|
if args.action in {'provision', 'activate'}:
|
||||||
provision(lanes, receipt)
|
provision(lanes, receipt)
|
||||||
|
elif args.action == 'resume-activate':
|
||||||
|
require(args.prior_receipt, 'prior_receipt_required')
|
||||||
|
resume_provision(lanes, receipt, args.prior_receipt)
|
||||||
else:
|
else:
|
||||||
receipt['lanes'] = [{'ccr': lane['ccr']} for lane in lanes]
|
receipt['lanes'] = [{'ccr': lane['ccr']} for lane in lanes]
|
||||||
receipt['phase'] = 'native_verification'
|
receipt['phase'] = 'native_verification'
|
||||||
|
|
@ -307,10 +341,11 @@ def run(args, receipt):
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
parser = argparse.ArgumentParser(description=__doc__)
|
parser = argparse.ArgumentParser(description=__doc__)
|
||||||
parser.add_argument('action', choices=['provision', 'verify', 'activate'])
|
parser.add_argument('action', choices=['provision', 'verify', 'activate', 'resume-activate'])
|
||||||
parser.add_argument('--kubeconfig', required=True)
|
parser.add_argument('--kubeconfig', required=True)
|
||||||
parser.add_argument('--receipt', required=True)
|
parser.add_argument('--receipt', required=True)
|
||||||
parser.add_argument('--recovery')
|
parser.add_argument('--recovery')
|
||||||
|
parser.add_argument('--prior-receipt')
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
receipt = {'schema': 'platform.keycape-approval-custody.v1', 'status': 'failed', 'lanes': [],
|
receipt = {'schema': 'platform.keycape-approval-custody.v1', 'status': 'failed', 'lanes': [],
|
||||||
'started_at': datetime.now(timezone.utc).isoformat(), 'credential_values_emitted': False,
|
'started_at': datetime.now(timezone.utc).isoformat(), 'credential_values_emitted': False,
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
"""Opt-in local OpenBao exercise; no production API or credential helper used."""
|
"""Opt-in local OpenBao exercise; no production API or credential helper used."""
|
||||||
import importlib.util
|
import importlib.util
|
||||||
import json
|
import json
|
||||||
|
import copy
|
||||||
|
import tempfile
|
||||||
import os
|
import os
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import secrets
|
import secrets
|
||||||
|
|
@ -78,6 +80,7 @@ class CustodyExercise(unittest.TestCase):
|
||||||
self.assertTrue(values[0] != values[1])
|
self.assertTrue(values[0] != values[1])
|
||||||
self.assertTrue(all(x['kv_version'] == 1 for x in receipt['lanes']))
|
self.assertTrue(all(x['kv_version'] == 1 for x in receipt['lanes']))
|
||||||
self.assertFalse(any(value in json.dumps(receipt) for value in values))
|
self.assertFalse(any(value in json.dumps(receipt) for value in values))
|
||||||
|
type(self).initial_receipt = copy.deepcopy(receipt)
|
||||||
|
|
||||||
def test_02_retry_refuses_existing_custody(self):
|
def test_02_retry_refuses_existing_custody(self):
|
||||||
with self.assertRaisesRegex(lane.LaneError, 'existing_custody_requires'):
|
with self.assertRaisesRegex(lane.LaneError, 'existing_custody_requires'):
|
||||||
|
|
@ -110,5 +113,26 @@ class CustodyExercise(unittest.TestCase):
|
||||||
with self.assertRaisesRegex(lane.LaneError, 'attended_warden_envelope_required'):
|
with self.assertRaisesRegex(lane.LaneError, 'attended_warden_envelope_required'):
|
||||||
lane.run(None, {})
|
lane.run(None, {})
|
||||||
|
|
||||||
|
def test_07_resume_reattaches_readers_without_rewriting_values(self):
|
||||||
|
before = [lane.read_optional(x['metadata']) for x in self.lanes]
|
||||||
|
for item in self.lanes:
|
||||||
|
lane.bao(['write', 'auth/kubernetes/role/' + item['role'], '-'], payload=dict(item['role_payload'], policies=[]))
|
||||||
|
prior = dict(self.initial_receipt, status='failed', verifier_delivery_disabled_custody_versions_retained=True,
|
||||||
|
keycape={'compatible_pair_restored': True})
|
||||||
|
with tempfile.TemporaryDirectory() as directory:
|
||||||
|
path = Path(directory) / 'prior.json'; path.write_text(json.dumps(prior))
|
||||||
|
receipt = {'lanes': []}
|
||||||
|
lane.resume_provision(self.lanes, receipt, path)
|
||||||
|
self.assertTrue(receipt['custody_versions_unchanged'])
|
||||||
|
self.assertTrue(all(x['existing_version_reused'] and not x['custody_seeded'] for x in receipt['lanes']))
|
||||||
|
self.assertEqual(before, [lane.read_optional(x['metadata']) for x in self.lanes])
|
||||||
|
self.assertTrue(all(lane.role_matches(lane.read_optional('auth/kubernetes/role/' + x['role']), x) for x in self.lanes))
|
||||||
|
|
||||||
|
def test_08_resume_requires_completed_rollback(self):
|
||||||
|
with tempfile.TemporaryDirectory() as directory:
|
||||||
|
path = Path(directory) / 'prior.json'; path.write_text(json.dumps({'status':'failed'}))
|
||||||
|
with self.assertRaisesRegex(lane.LaneError, 'verified_partial_activation_receipt_required'):
|
||||||
|
lane.resume_provision(self.lanes, {'lanes': []}, path)
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ repo: railiance-platform
|
||||||
status: blocked
|
status: blocked
|
||||||
owner: codex
|
owner: codex
|
||||||
created: "2026-09-05"
|
created: "2026-09-05"
|
||||||
updated: "2026-09-08"
|
updated: "2026-09-09"
|
||||||
related:
|
related:
|
||||||
- RPF-WP-0032
|
- RPF-WP-0032
|
||||||
- RPF-WP-0033
|
- RPF-WP-0033
|
||||||
|
|
@ -228,6 +228,18 @@ procedure is exercised and the admitted attended rollout is carried through.
|
||||||
Verifier-side scope, separate client-side/audit lanes and live acceptance remain
|
Verifier-side scope, separate client-side/audit lanes and live acceptance remain
|
||||||
as defined in the reviewed requests.
|
as defined in the reviewed requests.
|
||||||
|
|
||||||
|
2026-09-09 verifier-side return: both CCRs are verified after recorded user approval,
|
||||||
|
CAS=0 version-1 custody, native read/auth denials and revocation, Valid stores,
|
||||||
|
SecretSynced delivery, compatible KeyCape rollout and live positive/negative
|
||||||
|
service checks. Existing human OpenBao login passed again on the new image.
|
||||||
|
Receipt: `docs/evidence/2026-09-09-keycape-verifier-admission.json`. Failed attempts restored the compatible
|
||||||
|
configuration/image and detached delivery; final resume reused version 1.
|
||||||
|
|
||||||
|
T05 remains progress for the separately admitted client-side read lanes and
|
||||||
|
associated owner handoffs. Neither Warden fetch selector is resolvable through
|
||||||
|
these verifier-only CCRs. Do not re-request the completed two named reviews or
|
||||||
|
reseed these paths. Rotation is a distinct, version-guarded operation.
|
||||||
|
|
||||||
## Dependency review — 2026-09-06
|
## Dependency review — 2026-09-06
|
||||||
|
|
||||||
SECRETS-WP-0008-T02 now records the local PIP claim/validation join implemented
|
SECRETS-WP-0008-T02 now records the local PIP claim/validation join implemented
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue