diff --git a/docs/user-engine-platform-expansion-contract.md b/docs/user-engine-platform-expansion-contract.md new file mode 100644 index 0000000..279e36b --- /dev/null +++ b/docs/user-engine-platform-expansion-contract.md @@ -0,0 +1,62 @@ +# user-engine platform expansion contract + +This contract activates `NK-WP-0024` without weakening the proven portal. +The current production image continues to use its existing integration path +until all three endpoints below are deployed and verified together. + +## Authorization + +user-engine is the protected system and calls flex-auth `POST /v1/check` for +every domain mutation. Requests use the existing user-engine resource types +and action names, including `user-engine:user`, `user-engine:membership`, +`user-engine:invitation`, `user-engine:tenant`, and `user-engine:outbox`. + +Policy roles are: + +- self: actions whose authorization context has `self=true` and whose target + user matches the authenticated subject's user projection; +- tenant-admin: tenant-scoped membership, invitation, account lifecycle, and + recovery actions within the actor's tenant only; +- platform-admin: tenant creation, platform recovery, outbox delivery, and + dead-letter replay across the platform tenant; + +The HTTP adapter is fail closed. Unreachable, malformed, timed-out, or +non-conforming flex-auth responses become a deny decision. The flex-auth +decision ID is stored on the corresponding user-engine audit record. + +Required runtime setting: `USER_ENGINE_FLEX_AUTH_URL`. + +## Durable event and mail delivery + +The transactional outbox remains the source of delivery state. The worker +endpoint sends a CloudEvents-like, redacted JSON envelope to the platform +event receiver and supplies the outbox event ID as `Idempotency-Key`. +Invitation-created and invitation-resent events are additionally delivered to +the approved mail receiver using the same idempotency key. Mailbox ownership +is evidence only and is never passed into an authorization decision as proof +of authority. + +Required runtime settings: + +- `USER_ENGINE_EVENT_URL` +- `USER_ENGINE_MAIL_URL` (optional until the approved receiver exists) +- `USER_ENGINE_DELIVERY_TOKEN` from the OpenBao-backed runtime lane + +Delivery uses bounded attempts already persisted in the user-engine outbox. +Failures retain a redacted reason, dead-letter at the configured threshold, +and can be replayed through the platform-operator route. + +## Production activation gate + +Do not add these environment variables to the live Deployment until: + +1. flex-auth has a cluster-local Service and a validated user-engine policy; +2. the event and mail receivers accept idempotent, authenticated delivery; +3. their credentials are present under the approved OpenBao user-engine lane; +4. NetworkPolicies allow only the named receivers; +5. the deployed failure matrix proves deny/unavailability, receiver failure, + dead-letter/replay, invitation expiry/replay, provider outage recovery, and + cross-tenant denial. + +This gate prevents a partial rollout from making the portal unavailable or +silently dropping notifications. diff --git a/tests/test_user_engine_platform_expansion.py b/tests/test_user_engine_platform_expansion.py new file mode 100644 index 0000000..22c7de4 --- /dev/null +++ b/tests/test_user_engine_platform_expansion.py @@ -0,0 +1,30 @@ +from pathlib import Path +import unittest + + +ROOT = Path(__file__).parents[1] + + +class UserEnginePlatformExpansionContractTests(unittest.TestCase): + def test_contract_keeps_authorization_fail_closed(self): + text = (ROOT / "docs/user-engine-platform-expansion-contract.md").read_text() + self.assertIn("fail closed", text) + self.assertIn("decision ID", text) + self.assertIn("USER_ENGINE_FLEX_AUTH_URL", text) + + def test_contract_requires_idempotent_bounded_delivery(self): + text = (ROOT / "docs/user-engine-platform-expansion-contract.md").read_text() + self.assertIn("Idempotency-Key", text) + self.assertIn("bounded attempts", text) + self.assertIn("dead-letter", text) + self.assertIn("OpenBao", text) + + def test_live_manifest_does_not_enable_partial_integration(self): + manifest = (ROOT / "sso-mfa/k8s/user-engine/runtime.yaml").read_text() + self.assertNotIn("USER_ENGINE_FLEX_AUTH_URL", manifest) + self.assertNotIn("USER_ENGINE_EVENT_URL", manifest) + self.assertNotIn("USER_ENGINE_MAIL_URL", manifest) + + +if __name__ == "__main__": + unittest.main() diff --git a/workplans/NK-WP-0024-user-engine-portal-integration-expansion.md b/workplans/NK-WP-0024-user-engine-portal-integration-expansion.md index a4eb3c3..4c5fdac 100644 --- a/workplans/NK-WP-0024-user-engine-portal-integration-expansion.md +++ b/workplans/NK-WP-0024-user-engine-portal-integration-expansion.md @@ -4,14 +4,17 @@ type: workplan title: "Expand user-engine platform integrations beyond the Binky MVP" domain: infotech repo: net-kingdom -status: backlog +status: active owner: codex topic_slug: netkingdom created: "2026-07-30" -updated: "2026-07-30" +updated: "2026-08-08" depends_on: - NK-WP-0023 - USER-WP-0021 + - FLEX-WP-0009 + - AUDIT-WP-0003 + - EMAIL-WP-0004 state_hub_workstream_id: "9bd05700-a839-4014-8f88-8a78f2757721" --- @@ -20,11 +23,20 @@ state_hub_workstream_id: "9bd05700-a839-4014-8f88-8a78f2757721" Track deferred integration breadth separately from the completed live MVP. Activate with the corresponding user-engine product-expansion tasks. +Provider workplans established 2026-08-08: + +- `FLEX-WP-0009` provides the user-engine policy package and cluster service. +- `AUDIT-WP-0003` provides authenticated durable event ingestion. +- `EMAIL-WP-0004` provides transactional invitation-mail delivery. + +This workplan remains the consumer-side coordination and deployed integration +gate; provider implementation status stays authoritative in the owning repos. + ## T01 - Establish flex-auth production authorization ```task id: NK-WP-0024-T01 -status: todo +status: progress priority: high state_hub_task_id: "7dff0ac6-c5eb-486c-83db-a1400f30f77d" ``` @@ -33,11 +45,18 @@ Define self, tenant-admin, and platform-admin resource/action vocabulary, deploy the policy package, replace user-engine's local runtime adapter with a fail-closed flex-auth HTTP adapter, and correlate decision IDs with audit. +2026-08-08 implementation: added the fail-closed flex-auth HTTP adapter in +user-engine, including request mapping, decision-ID propagation, timeout and +malformed-response denial tests. The role/resource contract and safe rollout +gate are recorded in `docs/user-engine-platform-expansion-contract.md`. +Deployment remains pending because no cluster-local flex-auth Service or +validated user-engine policy package currently exists on railiance01. + ## T02 - Add invitation and verification mail delivery ```task id: NK-WP-0024-T02 -status: todo +status: progress priority: medium state_hub_task_id: "9d5f272b-115c-404d-8387-7f987cee65ea" ``` @@ -45,11 +64,17 @@ state_hub_task_id: "9d5f272b-115c-404d-8387-7f987cee65ea" Deliver invitation and verification messages through approved custody and mail lanes. Mailbox ownership remains evidence, never authorization. +2026-08-08 implementation: added authenticated, idempotent invitation-mail +delivery from durable outbox events. The delivery token is a required runtime +secret and the NetKingdom contract pins it to the existing OpenBao runtime +lane. Live completion waits for an approved mail receiver and its custody +entry; production is deliberately not pointed at a fabricated endpoint. + ## T03 - Operate durable event delivery ```task id: NK-WP-0024-T03 -status: todo +status: progress priority: high state_hub_task_id: "17d6390b-05a8-436c-879b-4e0331d85be5" ``` @@ -57,11 +82,17 @@ state_hub_task_id: "17d6390b-05a8-436c-879b-4e0331d85be5" Connect the user-engine transactional outbox to the platform event lane with bounded retries, replay, dead-letter visibility, redaction, and correlation. +2026-08-08 implementation: added an authenticated HTTP event adapter carrying +event ID, tenant, correlation, occurrence time, and redacted domain payload. +It uses `Idempotency-Key`; the existing durable store supplies bounded retry, +dead-letter, diagnostics, and replay. Live completion waits for a selected +cluster event receiver and scoped delivery credential. + ## T04 - Run expanded integration failure matrix ```task id: NK-WP-0024-T04 -status: todo +status: progress priority: high state_hub_task_id: "4c54e8e4-19d9-4470-bb43-3d43cf27af71" ``` @@ -69,3 +100,11 @@ state_hub_task_id: "4c54e8e4-19d9-4470-bb43-3d43cf27af71" Prove flex-auth denial/unavailability, mail failure, identity-provider outage, partial provisioning recovery, invitation replay/expiry, and cross-tenant negative behavior through the deployed path. + +2026-08-08 source matrix: user-engine's full suite passes 119 tests with three +external-provider tests skipped. New coverage proves flex-auth allow mapping, +decision correlation, fail-closed unavailability, invitation mail routing, +event routing, and idempotency. Existing USER-WP-0021 coverage proves provider +outage, recovery, invitation expiry/replay, dead-letter/replay, and +cross-tenant denial. The deployed-path matrix remains pending on the three +production endpoints above.