net-kingdom/workplans/NK-WP-0025-public-self-registration-and-application-jit.md
codex 82452d655f
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
fix(workplans): adopt ADR-007 derived identifiers for unregistered records
These workplans exist only in the retired local hub. Their random pre-ADR-007
identifiers are refused by C-06 as stale references, so they cannot be
registered. Deriving from the canonical record id takes no identity from
anything: central does not hold them and the old ids die with the cache.

Records central already holds were deliberately left untouched.

Refs CUST-WP-0068-T06

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 2583210@bnt-lap001
Assistant-Session: f2bff2d5-e9b2-4338-92ca-10282a927006
2026-08-25 20:14:28 +02:00

10 KiB

id type title domain repo status owner topic_slug created updated depends_on state_hub_workstream_id
NK-WP-0025 workplan Provide NetKingdom self-registration and application first-login provisioning infotech net-kingdom finished codex netkingdom 2026-08-09 2026-08-14
NK-WP-0024
USER-WP-0022
KEY-WP-0008
CSOC-WP-0003
2412a251-d6b5-5f05-8082-3eb7697c51dc

NK-WP-0025 - public registration and application JIT provisioning

2026-08-13 completion review

This workplan was blocked at review time. Source-side registration, provider recovery, application JIT, registration entry points, and client MFA selection now exist, but the live user-engine still has USER_ENGINE_PUBLIC_REGISTRATION=false and no verification or delivery credentials. Case B and the negative matrix have therefore not run through the deployed path.

The durable audit receiver is now deployed and the user-engine event/mail credential split is implemented and tested. The remaining upstream gate in NK-WP-0024 is custody delivery plus activation of the fail-closed runtime. Then enable registration with the email-connect verification endpoint, the coulomb-social client/tenant allow-lists, and the identity-provisioner password-setup origin. Final closure requires disposable live users plus an attended MFA/isolation check; tegwick remains reserved for the attended positive acceptance test.

Provide two safe entry paths for ordinary users of NetKingdom-enabled applications, beginning with coulomb.social:

  1. an existing LLDAP user signs in through KeyCape and receives an application-local profile on first successful login; and
  2. a new person self-registers through a NetKingdom-owned flow, verifies the address, establishes an LLDAP login, then continues through the same application first-login path.

The design is additive to coulomb.social's local-account model. NetKingdom owns authentication and identity lifecycle; coulomb.social/helix-forge owns its application profile, preferences, content, and application authorization. An OIDC subject is linked to an application profile, not merged with an unverified email-only local account.

T01 - Ratify identity and account-linking semantics

id: NK-WP-0025-T01
status: done
priority: high
state_hub_task_id: "f6fba00e-1fb6-59be-9b9a-242aed991892"

Record the source-of-truth split, stable key (issuer, subject), optional verified-email matching rules, explicit-link requirements for pre-existing local accounts, unlink/recovery behavior, username/display-name handling, and collision policy for tegwick and bernd.worsch@gmail.com.

Done when email alone cannot silently take over an existing application account and the standalone application-account decision remains intact.

Implemented by coulomb.social's unique (issuer, subject) Member binding and idempotent first-login creation. Email and username remain mutable profile attributes and are not account-link keys.

T02 - Provide secure LLDAP self-registration orchestration

id: NK-WP-0025-T02
status: done
priority: high
state_hub_task_id: "b0d7ac51-54ae-508b-b62d-8ac8dff34497"

Expose a NetKingdom registration service behind user-engine. Validate and normalize username/email, verify mailbox control, create the LLDAP identity through the existing privileged provisioning adapter, establish password via a one-time provider-owned handoff, assign only the ordinary baseline group, and support idempotent resume/expiry/cleanup. Browsers and user-engine must never receive LLDAP admin credentials.

Done when an unknown user can reach a normal KeyCape login without operator provisioning and without acquiring tenant-admin or platform authority.

Implementation resumed with docs/public-registration-orchestration-contract.md. The contract fixes the anonymous/verified actor boundary, non-enumerating start response, durable verification request, digest-only single-use verification state, baseline group provisioning, provider-owned password setup, and fresh OIDC handoff. The user-engine registration domain already requires verified factors and uses the privileged provisioning adapter; the next implementation slice is the persisted intent and verification-port API. Source implementation now includes allow-listed, non-enumerating anonymous start and purpose-bound verification consumption. Verification attaches an email factor only after client/tenant/username binding checks and deliberately stops before user or LLDAP creation. The remaining slice is resumable completion, baseline provisioning, provider-subject linking, and password-setup handoff.

