diff --git a/.custodian-brief.md b/.custodian-brief.md index 7de901c..f33d0b3 100644 --- a/.custodian-brief.md +++ b/.custodian-brief.md @@ -2,23 +2,24 @@ # Custodian Brief — key-cape **Domain:** infotech -**Last synced:** 2026-10-11 21:03 UTC +**Last synced:** 2026-10-12 05:31 UTC **State Hub:** http://127.0.0.1:8000 *(adjust if running on a remote machine)* ## Active Workstreams -### Fix findings from the first attended MFA run (factor recovery preview, OTP prompt help) -Progress: 0/2 done | workplan_id: `a67f9d60-9b8f-5111-9fe5-9f64e631b1c3` - -**Open tasks:** -- · Recovery preview rejects valid input (F6) `b385602d` -- · Link the OTP prompt to lost-authenticator help (F3) `3ad155dd` - ### 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` +- ! 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.)* + +### Fix findings from the first attended MFA run (factor recovery preview, OTP prompt help) +Progress: 0/2 done | workplan_id: `a67f9d60-9b8f-5111-9fe5-9f64e631b1c3` + +**Open tasks:** +- ► Recovery preview rejects valid input (F6) `b385602d` +- ► Link the OTP prompt to lost-authenticator help (F3) `3ad155dd` ### Forgejo as a NetKingdom OIDC client, so the forge can close Progress: 2/4 done | workplan_id: `098e3e2c-a138-560e-b2d3-3a15eb49d8a9` diff --git a/WORK-RECORDS.md b/WORK-RECORDS.md index 2a7c960..c3c2092 100644 --- a/WORK-RECORDS.md +++ b/WORK-RECORDS.md @@ -50,7 +50,7 @@ | 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 | ready | — | workplans/KEY-WP-0040-mfa-attended-run-findings.md | +| workplan | KEY-WP-0040 | active | — | workplans/KEY-WP-0040-mfa-attended-run-findings.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 | @@ -214,8 +214,8 @@ | 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 | todo | — | 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 | todo | — | workplans/KEY-WP-0040-mfa-attended-run-findings.md | -| task | KEY-WP-0040-T02 | todo | — | workplans/KEY-WP-0040-mfa-attended-run-findings.md | +| task | KEY-WP-0040-T01 | progress | — | workplans/KEY-WP-0040-mfa-attended-run-findings.md | +| task | KEY-WP-0040-T02 | progress | — | workplans/KEY-WP-0040-mfa-attended-run-findings.md | | intake | KEY-IN-0001 | closed | — | intakes/intakes.md | diff --git a/src/internal/server/oidc/authorize.go b/src/internal/server/oidc/authorize.go index 69e5112..7f976c5 100644 --- a/src/internal/server/oidc/authorize.go +++ b/src/internal/server/oidc/authorize.go @@ -760,6 +760,7 @@ var mfaChallengeTemplate = template.Must(template.New("mfa-challenge").Parse(` +

Code rejected or lost your authenticator?

