From f890760176a43e0421dbd5ef07d0d9da409b00b5 Mon Sep 17 00:00:00 2001 From: tegwick Date: Wed, 21 Oct 2026 01:04:19 +0200 Subject: [PATCH] NK-WP-0049: T33 attempt assembly requires secrets-engine selector --admission; T35/T36 owner prep Bind secrets-engine selector/source pins and founder-signed attempt admission (d1ab097d) as T33's required pre-key step; record kc-tls owner (RCLUSTER-WP-0018) and public served-chain metadata; add founder gates T39 (TLS disposition, decision 9f9d0d88) and T40 (attempt signature). Preparation only. Co-Authored-By: Claude Opus 5.5 Assistant: claude-code Assistant-Model: opus Assistant-Process: 809600@bnt-lap001 Assistant-Session: 40ba228f-c8a0-4ec9-bb29-3129951afdc4 --- WORK-RECORDS.md | 2 + docs/bootstrap-recovery-ceremony.md | 49 ++++++++++++++ .../readiness-observation.json | 63 ++++++++++++++++++ ...-bootstrap-secret-free-recovery-handoff.md | 64 ++++++++++++++++++- 4 files changed, 175 insertions(+), 3 deletions(-) create mode 100644 docs/evidence/2026-10-21-nk49-t33-readiness/readiness-observation.json diff --git a/WORK-RECORDS.md b/WORK-RECORDS.md index b40e513..9e6a40e 100644 --- a/WORK-RECORDS.md +++ b/WORK-RECORDS.md @@ -286,6 +286,8 @@ | task | NK-WP-0049-T36 | progress | — | workplans/NK-WP-0049-bootstrap-secret-free-recovery-handoff.md | | task | NK-WP-0049-T37 | done | — | workplans/NK-WP-0049-bootstrap-secret-free-recovery-handoff.md | | task | NK-WP-0049-T38 | done | — | workplans/NK-WP-0049-bootstrap-secret-free-recovery-handoff.md | +| task | NK-WP-0049-T39 | wait | — | workplans/NK-WP-0049-bootstrap-secret-free-recovery-handoff.md | +| task | NK-WP-0049-T40 | wait | — | workplans/NK-WP-0049-bootstrap-secret-free-recovery-handoff.md | | task | NK-WP-0050-T01 | done | — | workplans/NK-WP-0050-playwright-consumer-registration.md | | task | NK-WP-0050-T02 | progress | — | workplans/NK-WP-0050-playwright-consumer-registration.md | | task | NK-WP-0050-T03 | wait | — | workplans/NK-WP-0050-playwright-consumer-registration.md | diff --git a/docs/bootstrap-recovery-ceremony.md b/docs/bootstrap-recovery-ceremony.md index a9c3f94..43a1095 100644 --- a/docs/bootstrap-recovery-ceremony.md +++ b/docs/bootstrap-recovery-ceremony.md @@ -535,3 +535,52 @@ T20/T31 are done. Safe evidence and source/ciphertext/target bindings are in This accepts internal readback and stored-row signature continuity. Existing-token MFA, Authelia and KeyCape authentication remain separate; event completeness is not claimed. The published Storage Share kit remains its recorded version. + +## KeyCape original attempt assembly (NK-WP-0049-T33) — 2026-10-21 + +This is the attended order for the original KeyCape attempt. It is preparation and +authorizes nothing. T18 owns the actual run. Abort at any non-zero step, before the +hidden key prompt. + +1. **Founder attempt admission (surviving offline authorization).** The founder + signs a `secrets-engine.recovery-attempt-admission.v1` document with namespace + `secrets-engine-recovery-attempt`, case `secrets15-nk49-readiness`, a window of + at most 24 h, issuer `https://kc.coulomb.social`, and the exact hashes below + (secrets-engine `docs/founder-admission.md`; approved in principle by hub decision + `d1ab097d-cc79-4612-9c17-bb9b364081ac`, not yet signed). The verifier anchor + `/etc/secrets-engine/admission/allowed_signers` (root-owned, runtime-unwritable) + must exist on the attempt host. It is absent on the workstation today. +2. **Selector preflight with admission. This step is required.** From a secrets-engine checkout at + `0f44fbeea18147e65f379632621f2f29ded5f310` (or a replacement the owner names): + ```sh + python3 -I tools/check-keycape-recovery-selector.py \ + --selector docs/keycape-recovery-selector.json \ + --source-sha256 sha256:c0ade429edf452ad17f05d690b7ebef091db7f1a42b5ac350f1e40955c8586c0 \ + --admission + ``` + Proceed only with exit 0 and status `founder_admitted_attempt_not_native_acceptance`. + The selector bytes must hash to + `sha256:02077af3c18b24f5e4a85992a3d4d0df51a8e458de1e48b48f7eb5f482257012`. The + NK49 admission's consumer block must equal the selector's eight fields + (`secrets-engine-openbao`, `KEYCAPE_SECRETS_ENGINE_OPENBAO_CLIENT_SECRET`, + audience `secrets-engine-openbao`, scope `openbao:login`, subject + `service:secrets-engine`, tenant `tenant:platform`, roles `secrets-engine`, + deny `openbao:admin`). A pass shows founder authority for this one attempt. It + is not native acceptance and not single use. The NK49 execution keeps single-use + consumption and cleanup. +3. **TLS/trust input per T35 disposition** (founder decision + `9f9d0d88-837a-433c-8a11-d30b633b0073`). Under reissue, record the public chain + with railiance-cluster `tools/public-tls-metadata.py` at attempt time and compare + it to the admission. Under original recovery, use only the custody path that + RCLUSTER-WP-0018-T03/T04 delivers. No self-signed substitute is allowed. The TLS + key stays in the owner's own terminal. +4. **Existing six-argument launcher** `tools/bootstrap-recovery/run-isolated-keycape.sh` + with the reviewed admission digest. Its T34 source closure and abort/cleanup + behaviour are unchanged. The private manifest is derived and the archive + registration is compared to the selector inside custody only. + +Still open before T33 can close: T35 owner metadata (RCLUSTER-WP-0018-T02) and +the founder disposition; the execution deadline, safe receipt location and +independent receipt verifier fixed in the packet; the anchor installed on the +attempt host. Public observations are in +`docs/evidence/2026-10-21-nk49-t33-readiness/readiness-observation.json`. diff --git a/docs/evidence/2026-10-21-nk49-t33-readiness/readiness-observation.json b/docs/evidence/2026-10-21-nk49-t33-readiness/readiness-observation.json new file mode 100644 index 0000000..164ae31 --- /dev/null +++ b/docs/evidence/2026-10-21-nk49-t33-readiness/readiness-observation.json @@ -0,0 +1,63 @@ +{ + "schema": "nk49.t35-t36-readiness-observation.v1", + "observed_at": "2026-10-21", + "status": "preparation_only_not_recovery_or_owner_assent", + "t35_public_tls": { + "source": "currently served chain at kc.coulomb.social:443 (public), projected by railiance-cluster tools/public-tls-metadata.py @ a594840; trust file = Mozilla ISRG Root X1", + "originality_proven": false, + "schema": "nk49.public-tls-metadata.v1", + "status": "public_metadata_only_not_originality_or_owner_assent", + "tls_cert_sha256": "f14c928a51b3cf98b0c0964b9bf9a33f4b37e9f30b5c3e949f819fe120ebd15a", + "trust_sha256": "22b557a27055b33606b6559f37703928d3e4ad79f110b407d04986e1843543d1", + "leaf_public_spki_sha256": "sha256:236fa4dcf1ce1d58be398dd01ab067157281e54356a550be35a157527b464b6d", + "leaf_dns_sans": [ + "kc.coulomb.social" + ], + "leaf_not_before": "2026-09-25T17:57:22+00:00", + "leaf_not_after": "2026-12-24T17:57:21+00:00", + "server_chain_certificates": 3, + "trust_certificates": 1, + "network_contacted": false, + "private_key_argument_supported": false, + "issuer_source": "net-kingdom sso-mfa/k8s/cert-manager/issuers.yaml: ClusterIssuer letsencrypt-prod, ACME HTTP-01, ingressClassName traefik, account key secret letsencrypt-prod-account-key", + "credential_routing": "warden route find returned only SSH-certificate lanes; no TLS lane. Not evidence of absence of an owner.", + "owner": "railiance-cluster (cert-manager, Traefik, cluster backup posture); live work RCLUSTER-WP-0018 T02 todo, T03/T04 wait, T05 todo" + }, + "t36_selector": { + "owner_repo": "secrets-engine", + "commit": "0f44fbeea18147e65f379632621f2f29ded5f310", + "selector_path": "docs/keycape-recovery-selector.json", + "selector_sha256": "sha256:02077af3c18b24f5e4a85992a3d4d0df51a8e458de1e48b48f7eb5f482257012", + "source_path": "src/secrets_engine/service_auth.py", + "source_sha256": "sha256:c0ade429edf452ad17f05d690b7ebef091db7f1a42b5ac350f1e40955c8586c0", + "consumer": { + "client_id": "secrets-engine-openbao", + "secret_env": "KEYCAPE_SECRETS_ENGINE_OPENBAO_CLIENT_SECRET", + "audience": "secrets-engine-openbao", + "scope": "openbao:login", + "subject": "service:secrets-engine", + "tenant": "tenant:platform", + "roles": "secrets-engine", + "deny_scope": "openbao:admin" + }, + "preflight_cases": [ + { + "case": "exact selector + source hash, no admission", + "rc": 0, + "status": "source_selector_matches_not_native_assent" + }, + { + "case": "wrong source hash", + "rc": 1, + "status": "source_selector_refused" + }, + { + "case": "unsigned admission document", + "rc": 1, + "status": "source_selector_refused" + } + ], + "admission_anchor_on_workstation": "absent (/etc/secrets-engine/admission)", + "builder_tests": "net-kingdom tests/test_private_manifest_binding.py + tests/test_prepare_isolated_keycape.py: 27 passed (synthetic inputs; positive bindings, archive/size/transfer/source/issuer/consumer-overlap/symlink/mode/digest negatives)" + } +} diff --git a/workplans/NK-WP-0049-bootstrap-secret-free-recovery-handoff.md b/workplans/NK-WP-0049-bootstrap-secret-free-recovery-handoff.md index 2078d62..8c04441 100644 --- a/workplans/NK-WP-0049-bootstrap-secret-free-recovery-handoff.md +++ b/workplans/NK-WP-0049-bootstrap-secret-free-recovery-handoff.md @@ -559,7 +559,7 @@ id: NK-WP-0049-T18 status: wait priority: high assignee: codex -depends_on: [NK-WP-0049-T15, NK-WP-0049-T16, NK-WP-0049-T19, NK-WP-0049-T32, NK-WP-0049-T33] +depends_on: [NK-WP-0049-T15, NK-WP-0049-T16, NK-WP-0049-T19, NK-WP-0049-T32, NK-WP-0049-T33, NK-WP-0049-T40] blocking_reason: "Source and synthetic KeyCape lifecycle proof are delivered in T32; await exact owner-held recovery attempt preparation T33, selected consumer acceptance and retained-token MFA for the selected flow." state_hub_task_id: "141dd773-c1fa-56c1-a852-bbe2372e5125" ``` @@ -961,8 +961,8 @@ id: NK-WP-0049-T33 status: wait priority: high assignee: codex -depends_on: [NK-WP-0049-T32, NK-WP-0049-T34, NK-WP-0049-T35, NK-WP-0049-T36] -blocking_reason: "Source custody/preflight preparation is evidenced in T34; await original TLS/trust recovery disposition T35 and selected original registration/private-admission metadata T36 before final reviewed command and existing owner process." +depends_on: [NK-WP-0049-T32, NK-WP-0049-T34, NK-WP-0049-T35, NK-WP-0049-T36, NK-WP-0049-T39] +blocking_reason: "Attempt order is assembled with the secrets-engine selector preflight --admission step required (2026-10-21). Still waiting on: T35 owner metadata (RCLUSTER-WP-0018-T02); founder TLS disposition T39; attempt-host admission anchor; fixed deadline/receipt location/verifier." state_hub_task_id: "56c8d469-7a10-5d50-a30e-95cbf3cd4756" ``` @@ -1084,6 +1084,20 @@ publication action remain live; do not bypass the historical identity guard. Evidence: Custodian October17 `cluster-integration.json`, `coordinator-checkpoint.json` and October16 `cluster-historical-publication-r1/actual-source-allocator-preview.json`. +2026-10-21: the publication blocker above is superseded. RCLUSTER-WP-0018 is +published (c562d71) and active. The owner is railiance-cluster (cert-manager, +Traefik, backup posture). Its live owner work records are T02 (custody metadata, +todo), T03/T04 (extraction or reissue, owner-attended verify, wait) and T05 +(future archive coverage, todo). `warden route find` has no TLS lane. Source: +letsencrypt-prod with ACME HTTP-01 via Traefik, auto-renewing. Served public +chain today: leaf valid 2026-09-25..12-24, cert sha256 f14c928a...d15a, SPKI +sha256:236fa4dc...6b6d, ISRG Root X1 trust 22b557a2...43d1. That is current, not +proven original. The only owner-path disposition today is reissue. Original-key +recovery is an unavailable capability, owned under RCLUSTER-WP-0018-T02, and +unblocks when the owner records independent custody of the kc-tls key. +Owner message dae2e1bb requests T02 metadata. Founder disposition gate is T39. +T35 stays progress. + ## Prepare selected original service registration and private admission bindings ```task @@ -1140,6 +1154,50 @@ T33 continues to require the complete reviewed TLS/custody/admission packet. This extraction records one source outcome rather than counting it again as original registration validation or consumer continuity. +2026-10-21: selector bound to secrets-engine `0f44fbe`. Selector +sha256:02077af3...7012, `service_auth.py` sha256:c0ade429...86c0. Offline preflight +passes. A wrong source hash and an unsigned admission each refuse with rc=1. The +builder suite passes 27 (synthetic positive and negative +source/archive/selector cases). The founder-signed attempt admission (decision +d1ab097d, SECRETS-WP-0015-T04) is the surviving offline authorization, and T33 +now requires `--admission` to pass. Owner message 0fec1113 asks secrets-engine to +confirm the pins, attempt-host anchor coverage (absent on the workstation) and +T04 consumption. Exact archive-to-selector equality is only checkable inside +custody, at attempt time. Evidence: +`docs/evidence/2026-10-21-nk49-t33-readiness/readiness-observation.json`. +T36 stays progress until the owner confirms. + +## Founder disposition of original KeyCape TLS recovery + +```task +id: NK-WP-0049-T39 +status: wait +priority: high +assignee: founder +needs_human: true +decision_id: "9f9d0d88-837a-433c-8a11-d30b633b0073" +depends_on: [NK-WP-0049-T35] +blocking_reason: "Founder decides: accept ACME letsencrypt-prod reissue as the kc.coulomb.social TLS disposition, or require original key custody via RCLUSTER-WP-0018." +``` + +Founder gate for T35. Approves no mutation and no attempt. + +## Founder signs the exact KeyCape attempt admission + +```task +id: NK-WP-0049-T40 +status: wait +priority: high +assignee: founder +needs_human: true +decision_id: "d1ab097d-cc79-4612-9c17-bb9b364081ac" +depends_on: [NK-WP-0049-T33] +blocking_reason: "Approved in principle (d1ab097d); founder signs the <=24h secrets-engine-recovery-attempt admission only once T33's packet is ready." +``` + +The signed document is T33's required `--admission` input and T18's surviving +offline authorization. The signature does not run the attempt. + ## Verify actual SOPS compatibility and sealed-descriptor read/write custody ```task