From 573f7073488c12459a04f9bf1a63d719e2ba49fd Mon Sep 17 00:00:00 2001 From: codex Date: Sun, 1 Nov 2026 23:25:29 +0100 Subject: [PATCH] docs: preserve reviewed NetKingdom session artifacts --- ...6-authorization-code-protocol-hardening.md | 123 ++++++ ...nonical-model-and-discovery-conformance.md | 118 ++++++ ...EY-WP-0018-export-completeness-evidence.md | 104 +++++ ...19-upstream-provider-token-verification.md | 155 ++++++++ ...WP-0020-migration-contract-preservation.md | 107 +++++ ...Y-WP-0021-snapshot-attribute-validation.md | 101 +++++ ...cement-harness-and-external-conformance.md | 136 +++++++ .../KEY-WP-0023-live-migration-proof.md | 136 +++++++ .../KEY-WP-0024-tenant-roles-opt-in-wiring.md | 62 +++ ...WP-0025-runtime-lifecycle-and-readiness.md | 118 ++++++ ...aging-bootstrap-and-credential-handling.md | 123 ++++++ ...P-0027-rollout-readiness-and-live-state.md | 130 +++++++ ...WP-0028-browser-client-field-validation.md | 89 +++++ .../KEY-WP-0029-scope-reassessment.md | 81 ++++ .../KEY-WP-0030-tenant-precondition-guard.md | 73 ++++ .../KEY-WP-0031-gate-house-tenant-ruling.md | 81 ++++ ...WP-0032-principal-type-provenance-guard.md | 75 ++++ .../KEY-WP-0033-vergabe-fresh-login.md | 93 +++++ .../workplans/KEY-WP-0034-account-recovery.md | 64 +++ .../workplans/KEY-WP-0035-optional-mfa.md | 112 ++++++ .../KEY-WP-0036-platform-factor-recovery.md | 83 ++++ ...ets-engine-openbao-registration-custody.md | 135 +++++++ .../KEY-WP-0038-forgejo-oidc-client.md | 212 ++++++++++ ...Y-WP-0039-openbao-negative-probe-tokens.md | 368 ++++++++++++++++++ .../KEY-WP-0040-mfa-attended-run-findings.md | 162 ++++++++ .../KEY-WP-0041-sign-in-diagnosis-page.md | 63 +++ .../consistency.stderr.txt | 1 + .../consistency.stdout.json | 123 ++++++ .../final-verification.json | 54 +++ .../manifest.json | 70 ++++ .../normal-publication.json | 34 ++ .../normal-sync.stderr.txt | 13 + .../normal-sync.stdout.json | 0 .../one-path.patch | 13 + .../progress-receipt.json | 14 + .../progress-request.json | 7 + .../sync.py | 36 ++ .../close-progress-receipt-r3.json | 14 + .../close-verification-r3.json | 58 +++ .../KEY39.preimage.md | 351 +++++++++++++++++ .../KEY39.proposed.md | 364 +++++++++++++++++ .../apply.py | 36 ++ .../baseline.json | 54 +++ .../canonical-receiver.json | 173 ++++++++ .../canonical-reconcile.py | 32 ++ .../consistency-backup/.custodian-brief.md | 37 ++ .../consistency-backup/WORK-RECORDS.md | 224 +++++++++++ .../consistency-backup/originals.json | 232 +++++++++++ .../workplans/ADHOC-2026-09-05.md | 27 ++ .../workplans/ADHOC-2026-09-07.md | 36 ++ 50 files changed, 5107 insertions(+) create mode 100644 docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0016-authorization-code-protocol-hardening.md create mode 100644 docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0017-canonical-model-and-discovery-conformance.md create mode 100644 docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0018-export-completeness-evidence.md create mode 100644 docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0019-upstream-provider-token-verification.md create mode 100644 docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0020-migration-contract-preservation.md create mode 100644 docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0021-snapshot-attribute-validation.md create mode 100644 docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0022-replacement-harness-and-external-conformance.md create mode 100644 docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0023-live-migration-proof.md create mode 100644 docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0024-tenant-roles-opt-in-wiring.md create mode 100644 docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0025-runtime-lifecycle-and-readiness.md create mode 100644 docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0026-packaging-bootstrap-and-credential-handling.md create mode 100644 docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0027-rollout-readiness-and-live-state.md create mode 100644 docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0028-browser-client-field-validation.md create mode 100644 docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0029-scope-reassessment.md create mode 100644 docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0030-tenant-precondition-guard.md create mode 100644 docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0031-gate-house-tenant-ruling.md create mode 100644 docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0032-principal-type-provenance-guard.md create mode 100644 docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0033-vergabe-fresh-login.md create mode 100644 docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0034-account-recovery.md create mode 100644 docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0035-optional-mfa.md create mode 100644 docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0036-platform-factor-recovery.md create mode 100644 docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0037-secrets-engine-openbao-registration-custody.md create mode 100644 docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0038-forgejo-oidc-client.md create mode 100644 docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0039-openbao-negative-probe-tokens.md create mode 100644 docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0040-mfa-attended-run-findings.md create mode 100644 docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0041-sign-in-diagnosis-page.md create mode 100644 docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency.stderr.txt create mode 100644 docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency.stdout.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/final-verification.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/manifest.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/normal-publication.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/normal-sync.stderr.txt create mode 100644 docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/normal-sync.stdout.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/one-path.patch create mode 100644 docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/progress-receipt.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/progress-request.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/sync.py create mode 100644 docs/changes/netkingdom-target-2026-10-17/key39-finalization-current-r2/close-progress-receipt-r3.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/key39-finalization-current-r2/close-verification-r3.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/key39-finalization-owner-followthrough-r1/KEY39.preimage.md create mode 100644 docs/changes/netkingdom-target-2026-10-17/key39-finalization-owner-followthrough-r1/KEY39.proposed.md create mode 100644 docs/changes/netkingdom-target-2026-10-17/key39-finalization-owner-followthrough-r1/apply.py create mode 100644 docs/changes/netkingdom-target-2026-10-17/key39-finalization-owner-followthrough-r1/baseline.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/key39-finalization-owner-followthrough-r1/canonical-receiver.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/key39-finalization-owner-followthrough-r1/canonical-reconcile.py create mode 100644 docs/changes/netkingdom-target-2026-10-17/key39-finalization-owner-followthrough-r1/consistency-backup/.custodian-brief.md create mode 100644 docs/changes/netkingdom-target-2026-10-17/key39-finalization-owner-followthrough-r1/consistency-backup/WORK-RECORDS.md create mode 100644 docs/changes/netkingdom-target-2026-10-17/key39-finalization-owner-followthrough-r1/consistency-backup/originals.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/key39-finalization-owner-followthrough-r1/consistency-backup/workplans/ADHOC-2026-09-05.md create mode 100644 docs/changes/netkingdom-target-2026-10-17/key39-finalization-owner-followthrough-r1/consistency-backup/workplans/ADHOC-2026-09-07.md diff --git a/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0016-authorization-code-protocol-hardening.md b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0016-authorization-code-protocol-hardening.md new file mode 100644 index 00000000..52839bd0 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0016-authorization-code-protocol-hardening.md @@ -0,0 +1,123 @@ +--- +id: KEY-WP-0016 +type: workplan +title: "Authorization-code protocol hardening and token verification bindings" +domain: infotech +repo: key-cape +status: finished +owner: claude +topic_slug: authorization-code-protocol-hardening +created: "2026-09-06" +updated: "2026-09-06" +state_hub_workstream_id: "22698171-f283-59a9-9443-183faaeb6287" +--- + +Closes gap G01 of `history/2026-09-05-011726-scope-intent-assessment.md`, the +assessment's first recommended item. The browser grant validates PKCE, client id +and scopes but does not bind the redirect URI, authenticate confidential clients, +enforce grant-type eligibility, or consume the authorization code atomically. +UserInfo verification is weaker than the caller CLI's contract. + +Scope is the local protocol surface only. Upstream provider-token verification +(the Authelia adapter's deliberate no-signature-check trust assumption) is a +separate trust-contract decision and is not addressed here. + +## Consume authorization codes atomically + +```task +id: KEY-WP-0016-T01 +status: done +priority: high +state_hub_task_id: "a5171df0-4905-518d-a52f-7152b980af79" +``` + +`SessionStore.Get` reads a code and `Delete` removes it in separate lock +acquisitions, with signing in between, so two simultaneous exchanges can both +observe the same session before either deletes it. Replace the token path's +lookup with a single-operation consume that removes the session under the same +lock that reads it, so exactly one concurrent exchange can succeed. A failed +exchange must not leave a replayable code. Cover with a concurrent-reuse test +that asserts exactly one success across parallel requests. + +Added `SessionStore.Consume`, which reads and deletes under one lock, and moved +the token path onto it. Verified the defect was real before claiming the fix: +with `Get`/`Delete` restored, 9 of 16 concurrent exchanges succeeded and a +PKCE-failed exchange left the code replayable. Both now fail closed — +`TestConcurrentCodeExchangeSucceedsExactlyOnce`, `TestFailedExchangeConsumesTheCode`. + +## Bind redirect URI, grant type and confidential client authentication + +```task +id: KEY-WP-0016-T02 +status: done +priority: high +state_hub_task_id: "1d0fb108-273f-5c9a-a7c7-7640f19bad9d" +``` + +On the authorization-code path: require `redirect_uri` and compare it for exact +equality with the value stored at authorization time; reject a client whose +registration does not permit `authorization_code` (an empty `grantTypes` stays an +implicit authorization-code client, matching config validation, but a +`client_credentials`-only client must be refused); and authenticate confidential +clients with a constant-time secret comparison, accepting the same +`client_secret_basic` and form-encoded credentials as the service grant. Public +clients continue to present no secret and must not be able to supply one to +impersonate a confidential registration. Negative tests per condition. + +Implemented all three in `token.go`, with `basicClientCredentials` and +`secretsEqual` shared with the service grant (digest-first comparison, so a +wrong-length secret is indistinguishable from a wrong value). Tests: +`TestCodeExchangeRejectsRedirectURIMismatchAndOmission` (omitted, different, +traversal-shaped and trailing-slash variants), +`TestCodeExchangeRejectsServiceOnlyClient`, +`TestCodeExchangeAllowsImplicitAuthorizationCodeClient`, +`TestConfidentialCodeClientRequiresItsSecret` and +`TestPublicClientSecretIsIgnoredAndEmptyConfidentialSecretIsRejected`. The +`keycape login` CLI already sent `redirect_uri`, so no caller change was needed; +the authclient test stub was recording no redirect and is now faithful to real +`/authorize`. + +## Harden UserInfo token verification + +```task +id: KEY-WP-0016-T03 +status: done +priority: high +state_hub_task_id: "336db63a-e4fd-522d-86b6-0bf54a31af1c" +``` + +UserInfo verifies an RSA signature and expiry but ignores its own `Issuer` field +and does not check the JOSE header algorithm, the issuer claim, or the token +purpose, so an ID token or a foreign-issuer token of the right shape is accepted +where an access token is required. Enforce `alg=RS256` from the header, reject +`none` and any other algorithm, require the configured issuer, and reject tokens +that are not access tokens. Negative tests per condition. + +`validateJWT` became `validateAccessToken`, checking the JOSE header before +trusting the signature, requiring the configured issuer (an empty configured +issuer is a misconfiguration, not a skipped check), and requiring the `scope` +claim the token endpoint sets only on access tokens. Purpose is decided +verification-side deliberately: adding a `token_use` claim would change the +issued-token contract that consumers pin exactly. Tests: +`TestUserinfoRejectsUnverifiedTokenShapes` (foreign issuer, missing issuer, ID +token presented as an access token) and `TestUserinfoRejectsNonRS256Algorithms`, +which restates the header algorithm over a genuine RS256 signature so it +isolates the header check. + +## Reconcile scope and assessment records + +```task +id: KEY-WP-0016-T04 +status: done +priority: medium +state_hub_task_id: "90efa6ef-9495-5716-bee2-49d01c9ce05d" +``` + +Update `SCOPE.md`'s protocol-hardening limit and G01's closure status in the +assessment to state exactly which bindings are now enforced and which remain +open, naming the upstream-trust item as still outstanding. Do not claim complete +profile conformance: G01 closure covers these bindings, not the whole profile. + +Updated `SCOPE.md`'s protocol-hardening limit and recorded a partial-closure +status under G01 in the assessment, naming the Authelia upstream-trust item as +still open. G01 is explicitly not fully closed. diff --git a/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0017-canonical-model-and-discovery-conformance.md b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0017-canonical-model-and-discovery-conformance.md new file mode 100644 index 00000000..00eb0397 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0017-canonical-model-and-discovery-conformance.md @@ -0,0 +1,118 @@ +--- +id: KEY-WP-0017 +type: workplan +title: "Reconcile the canonical model and discovery metadata with the runtime" +domain: infotech +repo: key-cape +status: finished +owner: claude +topic_slug: canonical-model-and-discovery-conformance +created: "2026-09-07" +updated: "2026-09-07" +state_hub_workstream_id: "0904caad-ad8b-573f-9cdb-b6da09262a18" +--- + +Closes gap G02 of `history/2026-09-05-011726-scope-intent-assessment.md`. +`spec/canonical-model.yaml` and `src/internal/domain/model.go` both claim to be +the source of truth for the client registration shape, and they disagree: the +spec restricts grants to `authorization_code`, requires redirect URIs for every +client, and omits the service-subject, tenant, audience, lifetime, role and +MFA/handoff fields the runtime actually reads. Discovery advertises a fixed basic +scope and claim list that omits core profile claims and every configured resource +scope. + +The point of the work is the executable link, not the edit: reconciling the two +files once leaves them free to drift again the same afternoon. + +## Reconcile the client entity with the runtime model + +```task +id: KEY-WP-0017-T01 +status: done +priority: high +state_hub_task_id: "996f6895-f36b-585e-844d-3e55d02eefda" +``` + +Bring the `Client` entity in `spec/canonical-model.yaml` in line with +`domain.Client`: add `audience`, `serviceSubject`, `tenant`, `roles`, +`mfaRequired`, `registrationUrl` and `enrollmentUrl`; extend the `grantTypes` +enum to include `client_credentials`; and make `redirectUris` required only for +clients that use the authorization-code grant, matching what config validation +already enforces. Record which fields are deliberately absent from the canonical +model rather than silently omitted — `clientSecret` and `tokenLifetime` are +runtime policy, not identity data, and both are `yaml:"-"` in the Go model. + +Added all seven fields, extended the grant enum, and made `redirectUris` +optional at the schema level with the authorization-code condition stated in its +description. `tokenProfile` and `environments` were declared in the spec but read +by nothing; they are kept and marked `runtime: false` rather than deleted, since +removing them would discard intent the check can now hold honest. + +## Add an executable conformance check + +```task +id: KEY-WP-0017-T02 +status: done +priority: high +state_hub_task_id: "77df5043-2b23-56cd-947e-317f7117ac18" +``` + +Add a test that compares the canonical model's `Client` field set against +`domain.Client`'s YAML tags by reflection and fails on divergence in either +direction: a Go field with no spec entry, or a spec field the runtime does not +read. Fields intentionally excluded must be named in one list the test reads, so +excluding a field is a deliberate, reviewable act rather than an omission. This +is the generation/conformance link the assessment asks for. + +`src/internal/domain/conformance_test.go` checks `Client`, `User`, `Group` and +`Membership` by reflection over YAML tags. It earned its place on the first run +by finding drift outside the assessment's list: `User.tenant` was read by the +runtime and undeclared in the spec. `yaml:"-"` fields are excluded by +construction, which is what keeps `clientSecret` and `tokenLifetime` out without +a hand-maintained exception list. + +## Reconcile discovery metadata with the profile surface + +```task +id: KEY-WP-0017-T03 +status: done +priority: medium +state_hub_task_id: "5eb36f9b-a8e0-541e-8f81-310db49b77a0" +``` + +`claims_supported` omits `tenant`, `principal_type`, `assurance`, `scope` and +`nonce`, which the token endpoint emits on every token, and `scopes_supported` is +a fixed list that omits every configured resource scope. Advertise the core +profile claims and derive the scope list from the registered clients. Keep the +distinction the assessment asks for: an omitted core claim is a contract defect, +while optional discovery metadata is not, so document which is which rather than +treating every omission alike. + +`claims_supported` now lists the core profile claims (`tenant`, `principal_type`, +`assurance`, `scope`, `nonce`) alongside the scope-gated human claims and the +optional cached `tenant_roles`. `scopes_supported` is derived from the registered +clients — baseline OIDC scopes first, configured resource scopes sorted after, so +the document is stable regardless of map iteration order — with the baseline +still advertised when no clients are configured. +`TestDiscoveryAdvertisesEveryCoreProfileClaim` states the distinction the +assessment asked for: a missing core claim fails, optional metadata does not. + +## State a single source of truth + +```task +id: KEY-WP-0017-T04 +status: done +priority: medium +state_hub_task_id: "63290e2a-bae1-51c3-9cb9-d49994d804b6" +``` + +Two files currently claim to be authoritative. State in both that the Go model is +the runtime authority and the canonical model is the reviewed contract the +conformance check holds it to, so the next reader knows which one to edit first. +Update `SCOPE.md` and G02's status in the assessment to say what is now enforced +and what remains — this closes the client-registration and discovery drift, not +schema enforcement in general (G06 is a separate gap). + +Both files now state the split: the Go model is the runtime authority, the YAML +the reviewed contract, and the conformance test the link. SCOPE.md and G02's +status say what is enforced and that G06 remains open. diff --git a/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0018-export-completeness-evidence.md b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0018-export-completeness-evidence.md new file mode 100644 index 00000000..325fc483 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0018-export-completeness-evidence.md @@ -0,0 +1,104 @@ +--- +id: KEY-WP-0018 +type: workplan +title: "Make the LLDAP export report its own completeness" +domain: infotech +repo: key-cape +status: finished +owner: claude +topic_slug: export-completeness-evidence +created: "2026-09-07" +updated: "2026-09-07" +state_hub_workstream_id: "82ff99a1-65cc-5e1f-93fc-5bc8911ce77a" +--- + +Closes gap G05 of `history/2026-09-05-011726-scope-intent-assessment.md`. + +`src/internal/migration/lldapexport/exporter.go` discovers groups by walking each +user's memberships, so a group nobody belongs to is absent from the snapshot and +nothing says so. A `LookupGroups` failure is skipped by a `continue` under a +comment claiming it is recorded in the incompatibility report — it is not. The +run then emits `result: "success"` and the CLI reports a clean export. Group +order comes from map iteration, so two exports of an unchanged directory differ. + +A second defect falls out of reading the adapter: `LDAPAdapter.LookupGroups` +never populates `Group.Members`, and the exporter builds every membership from +that field. Against a real LLDAP directory the `memberships` block is therefore +always empty, while the fixture-based tests pass because the mock fills it in. + +The unit of work is the evidence, not the enumeration: an export that cannot +prove it saw the whole directory must say which one it is. + +## Enumerate groups independently of membership + +```task +id: KEY-WP-0018-T01 +status: done +priority: medium +state_hub_task_id: "abb11f82-06e9-58ee-8be2-6d20491e9524" +``` + +Add an optional `domain.GroupLister` capability (`ListGroups`) and implement it +on `LDAPAdapter` with a direct group-subtree search that reads `member` and +`uniqueMember`, so `Group.Members` is populated from the directory rather than +left empty. Keep it optional rather than widening `UserRepository`: the OIDC +layer has no use for it, and every existing implementation and test double would +otherwise have to grow a method it never calls. + +Added `domain.GroupLister` and `LDAPAdapter.ListGroups`, searching the group +subtree for `groupOfNames`/`groupOfUniqueNames` and reading `member` with +`uniqueMember` as the fallback attribute. Compile-time assertions on the adapter +now state both contracts it satisfies. The adapter tests assert the filter is +*not* a membership filter and that a member-less group survives the mapping, +which is the property the whole task exists for. + +## Report or fail on incomplete reads + +```task +id: KEY-WP-0018-T02 +status: done +priority: medium +state_hub_task_id: "d08f62dd-42e8-5c22-ba50-15b53f7473fd" +``` + +Record on the result which enumeration actually ran — a complete directory +enumeration or the membership-derived fallback — and treat a failed group +enumeration as fatal rather than as a silent gap. In the fallback path, record +each skipped user in the incompatibility report instead of discarding the error, +and derive memberships from the discovered user/group pairs. Emit `partial` +telemetry, and have the CLI say which enumeration produced the snapshot, so a +degraded export is legible without reading the YAML. + +`ExportResult` gained `groupEnumeration` — written unconditionally, so nobody has +to infer completeness from an absent field — and a `Complete()` predicate that +requires both a directory enumeration and an empty report. The fallback path +records its own incompleteness as a report entry before it starts, so the +snapshot says so even when every user lookup succeeds. + +The fallback also had to stop deriving memberships from `Group.Members`: the +LLDAP adapter never populated that field, so against a real directory the +`memberships` block was always empty while the fixture-backed tests passed. It +now derives each membership from the user/group pair actually observed. + +## Define ordering and test the failure modes + +```task +id: KEY-WP-0018-T03 +status: done +priority: medium +state_hub_task_id: "3cd47458-9469-5cd9-a0b1-f2894402e1f8" +``` + +Sort users, groups and memberships on stable keys so an unchanged directory +exports byte-identically. Cover the cases the assessment names and the current +suite does not: an empty group, a group enumeration failure, a per-user lookup +failure in the fallback path, and repeat-run determinism. + +Sorted users and groups by ID and memberships by group then user. Four exporter +tests and two adapter tests cover the named cases; the determinism test runs the +export three times over deliberately unsorted input and compares the full +ordering, rather than asserting on a single sorted field. + +Recorded in `SCOPE.md` and in G05's status what this does and does not establish: +completeness evidence for the user/group/membership surface, not for credential +migration, which stays under G03. diff --git a/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0019-upstream-provider-token-verification.md b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0019-upstream-provider-token-verification.md new file mode 100644 index 00000000..dfeb9229 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0019-upstream-provider-token-verification.md @@ -0,0 +1,155 @@ +--- +id: KEY-WP-0019 +type: workplan +title: "Verify upstream Authelia ID tokens" +domain: infotech +repo: key-cape +status: finished +owner: claude +topic_slug: upstream-provider-token-verification +created: "2026-09-07" +updated: "2026-09-07" +state_hub_workstream_id: "87ea18ea-dc60-52ce-baf4-476b46aa06f9" +--- + +Closes the remaining half of gap G01 in +`history/2026-09-05-011726-scope-intent-assessment.md`. The Authelia adapter +decodes upstream ID-token claims without verifying the signature, issuer, +audience or expiry, justified in a comment by a server-to-server TLS boundary. + +**Operator decision (2026-09-07, Bernd):** verify the token itself. The +KeyCape→Authelia hop is to be HTTPS as defence in depth, but KeyCape will not +monitor, check or gate on that — transport enforcement is friction without +protection value here, and a check that must be configured correctly to help is +itself a failure mode. Verification is chosen precisely because it does not +depend on the transport being what we believe it is. No HTTPS validation, opt-in +flag or transport telemetry is to be added. + +## Extract a shared RS256/JWKS verifier + +```task +id: KEY-WP-0019-T01 +status: done +priority: high +state_hub_task_id: "68c1b099-96b4-5065-a4be-8592438a0ca9" +``` + +`internal/authclient` already contains a strict RS256 verifier — kid required, +no `crit`, RSA-only keys of at least 2048 bits with an odd exponent. The upstream +verification needs the same rules. Duplicating security-critical verification is +how two copies drift and one silently misses a fix, so extract the reusable half +into `internal/jose`: strict JWK-set parsing and signature verification returning +claims. Claim policy stays with each caller, whose audience, nonce and issuer +rules genuinely differ. + +Added `internal/jose`: strict JWK-set parsing (RSA signing keys only, at least +2048 bits, odd exponent 3 or greater, no duplicate key ids) and RS256 signature +verification returning claims. All failures collapse to one `ErrVerification` +so a caller cannot leak which check failed back to whoever supplied the token. +`authclient` is not yet migrated onto it — see T05. + +## Verify the Authelia ID token + +```task +id: KEY-WP-0019-T02 +status: done +priority: high +state_hub_task_id: "8105030b-c91f-5a40-a221-4a38c8531e1c" +``` + +Before trusting any claim from the upstream ID token: verify the RS256 signature +against Authelia's published keys, require the issuer Authelia advertises, require +KeyCape's own client ID in the audience, and reject expired or not-yet-valid +tokens. Fail closed — an unavailable or unparseable key set denies the login +rather than falling back to unverified claims. Resolve Authelia's metadata and +key set from the server-side token base URL so a split-horizon deployment, where +the advertised public URL is not reachable from KeyCape, still verifies; allow +explicit issuer and JWKS overrides for deployments where that inference is wrong. +Refresh the key set once on an unknown key id so provider key rotation does not +require a KeyCape restart. Replace the comment claiming the TLS boundary as +justification. + +`idTokenVerifier` in the adapter resolves the issuer and JWKS path from the +provider's discovery document, rebasing the advertised `jwks_uri` path onto the +server-side token base URL so split-horizon deployments resolve, with +`issuer`/`jwksUrl` config overrides where that inference is wrong. Key sets are +cached and refetched once on an unknown key id. `parseIDTokenClaims` remains for +diagnostics but is documented as outside the authentication path. + +## Prove the rejections + +```task +id: KEY-WP-0019-T03 +status: done +priority: high +state_hub_task_id: "163f64dd-aa87-5ce3-b3be-d134fa993eb6" +``` + +Negative tests per condition: forged signature, `alg: none` and other algorithms, +unknown key id, wrong issuer, missing and wrong audience, expired token, future +`iat`/`nbf`, malformed key set, and an unreachable JWKS endpoint. A positive case +must confirm a genuine token still authenticates, and a rotation case that a new +key id is picked up after one refresh. + +Thirteen rejection cases in `verify_test.go` plus the algorithm and rotation +cases. Confirmed they catch the original defect rather than merely passing: with +the unverified parse restored, all fifteen fail. The existing adapter tests now +build genuinely signed tokens against a fixture provider, so they exercise the +real verification path instead of being routed around it. + +## Reconcile the records + +```task +id: KEY-WP-0019-T04 +status: done +priority: medium +state_hub_task_id: "8a208092-7e85-5ad9-a8c4-0dddc66aaaee" +``` + +Update `SCOPE.md` and G01's status in the assessment to state that upstream +provider tokens are now verified independently of transport, and record the +operator decision and its reasoning so the absence of transport enforcement reads +as a choice rather than an oversight. G01 closes with this; complete profile +conformance is still not claimed. + +SCOPE.md and G01's status record the verification and the operator decision, +including why no transport enforcement exists, so its absence reads as a choice. + +## Consolidate the caller verifier + +```task +id: KEY-WP-0019-T05 +status: done +priority: medium +state_hub_task_id: "0dc909fe-1772-5816-9e11-dbe7664a4e1f" +``` + +Move `internal/authclient`'s inline verification onto `internal/jose` so one +implementation serves both paths. Kept separate from T01/T02 deliberately: the +caller verifier is a tested security path, and destabilising it in the same +change that introduces upstream verification would confuse the evidence for both. + +`Client.Verify` now fetches the raw key set and delegates parsing and signature +checking to `internal/jose`, keeping its own claim policy — audience and nonce +bindings are the caller's and cannot live in a shared verifier. Its existing +tests pass unchanged, which is the point: the migration preserved behaviour. + +One deliberate strictness increase: a key set containing any malformed RSA +signing key is now refused outright, where the previous code would have used a +good key alongside a bad one. KeyCape's own `/jwks` publishes a single key, so +this affects no current deployment. + +Also added direct tests for `internal/jose` (17 cases). It is now the single +verifier behind both paths, so testing it only through its callers would leave +its edges — duplicate key ids, `crit`, even exponents, undersized moduli — to be +covered by accident. + +Follow-on evidence for the same task: "existing tests pass unchanged" shows the +move preserved behaviour, not that the behaviour is checked, so the check was +made explicit. Disabling the RSA comparison in `jose.Verify` fails both callers' +suites, which establishes the shared verifier is load-bearing on each path. +Added two caller-side cases the existing tamper case cannot reach, since it +alters signature bytes for a key that is legitimately published: a structurally +valid token signed by an unpublished key under a published kid, which fails only +if key selection is bound to the key set, and an undersized modulus in the +published set, which must deny rather than fall through to the claims. diff --git a/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0020-migration-contract-preservation.md b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0020-migration-contract-preservation.md new file mode 100644 index 00000000..7b6f3be9 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0020-migration-contract-preservation.md @@ -0,0 +1,107 @@ +--- +id: KEY-WP-0020 +type: workplan +title: "Make the Keycloak transform preserve or name every policy field" +domain: infotech +repo: key-cape +status: finished +owner: claude +topic_slug: migration-contract-preservation +created: "2026-09-07" +updated: "2026-09-07" +state_hub_workstream_id: "803845de-98ad-5a5b-89ec-11bd9e0fafec" +--- + +Closes the first half of gap G03 of +`history/2026-09-05-011726-scope-intent-assessment.md` — semantic preservation. +The second half, proof against a running Keycloak, is G04 and stays open. + +`keycape-to-keycloak` calls `Transform`, which passes no clients, so the realm it +writes has an empty `clients` array and nothing says the service-identity +contract was not migrated. `TransformWithClients` exists but no caller reaches +it. Where clients are supplied, `mapClient` always enables the standard flow +regardless of grant types, and drops `Audience`, `ServiceSubject`, `Tenant`, +`Roles`, `TokenLifetime`, `MFARequired`, `SecretRef` and the handoff URLs. +`mapUser` drops the canonical ID, tenant and roles. Realm roles and client scopes +are written as empty containers with a comment saying they "can be extended". + +The failure mode is not the missing mapping — it is that a dropped field and an +inapplicable one look identical in the output. An operator diffing the realm JSON +against the KeyCape config has no way to tell what this tool decided not to carry +across. Preserve what Keycloak can express, and name the rest. + +## Give the CLI the client registrations + +```task +id: KEY-WP-0020-T01 +status: done +priority: high +state_hub_task_id: "6f6bcfd7-16c5-5311-9568-ad9aa9c3b60a" +``` + +Add a `-clients` flag reading the KeyCape config's client registrations and pass +them through `TransformWithClients`. When no client file is given, say so on +stderr and in the report rather than emitting an empty `clients` array that reads +like a realm with no clients. + +Added `-clients`, reading registrations through a new `config.Registrations()` +that converts without resolving secrets — migration tooling needs the contract, +never the material, so `ClientSecret` is left empty by construction rather than +by remembering not to use it. An unparseable `tokenLifetime` is an error there, +since silently dropping a per-client lifetime is the defect class this closes. +Run against `config/dev-config.yaml`, all five registrations now reach the realm. + +## Preserve what Keycloak can express + +```task +id: KEY-WP-0020-T02 +status: done +priority: high +state_hub_task_id: "88c0126c-fd73-5178-af1f-a018708ff8fb" +``` + +Derive `standardFlowEnabled` and `serviceAccountsEnabled` from the declared grant +types instead of hardcoding the standard flow on. Carry the audience, tenant, +service subject, roles and principal type as protocol mappers, per-client +lifetime as the Keycloak client attribute that expresses it, and the handoff URLs +and secret reference as attributes — the reference, never a secret value. Derive +realm roles and client scopes from the users and clients actually present rather +than emitting empty containers. Carry the user's canonical ID, tenant and roles. + +Flows now follow the grants: the old mapping set `standardFlowEnabled` on every +client, so migrating a `client_credentials` service registration silently gave it +the browser flow. Audience, tenant, service subject and roles ride as protocol +mappers, since Keycloak has no native concept for the profile's claims and would +otherwise issue tokens the profile rejects. Lifetime, handoff URLs and the secret +*reference* are attributes; a test marshals the realm and fails if a resolved +secret value appears, because a realm import file is not a custody boundary. +Realm roles and client scopes are derived and sorted. + +## Name everything not preserved + +```task +id: KEY-WP-0020-T03 +status: done +priority: high +state_hub_task_id: "5b8127b2-c01c-5d3c-afbf-adbb368941e6" +``` + +Report each field the transform cannot carry, at the point it is dropped, so the +output distinguishes a deliberate omission from a gap: passwords and MFA +credentials, MFA policy enforcement, and subject continuity — LLDAP's canonical +ID is a DN, and Keycloak will mint its own `sub`, so tokens after a migration +will not carry the same subject unless something downstream maps it. Fail the +realm-level validation when a supplied client's contract is not fully +represented. Test that each unpreserved field appears in the report. + +`UnpreservedReport()` is separate from `ValidationReport()`. Folding them +together was the first attempt and it broke an existing test asserting a clean +export reports nothing — correctly so: consistency with the snapshot and +completeness of the migration are different questions, and one list cannot answer +both. Keeping them apart preserves the existing contract and makes the +distinction the point rather than a side effect. + +Six tests cover the flows, the mappers and lifetime, the secret-value exclusion, +the derived roles and scopes, every named limit, and the separation of the two +reports. Mutation-checked: restoring the hardcoded `standardFlowEnabled` fails +the flow test. An incomplete transform now emits `partial` telemetry. diff --git a/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0021-snapshot-attribute-validation.md b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0021-snapshot-attribute-validation.md new file mode 100644 index 00000000..1f6efeb3 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0021-snapshot-attribute-validation.md @@ -0,0 +1,101 @@ +--- +id: KEY-WP-0021 +type: workplan +title: "Make snapshot attribute validation enforce a real rule" +domain: infotech +repo: key-cape +status: finished +owner: claude +topic_slug: snapshot-attribute-validation +created: "2026-09-07" +updated: "2026-09-07" +state_hub_workstream_id: "733ad1a5-c4d8-5c2f-8608-c51bf1d5b17e" +--- + +Closes gap G06 of `history/2026-09-05-011726-scope-intent-assessment.md`: +`checkNoUnknownAttributes` is named for a rule it does not implement. It rejects +blank keys, which is not an allow-list, so a snapshot carrying arbitrary or +malformed LDAP attribute names passes a check whose name says otherwise. A rule +that passes for the wrong reason is worse than an absent one, because a report +listing `no_unknown_attributes: passed` is read as evidence. + +One part of the assessment's description needs correcting rather than +implementing: it says group membership validation checks nonempty member IDs +rather than complete referenced identity existence. `checkValidGroupMemberships` +does only check emptiness, but the semantic rule `checkReferencedUsersExist` +already resolves every member against the user set and fails on an unknown one. +The reference constraint exists; it lives in a different rule than the one the +assessment looked at. + +## Enforce real attribute constraints + +```task +id: KEY-WP-0021-T01 +status: done +priority: medium +state_hub_task_id: "e2921983-853c-501b-8dfc-158751dea093" +``` + +`ldapAttributes` is defined by the canonical model as raw attributes *not covered +by* the model, so an allow-list of permitted names cannot be derived from the +schema — the field exists precisely to carry what the schema does not name. Two +constraints are derivable and worth enforcing: + +- the attribute name must be a syntactically valid LDAP attribute descriptor + (RFC 4512: a letter followed by letters, digits or hyphens, or a numeric OID), + since a name that cannot be written to a directory cannot round-trip; and +- it must not shadow a canonical field's documented LDAP mapping (`uid`, `cn`, + `mail`), because two sources for one value is how an export and a directory + come to disagree, and the model already owns those. + +Attribute descriptors are case-insensitive in LDAP, so keys differing only by +case are a collision, not two attributes. Keep the blank-key check. Rename the +rule to describe what it actually enforces rather than leaving a name that +overpromises. + +Implemented as `raw_attributes_well_formed`, replacing `no_unknown_attributes` +in both `validator.go` and `spec/ldap-schema.yaml`, with the schema entry stating +plainly that this is not an allow-list and why one cannot be derived. + +## Prove each rule with an invalid snapshot + +```task +id: KEY-WP-0021-T02 +status: done +priority: medium +state_hub_task_id: "de4caf0a-39c6-5148-a986-db18a1308505" +``` + +The assessment asks for invalid-snapshot cases proving each stated rule. Add one +per constraint — malformed descriptor, shadowed canonical mapping, case-only +duplicate, blank key — plus passing cases for legitimate raw attributes and a +valid numeric OID, so the rule is shown to be selective rather than merely +strict. + +Eleven malformed-descriptor cases, six canonical-mapping cases across casings, a +case-only duplicate, a message-content case, and five accepted-attribute cases. +Confirmed they test the rule rather than merely passing: making the check return +early fails four of the five tests. + +## Reconcile the records + +```task +id: KEY-WP-0021-T03 +status: done +priority: medium +state_hub_task_id: "8a48c6cc-1e7e-5fd4-abfa-2b893c3d400d" +``` + +Update `SCOPE.md` and G06's status. State what the rule now enforces and, just as +importantly, what it still does not: this is not schema-derived validation of +every attribute against a directory schema, and the canonical model cannot +provide one for a field defined as everything the model does not cover. Correct +the assessment's reference-constraint premise rather than restating it. + +`history/…-scope-intent-assessment.md` was being edited by a concurrent session +when this workplan was written; make this edit only against a clean tree. + +Done against a clean tree after the other session committed. G06's status records +both corrections: no allow-list is derivable for a field defined as what the +model does not cover, and the reference constraint was already implemented in +`checkReferencedUsersExist` rather than missing. diff --git a/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0022-replacement-harness-and-external-conformance.md b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0022-replacement-harness-and-external-conformance.md new file mode 100644 index 00000000..7c69629e --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0022-replacement-harness-and-external-conformance.md @@ -0,0 +1,136 @@ +--- +id: KEY-WP-0022 +type: workplan +title: "Make the replacement harness runnable and target conformance externally" +domain: infotech +repo: key-cape +status: finished +owner: claude +topic_slug: replacement-harness-and-external-conformance +created: "2026-09-07" +updated: "2026-09-07" +state_hub_workstream_id: "94d74b17-1b2f-57dd-b6c3-50f10a7dccd9" +--- + +Closes the runnable half of gap G04 of +`history/2026-09-05-011726-scope-intent-assessment.md`. The Scenario B and C +scripts cannot execute: they reference compose files that do not exist, call +binaries at `src/bin/` where the Makefile builds to root `bin/`, pass `--base-dn` +to `lldap-to-ldap` whose flag is `--basedn`, and invoke a hardcoded workstation Go +path from outside the Go module. + +The deeper problem is that repairing the shell would not prove anything. Both +scripts set `KEYCAPE_TEST_ISSUER`, but the profile suite builds its own `httptest` +server and never reads it, so the tests pass identically whether or not the +external provider is running. A harness that cannot fail for the reason it exists +is worse than a missing one. + +## Repair the harness prerequisites + +```task +id: KEY-WP-0022-T01 +status: done +priority: high +state_hub_task_id: "1d552328-e75b-57fd-83bc-e469ae46b397" +``` + +Fix the binary paths, the `--basedn` flag, and the Go invocation so the scripts +run from a clean checkout with `make build`: use the module directory rather than +a workstation-specific Go path, and fail with a clear message when a prerequisite +binary or a required environment value is absent instead of part-way through. + +Scenario B rewritten: root `bin/`, `go -C src` so the module root is right, +prerequisite checks before anything starts, and a cleanup trap so a failure does +not leave a stack running. Scenario C repaired in place — `bin/` paths, `--basedn` +for `lldap-to-ldap` (`lldap-export` correctly takes `--base-dn`; the two flags +genuinely differ), and the same module-relative test invocation. + +## Add the missing stacks + +```task +id: KEY-WP-0022-T02 +status: done +priority: high +state_hub_task_id: "06f34e8b-f3d9-53ed-95c5-02e5063774a0" +``` + +Provide `docker-compose.scenario-b.yml` (Keycloak over the migrated realm) and +`docker-compose.scenario-c.yml` (OpenLDAP seeded from generated LDIF, plus +Keycloak). Validate them with `docker compose config` rather than assuming they +parse. Whether a full scenario passes end to end is a separate claim from whether +the stack definition is valid, and only the second is established here. + +Both files added and validated with `docker compose config`. Scenario B was also +started for real; Scenario C's stack has not been run. + +## Target conformance at an external issuer + +```task +id: KEY-WP-0022-T03 +status: done +priority: high +state_hub_task_id: "d837730d-e9d0-5adb-9dda-ffc47661fca3" +``` + +Add a suite that reads `KEYCAPE_TEST_ISSUER` and exercises that issuer over HTTP: +discovery metadata, the JWKS parsing under the same strict rules the runtime +applies, and — when client credentials are supplied — a real token exchange whose +claims are verified against the published keys. It must skip cleanly when the +variable is unset so ordinary `make test` is unaffected, and it must fail when +pointed at an issuer that does not meet the profile, which is the property the +current harness lacks. + +`src/tests/conformance` checks that the issuer advertises itself, the profile +authorization surface (code, authorization_code, S256, RS256), that published +keys parse under `internal/jose` — the same rules the runtime applies, so a +provider publishing keys KeyCape would refuse fails here rather than at first +login — that excluded grants are absent, and, with credentials, that an issued +token verifies against the published keys and carries a matching issuer. + +## Prove the suite against a real provider + +```task +id: KEY-WP-0022-T04 +status: done +priority: high +state_hub_task_id: "cb4d8af7-76a4-50b8-bee5-eb1c52c2f894" +``` + +Run the new suite against an actual Keycloak instance, not only against KeyCape, +since the point of the scenario is a replacement provider. Record what genuinely +passed and what remains unproven. Do not describe a partially exercised scenario +as a demonstrated live swap. + +Run against Keycloak 26.0. Discovery, authorization surface and published-key +checks passed. A `client_credentials` exchange against a service client created +through the admin API returned a token that verified against Keycloak's JWKS via +`internal/jose`, with a matching issuer claim — the end-to-end property a relying +party depends on, proved against a replacement provider rather than against +KeyCape's own handlers. + +The suite also failed, correctly. Stock Keycloak advertises `implicit` and +`password` in its discovery document. In Keycloak these are server capabilities, +not per-client toggles, so no realm the transformer emits can remove them: a +migrated Keycloak presents a wider grant surface than KeyCape. Scenario B +therefore reports a real failure today, which is the harness working. KeyCape +itself was not targeted in this run — the dev stack needs key material absent +from the checkout — so the suite is proved against the replacement provider, not +yet against both sides of the swap. + +## Reconcile the records + +```task +id: KEY-WP-0022-T05 +status: done +priority: medium +state_hub_task_id: "2cb63801-8182-5c71-ab8d-9a1c377f418c" +``` + +Update `SCOPE.md` and G04's status with what the harness now does and what +replacement claims remain unproven — in particular that directory migration, MFA +and unchanged relying-party behaviour against a live Keycloak are not established +by this work. G04 does not fully close here. + +SCOPE.md and G04's status record both the working harness and the grant-surface +finding, which substantiates with evidence what SCOPE previously asserted without +it. Scenario C has still never been run end to end. diff --git a/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0023-live-migration-proof.md b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0023-live-migration-proof.md new file mode 100644 index 00000000..3e7c83cb --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0023-live-migration-proof.md @@ -0,0 +1,136 @@ +--- +id: KEY-WP-0023 +type: workplan +title: "Prove the migration against live directories and fix what that surfaced" +domain: infotech +repo: key-cape +status: finished +owner: claude +topic_slug: live-migration-proof +created: "2026-09-08" +updated: "2026-09-08" +state_hub_workstream_id: "9f19bfa0-78d1-5b5c-9b64-e251fa4838d6" +--- + +Finishes the unproven half of gap G04. KEY-WP-0022 made the harness runnable and +targeted conformance at a live issuer; what remained was directory migration +against a real directory, both sides of the swap measured, and the explicit +limits recorded. + +Running it found four defects that no amount of reading would have. That is the +result worth keeping: every one of them passed the full unit suite. + +## Run the export against a live LLDAP + +```task +id: KEY-WP-0023-T01 +status: done +priority: high +state_hub_task_id: "b7b2ea88-8457-5fae-a500-a2d0f75754de" +``` + +**Defect: every LLDAP user search pointed at a branch that does not exist.** +`Config.userOU()` defaulted to `ou=users`; LLDAP stores users under `ou=people`, +and nothing in this repository set `UserOU` to correct it. `LookupUser`, +`ListUsers` and `ValidatePassword` all derive their search base from it, so all +three silently found nothing against a stock LLDAP — human login included, not +just the export. + +Symptom that exposed it: exporting from a live LLDAP returned **zero users while +still emitting a membership referencing `uid=admin`**, a snapshot the repository's +own validator rejects with `referenced_users_exist`. Default corrected to +`ou=people` and pinned by a test; the re-run exports one user and validates +clean. + +## Correct the rule I broke in KEY-WP-0021 + +```task +id: KEY-WP-0023-T02 +status: done +priority: high +state_hub_task_id: "2f988626-ed08-52ce-919a-cd45d48e712d" +``` + +**Defect of my own making.** The LLDAP adapter records `_validation_warning` +inside `LDAPAttributes`. The `raw_attributes_well_formed` rule added in +KEY-WP-0021 rejects underscores as invalid attribute descriptors, so the +exporter's own annotation made its own output fail validation — a false positive +shipped one workplan earlier and caught only by validating a real export. + +Keys prefixed with `_` are now recognised as tooling annotations and skipped, +documented as such. A test proves the exemption does not weaken the rule: a +malformed real attribute alongside an annotation still fails. + +## Prove the LDIF loads into a real OpenLDAP + +```task +id: KEY-WP-0023-T03 +status: done +priority: high +state_hub_task_id: "0f503342-0ca8-55ad-b7e4-e2316d4d7bad" +``` + +**Defect: migrated group memberships were dangling.** `resolveMemberDN` returned +a member that already looked like a DN unchanged, so members kept their *source* +DNs (`uid=admin,ou=people,…`) while `writeUser` wrote entries to the *target* +branch (`ou=users`). Groups named entries the migrated directory does not +contain. Only the naming value is carried across now; branch and naming +attribute come from the target. + +**Defect: empty groups could not be loaded at all.** `groupOfNames` makes +`member` a MUST, so OpenLDAP rejected every memberless group with an object class +violation and aborted the load. Since KEY-WP-0018 the export deliberately +preserves empty groups, so this was reachable by any real migration. Empty groups +now reference a placeholder entry the same LDIF creates — an `organizationalRole`, +not a person, so it cannot be mistaken for a migrated user — emitted only when +some group needs it. + +**Defect: the compose file could not start.** `bitnami/openldap:2.6` does not +exist in the registry. It passed `docker compose config` in KEY-WP-0022, which is +exactly the gap between a file that parses and a stack that runs. Pinned to +`osixia/openldap:1.5.0`, the image the scenario was actually proved against. + +Proof: LLDAP → export → validate → LDIF → `ldapadd` into OpenLDAP 1.5.0, all +seven entries added with no errors, and every `member` value in the loaded +directory resolves to an entry that exists. + +## Measure both sides of the swap + +```task +id: KEY-WP-0023-T04 +status: done +priority: high +state_hub_task_id: "8a7fe95e-d463-5e74-81fa-5fa60a879774" +``` + +KeyCape was run locally against a generated key and targeted with the +conformance suite: all five checks pass, including the excluded-grant check +Keycloak fails. The Keycloak side was exercised twice — the stock realm in +KEY-WP-0022, and here the *migrated* realm produced from the live export, which +imported cleanly and served discovery. The migrated user arrived carrying +`keycape.canonicalId`. + +The divergence is now measured rather than asserted: KeyCape passes 5/5, a +migrated Keycloak passes 4/5 and fails on advertising the excluded `implicit` and +`password` grants. + +## Record what is proved and what is not + +```task +id: KEY-WP-0023-T05 +status: done +priority: medium +state_hub_task_id: "acef6ceb-6dea-56e1-94bc-e5d589ef3b6f" +``` + +G04 is substantially closed: stacks are reproducible and were run, conformance is +externally targeted and was run against three issuers, and directory migration is +proved end to end into a real directory. + +Not closed, and stated as such: no browser login was performed against the +migrated realm, so unchanged relying-party behaviour is not established, and MFA +was not exercised. Credential and MFA migration is a **scope exclusion** rather +than a test gap — SCOPE says it is not supplied, so no harness can demonstrate +it. `keycape-to-keycloak` already reports that subject continuity is not +preserved: the canonical ID survives as an attribute while Keycloak mints its own +`sub`, so relying parties keyed on `sub` will not recognise migrated users. diff --git a/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0024-tenant-roles-opt-in-wiring.md b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0024-tenant-roles-opt-in-wiring.md new file mode 100644 index 00000000..baa0bbd1 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0024-tenant-roles-opt-in-wiring.md @@ -0,0 +1,62 @@ +--- +id: KEY-WP-0024 +type: workplan +title: "Wire tenant_roles as explicit opt-in configuration" +domain: infotech +repo: key-cape +status: finished +owner: claude +topic_slug: tenant-roles-opt-in-wiring +created: "2026-09-08" +updated: "2026-09-08" +state_hub_workstream_id: "d75f52f2-df0d-5a56-8394-892feeec4b4d" +--- + +Closes gap G07. The tenant-engine client and `TokenHandler.TenantEngine` were +implemented and tested, but `main.go` supplied no client and exposed no +configuration, so the stock executable left the field nil and never emitted +`tenant_roles`. The capability existed at library level only. + +Wired rather than documented away as library-only: the claim is cheap, the +adapter already fails open, and leaving a tested capability unreachable from the +binary invites the same gap being rediscovered later. + +## Add opt-in configuration and wire the client + +```task +id: KEY-WP-0024-T01 +status: done +priority: medium +state_hub_task_id: "0e53485b-7e94-5dd8-8e02-1d18f5413a78" +``` + +Added a `tenantEngine` config block with `baseURL` and an optional `timeout`. +An empty `baseURL` disables the claim, which keeps the stock server's behaviour +exactly as it was — enabling it is a deliberate act. + +Validation treats a configured source as one that must work: the URL must be +http/https with a host, and the timeout must parse and fall in (0, 10s], since it +sits on the synchronous token-issuance path. A `timeout` set without a `baseURL` +is rejected rather than ignored — it means someone expected the claim to be on +and it silently would not have been. + +## Verify it in the built executable + +```task +id: KEY-WP-0024-T02 +status: done +priority: medium +state_hub_task_id: "c4279ce0-f6f8-5612-a48a-c1d2fd3da3b6" +``` + +G07's closure criterion is the built executable, not the wiring, so all three +behaviours were checked by running `bin/keycape` and inspecting real tokens +rather than by reading the code: + +- configured against a stub tenant-engine, a `client_credentials` token carries + `tenant_roles: [capability:approve, capability:observe]`; +- with no `tenantEngine` block, the claim is absent; +- configured but with the source down, issuance still succeeds and the claim is + absent — the fail-open behaviour the adapter documents, confirmed end to end. + +Nine validation cases cover the accepted and rejected configurations. diff --git a/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0025-runtime-lifecycle-and-readiness.md b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0025-runtime-lifecycle-and-readiness.md new file mode 100644 index 00000000..a53e6467 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0025-runtime-lifecycle-and-readiness.md @@ -0,0 +1,118 @@ +--- +id: KEY-WP-0025 +type: workplan +title: "Give the runtime real readiness, graceful shutdown and stated operational limits" +domain: infotech +repo: key-cape +status: finished +owner: claude +topic_slug: runtime-lifecycle-and-readiness +created: "2026-09-08" +updated: "2026-09-08" +state_hub_workstream_id: "70f74bbd-6748-5bbe-b261-a310b6f24e0c" +--- + +Closes gap G08. `/healthz` returned a constant without probing anything, the +server called `ListenAndServe` with no signal handling, and the operational +limits of in-memory state, startup-loaded keys and local-only logout were spread +across code comments rather than stated anywhere an operator would look. + +G08 explicitly does not require shared storage or refresh tokens. The work is +therefore to make readiness real, make restarts survivable for in-flight +requests, and write the limits down honestly. + +## Separate readiness from liveness + +```task +id: KEY-WP-0025-T01 +status: done +priority: medium +state_hub_task_id: "0e0d3349-efaf-546c-a58d-2a9b736278a9" +``` + +Added `internal/server/readiness` and a `/readyz` endpoint probing LLDAP, +Authelia and privacyIDEA. `/healthz` stays liveness and deliberately probes +nothing: wiring liveness to dependency health means an orchestrator restarts +KeyCape when a dependency blinks, and since a restart also discards every +in-flight login, the reaction is worse than the condition. + +Design decisions worth keeping: + +- **LLDAP is probed with a bind, not a dial.** A rotated or revoked service + password leaves the port open and every lookup failing — precisely the state + readiness exists to catch, and precisely what a TCP dial would miss. +- **The body names the failing check but never the reason.** The endpoint is + unauthenticated, and upstream error text carries hostnames and sometimes + credentials-in-URLs. Detail goes to the log. A test asserts a probe error + containing a credentialed URL does not reach the response. +- **Results are cached for 2s.** Not an optimisation: without it, anyone able to + reach an unauthenticated endpoint could drive one upstream request per + dependency per call. +- **Probes run concurrently, each bounded at 3s**, so readiness costs the slowest + probe rather than their sum, and a hung dependency makes the endpoint answer + rather than hang alongside it. + +## Shut down without dropping requests + +```task +id: KEY-WP-0025-T02 +status: done +priority: medium +state_hub_task_id: "0bea0780-523a-587e-a157-d429df15307b" +``` + +`SIGTERM`/`SIGINT` now drain in-flight requests for up to 15 seconds. The grace +period sits under the 30s read/write timeouts so a stuck request cannot outlive +the window an orchestrator allows before `SIGKILL`. + +In-memory login state is deliberately not preserved: a browser mid-login must +start again after a restart. That is a property of the single-replica topology, +documented rather than papered over. + +## State the operational limits + +```task +id: KEY-WP-0025-T03 +status: done +priority: medium +state_hub_task_id: "97eba4bb-f8a0-5941-bdc8-6d59b0fa2942" +``` + +`docs/operations.md` states the supported topology — exactly one replica, because +authorization codes, login sessions and handoffs are process-local and no sticky +configuration makes a second replica safe — and the consequences: restarts drop +in-flight logins while issued tokens keep working, and rolling deploys have a +window where new logins fail. + +It also records three limits that are easy to get wrong in operation: + +- the key ID is the constant `key-1`, so rotating the key without changing it can + leave consumers caching JWKS by `kid` rejecting freshly issued tokens; +- removing a client registration does not revoke its issued tokens, since there + is no revocation or introspection endpoint — they stay valid until expiry; +- `/logout` clears the local session only, not the Authelia session or any token. + +No throughput or resource figures are given: nothing here benchmarks KeyCape, so +any number would be invented. + +## Verify in the running server + +```task +id: KEY-WP-0025-T04 +status: done +priority: medium +state_hub_task_id: "bc64fdc2-0a31-5dab-afd1-5e7b6d11d2b3" +``` + +Checked against the built executable rather than the handlers, following the +lesson from KEY-WP-0023: + +- with every dependency down, `/readyz` returns 503 naming all three as failed + while `/healthz` still returns 200 — the split doing its job; +- starting LLDAP flips its check to `ok` while the others stay failed, so the + probes are measuring what they claim; +- 40 requests issued across a `SIGTERM` all returned 200, and the log shows the + signal handled and shutdown completed. + +Seven unit tests cover the reporter, including the disclosure, caching, timeout +and concurrency properties above. diff --git a/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0026-packaging-bootstrap-and-credential-handling.md b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0026-packaging-bootstrap-and-credential-handling.md new file mode 100644 index 00000000..1f96a2ac --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0026-packaging-bootstrap-and-credential-handling.md @@ -0,0 +1,123 @@ +--- +id: KEY-WP-0026 +type: workplan +title: "Reconcile packaging, bootstrap and migration credential handling" +domain: infotech +repo: key-cape +status: finished +owner: claude +topic_slug: packaging-bootstrap-and-credential-handling +created: "2026-09-08" +updated: "2026-09-08" +state_hub_workstream_id: "d2b04d59-41b1-531b-8cab-a980328611b4" +--- + +Closes gap G09. Five separate defects, only loosely related: the dev stack could +not start from a clean checkout, the image shipped one binary of five, the +publish workflow named a different registry than the one the cluster pulls from, +the exporter took a password on argv, and every migration artifact was written +world-readable. + +## Take the bind password off argv + +```task +id: KEY-WP-0026-T01 +status: done +priority: medium +state_hub_task_id: "510a4603-337f-5ceb-b6b7-b9126d8605f0" +``` + +`lldap-export` took `--bind-pw`, so the service account password was visible to +any local user through `ps`, and captured by shell history and process +accounting. It now prefers `KEYCAPE_LLDAP_BIND_PW`, accepts `--bind-pw-file`, and +keeps `--bind-pw` working with a warning — deprecated rather than removed, +because existing runbooks use it and silently breaking them would be worse than +the exposure for one more cycle. + +Conflicting sources are rejected rather than silently ranked: with two supplied, +an operator cannot tell which bind was attempted. A password file has its +trailing newline trimmed, since editors and heredocs add one and binding with it +fails confusingly. Both migration scripts now pass the password by environment. + +## Stop writing migration artifacts world-readable + +```task +id: KEY-WP-0026-T02 +status: done +priority: medium +state_hub_task_id: "679e21a1-e37b-574b-bc25-b9fd46bdfd16" +``` + +The canonical export, the generated LDIF and the Keycloak realm were all written +`0644`. None carries credential material, but the snapshot is a directory dump — +every username, display name, email and group membership in the estate — and it +tended to land in `/tmp`. All three are `0600` now. + +## Ship every binary in the image + +```task +id: KEY-WP-0026-T03 +status: done +priority: medium +state_hub_task_id: "5252a13c-cd5d-5686-a7f5-04587d7d9b1d" +``` + +The image packaged `keycape` alone, so the validator and the three migration +binaries needed a Go toolchain on the host — which defeats shipping an image for +the cutover work they exist to support. All five are built and copied; the issuer +remains the entrypoint and the rest are reachable by overriding it. Verified by +building the image and running each binary inside it. + +## Reconcile the publish target + +```task +id: KEY-WP-0026-T04 +status: done +priority: medium +state_hub_task_id: "45b8c386-41bd-514c-9247-5eba9663d8fb" +``` + +The workflow published to `92.205.130.254:32166/coulomb/key-cape` while the +cluster runs `forgejo.coulomb.social/coulomb/key-cape` (KEY-WP-0013). Whether +those are one registry behind two names or two registries is not determinable +from this repository, which is exactly the problem: a push could not be assumed +to have deployed the current source. Now defaults to the recorded Forgejo name +and stays overridable through a repository variable. + +**Unverified, and needs an operator's eye:** this repository cannot test that the +runner resolves that hostname or that `REGISTRY_USER`/`REGISTRY_TOKEN` are valid +for it. If the next publish fails, set the `REGISTRY` repository variable back to +the address. + +## Make the dev stack bootstrappable + +```task +id: KEY-WP-0026-T05 +status: done +priority: medium +state_hub_task_id: "a750ac2f-cad2-5e7f-a505-2ca6b94f1a5a" +``` + +`docker-compose.dev.yml` mounts `config/dev-key.pem` and `config/authelia/`, +neither in the checkout — correctly, since one is a private key and the other +carries a password hash. `scripts/bootstrap-dev.sh` generates both locally: an +RSA key created under `umask 077` rather than chmodded afterwards, so it is never +briefly world-readable, plus an Authelia configuration, its OIDC signing key and +a user fixture. Everything it writes is git-ignored, and re-running it leaves +existing material alone. + +The fixture password is published in the script deliberately: a reader can see +exactly what it unlocks, which is nothing deployed. + +## Reconcile the records + +```task +id: KEY-WP-0026-T06 +status: done +priority: medium +state_hub_task_id: "cc7c86ae-9c78-5422-8f3c-19441ceeb513" +``` + +`SCOPE.md` and G09's status record what is fixed and what is not: the registry +change is unverified from here, and a documented external bootstrap remains the +route for anything beyond development. diff --git a/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0027-rollout-readiness-and-live-state.md b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0027-rollout-readiness-and-live-state.md new file mode 100644 index 00000000..57e01b2d --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0027-rollout-readiness-and-live-state.md @@ -0,0 +1,130 @@ +--- +id: KEY-WP-0027 +type: workplan +title: "Establish the live state and the rollout gap for G10" +domain: infotech +repo: key-cape +status: finished +owner: claude +topic_slug: rollout-readiness-and-live-state +created: "2026-09-08" +updated: "2026-09-08" +state_hub_workstream_id: "2ab60558-ee14-542c-9f19-bb487de8e668" +--- + +G10 cannot be closed from this repository: it waits on named custody and platform +owners. What was doable was to stop guessing about the live state, verify the +handoffs actually went out, and find out whether main is safe to roll out. The +last question turned up a defect in this repository's own recent work. + +## Verify the handoff receipts independently + +```task +id: KEY-WP-0027-T01 +status: done +priority: medium +state_hub_task_id: "00de2bad-04da-5b58-8cde-aaa144272541" +``` + +KEY-WP-0009 was reopened once because delivery was claimed without receipts, so +the claim that it has now been sent deserved the same scrutiny rather than being +taken on trust. `GET /messages/?from_agent=key-cape` shows all seven present and +dated 2026-09-08T08:21Z: the four KEY-WP-0009 handoffs (netkingdom, +secrets-engine, ops-warden, railiance-platform) and the three admission requests +(railiance-platform custody, approval-engine human callback, ops-warden token +split). No replies had arrived at the time of writing, which is expected hours +after sending and is not itself a blocker to record. + +## Record the live state as observed, not as remembered + +```task +id: KEY-WP-0027-T02 +status: done +priority: medium +state_hub_task_id: "2d513229-1dbd-500e-9100-c0173be7b4de" +``` + +Read-only cluster inspection, `sso` namespace, 2026-09-08: + +- image `forgejo.coulomb.social/coulomb/key-cape:main-153258b`, predating every + change from KEY-WP-0016 onward; +- environment carries `KEYCAPE_RAPP_QONTO_CLIENT_SECRET` only, so the two + approval clients are still not materialized — KEY-WP-0013-T02 remains correctly + blocked; +- four registered clients: `demo-app`, `netkingdom-bootstrap-console` and + `openbao-admin` (public, authorization_code) and `rapp-qonto-client` + (confidential, client_credentials); +- no `tenantEngine` block, so KEY-WP-0024 is inert on rollout. + +**Correction to an earlier claim of mine.** I reported the `ou=people` fix +(KEY-WP-0023) as a production-relevant correction to human login. The deployed +config sets `userOU: "ou=people"` explicitly, so production was never affected. +The fix matters for anyone relying on the default — the dev stack, the scenario +scripts, a fresh deployment — not for this one. + +## Find out whether main is safe to roll out + +```task +id: KEY-WP-0027-T03 +status: done +priority: high +state_hub_task_id: "23c8d04b-c2b7-5347-80a0-6388c1c41c53" +``` + +**Defect found in KEY-WP-0019, before it shipped.** Upstream verification +discovers the expected issuer server-side from `authelia.tokenBaseURL`. The +deployed Authelia derives its advertised issuer from the request Host, confirmed +directly: + +| Host presented | issuer advertised | +| --- | --- | +| `authelia.sso.svc.cluster.local:9091` | `http://authelia.sso.svc.cluster.local:9091` | +| `auth.coulomb.social` | `http://auth.coulomb.social` | + +KeyCape fetches over the in-cluster address, so it would expect the first value +while tokens minted for the browser-facing host carry the second. Verification +fails closed, so if they differ **every human login fails after rollout**, and it +presents as a broken login rather than a configuration error. + +Which value the ID token actually carries could not be settled from here without +performing a real login against production Authelia, which was not done. The risk +is therefore recorded as a rollout precondition, not as a confirmed breakage. + +Two mitigations, both in this repository: + +- `docs/operations.md` documents pinning `authelia.issuer` and `authelia.jwksUrl` + explicitly, with the one-line `curl` that reveals the value the provider will + advertise for a given Host. The overrides already existed; nothing said when + they were needed. +- The failure is now diagnosable. The authentication failure event carries a + specific reason — `id_token_issuer_mismatch`, distinct from + `id_token_signature`, `provider_keys_unavailable` and the rest — so an operator + can tell a misconfiguration from an attack. Five cases cover the mapping. The + browser still learns nothing. + +## Record what each owner owes + +```task +id: KEY-WP-0027-T04 +status: done +priority: medium +state_hub_task_id: "7ba84b61-0458-5820-8ded-c6f59d820303" +``` + +G10's status now names the live state, the outstanding requests with their +receipt ids, and the issuer precondition that must be checked before main is +deployed. The precondition was also sent to railiance-platform as the deployment +owner (message c8b1ad10), with the exact `curl` to settle it, the config keys to +pin, and the `error_type` to look for if it bites — plus a note that the two +approval clients remain unprovisioned, confirmed by inspection rather than +asserted. G10 stays open: nothing here admits custody, registers the human +callback, or reconciles the OpenBao-token boundary, and no repo-local change can. + + +2026-09-08 live return: the actual signed-token uncertainty in T03 is now +resolved by KEY-WP-0013-T02's admitted probe. The issuer is exactly +`https://auth.coulomb.social`; signature/audience/time/nonce checks passed and +the Job exited 0. The temporary route, policies, service and Job/Pod were removed. +`docs/evidence/2026-09-08-upstream-issuer-live-proof.json` is the source receipt. +The configuration owner still ensures the exact pin before main rollout; no +normal config or Deployment changed during this proof. diff --git a/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0028-browser-client-field-validation.md b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0028-browser-client-field-validation.md new file mode 100644 index 00000000..46b0672f --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0028-browser-client-field-validation.md @@ -0,0 +1,89 @@ +--- +id: KEY-WP-0028 +type: workplan +title: "Reject service-identity fields that a browser client silently ignores" +domain: infotech +repo: key-cape +status: finished +owner: claude +topic_slug: browser-client-field-validation +created: "2026-09-09" +updated: "2026-09-09" +state_hub_workstream_id: "6d0cb4fd-17da-5466-8011-5ade1ac46171" +--- + +`serviceSubject` and `roles` are read only on the `client_credentials` path. On a +browser client they are accepted at startup and then ignored: the subject and +roles come from the directory user. A registration that looks effective and is +not fails later, somewhere else, as a downstream rejection rather than as a +registration defect. + +`tokenLifetime` was already rejected this way for non-service clients, so the +rule exists — it was just incomplete. + +## Reject the ignored fields + +```task +id: KEY-WP-0028-T01 +status: done +priority: medium +state_hub_task_id: "e4bce8c7-0e60-55e8-ad32-a3a0c4e46044" +``` + +Config validation now rejects `serviceSubject` and `roles` on any client that +does not use `client_credentials`, naming where the value actually comes from. +Nothing in `config/dev-config.yaml`, the example fixture or the live deployment +sets either field on a browser client, so this breaks no existing configuration — +checked against all three rather than assumed. + +Re-verified against `sso/keycape-config` on 2026-09-09, after a peer raised that +this validation fails closed at startup and a rollout is imminent, so a stray +field would surface as KeyCape failing to boot. The deployed config now holds six +clients: `demo-app`, `netkingdom-bootstrap-console` and `openbao-admin` are +browser clients carrying neither field, while `rapp-qonto-client`, +`secrets-engine-approval` and `approval-engine-operator` carry both legitimately +on the `client_credentials` path. The rule rejects none of them. Kept as errors +rather than downgraded to warnings on that evidence: a warning for a field that +does nothing is the state this change exists to end. + +**`tenant` is deliberately excluded from the rule**, and a test pins that. A +browser client may declare a tenant; `humanTenant` resolves it against the +directory and refuses issuance when the two disagree (KEY-WP-0013-T05). An +earlier version of this change rejected `tenant` too, which would have made that +feature unusable. + +## Correction + +```task +id: KEY-WP-0028-T02 +status: done +priority: medium +state_hub_task_id: "5ab951f8-2f9c-522c-b6ea-7f6d572cd618" +``` + +This work started from KEY-WP-0013-T05's blocker paragraph, which described a +human token as unable to carry `tenant:platform`. That was true when written and +had already been fixed in a concurrent session by the time this task began; the +paragraph was read as current state rather than as a dated record. + +Two consequences, both corrected within the session: + +- the `tenant` rejection was removed before it was committed, and a test now + asserts a browser client carrying `tenant: tenant:platform` validates cleanly; +- a hub decision (`0145ab57`) recording the tenant question as open and + unimplemented was withdrawn with a rationale pointing at the implementation. + Nothing was built on it. + +A third instance followed the same day, outside this task: a message to +railiance-platform (d361bf28) listed the upstream issuer pin as outstanding when +`docs/evidence/2026-09-09-upstream-issuer-pin.json` showed it done ten hours +earlier — `check_before.issuer_matches: false`, then pinned to +`https://auth.coulomb.social`. Corrected in dd827b9f. The concern itself was +sound; only the claim that it was still owed was wrong, and the two are worth +separating rather than letting the correct half excuse the other. + +The lesson worth keeping: in a repository where several sessions work at once, +prose ages faster than the code and the receipts it describes. `docs/evidence/` +is the authoritative artifact for anything claiming a live proof here. Read it +before writing "outstanding", "unproven" or "not done" — that check is cheaper +than any of the corrections it would have prevented. diff --git a/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0029-scope-reassessment.md b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0029-scope-reassessment.md new file mode 100644 index 00000000..392f9775 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0029-scope-reassessment.md @@ -0,0 +1,81 @@ +--- +id: KEY-WP-0029 +type: workplan +title: "Reassess SCOPE against the current revision" +domain: infotech +repo: key-cape +status: finished +owner: claude +topic_slug: scope-reassessment +created: "2026-09-09" +updated: "2026-09-09" +state_hub_workstream_id: "cebe226b-5418-5559-bc48-379e843bca78" +state_hub_workstream_id: "cebe226b-5418-5559-bc48-379e843bca78" +--- + +SCOPE.md declared itself reviewed against `b989de4`, 63 commits behind. Its +limits section had been kept current by appending to bullets, which works until +the header asserts a review date that no longer describes what was reviewed. + +## Recheck every claim against source + +```task +id: KEY-WP-0029-T01 +status: done +priority: medium +state_hub_task_id: "b12dfe7d-f4b6-5c44-89c6-9d3c02435d4c" +``` + +Routes read from the mux registrations, subcommands from the dispatch in +`main.go` and `internal/authclient/cli.go`, flags from the flag sets, claim +behaviour from `token.go` and `userinfo.go`. Where the existing prose and the +code disagreed, the code won. + +Three claims were false and are corrected: caller commands omitted +`verify-client`; the container was described as packaging only `keycape`, untrue +since KEY-WP-0026; and the issuer API row omitted `/readyz`. Capabilities that +had only reached SCOPE piecemeal — the authorization-code bindings, upstream +token verification, readiness and drain, the human tenant rules, +authentication-time assurance, the shared verifier — are now stated in the +capability table rather than only in the limits prose. + +## Separate source behaviour from deployed behaviour + +```task +id: KEY-WP-0029-T02 +status: done +priority: medium +state_hub_task_id: "28ae58c0-9bb9-573f-9cf3-51230d053e3c" +``` + +The document now says plainly that it describes source, and that the two are +different claims with different evidence: the test suite and harness runs for +one, committed receipts in `docs/evidence/` for the other. + +What is proven live is narrower and is stated exactly — the issuer pin, whose +pre-pin state did *not* match, so that risk was real rather than hypothetical; +and the two approval clients verified in an attended owner process. Both receipts +decline real predecessor rotation and observed wall-clock expiry, and SCOPE +declines them too. The deployed image predates this revision, so several +behaviours SCOPE describes are running nowhere yet, two of them fail-closed at +startup. + +This is the correction that matters. Four times in two days a statement about +current state was written from a workplan paragraph, a partial `git status`, or +an inference about what was admitted, without reading the authoritative artifact. +A SCOPE that does not distinguish "the code does this" from "this is running" +invites exactly that error from the next reader. + +## Record the reassessment + +```task +id: KEY-WP-0029-T03 +status: done +priority: low +state_hub_task_id: "7ef92d4f-eb56-5208-8b85-ed7bb2656c46" +``` + +`history/2026-09-09-scope-reassessment.md` records the method, what changed since +2026-09-05, the corrections, and what remains. It supersedes rather than replaces +the 2026-09-05 assessment, which stays as the record of the ten-gap backlog and +its closures; both are linked from SCOPE. diff --git a/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0030-tenant-precondition-guard.md b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0030-tenant-precondition-guard.md new file mode 100644 index 00000000..0f74e0d5 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0030-tenant-precondition-guard.md @@ -0,0 +1,73 @@ +--- +id: KEY-WP-0030 +type: workplan +title: "Make the static-registration precondition a checked condition" +domain: infotech +repo: key-cape +status: finished +owner: claude +topic_slug: tenant-precondition-guard +created: "2026-09-09" +updated: "2026-09-09" +state_hub_workstream_id: "9ed8f851-080a-5d15-8642-f799c2e3295c" +--- + +`informed-decision` replied on KEY-WP-0013-T05 and asked for one thing that is +ours to do: the caveat under which a registration-bound human tenant is safe +should be "a condition of the capability, so a future change to registration +policy has to confront it", not reasoning left in a message. + +They are right that it was only prose. `docs/tenant-claim-contract.md` states it, +and a separate test asserted `registration_endpoint` is absent — but nothing +connected the two, so a future session adding dynamic registration would see a +test about discovery metadata, not a warning about relabelling users. + +## Tie the exclusion to the capability it protects + +```task +id: KEY-WP-0030-T01 +status: done +priority: medium +state_hub_task_id: "4729d591-39d4-5f33-bef8-db8057090913" +``` + +`TestRegistrationBoundTenantRequiresStaticRegistration` asserts both halves +together: that a client-declared tenant is issued, and that dynamic client +registration is not advertised. Whichever is removed first, the failure points at +the other. + +The failure message carries the reasoning rather than the observation. Adding +dynamic registration while a registration may declare its users' tenant means +anyone able to register a client can relabel the users who log in through it into +a zone of their choosing; the message says that and names the two ways out, drop +the capability or gate it to statically configured registrations. + +Verified in both directions rather than assumed: advertising a +`registration_endpoint` fails it with the escalation message, and neutering +`humanTenant` fails it with the message saying to remove the guard along with the +capability it protects. + +This is deliberately not a vote on the tenant question. It makes the precondition +of one option checkable; it does not choose between them, and if option 1 lands +the capability and this guard are removed together. + +## What stays with the owners + +```task +id: KEY-WP-0030-T02 +status: done +priority: medium +state_hub_task_id: "32b10327-f55d-5a0e-a4bc-be92929469dc" +``` + +`informed-decision` prefers option 2 and explicitly declines to treat that as the +answer, flagging it to gate-house on GH-DEC-2026-012 and naming approval-engine's +stake. KeyCape leans the same way, which is a reason for more care rather than +less: the code already implements option 2 (`329e48f`), so the doctrine question +is being decided against a working implementation. That is worth saying out loud +to everyone waiting on it, and was. + +Their scope set `[openid, approval:read, approval:approve]` is confirmed and +already published. `client_id` and callback follow at their T07, deliberately not +before the tenant question resolves — registering a client that fails closed at +first use is the failure this exchange existed to avoid. diff --git a/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0031-gate-house-tenant-ruling.md b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0031-gate-house-tenant-ruling.md new file mode 100644 index 00000000..d2ede9d4 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0031-gate-house-tenant-ruling.md @@ -0,0 +1,81 @@ +--- +id: KEY-WP-0031 +type: workplan +title: "Implement the gate-house tenant ruling" +domain: infotech +repo: key-cape +status: finished +owner: claude +topic_slug: gate-house-tenant-ruling +created: "2026-09-10" +updated: "2026-09-10" +state_hub_workstream_id: "2bc4d085-9b10-5160-bc8a-4fccc5d41ba9" +--- + +gate-house ruled GH-DEC-2026-013 (`gate-house@16c1d46`) on the human-tenant +question KeyCape declined to settle for itself. Directory-sourced is the terminal +state; the registration-bound shape is granted as a declared bounded gap under +three conditions, on the general property that *a transitional shape is +admissible where it fails closed on exactly the case distinguishing it from the +correct resolution*. Ours refuses issuance where registration and directory +disagree, which is that case. + +Conditions (a) and (c) needed nothing from this repository — (a) is already the +implemented behaviour, and its strengthening, that a future change preferring the +**directory** is equally void because picking any winner is a silent +cross-tenant assertion, is recorded in the contract. This workplan covers the two +items that were implementation. + +## Carry the tenant claim's provenance + +```task +id: KEY-WP-0031-T01 +status: done +priority: high +state_hub_task_id: "46d42120-eca5-5b7f-8445-2211b3b8eab0" +``` + +gate-house ruled on GH-DEC-2026-013 while this was in flight. Section 5 is a +finding nobody asked for and is ours to implement: `tenant` is a bare string, so +a consumer cannot tell a zone the directory asserted about the person from one a +registration supplied about the client they came through. approval-engine +exact-matches that string while its contract reads as though it relies on the +first — the check is sound and the property a reader infers from it is absent. + +Every token now carries `tenant_source` beside `tenant`: `directory`, +`registration`, or `default`. It is advertised in `claims_supported` and asserted +on both grants at the token level, not only in the resolution function, because +the claim a consumer reads is the thing under obligation. + +Three values where the ruling names two, and that is the substantive judgement +here. Labelling an unasserted profile default as `directory` would reproduce the +same defect one level down — a consumer reading an assertion the identity layer +never made. The ruling cites GH-DEC-2026-011 §3 on unknown versus absent for the +case it examined; the same rule applies to our own fallback, so the fallback is +named rather than folded into the strongest neighbouring value. + +The agreement case resolves to `directory` deliberately: if a registration +declares the zone the directory also assigned, the directory did assert it, and +reporting the weaker source would understate what is known. + +## Condition (b) corrected a message I had just shipped + +```task +id: KEY-WP-0031-T02 +status: done +priority: high +state_hub_task_id: "b21aeac8-8370-5572-ab00-6e9812fa48e7" +``` + +The guard committed in `5f516a0` told a future reader that adding dynamic +registration could be resolved by dropping the capability **or gating it to +statically configured registrations**. Condition (b) voids the second: admitting +dynamic registration voids the registration-bound shape that day, and the +directory becomes the only source whatever state the adapter is in. gate-house +strengthened our own "must be revisited" on the grounds that *revisited* implies +the answer might survive review, and it would not. + +So the message offered a way out the ruling forbids, in the exact place someone +would read it while making that change. Corrected the same day. Worth recording +rather than quietly editing: the guard was written to force a confrontation, and +a guard that suggests an inadmissible resolution is worse than none. diff --git a/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0032-principal-type-provenance-guard.md b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0032-principal-type-provenance-guard.md new file mode 100644 index 00000000..a04f52f8 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0032-principal-type-provenance-guard.md @@ -0,0 +1,75 @@ +--- +id: KEY-WP-0032 +type: workplan +title: "Pin the single route by which a principal becomes human" +domain: infotech +repo: key-cape +status: finished +owner: claude +topic_slug: principal-type-provenance-guard +created: "2026-09-10" +updated: "2026-09-10" +state_hub_workstream_id: "150b892c-f112-5fb9-b0f0-8072a52b85d5" +--- + +GH-DEC-2026-016 §5 applies A-16 to what makes a principal *human*: if `human` is +reachable by two routes — asserted by the identity layer about the person, or +supplied by a registration about the client they came through — the record must +say which, and a human-in-the-loop control must not be discharged on the +registration-supplied one. Refusing a service principal while accepting an +unverified assertion of humanity moves the defect rather than closing it. + +gate-house said explicitly this was not a request. It is built because the +property it protects is true *today* and is the kind a plausible change erases +quietly. + +## Pin the property, not the behaviour + +```task +id: KEY-WP-0032-T01 +status: done +priority: medium +state_hub_task_id: "2f42a72e-6292-5c02-9223-835db17b0a63" +``` + +`principal_type` has exactly one route to `human`: a string literal on the +authorization-code path, reached only after an upstream login resolved to a +directory user. No configuration field can assert it — verified against source +rather than assumed, and it is why the tenant provenance work has no counterpart +here and why A-16 does not yet bite. + +Asserting the *behaviour* would not have protected that. A test checking "a human +token says human" passes just as happily when the value starts coming from a +registration. So the guard parses the package and requires every assignment to +`principal_type` to be a string literal, with the literal set exactly +`{human, service}` — one route each. `claims["principal_type"] = +client.PrincipalType` would look like a feature in review and fails the build +instead, with a message saying that if it is deliberate the claim must carry its +provenance the way `tenant_source` does. + +Both assignment shapes are covered. The browser path assigns into a map; the +service path uses a key-value pair inside a map literal. Checking only +assignments found one of two routes and passed — a guard proving less than it +claimed, caught because the expected literal set was pinned rather than merely +counted. + +Verified by mutation: replacing the literal with a function of the client +registration fails it at the exact line, and restoring it passes. + +## What this deliberately does not do + +```task +id: KEY-WP-0032-T02 +status: done +priority: medium +state_hub_task_id: "13199970-75d1-5c38-aa8c-27d6e38dcf65" +``` + +No `principal_type_source` claim was added. There is one route, so a provenance +marker would encode a distinction that does not exist and invite consumers to +branch on it — the opposite of the tenant case, where two routes existed and the +claim could not say which had been taken. + +The guard is the honest response to "not a request today": it makes the absence +of a second route a checked fact rather than a remembered one, and hands the +decision to whoever creates the second route, at the moment they create it. diff --git a/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0033-vergabe-fresh-login.md b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0033-vergabe-fresh-login.md new file mode 100644 index 00000000..5b478f95 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0033-vergabe-fresh-login.md @@ -0,0 +1,93 @@ +--- +id: KEY-WP-0033 +type: workplan +title: "Preserve fresh-user authentication for the Vergabe company handoff" +domain: infotech +repo: key-cape +status: finished +flavor: implementation +owner: codex +topic_slug: netkingdom +created: "2026-09-12" +updated: "2026-09-25" +related: [VERGABE-WP-0019, NK-WP-0037, USER-WP-0025] +state_hub_workstream_id: "144dd430-1a09-51e1-9aad-e799ee86c338" +--- + +## Forward login freshness to the actual authentication provider + +```task +id: KEY-WP-0033-T01 +status: done +priority: high +state_hub_task_id: "fe05486b-f079-503b-ade1-fca65bf64b34" +``` + +KeyCape parsed prompt=login and max_age but dropped them before Authelia. +Forward these through the provider-neutral AuthRequest and the Authelia adapter. +Absent values retain ordinary SSO behavior. Handler and adapter regressions +cover forced login and zero/nonzero maximum age. Full Go suite passes after +correcting the existing example-count regression: the example file already +contains four service clients and the admitted human approver client. + +## Publish, preflight and prove the fresh recipient boundary + +```task +id: KEY-WP-0033-T02 +status: done +priority: high +state_hub_task_id: "7b58f08c-6063-554a-8700-a1edbc805ca4" +``` + +Publish the exact source and validate current live config before replacing the +single issuer instance. The live image is dcebd46/digest 7ff54c54; current main +also contains startup validation and tenant provenance changes documented in +docs/operations.md. Preserve all unrelated client, credential and MFA policy +configuration. NK-WP-0037 adds the exact public Vergabe callback with no tenant +assertion or MFA downgrade. Confirm prompt=login reaches Authelia through the +live redirect, wrong callbacks and missing PKCE fail, and the actual recipient +uses their own identity. Provider restart invalidates pending in-memory logins; +USER-WP-0025-T03 still owns full provider sign-out coordination. + +2026-09-12 release prepared: source 8d4336e, Forgejo image run 45 succeeded, +digest sha256:5f10f36a5da23ce1aaf3df9b84a8ff98d7926f34ceaa19e63bd3356adb68e01a. +The exact client-registration and deployment server dry runs pass. The runtime +is unchanged. Await the attended window required by docs/operations.md; the +complete packet is railiance-apps/docs/vergabe-demo-company-sso-rollout.md. + +2026-09-12 attended rollout executed after explicit operator approval. KeyCape +and password setup are Ready on the prepared digests; exact public client +registration was CAS-applied (config resourceVersion 60123977) with unrelated +config bytes/Secret data preserved. Existing portal and product client both +pass fresh-login forwarding, wrong-callback and missing-PKCE checks (6 checks). +Vergabe Helm revision 2 is Ready; identity migration completed, both PVCs remain, +and requests remain 60m CPU/256Mi memory. Eleven live product checks pass: +company welcome, anonymous gate, no-store, secure scoped CSRF, POST/CSRF-only +login start, native issuer redirect, private company/media protection and +invalid callback/confirmation rejection. Initial readback showed zero accounts, +identity mappings and staff accounts. Native invited-user sign-in/MFA and +confirmation are now requested from the operator; no user credential was used +by the agent. Recovery and two-user acceptance remain their existing tasks. +Evidence: railiance-apps/docs/evidence/2026-09-12-demo-company-sso-live.md. + +2026-09-23 regression from the attended recipient sign-in (net-kingdom message +ee4808e2, NK-WP-0037-T02): Authelia 4.38 registers the authorization request +only after the login, so forwarding prompt=login fails every real login ("auth_time +happened before the authorization request was registered"). The 2026-09-12 +forwarding checks were redirect-only and never completed a login. Fix (owner +option a): KeyCape no longer sends prompt to Authelia. prompt=login and +max_age=0 travel as max_age=10, a shorter max_age is raised to 10, a longer one +passes through. KeyCape then enforces the exact requirement itself against the +verified upstream auth_time: prompt=login needs an authentication no earlier +than the KeyCape request, max_age needs one within that age, and a missing +auth_time fails closed (event `stale_upstream_authentication`). Known edge: a +user whose Authelia login is under 10s old at request time is not re-prompted +and is refused rather than admitted. Also fixed the stale example-count test left +by 651625c/1620ce2 (6 service + 1 human). Full Go suite passes. + +The image was already the live issuer +(`sha256:8dc35801`, Forgejo run 69, source `11ce29a`). No second rollout. +2026-09-24 23:46 UTC the founder completed the Vergabe journey: one +`token_exchange_error`, then `auth_success` and `token_issued` for +`vergabe-demo-company`. No `stale_upstream_authentication`. Receipt: +`docs/evidence/2026-09-24-fresh-login-and-account-switch.md`. diff --git a/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0034-account-recovery.md b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0034-account-recovery.md new file mode 100644 index 00000000..eb58f99b --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0034-account-recovery.md @@ -0,0 +1,64 @@ +--- +id: KEY-WP-0034 +type: workplan +title: "Browser authentication recovery and confirmed shared sign-out" +domain: infotech +repo: key-cape +status: finished +flavor: implementation +owner: codex +topic_slug: key-cape +created: "2026-09-12" +updated: "2026-09-25" +state_hub_workstream_id: "49b0cd1d-fb9d-5351-99eb-9be9143a092d" +--- + +The operator reports a dead-end authentication error after using an account +outside the product tenant. Recent issuer telemetry indicates token exchange +failure; tenant rejection and provider failure must not be conflated. + +## Implement and validate recovery + +```task +id: KEY-WP-0034-T01 +status: done +priority: high +state_hub_task_id: "ba285335-4ead-5478-8fb7-89a2ec1ba695" +``` + +Route failed browser login to the public account recovery surface without codes, +state or unverified identity. Show verified portal identity, tenant memberships, +and recorded workload memberships; preserve operator/customer separation. +Provide CSRF-protected portal logout and confirmed shared provider sign-out with +fixed owner-configured return locations. No automatic reauthentication loops, +MFA downgrade, global JWT revocation claim or inferred workload entitlements. + +## Publish and verify the recovery flow + +```task +id: KEY-WP-0034-T02 +status: done +priority: high +state_hub_task_id: "4c836ddf-2eb8-528a-b63f-8499f3a6cbf2" +``` + +Publish immutable images, update canonical runtime pins, verify anonymous +recovery and sign-out confirmation live, and record actual account switching +only after browser evidence. Existing application sessions may outlive provider +logout. Related: USER-WP-0025-T03 and VERGABE-WP-0019-T06. + +Source verification: Full Go suite passed; final OIDC/config tests passed after recovery expansion. Immutable publication and live checks are in progress. + +### Live recovery rollout — 2026-09-12 + +Recovery is deployed in KeyCape 4d8b8fe, User Engine e54b6ee and Vergabe c067993 +(Helm revision 3). All three are Ready. Six provider checks, eleven product +checks and six fresh anonymous Chromium checks pass, including actual provider +logout POST and return to the portal without test overrides. Detailed receipt: railiance-apps/docs/evidence/2026-09-12-account-recovery-live.md. + +2026-09-24 23:42–23:45 UTC the founder completed the account switch. The issuer +recorded one `mfa_failed` for `user-engine-portal`, then three separate +`auth_start` → `auth_success` → `token_issued` sequences. A silent reuse of +the previous portal session would not have produced those new successes. +Provider logout still does not revoke already issued application JWTs. +Receipt: `docs/evidence/2026-09-24-fresh-login-and-account-switch.md`. diff --git a/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0035-optional-mfa.md b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0035-optional-mfa.md new file mode 100644 index 00000000..29eb711f --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0035-optional-mfa.md @@ -0,0 +1,112 @@ +--- +id: KEY-WP-0035 +type: workplan +title: "Opt-in MFA for demo-company login" +domain: infotech +repo: key-cape +status: finished +owner: codex +topic_slug: infotech +created: "2026-09-13" +updated: "2026-09-14" +state_hub_workstream_id: "b27601d8-d0a5-5c79-a1ff-c2a4cedafa74" +--- + +Requested behavior: password-only login before OTP activation; require OTP after +activation. Do not lower assurance of unrelated applications. + +## Implement enrollment-dependent browser policy + +```task +id: KEY-WP-0035-T01 +status: done +priority: high +state_hub_task_id: "c1ed06c2-c689-53d1-afc7-e10d79e9e94f" +``` + +Add mfaOptional, preserve explicit AAL2 and existing client policy, fail closed on +provider lookup errors and malformed/incomplete responses. Preserve the setting +in config/runtime/canonical model and flag manual migration requirements. +Validation: `go test ./...` and `git diff --check` passed on 2026-09-13. See docs/optional-mfa.md. + +## Restore authoritative factor lookup and enable the reviewed client + +```task +id: KEY-WP-0035-T02 +status: done +priority: high +state_hub_task_id: "b066a273-f91c-50ee-b497-a39eace03b3a" +``` + +Factor lookup restored on 2026-09-13 through the new dedicated service lane +RPF-WP-0040 / CCR-2026-0023. Native ESO delivery, scoped provider user lookup, +renewal and mounted replacement passed. KeyCape now uses adminTokenFile; other +configuration and current MFA policy were preserved. Historical NK-WP-0033 +resolver lanes remain separate. The remaining T02 work is the exact scoped +client migration and no-factor/enrolled/error/step-up acceptance before enabling +optional policy. See railiance-platform/docs/evidence/2026-09-13-keycape-factor-custody.md. + +## Verify optional enrollment and account management access + +```task +id: KEY-WP-0035-T03 +status: done +priority: high +state_hub_task_id: "93ce2e97-6c5a-58a7-8f3c-1c4f0eb6f1b5" +``` + +Verify provider self-service login, possession-confirmed activation, cancellation +and removal/recovery. Resolve shared portal assurance scope before surfacing the +verified OTP setup link. Actual user login acceptance remains open under +KEY-WP-0034 and VERGABE-WP-0019; this work does not finish either workplan. + +## Support bounded provider credential renewal without issuer restart + +```task +id: KEY-WP-0035-T04 +status: done +priority: high +state_hub_task_id: "494f554d-6542-5797-804f-6df4a05dcadb" +``` + +Supports platform journey P05 and USER-WP-0030-T03. Add an exclusive mounted adminTokenFile credential source, fresh reads for lookup and validation, no stale fallback, bounded input, sanitized failures and atomic replacement acceptance. Preserve inline configuration compatibility. Require provider success and true validation value before AAL2; bound default request time and reject credential-bearing redirects. Test renewal, rejection, recovery and policy regressions. This implements the consumer delivery contract; owner credential issuance/custody and live P04/P05/P06 acceptance remain T02/T03. + +All Go regression/conformance suites pass, including five new renewal/validation tests with invalid-source subcases. Added exact-commit authentication acceptance CI. Provider-mounted credential delivery and effective optional policy remain gated separately. + +Consumer source 632b1f1 deployed and Ready 1/1; CI, four provider HTTP checks and twelve live browser checks passed. See docs/credential-renewal-release-2026-09-13.md. T02/T03 retain actual credential delivery and live recovery/policy acceptance. + +2026-09-13 custody activation supersedes the earlier owner-handoff gate: the new factor service is live and renewable. T02/T03 now track effective policy/onboarding acceptance, not missing credential ownership. + +## Require confirmed enrollment and actual OTP evidence + +```task +id: KEY-WP-0035-T05 +status: done +priority: high +state_hub_task_id: "94f579d1-a7af-564c-ab0c-ae76fce343aa" +``` + +Live provider source inspection found active tokens can remain in verification +state; live policy inspection found password passthrough enabled for users without +factors. Distinguish pending enrollment from completed enrollment and require +TOTP/HOTP serial/type evidence before AAL2. Cover pending/confirmed/cancelled, +existing-factor plus pending enrollment, missing/unknown state, incomplete pages, +password passthrough and static-password token rejection. All Go suites pass +locally; source 113f3a6 was published, CI acceptance/build passed, and the guarded issuer replacement is live. See docs/enrollment-assurance-release-2026-09-13.md. + +Provider recovery testing additionally proved that withdrawing reader policy +returns HTTP 200 with an empty list. Added and deployed a same-realm disabled, +unassigned visibility probe before no-factor decisions, with missing visibility +failing closed. Native scope proof passed. Isolated installed-provider tests +passed possession confirmation/cancellation, permission withdrawal/recovery and +provider-issued JWT expiry; these are now included in CI. T02/T03 remain the +actual optional-client and self-service browser activation gates. + +2026-09-14: T02/T03 completed through USER-WP-0033 P06. Installed-provider +native and five browser checks passed, including confirmed activation, +cancellation, old-session MFA and denied active-factor replacement. Portal +guards, provider restrictions and both scoped optional-client policies are live; +PVC readback confirms effective state. Full release evidence and rollback: +`../user-engine/docs/evidence/2026-09-13-p06-authentication-policy.md`. +Residual actual-user and workload sign-in acceptance remains live under +KEY-WP-0034, USER-WP-0028 and VERGABE-WP-0019 before this closure. diff --git a/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0036-platform-factor-recovery.md b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0036-platform-factor-recovery.md new file mode 100644 index 00000000..fd296bc0 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0036-platform-factor-recovery.md @@ -0,0 +1,83 @@ +--- +id: KEY-WP-0036 +type: workplan +title: "Audited platform administrator lost-factor recovery" +domain: infotech +repo: key-cape +status: finished +owner: codex +topic_slug: infotech +created: "2026-09-13" +updated: "2026-09-13" +state_hub_workstream_id: "f9ba00a6-5758-5677-9a49-da1e35875f7a" +--- + +Implements the provider part of P04 under USER-WP-0030-T03. The existing factor +read credential is deliberately not elevated to recovery authority. Recovery +now has a private signed-OIDC service and platform-only portal browser flow. + +## Implement bounded and audited provider recovery + +```task +id: KEY-WP-0036-T01 +status: done +priority: high +state_hub_task_id: "f3287544-8412-5b1f-9d16-9f7b9e895ceb" +``` + +`scripts/factor_recovery.py` previews exact ownership and shared-identity scope, +requires an operator verification attestation and matching metadata version, +disables one factor with a row lock, requires pre-mutation audit durability, +reads back state, records completion and reconciles retries. Reject changed or +replaced factors and reference conflicts. Nine isolated unit tests pass, including replacement between disable and readback. +The installed-provider Job provider-recovery-contract-02 passed real database +and audit persistence, disable/readback and replay assertions in an isolated +fixture. Acceptance CI includes both suites. No real user's factor was changed. + +## Validate the attended owner entry point + +```task +id: KEY-WP-0036-T02 +status: cancel +priority: high +state_hub_task_id: "2d59da13-782e-5aeb-8957-69e41a15ddb1" +``` + +railiance-platform/scripts/keycape_factor_recovery.py derives actor from an +OpenBao platform-admin entity, rejects root/workload substitutes, checks cluster +identity, and emits only a private metadata receipt. Runbook: +railiance-platform/docs/keycape-factor-recovery.md. Offline owner tests pass. +Superseded: routine recovery now independently verifies the platform operator's +signed KeyCape session through the private service. The extra OpenBao/Kubernetes +operator front door is not part of the supported P04 journey; its native acceptance +is cancelled rather than falsely marked passed. No credential handoff is pending. + +## Integrate platform support recovery with the browser journey + +```task +id: KEY-WP-0036-T03 +status: done +priority: high +state_hub_task_id: "f7248d5e-6f68-5637-ada5-2527436f5f9e" +``` + +Implement an authenticated bounded recovery service and platform-only portal +flow with fresh assurance, server-derived actor, CSRF, ownership-verification +process, exact target/scope confirmation, audit/reference readback and safe +retry. Never grant Kubernetes access to the portal. Test unauthorized/tenant +admin denial, stale confirmation, provider failure, retry and replacement OTP +enrollment using disposable identities. Mandatory-AAL2 access is restored only +after possession of a replacement factor is proven. Keep P04 incomplete until +browser success, failure and recovery paths pass; CLI tests alone do not close it. + +Completion evidence: user-engine/docs/evidence/2026-09-13-p04-recovery.md. +KeyCape 63b3070 and portal c298cd9 are released. Fifteen recovery tests, 219 portal +regressions with PostgreSQL and 19 Chromium checks pass. Installed-provider +provider-p04-contract-03 verifies signature/role/assurance denials, confirmation, +multi-owner refusal, ownership locking, audited mutation/retry/reference lookup and replacement OTP possession/login. +Production health, anonymous/invalid-token denials, signing-key connectivity, +rollout and new-enrollment policy readback pass. Positive mutation used disposable +fixtures; no real account was changed. Provider and portal implement the same +supported recovery contract; no outstanding P04 implementation remains. +Broader policy/self-service and notification operations remain live under +USER-WP-0030-T03 and KEY-WP-0035, not residuals of this bounded recovery plan. diff --git a/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0037-secrets-engine-openbao-registration-custody.md b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0037-secrets-engine-openbao-registration-custody.md new file mode 100644 index 00000000..ad7fed77 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0037-secrets-engine-openbao-registration-custody.md @@ -0,0 +1,135 @@ +--- +id: KEY-WP-0037 +type: workplan +title: "Secrets Engine OpenBao live registration and protected custody" +domain: infotech +repo: key-cape +status: finished +owner: codex +topic_slug: netkingdom +created: "2026-10-04" +updated: "2026-10-07" +state_hub_workstream_id: "62800ecf-064b-5546-a1e4-4521b5932973" +--- + +KeyCape's accepted provider contract (KEY-WP-0009) does not establish actual +registration or custody. This plan names those missing owner gates for +RPF-WP-0035-T02. The founder authorized workers to attend the review requests, +not live registration, credential generation, rotation, fetching or deployment. + +## Establish the exact registration and deployment authority + +```task +id: KEY-WP-0037-T01 +status: done +priority: high +state_hub_task_id: "0409f656-65fb-5ee2-a934-a687a3e139f1" +``` + +Reviewed KeyCape base 672e1faf4b3401ff7bf68bb0b29f8dc50ca51ac6: +`config/service-clients.example.yaml`, `docs/openbao-service-auth-contract.md`, +`src/internal/config/config.go`, `src/cmd/keycape/clients_test.go`. +The exact service registration remains accepted: secrets-engine-openbao, +service:secrets-engine, tenant:platform, role secrets-engine, only openbao:login, +client_credentials, confidential, tokenLifetime 15m and issuer +https://kc.coulomb.social. Audience defaults to clientId, with no resource +audience override. See the owner packet for concrete deployment/custody proposals. +NetKingdom owns the live sso/keycape deployment and config, not this example. +Existing approved approval-client rollout demonstrates the owner procedure; +it does not approve or provision this different service client. + +## Agree and prepare the separately approved protected custody operation + +```task +id: KEY-WP-0037-T02 +status: done +priority: high +state_hub_task_id: "ac798fb4-ed77-5e45-83ab-f56eea92be34" +``` + +Platform owns the authoritative OpenBao client-secret custody and least-privilege +issuer-side delivery; secrets-engine owns its explicitly selected mode-0600 +client-side file outside every worktree. The proposal in +`docs/secrets-engine-openbao-owner-packet.md` provides candidate coordinates, +not a live custody claim or allocated CCR. Complete the platform CCR(s), delivery +policy/roles, two target bindings, exact attended child and rollback before +seeking the founder's separate concrete approval. Values stay out of Hub/Git/chat, +argv and logs. Existing verifier/requester values must not be reused. + +**Done 2026-10-05.** The founder resolved `632a8893-0d6a-4591-a50b-40bbdb798f32`. +The custody boundary is approved: platform owns OpenBao client-secret custody +and issuer-side delivery, and secrets-engine owns the mode-0600 file outside +every worktree. The owner packet is still coordinates. This approval does not +register the client, generate a secret, or deploy. + +## Prepare and approve the deployment-owned registration operation + +```task +id: KEY-WP-0037-T03 +status: done +priority: high +needs_human: true +decision_id: caafe4be-4f68-47b4-9567-316d47b94992 +blocking_reason: "Custody boundary 632a8893 is approved. Registration merge and rollout still need an exact reviewed revision and a separate live go. Opening caafe4be does not deploy." +state_hub_task_id: "52f03e39-d05b-5da5-85df-e28c059430e4" +``` + +Coordinate source registration merge and the single new env secretKeyRef with +NetKingdom's `sso-mfa/k8s/keycape/deployment.yaml` and owner rollout implementation. +An exact reviewed source revision, image digest, config preservation test, +synthetic issuance/denial verification and founder live go are required. +Use an idempotent exact-match merge: absence permits adding only this client; +matching client permits noop; duplicates or drift fail closed. Preserve signing +key, unrelated clients, existing policy and confidential config bytes. +No deployment changes are authorized by this source plan. + +**Done 2026-10-07 under live go `caafe4be`.** +- Custody (decision `2ab717e1`): KV v1 written with cas=0. +- ESO amendment `1fdd993b`; the sso Secret is synced. +- NK-WP-0046 (`1fbb8db`): env binding, then the exact-match config add + (rv 71471711 to 71655453, unrelated bytes preserved), then a restart on image + 6f79a2af. + +**Prepared 2026-10-06 (source only).** NetKingdom NK-WP-0046 (`a1e7302`) holds +the exact-match single-client merge helper, its rollback and the proposed env +patch. secrets-engine SECRETS-WP-0012 (`a3897c2`) names the consumer file. +Platform CCR-2026-0033 (`df24a77`, still proposed) holds the KV path +`platform/workloads/keycape/secrets-engine-openbao`, the sso-only ESO and the +attended custody script; its approval is RPF-WP-0035-T09. The live order is +custody run, then issuer Secret, env patch, this merge and a restart. Nothing +is live. The founder keeps the deployed image (NK-WP-0046-T02, 2026-10-06). +Revised the same day: the value lives only in OpenBao (ops-mason MASON-WP-0009) +and has no consumer file. The custody approvals A1-A3 are decision +`2ab717e1-7881-491e-aed4-28946550c289`. + +## Verify actual issuance and publish non-secret owner receipt + +```task +id: KEY-WP-0037-T04 +status: done +priority: high +depends_on: + - KEY-WP-0037-T03 +blocking_reason: "Live issuance proof waits on the registration operation (T03)." +state_hub_task_id: "40b80bcd-243d-5bf8-ba18-b992e3c5f8f7" +``` + +After authorized registration and custody, verify issuer/JWKS signature, subject, +audience, principal_type, tenant, roles, scope and 900-second lifetime; refuse +wrong credential/excess scope and preserve existing human login. Record only +booleans and identities, immutable image/source coordinates and custody binding +metadata. Hand off to RPF-WP-0035-T02 for isolated OpenBao metadata provisioning, +claim/expiry/use-limit denial and self-revocation proof. Never report an example +registration, discovery metadata or source test as live acceptance. + +**Done 2026-10-07.** `keycape verify-client` passed all 10 checks: +- issuer, JWKS signature and claims (`service:secrets-engine`, tenant:platform, + role secrets-engine); +- no excess scope; +- `openbao:admin` and a wrong secret refused. + +The founder's KeyCape login after the restart also proved the CCR-2026-0034 +operator reader (net-kingdom-admins, aal2). Receipt: +`docs/evidence/2026-10-07-secrets-engine-openbao-issuance.json`. The 900s lifetime +was not measured. Handed to RPF-WP-0035-T02 for the OpenBao JWT role and login +proof. diff --git a/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0038-forgejo-oidc-client.md b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0038-forgejo-oidc-client.md new file mode 100644 index 00000000..8cd5bc66 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0038-forgejo-oidc-client.md @@ -0,0 +1,212 @@ +--- +id: KEY-WP-0038 +type: workplan +title: "Forgejo as a NetKingdom OIDC client, so the forge can close" +domain: infotech +repo: key-cape +status: active +flavor: implementation +owner: claude-code +topic_slug: railiance +created: "2026-10-08" +updated: "2026-10-16" +depends_on: [] +related_repos: + - prj-forgejo-org-refactor + - railiance-apps + - railiance-platform +state_hub_workstream_id: "098e3e2c-a138-560e-b2d3-3a15eb49d8a9" +--- + +# KEY-WP-0038 - Forgejo as a NetKingdom OIDC client, so the forge can close + +## Why + +`forgejo.coulomb.social` is closing: private by default, no anonymous access +(prj-forgejo-org-refactor ORGREF-WP-0002, decision +`decisions/forge-visibility-policy.md`). The founder's condition is that Forgejo +sign-in runs through NetKingdom **before** anything closes, so that every human, +agent and service reader has a governed identity rather than an ad-hoc local +account. This plan is ORGREF-WP-0002-T04. Its Forgejo-side counterpart is +railiance-apps `RAPPS-WP-0016` (ORGREF-WP-0002-T05), which owns the Forgejo +Helm release. + +## Boundaries + +- Registration follows the existing human browser-client pattern + (`docs/approval-engine-provisioning-request.yaml`, `human_registration`) and the + client fields validated in KEY-WP-0028. Redirect URIs are exact, never + wildcards. +- The client secret goes to OpenBao through railiance-platform custody. No secret + value in git, State Hub, logs or chat. +- Founder guardrail for the whole closure: tighten gradually, never lock anyone + out, never risk assets. Nothing in this plan changes who can read the forge + today. It only makes the NetKingdom path available. + +## T01 — Define what Forgejo receives + +```task +id: KEY-WP-0038-T01 +status: done +priority: high +state_hub_task_id: "5f019d13-1fa3-5b88-a27a-75a3339ca96e" +``` + +Write the claim contract Forgejo consumes, as a non-secret request packet under +`docs/`: + +- issuer `https://kc.coulomb.social`; confidential client `forgejo`; authorization + code with PKCE; scopes `openid profile email groups` +- redirect URI + `https://forgejo.coulomb.social/user/oauth2/netkingdom/callback`. The + authentication-source name `netkingdom` must match railiance-apps' + configuration exactly. +- subject and username: canonical directory subject, plus a stable + `preferred_username` that Forgejo can match to the existing local accounts + (founder and agent accounts) for linking +- `groups` naming the Forgejo organisations and teams to map to (for example + `forge:coulomb:owners`, `forge:coulomb:readers`), and `principal_type` so that + human and service principals stay distinguishable +- MFA: forge administrators and owners require assurance with a fresh second + factor; plain readers follow the client-aware MFA policy (KEY-WP-0008) + +Acceptance: the founder accepts the packet. The group names are agreed with +railiance-apps. + +2026-10-08: packet drafted at `docs/forgejo-oidc-client-request.yaml`. It covers a +confidential client `forgejo` with PKCE and `mfaRequired: true`, the exact +callback, scopes, a custody path, the claims as emitted at this revision, new +LLDAP groups `forge-admins`, `forge-coulomb-owners` and `forge-coulomb-readers` +with a required-claim gate, no automatic account creation, and linking of local +accounts. It also proposes that agents and services use scoped tokens bound to +service identities, not browser sign-in. + +2026-10-08: accepted by the founder (in session). railiance-apps' RAPPS-WP-0016 +uses the same authentication-source name `netkingdom` and the same group names, +so the agreement condition holds. Before T02, the LLDAP groups `forge-admins`, +`forge-coulomb-owners` and `forge-coulomb-readers` must exist, with the founder +in the first two. + +## T02 — Register the client and hand over its secret + +```task +id: KEY-WP-0038-T02 +status: wait +depends_on: [KEY-WP-0038-T01, NK-WP-0048-T06] +blocking_reason: "Registration runs as the first KnownSecurityCase of type oidc-browser-relying-party (NK-WP-0048-T06), not as a hand-built rollout." +priority: high +state_hub_task_id: "ddcfb482-8f33-5c81-9ce8-84be8fa5dc30" +``` + +**Changed 2026-10-08 (founder direction):** no case-specific helper script. +Registration is the first case of the SecurityCaseType `oidc-browser-relying-party` +(net-kingdom NK-WP-0048, canon `security-case-types_v0.1`). The parameters +accepted in T01 become the case; ops-mason materializes it (MASON-WP-0010) and +ops-warden follows through (WARDEN-WP-0044). The registration capability is +net-kingdom's (`net-kingdom.keycape-client-registration`); key-cape's part is its +field rules and reviewing the type's composition. + +The original intent below still describes what must result. + +Register `forgejo` from the packet. Mint the client secret straight into custody +(OpenBao path `platform/workloads/keycape/forgejo`, field `CLIENT_SECRET`, derived +by the case type since net-kingdom 074b3a0; custody owner railiance-platform, +consumers KeyCape via ESO and Forgejo). Record a receipt with no +secret value. + +Acceptance: discovery metadata lists the client's redirect URI. The secret is +readable by Forgejo's custody path only. + +2026-10-10: key-cape reviewed the type's composition +(`docs/oidc-browser-relying-party-type-review.md`). net-kingdom fixed the findings +in `61ab6bc`, a 1.0 patch: redirects with `?` or `#` refused, the groups scope +required, and descriptions corrected. A new 1.1 (`e2174ce`, founder go) adds the +`consumer-enforces-groups` verification. For Forgejo, that is the required-claim +setting railiance-apps configures. `security-cases/forgejo-netkingdom-oidc.yaml` +is re-pinned to @1.1, with parameters unchanged. KeyCape added `keycape +check-config` (`c528daa`), which register-client runs before the restart. T02 still +waits on NK-WP-0048-T06, which needs both executors' light paths (MASON-WP-0010-T03, +WARDEN-WP-0044-T03). + +### Exact offline checker owner review — 2026-10-16 + +KeyCape owner review under KEY-WP-0038-T02 endorses only the offline config +validation behavior of source `3c71226a1a7b8a2bdb710e9c497e1a1f7f8a3987`, +`src` tree `f0010b9cab17dbc3a787628a29657d98b900320f`, compiled artifact +`sha256:be37738fe1c08259fb4f9ca0daf0f9301bfffc237e5bb38435525dfbf11ccff5`. +The exact source/archive, 108 source files, 9876 cached Go 1.23.0 toolchain +files and all ten selected module trees matched the candidate closure. Fourteen +actual artifact checks and the cmd/config/policy Go tests passed using only +synthetic inputs. The check dispatches before private-key loading, upstream +adapter construction or server startup and uses placeholder client secrets. + +[Owner review](../docs/offline-checker-owner-review-2026-10-16.md) records the +exact evidence and limits: non-secret portal/logout URL environment overlays, +optional/nonempty env-name assertion and policy-store assertion, permissive YAML +loading, and no key/custody/readiness or authentication proof. It does not endorse +the caller's inherited environment, executable storage/custody, the NetKingdom +descriptor/type, Warden admission or any browser/native identity. Parent review +identified caller environment inheritance; NetKingdom owns its correction and +regression checks. T02 remains `wait`; T03 and all live acceptance gates remain +unchanged. No live config, credentials, cluster objects or paid calls were used. + +## T03 — Prove the claims for each kind of identity + +```task +id: KEY-WP-0038-T03 +status: wait +depends_on: [KEY-WP-0038-T02] +blocking_reason: "Claims are verified against the registered client." +priority: high +state_hub_task_id: "40ddaa29-7245-508c-ad48-ddc063e25115" +``` + +Before Forgejo uses the client, verify the issued ID token and UserInfo for one +human (the founder, with MFA), one agent identity and one service identity: +subject, `preferred_username`, `groups`, `principal_type`, assurance. Record the +claims, minus tokens, as evidence. + +Decide with railiance-apps whether agents and services sign in to Forgejo at +all, or receive scoped Forgejo tokens bound to a NetKingdom service identity +(ORGREF-WP-0002-T06). Record the decision. + +Acceptance: evidence for all three principals; the agent and service decision is +recorded. Then hand over to RAPPS-WP-0016. + +## T04 — Implement the `keycape-client` entry point + +```task +id: KEY-WP-0038-T04 +status: cancel +priority: high +state_hub_task_id: "583cf1f1-16dc-515f-83bc-1efc45024a89" +``` + +**Cancelled 2026-10-08: moved to net-kingdom NK-WP-0048-T09 (done).** The live +registration machinery (contained kubectl, cluster and issuer pins) already lives +in net-kingdom `sso-mfa/k8s/keycape/`, where KeyCape is deployed; a copy here +would drift. The entry point is `net-kingdom/sso-mfa/k8s/keycape/keycape-client.py` +and the declaration is `net-kingdom.keycape-client-registration`; key-cape's draft +declaration is removed. key-cape still owns the client field rules (canonical +model) and their enforcement at authorization time. + +`capabilities/playbooks/key-cape.oidc-client-registration.yaml` (contract PASS, +`maturity: planned`) declares client registration as a playbook capability, so +that the SecurityCaseType `oidc-browser-relying-party` (net-kingdom NK-WP-0048) +can compose it. Implement the entry point it names, `tools/keycape-client/`: + +- `keycape-client register --client-file ` with modes + `inspect`, `dry-run`, `apply` and `remove`; adds, updates or removes exactly one + client in the live registry; idempotent; never prints or writes secret values; + a confidential client's secret is read from its custody reference +- one implementation replacing the per-client helpers in net-kingdom + `sso-mfa/k8s/keycape/` (register-coulomb-social, portal-client-rollout, + vergabe-client-rollout, secrets-engine-openbao-client-rollout), keeping their + safeguards (byte-preserving merge, compare-and-swap, cluster pin, safe receipts) + and their tests as regression cases +- client field validation by the KEY-WP-0028 rules +- then set the declaration's `maturity` to `implemented` + +Longer term, loading clients from a non-secret registry with secrets referenced +from custody would remove the type's only attended step (see the type README, +"Attended steps"). diff --git a/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0039-openbao-negative-probe-tokens.md b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0039-openbao-negative-probe-tokens.md new file mode 100644 index 00000000..4398517a --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0039-openbao-negative-probe-tokens.md @@ -0,0 +1,368 @@ +--- +id: KEY-WP-0039 +type: workplan +title: "Negative-check tokens for the secrets-engine OpenBao JWT role" +domain: infotech +repo: key-cape +status: active +flavor: implementation +owner: claude-code +topic_slug: netkingdom +created: "2026-10-10" +updated: "2026-10-17" +depends_on: [] +related_repos: + - railiance-platform + - secrets-engine +state_hub_workstream_id: "09c3bddf-6058-5603-8488-cccdb7be4f86" +--- + +# KEY-WP-0039 - Negative-check tokens for the secrets-engine OpenBao JWT role + +## Why + +CCR-2026-0038 (railiance-platform RPF-WP-0035-T02) requires the OpenBao role +`secrets-engine-openbao` on `auth/keycape-services` to refuse JWTs with a wrong +`aud`, `sub`, `tenant`, `principal_type`, `roles` or `scope`, a coding-agent JWT +and an expired JWT. The positive login proof passed on 2026-10-10 (secrets-engine +`docs/evidence/2026-10-10-keycape-openbao-probe.json`). railiance-platform asked +whether KeyCape can issue the negative tokens without touching the +`secrets-engine` registration. + +KeyCape signs only what a registration declares. It has no way to mint a token +with arbitrary claims, and adding one would create a forging path in the issuer. +So the negative tokens must come from real, separate registrations that differ +from `secrets-engine` in exactly one property each. + +## Boundaries + +- The `secrets-engine` registration is not changed. +- Probe clients are confidential `client_credentials` clients with the minimum + 60-second lifetime, scopes that grant nothing, and secrets minted into custody + like any other client. They are removed after the proof. +- The probe script prints only accept or refuse booleans per case, never tokens or + secrets. It runs attended. + +## T01 — Define the probe registrations + +```task +id: KEY-WP-0039-T01 +status: done +priority: medium +state_hub_task_id: "4d06b059-3768-53bb-83a4-a9a71c0850b8" +``` + +Write a non-secret request packet under `docs/` with one probe client per claim +mismatch, each identical to `secrets-engine` except for: + +- `aud`: a different access-token resource audience +- `sub`: a different service subject +- `tenant`: a tenant other than `platform` +- `roles`: a role other than `secrets-engine` +- `scope`: a scope outside the role's bound scope + +Two cases need no probe client. A service token always carries +`principal_type: service` (`src/internal/server/oidc/token.go`), so the wrong +`principal_type` case uses a human access token from an attended login. The +coding-agent case uses the existing `codex-railiance-platform` registration (role +`coding-agent`, `docs/openbao-service-auth-contract.md`) if it is registered live; +T01 confirms that. + +The expired-JWT case reuses any probe client: request a token, wait past its +60-second lifetime, then present it. + +Acceptance: railiance-platform agrees the packet covers every refusal that +CCR-2026-0038 names. + +2026-10-10: railiance-platform agreed that the case list covers every +CCR-2026-0038 claim negative. The remaining CCR checks (capabilities-self deny, no +provider fallback) are theirs and secrets-engine's. Packet written at +`docs/openbao-negative-probe-clients.yaml`. It departs from the agreed list in two +ways, both sent back for re-agreement: + +- **Expired case.** A token from another probe already fails a claim, so its + refusal does not show that expiry was checked. It needs a sixth client, + `openbao-probe-expiry`, identical to `secrets-engine-openbao` in every claim. That + makes it a second credential able to log in as secrets-engine for the session. + OpenBao's `expiration_leeway` (150s by default) also extends the wait. +- **Audience pin.** A service token's `aud` is its `clientId` unless `audience` is + set. Every probe except the aud probe therefore pins + `audience: secrets-engine-openbao`, so it fails only on its own claim. + +Condition (a), no OpenBao custody, holds. The values still have to reach KeyCape's +environment, because KeyCape resolves `secretRef` at startup. The packet uses a +transient Kubernetes Secret that is deleted in the same session. + +## T02 — Register the probe clients, run the attended probe, then remove them + +```task +id: KEY-WP-0039-T02 +status: wait +depends_on: [KEY-WP-0039-T01, KEY-WP-0038-T02, KEY-WP-0038-T03] +blocking_reason: "Ordered identity gate: Forgejo first SecurityCase registration/claims proof, then NetKingdom registry session and attended human/coding-agent custody; original secrets-engine-openbao registration go remains resolved; probe/coding-client custody and attended readiness are not established." +priority: medium +state_hub_task_id: "03da4c38-1dac-53b1-8b0b-e697df48e9d3" +``` + +2026-10-11: requalified — live registration go caafe4be was already resolved; owner authorization present. See the-custodian docs/decisions/2026-10-11-security-unblock-decisions.md. + +Write the attended script, which prints booleans only. + +2026-10-10: script written at `scripts/openbao_negative_probe.py`, with tests in +`scripts/test_openbao_negative_probe.py`. It meets condition (b): one refused +boolean per case plus the refusal class (`claim_mismatch`, `expired`, +`not_service_principal`), and no token, secret, claim value or OpenBao error text. +A transport failure or an unexpected response is reported as `inconclusive`, never +as a refusal. A token that OpenBao accepts is revoked at once and fails the run. +Still waiting: railiance-platform's re-agreement on the two departures above, and +an attended registry session with net-kingdom. Register the clients +through the normal registration path, hand the run to railiance-platform, record +a value-free receipt in `docs/evidence/`, and deregister the clients afterwards. + +Acceptance: every case is refused, the receipt is committed, and the probe clients +are gone. + + +2026-10-11 source preparation: T01 remains progress because the exact expiry +client and audience-pin amendments require platform-owner re-agreement; the +prior agreement covered the earlier case list, not this revised packet. The +prepared runner revision adds a valid expiry positive control and immediate +revoke, exact decoded claim-isolation preflight (not signature evidence), +semantic refusal classes, bounded expiry TTL and redirect refusal. Seventeen +local tests pass; original-source semantic regressions demonstrate two false +successes. See `docs/openbao-negative-probe-session-proposal.md` for exact owner +returns, live authority, effective leeway, custody boundaries and cleanup. No +live token, registry or OpenBao action occurred. T02 continues to wait for owner +re-agreement plus separate attended live authorization. MASON-WP-0009-T05 +operator/ESO capability and nonmember checks remain independent acceptance. + +2026-10-11: Platform independently re-agreed the final amended design (session proposal SHA25623f13d2a5a4204f749f35c5d44ef39d213838cd2fce1c36fb4775254ef1c7f09). Original conditional review and narrow confirmation are retained in railiance-platform/docs/reviews/2026-10-11-keycape-openbao-negative-session-owner-review*. Source acceptance T01 is complete; actual registry/leeway/readiness, founder live go, negative/cleanup and separate capability/no-fallback obligations remain T02 and owner plans. + + +2026-10-17 Custodian coordinator finalization review (not a KeyCape formal owner +acknowledgment): the [infrastructure assessment](../../the-custodian/docs/changes/netkingdom-target-2026-10-17/key39-infra-finalization-r1/ASSESSMENT.md) +confirms all 14 executable/test/producer-decision pins and three Platform source +pins still match. Source reviews remain complete: T01/T03 done, T02 wait. Existing +source acceptance remains valid. Resolved registration go caafe4be covers only +the original secrets-engine-openbao client (KEY-WP-0037-T03, deployed 6f79a2af, +NK-WP-0046 exact-match helper); it does not authorize new probe clients or +coding-client custody, and no renewed original-client approval is requested. +The standing attended rule remains separate, for reviewed commands within +existing scope; no new protocol or blanket approval request is introduced. The +remaining sequence is KEY-WP-0038-T02/T03 first-case registration/claims proof, +admission of the existing coding-client proposal with sanitized owner readiness +returns, then one attended negative/revoke/cleanup receipt committed with probe +clients removed. No native acceptance or credential/registration action is +inferred from this coordinator review. + + +## KeyCape issuer-source compatibility inspection for lifecycle v2 + +```task +id: KEY-WP-0039-T03 +status: done +priority: medium +state_hub_task_id: "54d37ad8-2352-51c2-bb2e-af6070dbbd73" +``` + +2026-10-11: Completed KeyCape's own issuer-source inspection for +KEY-WP-0039-T02/attended-lifecycle-v2, exact manifest SHA256 +6fc0fe47f99a97c11c6e7538f5ea02054335df7d46055d3f84bed4e194aeaaed. +Read AGENTS.md, governing INTENT.md, current client-credentials claim source, +registration fragments, native caller-authentication commands and frozen v2 +issuer transport/probe wiring. Seventeen unchanged runner tests pass locally. +This task completes only issuer-source inspection; the resulting narrow +source acknowledgment is in docs/reviews/2026-10-11-key39-lifecycle-v2-keycape-source-ack.json (retained frozen prior acknowledgment). +T01 stays done and T02 stays open for actual native registration, token input, +refusal and cleanup proof. No Secret, token, credential or native environment was accessed; no owner +checkout was changed during this source inspection. Owner source files were +read for the bounded issuer compatibility check. + +Current token.go:306-309 returns HTTP 401 invalid_profile_usage with +feature=client_id for a removed confidential client; wrong-secret +feature=Authorization cannot count as removed-registration evidence. The +proposal and packet comments are narrowly corrected against that current +issuer contract. Prior T01/source re-agreement history is preserved; this source +acknowledgment supplies no NetKingdom runtime-owner review or founder live go. + +The private token-producer handoff remains a real execution gap under T02. +Existing keycape login/service-token commands require a new private output +file; login also needs a concrete admitted public PKCE client/callback and +attended browser/MFA. The lifecycle expects an in-memory private stdin object +containing human_token and coding_agent_token, but no reviewed file-to-process +or process-to-process producer, admitted human client/callback, custody-injected +coding-agent source, native placement or producer cleanup receipt has been +provided. No unattended token producer or admitted secret route is invented. +Resolve those exact owner contracts before founder native approval; existing +static registration is not live token custody or producer admission. + + +2026-10-11 same T03 source-inspection refinement for lifecycle V4: completed +KeyCape issuer-facing inspection before the new formal acknowledgment in +docs/reviews/2026-10-11-key39-lifecycle-v4-keycape-source-ack.json. Exact V4 +manifest SHA256 d079f85c47d5c2635e1ee633ea4d1346b1737ce2be9ca273f22a38e620f418d8, +executor b4687c2d07819c43b45d0cddcad0488b62eb49175b2a06a43f7806a0838d3a4d. +NetKingdom's independent V4 source review is approved, receipt SHA256 +4f4498203a31bd46dbc3715c0df1cbb9eee63bed5b2c1baf430ff3c4a37c13cb. +The V2/V3 unsuccessful reviews and earlier KeyCape acknowledgment remain +retained as one correction trial; they are not extra scored successes. + +V4 retains the unchanged issuer runner and six-client claim design, and fixes +finite/nonboolean windows, true total request deadlines, late-known OpenBao +session private revoke/refusal and guarded bounded final Secret deletion. +The issuer-facing acknowledgement covers only stopping late token exchanges, +claim source/shape and preserving token cleanup failure/unknown outcomes; it +does not replace NetKingdom runtime or Platform backend acceptance. Tests: 41 +supplied lifecycle, 17 unchanged runner and 17 independent owner checks pass +locally. No native tokens, credentials, issuer mutations or live cleanup were +performed. T01 stays done; T02 stays open and the genuine private token-producer +handoff remains its next source prerequisite before actual founder native go. + + +2026-10-11 same T03 source-inspection refinement for lifecycle V5: completed +KeyCape's own current issuer-source/claim/cleanup-contract inspection before +any new formal acknowledgment. Current token.go:306-309 still distinguishes +removed-client invalid_profile_usage feature=client_id from wrong-secret +feature=Authorization. The six-client design, audience source and expiry +positive control remain unchanged, and the original runner's 17 tests pass. +Exact V5 manifest SHA256 +d1f997624db46693a7126a6eb6435f6b3a353d5bd67dcc8a539683e4b7147285; +executor 3bcceebc845b17492040c76223d77088257c547a2b890198e7b5c2b04a0cd360. +V5 has 46 passing supplied lifecycle checks. It adds a single exceptional +response-close abort under a fixed20ms cap clipped to nonresettable aggregate +cleanup, and to positive original request time remaining, with expired aggregate +zero calls, fixed private errors and truthful known-session revoke/unknown +outcomes. This inspection covers issuer-facing token lifetime and claim +interfaces only; NetKingdom and Platform independently own their runtime and +backend review decisions. Previous V2/V3 Net source refusals and Platform V4 +exceptional-close refusal remain retained; KeyCape's V2/V4 acknowledgments are +historical narrow source inspections, never aggregate native approval. +T01 remains done, T02 open. No native actions or credentials occurred. The real +private token-producer/client/callback/coding-agent custody/cleanup handoff gap +stays the next prerequisite; no unattended admission is inferred. + +Exact independent V5 source decisions were then verified: NetKingdom approved +receipt SHA256 32a62bd1ca1bd7311965d016d4d6a6e4d9177f3423c63add436d9d309865eacc +(46 supplied +17 runner +21 independent checks); Platform approved receipt +SHA256 ba872dc53061b1dcca9dea1e8897597c394c1f9365e0fc49ee6f2095f57f11e0 +(46 supplied +5 independent +17 previously verified runner checks). Those are +foreign owners' own source decisions, not KeyCape runtime/backend reviews or +founder native authority. Formal KeyCape bounded issuer acknowledgment follows +in docs/reviews/2026-10-11-key39-lifecycle-v5-keycape-source-ack.json. + + +2026-10-11 T02 continuing source preparation: private producer decision packet +`KEY-WP-0039-T02/private-producer-handoff-v1` stages supported existing CLI +flows, selected existing netkingdom-bootstrap-console literal-loopback human +client subject to owner purpose/MFA/listener admission, and the existing +codex-railiance-platform source identity subject to actual live/custody/consumer +admission. No existing coding-agent delivery receipt was found; AppRole tokens +do not substitute for issuer JWTs. A bounded pipe-only adapter for the current +CLI's two caller-owned private files has 18 passing synthetic checks, with +identity-guarded explicit file consumption and fixed value-free failure output. +Decoded claim shape does not prove signatures or authenticated admission. +See docs/openbao-negative-token-producer-decision.md and .json for exact owner +choices and missing receipts. The exact trusted workstation-to-node launcher, +private-file allowance and railiance01 Bao route/public-health guard still need +source review and founder disposition before any token/temporary credential/ +mutation/restart. No registration, custody, login, native action or credential +request occurred. V5 and failed review histories retained; T01 stays done and +T02 open. No new work record or native acceptance is inferred. + + +2026-10-11 same T02 source-preparation correction for +KEY-WP-0039-T02/private-producer-handoff-v2: NetKingdom's exact V1 consumer +refusal remains recorded (no child-ready gate, stdio alias, non-atomic unlink). +V2 has source-pinned private launch/ready handshake compatible with exact +Net V9 manifest84542ddc: fresh nonce/source/envelope/health/window marker before +ANY caller-token file load/consume/write; separate local data/control pipes +reject kernel identity aliases of stdin/stdout/stderr. Adapter read/write budget +only clips downward after readiness. 31 distinct producer/launcher synthetic +checks and 74 compatible consumer supplied checks pass. No SSH/native/login or +credential access was performed. Stat/unlink remains non-atomic and requires +continuously exclusive sameUID custody; the retained race test is a custody +violation counterexample, not a claim of repaired filesystem atomicity. Receipt +keeps atomic delete/copy cleanup unverified and possible copies true. Exact +trusted launcher/directSSH profile/private tmpfs allowance, selected human +client/MFA purpose reuse and coding-agent registration/custody/delivery remain +unadmitted owner choices. Native node route, role/leeway/window/founder decision +and actual negative/cleanup acceptance remain pending. T01done and T02open; no +new task or plan, and V1/V5-V8/current failure histories are not overwritten. + + +2026-10-11 same T02 producer-preparation correction V3: Platform V2 pair +source refusal RPF-KEY39-PAIR-V2-F01 is retained. Private write uncertainty +now propagates into sticky remote consumer uncertainty before launcher early +return; local SSH termination never proves remote no-effects/recovery. Actual +full-payload then interrupted confirmation and4096-byte partialpipe failures +are retained regressions. 33 distinct source checks pass. Exact Net V9 source +bytes unchanged; new source reviews required, not implicit repin. Native +client/MFA/coding-agent custody/transport/tmpfsfile and actualroute/session +authority remain unadmitted. T01done/T02open; no new task/plan or native action. + + +2026-10-11 same T02 producer-preparation V4: exact V3 Net consumer refusal +NET-PRODUCER-V3-F01 retained. Actual CLI contract acquisition now bounded +nofollow/nonblocking regular stable reads and one ORIGINAL5-second preparation +budget across contract/source/envelope/SSHspawn/ready/file/delivery phases, not +a renewed launcher allowance. 40 own source tests plus9 retained paired +wrapper/child handshake regressions pass with synthetic/no native transport. +V3stickyremoteunknown/full+partialdelivery regressions retained; Net V9 exact +bytes unchanged. Prior issuer/custody/transport/file/route/readiness-Argo/role/ +maintenanceexception/founder choices remain unadmitted. No SSH/network/Secrets/ +credentials/native/owner checkout changes; no task/plan duplication. T01done +and T02open; source correction is not native acceptance. + + +2026-10-11 T02 source-design disposition checkpoint: founder accepted exact +source memo `infd-20261011-key39-source-design-r1`, review +`review-62626f54-0212-4684-9b45-0a7222d61957` version1 at `2026-10-11T17:19:01Z`. +Disposition `disp-870f9c1b-0444-4216-ae7f-f5463d590e36`, approval +`ca118afc-d746-4454-a5b9-9b387b120cd9`; authenticated person subject +`uid=platform-root,ou=people,dc=netkingdom,dc=local`. Exact canonical public review URL: +`https://decisions.coulomb.social/review?memo_id=review-62626f54-0212-4684-9b45-0a7222d61957`. +Root's authenticated confirmed readback is +`the-custodian/docs/evidence/2026-10-10-security-consolidation/key39-source-design-publication/founder-source-disposition-readback-r1.json`, +SHA256 `453213194d605336e9c424976f1ac27691def39e225026f0a99628d1cfc97d1d`; observed +`2026-10-11T17:30:47.080081+00:00`. Source commands SHA256 +`36e20813596f3ddbb86e01fd0b838867ece2f4916c5880305cf12cd61b567f89` and source inventory SHA256 +`aea196db9e513900d5e7165455d6e9c3dc68749a606dc219ec340ac595642cc0` bind the accepted source direction. + +Acceptance covers recommended source preparation for existing bootstrap-console +human-client purpose reuse, returning existing coding-agent custody admission or +preparing a narrow owner proposal, proposed founder directSSH source profile, +and conditional exclusive-owner tmpfs files. It does not establish actual +registration/MFA/listener, coding-agent custody/delivery, transport/file admission, +node-local Bao route, issuer/OpenBao role/leeway/maintenance readiness or fresh +attended native go. The published choice documents remain historical exact +pending proposals; this appended receipt records their later source disposition. +T02 remains wait on external owner custody/admission and separate attended +execution authorization. Source acceptance permits further source preparation; +actual registry, token exchange, negative probe, revoke and cleanup remain pending. T01 and T03 +remain done. No native or credential action is authorized by this source receipt. +See docs/proposals/2026-10-11-key39-next-execution-prerequisites.json for exact +owner returns and safe metadata evidence needed before preparing a runnable +attended execution proposal. + +2026-10-11 ordered-goal qualification (T02 only): the final source runner already +exists and its 17 local semantic tests pass. No new helper or source protocol is +needed to repeat accepted preparation. Native execution follows the first +Forgejo SecurityCase registration and claims proof (KEY-WP-0038-T02/T03); +only those task metadata were checked, not wait-task bodies. NetKingdom owns the +registry/env/restart session, KeyCape owns issuance/isolation, and Platform owns +leeway/refusal/capability/no-fallback/cleanup proof. The live registration go is +already recorded resolved; do not ask again. Attended human authentication and +owner-held existing coding-agent custody go on the single attended checklist. +No credentials, live registry/API changes or KEY-WP-0040 recovery work occurred. + +2026-10-17 Platform owner returns (source-prepared, not admitted): +railiance-platform `docs/proposals/2026-10-17-key39-t02-owner-returns.md` +(commit `0a9617a`). The coding client `codex-railiance-platform` has no +admission, custody or delivery record. The existing narrow proposal +`docs/proposals/2026-10-11-key39-coding-client-custody-proposal.json` (`9460f6f`) +remains the one to admit, so the coding-agent case stays unrunnable until owners +admit it. The role source (`openbao/auth/secrets-engine-jwt-role.json`) matches +the packet's claim design. It declares no `expiration_leeway`, so the expiry +probe must assume OpenBao's 150s default until an attended sanitized read +confirms the live value. The node-local Bao origin is not declared in source and +also needs that attended read. T02 stays wait. diff --git a/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0040-mfa-attended-run-findings.md b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0040-mfa-attended-run-findings.md new file mode 100644 index 00000000..8e8730c8 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0040-mfa-attended-run-findings.md @@ -0,0 +1,162 @@ +--- +id: KEY-WP-0040 +type: workplan +title: "Fix findings from the first attended MFA run (factor recovery preview, OTP prompt help)" +domain: infotech +repo: key-cape +status: finished +owner: claude-code +topic_slug: netkingdom +created: "2026-10-11" +updated: "2026-10-17" +flavor: implementation +depends_on: [] +state_hub_workstream_id: "a67f9d60-9b8f-5111-9fe5-9f64e631b1c3" +--- + +From the attended run of 2026-10-11 (user-engine `docs/evidence/2026-10-11-mfa-attended-u05-u08.md`). +U05–U07 passed. U08 is blocked by T01. + +## Recovery preview rejects valid input (F6) + +```task +id: KEY-WP-0040-T01 +status: done +priority: high +state_hub_task_id: "b385602d-714a-5fdb-b40b-bd870420f28e" +``` + +Operator `platform-root` (holds platform-operator, fresh AAL2) previewed login `tegwick`, +reference `U08-2026-10-11` at `/platform/factor-recovery`. Every attempt returned `invalid_request` +("Enter a valid login and support reference."). Both inputs match `[A-Za-z0-9_.@:/-]{1,150}`. +The `factor-recovery` container in `mfa/privacyidea` logged nothing. + +Candidate causes in `scripts/recovery_service.py` / `scripts/factor_recovery.py`: +- the actor `sub` (KeyCape `user.ID`) failing `_recover`'s character rule; +- a privacyIDEA serial failing it; +- the request body differing from what is validated, e.g. form encoding through the portal. + +Done when: +- the failing check is identified and fixed with a regression test; +- the sidecar logs the failing check (field name and error code, never values); +- a preview for `tegwick` lists the active TOTP (serial suffix …E1D) without applying anything. + +Then hand back to user-engine, which re-runs U08 (USER-WP-0028-T03). The fix and roll-out +of the sidecar are blue lane under the build-mode change rule. + +2026-10-12 source repair: LLDAP adapter returns entry.DN as user.ID and the token +issuer emits that exact value as sub. Recovery incorrectly applied the login +regex to actor, rejecting comma/equal characters. Two DN-shaped subject tests +reproduce invalid_request against the old source. The repair validates the +bounded printable ASCII actor separately, preserves exact ticket/audit binding, +and keeps target/serial/realm/reference validation strict. Validation diagnostics +emit only closed field names and error codes to stderr; provider logging stays +disabled. Twenty recovery tests pass, including identity switching, malformed +input, provider field failures, replay and mutation/audit guards. Go OIDC and +LLDAP suites pass. No live factor was read or changed. T01 stays progress until +the deployed authenticated preview succeeds and user-engine can rerun U08. + +## Link the OTP prompt to lost-authenticator help (F3) + +```task +id: KEY-WP-0040-T02 +status: done +priority: medium +state_hub_task_id: "3ad155dd-15ef-5522-bdf9-c78832728553" +``` + +The OTP input offers no way out for a user whose code is rejected or whose device is lost. Add +"Code rejected or lost your authenticator?" linking to +`https://users.coulomb.social/security` (the lost-authenticator section). The same link serves users +who have no factor yet (the dead end noted in `mfa-attended-run.md`). + +2026-10-12: source link added to the MFA challenge with noreferrer, preserving +challenge state and requiring no query/token in the destination. Go OIDC tests +pass. Runtime publication and attended journey verification remain open; T02 +is not represented as deployed. + +2026-10-12 blue-lane runtime repair completed: immutable ConfigMap +factor-recovery-01119187d90d7b76 replaced only the recovery-code volume through +UID/resourceVersion CAS. Server dry-run proved no other pod-template change. +privacyIDEA generation 11 is Ready; recovery health returns 200 and anonymous +recovery is denied (403). All Go tests pass in the actual checkout. An early +ready=false observation was retained while the rollout completed; no apply was +replayed. Abort owner: key-cape; rollback restores prior immutable ConfigMap +factor-recovery-79e2964c112f3688 with the same CAS helper. Receipt and exact +proposal: the-custodian/docs/evidence/2026-10-12-netkingdom-security/. +T01 remains progress for the founder's authenticated tegwick preview and U08. +T02 is a local source edit only; its source/image publication remains pending. + +2026-10-14: the MFA challenge render test now requires the lost-authenticator +link after the OTP form, with rel="noreferrer" and no request state in the URL. +T02 stays progress until that page is published and an attended journey sees it. + +## Current publication and attended acceptance — 2026-10-16 + +The source-only publication statements above describe the October 12–14 state. +They are superseded for T02: the October 15 founder-run publication recorded in +`docs/operations.md` (Current image) and KEY-WP-0041-T02 delivered image +`sha256:cdec9dd7c515f7e0a6169fc3fa560ee2cc47bf40d899755dec808c362f5ce010` +from source `8c6c071`. That source includes the OTP help link commit `205a1c0`. +The founder verified the image-only diff, server dry-run, Recreate rollout, +Ready pod with zero restarts and discovery HTTP200. No additional image rollout +is needed to make the existing T02 link available. This reconciliation makes +no new live observation and does not infer attended acceptance from publication. + +T01 still needs the actual authenticated preview; T02 still needs the actual +OTP-page observation. Both remain progress. Resume the existing owner procedure +`user-engine/docs/mfa-attended-run.md`, corrected by its October 11 evidence +`user-engine/docs/evidence/2026-10-11-mfa-attended-u05-u08.md`: + +1. In the real user-engine-portal OTP challenge, observe **Code rejected or lost + your authenticator?**, follow it to `https://users.coulomb.social/security`, + and confirm the rejected-code/lost-device guidance is reachable. Preserve no + challenge URL, request state, cookie or OTP value in evidence. Record time, + client, link text/destination and outcome. This supplies T02 acceptance. +2. With platform-root's own fresh AAL2 browser session, open the existing + `/platform/factor-recovery` form. Preview `tegwick` using the existing support + reference `U08-2026-10-11`; inspect any existing reference before changing it. + Confirm the current factor metadata and shared-identity warning. The October + 11 suffix was …E1D; a changed factor is a reason to reconcile current state, + never to force the old selection. Record only time, preview outcome and serial + suffix. This preview alone makes no factor change and supplies the remaining + T01 preview evidence. +3. Only in the separately attended recovery window, after the operator verifies + the person and exact current scope, continue the existing U08 confirmation, + audited disable, reference readback, replacement enrollment and fresh login + procedure. Preserve the original reference and safe recovery record id. + Neither the published bug fix nor a passing preview closes U08. Stop on + stale metadata, replaced factor or failed audit/readback; do not reset or + re-enroll merely to make an acceptance check pass. + +No authenticated preview, real-factor mutation or attended page observation was +performed during this source reconciliation. No passwords, codes, token values, +provider keys or production Secret objects were requested or read. + +## T02 attended acceptance — 2026-10-17 + +The founder reported in session that the real user-engine-portal OTP challenge +shows **Code rejected or lost your authenticator?**, that it leads to +`https://users.coulomb.social/security`, and that the lost-device guidance is +reachable there ("T02 is fine"). This is the founder's own observation, on the +deployed image `cdec9dd7` from source `8c6c071`. No exact time or client was +recorded, and no challenge URL, request state, cookie or OTP value was preserved. +T02 is done. T01 still waits for the authenticated preview (step 2 above). + +## T01 attended preview acceptance — 2026-10-17 + +The founder, signed in as platform-root with its own fresh OTP (AAL2), opened +`/platform/factor-recovery` and previewed login `tegwick` with the existing +reference `U08-2026-10-11`. The founder reported that the preview listed +tegwick's TOTP. The `invalid_request` failure from October 11 (F6) did not recur. +No confirm or disable action was taken, and no factor was changed. When asked +afterwards, the founder confirmed that the listed TOTP's serial suffix is …E1D, +the same active factor as on October 11. The exact time was not recorded. + +All three Done-when conditions now hold: the DN-shaped actor defect was fixed +with regression tests (2026-10-12); the sidecar logs only closed field names and +error codes; and the deployed preview for tegwick lists the active TOTP …E1D +without applying anything. That third condition was met only after the suffix +was confirmed, not before. T01 and the workplan are done. U08 itself, meaning +the attended disable, re-enrolment and fresh login, belongs to user-engine +(USER-WP-0028-T03) and is handed back to it. This workplan does not close U08. diff --git a/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0041-sign-in-diagnosis-page.md b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0041-sign-in-diagnosis-page.md new file mode 100644 index 00000000..56fd0581 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0041-sign-in-diagnosis-page.md @@ -0,0 +1,63 @@ +--- +id: KEY-WP-0041 +type: workplan +title: "Sign-in diagnosis page and auth log uid (NK-WP-0051-T02)" +domain: infotech +repo: key-cape +status: finished +owner: claude-code +topic_slug: netkingdom +created: "2026-10-14" +updated: "2026-10-15" +flavor: implementation +depends_on: [] +state_hub_workstream_id: "092c7590-3700-524d-bbe0-a934e063be6b" +--- + +Demand: NetKingdom `NK-WP-0051-T02`. The field names (`uid`, `groups`, +`assurance`, `client_id`) and the never-show/never-log list come only from +`net-kingdom/docs/sign-in-diagnosis.md`; this workplan does not restate them. + +## Opt-in pre-redirect page and auth log fields + +```task +id: KEY-WP-0041-T01 +status: done +priority: high +state_hub_task_id: "5947be07-bc84-5672-b807-608070a39d10" +``` + +- Client registration flag `signInDiagnosis` (default false) in the YAML + config, `domain.Client` and `spec/canonical-model.yaml`. +- With the flag on, `/authorize/callback` renders a page before the redirect + with `uid`, sorted `groups`, `assurance` and `client_id`, one continue link + to the unchanged redirect (code + state), and a sign-out hint. html/template + escaping, `no-store`, `Referrer-Policy: no-referrer`, restrictive CSP. +- `assurance` is `aal2` only when this request verified a one-time code; a + reused aal2 session is `aal1` (contract wording, stricter than the session + satisfied `MFAVerified`). +- The `auth_success` log line gains `uid` and `assurance` beside `client_id`. +- Enabled for `openbao-admin` in `config/dev-config.yaml`; the live flag is set + in `sso/keycape-config` (see `docs/operations.md`). +- Tests: `src/internal/server/oidc/signin_diagnosis_test.go`. + +## Deploy the image and record its digest + +```task +id: KEY-WP-0041-T02 +status: done +priority: high +state_hub_task_id: "bd5db26c-b71c-5291-bdc3-7c0f718651db" +``` + +Build and push with `make push IMAGE_TAG=`, set `signInDiagnosis: true` +on the OpenBao browser client in `sso/keycape-config`, roll the issuer under +the usual live-change approval, and send the digest to net-kingdom for the +evidence table in `docs/sign-in-diagnosis.md`. Not done by the implementing +session: deployment needs operator approval. + +2026-10-15: image `sha256:cdec9dd7c515f7e0a6169fc3fa560ee2cc47bf40d899755dec808c362f5ce010` +(source `8c6c071`) is live; see `docs/operations.md`. 2026-10-15: `signInDiagnosis: true` set on `openbao-admin` with +net-kingdom `openbao-signin-diagnosis-rollout.py` (rv 71655453 → 75372162, +unrelated bytes preserved) and the issuer restarted; pod ready, discovery 200. +Accepted 2026-10-15: an attended OpenBao login showed the page and completed. diff --git a/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency.stderr.txt b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency.stderr.txt new file mode 100644 index 00000000..3345563f --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency.stderr.txt @@ -0,0 +1 @@ +WARN: git commit failed for writeback: Generated-only commits rejected: staged diff --git a/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency.stdout.json b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency.stdout.json new file mode 100644 index 00000000..7817dab6 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency.stdout.json @@ -0,0 +1,123 @@ +{ + "repo_slug": "key-cape", + "repo_path": "/home/worsch/key-cape", + "source_revision": "d803e9282a47b49daf930fb905312fa5104c0ace", + "source_revision_reason": null, + "issues": [ + { + "severity": "WARN", + "check_id": "C-24", + "message": "Repo classification advisory: capability_tag 'security' is not a recommended family tag (allowed, check for synonym)", + "file_path": "", + "db_id": "", + "file_value": "", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-11", + "message": "Task 'ADHOC-2026-09-27-T02' has no state_hub_task_id", + "file_path": "workplans/ADHOC-2026-09-27.md#ADHOC-2026-09-27-T02", + "db_id": "", + "file_value": "", + "db_value": "", + "fixable": true + }, + { + "severity": "WARN", + "check_id": "C-20", + "message": "Workplan dependency target 'USER-WP-0020' is not linked to State Hub", + "file_path": "workplans/KEY-WP-0007-user-engine-portal-oidc-client.md", + "db_id": "", + "file_value": "USER-WP-0020", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-20", + "message": "Workplan dependency target 'NK-WP-0025' is not linked to State Hub", + "file_path": "workplans/KEY-WP-0008-registration-handoff-and-client-mfa-policy.md", + "db_id": "", + "file_value": "NK-WP-0025", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-20", + "message": "Workplan dependency target 'NK-WP-0030' is not linked to State Hub", + "file_path": "workplans/KEY-WP-0009-provider-capabilities-and-service-identities.md", + "db_id": "", + "file_value": "NK-WP-0030", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-20", + "message": "Workplan dependency target 'RAILIANCE-WP-0027' is not linked to State Hub", + "file_path": "workplans/KEY-WP-0010-openbao-operator-loopback-callback.md", + "db_id": "", + "file_value": "RAILIANCE-WP-0027", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-41", + "message": "Task wait-qualifier drift 'KEY-WP-0039-T02': file={'needs_human': False, 'blocking_reason': 'Ordered identity gate: Forgejo first SecurityCase registration/claims proof, then NetKingdom registry session and attended human/coding-agent custody; original secrets-engine-openbao registration go remains resolved; probe/coding-client custody and attended readiness are not established.', 'decision_id': None, 'blocked_on': None, 'wait_until': None} db={'needs_human': False, 'blocking_reason': 'Ordered identity gate: Forgejo first SecurityCase registration/claims proof, then NetKingdom registry session and attended human/coding-agent custody; registration authorization already resolved.', 'decision_id': None, 'blocked_on': None, 'wait_until': None} (file wins)", + "file_path": "workplans/KEY-WP-0039-openbao-negative-probe-tokens.md#KEY-WP-0039-T02", + "db_id": "03da4c38-1dac-53b1-8b0b-e697df48e9d3", + "file_value": "{\"blocked_on\": null, \"blocking_reason\": \"Ordered identity gate: Forgejo first SecurityCase registration/claims proof, then NetKingdom registry session and attended human/coding-agent custody; original secrets-engine-openbao registration go remains resolved; probe/coding-client custody and attended readiness are not established.\", \"decision_id\": null, \"needs_human\": false, \"wait_until\": null}", + "db_value": "{\"blocked_on\": null, \"blocking_reason\": \"Ordered identity gate: Forgejo first SecurityCase registration/claims proof, then NetKingdom registry session and attended human/coding-agent custody; registration authorization already resolved.\", \"decision_id\": null, \"needs_human\": false, \"wait_until\": null}", + "fixable": true + }, + { + "severity": "INFO", + "check_id": "C-08", + "message": "Retired DB workstream 'keycape-implementation@retired-20260826' (id=2c9caad8\u2026, status=archived) has no backing workplan file \u2014 projection tombstone, not live work", + "file_path": "", + "db_id": "2c9caad8-2ced-492d-9d63-376387b4b9b0", + "file_value": "", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-28", + "message": "2 unread inbox message(s) older than 3 day(s): the-custodian:23172cb2, the-custodian:51e9eace", + "file_path": "", + "db_id": "", + "file_value": "", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-28", + "message": "2 unread message(s) lack thread_id for supersession tracking: the-custodian:23172cb2, the-custodian:51e9eace", + "file_path": "", + "db_id": "", + "file_value": "", + "db_value": "", + "fixable": false + } + ], + "fixes_applied": [ + "bindings: synced 44/44", + "C-17 cleared: pushed 1 backlogged commit(s)", + "C-11 skipped: task 'ADHOC-2026-09-27-T02' in finished workstream \u2014 not created", + "C-41 fixed: task 03da4c38\u2026 wait qualifiers synced", + "brief: .custodian-brief.md updated", + "push: pushed" + ], + "summary": { + "fail": 0, + "automation_error": 0, + "warn": 9, + "info": 1 + }, + "result": "warn" +} diff --git a/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/final-verification.json b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/final-verification.json new file mode 100644 index 00000000..ddaa424c --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/final-verification.json @@ -0,0 +1,54 @@ +{ + "commit": "d803e9282a47b49daf930fb905312fa5104c0ace", + "receiver_status": "applied", + "acknowledge_retirements": false, + "instance_role": "primary", + "instance_label": "railiance01", + "workplan": { + "id": "09c3bddf-6058-5603-8488-cccdb7be4f86", + "status": "active", + "record_id": "KEY-WP-0039" + }, + "task_bindings": [ + { + "record_id": "KEY-WP-0039-T01", + "state_hub_task_id": "4d06b059-3768-53bb-83a4-a9a71c0850b8", + "status": "done", + "workplan_id": "09c3bddf-6058-5603-8488-cccdb7be4f86", + "depends_on_source": [] + }, + { + "record_id": "KEY-WP-0039-T02", + "state_hub_task_id": "03da4c38-1dac-53b1-8b0b-e697df48e9d3", + "status": "wait", + "workplan_id": "09c3bddf-6058-5603-8488-cccdb7be4f86", + "depends_on_source": [ + "KEY-WP-0039-T01", + "KEY-WP-0038-T02", + "KEY-WP-0038-T03" + ] + }, + { + "record_id": "KEY-WP-0039-T03", + "state_hub_task_id": "54d37ad8-2352-51c2-bb2e-af6070dbbd73", + "status": "done", + "workplan_id": "09c3bddf-6058-5603-8488-cccdb7be4f86", + "depends_on_source": [] + } + ], + "all_other_task_fields_unchanged": true, + "only_task_field_delta": { + "task": "KEY-WP-0039-T02", + "field": "blocking_reason", + "old": "Ordered identity gate: Forgejo first SecurityCase registration/claims proof, then NetKingdom registry session and attended human/coding-agent custody; registration authorization already resolved.", + "new": "Ordered identity gate: Forgejo first SecurityCase registration/claims proof, then NetKingdom registry session and attended human/coding-agent custody; original secrets-engine-openbao registration go remains resolved; probe/coding-client custody and attended readiness are not established." + }, + "needs_human": false, + "primary_task_blocking_reason": "Ordered identity gate: Forgejo first SecurityCase registration/claims proof, then NetKingdom registry session and attended human/coding-agent custody; original secrets-engine-openbao registration go remains resolved; probe/coding-client custody and attended readiness are not established.", + "frontmatter_unchanged": true, + "only_committed_path": "workplans/KEY-WP-0039-openbao-negative-probe-tokens.md", + "remote_forge_matches": true, + "owner_worktree_and_index_clean": true, + "normal_sync_refusal": "unrelated grandfathered ADHOC-2026-09-27-T02 UUID; not modified", + "native_acceptance_inferred": false +} diff --git a/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/manifest.json b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/manifest.json new file mode 100644 index 00000000..bfb32912 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/manifest.json @@ -0,0 +1,70 @@ +{ + "scope": "only authorized KEY39 T02 blocking_reason suffix correction; all other task fields/frontmatter unchanged; no native action", + "commit": "d803e9282a47b49daf930fb905312fa5104c0ace", + "files": { + "KEY39.preimage.md": "sha256:618600946f6dd4128206db7ab23da9cc96e22cb84e0fd301b1b2eacd5c9bdba4", + "KEY39.proposed.md": "sha256:517f9526d574ce3faafad18837e0039db8f3f7990eac3e34a1678cdd7307a9e1", + "apply.py": "sha256:1c4997372c3c43e78ffcce533e33b0c38ececaf252cd4bf62db69cf43f11967a", + "baseline.json": "sha256:7415b840f97b217a29004cd9b3a4a0737dbc31c9c03b821d326442bc793c95ab", + "canonical-receiver.json": "sha256:31a225513ccee3266fd59476950875c78bcbe57d13558b20940a096e95536e80", + "canonical-reconcile.py": "sha256:dca4dcfc37db7f68e3059fe99b910ff7f207e2ef3c4b69f926ebaf46317d078a", + "commit.json": "sha256:9e534c5ab7bd5ebb728774c58d363eaa52bcbb3f561118df75ad07eef9699974", + "consistency-backup/.custodian-brief.md": "sha256:96d84539b3a92786bff37fcc70161adec4648b9547287e52e6b817bc080a080a", + "consistency-backup/WORK-RECORDS.md": "sha256:431842547bd82750f284f8c1c9f519892f1bb1c1a684b9b1e38adc6fdfdc299d", + "consistency-backup/originals.json": "sha256:0d7851a532b1b451b20eb00a1eb98727b004da073173263251cf3fae40a34e52", + "consistency-backup/workplans/ADHOC-2026-09-05.md": "sha256:76123d22d0c1efc51212f8914e386f078b3c6d908959af6978ae73874e492b04", + "consistency-backup/workplans/ADHOC-2026-09-07.md": "sha256:c335ebfe91dad291277e8f17034ac8f785b3bc767f281f205c446c08f03d7978", + "consistency-backup/workplans/ADHOC-2026-09-27.md": "sha256:2794d1940fc3c5e4a2df57de9d4b742fa6aa8602b67f1d3177c0d96bc5aaf314", + "consistency-backup/workplans/KEY-WP-0001-keycape-implementation.md": "sha256:29a2bf4892b7185e28e9126a3d8311d0fed34a876f416a3e75c605915fde0969", + "consistency-backup/workplans/KEY-WP-0002-container-image-gitea.md": "sha256:76fee187185b30219bc257ccc9b587661df4489574a2bb57703a365a5d803cec", + "consistency-backup/workplans/KEY-WP-0003-bootstrap-console-oidc-mfa-login.md": "sha256:5e949c696e587540d305b682eb1f8390ec544bbec6a566be8d04e73fb1b254ca", + "consistency-backup/workplans/KEY-WP-0004-binky-hedgehog-tenant-onboarding.md": "sha256:260ba7d3116db3bc5a72e5cd8b9984137e7e372c8128d21073614dbd948c10c2", + "consistency-backup/workplans/KEY-WP-0005-iam-profile-core-claims.md": "sha256:c6702b644cdd046c0d3bba8ba8837050826e4dc3d855bd73327ae94218ba5b57", + "consistency-backup/workplans/KEY-WP-0006-client-credentials-service-tokens.md": "sha256:b3227849978c95cad89e02afb74bf2217b6188ae65c5b0af6a21703d65477701", + "consistency-backup/workplans/KEY-WP-0007-user-engine-portal-oidc-client.md": "sha256:5408ff6dfeb58a8a7756b48fe4f54f7c7b98d7940cb9aaa5c7864f1247e58150", + "consistency-backup/workplans/KEY-WP-0008-registration-handoff-and-client-mfa-policy.md": "sha256:bfc9b2118f0712e92a86bbb0f14a2857fafc3a42a3c14c0e2535002aaa57a2f5", + "consistency-backup/workplans/KEY-WP-0009-provider-capabilities-and-service-identities.md": "sha256:d1daac9815930104a392ea96c14922e34f947720a31d1b0ac45a770997e0f476", + "consistency-backup/workplans/KEY-WP-0010-openbao-operator-loopback-callback.md": "sha256:41d28a16bef9313b776c5c6305316a9e7d4f5528b7fb7427b8384a0ed0ad35c4", + "consistency-backup/workplans/KEY-WP-0011-live-secret-exposure-recovery.md": "sha256:3c5458180fe81a04e357f8db737e4287f79640b5ab02682bcadc595b73f846cd", + "consistency-backup/workplans/KEY-WP-0012-userinfo-canonical-subject-resolution.md": "sha256:68d34d75c914e7d2d8177000e800d6372cccda2b850c03ab85da42b90a66b91c", + "consistency-backup/workplans/KEY-WP-0013-approval-engine-resource-audience.md": "sha256:55c88bf4ed9a456db59b319bbaf960dfe6f4147437e339f950e0583d7582057a", + "consistency-backup/workplans/KEY-WP-0014-native-credential-lane-handoff.md": "sha256:22aac43ae5039aebd11f9f0aa57a882ad009ad6ed119ec08eb85b32a5af48cd7", + "consistency-backup/workplans/KEY-WP-0015-scope-intent-assessment.md": "sha256:60b9f2265eff880d916c3ee8dba36aec9984ec310804de776cebf89cc98fdb95", + "consistency-backup/workplans/KEY-WP-0016-authorization-code-protocol-hardening.md": "sha256:ed48128a1cb51cbe3d154727bafd5a5ffb3b5b618c20649b2a0e7db154cf3a0c", + "consistency-backup/workplans/KEY-WP-0017-canonical-model-and-discovery-conformance.md": "sha256:8700e0e5aa07dcd7f651e792cca12a0330b7ee9d2f6064b4cc3fcf4c7d509340", + "consistency-backup/workplans/KEY-WP-0018-export-completeness-evidence.md": "sha256:15026b708d116f803b0b2fa43f30a4dbced76e4d8137b5fcb5b4275e593d7c7b", + "consistency-backup/workplans/KEY-WP-0019-upstream-provider-token-verification.md": "sha256:0d2d8b5bda1e0d333935812d8d3749cb7b0c5523a3987d1b26185f9f6d0f0fa2", + "consistency-backup/workplans/KEY-WP-0020-migration-contract-preservation.md": "sha256:cc842d15bf42ddc4d0f4056bc19b3176cab2c5f3c1a2fe6d648e5dbd077be8b3", + "consistency-backup/workplans/KEY-WP-0021-snapshot-attribute-validation.md": "sha256:b603248693073a9b9b25b4d0a4147d8d88c16a07dc9936e48fa1f3c76fe8bab6", + "consistency-backup/workplans/KEY-WP-0022-replacement-harness-and-external-conformance.md": "sha256:8c135e84be81de31b4949f52ac28dccd404f2e4c3eaa576d733bd50160427a53", + "consistency-backup/workplans/KEY-WP-0023-live-migration-proof.md": "sha256:e581cd11f3905805b5e8ba8e2ede85092577c7d086e1761f2a2d033fdead4e47", + "consistency-backup/workplans/KEY-WP-0024-tenant-roles-opt-in-wiring.md": "sha256:af7842fa97263bfd46a4b5ae84c5c1ecddb06ab9623586e0292dcc93099fea35", + "consistency-backup/workplans/KEY-WP-0025-runtime-lifecycle-and-readiness.md": "sha256:0addb34e6d85e376c6a5db7afba597b6f268afa98b86c8c60add9be174dd1b26", + "consistency-backup/workplans/KEY-WP-0026-packaging-bootstrap-and-credential-handling.md": "sha256:c1bc0de47511ae49102009a172c84d9d5d64e829a18c8d40564fe817935563b3", + "consistency-backup/workplans/KEY-WP-0027-rollout-readiness-and-live-state.md": "sha256:f559eae61d433d451f80ea575254bb14a47f2485267b412e42d524b434bb1b59", + "consistency-backup/workplans/KEY-WP-0028-browser-client-field-validation.md": "sha256:fbd51422fa5e6449000e4520e6639f6135c7393165a5ae02b1e92892edba0480", + "consistency-backup/workplans/KEY-WP-0029-scope-reassessment.md": "sha256:3241f3b94db9cad5e3770cf1fedc5c211288357e4f69b5ba9da57065094bdf0e", + "consistency-backup/workplans/KEY-WP-0030-tenant-precondition-guard.md": "sha256:c94c3afc7fb4270dfc18f3fe8a32946c524e4a6ba2d02a3d8fda69c9b8441af0", + "consistency-backup/workplans/KEY-WP-0031-gate-house-tenant-ruling.md": "sha256:0abb135367f0366aa071769c4bd76fdeb321d810ec75ad3b58d718734f78382b", + "consistency-backup/workplans/KEY-WP-0032-principal-type-provenance-guard.md": "sha256:6a5cd483a40b9477227b02156c42b3469094dac145b33727b336118898aa782a", + "consistency-backup/workplans/KEY-WP-0033-vergabe-fresh-login.md": "sha256:c01bb51cf2dcad06ea846040dddef09845041fa7271cd7015e5a34a49e60bb8f", + "consistency-backup/workplans/KEY-WP-0034-account-recovery.md": "sha256:7db9adcd418ccabfbce672aec283249a6e374212492f0349e8c47a568f130eda", + "consistency-backup/workplans/KEY-WP-0035-optional-mfa.md": "sha256:c60ea8d0c195686c0d2317f657aad2de27c9b9432d311627c0e1d06124cabb49", + "consistency-backup/workplans/KEY-WP-0036-platform-factor-recovery.md": "sha256:5ceb08fe5789b4f02550bb04fcaf8eeb25a43ac3f506dfc62d60a513bcd0013f", + "consistency-backup/workplans/KEY-WP-0037-secrets-engine-openbao-registration-custody.md": "sha256:3a90a0c460fc439570820b96d1ddc3b7f8646c557ca3a8aa27c6d546bb1a845a", + "consistency-backup/workplans/KEY-WP-0038-forgejo-oidc-client.md": "sha256:dc8d88d462799e7e6e44da759a973d52e44102a271969a5daaa9d30ec015b064", + "consistency-backup/workplans/KEY-WP-0039-openbao-negative-probe-tokens.md": "sha256:517f9526d574ce3faafad18837e0039db8f3f7990eac3e34a1678cdd7307a9e1", + "consistency-backup/workplans/KEY-WP-0040-mfa-attended-run-findings.md": "sha256:98b23d8810bff047067169232213cb313ecc14ff79751224842723afe808fbd2", + "consistency-backup/workplans/KEY-WP-0041-sign-in-diagnosis-page.md": "sha256:7e44f46acd6b3942c1852b49e0d64b205dfd7d9bb378aed5241b1bf22d7ce88a", + "consistency.stderr.txt": "sha256:f4a6aa1609a13600a0b97cbb08bbc03b3e247b619ff37912d37f7e109ce798ba", + "consistency.stdout.json": "sha256:a77c1056e9cbf8fb5ecced6ef316b7bde9646d258452f875575c4e039fea7709", + "final-verification.json": "sha256:43e0489c538788921be2f9ea2dec89451730a46ea6ace5b5d35b6e9d812db7a6", + "normal-publication.json": "sha256:724f185f729f8224fc85a257cb9c564d77585e81f672e11eb2c77f4de56c20ce", + "normal-sync.stderr.txt": "sha256:832bd22063c46072d0d91381f7d1356be933b4d09ded52fa19282da13c2855b1", + "normal-sync.stdout.json": "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "one-path.patch": "sha256:3dd74d482fe512683731cde8b57d3e3259f0fb523272ee88e6838a17ceb35611", + "progress-receipt.json": "sha256:ae2bc1eb3c069753e269bb176b0660ba732a3ab966853a5d6a0d69613b1bf835", + "progress-request.json": "sha256:df686f30b8ca8704745bd3317a87611a292409719f0e5a996d267fb087da63f7", + "sync.py": "sha256:f9714d9ddd0b47e9e359edabc2b3f7edf84c2c9b4d55952215f2df65750415d0" + } +} diff --git a/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/normal-publication.json b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/normal-publication.json new file mode 100644 index 00000000..47333f2f --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/normal-publication.json @@ -0,0 +1,34 @@ +{ + "published_candidate_commit": "d803e9282a47b49daf930fb905312fa5104c0ace", + "normal_commands": [ + { + "command": [ + "statehub", + "fix-consistency", + "--repo", + "key-cape", + "--json" + ], + "exit_code": 0, + "label": "consistency" + }, + { + "command": [ + "/home/worsch/repo-manager/.venv/bin/rmgr", + "sync", + "--path", + "/home/worsch/key-cape", + "--push" + ], + "exit_code": 1, + "label": "normal-sync" + } + ], + "unrelated_writeback_restored": [ + ".custodian-brief.md" + ], + "all_original_index_worktree_fields_preserved": true, + "one_path_commit": true, + "normal_hooks": "changed-staged-blobs scanner8.30.1 clean", + "native_or_credential_actions": 0 +} diff --git a/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/normal-sync.stderr.txt b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/normal-sync.stderr.txt new file mode 100644 index 00000000..682f290e --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/normal-sync.stderr.txt @@ -0,0 +1,13 @@ +Traceback (most recent call last): + File "/home/worsch/repo-manager/.venv/bin/rmgr", line 8, in + sys.exit(main()) + ^^^^^^ + File "/home/worsch/repo-manager/src/repo_manager/cli.py", line 853, in main + result = sync_repository_projection( + ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/home/worsch/repo-manager/src/repo_manager/projection_sync.py", line 98, in sync_repository_projection + assignments = ensure_missing_work_record_identifiers(repo_root) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/home/worsch/repo-manager/src/repo_manager/identifiers.py", line 962, in ensure_missing_work_record_identifiers + raise ValueError( +ValueError: missing UUIDs on grandfathered or noncanonical records require an explicit identity decision: [{'kind': 'task', 'record_id': 'ADHOC-2026-09-27-T02', 'path': 'workplans/ADHOC-2026-09-27.md'}] diff --git a/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/normal-sync.stdout.json b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/normal-sync.stdout.json new file mode 100644 index 00000000..e69de29b diff --git a/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/one-path.patch b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/one-path.patch new file mode 100644 index 00000000..27ada3ad --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/one-path.patch @@ -0,0 +1,13 @@ +diff --git a/workplans/KEY-WP-0039-openbao-negative-probe-tokens.md b/workplans/KEY-WP-0039-openbao-negative-probe-tokens.md +index b22aa6a..4398517 100644 +--- a/workplans/KEY-WP-0039-openbao-negative-probe-tokens.md ++++ b/workplans/KEY-WP-0039-openbao-negative-probe-tokens.md +@@ -99,7 +99,7 @@ transient Kubernetes Secret that is deleted in the same session. + id: KEY-WP-0039-T02 + status: wait + depends_on: [KEY-WP-0039-T01, KEY-WP-0038-T02, KEY-WP-0038-T03] +-blocking_reason: "Ordered identity gate: Forgejo first SecurityCase registration/claims proof, then NetKingdom registry session and attended human/coding-agent custody; registration authorization already resolved." ++blocking_reason: "Ordered identity gate: Forgejo first SecurityCase registration/claims proof, then NetKingdom registry session and attended human/coding-agent custody; original secrets-engine-openbao registration go remains resolved; probe/coding-client custody and attended readiness are not established." + priority: medium + state_hub_task_id: "03da4c38-1dac-53b1-8b0b-e697df48e9d3" + ``` diff --git a/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/progress-receipt.json b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/progress-receipt.json new file mode 100644 index 00000000..be872c50 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/progress-receipt.json @@ -0,0 +1,14 @@ +{ + "workplan_id": "09c3bddf-6058-5603-8488-cccdb7be4f86", + "id": "777d28ea-7ebc-4f2a-814f-adad63d72dd5", + "topic_id": null, + "task_id": "03da4c38-1dac-53b1-8b0b-e697df48e9d3", + "decision_id": null, + "event_type": "note", + "summary": "Published KEY39 current narrative approval-scope clarification in commit 26ec9bb9b195ad377448570d6b161fd682311a0b, then the separately authorized T02 blocking_reason suffix correction in final commit d803e9282a47b49daf930fb905312fa5104c0ace. Decision caafe4be remains valid only for original secrets-engine-openbao registration under KEY-WP-0037-T03 / deployed 6f79a2af / NK-WP-0046 exact helper; it does not authorize new probe clients or coding-client custody. Standing attended rule remains separate within reviewed existing scope; no renewed original-client approval or new protocol requested. Both ordinary one-file commits passed normal hooks, consistency passed, generated brief writeback restored, unrelated historical ADHOC UUID gap preserved. Canonical primary receiving applied each exact published commit with retirement acknowledgment false. Primary task 03da4c38-1dac-53b1-8b0b-e697df48e9d3 blocking_reason equals the entire corrected source value and needs_human remains false. T01 done, T02 wait, T03 done; UUIDs, dependencies, priorities and frontmatter unchanged. No native, credential or registration action. New work stopped after source/metadata verification.", + "detail": null, + "author": "codex", + "session_id": null, + "created_at": "2026-10-17T11:42:42.743840Z", + "workstream_id": "09c3bddf-6058-5603-8488-cccdb7be4f86" +} diff --git a/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/progress-request.json b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/progress-request.json new file mode 100644 index 00000000..f97b2b7d --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/progress-request.json @@ -0,0 +1,7 @@ +{ + "summary": "Published KEY39 current narrative approval-scope clarification in commit 26ec9bb9b195ad377448570d6b161fd682311a0b, then the separately authorized T02 blocking_reason suffix correction in final commit d803e9282a47b49daf930fb905312fa5104c0ace. Decision caafe4be remains valid only for original secrets-engine-openbao registration under KEY-WP-0037-T03 / deployed 6f79a2af / NK-WP-0046 exact helper; it does not authorize new probe clients or coding-client custody. Standing attended rule remains separate within reviewed existing scope; no renewed original-client approval or new protocol requested. Both ordinary one-file commits passed normal hooks, consistency passed, generated brief writeback restored, unrelated historical ADHOC UUID gap preserved. Canonical primary receiving applied each exact published commit with retirement acknowledgment false. Primary task 03da4c38-1dac-53b1-8b0b-e697df48e9d3 blocking_reason equals the entire corrected source value and needs_human remains false. T01 done, T02 wait, T03 done; UUIDs, dependencies, priorities and frontmatter unchanged. No native, credential or registration action. New work stopped after source/metadata verification.", + "event_type": "note", + "author": "codex", + "workplan_id": "09c3bddf-6058-5603-8488-cccdb7be4f86", + "task_id": "03da4c38-1dac-53b1-8b0b-e697df48e9d3" +} diff --git a/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/sync.py b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/sync.py new file mode 100644 index 00000000..87724aaf --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/sync.py @@ -0,0 +1,36 @@ +from pathlib import Path +import subprocess,json,hashlib,stat +root=Path('/home/worsch/key-cape');out=Path('/home/worsch/the-custodian/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1');backup=out/'consistency-backup';backup.mkdir() +def git(*args):return subprocess.check_output(['git','-C',str(root),*args]).decode() +head=git('rev-parse','HEAD').strip();assert head==json.loads((out/'commit.json').read_text())['commit'] +assert git('diff-tree','--no-commit-id','--name-only','-r',head).splitlines()==['workplans/KEY-WP-0039-openbao-negative-probe-tokens.md'] +assert not git('status','--porcelain=v1') +paths=sorted({p.relative_to(root).as_posix() for p in (root/'workplans').rglob('*.md')}|{n for n in ('.custodian-brief.md','WORK-RECORDS.md') if (root/n).exists()}) +original={} +for rel in paths: + p=root/rel;assert p.is_file() and not p.is_symlink() + dest=backup/rel;dest.parent.mkdir(parents=True,exist_ok=True);dest.write_bytes(p.read_bytes()) + original[rel]={'sha256':hashlib.sha256(p.read_bytes()).hexdigest(),'mode':stat.S_IMODE(p.stat().st_mode),'index':git('ls-files','--stage','--',rel)} +(backup/'originals.json').write_text(json.dumps(original,indent=2)+'\n') +results=[];changed=[] +try: + for label,argv in [('consistency',['statehub','fix-consistency','--repo','key-cape','--json']),('normal-sync',['/home/worsch/repo-manager/.venv/bin/rmgr','sync','--path',str(root),'--push'])]: + cp=subprocess.run(argv,cwd=root,stdout=subprocess.PIPE,stderr=subprocess.PIPE,timeout=240) + (out/(label+'.stdout.json')).write_bytes(cp.stdout);(out/(label+'.stderr.txt')).write_bytes(cp.stderr) + results.append({'command':argv,'exit_code':cp.returncode,'label':label}) +finally: + assert git('rev-parse','HEAD').strip()==head,'unexpected commit; stop for review' + for rel,b in original.items(): + p=root/rel + if not p.exists() or hashlib.sha256(p.read_bytes()).hexdigest()!=b['sha256']: + changed.append(rel);p.write_bytes((backup/rel).read_bytes());p.chmod(b['mode']) + current=git('ls-files','--stage','--',rel) + if current!=b['index']: + assert b['index'];mode,oid,stage,path=b['index'].split();assert stage=='0' + git('update-index','--cacheinfo',mode,oid,path) + assert git('ls-files','--stage','--',rel)==b['index'] + assert hashlib.sha256(p.read_bytes()).hexdigest()==b['sha256'] + assert not git('status','--porcelain=v1'),git('status','--porcelain=v1') + receipt={'published_candidate_commit':head,'normal_commands':results,'unrelated_writeback_restored':changed,'all_original_index_worktree_fields_preserved':True,'one_path_commit':True,'normal_hooks':'changed-staged-blobs scanner8.30.1 clean','native_or_credential_actions':0} + (out/'normal-publication.json').write_text(json.dumps(receipt,indent=2)+'\n') + print(json.dumps(receipt)) diff --git a/docs/changes/netkingdom-target-2026-10-17/key39-finalization-current-r2/close-progress-receipt-r3.json b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-current-r2/close-progress-receipt-r3.json new file mode 100644 index 00000000..0216facb --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-current-r2/close-progress-receipt-r3.json @@ -0,0 +1,14 @@ +{ + "workplan_id": "09c3bddf-6058-5603-8488-cccdb7be4f86", + "id": "32796349-c902-456c-97e6-e4de38467aac", + "topic_id": null, + "task_id": "03da4c38-1dac-53b1-8b0b-e697df48e9d3", + "decision_id": null, + "event_type": "note", + "summary": "Reviewed KEY-WP-0039 and infrastructure owner reviews for finalization. Revalidated17 executable/producer/Platform source pins plus exact source-only infrastructure approval and seven KEY38/KEY39 source/Hub task bindings. T01/T03 complete; T02 remains wait for KEY38 first-case claims proof, existing coding-client custody admission/readiness returns, and actual attended negative/expiry/revoke/client-removal receipt. Kept parent active and existing approvals intact; no native or credential action. Evidence: docs/changes/netkingdom-target-2026-10-17/key39-finalization-current-r2/close-verification-r3.json.", + "detail": null, + "author": "codex", + "session_id": null, + "created_at": "2026-10-17T21:58:46.203045Z", + "workstream_id": "09c3bddf-6058-5603-8488-cccdb7be4f86" +} diff --git a/docs/changes/netkingdom-target-2026-10-17/key39-finalization-current-r2/close-verification-r3.json b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-current-r2/close-verification-r3.json new file mode 100644 index 00000000..40f35578 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-current-r2/close-verification-r3.json @@ -0,0 +1,58 @@ +{ + "observed_at": "2026-10-17T21:58:43.188615+00:00", + "review_files_and_source_pins_verified": 18, + "plans": { + "KEY-WP-0039": { + "source_sha256": "sha256:517f9526d574ce3faafad18837e0039db8f3f7990eac3e34a1678cdd7307a9e1", + "parent_status": "active", + "tasks": [ + { + "record_id": "KEY-WP-0039-T01", + "uuid": "4d06b059-3768-53bb-83a4-a9a71c0850b8", + "source_status": "done" + }, + { + "record_id": "KEY-WP-0039-T02", + "uuid": "03da4c38-1dac-53b1-8b0b-e697df48e9d3", + "source_status": "wait" + }, + { + "record_id": "KEY-WP-0039-T03", + "uuid": "54d37ad8-2352-51c2-bb2e-af6070dbbd73", + "source_status": "done" + } + ] + }, + "KEY-WP-0038": { + "source_sha256": "sha256:dc8d88d462799e7e6e44da759a973d52e44102a271969a5daaa9d30ec015b064", + "parent_status": "active", + "tasks": [ + { + "record_id": "KEY-WP-0038-T01", + "uuid": "5f019d13-1fa3-5b88-a27a-75a3339ca96e", + "source_status": "done" + }, + { + "record_id": "KEY-WP-0038-T02", + "uuid": "ddcfb482-8f33-5c81-9ce8-84be8fa5dc30", + "source_status": "wait" + }, + { + "record_id": "KEY-WP-0038-T03", + "uuid": "40ddaa29-7245-508c-ad48-ddc063e25115", + "source_status": "wait" + }, + { + "record_id": "KEY-WP-0038-T04", + "uuid": "583cf1f1-16dc-515f-83bc-1efc45024a89", + "source_status": "cancel" + } + ] + } + }, + "disposition": "Source work complete; implementation remains active with native T02 wait. No new approval requested; existing source acceptance preserved.", + "native_actions": false, + "credentials_accessed": false, + "task_status_changes": false, + "assessment_reference": "../key39-infra-finalization-r1/ASSESSMENT.md" +} diff --git a/docs/changes/netkingdom-target-2026-10-17/key39-finalization-owner-followthrough-r1/KEY39.preimage.md b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-owner-followthrough-r1/KEY39.preimage.md new file mode 100644 index 00000000..f51f2e67 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-owner-followthrough-r1/KEY39.preimage.md @@ -0,0 +1,351 @@ +--- +id: KEY-WP-0039 +type: workplan +title: "Negative-check tokens for the secrets-engine OpenBao JWT role" +domain: infotech +repo: key-cape +status: active +flavor: implementation +owner: claude-code +topic_slug: netkingdom +created: "2026-10-10" +updated: "2026-10-17" +depends_on: [] +related_repos: + - railiance-platform + - secrets-engine +state_hub_workstream_id: "09c3bddf-6058-5603-8488-cccdb7be4f86" +--- + +# KEY-WP-0039 - Negative-check tokens for the secrets-engine OpenBao JWT role + +## Why + +CCR-2026-0038 (railiance-platform RPF-WP-0035-T02) requires the OpenBao role +`secrets-engine-openbao` on `auth/keycape-services` to refuse JWTs with a wrong +`aud`, `sub`, `tenant`, `principal_type`, `roles` or `scope`, a coding-agent JWT +and an expired JWT. The positive login proof passed on 2026-10-10 (secrets-engine +`docs/evidence/2026-10-10-keycape-openbao-probe.json`). railiance-platform asked +whether KeyCape can issue the negative tokens without touching the +`secrets-engine` registration. + +KeyCape signs only what a registration declares. It has no way to mint a token +with arbitrary claims, and adding one would create a forging path in the issuer. +So the negative tokens must come from real, separate registrations that differ +from `secrets-engine` in exactly one property each. + +## Boundaries + +- The `secrets-engine` registration is not changed. +- Probe clients are confidential `client_credentials` clients with the minimum + 60-second lifetime, scopes that grant nothing, and secrets minted into custody + like any other client. They are removed after the proof. +- The probe script prints only accept or refuse booleans per case, never tokens or + secrets. It runs attended. + +## T01 — Define the probe registrations + +```task +id: KEY-WP-0039-T01 +status: done +priority: medium +state_hub_task_id: "4d06b059-3768-53bb-83a4-a9a71c0850b8" +``` + +Write a non-secret request packet under `docs/` with one probe client per claim +mismatch, each identical to `secrets-engine` except for: + +- `aud`: a different access-token resource audience +- `sub`: a different service subject +- `tenant`: a tenant other than `platform` +- `roles`: a role other than `secrets-engine` +- `scope`: a scope outside the role's bound scope + +Two cases need no probe client. A service token always carries +`principal_type: service` (`src/internal/server/oidc/token.go`), so the wrong +`principal_type` case uses a human access token from an attended login. The +coding-agent case uses the existing `codex-railiance-platform` registration (role +`coding-agent`, `docs/openbao-service-auth-contract.md`) if it is registered live; +T01 confirms that. + +The expired-JWT case reuses any probe client: request a token, wait past its +60-second lifetime, then present it. + +Acceptance: railiance-platform agrees the packet covers every refusal that +CCR-2026-0038 names. + +2026-10-10: railiance-platform agreed that the case list covers every +CCR-2026-0038 claim negative. The remaining CCR checks (capabilities-self deny, no +provider fallback) are theirs and secrets-engine's. Packet written at +`docs/openbao-negative-probe-clients.yaml`. It departs from the agreed list in two +ways, both sent back for re-agreement: + +- **Expired case.** A token from another probe already fails a claim, so its + refusal does not show that expiry was checked. It needs a sixth client, + `openbao-probe-expiry`, identical to `secrets-engine-openbao` in every claim. That + makes it a second credential able to log in as secrets-engine for the session. + OpenBao's `expiration_leeway` (150s by default) also extends the wait. +- **Audience pin.** A service token's `aud` is its `clientId` unless `audience` is + set. Every probe except the aud probe therefore pins + `audience: secrets-engine-openbao`, so it fails only on its own claim. + +Condition (a), no OpenBao custody, holds. The values still have to reach KeyCape's +environment, because KeyCape resolves `secretRef` at startup. The packet uses a +transient Kubernetes Secret that is deleted in the same session. + +## T02 — Register the probe clients, run the attended probe, then remove them + +```task +id: KEY-WP-0039-T02 +status: wait +depends_on: [KEY-WP-0039-T01, KEY-WP-0038-T02, KEY-WP-0038-T03] +blocking_reason: "Ordered identity gate: Forgejo first SecurityCase registration/claims proof, then NetKingdom registry session and attended human/coding-agent custody; registration authorization already resolved." +priority: medium +state_hub_task_id: "03da4c38-1dac-53b1-8b0b-e697df48e9d3" +``` + +2026-10-11: requalified — live registration go caafe4be was already resolved; owner authorization present. See the-custodian docs/decisions/2026-10-11-security-unblock-decisions.md. + +Write the attended script, which prints booleans only. + +2026-10-10: script written at `scripts/openbao_negative_probe.py`, with tests in +`scripts/test_openbao_negative_probe.py`. It meets condition (b): one refused +boolean per case plus the refusal class (`claim_mismatch`, `expired`, +`not_service_principal`), and no token, secret, claim value or OpenBao error text. +A transport failure or an unexpected response is reported as `inconclusive`, never +as a refusal. A token that OpenBao accepts is revoked at once and fails the run. +Still waiting: railiance-platform's re-agreement on the two departures above, and +an attended registry session with net-kingdom. Register the clients +through the normal registration path, hand the run to railiance-platform, record +a value-free receipt in `docs/evidence/`, and deregister the clients afterwards. + +Acceptance: every case is refused, the receipt is committed, and the probe clients +are gone. + + +2026-10-11 source preparation: T01 remains progress because the exact expiry +client and audience-pin amendments require platform-owner re-agreement; the +prior agreement covered the earlier case list, not this revised packet. The +prepared runner revision adds a valid expiry positive control and immediate +revoke, exact decoded claim-isolation preflight (not signature evidence), +semantic refusal classes, bounded expiry TTL and redirect refusal. Seventeen +local tests pass; original-source semantic regressions demonstrate two false +successes. See `docs/openbao-negative-probe-session-proposal.md` for exact owner +returns, live authority, effective leeway, custody boundaries and cleanup. No +live token, registry or OpenBao action occurred. T02 continues to wait for owner +re-agreement plus separate attended live authorization. MASON-WP-0009-T05 +operator/ESO capability and nonmember checks remain independent acceptance. + +2026-10-11: Platform independently re-agreed the final amended design (session proposal SHA25623f13d2a5a4204f749f35c5d44ef39d213838cd2fce1c36fb4775254ef1c7f09). Original conditional review and narrow confirmation are retained in railiance-platform/docs/reviews/2026-10-11-keycape-openbao-negative-session-owner-review*. Source acceptance T01 is complete; actual registry/leeway/readiness, founder live go, negative/cleanup and separate capability/no-fallback obligations remain T02 and owner plans. + + +## KeyCape issuer-source compatibility inspection for lifecycle v2 + +```task +id: KEY-WP-0039-T03 +status: done +priority: medium +state_hub_task_id: "54d37ad8-2352-51c2-bb2e-af6070dbbd73" +``` + +2026-10-11: Completed KeyCape's own issuer-source inspection for +KEY-WP-0039-T02/attended-lifecycle-v2, exact manifest SHA256 +6fc0fe47f99a97c11c6e7538f5ea02054335df7d46055d3f84bed4e194aeaaed. +Read AGENTS.md, governing INTENT.md, current client-credentials claim source, +registration fragments, native caller-authentication commands and frozen v2 +issuer transport/probe wiring. Seventeen unchanged runner tests pass locally. +This task completes only issuer-source inspection; the resulting narrow +source acknowledgment is in docs/reviews/2026-10-11-key39-lifecycle-v2-keycape-source-ack.json (retained frozen prior acknowledgment). +T01 stays done and T02 stays open for actual native registration, token input, +refusal and cleanup proof. No Secret, token, credential or native environment was accessed; no owner +checkout was changed during this source inspection. Owner source files were +read for the bounded issuer compatibility check. + +Current token.go:306-309 returns HTTP 401 invalid_profile_usage with +feature=client_id for a removed confidential client; wrong-secret +feature=Authorization cannot count as removed-registration evidence. The +proposal and packet comments are narrowly corrected against that current +issuer contract. Prior T01/source re-agreement history is preserved; this source +acknowledgment supplies no NetKingdom runtime-owner review or founder live go. + +The private token-producer handoff remains a real execution gap under T02. +Existing keycape login/service-token commands require a new private output +file; login also needs a concrete admitted public PKCE client/callback and +attended browser/MFA. The lifecycle expects an in-memory private stdin object +containing human_token and coding_agent_token, but no reviewed file-to-process +or process-to-process producer, admitted human client/callback, custody-injected +coding-agent source, native placement or producer cleanup receipt has been +provided. No unattended token producer or admitted secret route is invented. +Resolve those exact owner contracts before founder native approval; existing +static registration is not live token custody or producer admission. + + +2026-10-11 same T03 source-inspection refinement for lifecycle V4: completed +KeyCape issuer-facing inspection before the new formal acknowledgment in +docs/reviews/2026-10-11-key39-lifecycle-v4-keycape-source-ack.json. Exact V4 +manifest SHA256 d079f85c47d5c2635e1ee633ea4d1346b1737ce2be9ca273f22a38e620f418d8, +executor b4687c2d07819c43b45d0cddcad0488b62eb49175b2a06a43f7806a0838d3a4d. +NetKingdom's independent V4 source review is approved, receipt SHA256 +4f4498203a31bd46dbc3715c0df1cbb9eee63bed5b2c1baf430ff3c4a37c13cb. +The V2/V3 unsuccessful reviews and earlier KeyCape acknowledgment remain +retained as one correction trial; they are not extra scored successes. + +V4 retains the unchanged issuer runner and six-client claim design, and fixes +finite/nonboolean windows, true total request deadlines, late-known OpenBao +session private revoke/refusal and guarded bounded final Secret deletion. +The issuer-facing acknowledgement covers only stopping late token exchanges, +claim source/shape and preserving token cleanup failure/unknown outcomes; it +does not replace NetKingdom runtime or Platform backend acceptance. Tests: 41 +supplied lifecycle, 17 unchanged runner and 17 independent owner checks pass +locally. No native tokens, credentials, issuer mutations or live cleanup were +performed. T01 stays done; T02 stays open and the genuine private token-producer +handoff remains its next source prerequisite before actual founder native go. + + +2026-10-11 same T03 source-inspection refinement for lifecycle V5: completed +KeyCape's own current issuer-source/claim/cleanup-contract inspection before +any new formal acknowledgment. Current token.go:306-309 still distinguishes +removed-client invalid_profile_usage feature=client_id from wrong-secret +feature=Authorization. The six-client design, audience source and expiry +positive control remain unchanged, and the original runner's 17 tests pass. +Exact V5 manifest SHA256 +d1f997624db46693a7126a6eb6435f6b3a353d5bd67dcc8a539683e4b7147285; +executor 3bcceebc845b17492040c76223d77088257c547a2b890198e7b5c2b04a0cd360. +V5 has 46 passing supplied lifecycle checks. It adds a single exceptional +response-close abort under a fixed20ms cap clipped to nonresettable aggregate +cleanup, and to positive original request time remaining, with expired aggregate +zero calls, fixed private errors and truthful known-session revoke/unknown +outcomes. This inspection covers issuer-facing token lifetime and claim +interfaces only; NetKingdom and Platform independently own their runtime and +backend review decisions. Previous V2/V3 Net source refusals and Platform V4 +exceptional-close refusal remain retained; KeyCape's V2/V4 acknowledgments are +historical narrow source inspections, never aggregate native approval. +T01 remains done, T02 open. No native actions or credentials occurred. The real +private token-producer/client/callback/coding-agent custody/cleanup handoff gap +stays the next prerequisite; no unattended admission is inferred. + +Exact independent V5 source decisions were then verified: NetKingdom approved +receipt SHA256 32a62bd1ca1bd7311965d016d4d6a6e4d9177f3423c63add436d9d309865eacc +(46 supplied +17 runner +21 independent checks); Platform approved receipt +SHA256 ba872dc53061b1dcca9dea1e8897597c394c1f9365e0fc49ee6f2095f57f11e0 +(46 supplied +5 independent +17 previously verified runner checks). Those are +foreign owners' own source decisions, not KeyCape runtime/backend reviews or +founder native authority. Formal KeyCape bounded issuer acknowledgment follows +in docs/reviews/2026-10-11-key39-lifecycle-v5-keycape-source-ack.json. + + +2026-10-11 T02 continuing source preparation: private producer decision packet +`KEY-WP-0039-T02/private-producer-handoff-v1` stages supported existing CLI +flows, selected existing netkingdom-bootstrap-console literal-loopback human +client subject to owner purpose/MFA/listener admission, and the existing +codex-railiance-platform source identity subject to actual live/custody/consumer +admission. No existing coding-agent delivery receipt was found; AppRole tokens +do not substitute for issuer JWTs. A bounded pipe-only adapter for the current +CLI's two caller-owned private files has 18 passing synthetic checks, with +identity-guarded explicit file consumption and fixed value-free failure output. +Decoded claim shape does not prove signatures or authenticated admission. +See docs/openbao-negative-token-producer-decision.md and .json for exact owner +choices and missing receipts. The exact trusted workstation-to-node launcher, +private-file allowance and railiance01 Bao route/public-health guard still need +source review and founder disposition before any token/temporary credential/ +mutation/restart. No registration, custody, login, native action or credential +request occurred. V5 and failed review histories retained; T01 stays done and +T02 open. No new work record or native acceptance is inferred. + + +2026-10-11 same T02 source-preparation correction for +KEY-WP-0039-T02/private-producer-handoff-v2: NetKingdom's exact V1 consumer +refusal remains recorded (no child-ready gate, stdio alias, non-atomic unlink). +V2 has source-pinned private launch/ready handshake compatible with exact +Net V9 manifest84542ddc: fresh nonce/source/envelope/health/window marker before +ANY caller-token file load/consume/write; separate local data/control pipes +reject kernel identity aliases of stdin/stdout/stderr. Adapter read/write budget +only clips downward after readiness. 31 distinct producer/launcher synthetic +checks and 74 compatible consumer supplied checks pass. No SSH/native/login or +credential access was performed. Stat/unlink remains non-atomic and requires +continuously exclusive sameUID custody; the retained race test is a custody +violation counterexample, not a claim of repaired filesystem atomicity. Receipt +keeps atomic delete/copy cleanup unverified and possible copies true. Exact +trusted launcher/directSSH profile/private tmpfs allowance, selected human +client/MFA purpose reuse and coding-agent registration/custody/delivery remain +unadmitted owner choices. Native node route, role/leeway/window/founder decision +and actual negative/cleanup acceptance remain pending. T01done and T02open; no +new task or plan, and V1/V5-V8/current failure histories are not overwritten. + + +2026-10-11 same T02 producer-preparation correction V3: Platform V2 pair +source refusal RPF-KEY39-PAIR-V2-F01 is retained. Private write uncertainty +now propagates into sticky remote consumer uncertainty before launcher early +return; local SSH termination never proves remote no-effects/recovery. Actual +full-payload then interrupted confirmation and4096-byte partialpipe failures +are retained regressions. 33 distinct source checks pass. Exact Net V9 source +bytes unchanged; new source reviews required, not implicit repin. Native +client/MFA/coding-agent custody/transport/tmpfsfile and actualroute/session +authority remain unadmitted. T01done/T02open; no new task/plan or native action. + + +2026-10-11 same T02 producer-preparation V4: exact V3 Net consumer refusal +NET-PRODUCER-V3-F01 retained. Actual CLI contract acquisition now bounded +nofollow/nonblocking regular stable reads and one ORIGINAL5-second preparation +budget across contract/source/envelope/SSHspawn/ready/file/delivery phases, not +a renewed launcher allowance. 40 own source tests plus9 retained paired +wrapper/child handshake regressions pass with synthetic/no native transport. +V3stickyremoteunknown/full+partialdelivery regressions retained; Net V9 exact +bytes unchanged. Prior issuer/custody/transport/file/route/readiness-Argo/role/ +maintenanceexception/founder choices remain unadmitted. No SSH/network/Secrets/ +credentials/native/owner checkout changes; no task/plan duplication. T01done +and T02open; source correction is not native acceptance. + + +2026-10-11 T02 source-design disposition checkpoint: founder accepted exact +source memo `infd-20261011-key39-source-design-r1`, review +`review-62626f54-0212-4684-9b45-0a7222d61957` version1 at `2026-10-11T17:19:01Z`. +Disposition `disp-870f9c1b-0444-4216-ae7f-f5463d590e36`, approval +`ca118afc-d746-4454-a5b9-9b387b120cd9`; authenticated person subject +`uid=platform-root,ou=people,dc=netkingdom,dc=local`. Exact canonical public review URL: +`https://decisions.coulomb.social/review?memo_id=review-62626f54-0212-4684-9b45-0a7222d61957`. +Root's authenticated confirmed readback is +`the-custodian/docs/evidence/2026-10-10-security-consolidation/key39-source-design-publication/founder-source-disposition-readback-r1.json`, +SHA256 `453213194d605336e9c424976f1ac27691def39e225026f0a99628d1cfc97d1d`; observed +`2026-10-11T17:30:47.080081+00:00`. Source commands SHA256 +`36e20813596f3ddbb86e01fd0b838867ece2f4916c5880305cf12cd61b567f89` and source inventory SHA256 +`aea196db9e513900d5e7165455d6e9c3dc68749a606dc219ec340ac595642cc0` bind the accepted source direction. + +Acceptance covers recommended source preparation for existing bootstrap-console +human-client purpose reuse, returning existing coding-agent custody admission or +preparing a narrow owner proposal, proposed founder directSSH source profile, +and conditional exclusive-owner tmpfs files. It does not establish actual +registration/MFA/listener, coding-agent custody/delivery, transport/file admission, +node-local Bao route, issuer/OpenBao role/leeway/maintenance readiness or fresh +attended native go. The published choice documents remain historical exact +pending proposals; this appended receipt records their later source disposition. +T02 remains wait on external owner custody/admission and separate attended +execution authorization. Source acceptance permits further source preparation; +actual registry, token exchange, negative probe, revoke and cleanup remain pending. T01 and T03 +remain done. No native or credential action is authorized by this source receipt. +See docs/proposals/2026-10-11-key39-next-execution-prerequisites.json for exact +owner returns and safe metadata evidence needed before preparing a runnable +attended execution proposal. + +2026-10-11 ordered-goal qualification (T02 only): the final source runner already +exists and its 17 local semantic tests pass. No new helper or source protocol is +needed to repeat accepted preparation. Native execution follows the first +Forgejo SecurityCase registration and claims proof (KEY-WP-0038-T02/T03); +only those task metadata were checked, not wait-task bodies. NetKingdom owns the +registry/env/restart session, KeyCape owns issuance/isolation, and Platform owns +leeway/refusal/capability/no-fallback/cleanup proof. The live registration go is +already recorded resolved; do not ask again. Attended human authentication and +owner-held existing coding-agent custody go on the single attended checklist. +No credentials, live registry/API changes or KEY-WP-0040 recovery work occurred. + +2026-10-17 Platform owner returns (source-prepared, not admitted): +railiance-platform `docs/proposals/2026-10-17-key39-t02-owner-returns.md` +(commit `0a9617a`). The coding client `codex-railiance-platform` has no +admission, custody or delivery record. The existing narrow proposal +`docs/proposals/2026-10-11-key39-coding-client-custody-proposal.json` (`9460f6f`) +remains the one to admit, so the coding-agent case stays unrunnable until owners +admit it. The role source (`openbao/auth/secrets-engine-jwt-role.json`) matches +the packet's claim design. It declares no `expiration_leeway`, so the expiry +probe must assume OpenBao's 150s default until an attended sanitized read +confirms the live value. The node-local Bao origin is not declared in source and +also needs that attended read. T02 stays wait. diff --git a/docs/changes/netkingdom-target-2026-10-17/key39-finalization-owner-followthrough-r1/KEY39.proposed.md b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-owner-followthrough-r1/KEY39.proposed.md new file mode 100644 index 00000000..fd0fc46e --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-owner-followthrough-r1/KEY39.proposed.md @@ -0,0 +1,364 @@ +--- +id: KEY-WP-0039 +type: workplan +title: "Negative-check tokens for the secrets-engine OpenBao JWT role" +domain: infotech +repo: key-cape +status: active +flavor: implementation +owner: claude-code +topic_slug: netkingdom +created: "2026-10-10" +updated: "2026-10-17" +depends_on: [] +related_repos: + - railiance-platform + - secrets-engine +state_hub_workstream_id: "09c3bddf-6058-5603-8488-cccdb7be4f86" +--- + +# KEY-WP-0039 - Negative-check tokens for the secrets-engine OpenBao JWT role + +## Why + +CCR-2026-0038 (railiance-platform RPF-WP-0035-T02) requires the OpenBao role +`secrets-engine-openbao` on `auth/keycape-services` to refuse JWTs with a wrong +`aud`, `sub`, `tenant`, `principal_type`, `roles` or `scope`, a coding-agent JWT +and an expired JWT. The positive login proof passed on 2026-10-10 (secrets-engine +`docs/evidence/2026-10-10-keycape-openbao-probe.json`). railiance-platform asked +whether KeyCape can issue the negative tokens without touching the +`secrets-engine` registration. + +KeyCape signs only what a registration declares. It has no way to mint a token +with arbitrary claims, and adding one would create a forging path in the issuer. +So the negative tokens must come from real, separate registrations that differ +from `secrets-engine` in exactly one property each. + +## Boundaries + +- The `secrets-engine` registration is not changed. +- Probe clients are confidential `client_credentials` clients with the minimum + 60-second lifetime, scopes that grant nothing, and secrets minted into custody + like any other client. They are removed after the proof. +- The probe script prints only accept or refuse booleans per case, never tokens or + secrets. It runs attended. + +## T01 — Define the probe registrations + +```task +id: KEY-WP-0039-T01 +status: done +priority: medium +state_hub_task_id: "4d06b059-3768-53bb-83a4-a9a71c0850b8" +``` + +Write a non-secret request packet under `docs/` with one probe client per claim +mismatch, each identical to `secrets-engine` except for: + +- `aud`: a different access-token resource audience +- `sub`: a different service subject +- `tenant`: a tenant other than `platform` +- `roles`: a role other than `secrets-engine` +- `scope`: a scope outside the role's bound scope + +Two cases need no probe client. A service token always carries +`principal_type: service` (`src/internal/server/oidc/token.go`), so the wrong +`principal_type` case uses a human access token from an attended login. The +coding-agent case uses the existing `codex-railiance-platform` registration (role +`coding-agent`, `docs/openbao-service-auth-contract.md`) if it is registered live; +T01 confirms that. + +The expired-JWT case reuses any probe client: request a token, wait past its +60-second lifetime, then present it. + +Acceptance: railiance-platform agrees the packet covers every refusal that +CCR-2026-0038 names. + +2026-10-10: railiance-platform agreed that the case list covers every +CCR-2026-0038 claim negative. The remaining CCR checks (capabilities-self deny, no +provider fallback) are theirs and secrets-engine's. Packet written at +`docs/openbao-negative-probe-clients.yaml`. It departs from the agreed list in two +ways, both sent back for re-agreement: + +- **Expired case.** A token from another probe already fails a claim, so its + refusal does not show that expiry was checked. It needs a sixth client, + `openbao-probe-expiry`, identical to `secrets-engine-openbao` in every claim. That + makes it a second credential able to log in as secrets-engine for the session. + OpenBao's `expiration_leeway` (150s by default) also extends the wait. +- **Audience pin.** A service token's `aud` is its `clientId` unless `audience` is + set. Every probe except the aud probe therefore pins + `audience: secrets-engine-openbao`, so it fails only on its own claim. + +Condition (a), no OpenBao custody, holds. The values still have to reach KeyCape's +environment, because KeyCape resolves `secretRef` at startup. The packet uses a +transient Kubernetes Secret that is deleted in the same session. + +## T02 — Register the probe clients, run the attended probe, then remove them + +```task +id: KEY-WP-0039-T02 +status: wait +depends_on: [KEY-WP-0039-T01, KEY-WP-0038-T02, KEY-WP-0038-T03] +blocking_reason: "Ordered identity gate: Forgejo first SecurityCase registration/claims proof, then NetKingdom registry session and attended human/coding-agent custody; registration authorization already resolved." +priority: medium +state_hub_task_id: "03da4c38-1dac-53b1-8b0b-e697df48e9d3" +``` + +2026-10-11: requalified — live registration go caafe4be was already resolved; owner authorization present. See the-custodian docs/decisions/2026-10-11-security-unblock-decisions.md. + +Write the attended script, which prints booleans only. + +2026-10-10: script written at `scripts/openbao_negative_probe.py`, with tests in +`scripts/test_openbao_negative_probe.py`. It meets condition (b): one refused +boolean per case plus the refusal class (`claim_mismatch`, `expired`, +`not_service_principal`), and no token, secret, claim value or OpenBao error text. +A transport failure or an unexpected response is reported as `inconclusive`, never +as a refusal. A token that OpenBao accepts is revoked at once and fails the run. +Still waiting: railiance-platform's re-agreement on the two departures above, and +an attended registry session with net-kingdom. Register the clients +through the normal registration path, hand the run to railiance-platform, record +a value-free receipt in `docs/evidence/`, and deregister the clients afterwards. + +Acceptance: every case is refused, the receipt is committed, and the probe clients +are gone. + + +2026-10-11 source preparation: T01 remains progress because the exact expiry +client and audience-pin amendments require platform-owner re-agreement; the +prior agreement covered the earlier case list, not this revised packet. The +prepared runner revision adds a valid expiry positive control and immediate +revoke, exact decoded claim-isolation preflight (not signature evidence), +semantic refusal classes, bounded expiry TTL and redirect refusal. Seventeen +local tests pass; original-source semantic regressions demonstrate two false +successes. See `docs/openbao-negative-probe-session-proposal.md` for exact owner +returns, live authority, effective leeway, custody boundaries and cleanup. No +live token, registry or OpenBao action occurred. T02 continues to wait for owner +re-agreement plus separate attended live authorization. MASON-WP-0009-T05 +operator/ESO capability and nonmember checks remain independent acceptance. + +2026-10-11: Platform independently re-agreed the final amended design (session proposal SHA25623f13d2a5a4204f749f35c5d44ef39d213838cd2fce1c36fb4775254ef1c7f09). Original conditional review and narrow confirmation are retained in railiance-platform/docs/reviews/2026-10-11-keycape-openbao-negative-session-owner-review*. Source acceptance T01 is complete; actual registry/leeway/readiness, founder live go, negative/cleanup and separate capability/no-fallback obligations remain T02 and owner plans. + + +2026-10-17 Custodian coordinator finalization review (not a KeyCape formal owner +acknowledgment): the [infrastructure assessment](../../the-custodian/docs/changes/netkingdom-target-2026-10-17/key39-infra-finalization-r1/ASSESSMENT.md) +confirms all 14 executable/test/producer-decision pins and three Platform source +pins still match. Source reviews remain complete: T01/T03 done, T02 wait. Existing +source acceptance, resolved registration go caafe4be and the standing attended +rule remain valid; no new protocol or blanket approval request is needed. The +remaining sequence is KEY-WP-0038-T02/T03 first-case registration/claims proof, +admission of the existing coding-client proposal with sanitized owner readiness +returns, then one attended negative/revoke/cleanup receipt committed with probe +clients removed. No native acceptance or credential/registration action is +inferred from this coordinator review. + + +## KeyCape issuer-source compatibility inspection for lifecycle v2 + +```task +id: KEY-WP-0039-T03 +status: done +priority: medium +state_hub_task_id: "54d37ad8-2352-51c2-bb2e-af6070dbbd73" +``` + +2026-10-11: Completed KeyCape's own issuer-source inspection for +KEY-WP-0039-T02/attended-lifecycle-v2, exact manifest SHA256 +6fc0fe47f99a97c11c6e7538f5ea02054335df7d46055d3f84bed4e194aeaaed. +Read AGENTS.md, governing INTENT.md, current client-credentials claim source, +registration fragments, native caller-authentication commands and frozen v2 +issuer transport/probe wiring. Seventeen unchanged runner tests pass locally. +This task completes only issuer-source inspection; the resulting narrow +source acknowledgment is in docs/reviews/2026-10-11-key39-lifecycle-v2-keycape-source-ack.json (retained frozen prior acknowledgment). +T01 stays done and T02 stays open for actual native registration, token input, +refusal and cleanup proof. No Secret, token, credential or native environment was accessed; no owner +checkout was changed during this source inspection. Owner source files were +read for the bounded issuer compatibility check. + +Current token.go:306-309 returns HTTP 401 invalid_profile_usage with +feature=client_id for a removed confidential client; wrong-secret +feature=Authorization cannot count as removed-registration evidence. The +proposal and packet comments are narrowly corrected against that current +issuer contract. Prior T01/source re-agreement history is preserved; this source +acknowledgment supplies no NetKingdom runtime-owner review or founder live go. + +The private token-producer handoff remains a real execution gap under T02. +Existing keycape login/service-token commands require a new private output +file; login also needs a concrete admitted public PKCE client/callback and +attended browser/MFA. The lifecycle expects an in-memory private stdin object +containing human_token and coding_agent_token, but no reviewed file-to-process +or process-to-process producer, admitted human client/callback, custody-injected +coding-agent source, native placement or producer cleanup receipt has been +provided. No unattended token producer or admitted secret route is invented. +Resolve those exact owner contracts before founder native approval; existing +static registration is not live token custody or producer admission. + + +2026-10-11 same T03 source-inspection refinement for lifecycle V4: completed +KeyCape issuer-facing inspection before the new formal acknowledgment in +docs/reviews/2026-10-11-key39-lifecycle-v4-keycape-source-ack.json. Exact V4 +manifest SHA256 d079f85c47d5c2635e1ee633ea4d1346b1737ce2be9ca273f22a38e620f418d8, +executor b4687c2d07819c43b45d0cddcad0488b62eb49175b2a06a43f7806a0838d3a4d. +NetKingdom's independent V4 source review is approved, receipt SHA256 +4f4498203a31bd46dbc3715c0df1cbb9eee63bed5b2c1baf430ff3c4a37c13cb. +The V2/V3 unsuccessful reviews and earlier KeyCape acknowledgment remain +retained as one correction trial; they are not extra scored successes. + +V4 retains the unchanged issuer runner and six-client claim design, and fixes +finite/nonboolean windows, true total request deadlines, late-known OpenBao +session private revoke/refusal and guarded bounded final Secret deletion. +The issuer-facing acknowledgement covers only stopping late token exchanges, +claim source/shape and preserving token cleanup failure/unknown outcomes; it +does not replace NetKingdom runtime or Platform backend acceptance. Tests: 41 +supplied lifecycle, 17 unchanged runner and 17 independent owner checks pass +locally. No native tokens, credentials, issuer mutations or live cleanup were +performed. T01 stays done; T02 stays open and the genuine private token-producer +handoff remains its next source prerequisite before actual founder native go. + + +2026-10-11 same T03 source-inspection refinement for lifecycle V5: completed +KeyCape's own current issuer-source/claim/cleanup-contract inspection before +any new formal acknowledgment. Current token.go:306-309 still distinguishes +removed-client invalid_profile_usage feature=client_id from wrong-secret +feature=Authorization. The six-client design, audience source and expiry +positive control remain unchanged, and the original runner's 17 tests pass. +Exact V5 manifest SHA256 +d1f997624db46693a7126a6eb6435f6b3a353d5bd67dcc8a539683e4b7147285; +executor 3bcceebc845b17492040c76223d77088257c547a2b890198e7b5c2b04a0cd360. +V5 has 46 passing supplied lifecycle checks. It adds a single exceptional +response-close abort under a fixed20ms cap clipped to nonresettable aggregate +cleanup, and to positive original request time remaining, with expired aggregate +zero calls, fixed private errors and truthful known-session revoke/unknown +outcomes. This inspection covers issuer-facing token lifetime and claim +interfaces only; NetKingdom and Platform independently own their runtime and +backend review decisions. Previous V2/V3 Net source refusals and Platform V4 +exceptional-close refusal remain retained; KeyCape's V2/V4 acknowledgments are +historical narrow source inspections, never aggregate native approval. +T01 remains done, T02 open. No native actions or credentials occurred. The real +private token-producer/client/callback/coding-agent custody/cleanup handoff gap +stays the next prerequisite; no unattended admission is inferred. + +Exact independent V5 source decisions were then verified: NetKingdom approved +receipt SHA256 32a62bd1ca1bd7311965d016d4d6a6e4d9177f3423c63add436d9d309865eacc +(46 supplied +17 runner +21 independent checks); Platform approved receipt +SHA256 ba872dc53061b1dcca9dea1e8897597c394c1f9365e0fc49ee6f2095f57f11e0 +(46 supplied +5 independent +17 previously verified runner checks). Those are +foreign owners' own source decisions, not KeyCape runtime/backend reviews or +founder native authority. Formal KeyCape bounded issuer acknowledgment follows +in docs/reviews/2026-10-11-key39-lifecycle-v5-keycape-source-ack.json. + + +2026-10-11 T02 continuing source preparation: private producer decision packet +`KEY-WP-0039-T02/private-producer-handoff-v1` stages supported existing CLI +flows, selected existing netkingdom-bootstrap-console literal-loopback human +client subject to owner purpose/MFA/listener admission, and the existing +codex-railiance-platform source identity subject to actual live/custody/consumer +admission. No existing coding-agent delivery receipt was found; AppRole tokens +do not substitute for issuer JWTs. A bounded pipe-only adapter for the current +CLI's two caller-owned private files has 18 passing synthetic checks, with +identity-guarded explicit file consumption and fixed value-free failure output. +Decoded claim shape does not prove signatures or authenticated admission. +See docs/openbao-negative-token-producer-decision.md and .json for exact owner +choices and missing receipts. The exact trusted workstation-to-node launcher, +private-file allowance and railiance01 Bao route/public-health guard still need +source review and founder disposition before any token/temporary credential/ +mutation/restart. No registration, custody, login, native action or credential +request occurred. V5 and failed review histories retained; T01 stays done and +T02 open. No new work record or native acceptance is inferred. + + +2026-10-11 same T02 source-preparation correction for +KEY-WP-0039-T02/private-producer-handoff-v2: NetKingdom's exact V1 consumer +refusal remains recorded (no child-ready gate, stdio alias, non-atomic unlink). +V2 has source-pinned private launch/ready handshake compatible with exact +Net V9 manifest84542ddc: fresh nonce/source/envelope/health/window marker before +ANY caller-token file load/consume/write; separate local data/control pipes +reject kernel identity aliases of stdin/stdout/stderr. Adapter read/write budget +only clips downward after readiness. 31 distinct producer/launcher synthetic +checks and 74 compatible consumer supplied checks pass. No SSH/native/login or +credential access was performed. Stat/unlink remains non-atomic and requires +continuously exclusive sameUID custody; the retained race test is a custody +violation counterexample, not a claim of repaired filesystem atomicity. Receipt +keeps atomic delete/copy cleanup unverified and possible copies true. Exact +trusted launcher/directSSH profile/private tmpfs allowance, selected human +client/MFA purpose reuse and coding-agent registration/custody/delivery remain +unadmitted owner choices. Native node route, role/leeway/window/founder decision +and actual negative/cleanup acceptance remain pending. T01done and T02open; no +new task or plan, and V1/V5-V8/current failure histories are not overwritten. + + +2026-10-11 same T02 producer-preparation correction V3: Platform V2 pair +source refusal RPF-KEY39-PAIR-V2-F01 is retained. Private write uncertainty +now propagates into sticky remote consumer uncertainty before launcher early +return; local SSH termination never proves remote no-effects/recovery. Actual +full-payload then interrupted confirmation and4096-byte partialpipe failures +are retained regressions. 33 distinct source checks pass. Exact Net V9 source +bytes unchanged; new source reviews required, not implicit repin. Native +client/MFA/coding-agent custody/transport/tmpfsfile and actualroute/session +authority remain unadmitted. T01done/T02open; no new task/plan or native action. + + +2026-10-11 same T02 producer-preparation V4: exact V3 Net consumer refusal +NET-PRODUCER-V3-F01 retained. Actual CLI contract acquisition now bounded +nofollow/nonblocking regular stable reads and one ORIGINAL5-second preparation +budget across contract/source/envelope/SSHspawn/ready/file/delivery phases, not +a renewed launcher allowance. 40 own source tests plus9 retained paired +wrapper/child handshake regressions pass with synthetic/no native transport. +V3stickyremoteunknown/full+partialdelivery regressions retained; Net V9 exact +bytes unchanged. Prior issuer/custody/transport/file/route/readiness-Argo/role/ +maintenanceexception/founder choices remain unadmitted. No SSH/network/Secrets/ +credentials/native/owner checkout changes; no task/plan duplication. T01done +and T02open; source correction is not native acceptance. + + +2026-10-11 T02 source-design disposition checkpoint: founder accepted exact +source memo `infd-20261011-key39-source-design-r1`, review +`review-62626f54-0212-4684-9b45-0a7222d61957` version1 at `2026-10-11T17:19:01Z`. +Disposition `disp-870f9c1b-0444-4216-ae7f-f5463d590e36`, approval +`ca118afc-d746-4454-a5b9-9b387b120cd9`; authenticated person subject +`uid=platform-root,ou=people,dc=netkingdom,dc=local`. Exact canonical public review URL: +`https://decisions.coulomb.social/review?memo_id=review-62626f54-0212-4684-9b45-0a7222d61957`. +Root's authenticated confirmed readback is +`the-custodian/docs/evidence/2026-10-10-security-consolidation/key39-source-design-publication/founder-source-disposition-readback-r1.json`, +SHA256 `453213194d605336e9c424976f1ac27691def39e225026f0a99628d1cfc97d1d`; observed +`2026-10-11T17:30:47.080081+00:00`. Source commands SHA256 +`36e20813596f3ddbb86e01fd0b838867ece2f4916c5880305cf12cd61b567f89` and source inventory SHA256 +`aea196db9e513900d5e7165455d6e9c3dc68749a606dc219ec340ac595642cc0` bind the accepted source direction. + +Acceptance covers recommended source preparation for existing bootstrap-console +human-client purpose reuse, returning existing coding-agent custody admission or +preparing a narrow owner proposal, proposed founder directSSH source profile, +and conditional exclusive-owner tmpfs files. It does not establish actual +registration/MFA/listener, coding-agent custody/delivery, transport/file admission, +node-local Bao route, issuer/OpenBao role/leeway/maintenance readiness or fresh +attended native go. The published choice documents remain historical exact +pending proposals; this appended receipt records their later source disposition. +T02 remains wait on external owner custody/admission and separate attended +execution authorization. Source acceptance permits further source preparation; +actual registry, token exchange, negative probe, revoke and cleanup remain pending. T01 and T03 +remain done. No native or credential action is authorized by this source receipt. +See docs/proposals/2026-10-11-key39-next-execution-prerequisites.json for exact +owner returns and safe metadata evidence needed before preparing a runnable +attended execution proposal. + +2026-10-11 ordered-goal qualification (T02 only): the final source runner already +exists and its 17 local semantic tests pass. No new helper or source protocol is +needed to repeat accepted preparation. Native execution follows the first +Forgejo SecurityCase registration and claims proof (KEY-WP-0038-T02/T03); +only those task metadata were checked, not wait-task bodies. NetKingdom owns the +registry/env/restart session, KeyCape owns issuance/isolation, and Platform owns +leeway/refusal/capability/no-fallback/cleanup proof. The live registration go is +already recorded resolved; do not ask again. Attended human authentication and +owner-held existing coding-agent custody go on the single attended checklist. +No credentials, live registry/API changes or KEY-WP-0040 recovery work occurred. + +2026-10-17 Platform owner returns (source-prepared, not admitted): +railiance-platform `docs/proposals/2026-10-17-key39-t02-owner-returns.md` +(commit `0a9617a`). The coding client `codex-railiance-platform` has no +admission, custody or delivery record. The existing narrow proposal +`docs/proposals/2026-10-11-key39-coding-client-custody-proposal.json` (`9460f6f`) +remains the one to admit, so the coding-agent case stays unrunnable until owners +admit it. The role source (`openbao/auth/secrets-engine-jwt-role.json`) matches +the packet's claim design. It declares no `expiration_leeway`, so the expiry +probe must assume OpenBao's 150s default until an attended sanitized read +confirms the live value. The node-local Bao origin is not declared in source and +also needs that attended read. T02 stays wait. diff --git a/docs/changes/netkingdom-target-2026-10-17/key39-finalization-owner-followthrough-r1/apply.py b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-owner-followthrough-r1/apply.py new file mode 100644 index 00000000..839c0f18 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-owner-followthrough-r1/apply.py @@ -0,0 +1,36 @@ +from pathlib import Path +import hashlib,json,re,subprocess,yaml +root=Path('/home/worsch/key-cape');out=Path('/home/worsch/the-custodian/docs/changes/netkingdom-target-2026-10-17/key39-finalization-owner-followthrough-r1');out.mkdir() +rel='workplans/KEY-WP-0039-openbao-negative-probe-tokens.md';path=root/rel + +def git(*args):return subprocess.check_output(['git','-C',str(root),*args]).decode() +assert git('rev-parse','HEAD').strip()=='e76d4130f31a02554048f30bf838043cea7a5b80' +assert not git('status','--porcelain=v1') +assert not git('diff','--cached','--name-only') +before=path.read_text();anchor='## KeyCape issuer-source compatibility inspection for lifecycle v2' +assert before.count(anchor)==1 +paragraph='''2026-10-17 Custodian coordinator finalization review (not a KeyCape formal owner +acknowledgment): the [infrastructure assessment](../../the-custodian/docs/changes/netkingdom-target-2026-10-17/key39-infra-finalization-r1/ASSESSMENT.md) +confirms all 14 executable/test/producer-decision pins and three Platform source +pins still match. Source reviews remain complete: T01/T03 done, T02 wait. Existing +source acceptance, resolved registration go caafe4be and the standing attended +rule remain valid; no new protocol or blanket approval request is needed. The +remaining sequence is KEY-WP-0038-T02/T03 first-case registration/claims proof, +admission of the existing coding-client proposal with sanitized owner readiness +returns, then one attended negative/revoke/cleanup receipt committed with probe +clients removed. No native acceptance or credential/registration action is +inferred from this coordinator review. + + +''' +after=before.replace(anchor,paragraph+anchor) +blocks=lambda s:re.findall(r'^```task\n.*?^```',s,re.M|re.S) +assert blocks(before)==blocks(after) +assert before.split('---',2)[1]==after.split('---',2)[1] +(out/'KEY39.preimage.md').write_text(before);(out/'KEY39.proposed.md').write_text(after) +baseline={'base_commit':git('rev-parse','HEAD').strip(),'branch':git('branch','--show-current').strip(),'status':git('status','--porcelain=v1'),'index':git('ls-files','--stage'),'path':rel,'before_sha256':hashlib.sha256(before.encode()).hexdigest(),'after_sha256':hashlib.sha256(after.encode()).hexdigest(),'frontmatter':yaml.safe_load(before.split('---',2)[1]),'task_blocks':[yaml.safe_load(t.split('\n',1)[1].rsplit('```',1)[0]) for t in blocks(before)]} +(out/'baseline.json').write_text(json.dumps(baseline,indent=2)+'\n') +path.write_text(after) +assert git('diff','--name-only').splitlines()==[rel] +(out/'one-path.patch').write_text(git('diff','--',rel)) +print(json.dumps({'status':'one T02 narrative paragraph applied','task_blocks_unchanged':True,'frontmatter_unchanged':True,'base':baseline['base_commit'],'task_bindings':baseline['task_blocks']},indent=2)) diff --git a/docs/changes/netkingdom-target-2026-10-17/key39-finalization-owner-followthrough-r1/baseline.json b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-owner-followthrough-r1/baseline.json new file mode 100644 index 00000000..58d3244c --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-owner-followthrough-r1/baseline.json @@ -0,0 +1,54 @@ +{ + "base_commit": "e76d4130f31a02554048f30bf838043cea7a5b80", + "branch": "main", + "status": "", + "index": "100644 0e8a5d95b6d263fbaeae4261a7051112fb27ee18 0\t.claude/rules/agents.md\n100644 7c2a64580a2fb55d6586e154ee70d801430faa3e 0\t.claude/rules/architecture.md\n100644 2ab2aee3c221b4c2f637786f66de431236b61bc5 0\t.claude/rules/credential-routing.md\n100644 5b6b1876a29a4de2c3e6fd0f67c3ffbeb144c496 0\t.claude/rules/first-session.md\n100644 b74d916907b49e9abed8a8bd8f4f3cd7aadf95ef 0\t.claude/rules/repo-boundary.md\n100644 c4b8fa16164b5a8b37fee603997121a5c9680aac 0\t.claude/rules/repo-identity.md\n100644 0c2d89e25d9b55e90982f7596f7336328a49c786 0\t.claude/rules/session-protocol.md\n100644 dc53ac69288ec906421a0e8e1e1083528c047702 0\t.claude/rules/stack-and-commands.md\n100644 f43cfff94d75b083b3ca10e7982654d71e75dfdf 0\t.claude/rules/workplan-convention.md\n100644 d3b36c3bffbeb922769a2a788e35b4113fccaa3f 0\t.custodian-brief.md\n100644 b1bcdc2c729352cada7b654b59a07fe6850fc466 0\t.forgejo/workflows/acceptance.yaml\n100644 0e16cc2ea75d37e1da788ca847c127fc04f5a98f 0\t.forgejo/workflows/image.yaml\n100644 e8491b77aad0b288977d3fb5eb8ca8db4a7725de 0\t.gitea/workflows/image.yaml\n100644 32a386e9115c36d1689d9ff595eab8d66f037286 0\t.github/workflows/ci.yml\n100644 e705f7f1738a49dd9a78b5907d68d9429107c67c 0\t.gitignore\n100644 4fc521fc9c4263b37c6978ea68651d1cb0c25faf 0\t.repo-classification.yaml\n100644 4a0df33e6635969b7158daec3e80bd28e5f0bbc1 0\tAGENTS.md\n100644 e81c1b48c16dc3bf49af30e48b476107d916ee21 0\tCLAUDE.md\n100644 6adf9ce0636d7782fd231e418a0009deeee9868b 0\tDockerfile\n100644 b2519df87f5b29b324187f97f7a0d92dff3fc874 0\tINTENT.md\n100644 7e08e28d5f32bdea3a9e1c828ae9f1e21d9edae7 0\tLICENSE\n100644 512fa11a68e460d410deff823e58b9608a342932 0\tMakefile\n100644 5435b16714d66247db4d99ab7782cf8cd21532d9 0\tREADME.md\n100644 ab2522b2e0c0e6c008810e9929fc0b8d35121512 0\tSCOPE.md\n100644 d86b29bbc480acfa6d12d5052039578d7d4e380c 0\tWORK-RECORDS.md\n100755 e7887dfbae490351f88d1f249e928f695f6a16d2 0\tbin/keycape\n100755 592504fd11f4c4fc821f3d6b0350b1285c38b0b0 0\tbin/keycape-to-keycloak\n100755 e49fe216aa04525d611c99ddf930639f98929ad0 0\tbin/lldap-export\n100755 9609697bd8e21396add3009893fcd8a11352eabd 0\tbin/lldap-to-ldap\n100755 b732e3274c05371e62768da3c3bdc3ac767329a0 0\tbin/validator\n100644 b43901fe8a7c3ca61f509b5cb2eac8b2ef091064 0\tcapabilities/playbooks/key-cape.lightweight-sso.yaml\n100644 2872ef7727ac0fc2c3943296098fbea6f2a85f62 0\tcapabilities/playbooks/key-cape.privacyidea-token-authority.yaml\n100644 bf8ea915b24d93be803ed1ef8c18c13a82976c9b 0\tconfig/dev-config.yaml\n100644 a4b13e5cffdc1cb683a202b90724bd86f565ccda 0\tconfig/service-clients.example.yaml\n100644 fa050d2a3722fe7372938ec2cb34b31244b7aa61 0\tdocker-compose.dev.yml\n100644 a8976c28eb6c40af92e1daadcf168aaef8647ad5 0\tdocker-compose.scenario-b.yml\n100644 5743fa39140e5bac1f2cad2479c5928470e76e1c 0\tdocker-compose.scenario-c.yml\n100644 52ab3e94c61c02ea0b13a0b8cf4e3f7d0f89eb40 0\tdocs/adr/ADR-0001-choose-go-for-keycape.md\n100644 f42bfba2462bbc739f409bb817ab85904236e3d1 0\tdocs/approval-clients-deployment.patch.yaml\n100644 08b3e12ea62dd0da1354c59060434b88c9e7040f 0\tdocs/approval-clients-rollout.md\n100644 3c35fe6242d82b72a0a66b3da17f8e5dd078df2e 0\tdocs/approval-engine-auth-contract.md\n100644 902c20cb3cfa6d4592947ac76df88a2fa5186649 0\tdocs/approval-engine-provisioning-request.yaml\n100644 f305058adf60bef56762c2588b5b5b2474f45430 0\tdocs/authorization-code-bindings.md\n100644 de742b60f5872ad84195f33f2a5763952653fcd5 0\tdocs/credential-renewal-release-2026-09-13.md\n100644 c2061c6a760e810adccdc404e044cb79baa4fdcb 0\tdocs/enrollment-assurance-release-2026-09-13.md\n100644 85dab93ed1f2196810ead4f6b81a897fd06c33ff 0\tdocs/evidence/2026-09-08-upstream-issuer-live-proof.json\n100644 6ae6fc721df48426e93e505206a0df8e18ef2499 0\tdocs/evidence/2026-09-09-keycape-verifier-admission.json\n100644 62fe341c90c514a92585641d14287f60076e8836 0\tdocs/evidence/2026-09-09-upstream-issuer-pin.json\n100644 cc87339baeae5406bd7ce76fe89bd0aaf95b366f 0\tdocs/evidence/2026-09-14-informed-decision-browser-registration.json\n100644 200863c7e6b87f3045c1bffd2b951be1bdee5d84 0\tdocs/evidence/2026-09-24-fresh-login-and-account-switch.md\n100644 2278d9917b56923887b64b157b10de537739ee55 0\tdocs/evidence/2026-10-07-secrets-engine-openbao-issuance.json\n100644 d97906b5ee836fca2e52bbe42a462b02200f4fe5 0\tdocs/evidence/2026-10-11-key39-next-source-inspection.json\n100644 88d98b835b63a016ede535cdf6416add36c17514 0\tdocs/evidence/upstream-issuer-probe.json\n100644 04d0bccc32fa1e0d5739f22b5d71a638e345e386 0\tdocs/forgejo-oidc-client-request.yaml\n100644 e714df21c4113ac853f2fbbf9309c06ea1b05863 0\tdocs/native-authentication.md\n100644 9f3c00bf7dcef2b3fa40a1c0fd99a7c03cb8facf 0\tdocs/offline-checker-owner-review-2026-10-16.json\n100644 be834c5854e2d1ac3f2747fb45dd80953508ff45 0\tdocs/offline-checker-owner-review-2026-10-16.md\n100644 259f7e10fb0a6aaa071aac342906cde85c056609 0\tdocs/oidc-browser-relying-party-type-review.md\n100644 713036618418c02f612071f41c4864b9b67c900a 0\tdocs/openbao-negative-probe-clients.yaml\n100644 fe9a9945d837fec98dc15452c1e7935b6a4fd0f1 0\tdocs/openbao-negative-probe-session-proposal.md\n100644 d3b806af1dd024bfc0f518d30ef26e1b715be361 0\tdocs/openbao-negative-token-producer-decision.json\n100644 e44cbcb98a7bb4efd727a026e70db45f5195fa30 0\tdocs/openbao-negative-token-producer-decision.md\n100644 3416a5943e1b535870d16841c10a89fdb65521c8 0\tdocs/openbao-service-auth-contract.md\n100644 3092a16568b37610407e4d613b01a18bead5e960 0\tdocs/operations.md\n100644 f95f57aaa5cfd6203ea5d1b0ce99369ec32c40cc 0\tdocs/optional-mfa.md\n100644 2be7eb82c4ee1ec9a4b9227d2163b063063e40f7 0\tdocs/proposals/2026-10-11-key39-founder-source-design-decision.json\n100644 ad23cbde3d063f40e85948d0b37ec99847f24840 0\tdocs/proposals/2026-10-11-key39-founder-source-design-decision.md\n100644 09fe2e81cdda1372fc0ed7ec2e3103d303d7211d 0\tdocs/proposals/2026-10-11-key39-next-execution-prerequisites.json\n100644 714a84c4a5d9e9258110b703297763537bbd07ec 0\tdocs/qonto-runtime-identity-contract.md\n100644 315a7210651a2777eeca2a5f502cfa5b91207600 0\tdocs/reviews/2026-10-11-key39-lifecycle-v2-keycape-source-ack.json\n100644 6c462037bb51a833451f37ddcca6a347656f354d 0\tdocs/reviews/2026-10-11-key39-lifecycle-v2-keycape-source-ack.sources.json\n100644 af94cc71214d638d2e33cdb455b29c9ef5cfb1f4 0\tdocs/reviews/2026-10-11-key39-lifecycle-v4-keycape-source-ack.json\n100644 1a1ebdddca7e3e1c419ef293466174644dfdec1b 0\tdocs/reviews/2026-10-11-key39-lifecycle-v4-keycape-source-ack.sources.json\n100644 9ad2ee43cf5c66a66ff0472f4c9f40d0e550283a 0\tdocs/reviews/2026-10-11-key39-lifecycle-v5-keycape-source-ack.json\n100644 567ee0f3aa1b14f8633e874a35530eb76b8c93c4 0\tdocs/reviews/2026-10-11-key39-lifecycle-v5-keycape-source-ack.sources.json\n100644 f0aa85ec5484751c8e490e2c60a77a600e21a1e1 0\tdocs/reviews/2026-10-11-key39-v7-input-keycape-source-review.json\n100644 f4d4d9013656d1039585e32e3a9777eab096fa71 0\tdocs/reviews/2026-10-11-key39-v7-input-keycape-source-review.sources.json\n100644 2b5ccdb5341c43030ff87a62d418e2322d4e75af 0\tdocs/reviews/2026-10-11-key39-v7-input-keycape-work-record.json\n100644 7e4d3ec75f7671b2327f7eccfc51d1d902db275f 0\tdocs/reviews/2026-10-11-key39-v8-input-keycape-source-ack.json\n100644 1d9a58cd94ceba2d4727060eaf13cc0442240352 0\tdocs/reviews/2026-10-11-key39-v8-input-keycape-source-ack.sources.json\n100644 69a2ab4de8cbe2987a2be322648aa90ace9e26d6 0\tdocs/reviews/2026-10-11-key39-v8-input-keycape-work-record.json\n100644 553e90a108f111a9c42b88e16e1f38ff38af3231 0\tdocs/reviews/2026-10-11-key39-v9-v4-keycape-source-ack.json\n100644 524f47af20bae995988f59285c5f29283261e007 0\tdocs/reviews/2026-10-11-key39-v9-v4-keycape-source-ack.sources.json\n100644 acf3e488fa9af74216f669e3f84bdf5d22e3963b 0\tdocs/reviews/2026-10-11-key39-v9-v4-keycape-work-record.json\n100644 2b7e30ef9b82ebbf22ad589d31d0fccb74b33c95 0\tdocs/secrets-engine-openbao-owner-packet.md\n100644 ce5f53365f2ae3b14cd53f4b43c248f77c5a0223 0\tdocs/sitting-requester-registration.json\n100644 d45478c9ef6363a735fd3605417e9275c18a2a94 0\tdocs/t03-requester-registration.json\n100644 32efcf0a5afc5c660bd754eb494020c32f609bdd 0\tdocs/t03-requester-registration.md\n100644 9ab277b85b550ac52ccf98bfda0a0dd7c744e171 0\tdocs/tenant-claim-contract.md\n100644 4e7d26b6a1cbcacf6498aed0e33da8964160c39e 0\tdocs/upstream-issuer-proof.md\n100644 4f305e21acfa7ad7f44b70542ba8f627b5ef9362 0\thistory/2026-09-05-011726-scope-intent-assessment.md\n100644 394eed11f5afb16de7eda7b09f99cf59dbbbacce 0\thistory/2026-09-09-scope-reassessment.md\n100644 2c9e75f72d9983b1b2980d79b12380564465e640 0\thistory/2026-09-28-railiance-admin-role.md\n100644 77233c20c2230459b2d78d9d28578e9472e568ce 0\thistory/KEY-WP-0011-live-secret-exposure-recovery.md\n100644 8eef81bac925dacfa8408bdfb4e5a2d672ba903e 0\thistory/KEY-WP-0011-rotated-credentials-2026-08-23-final.tar.age\n100644 845c3025f781d2296f40b4ec93c43935a480295d 0\tintakes/intakes.md\n100644 569abe99c95e163d39048f4b02ccb47165751c7e 0\tregistry/README.md\n100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0\tregistry/capabilities/.gitkeep\n100644 59cc40e2ccad5b2ce9111366efbc9e48f8e18e70 0\tregistry/capabilities/capability.iam.key-cape.md\n100644 0718f5218c9f4b4ef84c6d9aacd4b61440098405 0\tregistry/indexes/capabilities.yaml\n100755 bf05bb3db90d62b77a1104e48d8e31045ccf0d8e 0\tscripts/bootstrap-dev.sh\n100644 29627c2056fbcf511bce069db0a8803c797295ff 0\tscripts/collect-upstream-issuer-proof.py\n100644 10832318492c4b34230400e458209777d58b08a1 0\tscripts/factor_recovery.py\n100644 2bc28a205ffbd14c4f077c61957f5e3c0af284fb 0\tscripts/keycape_onboarding_guard.py\n100644 e3a875e114d176431b517667c0c096ac4a974ad2 0\tscripts/openbao_negative_private_handoff.py\n100644 4eae60ab0d0766a390743ed33d665ea852bb285e 0\tscripts/openbao_negative_private_launcher.py\n100644 cb864ae0b2b46650a5e20ae90e9465701c362011 0\tscripts/openbao_negative_probe.py\n100644 477857655b8316659d7a3a2afcbe330e497d3851 0\tscripts/provider-browser-fixture.py\n100644 5ac86bfec073a054b712fa7a82fd44066f203a29 0\tscripts/provider-onboarding-contract.py\n100644 2abdf8d67ab9a61f93ab3ef90d36942ee7659702 0\tscripts/provider_browser.mjs\n100644 bc9f59737a06c59e7746aecd00df8112a9c830d1 0\tscripts/provider_browser_acceptance.py\n100644 ca0bb2a3554c569d3923c48bebe0850a1f4254ff 0\tscripts/recovery_service.py\n100644 0f2dca8e890f18bf5430596a1c722bdc4bcb4d0c 0\tscripts/render-upstream-issuer-probe.py\n100755 48a5832ed965624685eeeb9cad396b8e6a854426 0\tscripts/test-scenario-b.sh\n100755 e333ec26464ade014e45d7a13b153d0a6f29b0a1 0\tscripts/test-scenario-c.sh\n100644 25d961f46258329f2290811101388c293e16b025 0\tscripts/test_factor_recovery.py\n100644 c17061849c2a8f9b6c614e69888502607c612674 0\tscripts/test_openbao_negative_private_handoff.py\n100644 e1d98a393d5276d64d689c3b961790219e939f77 0\tscripts/test_openbao_negative_private_launcher.py\n100644 8344b22b4d62deb60ccafafa7acca336d226b0a8 0\tscripts/test_openbao_negative_private_preparation.py\n100644 242d4502ab1f2bda9886ec0936c647019c426696 0\tscripts/test_openbao_negative_probe.py\n100644 6cdb5acd55440ed089e2842f5c762627363defa1 0\tscripts/test_recovery_service.py\n100644 ab6f45297f354d006c4e24beb698bafe8a93a31b 0\tscripts/test_render_upstream_issuer_probe.py\n100644 fca9b9d450cdee91d383ff8a67c738774c5aca4c 0\tspec/canonical-model.yaml\n100644 41cc35711da626fd1986f0da439b230235cac4df 0\tspec/ldap-schema.yaml\n100644 083df0b3e537a5ebb09b9a454f25c68d650fdac8 0\tsrc/Makefile\n100644 faca3748339a924aff723dcb3ed10ec8d737248f 0\tsrc/cmd/keycape-to-keycloak/main.go\n100644 5edcfd7d4fe8f6af021676e8f0f8a0b258831c37 0\tsrc/cmd/keycape/checkconfig.go\n100644 93e4c44654ca28fb16027748c9c24d91d5ced4bd 0\tsrc/cmd/keycape/checkconfig_test.go\n100644 c2b60259d95831503e35a0c67fbe6e3d0c4169f3 0\tsrc/cmd/keycape/clients_test.go\n100644 16de34c66062718b91821d0b836e258c82783401 0\tsrc/cmd/keycape/main.go\n100644 daac6e986c48fcf2de9243ef78a40e0b88b9de4a 0\tsrc/cmd/lldap-export/main.go\n100644 5aa9a2bb1472931b7b9c798fbebfee45fe316f9b 0\tsrc/cmd/lldap-export/main_test.go\n100644 c98b19b31f2c2be0124c88f001b33048a7221968 0\tsrc/cmd/lldap-to-ldap/main.go\n100644 411272078c48cc52abce1007c0939528c66581fa 0\tsrc/cmd/validator/main.go\n100644 992c33f91d6b7f1244634cf583072f4bfbf73759 0\tsrc/go.mod\n100644 d1f81c7aef0bb7c7892e9dd2131653661659bd0b 0\tsrc/go.sum\n100644 b78811acf78adad92d9a412c13cbb7aa86866468 0\tsrc/internal/adapters/authelia/adapter.go\n100644 81ac6205d8217509e6130b159c77e94291ca55a5 0\tsrc/internal/adapters/authelia/adapter_test.go\n100644 810f5b9387721058fb629a305d3aaf5254c65a12 0\tsrc/internal/adapters/authelia/config.go\n100644 082708a178b818d83632bfe31c77696c03530c60 0\tsrc/internal/adapters/authelia/issuer_probe.go\n100644 7943c3f59d5c59aba5adaff9dab1af4d4055d635 0\tsrc/internal/adapters/authelia/issuer_probe_test.go\n100644 7c85d3b9ea7d7843092b24d812807fb6b48755dd 0\tsrc/internal/adapters/authelia/verify.go\n100644 d9003680dc01f3d709d938ff8843f0a96b8a69f6 0\tsrc/internal/adapters/authelia/verify_test.go\n100644 e15cecd7c7ee83383745a367502bf6ab3d1280fe 0\tsrc/internal/adapters/lldap/adapter.go\n100644 e153314a0fc06e33ce8b957b66cc1838ee93a180 0\tsrc/internal/adapters/lldap/adapter_test.go\n100644 fce760a4e97c94e1e542a4d2f0895a20c709fed9 0\tsrc/internal/adapters/lldap/config.go\n100644 d9e10ff898c19e85160e8b047f79a1d9daa797c7 0\tsrc/internal/adapters/lldap/config_internal_test.go\n100644 d76e938ebfc2f676d5dd0a0639707d1e7f4ebc08 0\tsrc/internal/adapters/lldap/railiance_admin_test.go\n100644 35e65335a00ead963c3cd8e95e1b94a5b34e6a5a 0\tsrc/internal/adapters/privacyidea/adapter.go\n100644 286562d56a3f263df68f34eb35bf68b6581688da 0\tsrc/internal/adapters/privacyidea/adapter_test.go\n100644 b34c9f3d19109a7a0acc309c27639ce913f1e751 0\tsrc/internal/adapters/privacyidea/config.go\n100644 017315272e6baa1151d4e73912b19c3bb8ca377b 0\tsrc/internal/adapters/privacyidea/credential_file_test.go\n100644 6f3b883c0c7eced42bfdbe45713b1be5dadf0bc9 0\tsrc/internal/adapters/privacyidea/enrollment_state_test.go\n100644 13316d5ffada681a8f956b519f5a3d6ef94c3233 0\tsrc/internal/adapters/privacyidea/optional_mfa_test.go\n100644 964fbb76048c44616d56b7da916d44c6b86432f0 0\tsrc/internal/adapters/privacyidea/read_scope_test.go\n100644 eeb54b9f0b7e1b9776fc37ecfc36599bbf07026a 0\tsrc/internal/adapters/tenantengine/adapter.go\n100644 d437fd14dcfffa53999b16849888a86c553d0a42 0\tsrc/internal/adapters/tenantengine/adapter_test.go\n100644 2b0417e3745a31e177d2b154454a0f132d2f7616 0\tsrc/internal/authclient/cli.go\n100644 0d39586235d89aa8097d76b7d210331e4a53083f 0\tsrc/internal/authclient/client.go\n100644 dfce39882584ab7f37ecb9878d6b1801b74d9a4c 0\tsrc/internal/authclient/client_test.go\n100644 66b0ea4961b3573152c491f597fad3e02ea59a1a 0\tsrc/internal/authclient/verify.go\n100644 a2a824f02554f0645a543934ee33419dca429085 0\tsrc/internal/authclient/verify_test.go\n100644 f28ad987f862203708795498947229df9de3757e 0\tsrc/internal/config/account_test.go\n100644 7895b73a96899a5443a0a044108e3dd3e4e76294 0\tsrc/internal/config/config.go\n100644 d4236a6d45fa57b865b0983b395c6e9b3974105b 0\tsrc/internal/config/config_test.go\n100644 585aa6243370bb60a94506f5fa670516959c12b0 0\tsrc/internal/config/optional_mfa_test.go\n100644 c020e4076637f2f062ae49a8578bea8dd493bd70 0\tsrc/internal/config/validate.go\n100644 afbc48c673df6fe76ed3f2fa2aea492e476ec37b 0\tsrc/internal/domain/assurance.go\n100644 b9096ef7c09da6a413ed392c751799b3bb1b72bf 0\tsrc/internal/domain/assurance_test.go\n100644 66c907c367651b193559701c2da564aea6757ae8 0\tsrc/internal/domain/auth.go\n100644 5984835dd46da65b095829e05948f841554f733f 0\tsrc/internal/domain/conformance_test.go\n100644 30b0a92d9444324cdf67df1e856feb984385c29e 0\tsrc/internal/domain/mfa.go\n100644 1cfda5371fe0c6eec07329b4990d3c6a105e92ee 0\tsrc/internal/domain/model.go\n100644 56252d821062128e3323a0cdca1064d292a97898 0\tsrc/internal/domain/repository.go\n100644 80ebf0763538369dff67abd3a17c6269022f5e2e 0\tsrc/internal/errors/taxonomy.go\n100644 2f5722e94e982c71b008b7f4c522d500df76a0e1 0\tsrc/internal/errors/taxonomy_test.go\n100644 14769bbe206e8fbbf6eb0ed4212e3ad656ffd996 0\tsrc/internal/jose/jose.go\n100644 34abfa36f8dd355df7713eb940b269f785805402 0\tsrc/internal/jose/jose_test.go\n100644 4119e8a8950a8461c5fe5cb4944af498df5df84f 0\tsrc/internal/migration/lldapexport/exporter.go\n100644 e9071d133a6f3fbd2421d3d0dd9fc8a051d61693 0\tsrc/internal/migration/lldapexport/exporter_test.go\n100644 4666a2b8579e73f76bd4ccf4518cdd9818035e2a 0\tsrc/internal/migration/tokeycloak/transformer.go\n100644 50dee2c66b1922e2d109ca963592fe3a0bf9711c 0\tsrc/internal/migration/tokeycloak/transformer_test.go\n100644 fd21830fd2299a401000bb418946d107bbefa1ee 0\tsrc/internal/migration/toldap/generator.go\n100644 5ec399dca0db878f3d5fab76504947635770727d 0\tsrc/internal/migration/toldap/generator_test.go\n100644 81fd2c8d24016adb626f7a88b442361cf6197124 0\tsrc/internal/server/errors/enforcement.go\n100644 0f8384bc5a147905ea99f33d1a4e573c7a85387e 0\tsrc/internal/server/errors/enforcement_test.go\n100644 7036039edd2837cb22e804b60633b2425a5fb817 0\tsrc/internal/server/oidc/account.go\n100644 1a2d68f1008418e138a6c2d293f2afda017c5ba5 0\tsrc/internal/server/oidc/account_test.go\n100644 72ad579561c84e9ed99da98dd86ca472880ff123 0\tsrc/internal/server/oidc/assurance_time_test.go\n100644 fb5b1f41c0619b7ea6d72c231aec2027769f7f77 0\tsrc/internal/server/oidc/audience_test.go\n100644 60447bc9cd056c2119b1611e38585bc81884a568 0\tsrc/internal/server/oidc/authorize.go\n100644 434b51089fe92497644cdc4504f765f75c21f542 0\tsrc/internal/server/oidc/authorize_test.go\n100644 4b0074b6b5376f11c3979b13b05761c521762bac 0\tsrc/internal/server/oidc/discovery.go\n100644 f2d069d54f67ac1cad061e53687a44ab0c1648ba 0\tsrc/internal/server/oidc/discovery_test.go\n100644 31954ca4dd03ccc36d9da03d5734ac029d46c9ce 0\tsrc/internal/server/oidc/handoff.go\n100644 d8a33fb4e5f90f92e7acab2f198c540d39cfdebc 0\tsrc/internal/server/oidc/handoff_test.go\n100644 d73a3ffcd40558731fb4fac2c66ff0588c0969f5 0\tsrc/internal/server/oidc/hardening_test.go\n100644 187bd993cb85369ada263c734bd4e7daec5bbb73 0\tsrc/internal/server/oidc/human_tenant_test.go\n100644 e74bd2e4a219c25aefc781841e73eb219e0945a5 0\tsrc/internal/server/oidc/jwks.go\n100644 1fd53f19e6c8a131a639d780bea7e1c0c609930a 0\tsrc/internal/server/oidc/jwks_test.go\n100644 7953f06b4948162868166a8ffafef36b2445509b 0\tsrc/internal/server/oidc/login_session.go\n100644 496d237a3f642ac94c869f0dc3438d9a2e262b9d 0\tsrc/internal/server/oidc/logout.go\n100644 47d4df2dee47489a0923df57fe654be87edf2b12 0\tsrc/internal/server/oidc/native_optional_test.go\n100644 2f8eecda6f29cf3d31177ec3b7ec3650a871b555 0\tsrc/internal/server/oidc/optional_mfa_test.go\n100644 221ed02325c872a7947e798607da236f15d7d6e3 0\tsrc/internal/server/oidc/policy_isolation_test.go\n100644 c70d6a4a7d78abd6c0911776f8857ddedb7858bd 0\tsrc/internal/server/oidc/policy_runtime_test.go\n100644 d1c9a9660f3e5fe6c878657957ce6bd7db175c6d 0\tsrc/internal/server/oidc/principal_type_guard_test.go\n100644 15d759c148e72d4a84df18a3d62db1c6838d1be5 0\tsrc/internal/server/oidc/session.go\n100644 ba82ee4b244e241d8d156e05315d8b71626b1cef 0\tsrc/internal/server/oidc/signin_diagnosis.go\n100644 72a1b3518b8824da04d1bf87bd4d73dfa1a7a934 0\tsrc/internal/server/oidc/signin_diagnosis_test.go\n100644 a955146312249d526fa7fedefd085c1140399ac0 0\tsrc/internal/server/oidc/tenant_precondition_test.go\n100644 1517610493b1e91c16f9bfee5fd63361be2d7992 0\tsrc/internal/server/oidc/tenant_test.go\n100644 b1d7f57106208d5cc8a80d964e1698d1dc59dd91 0\tsrc/internal/server/oidc/token.go\n100644 42314ec0fe6c254c3b29c4f859e090f18b316f21 0\tsrc/internal/server/oidc/token_test.go\n100644 c3d2014bd63dc2186d8a2123cef064b2cddfef36 0\tsrc/internal/server/oidc/userinfo.go\n100644 5a12f5e898c8f4526d505404eed14e6f466ca7d7 0\tsrc/internal/server/oidc/userinfo_test.go\n100644 29167da1dbb2b3a9d2e74f5f0319e0902d4173a3 0\tsrc/internal/server/policy/policy.go\n100644 4becee709136b31735eeeab3f1b6b1875de4407e 0\tsrc/internal/server/policy/policy_test.go\n100644 e0cfefbf7c014370890470fc42850c3b3a879daa 0\tsrc/internal/server/readiness/readiness.go\n100644 02cf96ed77554ce25eb4f62e54c00b02cc0089a5 0\tsrc/internal/server/readiness/readiness_test.go\n100644 ff11c05a76db4692e1de6d9ba19f5dc5d45ed2e8 0\tsrc/internal/server/telemetry/emitter.go\n100644 3d64e3d148536f96bddecd228c7eba6711194780 0\tsrc/internal/server/telemetry/emitter_test.go\n100644 793b79bcef80638f2c182645cba9773c17c1550c 0\tsrc/internal/server/telemetry/events.go\n100644 87baa854418f0a6617579e6566aa84217134c6e2 0\tsrc/internal/validator/report.go\n100644 78eb866cef192174b0a29f151eac64f4adc85472 0\tsrc/internal/validator/validator.go\n100644 a86eff31eb55d9cda811adc3508b44e82f6b2eba 0\tsrc/internal/validator/validator_test.go\n100644 75cddf3602a0dbb1d29162db1df187e43e3d4568 0\tsrc/tests/conformance/external_test.go\n100644 208e903a6377e3b894595de8c97248ba96e3dbb3 0\tsrc/tests/migration/fixtures_test.go\n100644 8a49ddec84f156a2ef0badf8f78d292243b7a5dd 0\tsrc/tests/migration/scenario_b_test.go\n100644 bf0a61a1c7e00fd6f3e919529603c5c7461f4a69 0\tsrc/tests/migration/scenario_c_test.go\n100644 377b0f33da592cfbc7eeb9bc74025280ce553a6d 0\tsrc/tests/negative/negative_test.go\n100644 8b802f71346bf27a58ae73877fa594be0557b396 0\tsrc/tests/profile/profile_test.go\n100644 9192fd9cde67896f741cf67dccd48d2250d6ad27 0\ttools/register-informed-decision.py\n100755 0dcb542eeb16827ccb0d0f05b6e302f6af1693e8 0\ttools/rotate-live-exposed-secret.sh\n100644 4b728fd406c815143f302a76512858d3577e6aca 0\twiki/KeyCapeSpecificationPack_v0.1.md\n100644 c803cabd2342689c3123e71b8773edb8a972125d 0\twiki/KeyCapeSpecification_v0.1.md\n100644 f7e78675df62c9b48e29f882fa99298d6d8ce7f1 0\tworkplans/ADHOC-2026-09-05.md\n100644 17401073c053c9710e6e680243bbd313b08d1fc1 0\tworkplans/ADHOC-2026-09-07.md\n100644 0b755547aa7d5589f0898df2f102a45b6aa24838 0\tworkplans/ADHOC-2026-09-27.md\n100644 74334411cd056e7c7253e84f1c4b40c42eb13de3 0\tworkplans/KEY-WP-0001-keycape-implementation.md\n100644 418f477c0ede13d7c003d744abf234347f5d5e31 0\tworkplans/KEY-WP-0002-container-image-gitea.md\n100644 9896c67d78f0efdd03325535eb41295250be3a19 0\tworkplans/KEY-WP-0003-bootstrap-console-oidc-mfa-login.md\n100644 a50e910558ee43073c92cb27865a1aceac990dd5 0\tworkplans/KEY-WP-0004-binky-hedgehog-tenant-onboarding.md\n100644 648d2405699dcf25b3121df6c46d871597100d7b 0\tworkplans/KEY-WP-0005-iam-profile-core-claims.md\n100644 632c4c2cad39cbde55c2ae1f2ba5ecf2f445b1a0 0\tworkplans/KEY-WP-0006-client-credentials-service-tokens.md\n100644 f1b04e1a61b398426c23d8d9ca655b3937d7bd10 0\tworkplans/KEY-WP-0007-user-engine-portal-oidc-client.md\n100644 438ab5480de3bd66d75b6ec7e09639d1b93e3e5f 0\tworkplans/KEY-WP-0008-registration-handoff-and-client-mfa-policy.md\n100644 c9e585db65424c7c4e3b5adf37a68de2bdfbdc1c 0\tworkplans/KEY-WP-0009-provider-capabilities-and-service-identities.md\n100644 81614eeb8c98205d6e5eb3201da10e36b76a21e9 0\tworkplans/KEY-WP-0010-openbao-operator-loopback-callback.md\n100644 f7b906f552f6e3279895a90afbe216b6e78ee5ff 0\tworkplans/KEY-WP-0011-live-secret-exposure-recovery.md\n100644 0f3deb626e5de9a9f898754ef81d3f2ee5282792 0\tworkplans/KEY-WP-0012-userinfo-canonical-subject-resolution.md\n100644 7d77aee3caf3d79fe6d6c1369249c6d94e0ae123 0\tworkplans/KEY-WP-0013-approval-engine-resource-audience.md\n100644 bacf04c07f59125a90c519d3c306a7414cb9a1a3 0\tworkplans/KEY-WP-0014-native-credential-lane-handoff.md\n100644 82b952a216346ffb7f5cab413623fe4a88f7700e 0\tworkplans/KEY-WP-0015-scope-intent-assessment.md\n100644 52839bd0f83d97656c8ad97a8ffc338d9019239a 0\tworkplans/KEY-WP-0016-authorization-code-protocol-hardening.md\n100644 00eb03971aae75724afbf7b8a4946ce6b7eda3af 0\tworkplans/KEY-WP-0017-canonical-model-and-discovery-conformance.md\n100644 325fc483d62dbad53b92b986de56112168a92b4a 0\tworkplans/KEY-WP-0018-export-completeness-evidence.md\n100644 dfeb92296f46d4ce2ac7df1fedcc61e9ccc5d6b3 0\tworkplans/KEY-WP-0019-upstream-provider-token-verification.md\n100644 7b6f3be976293469dee2180c10e16f313876d640 0\tworkplans/KEY-WP-0020-migration-contract-preservation.md\n100644 1f6efeb32a6eb8fcbc52f4d3b591e2fc6b9e006d 0\tworkplans/KEY-WP-0021-snapshot-attribute-validation.md\n100644 7c69629ecd20d0158aa1332d9d1442b796f6a7af 0\tworkplans/KEY-WP-0022-replacement-harness-and-external-conformance.md\n100644 3e7c83cb12e7fc31e682cf3c27f4c4767ded2905 0\tworkplans/KEY-WP-0023-live-migration-proof.md\n100644 baa0bbd195a92708f2befd027b938fcd1812ced7 0\tworkplans/KEY-WP-0024-tenant-roles-opt-in-wiring.md\n100644 a53e646761178748e8c4b00dcde888698447a948 0\tworkplans/KEY-WP-0025-runtime-lifecycle-and-readiness.md\n100644 1f96a2ac3ac939c4e6e0f66172477cf73db04e51 0\tworkplans/KEY-WP-0026-packaging-bootstrap-and-credential-handling.md\n100644 57e01b2db033847cc786504b8795e37112d988a7 0\tworkplans/KEY-WP-0027-rollout-readiness-and-live-state.md\n100644 46b0672fea90cd7cb672f7d119706c240474f8ca 0\tworkplans/KEY-WP-0028-browser-client-field-validation.md\n100644 392f97750f6cb8b12868a6fd016c7d396b58477e 0\tworkplans/KEY-WP-0029-scope-reassessment.md\n100644 0f74e0d5a037eec64b11b57c67b02057a3b0bfef 0\tworkplans/KEY-WP-0030-tenant-precondition-guard.md\n100644 d2ede9d494927976ccb9ba6a5aeaf1ed21c30837 0\tworkplans/KEY-WP-0031-gate-house-tenant-ruling.md\n100644 a04f52f817a51efc1d0d0ecb99efacea8dc53a17 0\tworkplans/KEY-WP-0032-principal-type-provenance-guard.md\n100644 5b478f953890339aec796c11a536af0ec3238789 0\tworkplans/KEY-WP-0033-vergabe-fresh-login.md\n100644 eb58f99bc26e3c3ac33230dfe432c8b1fabe47ce 0\tworkplans/KEY-WP-0034-account-recovery.md\n100644 29eb711f656c91808e91f3981a9168c3a0ebc253 0\tworkplans/KEY-WP-0035-optional-mfa.md\n100644 fd296bc05a6e565b0ef6e88a9ae3daa7c9f81786 0\tworkplans/KEY-WP-0036-platform-factor-recovery.md\n100644 ad7fed77a27aa8487693ad6fcf342502f418427a 0\tworkplans/KEY-WP-0037-secrets-engine-openbao-registration-custody.md\n100644 8cd5bc66f7b0115eec7813d6eec994a1bb618668 0\tworkplans/KEY-WP-0038-forgejo-oidc-client.md\n100644 f51f2e67204a6a344394461c7b5cf989d758bd38 0\tworkplans/KEY-WP-0039-openbao-negative-probe-tokens.md\n100644 8e8730c8e7733b13d6a9f42689a9d8e407769e81 0\tworkplans/KEY-WP-0040-mfa-attended-run-findings.md\n100644 56fd05819e3acd37b8e6cb468ec3d62307f1ae7a 0\tworkplans/KEY-WP-0041-sign-in-diagnosis-page.md\n", + "path": "workplans/KEY-WP-0039-openbao-negative-probe-tokens.md", + "before_sha256": "7b4b97f9a9e6b8291078e187b967a5356702dd7817880f92c12649d764e5e0ae", + "after_sha256": "c576db4b97ea5de0a50e17f251f8d5c57a744ded557bede963e48608bf3a6ca2", + "frontmatter": { + "id": "KEY-WP-0039", + "type": "workplan", + "title": "Negative-check tokens for the secrets-engine OpenBao JWT role", + "domain": "infotech", + "repo": "key-cape", + "status": "active", + "flavor": "implementation", + "owner": "claude-code", + "topic_slug": "netkingdom", + "created": "2026-10-10", + "updated": "2026-10-17", + "depends_on": [], + "related_repos": [ + "railiance-platform", + "secrets-engine" + ], + "state_hub_workstream_id": "09c3bddf-6058-5603-8488-cccdb7be4f86" + }, + "task_blocks": [ + { + "id": "KEY-WP-0039-T01", + "status": "done", + "priority": "medium", + "state_hub_task_id": "4d06b059-3768-53bb-83a4-a9a71c0850b8" + }, + { + "id": "KEY-WP-0039-T02", + "status": "wait", + "depends_on": [ + "KEY-WP-0039-T01", + "KEY-WP-0038-T02", + "KEY-WP-0038-T03" + ], + "blocking_reason": "Ordered identity gate: Forgejo first SecurityCase registration/claims proof, then NetKingdom registry session and attended human/coding-agent custody; registration authorization already resolved.", + "priority": "medium", + "state_hub_task_id": "03da4c38-1dac-53b1-8b0b-e697df48e9d3" + }, + { + "id": "KEY-WP-0039-T03", + "status": "done", + "priority": "medium", + "state_hub_task_id": "54d37ad8-2352-51c2-bb2e-af6070dbbd73" + } + ] +} diff --git a/docs/changes/netkingdom-target-2026-10-17/key39-finalization-owner-followthrough-r1/canonical-receiver.json b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-owner-followthrough-r1/canonical-receiver.json new file mode 100644 index 00000000..4937c1f6 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-owner-followthrough-r1/canonical-receiver.json @@ -0,0 +1,173 @@ +{ + "schema": "state-hub.repository-projection-reconcile.v1", + "instance_role": "primary", + "instance_label": "railiance01", + "expected_commit": "40de616e078906e04c692edf7cb7a8bfbc07b5ea", + "derived_commit": "40de616e078906e04c692edf7cb7a8bfbc07b5ea", + "outcome": { + "schema": "state-hub.projection-reset.v1", + "repo_slug": "key-cape", + "commit": "40de616e078906e04c692edf7cb7a8bfbc07b5ea", + "status": "applied", + "counts": { + "created": 0, + "updated": 44, + "retired": 0, + "refused": 0, + "released": 0, + "created_tasks": 0, + "updated_tasks": 0, + "cancelled_tasks": 0, + "created_edges": 0, + "removed_edges": 0, + "unresolved_edges": 1, + "external_edges": 8 + }, + "created": [], + "updated": [ + "ADHOC-2026-09-05", + "ADHOC-2026-09-07", + "ADHOC-2026-09-27", + "KEY-WP-0001", + "KEY-WP-0002", + "KEY-WP-0003", + "KEY-WP-0004", + "KEY-WP-0005", + "KEY-WP-0006", + "KEY-WP-0007", + "KEY-WP-0008", + "KEY-WP-0009", + "KEY-WP-0010", + "KEY-WP-0011", + "KEY-WP-0012", + "KEY-WP-0013", + "KEY-WP-0014", + "KEY-WP-0015", + "KEY-WP-0016", + "KEY-WP-0017", + "KEY-WP-0018", + "KEY-WP-0019", + "KEY-WP-0020", + "KEY-WP-0021", + "KEY-WP-0022", + "KEY-WP-0023", + "KEY-WP-0024", + "KEY-WP-0025", + "KEY-WP-0026", + "KEY-WP-0027", + "KEY-WP-0028", + "KEY-WP-0029", + "KEY-WP-0030", + "KEY-WP-0031", + "KEY-WP-0032", + "KEY-WP-0033", + "KEY-WP-0034", + "KEY-WP-0035", + "KEY-WP-0036", + "KEY-WP-0037", + "KEY-WP-0038", + "KEY-WP-0039", + "KEY-WP-0040", + "KEY-WP-0041" + ], + "retired": [], + "released": [], + "created_tasks": [], + "updated_tasks": [], + "cancelled_tasks": [], + "created_edges": [], + "removed_edges": [], + "unresolved_edges": [ + { + "workplan": "KEY-WP-0010", + "from": "KEY-WP-0010", + "target": "RAILIANCE-WP-0027", + "reason": "missing" + } + ], + "external_edges": [ + { + "workplan": "KEY-WP-0038", + "from": "KEY-WP-0038-T03", + "target": "KEY-WP-0038-T02", + "relationship_type": "starts_after", + "from_workplan_id": "098e3e2c-a138-560e-b2d3-3a15eb49d8a9", + "from_task_id": "40ddaa29-7245-508c-ad48-ddc063e25115", + "to_workplan_id": null, + "to_task_id": "ddcfb482-8f33-5c81-9ce8-84be8fa5dc30" + }, + { + "workplan": "KEY-WP-0038", + "from": "KEY-WP-0038-T02", + "target": "KEY-WP-0038-T01", + "relationship_type": "starts_after", + "from_workplan_id": "098e3e2c-a138-560e-b2d3-3a15eb49d8a9", + "from_task_id": "ddcfb482-8f33-5c81-9ce8-84be8fa5dc30", + "to_workplan_id": null, + "to_task_id": "5f019d13-1fa3-5b88-a27a-75a3339ca96e" + }, + { + "workplan": "KEY-WP-0038", + "from": "KEY-WP-0038-T02", + "target": "NK-WP-0048-T06", + "relationship_type": "starts_after", + "from_workplan_id": "098e3e2c-a138-560e-b2d3-3a15eb49d8a9", + "from_task_id": "ddcfb482-8f33-5c81-9ce8-84be8fa5dc30", + "to_workplan_id": null, + "to_task_id": "d9b26bcf-a7c6-59a2-92a0-f913d76a203c" + }, + { + "workplan": "KEY-WP-0039", + "from": "KEY-WP-0039-T02", + "target": "KEY-WP-0038-T03", + "relationship_type": "starts_after", + "from_workplan_id": "09c3bddf-6058-5603-8488-cccdb7be4f86", + "from_task_id": "03da4c38-1dac-53b1-8b0b-e697df48e9d3", + "to_workplan_id": null, + "to_task_id": "40ddaa29-7245-508c-ad48-ddc063e25115" + }, + { + "workplan": "KEY-WP-0039", + "from": "KEY-WP-0039-T02", + "target": "KEY-WP-0039-T01", + "relationship_type": "starts_after", + "from_workplan_id": "09c3bddf-6058-5603-8488-cccdb7be4f86", + "from_task_id": "03da4c38-1dac-53b1-8b0b-e697df48e9d3", + "to_workplan_id": null, + "to_task_id": "4d06b059-3768-53bb-83a4-a9a71c0850b8" + }, + { + "workplan": "KEY-WP-0039", + "from": "KEY-WP-0039-T02", + "target": "KEY-WP-0038-T02", + "relationship_type": "starts_after", + "from_workplan_id": "09c3bddf-6058-5603-8488-cccdb7be4f86", + "from_task_id": "03da4c38-1dac-53b1-8b0b-e697df48e9d3", + "to_workplan_id": null, + "to_task_id": "ddcfb482-8f33-5c81-9ce8-84be8fa5dc30" + }, + { + "workplan": "KEY-WP-0007", + "from": "KEY-WP-0007", + "target": "KEY-WP-0005", + "relationship_type": "blocks", + "from_workplan_id": "24db4178-705c-5b09-b7db-ee7a345d71a0", + "from_task_id": null, + "to_workplan_id": "711a8303-69aa-5db6-9043-92530c6dc81b", + "to_task_id": null + }, + { + "workplan": "KEY-WP-0009", + "from": "KEY-WP-0009", + "target": "KEY-WP-0006", + "relationship_type": "blocks", + "from_workplan_id": "c1a9b1cc-2ff0-566a-b544-1a2ef967fc0d", + "from_task_id": null, + "to_workplan_id": "d8d20d8f-61e0-54e2-9a88-a3958f0b6f2d", + "to_task_id": null + } + ], + "refused": [], + "notes": [] + } +} diff --git a/docs/changes/netkingdom-target-2026-10-17/key39-finalization-owner-followthrough-r1/canonical-reconcile.py b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-owner-followthrough-r1/canonical-reconcile.py new file mode 100644 index 00000000..5eed834d --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-owner-followthrough-r1/canonical-reconcile.py @@ -0,0 +1,32 @@ +from pathlib import Path +import hashlib,json,subprocess,urllib.request,urllib.error,yaml,re +root=Path('/home/worsch/key-cape');out=Path('/home/worsch/the-custodian/docs/changes/netkingdom-target-2026-10-17/key39-finalization-owner-followthrough-r1');base='http://127.0.0.1:8000';head='40de616e078906e04c692edf7cb7a8bfbc07b5ea' +def git(*args):return subprocess.check_output(['git','-C',str(root),*args]).decode() +assert git('rev-parse','HEAD').strip()==head +assert git('ls-remote','origin','refs/heads/main').split()[0]==head +assert not git('status','--porcelain=v1') +def get(path): + with urllib.request.urlopen(base+path,timeout=30) as response:return json.load(response) +health=get('/state/health');assert (health['instance_role'],health['instance_label'])==('primary','railiance01') +request_path='/repos/key-cape/work-record-projection/reconcile';body={'expected_commit':head,'acknowledge_retirements':False} +digest=hashlib.sha256(json.dumps({'method':'POST','path':request_path,'body':body},sort_keys=True,separators=(',',':')).encode()).hexdigest() +request=urllib.request.Request(base+request_path,data=json.dumps(body).encode(),method='POST',headers={'Content-Type':'application/json','Idempotency-Key':'rmgr-projection:v2:'+digest,'X-StateHub-Source-Agent':'custodian-codex'}) +try: + with urllib.request.urlopen(request,timeout=120) as response:receipt=json.load(response) +except urllib.error.HTTPError as error: + raw=error.read();(out/'canonical-receiver-refusal.json').write_bytes(raw);raise +(out/'canonical-receiver.json').write_text(json.dumps(receipt,indent=2)+'\n') +assert receipt['expected_commit']==receipt['derived_commit']==head +assert (receipt['instance_role'],receipt['instance_label'])==('primary','railiance01') +assert receipt['outcome']['status'] in ('applied','noop'),receipt['outcome']['status'] +snapshot=get('/repos/key-cape/work-record-projection/snapshot');baseline=json.loads((out/'baseline.json').read_text()) +planid=baseline['frontmatter']['state_hub_workstream_id'];plan=next(w for w in snapshot['workplans'] if w['id']==planid) +rows=[] +for block in baseline['task_blocks']: + task=next(t for t in snapshot['tasks'] if t['id']==block['state_hub_task_id']) + assert task['status']==block['status'];rows.append({'record_id':block['id'],'state_hub_task_id':task['id'],'status':task['status'],'workplan_id':task.get('workplan_id'),'depends_on_source':block.get('depends_on',[])}) +current=(root/baseline['path']).read_text();assert current==(out/'KEY39.proposed.md').read_text() +assert re.findall(r'^```task\n.*?^```',current,re.M|re.S)==re.findall(r'^```task\n.*?^```',(out/'KEY39.preimage.md').read_text(),re.M|re.S) +verification={'commit':head,'receiver_status':receipt['outcome']['status'],'acknowledge_retirements':False,'instance_role':receipt['instance_role'],'instance_label':receipt['instance_label'],'workplan':{'id':planid,'status':plan['status'],'record_id':'KEY-WP-0039'},'task_bindings':rows,'all_three_source_blocks_unchanged':True,'frontmatter_unchanged':True,'only_committed_path':baseline['path'],'remote_forge_matches':True,'owner_worktree_and_index_clean':not git('status','--porcelain=v1'),'normal_sync_refusal':'unrelated grandfathered ADHOC-2026-09-27-T02 UUID; not modified','native_acceptance_inferred':False} +assert plan['status']==baseline['frontmatter']['status'] +(out/'final-verification.json').write_text(json.dumps(verification,indent=2)+'\n');print(json.dumps(verification,indent=2)) diff --git a/docs/changes/netkingdom-target-2026-10-17/key39-finalization-owner-followthrough-r1/consistency-backup/.custodian-brief.md b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-owner-followthrough-r1/consistency-backup/.custodian-brief.md new file mode 100644 index 00000000..d3b36c3b --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-owner-followthrough-r1/consistency-backup/.custodian-brief.md @@ -0,0 +1,37 @@ + +# Custodian Brief — key-cape + +**Domain:** infotech +**Last synced:** 2026-10-17 02:16 UTC +**State Hub:** http://127.0.0.1:8000 *(adjust if running on a remote machine)* + +## Active Workstreams + +### Negative-check tokens for the secrets-engine OpenBao JWT role +Progress: 2/3 done | workplan_id: `09c3bddf-6058-5603-8488-cccdb7be4f86` + +**Open tasks:** +- ! T01 — Define the probe registrations `03da4c38` + *(wait: Ordered identity gate: Forgejo first SecurityCase registration/claims proof, then NetKingdom registry session and attended human/coding-agent custody; registration authorization already resolved.)* + +### Forgejo as a NetKingdom OIDC client, so the forge can close +Progress: 2/4 done | workplan_id: `098e3e2c-a138-560e-b2d3-3a15eb49d8a9` + +**Open tasks:** +- ! T01 — Define what Forgejo receives `ddcfb482` + *(wait: Registration runs as the first KnownSecurityCase of type oidc-browser-relying-party (NK-WP-0048-T06), not as a hand-built rollout.)* +- ! T02 — Register the client and hand over its secret `40ddaa29` + *(wait: Claims are verified against the registered client.)* + +## Inbox Hygiene + +**Stale unread:** 2 message(s) older than 3 day(s) — triage at session start. +**Missing thread_id:** 2 unread message(s) lack supersession chains. + +--- +## MCP Orientation (when available) + +If the state-hub MCP server is reachable, call: +`get_domain_summary("infotech")` +This provides richer cross-domain context. +If the MCP call fails, use this file as your orientation source. diff --git a/docs/changes/netkingdom-target-2026-10-17/key39-finalization-owner-followthrough-r1/consistency-backup/WORK-RECORDS.md b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-owner-followthrough-r1/consistency-backup/WORK-RECORDS.md new file mode 100644 index 00000000..d86b29bb --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-owner-followthrough-r1/consistency-backup/WORK-RECORDS.md @@ -0,0 +1,224 @@ +# Work Records — key-cape + +> Generated by `statehub fix-consistency` (CUST-WP-0061-T04, work-record +> stage 3). Do not edit by hand — edit the source file/block listed for +> each record and re-run fix-consistency to refresh this index. Archived +> workplans are omitted; closed decisions/intakes/engagements stay listed +> so recently-resolved work is still visible. [auto] + +| Kind | ID | Status | Lane | Source | +| --- | --- | --- | --- | --- | +| workplan | ADHOC-2026-09-05 | finished | — | workplans/ADHOC-2026-09-05.md | +| workplan | ADHOC-2026-09-07 | finished | — | workplans/ADHOC-2026-09-07.md | +| workplan | ADHOC-2026-09-27 | finished | — | workplans/ADHOC-2026-09-27.md | +| workplan | KEY-WP-0001 | finished | — | workplans/KEY-WP-0001-keycape-implementation.md | +| workplan | KEY-WP-0002 | finished | — | workplans/KEY-WP-0002-container-image-gitea.md | +| workplan | KEY-WP-0003 | finished | — | workplans/KEY-WP-0003-bootstrap-console-oidc-mfa-login.md | +| workplan | KEY-WP-0004 | finished | — | workplans/KEY-WP-0004-binky-hedgehog-tenant-onboarding.md | +| workplan | KEY-WP-0005 | finished | — | workplans/KEY-WP-0005-iam-profile-core-claims.md | +| workplan | KEY-WP-0006 | finished | — | workplans/KEY-WP-0006-client-credentials-service-tokens.md | +| workplan | KEY-WP-0007 | finished | — | workplans/KEY-WP-0007-user-engine-portal-oidc-client.md | +| workplan | KEY-WP-0008 | finished | — | workplans/KEY-WP-0008-registration-handoff-and-client-mfa-policy.md | +| workplan | KEY-WP-0009 | finished | — | workplans/KEY-WP-0009-provider-capabilities-and-service-identities.md | +| workplan | KEY-WP-0010 | finished | — | workplans/KEY-WP-0010-openbao-operator-loopback-callback.md | +| workplan | KEY-WP-0011 | finished | — | workplans/KEY-WP-0011-live-secret-exposure-recovery.md | +| workplan | KEY-WP-0012 | finished | — | workplans/KEY-WP-0012-userinfo-canonical-subject-resolution.md | +| workplan | KEY-WP-0013 | finished | — | workplans/KEY-WP-0013-approval-engine-resource-audience.md | +| workplan | KEY-WP-0014 | finished | — | workplans/KEY-WP-0014-native-credential-lane-handoff.md | +| workplan | KEY-WP-0015 | finished | — | workplans/KEY-WP-0015-scope-intent-assessment.md | +| workplan | KEY-WP-0016 | finished | — | workplans/KEY-WP-0016-authorization-code-protocol-hardening.md | +| workplan | KEY-WP-0017 | finished | — | workplans/KEY-WP-0017-canonical-model-and-discovery-conformance.md | +| workplan | KEY-WP-0018 | finished | — | workplans/KEY-WP-0018-export-completeness-evidence.md | +| workplan | KEY-WP-0019 | finished | — | workplans/KEY-WP-0019-upstream-provider-token-verification.md | +| workplan | KEY-WP-0020 | finished | — | workplans/KEY-WP-0020-migration-contract-preservation.md | +| workplan | KEY-WP-0021 | finished | — | workplans/KEY-WP-0021-snapshot-attribute-validation.md | +| workplan | KEY-WP-0022 | finished | — | workplans/KEY-WP-0022-replacement-harness-and-external-conformance.md | +| workplan | KEY-WP-0023 | finished | — | workplans/KEY-WP-0023-live-migration-proof.md | +| workplan | KEY-WP-0024 | finished | — | workplans/KEY-WP-0024-tenant-roles-opt-in-wiring.md | +| workplan | KEY-WP-0025 | finished | — | workplans/KEY-WP-0025-runtime-lifecycle-and-readiness.md | +| workplan | KEY-WP-0026 | finished | — | workplans/KEY-WP-0026-packaging-bootstrap-and-credential-handling.md | +| workplan | KEY-WP-0027 | finished | — | workplans/KEY-WP-0027-rollout-readiness-and-live-state.md | +| workplan | KEY-WP-0028 | finished | — | workplans/KEY-WP-0028-browser-client-field-validation.md | +| workplan | KEY-WP-0029 | finished | — | workplans/KEY-WP-0029-scope-reassessment.md | +| workplan | KEY-WP-0030 | finished | — | workplans/KEY-WP-0030-tenant-precondition-guard.md | +| workplan | KEY-WP-0031 | finished | — | workplans/KEY-WP-0031-gate-house-tenant-ruling.md | +| workplan | KEY-WP-0032 | finished | — | workplans/KEY-WP-0032-principal-type-provenance-guard.md | +| workplan | KEY-WP-0033 | finished | — | workplans/KEY-WP-0033-vergabe-fresh-login.md | +| workplan | KEY-WP-0034 | finished | — | workplans/KEY-WP-0034-account-recovery.md | +| workplan | KEY-WP-0035 | finished | — | workplans/KEY-WP-0035-optional-mfa.md | +| workplan | KEY-WP-0036 | finished | — | workplans/KEY-WP-0036-platform-factor-recovery.md | +| workplan | KEY-WP-0037 | finished | — | workplans/KEY-WP-0037-secrets-engine-openbao-registration-custody.md | +| workplan | KEY-WP-0038 | active | — | workplans/KEY-WP-0038-forgejo-oidc-client.md | +| workplan | KEY-WP-0039 | active | — | workplans/KEY-WP-0039-openbao-negative-probe-tokens.md | +| workplan | KEY-WP-0040 | finished | — | workplans/KEY-WP-0040-mfa-attended-run-findings.md | +| workplan | KEY-WP-0041 | finished | — | workplans/KEY-WP-0041-sign-in-diagnosis-page.md | +| task | ADHOC-2026-09-05-T01 | done | — | workplans/ADHOC-2026-09-05.md | +| task | ADHOC-2026-09-07-T01 | done | — | workplans/ADHOC-2026-09-07.md | +| task | ADHOC-2026-09-27-T01 | done | — | workplans/ADHOC-2026-09-27.md | +| task | ADHOC-2026-09-27-T02 | done | — | workplans/ADHOC-2026-09-27.md | +| task | KEY-WP-0001-T01 | done | — | workplans/KEY-WP-0001-keycape-implementation.md | +| task | KEY-WP-0001-T02 | done | — | workplans/KEY-WP-0001-keycape-implementation.md | +| task | KEY-WP-0001-T03 | done | — | workplans/KEY-WP-0001-keycape-implementation.md | +| task | KEY-WP-0001-T04 | done | — | workplans/KEY-WP-0001-keycape-implementation.md | +| task | KEY-WP-0001-T05 | done | — | workplans/KEY-WP-0001-keycape-implementation.md | +| task | KEY-WP-0001-T06 | done | — | workplans/KEY-WP-0001-keycape-implementation.md | +| task | KEY-WP-0001-T07 | done | — | workplans/KEY-WP-0001-keycape-implementation.md | +| task | KEY-WP-0001-T08 | done | — | workplans/KEY-WP-0001-keycape-implementation.md | +| task | KEY-WP-0001-T09 | done | — | workplans/KEY-WP-0001-keycape-implementation.md | +| task | KEY-WP-0001-T10 | done | — | workplans/KEY-WP-0001-keycape-implementation.md | +| task | KEY-WP-0001-T11 | done | — | workplans/KEY-WP-0001-keycape-implementation.md | +| task | KEY-WP-0001-T12 | done | — | workplans/KEY-WP-0001-keycape-implementation.md | +| task | KEY-WP-0001-T13 | done | — | workplans/KEY-WP-0001-keycape-implementation.md | +| task | KEY-WP-0001-T14 | done | — | workplans/KEY-WP-0001-keycape-implementation.md | +| task | KEY-WP-0001-T15 | done | — | workplans/KEY-WP-0001-keycape-implementation.md | +| task | KEY-WP-0001-T16 | done | — | workplans/KEY-WP-0001-keycape-implementation.md | +| task | KEY-WP-0001-T17 | done | — | workplans/KEY-WP-0001-keycape-implementation.md | +| task | KEY-WP-0001-T18 | done | — | workplans/KEY-WP-0001-keycape-implementation.md | +| task | KEY-WP-0001-T19 | done | — | workplans/KEY-WP-0001-keycape-implementation.md | +| task | KEY-WP-0001-T20 | done | — | workplans/KEY-WP-0001-keycape-implementation.md | +| task | KEY-WP-0001-T21 | done | — | workplans/KEY-WP-0001-keycape-implementation.md | +| task | KEY-WP-0001-T22 | done | — | workplans/KEY-WP-0001-keycape-implementation.md | +| task | KEY-WP-0001-T23 | done | — | workplans/KEY-WP-0001-keycape-implementation.md | +| task | KEY-WP-0002-T01 | done | — | workplans/KEY-WP-0002-container-image-gitea.md | +| task | KEY-WP-0002-T02 | done | — | workplans/KEY-WP-0002-container-image-gitea.md | +| task | KEY-WP-0002-T03 | done | — | workplans/KEY-WP-0002-container-image-gitea.md | +| task | KEY-WP-0002-T04 | done | — | workplans/KEY-WP-0002-container-image-gitea.md | +| task | KEY-WP-0002-T05 | done | — | workplans/KEY-WP-0002-container-image-gitea.md | +| task | KEY-WP-0002-T06 | done | — | workplans/KEY-WP-0002-container-image-gitea.md | +| task | KEY-WP-0003-T01 | done | — | workplans/KEY-WP-0003-bootstrap-console-oidc-mfa-login.md | +| task | KEY-WP-0003-T02 | done | — | workplans/KEY-WP-0003-bootstrap-console-oidc-mfa-login.md | +| task | KEY-WP-0003-T03 | done | — | workplans/KEY-WP-0003-bootstrap-console-oidc-mfa-login.md | +| task | KEY-WP-0003-T04 | done | — | workplans/KEY-WP-0003-bootstrap-console-oidc-mfa-login.md | +| task | KEY-WP-0003-T05 | done | — | workplans/KEY-WP-0003-bootstrap-console-oidc-mfa-login.md | +| task | KEY-WP-0004-T01 | done | — | workplans/KEY-WP-0004-binky-hedgehog-tenant-onboarding.md | +| task | KEY-WP-0004-T02 | done | — | workplans/KEY-WP-0004-binky-hedgehog-tenant-onboarding.md | +| task | KEY-WP-0004-T03 | done | — | workplans/KEY-WP-0004-binky-hedgehog-tenant-onboarding.md | +| task | KEY-WP-0004-T04 | done | — | workplans/KEY-WP-0004-binky-hedgehog-tenant-onboarding.md | +| task | KEY-WP-0004-T05 | done | — | workplans/KEY-WP-0004-binky-hedgehog-tenant-onboarding.md | +| task | KEY-WP-0004-T06 | cancel | — | workplans/KEY-WP-0004-binky-hedgehog-tenant-onboarding.md | +| task | KEY-WP-0004-T07 | done | — | workplans/KEY-WP-0004-binky-hedgehog-tenant-onboarding.md | +| task | KEY-WP-0005-T01 | done | — | workplans/KEY-WP-0005-iam-profile-core-claims.md | +| task | KEY-WP-0005-T02 | done | — | workplans/KEY-WP-0005-iam-profile-core-claims.md | +| task | KEY-WP-0005-T03 | done | — | workplans/KEY-WP-0005-iam-profile-core-claims.md | +| task | KEY-WP-0006-T01 | done | — | workplans/KEY-WP-0006-client-credentials-service-tokens.md | +| task | KEY-WP-0006-T02 | done | — | workplans/KEY-WP-0006-client-credentials-service-tokens.md | +| task | KEY-WP-0006-T03 | done | — | workplans/KEY-WP-0006-client-credentials-service-tokens.md | +| task | KEY-WP-0006-T04 | done | — | workplans/KEY-WP-0006-client-credentials-service-tokens.md | +| task | KEY-WP-0006-T05 | done | — | workplans/KEY-WP-0006-client-credentials-service-tokens.md | +| task | KEY-WP-0007-T01 | done | — | workplans/KEY-WP-0007-user-engine-portal-oidc-client.md | +| task | KEY-WP-0007-T02 | done | — | workplans/KEY-WP-0007-user-engine-portal-oidc-client.md | +| task | KEY-WP-0007-T03 | done | — | workplans/KEY-WP-0007-user-engine-portal-oidc-client.md | +| task | KEY-WP-0008-T01 | done | — | workplans/KEY-WP-0008-registration-handoff-and-client-mfa-policy.md | +| task | KEY-WP-0008-T02 | done | — | workplans/KEY-WP-0008-registration-handoff-and-client-mfa-policy.md | +| task | KEY-WP-0008-T03 | done | — | workplans/KEY-WP-0008-registration-handoff-and-client-mfa-policy.md | +| task | KEY-WP-0008-T04 | done | — | workplans/KEY-WP-0008-registration-handoff-and-client-mfa-policy.md | +| task | KEY-WP-0009-T01 | done | — | workplans/KEY-WP-0009-provider-capabilities-and-service-identities.md | +| task | KEY-WP-0009-T02 | done | — | workplans/KEY-WP-0009-provider-capabilities-and-service-identities.md | +| task | KEY-WP-0009-T03 | done | — | workplans/KEY-WP-0009-provider-capabilities-and-service-identities.md | +| task | KEY-WP-0009-T04 | done | — | workplans/KEY-WP-0009-provider-capabilities-and-service-identities.md | +| task | KEY-WP-0010-T01 | done | — | workplans/KEY-WP-0010-openbao-operator-loopback-callback.md | +| task | KEY-WP-0011-T01 | done | — | workplans/KEY-WP-0011-live-secret-exposure-recovery.md | +| task | KEY-WP-0011-T02 | done | — | workplans/KEY-WP-0011-live-secret-exposure-recovery.md | +| task | KEY-WP-0011-T03 | done | — | workplans/KEY-WP-0011-live-secret-exposure-recovery.md | +| task | KEY-WP-0012-T01 | done | — | workplans/KEY-WP-0012-userinfo-canonical-subject-resolution.md | +| task | KEY-WP-0012-T02 | done | — | workplans/KEY-WP-0012-userinfo-canonical-subject-resolution.md | +| task | KEY-WP-0012-T03 | done | — | workplans/KEY-WP-0012-userinfo-canonical-subject-resolution.md | +| task | KEY-WP-0013-T01 | done | — | workplans/KEY-WP-0013-approval-engine-resource-audience.md | +| task | KEY-WP-0013-T02 | done | — | workplans/KEY-WP-0013-approval-engine-resource-audience.md | +| task | KEY-WP-0013-T03 | done | — | workplans/KEY-WP-0013-approval-engine-resource-audience.md | +| task | KEY-WP-0013-T04 | done | — | workplans/KEY-WP-0013-approval-engine-resource-audience.md | +| task | KEY-WP-0013-T05 | done | — | workplans/KEY-WP-0013-approval-engine-resource-audience.md | +| task | KEY-WP-0013-T06 | done | — | workplans/KEY-WP-0013-approval-engine-resource-audience.md | +| task | KEY-WP-0013-T07 | done | — | workplans/KEY-WP-0013-approval-engine-resource-audience.md | +| task | KEY-WP-0014-T01 | done | — | workplans/KEY-WP-0014-native-credential-lane-handoff.md | +| task | KEY-WP-0014-T02 | done | — | workplans/KEY-WP-0014-native-credential-lane-handoff.md | +| task | KEY-WP-0014-T03 | done | — | workplans/KEY-WP-0014-native-credential-lane-handoff.md | +| task | KEY-WP-0014-T04 | done | — | workplans/KEY-WP-0014-native-credential-lane-handoff.md | +| task | KEY-WP-0015-T01 | done | — | workplans/KEY-WP-0015-scope-intent-assessment.md | +| task | KEY-WP-0016-T01 | done | — | workplans/KEY-WP-0016-authorization-code-protocol-hardening.md | +| task | KEY-WP-0016-T02 | done | — | workplans/KEY-WP-0016-authorization-code-protocol-hardening.md | +| task | KEY-WP-0016-T03 | done | — | workplans/KEY-WP-0016-authorization-code-protocol-hardening.md | +| task | KEY-WP-0016-T04 | done | — | workplans/KEY-WP-0016-authorization-code-protocol-hardening.md | +| task | KEY-WP-0017-T01 | done | — | workplans/KEY-WP-0017-canonical-model-and-discovery-conformance.md | +| task | KEY-WP-0017-T02 | done | — | workplans/KEY-WP-0017-canonical-model-and-discovery-conformance.md | +| task | KEY-WP-0017-T03 | done | — | workplans/KEY-WP-0017-canonical-model-and-discovery-conformance.md | +| task | KEY-WP-0017-T04 | done | — | workplans/KEY-WP-0017-canonical-model-and-discovery-conformance.md | +| task | KEY-WP-0018-T01 | done | — | workplans/KEY-WP-0018-export-completeness-evidence.md | +| task | KEY-WP-0018-T02 | done | — | workplans/KEY-WP-0018-export-completeness-evidence.md | +| task | KEY-WP-0018-T03 | done | — | workplans/KEY-WP-0018-export-completeness-evidence.md | +| task | KEY-WP-0019-T01 | done | — | workplans/KEY-WP-0019-upstream-provider-token-verification.md | +| task | KEY-WP-0019-T02 | done | — | workplans/KEY-WP-0019-upstream-provider-token-verification.md | +| task | KEY-WP-0019-T03 | done | — | workplans/KEY-WP-0019-upstream-provider-token-verification.md | +| task | KEY-WP-0019-T04 | done | — | workplans/KEY-WP-0019-upstream-provider-token-verification.md | +| task | KEY-WP-0019-T05 | done | — | workplans/KEY-WP-0019-upstream-provider-token-verification.md | +| task | KEY-WP-0020-T01 | done | — | workplans/KEY-WP-0020-migration-contract-preservation.md | +| task | KEY-WP-0020-T02 | done | — | workplans/KEY-WP-0020-migration-contract-preservation.md | +| task | KEY-WP-0020-T03 | done | — | workplans/KEY-WP-0020-migration-contract-preservation.md | +| task | KEY-WP-0021-T01 | done | — | workplans/KEY-WP-0021-snapshot-attribute-validation.md | +| task | KEY-WP-0021-T02 | done | — | workplans/KEY-WP-0021-snapshot-attribute-validation.md | +| task | KEY-WP-0021-T03 | done | — | workplans/KEY-WP-0021-snapshot-attribute-validation.md | +| task | KEY-WP-0022-T01 | done | — | workplans/KEY-WP-0022-replacement-harness-and-external-conformance.md | +| task | KEY-WP-0022-T02 | done | — | workplans/KEY-WP-0022-replacement-harness-and-external-conformance.md | +| task | KEY-WP-0022-T03 | done | — | workplans/KEY-WP-0022-replacement-harness-and-external-conformance.md | +| task | KEY-WP-0022-T04 | done | — | workplans/KEY-WP-0022-replacement-harness-and-external-conformance.md | +| task | KEY-WP-0022-T05 | done | — | workplans/KEY-WP-0022-replacement-harness-and-external-conformance.md | +| task | KEY-WP-0023-T01 | done | — | workplans/KEY-WP-0023-live-migration-proof.md | +| task | KEY-WP-0023-T02 | done | — | workplans/KEY-WP-0023-live-migration-proof.md | +| task | KEY-WP-0023-T03 | done | — | workplans/KEY-WP-0023-live-migration-proof.md | +| task | KEY-WP-0023-T04 | done | — | workplans/KEY-WP-0023-live-migration-proof.md | +| task | KEY-WP-0023-T05 | done | — | workplans/KEY-WP-0023-live-migration-proof.md | +| task | KEY-WP-0024-T01 | done | — | workplans/KEY-WP-0024-tenant-roles-opt-in-wiring.md | +| task | KEY-WP-0024-T02 | done | — | workplans/KEY-WP-0024-tenant-roles-opt-in-wiring.md | +| task | KEY-WP-0025-T01 | done | — | workplans/KEY-WP-0025-runtime-lifecycle-and-readiness.md | +| task | KEY-WP-0025-T02 | done | — | workplans/KEY-WP-0025-runtime-lifecycle-and-readiness.md | +| task | KEY-WP-0025-T03 | done | — | workplans/KEY-WP-0025-runtime-lifecycle-and-readiness.md | +| task | KEY-WP-0025-T04 | done | — | workplans/KEY-WP-0025-runtime-lifecycle-and-readiness.md | +| task | KEY-WP-0026-T01 | done | — | workplans/KEY-WP-0026-packaging-bootstrap-and-credential-handling.md | +| task | KEY-WP-0026-T02 | done | — | workplans/KEY-WP-0026-packaging-bootstrap-and-credential-handling.md | +| task | KEY-WP-0026-T03 | done | — | workplans/KEY-WP-0026-packaging-bootstrap-and-credential-handling.md | +| task | KEY-WP-0026-T04 | done | — | workplans/KEY-WP-0026-packaging-bootstrap-and-credential-handling.md | +| task | KEY-WP-0026-T05 | done | — | workplans/KEY-WP-0026-packaging-bootstrap-and-credential-handling.md | +| task | KEY-WP-0026-T06 | done | — | workplans/KEY-WP-0026-packaging-bootstrap-and-credential-handling.md | +| task | KEY-WP-0027-T01 | done | — | workplans/KEY-WP-0027-rollout-readiness-and-live-state.md | +| task | KEY-WP-0027-T02 | done | — | workplans/KEY-WP-0027-rollout-readiness-and-live-state.md | +| task | KEY-WP-0027-T03 | done | — | workplans/KEY-WP-0027-rollout-readiness-and-live-state.md | +| task | KEY-WP-0027-T04 | done | — | workplans/KEY-WP-0027-rollout-readiness-and-live-state.md | +| task | KEY-WP-0028-T01 | done | — | workplans/KEY-WP-0028-browser-client-field-validation.md | +| task | KEY-WP-0028-T02 | done | — | workplans/KEY-WP-0028-browser-client-field-validation.md | +| task | KEY-WP-0029-T01 | done | — | workplans/KEY-WP-0029-scope-reassessment.md | +| task | KEY-WP-0029-T02 | done | — | workplans/KEY-WP-0029-scope-reassessment.md | +| task | KEY-WP-0029-T03 | done | — | workplans/KEY-WP-0029-scope-reassessment.md | +| task | KEY-WP-0030-T01 | done | — | workplans/KEY-WP-0030-tenant-precondition-guard.md | +| task | KEY-WP-0030-T02 | done | — | workplans/KEY-WP-0030-tenant-precondition-guard.md | +| task | KEY-WP-0031-T01 | done | — | workplans/KEY-WP-0031-gate-house-tenant-ruling.md | +| task | KEY-WP-0031-T02 | done | — | workplans/KEY-WP-0031-gate-house-tenant-ruling.md | +| task | KEY-WP-0032-T01 | done | — | workplans/KEY-WP-0032-principal-type-provenance-guard.md | +| task | KEY-WP-0032-T02 | done | — | workplans/KEY-WP-0032-principal-type-provenance-guard.md | +| task | KEY-WP-0033-T01 | done | — | workplans/KEY-WP-0033-vergabe-fresh-login.md | +| task | KEY-WP-0033-T02 | done | — | workplans/KEY-WP-0033-vergabe-fresh-login.md | +| task | KEY-WP-0034-T01 | done | — | workplans/KEY-WP-0034-account-recovery.md | +| task | KEY-WP-0034-T02 | done | — | workplans/KEY-WP-0034-account-recovery.md | +| task | KEY-WP-0035-T01 | done | — | workplans/KEY-WP-0035-optional-mfa.md | +| task | KEY-WP-0035-T02 | done | — | workplans/KEY-WP-0035-optional-mfa.md | +| task | KEY-WP-0035-T03 | done | — | workplans/KEY-WP-0035-optional-mfa.md | +| task | KEY-WP-0035-T04 | done | — | workplans/KEY-WP-0035-optional-mfa.md | +| task | KEY-WP-0035-T05 | done | — | workplans/KEY-WP-0035-optional-mfa.md | +| task | KEY-WP-0036-T01 | done | — | workplans/KEY-WP-0036-platform-factor-recovery.md | +| task | KEY-WP-0036-T02 | cancel | — | workplans/KEY-WP-0036-platform-factor-recovery.md | +| task | KEY-WP-0036-T03 | done | — | workplans/KEY-WP-0036-platform-factor-recovery.md | +| task | KEY-WP-0037-T01 | done | — | workplans/KEY-WP-0037-secrets-engine-openbao-registration-custody.md | +| task | KEY-WP-0037-T02 | done | — | workplans/KEY-WP-0037-secrets-engine-openbao-registration-custody.md | +| task | KEY-WP-0037-T03 | done | — | workplans/KEY-WP-0037-secrets-engine-openbao-registration-custody.md | +| task | KEY-WP-0037-T04 | done | — | workplans/KEY-WP-0037-secrets-engine-openbao-registration-custody.md | +| task | KEY-WP-0038-T01 | done | — | workplans/KEY-WP-0038-forgejo-oidc-client.md | +| task | KEY-WP-0038-T02 | wait | — | workplans/KEY-WP-0038-forgejo-oidc-client.md | +| task | KEY-WP-0038-T03 | wait | — | workplans/KEY-WP-0038-forgejo-oidc-client.md | +| task | KEY-WP-0038-T04 | cancel | — | workplans/KEY-WP-0038-forgejo-oidc-client.md | +| task | KEY-WP-0039-T01 | done | — | workplans/KEY-WP-0039-openbao-negative-probe-tokens.md | +| task | KEY-WP-0039-T02 | wait | — | workplans/KEY-WP-0039-openbao-negative-probe-tokens.md | +| task | KEY-WP-0039-T03 | done | — | workplans/KEY-WP-0039-openbao-negative-probe-tokens.md | +| task | KEY-WP-0040-T01 | done | — | workplans/KEY-WP-0040-mfa-attended-run-findings.md | +| task | KEY-WP-0040-T02 | done | — | workplans/KEY-WP-0040-mfa-attended-run-findings.md | +| task | KEY-WP-0041-T01 | done | — | workplans/KEY-WP-0041-sign-in-diagnosis-page.md | +| task | KEY-WP-0041-T02 | done | — | workplans/KEY-WP-0041-sign-in-diagnosis-page.md | +| intake | KEY-IN-0001 | closed | — | intakes/intakes.md | diff --git a/docs/changes/netkingdom-target-2026-10-17/key39-finalization-owner-followthrough-r1/consistency-backup/originals.json b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-owner-followthrough-r1/consistency-backup/originals.json new file mode 100644 index 00000000..1ba4225b --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-owner-followthrough-r1/consistency-backup/originals.json @@ -0,0 +1,232 @@ +{ + ".custodian-brief.md": { + "sha256": "96d84539b3a92786bff37fcc70161adec4648b9547287e52e6b817bc080a080a", + "mode": 436, + "index": "100644 d3b36c3bffbeb922769a2a788e35b4113fccaa3f 0\t.custodian-brief.md\n" + }, + "WORK-RECORDS.md": { + "sha256": "431842547bd82750f284f8c1c9f519892f1bb1c1a684b9b1e38adc6fdfdc299d", + "mode": 420, + "index": "100644 d86b29bbc480acfa6d12d5052039578d7d4e380c 0\tWORK-RECORDS.md\n" + }, + "workplans/ADHOC-2026-09-05.md": { + "sha256": "76123d22d0c1efc51212f8914e386f078b3c6d908959af6978ae73874e492b04", + "mode": 420, + "index": "100644 f7e78675df62c9b48e29f882fa99298d6d8ce7f1 0\tworkplans/ADHOC-2026-09-05.md\n" + }, + "workplans/ADHOC-2026-09-07.md": { + "sha256": "c335ebfe91dad291277e8f17034ac8f785b3bc767f281f205c446c08f03d7978", + "mode": 420, + "index": "100644 17401073c053c9710e6e680243bbd313b08d1fc1 0\tworkplans/ADHOC-2026-09-07.md\n" + }, + "workplans/ADHOC-2026-09-27.md": { + "sha256": "2794d1940fc3c5e4a2df57de9d4b742fa6aa8602b67f1d3177c0d96bc5aaf314", + "mode": 420, + "index": "100644 0b755547aa7d5589f0898df2f102a45b6aa24838 0\tworkplans/ADHOC-2026-09-27.md\n" + }, + "workplans/KEY-WP-0001-keycape-implementation.md": { + "sha256": "29a2bf4892b7185e28e9126a3d8311d0fed34a876f416a3e75c605915fde0969", + "mode": 420, + "index": "100644 74334411cd056e7c7253e84f1c4b40c42eb13de3 0\tworkplans/KEY-WP-0001-keycape-implementation.md\n" + }, + "workplans/KEY-WP-0002-container-image-gitea.md": { + "sha256": "76fee187185b30219bc257ccc9b587661df4489574a2bb57703a365a5d803cec", + "mode": 420, + "index": "100644 418f477c0ede13d7c003d744abf234347f5d5e31 0\tworkplans/KEY-WP-0002-container-image-gitea.md\n" + }, + "workplans/KEY-WP-0003-bootstrap-console-oidc-mfa-login.md": { + "sha256": "5e949c696e587540d305b682eb1f8390ec544bbec6a566be8d04e73fb1b254ca", + "mode": 420, + "index": "100644 9896c67d78f0efdd03325535eb41295250be3a19 0\tworkplans/KEY-WP-0003-bootstrap-console-oidc-mfa-login.md\n" + }, + "workplans/KEY-WP-0004-binky-hedgehog-tenant-onboarding.md": { + "sha256": "260ba7d3116db3bc5a72e5cd8b9984137e7e372c8128d21073614dbd948c10c2", + "mode": 420, + "index": "100644 a50e910558ee43073c92cb27865a1aceac990dd5 0\tworkplans/KEY-WP-0004-binky-hedgehog-tenant-onboarding.md\n" + }, + "workplans/KEY-WP-0005-iam-profile-core-claims.md": { + "sha256": "c6702b644cdd046c0d3bba8ba8837050826e4dc3d855bd73327ae94218ba5b57", + "mode": 420, + "index": "100644 648d2405699dcf25b3121df6c46d871597100d7b 0\tworkplans/KEY-WP-0005-iam-profile-core-claims.md\n" + }, + "workplans/KEY-WP-0006-client-credentials-service-tokens.md": { + "sha256": "b3227849978c95cad89e02afb74bf2217b6188ae65c5b0af6a21703d65477701", + "mode": 420, + "index": "100644 632c4c2cad39cbde55c2ae1f2ba5ecf2f445b1a0 0\tworkplans/KEY-WP-0006-client-credentials-service-tokens.md\n" + }, + "workplans/KEY-WP-0007-user-engine-portal-oidc-client.md": { + "sha256": "5408ff6dfeb58a8a7756b48fe4f54f7c7b98d7940cb9aaa5c7864f1247e58150", + "mode": 420, + "index": "100644 f1b04e1a61b398426c23d8d9ca655b3937d7bd10 0\tworkplans/KEY-WP-0007-user-engine-portal-oidc-client.md\n" + }, + "workplans/KEY-WP-0008-registration-handoff-and-client-mfa-policy.md": { + "sha256": "bfc9b2118f0712e92a86bbb0f14a2857fafc3a42a3c14c0e2535002aaa57a2f5", + "mode": 420, + "index": "100644 438ab5480de3bd66d75b6ec7e09639d1b93e3e5f 0\tworkplans/KEY-WP-0008-registration-handoff-and-client-mfa-policy.md\n" + }, + "workplans/KEY-WP-0009-provider-capabilities-and-service-identities.md": { + "sha256": "d1daac9815930104a392ea96c14922e34f947720a31d1b0ac45a770997e0f476", + "mode": 420, + "index": "100644 c9e585db65424c7c4e3b5adf37a68de2bdfbdc1c 0\tworkplans/KEY-WP-0009-provider-capabilities-and-service-identities.md\n" + }, + "workplans/KEY-WP-0010-openbao-operator-loopback-callback.md": { + "sha256": "41d28a16bef9313b776c5c6305316a9e7d4f5528b7fb7427b8384a0ed0ad35c4", + "mode": 420, + "index": "100644 81614eeb8c98205d6e5eb3201da10e36b76a21e9 0\tworkplans/KEY-WP-0010-openbao-operator-loopback-callback.md\n" + }, + "workplans/KEY-WP-0011-live-secret-exposure-recovery.md": { + "sha256": "3c5458180fe81a04e357f8db737e4287f79640b5ab02682bcadc595b73f846cd", + "mode": 420, + "index": "100644 f7b906f552f6e3279895a90afbe216b6e78ee5ff 0\tworkplans/KEY-WP-0011-live-secret-exposure-recovery.md\n" + }, + "workplans/KEY-WP-0012-userinfo-canonical-subject-resolution.md": { + "sha256": "68d34d75c914e7d2d8177000e800d6372cccda2b850c03ab85da42b90a66b91c", + "mode": 420, + "index": "100644 0f3deb626e5de9a9f898754ef81d3f2ee5282792 0\tworkplans/KEY-WP-0012-userinfo-canonical-subject-resolution.md\n" + }, + "workplans/KEY-WP-0013-approval-engine-resource-audience.md": { + "sha256": "55c88bf4ed9a456db59b319bbaf960dfe6f4147437e339f950e0583d7582057a", + "mode": 420, + "index": "100644 7d77aee3caf3d79fe6d6c1369249c6d94e0ae123 0\tworkplans/KEY-WP-0013-approval-engine-resource-audience.md\n" + }, + "workplans/KEY-WP-0014-native-credential-lane-handoff.md": { + "sha256": "22aac43ae5039aebd11f9f0aa57a882ad009ad6ed119ec08eb85b32a5af48cd7", + "mode": 420, + "index": "100644 bacf04c07f59125a90c519d3c306a7414cb9a1a3 0\tworkplans/KEY-WP-0014-native-credential-lane-handoff.md\n" + }, + "workplans/KEY-WP-0015-scope-intent-assessment.md": { + "sha256": "60b9f2265eff880d916c3ee8dba36aec9984ec310804de776cebf89cc98fdb95", + "mode": 420, + "index": "100644 82b952a216346ffb7f5cab413623fe4a88f7700e 0\tworkplans/KEY-WP-0015-scope-intent-assessment.md\n" + }, + "workplans/KEY-WP-0016-authorization-code-protocol-hardening.md": { + "sha256": "ed48128a1cb51cbe3d154727bafd5a5ffb3b5b618c20649b2a0e7db154cf3a0c", + "mode": 420, + "index": "100644 52839bd0f83d97656c8ad97a8ffc338d9019239a 0\tworkplans/KEY-WP-0016-authorization-code-protocol-hardening.md\n" + }, + "workplans/KEY-WP-0017-canonical-model-and-discovery-conformance.md": { + "sha256": "8700e0e5aa07dcd7f651e792cca12a0330b7ee9d2f6064b4cc3fcf4c7d509340", + "mode": 420, + "index": "100644 00eb03971aae75724afbf7b8a4946ce6b7eda3af 0\tworkplans/KEY-WP-0017-canonical-model-and-discovery-conformance.md\n" + }, + "workplans/KEY-WP-0018-export-completeness-evidence.md": { + "sha256": "15026b708d116f803b0b2fa43f30a4dbced76e4d8137b5fcb5b4275e593d7c7b", + "mode": 420, + "index": "100644 325fc483d62dbad53b92b986de56112168a92b4a 0\tworkplans/KEY-WP-0018-export-completeness-evidence.md\n" + }, + "workplans/KEY-WP-0019-upstream-provider-token-verification.md": { + "sha256": "0d2d8b5bda1e0d333935812d8d3749cb7b0c5523a3987d1b26185f9f6d0f0fa2", + "mode": 420, + "index": "100644 dfeb92296f46d4ce2ac7df1fedcc61e9ccc5d6b3 0\tworkplans/KEY-WP-0019-upstream-provider-token-verification.md\n" + }, + "workplans/KEY-WP-0020-migration-contract-preservation.md": { + "sha256": "cc842d15bf42ddc4d0f4056bc19b3176cab2c5f3c1a2fe6d648e5dbd077be8b3", + "mode": 420, + "index": "100644 7b6f3be976293469dee2180c10e16f313876d640 0\tworkplans/KEY-WP-0020-migration-contract-preservation.md\n" + }, + "workplans/KEY-WP-0021-snapshot-attribute-validation.md": { + "sha256": "b603248693073a9b9b25b4d0a4147d8d88c16a07dc9936e48fa1f3c76fe8bab6", + "mode": 420, + "index": "100644 1f6efeb32a6eb8fcbc52f4d3b591e2fc6b9e006d 0\tworkplans/KEY-WP-0021-snapshot-attribute-validation.md\n" + }, + "workplans/KEY-WP-0022-replacement-harness-and-external-conformance.md": { + "sha256": "8c135e84be81de31b4949f52ac28dccd404f2e4c3eaa576d733bd50160427a53", + "mode": 420, + "index": "100644 7c69629ecd20d0158aa1332d9d1442b796f6a7af 0\tworkplans/KEY-WP-0022-replacement-harness-and-external-conformance.md\n" + }, + "workplans/KEY-WP-0023-live-migration-proof.md": { + "sha256": "e581cd11f3905805b5e8ba8e2ede85092577c7d086e1761f2a2d033fdead4e47", + "mode": 420, + "index": "100644 3e7c83cb12e7fc31e682cf3c27f4c4767ded2905 0\tworkplans/KEY-WP-0023-live-migration-proof.md\n" + }, + "workplans/KEY-WP-0024-tenant-roles-opt-in-wiring.md": { + "sha256": "af7842fa97263bfd46a4b5ae84c5c1ecddb06ab9623586e0292dcc93099fea35", + "mode": 420, + "index": "100644 baa0bbd195a92708f2befd027b938fcd1812ced7 0\tworkplans/KEY-WP-0024-tenant-roles-opt-in-wiring.md\n" + }, + "workplans/KEY-WP-0025-runtime-lifecycle-and-readiness.md": { + "sha256": "0addb34e6d85e376c6a5db7afba597b6f268afa98b86c8c60add9be174dd1b26", + "mode": 420, + "index": "100644 a53e646761178748e8c4b00dcde888698447a948 0\tworkplans/KEY-WP-0025-runtime-lifecycle-and-readiness.md\n" + }, + "workplans/KEY-WP-0026-packaging-bootstrap-and-credential-handling.md": { + "sha256": "c1bc0de47511ae49102009a172c84d9d5d64e829a18c8d40564fe817935563b3", + "mode": 420, + "index": "100644 1f96a2ac3ac939c4e6e0f66172477cf73db04e51 0\tworkplans/KEY-WP-0026-packaging-bootstrap-and-credential-handling.md\n" + }, + "workplans/KEY-WP-0027-rollout-readiness-and-live-state.md": { + "sha256": "f559eae61d433d451f80ea575254bb14a47f2485267b412e42d524b434bb1b59", + "mode": 420, + "index": "100644 57e01b2db033847cc786504b8795e37112d988a7 0\tworkplans/KEY-WP-0027-rollout-readiness-and-live-state.md\n" + }, + "workplans/KEY-WP-0028-browser-client-field-validation.md": { + "sha256": "fbd51422fa5e6449000e4520e6639f6135c7393165a5ae02b1e92892edba0480", + "mode": 420, + "index": "100644 46b0672fea90cd7cb672f7d119706c240474f8ca 0\tworkplans/KEY-WP-0028-browser-client-field-validation.md\n" + }, + "workplans/KEY-WP-0029-scope-reassessment.md": { + "sha256": "3241f3b94db9cad5e3770cf1fedc5c211288357e4f69b5ba9da57065094bdf0e", + "mode": 420, + "index": "100644 392f97750f6cb8b12868a6fd016c7d396b58477e 0\tworkplans/KEY-WP-0029-scope-reassessment.md\n" + }, + "workplans/KEY-WP-0030-tenant-precondition-guard.md": { + "sha256": "c94c3afc7fb4270dfc18f3fe8a32946c524e4a6ba2d02a3d8fda69c9b8441af0", + "mode": 420, + "index": "100644 0f74e0d5a037eec64b11b57c67b02057a3b0bfef 0\tworkplans/KEY-WP-0030-tenant-precondition-guard.md\n" + }, + "workplans/KEY-WP-0031-gate-house-tenant-ruling.md": { + "sha256": "0abb135367f0366aa071769c4bd76fdeb321d810ec75ad3b58d718734f78382b", + "mode": 420, + "index": "100644 d2ede9d494927976ccb9ba6a5aeaf1ed21c30837 0\tworkplans/KEY-WP-0031-gate-house-tenant-ruling.md\n" + }, + "workplans/KEY-WP-0032-principal-type-provenance-guard.md": { + "sha256": "6a5cd483a40b9477227b02156c42b3469094dac145b33727b336118898aa782a", + "mode": 420, + "index": "100644 a04f52f817a51efc1d0d0ecb99efacea8dc53a17 0\tworkplans/KEY-WP-0032-principal-type-provenance-guard.md\n" + }, + "workplans/KEY-WP-0033-vergabe-fresh-login.md": { + "sha256": "c01bb51cf2dcad06ea846040dddef09845041fa7271cd7015e5a34a49e60bb8f", + "mode": 420, + "index": "100644 5b478f953890339aec796c11a536af0ec3238789 0\tworkplans/KEY-WP-0033-vergabe-fresh-login.md\n" + }, + "workplans/KEY-WP-0034-account-recovery.md": { + "sha256": "7db9adcd418ccabfbce672aec283249a6e374212492f0349e8c47a568f130eda", + "mode": 420, + "index": "100644 eb58f99bc26e3c3ac33230dfe432c8b1fabe47ce 0\tworkplans/KEY-WP-0034-account-recovery.md\n" + }, + "workplans/KEY-WP-0035-optional-mfa.md": { + "sha256": "c60ea8d0c195686c0d2317f657aad2de27c9b9432d311627c0e1d06124cabb49", + "mode": 420, + "index": "100644 29eb711f656c91808e91f3981a9168c3a0ebc253 0\tworkplans/KEY-WP-0035-optional-mfa.md\n" + }, + "workplans/KEY-WP-0036-platform-factor-recovery.md": { + "sha256": "5ceb08fe5789b4f02550bb04fcaf8eeb25a43ac3f506dfc62d60a513bcd0013f", + "mode": 420, + "index": "100644 fd296bc05a6e565b0ef6e88a9ae3daa7c9f81786 0\tworkplans/KEY-WP-0036-platform-factor-recovery.md\n" + }, + "workplans/KEY-WP-0037-secrets-engine-openbao-registration-custody.md": { + "sha256": "3a90a0c460fc439570820b96d1ddc3b7f8646c557ca3a8aa27c6d546bb1a845a", + "mode": 420, + "index": "100644 ad7fed77a27aa8487693ad6fcf342502f418427a 0\tworkplans/KEY-WP-0037-secrets-engine-openbao-registration-custody.md\n" + }, + "workplans/KEY-WP-0038-forgejo-oidc-client.md": { + "sha256": "dc8d88d462799e7e6e44da759a973d52e44102a271969a5daaa9d30ec015b064", + "mode": 420, + "index": "100644 8cd5bc66f7b0115eec7813d6eec994a1bb618668 0\tworkplans/KEY-WP-0038-forgejo-oidc-client.md\n" + }, + "workplans/KEY-WP-0039-openbao-negative-probe-tokens.md": { + "sha256": "c576db4b97ea5de0a50e17f251f8d5c57a744ded557bede963e48608bf3a6ca2", + "mode": 420, + "index": "100644 fd0fc46e49164312208afcc2c53549973b56e1fd 0\tworkplans/KEY-WP-0039-openbao-negative-probe-tokens.md\n" + }, + "workplans/KEY-WP-0040-mfa-attended-run-findings.md": { + "sha256": "98b23d8810bff047067169232213cb313ecc14ff79751224842723afe808fbd2", + "mode": 420, + "index": "100644 8e8730c8e7733b13d6a9f42689a9d8e407769e81 0\tworkplans/KEY-WP-0040-mfa-attended-run-findings.md\n" + }, + "workplans/KEY-WP-0041-sign-in-diagnosis-page.md": { + "sha256": "7e44f46acd6b3942c1852b49e0d64b205dfd7d9bb378aed5241b1bf22d7ce88a", + "mode": 420, + "index": "100644 56fd05819e3acd37b8e6cb468ec3d62307f1ae7a 0\tworkplans/KEY-WP-0041-sign-in-diagnosis-page.md\n" + } +} diff --git a/docs/changes/netkingdom-target-2026-10-17/key39-finalization-owner-followthrough-r1/consistency-backup/workplans/ADHOC-2026-09-05.md b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-owner-followthrough-r1/consistency-backup/workplans/ADHOC-2026-09-05.md new file mode 100644 index 00000000..f7e78675 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-owner-followthrough-r1/consistency-backup/workplans/ADHOC-2026-09-05.md @@ -0,0 +1,27 @@ +--- +id: ADHOC-2026-09-05 +type: workplan +title: "Repository review and layer declaration" +domain: infotech +repo: key-cape +status: finished +owner: codex +created: "2026-09-05" +updated: "2026-09-05" +state_hub_workstream_id: "6f5a12f0-5dac-5e02-bbd9-9ab2263264ba" +--- + +## Resolve declaration intake and normalize repository records + +```task +id: ADHOC-2026-09-05-T01 +status: done +priority: low +state_hub_task_id: "54c89abe-2539-550d-bc3a-649af8d4580a" +``` + +Reviewed KEY-IN-0001 against local security-layer-model_v0.4. Declared Tooling +and the authentication/authorization boundary in INTENT.md and closed the intake. +Normalized two legacy completed workplan frontmatter statuses to finished. +Ignored the generated .repo-manager workstation cache. Existing task statuses +already conform to the State Hub task lifecycle canon. diff --git a/docs/changes/netkingdom-target-2026-10-17/key39-finalization-owner-followthrough-r1/consistency-backup/workplans/ADHOC-2026-09-07.md b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-owner-followthrough-r1/consistency-backup/workplans/ADHOC-2026-09-07.md new file mode 100644 index 00000000..17401073 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-owner-followthrough-r1/consistency-backup/workplans/ADHOC-2026-09-07.md @@ -0,0 +1,36 @@ +--- +id: ADHOC-2026-09-07 +type: workplan +title: "Document the KEY-WP-0016 authorization-code bindings for consumers" +domain: infotech +repo: key-cape +status: finished +owner: claude +created: "2026-09-07" +updated: "2026-09-07" +state_hub_workstream_id: "c2fdc5cc-89be-561d-b832-f0b3019ea1c7" +--- + +## Publish the consumer-facing rollout note + +```task +id: ADHOC-2026-09-07-T01 +status: done +priority: medium +state_hub_task_id: "2187cef2-07c0-5ac1-89ca-3bb25a160fa4" +``` + +KEY-WP-0016 changed `/token` and `/userinfo` behaviour without a consumer-facing +note; no document in `docs/` mentioned `redirect_uri` at all, so the change would +have reached a deployment silently. + +Published `docs/authorization-code-bindings.md` stating what an exchange must +send, who is affected and how to roll out. Verified the blast radius rather than +assuming it: all five browser registrations in `config/dev-config.yaml` +(`openbao-admin`, `demo-app`, `netkingdom-bootstrap-console`, +`user-engine-portal`, `coulomb-social`) are `clientType: public` with +`grantTypes: ["authorization_code"]`, so the grant-type and confidential-client +bindings cannot affect them and only the `redirect_uri` requirement can. Named +the realistic failure — a client that sends `redirect_uri` to `/authorize` but +omits it at `/token` — and said plainly that it has not been observed against a +live consumer. Referenced the note from SCOPE.md.