`)) diff --git a/src/internal/server/oidc/authorize_test.go b/src/internal/server/oidc/authorize_test.go index ade18a6..434b510 100644 --- a/src/internal/server/oidc/authorize_test.go +++ b/src/internal/server/oidc/authorize_test.go @@ -531,11 +531,19 @@ func TestAuthorizeCallback_MFARequired_RendersChallengeWithoutToken(t *testing.T t.Errorf("expected 200 challenge page, got %d (body: %s)", w.Code, w.Body.String()) } body := w.Body.String() - for _, want := range []string{"Verify sign-in", "alice", "Test Client", `name="mfa_token"`} { + for _, want := range []string{ + "Verify sign-in", "alice", "Test Client", `name="mfa_token"`, + `href="https://users.coulomb.social/security"`, + `rel="noreferrer"`, + "Code rejected or lost your authenticator?", + } { if !strings.Contains(body, want) { t.Errorf("challenge page missing %q in body: %s", want, body) } } + if formEnd := strings.Index(body, ""); formEnd < 0 || !strings.Contains(body[formEnd:], `href="https://users.coulomb.social/security"`) { + t.Error("lost-authenticator help link must follow the OTP form and carry no request state") + } if mfa.validateCalls != 0 { t.Errorf("MFA token should not be validated until form submission, got %d calls", mfa.validateCalls) } diff --git a/workplans/KEY-WP-0038-forgejo-oidc-client.md b/workplans/KEY-WP-0038-forgejo-oidc-client.md index ea89b6f..45f9e06 100644 --- a/workplans/KEY-WP-0038-forgejo-oidc-client.md +++ b/workplans/KEY-WP-0038-forgejo-oidc-client.md @@ -9,7 +9,7 @@ flavor: implementation owner: claude-code topic_slug: railiance created: "2026-10-08" -updated: "2026-10-10" +updated: "2026-10-14" depends_on: [] related_repos: - prj-forgejo-org-refactor @@ -43,7 +43,7 @@ Helm release. out, never risk assets. Nothing in this plan changes who can read the forge today. It only makes the NetKingdom path available. -## Tasks +## T01 — Define what Forgejo receives ```task id: KEY-WP-0038-T01 @@ -52,8 +52,6 @@ priority: high state_hub_task_id: "5f019d13-1fa3-5b88-a27a-75a3339ca96e" ``` -### T01 — Define what Forgejo receives - Write the claim contract Forgejo consumes, as a non-secret request packet under `docs/`: @@ -89,6 +87,8 @@ 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 @@ -98,8 +98,6 @@ priority: high state_hub_task_id: "ddcfb482-8f33-5c81-9ce8-84be8fa5dc30" ``` -### T02 — Register the client and hand over its secret - **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 @@ -130,6 +128,8 @@ check-config` (`c528daa`), which register-client runs before the restart. T02 st waits on NK-WP-0048-T06, which needs both executors' light paths (MASON-WP-0010-T03, WARDEN-WP-0044-T03). +## T03 — Prove the claims for each kind of identity + ```task id: KEY-WP-0038-T03 status: wait @@ -139,8 +139,6 @@ priority: high state_hub_task_id: "40ddaa29-7245-508c-ad48-ddc063e25115" ``` -### T03 — Prove the claims for each kind of identity - 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 @@ -153,6 +151,8 @@ all, or receive scoped Forgejo tokens bound to a NetKingdom service identity 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 @@ -160,8 +160,6 @@ priority: high state_hub_task_id: "583cf1f1-16dc-515f-83bc-1efc45024a89" ``` -### T04 — Implement the `keycape-client` entry point - **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 diff --git a/workplans/KEY-WP-0039-openbao-negative-probe-tokens.md b/workplans/KEY-WP-0039-openbao-negative-probe-tokens.md index ffdf12f..7911fff 100644 --- a/workplans/KEY-WP-0039-openbao-negative-probe-tokens.md +++ b/workplans/KEY-WP-0039-openbao-negative-probe-tokens.md @@ -9,7 +9,7 @@ flavor: implementation owner: claude-code topic_slug: netkingdom created: "2026-10-10" -updated: "2026-10-11" +updated: "2026-10-14" depends_on: [] related_repos: - railiance-platform @@ -43,7 +43,7 @@ from `secrets-engine` in exactly one property each. - The probe script prints only accept or refuse booleans per case, never tokens or secrets. It runs attended. -## Tasks +## T01 — Define the probe registrations ```task id: KEY-WP-0039-T01 @@ -52,8 +52,6 @@ priority: medium state_hub_task_id: "4d06b059-3768-53bb-83a4-a9a71c0850b8" ``` -### T01 — Define the probe registrations - Write a non-secret request packet under `docs/` with one probe client per claim mismatch, each identical to `secrets-engine` except for: @@ -95,18 +93,19 @@ Condition (a), no OpenBao custody, holds. The values still have to reach KeyCape 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: todo -depends_on: [KEY-WP-0039-T01] +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. -### T02 — Register the probe clients, run the attended probe, then remove them - Write the attended script, which prints booleans only. 2026-10-10: script written at `scripts/openbao_negative_probe.py`, with tests in @@ -327,3 +326,14 @@ 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. diff --git a/workplans/KEY-WP-0040-mfa-attended-run-findings.md b/workplans/KEY-WP-0040-mfa-attended-run-findings.md index d65929b..9074902 100644 --- a/workplans/KEY-WP-0040-mfa-attended-run-findings.md +++ b/workplans/KEY-WP-0040-mfa-attended-run-findings.md @@ -8,7 +8,7 @@ status: active owner: claude-code topic_slug: netkingdom created: "2026-10-11" -updated: "2026-10-12" +updated: "2026-10-14" flavor: implementation depends_on: [] state_hub_workstream_id: "a67f9d60-9b8f-5111-9fe5-9f64e631b1c3" @@ -86,3 +86,7 @@ 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.