From b808da601d3091821bd96245c0305ac6bd01d0fb Mon Sep 17 00:00:00 2001 From: tegwick Date: Sun, 27 Sep 2026 23:59:51 +0200 Subject: [PATCH] Draft execution-attribution and workload-step-up amendments; route remaining tasks - playbook-capability-contract_v0.2.md (proposed): adds the execution-attribution receipt field list (NK-WP-0040-T01). - iam-profile_v0.4.md (proposed): adds the workload-requested step-up contract (acr_values, no-factor refusal, assurance.level to flex-auth) (NK-WP-0042-T01). - Route the remaining externally-owned decisions (NK-WP-0040-T02 to audit-core/Railiance, NK-WP-0042-T02 to user-engine, NK-WP-0039-T04 to flex-auth/tenant-engine) and mark those workplans blocked. Assistant: claude-code Assistant-Model: sonnet Assistant-Process: 321494@bnt-lap001 Assistant-Session: 2c8a5cd1-573e-4bae-ab2b-29bc6c8ed4e9 --- canon/standards/iam-profile_v0.4.md | 99 +++++ .../playbook-capability-contract_v0.2.md | 386 ++++++++++++++++++ ...ex-auth-access-engine-coordinate-intake.md | 11 +- ...K-WP-0040-execution-attribution-receipt.md | 22 +- workplans/NK-WP-0042-workload-mfa-step-up.md | 24 +- 5 files changed, 534 insertions(+), 8 deletions(-) create mode 100644 canon/standards/iam-profile_v0.4.md create mode 100644 canon/standards/playbook-capability-contract_v0.2.md diff --git a/canon/standards/iam-profile_v0.4.md b/canon/standards/iam-profile_v0.4.md new file mode 100644 index 0000000..53639c2 --- /dev/null +++ b/canon/standards/iam-profile_v0.4.md @@ -0,0 +1,99 @@ +--- +id: netkingdom-iam-profile-v0.4 +type: standard +title: "NetKingdom IAM Profile v0.4" +domain: netkingdom +status: proposed +owner: net-kingdom +created: "2026-09-27" +updated: "2026-09-27" +scope: core-platform +supersedes: + - canon/standards/iam-profile_v0.3.md +adr: + - docs/adr/ADR-0011-iam-profile-ownership-and-version-governance.md + - docs/adr/ADR-0016-mfa-user-preference-and-workload-step-up.md +related: + - NK-WP-0042 +--- + +# NetKingdom IAM Profile v0.4 + +Everything in v0.3 stands unchanged except where this document says +otherwise. v0.3 remains `accepted` and normative; this document adds the +workload-requested step-up contract as a proposed amendment +(`NK-WP-0042-T01`). It does not require any workload to request MFA; +ADR-0016's default — MFA is a user preference — is unchanged, and the +enrollment/recovery usability gate in ADR-0016 §4 still applies before any +workload turns a requirement on. + +## Workload-Requested Step-Up (proposed) + +ADR-0016 lets a workload require AAL2 for all or part of its features +instead of leaving MFA to user preference. This section defines the +OIDC-level request and the no-factor refusal contract so every issuer +implements the same interface. **key-cape owns the implementation**; this +profile owns the interface, per NK-WP-0042-T01. + +### Requesting step-up + +- **Whole-client requirement.** A workload that requires AAL2 for all of + its sign-ins registers this at the client, unchanged from the existing + lightweight-mode client configuration (`mfaRequired` / P06 semantics). + This subsection does not change that path. +- **Per-request step-up.** A workload that requires AAL2 only for a + protected feature sends `acr_values` on the authorization request, + containing one of the `assurance.level` values this profile already + defines: `aal2` or `aal3`. The issuer MUST treat a recognized + `assurance.level` value in `acr_values` as a request for that floor. +- `max_age` MAY be combined with `acr_values` to also force a fresh + interactive login. `max_age` MUST NOT be used alone to request + strength: it bounds session age, not factor count. A request carrying + only `max_age` gets a fresh login at whatever assurance the user + already holds, not an implied AAL2. +- A request MUST NOT use a provider-native `acr` value as a substitute for + the profile's `assurance.level` vocabulary. Provider-native `acr`/`amr` + claims remain informational (Assurance Evidence, v0.3). + +### No-factor refusal + +If the requested floor is `aal2` or higher and privacyIDEA (or the +issuer's equivalent authoritative factor source) reports no active +factor for the user, the issuer MUST NOT silently issue a token at a +lower `assurance.level`. It MUST do one of: + +- **Inline enrollment detour:** present the enrollment flow itself and, + on completion, resume and complete the original authorization request; + or +- **Explicit refusal:** fail the authorization request with an + OAuth-shaped error distinct from a generic `access_denied` (e.g. + `error=mfa_required`), so the requesting client can route the user to + enrollment itself. + +Silent issuance of `assurance.level: aal1` (or omission of `assurance`) +in response to an `acr_values` request for `aal2`/`aal3` is nonconforming. +This governs the machine contract only; which of the two options a given +workload presents, and the exact enrollment/recovery UX, is +`NK-WP-0042-T02`'s open agreement with user-engine (U06) and a pilot +workload — this profile does not decide it. + +### Reaching flex-auth + +No new claim is introduced. The outcome is carried by the existing +`assurance.level` claim on the issued token (Assurance Evidence, v0.3): +a workload's per-request requirement is satisfied, refused, or detoured +before a token is issued, and flex-auth continues to read the issued +`assurance.level` exactly as the Identity To Authorization Contract table +already specifies. flex-auth MUST NOT infer a "requested vs. granted" +distinction from any other claim; there is only the token that was +issued. + +### Non-goals + +- This does not lower the IAM Profile v0.3 floor: privileged, destructive, + platform-root, secret, credential-vending, and emergency flows still + require `aal2` or stronger regardless of workload opt-in (ADR-0016 §3). +- This does not specify the enrollment/recovery user experience + (NK-WP-0042-T02) or which workloads turn step-up on (ADR-0016 §4). +- This does not add a Keycloak-specific or privacyIDEA-specific + mechanism; `acr_values` is the profile-level, provider-neutral request. diff --git a/canon/standards/playbook-capability-contract_v0.2.md b/canon/standards/playbook-capability-contract_v0.2.md new file mode 100644 index 0000000..e793137 --- /dev/null +++ b/canon/standards/playbook-capability-contract_v0.2.md @@ -0,0 +1,386 @@ +--- +id: netkingdom-playbook-capability-contract +type: standard +title: "NetKingdom Playbook Capability Contract v0.2" +domain: netkingdom +status: proposed +version: "0.2" +created: "2026-09-27" +updated: "2026-09-27" +scope: meta-orchestration +supersedes: + - canon/standards/playbook-capability-contract_v0.1.md +adr: + - docs/adr/ADR-0012-playbook-capability-contract-ownership.md +schema: + - canon/schemas/playbook-capability-declaration_v0.1.schema.json +related: + - NK-WP-0040 + - history/2026-09-23-railiance-clock-identity-and-layer-review.md +--- + +# NetKingdom Playbook Capability Contract v0.2 + +Everything in v0.1 stands unchanged except where this document says +otherwise. v0.1 remains `accepted` and governs declaration conformance; +this document adds the execution-attribution receipt as a proposed +amendment. It becomes conformance-checked only once +`NK-WP-0040-T02` lands the schema, evidence-holder agreement with +audit-core and Railiance, and the corresponding validator addition. +Until then, no declaration is required to emit a receipt. + +## Purpose + +The Playbook Capability Contract is the declared interface between +NetKingdom meta-orchestration and Railiance execution playbooks. + +It lets a playbook state: + +- which capability it provisions; +- which parameters it exposes, including defaults, constraints, and + security sensitivity; +- which resources and responsibilities it claims; +- which trust states it requires or satisfies; +- how it is published into a catalog; +- the execution-attribution receipt one run of it emits. + +NetKingdom consumes declarations to select playbooks, choose safe +parameter overrides, sequence trust states, and build a responsibility +map. Railiance owns the playbooks and execution mechanics. + +## Ownership + +NetKingdom owns this contract. Railiance publishes conformant +declarations. Execution stays in Railiance. See ADR-0012. + +## File Convention + +Declaration files SHOULD live in the publishing repo at: + +```text +capabilities/playbooks/.yaml +``` + +Each file describes one playbook or one stable playbook entry point. A +playbook with materially different modes may publish multiple +declarations if those modes provide different capabilities or expose +different security-sensitive parameters. + +## Top-Level Shape + +```yaml +apiVersion: netkingdom.io/playbook-capability/v0.1 +kind: PlaybookCapabilityDeclaration +metadata: + id: railiance-infra.bootstrap-host + name: Railiance S1 host bootstrap + owner: railiance-infra + repo: railiance-infra + domain: railiance + contract_version: "0.1" +spec: + playbook: {} + capabilities: [] + parameters: [] + responsibilities: [] + trust: {} + catalog: {} +``` + +## Capability Vocabulary + +`spec.capabilities[].id` MUST be one of the controlled vocabulary values +below. Capability ids are stable comparison keys. + +| Capability id | Tier | Meaning | +| --- | --- | --- | +| `s1.os-baseline` | S1 | Host provisioning, OS convergence, hardening, and substrate access baseline | +| `s1.secret-bootstrap` | S1 | Bootstrap secret material, SOPS/age handling, emergency material placement | +| `s2.cluster-runtime` | S2 | Kubernetes runtime, ingress, networking, admission, and cluster access | +| `s3.platform-services` | S3 | Databases, caches, object storage, brokers, and shared platform services | +| `c0.bootstrap-identity` | C0 | Local/bootstrap identity before runtime IAM exists | +| `c1.lightweight-sso` | C1 | key-cape lightweight SSO profile implementation | +| `c2a.light-2fa` | C2a | Lightweight built-in second factor such as TOTP/WebAuthn | +| `c2b.token-authority` | C2b | privacyIDEA or equivalent token authority | +| `c3.runtime-secrets` | C3 | OpenBao or equivalent runtime secret authority | +| `c4.fine-grained-authorization` | C4 | flex-auth and delegated PDP readiness | +| `c5.enterprise-federation` | C5 | expanded-mode Keycloak, enterprise federation, or SAML brokering | +| `c6.self-optimizing-audit` | C6 | audit feedback loops, drift surfacing, and continuous adaptation | + +The `S*` entries align to Railiance stack layers. The `C*` entries align +to the NetKingdom capability progression. A declaration may list more +than one capability only when the same playbook entry point truly +provides each one. + +## Resource Kinds + +Every capability and responsibility claim references one or more +resource kinds: + +| Resource kind | Meaning | +| --- | --- | +| `identities` | humans, service accounts, agents, groups, tenants, and assurance evidence | +| `roles_scopes_policies` | roles, scopes, policy packages, protected-system registrations, decision records | +| `secrets_credentials` | bootstrap material, runtime secrets, dynamic credentials, leases, credential rotations | +| `infrastructure_resources` | hosts, runtime, networking, platform services, storage, and deployment substrate | + +## Parameter Declarations + +Each parameter entry has this shape: + +```yaml +- name: swapfile_size_mb + type: integer + required: false + default: 4096 + constraints: + minimum: 0 + maximum: 65536 + sensitivity: operational + tuning_authority: netkingdom_tunable + description: Swap size applied by the bootstrap playbook. +``` + +Allowed `type` values: + +- `string` +- `integer` +- `number` +- `boolean` +- `array` +- `object` + +Allowed `sensitivity` values: + +- `public` - safe to display and tune freely. +- `operational` - affects behavior or sizing, not secret material. +- `security_sensitive` - affects security posture and requires platform + review. +- `secret_reference` - points to secret material but must not contain the + secret value itself. + +Allowed `tuning_authority` values: + +- `playbook_default` - NetKingdom should rely on the playbook default. +- `netkingdom_tunable` - NetKingdom may override for a scenario. +- `platform_only` - only platform-control-plane authority may override. +- `tenant_tunable` - tenant-scoped scenario owners may override within + constraints. +- `forbidden` - declaration exposes the value for audit only; callers may + not override it. + +Security-sensitive and secret-reference parameters MUST NOT be +`tenant_tunable`. Secret-reference defaults must be references or paths, +not plaintext secret values. + +For executable validation, a `secret_reference` value MUST be a non-whitespace +URI such as `openbao://kv/platform/example` or +`kubernetes://namespace/name#key`, or an explicit absolute/relative path +beginning with `/`, `./`, or `../`. Bare strings are rejected because the +composer cannot distinguish them from secret values. + +Supported constraints: + +| Constraint | Applies to | Meaning | +| --- | --- | --- | +| `enum` | all scalar types | value must be one of the listed values | +| `minimum` / `maximum` | integer, number | numeric bounds | +| `min_items` / `max_items` | array | array length bounds | +| `pattern` | string | regular expression the value must match | + +## Responsibility Claims + +Responsibility entries feed the responsibility map. They do not transfer +execution ownership to NetKingdom. + +```yaml +- resource_kind: infrastructure_resources + owner: railiance-infra + resources: + - server:target_hosts + - os-baseline + repo_owns: Provisioning, convergence, and verification mechanics. + netkingdom_orchestrates: Whether this substrate capability is selected, and which security posture is required. +``` + +`owner` names the repo or provider that holds execution ownership. +`repo_owns` explains the implementation responsibility. +`netkingdom_orchestrates` explains the meta-orchestration responsibility. + +## Trust States And Readiness + +Declarations use the trust-state vocabulary from the platform +architecture: + +- `bare_host_trust` +- `cluster_trust` +- `bootstrap_secret_trust` +- `bootstrap_identity_trust` +- `runtime_secret_trust` +- `runtime_identity_trust` +- `runtime_authorization_trust` +- `tenant_onboarding_trust` + +Each declaration lists states it requires and states it satisfies: + +```yaml +trust: + requires: + - state: bare_host_trust + readiness_checks: [] + satisfies: + - state: bootstrap_secret_trust + readiness_checks: + - id: sops-age-material-present + description: SOPS/age material is present for bootstrap secrets. + evidence: ansible role sops_agent converged successfully +``` + +Readiness checks are evidence obligations. The declaration names the +check; the Railiance playbook or verification tooling performs it. + +## Catalog And Consumption Model + +A catalog is an index of declarations. For v0.1, the catalog mechanism is +file-based: + +1. Railiance repos publish declarations under + `capabilities/playbooks/*.yaml`. +2. NetKingdom or a future catalog job discovers those files from known + orchestrated repos. +3. The validator checks each declaration against this contract. +4. A scenario states required capability ids and parameter overrides. +5. NetKingdom selects declarations that provide the required + capabilities. +6. NetKingdom applies only allowed parameter overrides, rejecting + out-of-range, tenant-forbidden, or security-unsafe overrides. +7. NetKingdom composes the responsibility and trust-state claims into a + scenario responsibility map and readiness sequence. + +The declaration is not an execution plan. It is the interface that lets a +separate playbook runner execute safely. + +## Scenario Shape + +The validator supports a small scenario file for single-provider conformance +demos: + +```yaml +id: scenario:s1-host-bootstrap-reference +authority: platform +requires: + capabilities: + - s1.os-baseline +parameter_overrides: + railiance-infra.bootstrap-host: + target_hosts: + - railiance01 + swapfile_size_mb: 8192 +``` + +Allowed scenario authorities are `platform`, `netkingdom`, and `tenant`. +Tenant authority cannot override `platform_only`, +`security_sensitive`, or `secret_reference` parameters. + +The demo composer refuses ambiguous providers and overrides aimed at unselected +declarations. Deterministic multi-provider selection, explicit provider pins, +trust sequencing, responsibility maps, and owner-routed readiness handoffs are +defined by `security-scenario-composition_v0.1.md` and implemented by +`tools/security-scenario-composer/`. Use that contract for operational planning; +the demo path does not authorize or execute playbooks. + +## Execution Attribution Receipt (proposed) + +Raised by the railiance-clock identity review +(`history/2026-09-23-railiance-clock-identity-and-layer-review.md`, +Ruling 1). IAM Profile v0.3 defines who acts. This contract's +responsibility claims define what is selected and who is responsible. +Neither defines the record that ties **one execution** to its actor +chain, artifact, and result. This section is that record's field list. +It is a proposal: it does not obligate any declaration to emit a receipt +until conformance is enforced per `NK-WP-0040-T02`. + +A receipt is a single, non-secret, structured record describing one +completed (or failed) execution of a declared capability. It never +embeds bearer credentials — tokens, secret values, or role response +bodies. Unknown attribution is recorded as an explicit `unknown`, never +inferred from path strings, repository ownership, or convenience +defaults. + +### Fields + +| Field | Meaning | +| --- | --- | +| `initiator.iss` / `initiator.sub` | Issuer and subject of the initiating actor, from its IAM Profile token | +| `delegation` | The delegation reference (`actor_sub` / `act.sub`) when the initiator acted through a delegating agent; absent, not `unknown`, when there was no delegation | +| `executing_workload` | The authoritative workload binding (Tenancy Posture `workload_identity`, proposed) that ran the playbook | +| `runtime_principal` | The runtime credential's principal identity (e.g. service account, agent runtime identity) that executed it, distinct from the workload binding | +| `tenant` | The tenant the execution acted for | +| `environment` | The environment the execution ran in | +| `run_id` | A stable, unique identifier for this one execution | +| `source_commit` | The source commit of the executed playbook or declaration | +| `artifact_digest` | The content digest of the executed artifact (image, package, or script) | +| `target` | The inventory reference of what the execution acted on | +| `action` | The action taken against the target | +| `decision_refs` | Decision and approval record identifiers that authorized the execution, where one was required | +| `time_interval.start` / `time_interval.end` | The attributed start and end of the execution | +| `time_interval.clock_source` | The clock the interval was measured against | +| `time_interval.bound` | The interval's precision bound, when one exists (e.g. clock skew tolerance); absent when no bound is established, never fabricated | + +### Emission and custody (open) + +Who emits the receipt, who holds it as evidence, and how a consumer +validates one are not decided by this section. `NK-WP-0040-T02` routes +that decision to audit-core (candidate evidence holder, per the security +layer model's Evidence role) and Railiance (candidate emitter, since it +executes). The receipt schema under `canon/schemas/` and its validator +land together with that agreement, not before it. + +### Non-goals + +- This is not an authorization decision record. `decision_refs` points at + one; it does not restate it. +- This is not a substitute for the Playbook Capability Contract's + responsibility claims or trust-state readiness checks, which describe + what a declaration is allowed to do, not what one run of it did. +- This does not create a fourth IAM principal type. `executing_workload` + and `runtime_principal` name existing principals and bindings; they do + not grant anything by themselves (railiance-clock review, Ruling 1). + +## Conformance + +A declaration conforms when it passes: + +```text +tools/playbook-capability-contract/playbook_contract_validator.py +``` + +The validator checks: + +- top-level API version and kind; +- required metadata; +- controlled capability ids and tiers; +- resource-kind vocabulary; +- parameter type/default/constraint/sensitivity/tuning rules; +- responsibility claims; +- trust-state and readiness-check shape; +- catalog publication metadata; +- optional scenario selection and parameter override compatibility. + +The execution-attribution receipt above is not yet enforced by this +validator; see `NK-WP-0040-T02`. + +## Reference Adoption + +The reference declaration for v0.1 is in: + +```text +../railiance-infra/capabilities/playbooks/railiance-infra.bootstrap-host.yaml +``` + +It describes the existing Railiance S1 Ansible bootstrap playbook and can +be selected by the sample scenario in: + +```text +examples/playbook-capability-contract/scenario-s1-host-bootstrap.yaml +``` diff --git a/workplans/NK-WP-0039-flex-auth-access-engine-coordinate-intake.md b/workplans/NK-WP-0039-flex-auth-access-engine-coordinate-intake.md index 7e09ef2..0f2b2fc 100644 --- a/workplans/NK-WP-0039-flex-auth-access-engine-coordinate-intake.md +++ b/workplans/NK-WP-0039-flex-auth-access-engine-coordinate-intake.md @@ -4,7 +4,7 @@ type: workplan title: "Take in the flex-auth to access-engine repository-coordinate rename" domain: infotech repo: net-kingdom -status: active +status: blocked flavor: implementation owner: claude-code topic_slug: netkingdom @@ -114,11 +114,18 @@ nothing else moved. ```task id: NK-WP-0039-T04 -status: todo +status: wait priority: high state_hub_task_id: "2541f523-e433-5900-b119-5825f0e71ef3" ``` +**Waiting 2026-09-27.** Routed the retire-vs-reconcile question to flex-auth +(`2c637dc9-14ad-4815-aeb4-43254d01280c`) and tenant-engine +(`9fc740da-1966-4d39-a28a-79fd4870edb1`). NetKingdom will act on whichever +answer comes back (retire and point to their authoritative declarations, or +keep a narrower reference); it should not pre-empt their decision by +deleting or editing the file first. + A read-only `kubectl diff` of `sso-mfa/k8s/tenant-engine/runtime.yaml` against railiance01 on 2026-09-23 showed live ahead of the file beyond the digests. flex-auth runs with `--caller-auth-mode enforce` and caller diff --git a/workplans/NK-WP-0040-execution-attribution-receipt.md b/workplans/NK-WP-0040-execution-attribution-receipt.md index 9258efc..9885a59 100644 --- a/workplans/NK-WP-0040-execution-attribution-receipt.md +++ b/workplans/NK-WP-0040-execution-attribution-receipt.md @@ -4,7 +4,7 @@ type: workplan title: "Define an execution-attribution receipt for Railiance runs" domain: infotech repo: net-kingdom -status: ready +status: blocked flavor: planning owner: claude-code topic_slug: netkingdom @@ -25,7 +25,7 @@ execution** to its actor chain, artifact and result. ```task id: NK-WP-0040-T01 -status: todo +status: done priority: medium state_hub_task_id: "9d02685a-a3d9-5b9f-b09e-aef1f196a97f" ``` @@ -46,11 +46,20 @@ fields: The receipt must never embed bearer credentials. Unknown attribution is kept explicit, not inferred. Start from the field list in railiance-clock's review. +**Done 2026-09-27.** Drafted as +`canon/standards/playbook-capability-contract_v0.2.md` (status `proposed`, +supersedes v0.1, which stays `accepted` and normative until this is +accepted). It adds an "Execution Attribution Receipt (proposed)" section +with every field above, explicit non-goals (no fourth IAM principal type, +not a decision record, not a substitute for responsibility claims), and the +never-embed-credentials / no-inferred-attribution rules. Emission, custody, +schema, and validator are explicitly left to T02. + ## Agree the evidence holder and schema with audit-core and Railiance ```task id: NK-WP-0040-T02 -status: todo +status: wait priority: medium state_hub_task_id: "963120c1-7c72-5806-942b-6f49e4c66e54" ``` @@ -59,3 +68,10 @@ audit-core holds evidence (layer model §3.3, Evidence role). Railiance executes and emits. Agree who emits, who holds and how the receipt is validated. Add a schema under `canon/schemas/` and a validator beside the existing playbook-capability tooling. + +**Waiting 2026-09-27.** Routed to audit-core (evidence-holder/schema +agreement) and Railiance (emitter confirmation) via State Hub messages +`48eb1101-a577-40c0-bbaa-39df02198155` and +`4f1c67bc-8de2-483f-bcac-dbdf107ce95a`. NetKingdom adds the schema and +validator once both reply; nothing here can be finished unilaterally +without pre-empting their agreement. diff --git a/workplans/NK-WP-0042-workload-mfa-step-up.md b/workplans/NK-WP-0042-workload-mfa-step-up.md index b04239a..d791c92 100644 --- a/workplans/NK-WP-0042-workload-mfa-step-up.md +++ b/workplans/NK-WP-0042-workload-mfa-step-up.md @@ -4,7 +4,7 @@ type: workplan title: "Let workloads require MFA for all or part of their features" domain: infotech repo: net-kingdom -status: backlog +status: blocked flavor: planning owner: claude-code topic_slug: netkingdom @@ -24,7 +24,7 @@ applies first. ```task id: NK-WP-0042-T01 -status: todo +status: done priority: medium state_hub_task_id: "d66a6347-b714-5cca-aeb5-7121328f4dec" ``` @@ -40,11 +40,23 @@ Specify: Record it as an IAM Profile amendment. key-cape owns the implementation. +**Done 2026-09-27.** Drafted as `canon/standards/iam-profile_v0.4.md` +(status `proposed`, supersedes v0.3, which stays `accepted` and normative +until this is accepted). The new "Workload-Requested Step-Up (proposed)" +section specifies `acr_values` carrying an `assurance.level` value +(`aal2`/`aal3`) as the step-up request, `max_age` as an optional additive +freshness request that must never stand in for strength alone, the two +conforming no-factor refusal shapes (inline enrollment detour or an +explicit `error=mfa_required`-class refusal, never silent AAL1), and that +flex-auth keeps reading the issued `assurance.level` with no new claim. +The exact enrollment/recovery UX is left to T02, which this section names +explicitly. + ## Agree the user-facing step-up and enrollment journey ```task id: NK-WP-0042-T02 -status: todo +status: wait priority: medium state_hub_task_id: "4e51585d-2b57-56d4-84c1-314041d26bcf" ``` @@ -54,3 +66,9 @@ user sees when a feature needs MFA and they have none. That includes the enrollment detour, the return to the feature, and recovery. It must be accepted from a user's perspective before any workload turns the requirement on. + +**Waiting 2026-09-27.** Routed to user-engine via State Hub message +`892fd489-113d-4108-8048-727f4b0ed048`, referencing the T01 contract and +asking for a proposed pilot workload and U06 touchpoints. This is a +UX/product agreement across two repos and a pilot workload owner; NetKingdom +cannot decide it unilaterally.