Link the MFA challenge to lost-authenticator help.
All checks were successful
Authentication acceptance / acceptance (push) Successful in 1m1s
Authentication acceptance / provider-contract (push) Successful in 24s
Build and Publish Container Image / build-and-push (push) Successful in 40s

The OTP page now points at the account security page without carrying challenge state, and the render test checks that link. Workplan headings sit above their task blocks so the hub titles match KEY-WP-0038 and KEY-WP-0039. KEY-WP-0039-T02 stays waiting on the Forgejo identity gate.

Assistant: grok
Assistant-Session: 01a13b3b-6856-7900-9d04-bccba2337a30
This commit is contained in:
tegwick 2026-10-14 18:29:33 +02:00
parent 61ee563b1f
commit 205a1c0edc
7 changed files with 55 additions and 33 deletions

View file

@ -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`

View file

@ -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 |

View file

@ -760,6 +760,7 @@ var mfaChallengeTemplate = template.Must(template.New("mfa-challenge").Parse(`<!
<input id="mfa_token" name="mfa_token" type="text" inputmode="numeric" autocomplete="one-time-code" required autofocus>
<button type="submit">Verify</button>
</form>
<p><a href="https://users.coulomb.social/security" rel="noreferrer">Code rejected or lost your authenticator?</a></p>
</main>
</body>
</html>`))

View file

@ -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, "</form>"); 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)
}

View file

@ -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

View file

@ -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.

View file

@ -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.