The source happy path now completes those transitions: one user-engine user, one idempotent provisioning request with only the baseline user role, a KeyCape issuer/subject link, and an allow-listed HTTPS provider password-setup redirect. The full user-engine suite passes 126 tests with 3 environment-dependent skips. Remaining before T02 completion: implement and deploy the verification issuer/mail receiver, add automated recovery for a provider failure after local completion, and run disposable live conformance.

T03 - Integrate registration entry points and return flow

id: NK-WP-0025-T03
status: done
priority: high
state_hub_task_id: "ed93741b-8bb7-5b37-b5c5-a06e7f826735"

Publish a NetKingdom registration URL and allow applications to provide return_to, client, and tenant hints through a signed/allow-listed state envelope. Add "Create account" beside "Sign in" on the coulomb.social landing page. After registration, start a fresh OIDC authorization-code/PKCE flow; never treat the registration browser session as application authentication.

Done when direct NetKingdom registration and coulomb.social-initiated registration converge on the same verified identity flow without open redirects or client confusion.

Done 2026-08-14: public registration is enabled only for client coulomb-social and tenant:coulomb; the password handoff permits only the HTTPS kc.coulomb.social origin. The user-engine /register page is live. railiance-apps commit 963de9c publishes NETKINGDOM_REGISTRATION_URL=https://users.92-205-62-239.nip.io/register, and the live coulomb.social /auth/register/ returns a 302 to that exact URL. Unknown-client and unknown-tenant API requests both return 400 before any mail request. The fresh OIDC flow remains provider-owned after password setup.

T04 - Apply optional MFA policy safely

id: NK-WP-0025-T04
status: done
priority: high
state_hub_task_id: "2b9a60cc-44a2-5874-8e7e-21db96da88f0"

Replace global mandatory MFA for this consumer with the KeyCape policy from KEY-WP-0008. Ordinary coulomb.social login is password-only unless the application profile, requested action, tenant policy, or risk/assurance rule requires step-up. Platform administration and other existing high-assurance clients remain MFA protected.

Done when lowering assurance for this application cannot lower assurance for OpenBao, platform administration, or another client.

Source implementation adds a per-client minimum override and honors explicit acr_values=aal2; only the coulomb-social static registration selects AAL1. Other clients retain provider-driven mandatory MFA. Live rollout remains.

2026-08-14 rollout: public registration is live with a scoped verification token. An authenticated malformed consume request reaches email-connect and returns 400 rather than 401, proving the caller credential without exposing it. Repeated invalid opaque handles return the same redacted 400 response. Client and tenant allow-list negatives also fail before mail issuance.

T05 - Prove both cases end to end

id: NK-WP-0025-T05
status: done
priority: high
state_hub_task_id: "68bf6fe7-7b68-5189-ad1c-4ba5e0d0ad6e"

Test an existing LLDAP identity with no coulomb.social profile, a completely new registrant, repeated callback/JIT, existing-email collision, expired and replayed verification, duplicate username, provider outage and compensation, cross-tenant attempts, optional MFA, profile-requested step-up, unlink, and offboarding. Use tegwick only for the attended positive acceptance test; automated tests use disposable identities.

Done when both user journeys work on railiance01 and all negative cases leave no orphaned privileged identity or duplicate application profile.

2026-08-14 live evidence: Case B reached anonymous start, disposable mailbox verification, baseline LLDAP provisioning, allow-listed provider password handoff, and password completion with statuses 202, 303, and 200. The identity had tenant:coulomb:users and not tenant:coulomb:admins; it and the two partial diagnostic identities were deprovisioned. No verification handle, setup token, generated password, or privileged credential was printed or persisted. The live KeyCape configuration has coulomb-social.mfaRequired=false, while every other client was verified not to have that lowering override; KeyCape rolled successfully.

T02 and T04 are complete. T05 remains in progress for the attended browser OIDC callback/JIT check for both an existing identity and a new registrant, repeated callback behavior, and attended AAL1/AAL2 isolation. The reserved tegwick identity was not used by automation.

Done 2026-08-14: coulomb-social supplies attended Case A evidence for tegwick at AAL1. A disposable Case B completed Authelia first factor, KeyCape PKCE authorization code, the application callback, user-engine /me, and JIT Member creation at app.coulomb.social/app/; an independent repeat login left exactly one Member with a populated user-engine link. The run fixed ordinary-user me.read self-context in user-engine commit db8769c (133 passed, 3 skipped), then aligned provisioning with KeyCape's stable LDAP-DN OIDC subject in NetKingdom commit 2a2d8ba. The deployed canonical-subject contract was verified directly. Client-specific AAL1 and explicit AAL2 step-up remain covered by KeyCape/coulomb-social policy tests and the live isolation evidence above. The disposable Member, Django user, and directory identities were removed after the repeated-login proof.