From 463fbbb954437e46171b50b322040cf2623a49f5 Mon Sep 17 00:00:00 2001 From: tegwick Date: Sat, 24 Oct 2026 18:54:25 +0200 Subject: [PATCH] NK-WP-0049-T43: phase B plan, unsigned admission template and decision; refresh position docs/nk49-phase-b-plan.md sets out the shared internal network for the isolated original KeyCape and the restored OpenBao, owner by owner, with what the pinned T18 machinery forces (a new reviewed module; the T40 window has expired). The unsigned admission template names placeholders only. Hub decision e4db6c9f routes the founder choices. The workplan position paragraph now matches the task counts (37 done, 1 in progress, 5 waiting). Co-Authored-By: Claude Sonnet 5.5 Assistant: claude-code Assistant-Model: sonnet Assistant-Process: 328925@bnt-lap001 Assistant-Session: 14b795fd-c632-4c01-8d28-e7e490c7b0ed --- WORK-RECORDS.md | 1 + docs/nk49-phase-b-admission.example.json | 52 +++++++ docs/nk49-phase-b-plan.md | 129 ++++++++++++++++++ ...-bootstrap-secret-free-recovery-handoff.md | 43 ++++-- 4 files changed, 214 insertions(+), 11 deletions(-) create mode 100644 docs/nk49-phase-b-admission.example.json create mode 100644 docs/nk49-phase-b-plan.md diff --git a/WORK-RECORDS.md b/WORK-RECORDS.md index 41dfc64..1cb1824 100644 --- a/WORK-RECORDS.md +++ b/WORK-RECORDS.md @@ -292,6 +292,7 @@ | task | NK-WP-0049-T40 | done | — | workplans/NK-WP-0049-bootstrap-secret-free-recovery-handoff.md | | task | NK-WP-0049-T41 | done | — | workplans/NK-WP-0049-bootstrap-secret-free-recovery-handoff.md | | task | NK-WP-0049-T42 | done | — | workplans/NK-WP-0049-bootstrap-secret-free-recovery-handoff.md | +| task | NK-WP-0049-T43 | 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/nk49-phase-b-admission.example.json b/docs/nk49-phase-b-admission.example.json new file mode 100644 index 0000000..64bbebd --- /dev/null +++ b/docs/nk49-phase-b-admission.example.json @@ -0,0 +1,52 @@ +{ + "_template": "UNSIGNED PROPOSAL. Placeholders in are unknown until the files exist and are reviewed. Signing it is the founder's act; this file authorizes nothing.", + "schema": "netkingdom.keycape-phase-b-attempt-admission.v1", + "namespace": "netkingdom-keycape-phase-b-attempt", + "attempt_id": "", + "not_before": "", + "not_after": "", + "abort_operator": "", + "case": "secrets15-nk49-readiness", + "issuer": "https://kc.coulomb.social", + "network": { + "name": ">", + "owner": "", + "internal": true, + "alias_for_keycape": "kc.coulomb.social", + "attached_containers_exactly": ["keycape-worker", "restored-openbao", "consumer-runner"], + "egress_refusal_probe_sha256": "" + }, + "bound_receipts": { + "t18_keycape_attempt_receipt_sha256": "16d72f7c…1651 (truncated in the public record; full digest from the founder workstation)", + "restored_openbao_receipt_sha256": "4e048dee…579d (same)", + "ciphertext_sha256": "7117b3fe1e4840c86b301c94c85e6b16709e5626bb0884bd0580bde630e30e30" + }, + "tls": { + "decision": "", + "trust_sha256": "", + "acme_duplicate_count_confirmed_by": "railiance-cluster" + }, + "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", + "secret_custody": "" + }, + "source_closure_sha256": { + "isolated-keycape-phase-b.py": "", + "run-isolated-keycape-phase-b.sh": "", + "verify-keycape-phase-b-receipt.py": "", + "unchanged_t38_pinned_sources": "" + }, + "not_authorized": [ + "production redirect or new client grant", + "live OpenBao read of secret data", + "human login or MFA", + "any contact with railiance01 or the cluster" + ] +} diff --git a/docs/nk49-phase-b-plan.md b/docs/nk49-phase-b-plan.md new file mode 100644 index 0000000..2f1830b --- /dev/null +++ b/docs/nk49-phase-b-plan.md @@ -0,0 +1,129 @@ +# NK49 phase B: isolated KeyCape and restored OpenBao on one internal network + +NK-WP-0049-T43. Drafted 2026-10-24. **Preparation only. This grants no authority, +starts nothing and is not T18 acceptance.** Every number marked *proposed* is a +suggestion for the owners, not a fact. + +## Purpose + +T18 needs a live consumer login against restored dependencies. The consumer is +secrets-engine's `secrets-engine-openbao` workload (SECRETS-WP-0015-T04): it obtains +a token from KeyCape, then logs in to OpenBao with the JWT role. Today the two +recovered pieces cannot meet: + +| Piece | State | Network today | +| --- | --- | --- | +| Isolated original KeyCape (NK49 T18 attempt) | Verified 2026-10-23, receipt `16d72f7c…1651`, then destroyed | `--network none`; TLS served on its own loopback | +| Restored original OpenBao (RPF-WP-0064-T07) | Verified 2026-10-24, receipt `4e048dee…579d`, then destroyed | `--network none`; listener on `127.0.0.1:8200` only | + +OpenBao's `keycape-services` JWT auth validates against `https://kc.coulomb.social/jwks`. +A networkless OpenBao proves configuration but cannot perform the login. Phase B is the +shared network that lets it. + +## Proposed topology + +``` + docker network create --internal (no gateway, no published ports) + ┌───────────────────────────────────────────────────────────────────────┐ + │ keycape-worker alias kc.coulomb.social :443 TLS ─▶ issuer :8080 │ + │ (+ issuer sharing its namespace, as in the T18 attempt) │ + │ │ + │ restored OpenBao alias bao-restored :8200 (unsealed, same window)│ + │ │ + │ consumer runner (secrets-engine owned; the only client) │ + └───────────────────────────────────────────────────────────────────────┘ + no other attachment · egress shown refused before admission +``` + +## Who owns what + +| Step | Owner | Notes | +| --- | --- | --- | +| Create and tear down the internal network | *Proposed:* railiance-platform | Its design stub already says phase B is "a single `docker network create --internal` network". One creator avoids a double teardown. | +| Restore and unseal original OpenBao, keep it up for the window | railiance-platform | Adapter runs phase A, then does **not** tear down when phase B is admitted in the same window (its doc). Needs a listener on the internal address, not loopback only. | +| Start the isolated original KeyCape on that network | NetKingdom (this plan) | Needs a new variant of the T18 machinery; see below. | +| Consumer login: KeyCape exchange, OpenBao JWT login, `openbao:admin` refusal, session revocation | secrets-engine | SECRETS-WP-0015-T04. | +| ACME issuance for `kc.coulomb.social` | railiance-cluster | Existing owner of the T33 reissue. | +| Window, abort, and every signature | founder | One window, several admissions. | + +## What NetKingdom must change (verified from current source) + +The T18 machinery is pinned: `prepare-isolated-keycape.py` lists `isolated-keycape.py`, +`isolated-keycape-session.py`, `run-isolated-keycape.sh`, `isolated-lldap.py`, +`identity-bundle.py`, `verify-identity.py` and others as the reviewed source closure, +and the T38 review and T40 admission bind their hashes. **They must stay unedited.** +Phase B is therefore a new module beside them, with its own review. + +Facts read from the code that force changes: + +1. `isolated-keycape-session.py` launches the worker with `--network none` and + `--add-host kc.coulomb.social:127.0.0.1`. Phase B needs the worker on the internal + network with `--network-alias kc.coulomb.social`. +2. `isolated-keycape.py` `inspect_session` refuses any worker whose `NetworkMode` is + not `none`, and checks that dependent containers have `container:`. A phase B + variant must accept exactly one named internal network and verify, by inspection, + that it is `Internal=true`, that its attached set is exactly the admitted containers, + and that the worker has a subnet route but **no default route**. +3. The probes assert that only `lo` exists and `/proc/net/route` has one line. In phase B + an interface and a subnet route exist by design, so "no external route" must be + proved differently: no default route, `Internal=true`, and refused egress probes + (to a public address and to a name outside the admitted aliases). +4. `verify-keycape-attempt-receipt.py` expects the `none` topology and must have a + phase B counterpart, so the receipt can still be verified independently. +5. The T40 admission window ended 2026-10-24T05:00Z. Phase B needs a **new founder + signature**; nothing signed before carries over. + +Proposed new files (names are suggestions): `isolated-keycape-phase-b.py`, +`run-isolated-keycape-phase-b.sh`, `verify-keycape-phase-b-receipt.py`, tests, and a +packet section. Effort is moderate to large and touches the most sensitive machinery +here, so it should get the same review depth as T38, not a quick patch. + +## Preconditions and ordering (proposed) + +1. Owners each pass their own preflight (RPF-WP-0064 adapter, the secrets-engine + selector with `--admission`, NK49 `prepare`-style validation). +2. One founder window, at most 24 h *(owner admission rules)*. OpenBao's own deadline is + 45 minutes from its first custody input, so **OpenBao is started last among the + long-lived pieces** and the consumer run follows immediately. +3. railiance-platform creates the network and shows egress refusal. +4. NK49 starts the isolated KeyCape (age key, TLS key and trust in the founder's + terminal, as in the T18 ceremony). +5. railiance-platform restores and unseals OpenBao onto the same network. +6. secrets-engine runs the consumer login. +7. Teardown in reverse; each owner returns a safe receipt. The network creator removes + the network last and shows it gone. + +## Evidence and claims + +Returned: NK49 phase B receipt (verified independently), railiance-platform's phase B +receipt, secrets-engine's consumer result. Together they would show, with no secret +values: the original issuer and signing key served over TLS on the internal network; +`openbao:login` accepted; `openbao:admin` refused; wrong secret refused; a session +revoked; egress refused; cleanup of containers, tmpfs and network. + +Not claimed: production continuity or redirect, new grants, a live OpenBao read of +secret data (`platform/workloads/keycape/secrets-engine-openbao` stays metadata only), +human login through Authelia, or owner acceptance. T18 also needs its owner acceptance. + +## Open decisions for the founder and owners + +1. **Network creator.** railiance-platform (proposed) or NetKingdom? +2. **TLS identity.** The T18 attempt reissued `kc.coulomb.social` through + `letsencrypt-prod` and destroyed the key. Phase B needs a certificate again: reissue + (consumes the duplicate-certificate allowance, limit 5 per week; railiance-cluster + should confirm the count) or retain the key from the earlier attempt (a custody + decision). No self-signed substitute is allowed (decision `9f9d0d88`). +3. **Consumer secret custody.** The consumer's client secret + `keycape-secrets-engine-openbao-client` is in the backup's KeyCape secrets. How does + the consumer runner receive it without the agent, argv or files seeing it? This is + secrets-engine's design to propose; NetKingdom must not hand it over implicitly. +4. **Trust in OpenBao.** The restored OpenBao must trust the ISRG Root X1 chain to + fetch `/jwks`. Confirm in the owner's rehearsal rather than assume. +5. **Scope.** Is this one workload consumer enough for T18's "selected current + consumer", or must a human-login consumer also be admitted? This plan assumes the + workload consumer and does not require Authelia in phase B. + +## Unsigned admission + +`docs/nk49-phase-b-admission.example.json` is a template with placeholders. It is not +signed, names no real hashes for unwritten files and authorizes nothing. 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 0869313..2bf1f10 100644 --- a/workplans/NK-WP-0049-bootstrap-secret-free-recovery-handoff.md +++ b/workplans/NK-WP-0049-bootstrap-secret-free-recovery-handoff.md @@ -28,18 +28,9 @@ These are retrospective task records of evidenced work, not additional delivery or newly granted authority. The earlier dated narrative remains in Git at `cc2dd01`; its evidence references are carried into the tasks below. -**Current position, source refinement 2026-10-17:** 25 tasks done; 4 in -progress; 1 ready to start (`todo`); 7 waiting on explicit prerequisites. These are task counts, not an effort-based -completion percentage. Assignees are also written in task descriptions because -the current Forge projector does not populate the dedicated Hub assignee field. The outage-retrieval tool and restore-packet draft are now prepared; -independent outage retrieval and Storage Share publication are accepted; -service admission remains an explicit gate. +**Current position, 2026-10-24:** 37 tasks done; 1 in progress; 0 ready to start (`todo`); 5 waiting on explicit prerequisites. These are task counts, not an effort-based completion percentage. Assignees are also written in task descriptions because the current Forge projector does not populate the dedicated Hub assignee field. Actual isolated recovery has passed for LLDAP (T16), Authelia (T17), privacyIDEA audit continuity (T20) and existing-token MFA (T19), all on the independently retrieved generation. What remains is the live consumer login (T18, via the phase B plan in T43), the T15 service-owner acceptance, caller admission (T21), credential dispositions (T22) and final acceptance (T03). -**Next actions:** finish the read-only caller ledger (T02); admit the drafted -isolated restore packet (T15) and record residual-scope handoffs (T24). -Independent outage retrieval (T23) and guide publication (T26) are done. After owner admission, execute the individual native service and -caller checks. A task's owner is accountable for the result; another repository's -approval or credential grant still requires its own owner path. +**Next actions:** answer the T43 decision (network owner, TLS identity, consumer secret custody, consumer scope) so the phase B module can be built and reviewed; obtain service-owner acceptance of the restore packet (T15); work through the T02 ledger with each caller owner (T21, T22). After owner admission, run the individual native service and caller checks. A task's owner is accountable for the result; another repository's approval or credential grant still requires its own owner path. **Closure:** T03 assembles owner acceptance after its named dependencies pass. Passing archive recovery does not prove existing-user authentication, retained @@ -1596,6 +1587,36 @@ password and the LLDAP user lookup (receipt field `user_to_token_resolver_proven is not T19 acceptance and touches no production service. Event handlers or policies that call outside the namespace are not exercised. +## Prepare the phase B plan: isolated KeyCape and restored OpenBao on one internal network + +```task +id: NK-WP-0049-T43 +status: wait +priority: high +assignee: claude-code +needs_human: true +decision_id: "e4db6c9f-d464-4185-b5a1-ea71e21d1b42" +depends_on: [NK-WP-0049-T18] +blocking_reason: "Plan and unsigned admission template delivered 2026-10-24. Await the founder/owner choices in the decision (network owner, TLS identity, consumer secret custody, consumer scope); nothing is built or signed before they are answered." +``` + +**Accountable worker:** claude-code. Every signature, window and secret input stays with +the founder and the named owners. + +Delivered 2026-10-24: `docs/nk49-phase-b-plan.md` (topology, ownership, the changes the +pinned T18 machinery forces, ordering, claims and open decisions) and +`docs/nk49-phase-b-admission.example.json` (unsigned template). Verified from source: +the worker is launched `--network none` with a loopback `--add-host`; `inspect_session` +refuses any other network mode; the loopback-only probes would fail by design on an +internal network; the T38/T40 source closure is hash-pinned, so phase B is a new reviewed +module and must not edit those files; the T40 admission window ended 2026-10-24T05:00Z, so +a new founder signature is required. The consumer's client secret is part of the backup's +KeyCape secrets, so its custody into the consumer runner is an open owner design. + +**Done when:** the decision is answered, the owners confirm their steps in their own +workplans, and the NK49 phase B module, verifier and tests are reviewed to T38 depth. This +task does not run phase B and does not replace T18's live consumer login or owner acceptance. + ## Accept remaining recovery and caller evidence and close the G08 handoff ```task