From 97423c61100cd1cedebb7da3f955c9c3b6fdea59 Mon Sep 17 00:00:00 2001 From: tegwick Date: Fri, 22 May 2026 22:26:36 +0200 Subject: [PATCH] Complete user-engine boundary contracts --- SCOPE.md | 5 + .../user-engine-boundary-contract_v0.1.md | 327 ++++++++++++++++++ docs/user-engine-interface-guidance.md | 11 +- ...r-engine-preparation-boundary-contracts.md | 33 +- 4 files changed, 365 insertions(+), 11 deletions(-) create mode 100644 canon/standards/user-engine-boundary-contract_v0.1.md diff --git a/SCOPE.md b/SCOPE.md index 0554c85..0527e8b 100644 --- a/SCOPE.md +++ b/SCOPE.md @@ -24,6 +24,9 @@ NetKingdom is a self-optimizing security platform for Kubernetes-based IT infras canonical spec: `canon/standards/iam-profile_v0.2.md`) - SSO/MFA Platform: Keycloak with LDAP/Entra federation, enterprise identity (NK-WP-0001) - Local Identity: file-based user store + minimal OIDC server for bootstrap phase (NK-WP-0002) +- User Engine Boundary Contract: source-of-truth, membership, + application-onboarding, projection, authorization, and audit contracts for + `user-engine` integration (`canon/standards/user-engine-boundary-contract_v0.1.md`) - Security bootstrapping: credential management, SOPS/age integration, OpenBao runtime secret authority - Architectural decisions (DECISIONS.md): identity source, secrets, GitOps, bootstrap user store @@ -117,3 +120,5 @@ keywords: [bootstrap, local-identity, oidc, minimal, dev, sandbox] - Start with: `wiki/` (specifications and decisions), `DECISIONS.md` (key architectural choices D1–D5) - Key files / directories: `sso-mfa/` (NK-WP-0001 active workplan), `local-identity/` (NK-WP-0002), `workplans/` - Entry points: `workplans/NK-WP-0001-sso-mfa-platform.md` and `NK-WP-0002-local-identity.md` for current work +- User-domain boundary contract: + `canon/standards/user-engine-boundary-contract_v0.1.md` diff --git a/canon/standards/user-engine-boundary-contract_v0.1.md b/canon/standards/user-engine-boundary-contract_v0.1.md new file mode 100644 index 0000000..61483d3 --- /dev/null +++ b/canon/standards/user-engine-boundary-contract_v0.1.md @@ -0,0 +1,327 @@ +--- +id: netkingdom-user-engine-boundary-contract +type: standard +title: "NetKingdom User Engine Boundary Contract v0.1" +domain: netkingdom +status: accepted +version: "0.1" +created: "2026-05-22" +updated: "2026-05-22" +scope: user-domain-boundaries +workplan: + - workplans/NK-WP-0014-user-engine-preparation-boundary-contracts.md +related: + - canon/standards/iam-profile_v0.2.md + - canon/standards/playbook-capability-contract_v0.1.md + - docs/responsibility-map.md + - docs/user-engine-interface-guidance.md + - docs/reviews/2026-05-22T19-19-59+0200-user-engine-architecture-review.md +--- + +# NetKingdom User Engine Boundary Contract v0.1 + +## Purpose + +This contract defines how `user-engine` integrates into the NetKingdom +landscape without duplicating IAM, authorization, application registration, +deployment, audit, or UI responsibilities. + +`user-engine` owns user-domain facts and profile projections. NetKingdom owns +the cross-repo boundary contract. Implementations and adapters must preserve +the ownership rules below even when they use local fixtures for development or +standalone deployments. + +## Ownership Model + +`user-engine` is a headless user-domain and profile service. It is not an +identity provider, MFA system, policy decision point, runtime secret store, +deployment engine, or UI repository. + +| Concern | Owner | user-engine responsibility | +| --- | --- | --- | +| OIDC discovery, login, token issuance | `key-cape`, Keycloak, or `local-identity` in dev | Consume verified IAM Profile claims | +| Passwords, passkeys, sessions, MFA | IAM/MFA stack | No storage or lifecycle responsibility | +| Stable identity links | user-engine | Map `(issuer, subject)` to `user_id` | +| Account lifecycle and profile facts | user-engine | Canonical source of truth | +| Product memberships | user-engine unless explicitly imported | Own, validate, export as read models | +| Coarse authentication groups and roles | IAM provider normalized through the IAM Profile | Consume as actor facts | +| Fine-grained authorization | `flex-auth` | Ask, enforce, and audit decisions | +| Runtime secrets | OpenBao/Railiance platform services | Consume scoped secret references through adapters | +| Deployment mechanics | Railiance and application repos | Publish requirements and readiness signals | +| Self-service/admin UI | future UI repos | Provide APIs and projections only | + +## Source-Of-Truth Matrix + +| Resource kind | Source of truth | user-engine relation | Boundary rule | +| --- | --- | --- | --- | +| OIDC issuer and discovery metadata | IAM Profile implementation | Consume issuer metadata and JWKS through adapters | Never hardcode provider-specific paths where discovery is available | +| Login, session, MFA, and token lifecycle | `key-cape`, Keycloak, or `local-identity` in non-production | Accept only verified claim envelopes | user-engine must not issue, refresh, or revoke tokens | +| Identity claims | IAM Profile implementation | Normalize into an actor envelope | Claims are authentication facts, not final authorization decisions | +| Human subject | IAM provider for authentication identity; user-engine for domain record | Link `(issuer, subject)` to `user_id` | Email, username, and display name are mutable attributes, not keys | +| Local/bootstrap identities | `local-identity` | Allowed only in local/test/standalone modes | Production adapters must reject local or loopback issuers | +| User record | user-engine | Canonical owner | Other systems may reference `user_id` but must not mutate the record directly | +| Account lifecycle | user-engine | Canonical owner | IAM disablement may trigger import/sync events, but user-engine records lifecycle state | +| Groups | IAM provider | Consume from claims or imports as identity facts | Groups do not overwrite user-engine-owned memberships | +| Coarse roles | IAM provider, normalized into `roles` | Consume as actor facts for policy input | Roles are not user-engine resource ownership | +| Fine-grained roles, scopes, policies | `flex-auth` | Register resources/actions and request decisions | user-engine does not interpret policy packages locally in production | +| Tenant identifiers | NetKingdom/IAM Profile contract | Store tenant-scoped records and memberships | Tenant admin authority never implies `tenant:platform` authority | +| Product memberships | user-engine unless an import contract says otherwise | Canonical owner for local facts | Every imported membership carries source, version, freshness, and delete semantics | +| External/provisioned memberships | Provisioning system under import contract | Store as externally mastered facts | user-engine may expose/read them but must not silently take ownership | +| Applications as profile consumers | user-engine | Own application record and allowed profile scopes | Does not replace IAM clients, protected systems, or deployments | +| OIDC clients | IAM provider | Store binding metadata only | Client secrets and redirect validation stay with IAM | +| Protected systems | `flex-auth` | Store binding metadata and resource/action vocabulary reference | flex-auth remains PDP and protected-system registry owner | +| Catalog namespaces | user-engine | Canonical owner and validator | Namespaces bind to one owning application and version policy | +| Profile and preference values | user-engine | Canonical owner | Attribute definitions must resolve to a catalog namespace or governed global key | +| Effective-profile projections | user-engine | Canonical resolver and projection API | Projections are read models with version/freshness metadata | +| Claims enrichment inputs | IAM-side adapter | Optional profile read from user-engine | Adapter owns cache, freshness, and failure mode; login must not synchronously depend on user-engine | +| Runtime secrets and DB credentials | OpenBao/Railiance platform services | Consume logical secret names through `SecretProvider` | Secret values must not be stored in profile/catalog records | +| Deployment metadata | Railiance/application repo | Store binding references where needed for onboarding | Railiance executes deployment and readiness mechanics | +| Authorization decisions | `flex-auth` | Enforce returned decisions and obligations | Sensitive writes fail closed when decisions are unavailable | +| Local audit record | user-engine | Canonical owner for local mutation trail | Must carry correlation fields to decision and event records | +| Decision audit | `flex-auth` | Store returned decision id | user-engine audit must link to the decision id where a check occurred | +| Platform audit sink | NetKingdom/Railiance audit service | Emit redacted summaries | Sensitive profile payloads are not exported by default | +| Domain events | user-engine outbox | Canonical source for user-domain lifecycle events | Events are durable after commit and carry schema/version identifiers | + +## Membership Synchronization Contract + +Every membership fact that crosses a system boundary must carry this envelope: + +| Field | Requirement | +| --- | --- | +| `membership_id` | Stable id for the fact in the owning system | +| `owner_system` | One of `user-engine`, `iam`, `provisioning`, or another explicit source | +| `source_system` | System that last supplied the fact | +| `subject_user_id` | user-engine `user_id` when resolved | +| `issuer` and `subject` | Original identity key when a user link is not yet resolved | +| `tenant` | Tenant context such as `tenant:platform` or `tenant:coulomb` | +| `scope_type` | `tenant`, `application`, `team`, `catalog`, or a contract extension | +| `scope_id` | Stable id inside the scope type | +| `relation` | Role, relation, or membership kind | +| `freshness` | Timestamp, version, sequence, or lease/expiry marker | +| `delete_semantics` | `tombstone`, `disable`, `expire`, or `hard_delete_requested` | +| `conflict_rule` | `owner_wins`, `newer_version_wins`, or an explicit manual-review rule | +| `correlation_id` | Request/import/export correlation id | + +Allowed ownership classes: + +| Class | Rule | +| --- | --- | +| `user_engine_mastered` | Created and mutated in user-engine; exported outward as read models only | +| `iam_imported_seed` | IAM group/role data may seed a local membership once, but does not continue overwriting it unless a contract says so | +| `externally_provisioned` | Provisioning source remains owner; user-engine stores the fact with source/version/delete semantics | +| `flex_auth_export` | user-engine exports subject facts to flex-auth for policy input; flex-auth does not become the membership store | +| `iam_export_requested` | Optional adapter-owned export for coarse IAM groups/claims; user-engine remains owner unless explicitly transferred | + +Conflict handling: + +- owner wins by default; +- non-owner updates are rejected or stored as pending import review; +- stale imports are ignored when their freshness marker is older than the + current fact; +- cross-tenant imports must be rejected unless the import contract explicitly + maps source tenant to target tenant; +- deletes from a non-owner source disable only that source's externally + mastered fact and must not remove user-engine-owned memberships. + +Freshness and invalidation: + +- membership writes emit outbox events with a version stamp; +- exports to flex-auth carry the membership version used to build the subject + fact; +- consumers must be able to tell whether an authorization decision used stale + membership facts; +- high-risk membership changes invalidate request caches and short-lived + decision caches. + +## Application Onboarding Contract + +A platform application is represented by several records. They are bound +together, but not merged into one ownership domain. + +| Binding | Owner | Required fields | +| --- | --- | --- | +| user-engine application | user-engine | `application_id`, display name, owner, allowed profile scopes, projection types, lifecycle state | +| IAM OIDC client | `key-cape` or Keycloak | client id, issuer, redirect URIs, allowed scopes, assurance requirements | +| flex-auth protected system | `flex-auth` | protected-system id, resource/action vocabulary, policy package binding | +| Catalog namespace | user-engine | namespace, owning application id, catalog id, versioning and deprecation policy | +| Deployment metadata | Railiance/application repo | environment, service name, tenant placement, ingress/readiness endpoints | +| Audit/event identity | user-engine plus platform sink | source application id, event subject prefix, correlation-id policy | + +Minimum binding record: + +```yaml +application_id: string +tenant_scope: string +iam: + issuer: string + oidc_client_id: string +flex_auth: + protected_system_id: string + resource_vocabulary_version: string +catalog: + namespace: string + active_catalog_version: string +deployment: + environment: string + service_name: string +audit: + event_source: string + correlation_policy: required +``` + +Safe onboarding checklist: + +1. Register or approve the application owner and tenant scope. +2. Create the IAM OIDC client with redirect URIs, scopes, and assurance + requirements. +3. Register the flex-auth protected system and policy package. +4. Create the user-engine application record and bind the IAM/flex-auth ids. +5. Register the catalog namespace and publish the first catalog version. +6. Define projection types the application may request. +7. Register event source and audit redaction rules. +8. Record Railiance deployment metadata and readiness endpoints. +9. Verify one authorized profile read, one denied profile read, one authorized + mutation, one denied mutation, and correlated audit/event output. + +## Projection And Claims-Enrichment Boundaries + +Projection types are distinct contracts: + +| Projection type | Consumer | Required boundary | +| --- | --- | --- | +| `self_service` | Current user | Includes only fields visible/editable by the subject under policy | +| `admin` | Tenant or platform admin tools | Requires flex-auth checks and tenant/platform authority separation | +| `application_runtime` | Registered applications | Limited to allowed profile scopes and catalog namespace bindings | +| `audit` | Operators and audit sinks | Redacted summaries with correlation metadata, not full sensitive payloads | +| `agent_context` | Autonomous or delegated agents | Policy-filtered context with actor/delegation evidence | +| `claims_enrichment` | IAM-side enrichment adapter | Optional, cache/freshness governed, and never token issuance itself | + +Required projection metadata: + +- projection type; +- target user id; +- tenant and application context; +- catalog/profile version stamps; +- redaction policy used; +- authorization decision id where a decision was needed; +- freshness timestamp; +- correlation id. + +Claims enrichment rules: + +- user-engine must not issue tokens; +- IAM owns token issuance and the enrichment adapter; +- enrichment adapters must define cache TTL, stale-read policy, and failure + behavior; +- privileged or destructive login claims must not depend on stale profile + enrichment; +- when user-engine is unavailable, the adapter either omits optional enriched + claims or fails according to IAM-side policy, not user-engine policy. + +## Authorization Contract + +user-engine is a policy enforcement point. `flex-auth` is the policy decision +point for production authorization decisions. + +Minimum authorization request: + +```yaml +actor: + issuer: string + subject: string + tenant: string + principal_type: human | service | agent + roles: [string] + groups: [string] + scopes: [string] + assurance: object +resource: + type: string + id: string +action: string +context: + tenant: string + application_id: string | null + target_user_id: string | null + projection_type: string | null + correlation_id: string +``` + +Resource/action vocabulary: + +| Resource | Baseline actions | +| --- | --- | +| `user-engine:user` | `read`, `create`, `update`, `deactivate`, `delete_request` | +| `user-engine:identity-link` | `read`, `link`, `unlink` | +| `user-engine:profile` | `read`, `update`, `resolve`, `project` | +| `user-engine:membership` | `read`, `assign`, `remove`, `import`, `export` | +| `user-engine:application` | `register`, `read`, `update`, `deactivate` | +| `user-engine:catalog` | `register`, `activate`, `deprecate`, `migrate` | +| `user-engine:projection` | `read`, `render`, `invalidate` | +| `user-engine:audit` | `read`, `export_summary` | + +Performance model: + +- single sensitive writes use synchronous flex-auth checks and fail closed; +- list screens and bulk admin operations should use batch checks; +- request-scoped memoization is allowed for identical checks within one + request; +- short-lived decision caches are allowed only for low-risk reads when the + policy package permits caching and names invalidation events; +- local policy fixtures are allowed only for tests and standalone development; +- production adapters must record the flex-auth decision id or equivalent + decision correlation. + +## Audit Correlation Contract + +Every user-engine mutation and sensitive read must create or reference a +correlation bundle: + +| Field | Requirement | +| --- | --- | +| `correlation_id` | Stable id propagated across request, decision, audit, and event records | +| `request_id` | Transport/request id where available | +| `actor` | IAM Profile actor envelope or redacted service/agent equivalent | +| `tenant` | Tenant context for the action | +| `application_id` | Application context where applicable | +| `target_user_id` | Target user where applicable | +| `resource` and `action` | user-engine resource/action pair | +| `authorization_decision_id` | flex-auth decision id when an authorization check occurred | +| `user_engine_audit_id` | Local audit record id | +| `outbox_event_id` | Event id when a domain event is emitted | +| `redaction_policy` | Policy used to summarize sensitive fields | +| `change_summary` | Redacted mutation summary | + +Platform audit sinks receive summaries. They must not receive full profile +payloads, secret values, or catalog-sensitive values unless a future contract +explicitly authorizes that export path. + +## user-engine Repo Readiness + +The user-engine repository may start production-code implementation when these +artifacts exist and remain aligned with this contract: + +| Artifact | Required purpose | Current expected path | +| --- | --- | --- | +| Scope boundary | State in/out-of-scope responsibilities | `/home/worsch/user-engine/SCOPE.md` | +| Architecture notes | Explain IAM, flex-auth, platform, UI, and deployment boundaries | `/home/worsch/user-engine/wiki/ArchitectureBlueprint.md` | +| Repo layout decision | Keep domain logic independent from transports and adapters | `/home/worsch/user-engine/docs/development.md` | +| Local development contract | Define standalone vs platform configuration and guardrails | `/home/worsch/user-engine/docs/configuration.md` | +| NetKingdom integration notes | Translate this contract into repo-local ports/adapters | `/home/worsch/user-engine/docs/interfaces/netkingdom-integration.md` | +| Implementation workplans | Carry implementation tasks in user-engine, not NetKingdom | `/home/worsch/user-engine/workplans/USER-WP-0001` through `USER-WP-0006` | + +Readiness checklist: + +- source-of-truth boundaries are documented before new stores are added; +- membership imports/exports identify owner, source, freshness, and delete + semantics; +- application records bind, but do not own, IAM clients and flex-auth protected + systems; +- projections include version/freshness and redaction metadata; +- claims enrichment is optional and adapter-owned; +- authorization ports can support synchronous, batch, memoized, and safe-cache + modes; +- audit records can correlate with flex-auth decisions and outbox events; +- implementation work remains in user-engine workplans while NetKingdom keeps + only boundary and orchestration responsibilities. + diff --git a/docs/user-engine-interface-guidance.md b/docs/user-engine-interface-guidance.md index 3a0773b..5f683b0 100644 --- a/docs/user-engine-interface-guidance.md +++ b/docs/user-engine-interface-guidance.md @@ -1,10 +1,12 @@ # User Engine Interface Guidance -Status: initial interface guidance +Status: accepted interface guidance Date: 2026-05-22 Owner: NetKingdom +Canonical contract: `canon/standards/user-engine-boundary-contract_v0.1.md` Related: +- `canon/standards/user-engine-boundary-contract_v0.1.md` - `docs/reviews/2026-05-22T19-19-59+0200-user-engine-architecture-review.md` - `docs/responsibility-map.md` - `canon/standards/iam-profile_v0.2.md` @@ -12,9 +14,12 @@ Related: ## Purpose -This document defines the cross-repo interface guidance for integrating +This document summarizes the cross-repo interface guidance for integrating `user-engine` into the NetKingdom landscape without duplicating identity, -authorization, deployment, or UI responsibilities. +authorization, deployment, audit, or UI responsibilities. The normative +versioned contract is +`canon/standards/user-engine-boundary-contract_v0.1.md`; this document remains +as the shorter implementation-facing guide. `user-engine` owns user-domain facts and profile projections. NetKingdom owns the security and orchestration contracts that decide how those facts relate to diff --git a/workplans/NK-WP-0014-user-engine-preparation-boundary-contracts.md b/workplans/NK-WP-0014-user-engine-preparation-boundary-contracts.md index 670eb72..32c7074 100644 --- a/workplans/NK-WP-0014-user-engine-preparation-boundary-contracts.md +++ b/workplans/NK-WP-0014-user-engine-preparation-boundary-contracts.md @@ -4,7 +4,7 @@ type: workplan title: "User Engine Preparation And Boundary Contracts" domain: netkingdom repo: net-kingdom -status: ready +status: finished owner: codex topic_slug: netkingdom planning_priority: high @@ -68,7 +68,7 @@ Out of scope: ```task id: NK-WP-0014-T1 -status: todo +status: done priority: high state_hub_task_id: "4e941174-ac55-4f6e-8568-40f45b1ed821" ``` @@ -84,7 +84,7 @@ be hardened into versioned contracts as implementation feedback arrives. ```task id: NK-WP-0014-T2 -status: todo +status: done priority: high state_hub_task_id: "5ddc46bb-6238-4944-a023-d8b46b410c76" ``` @@ -96,7 +96,7 @@ tenant boundaries are represented. ```task id: NK-WP-0014-T3 -status: todo +status: done priority: high state_hub_task_id: "696bc65b-0f8d-47b5-bccc-65ed285b42e6" ``` @@ -108,7 +108,7 @@ for adding one new application safely. ```task id: NK-WP-0014-T4 -status: todo +status: done priority: high state_hub_task_id: "99ab4535-cbbf-4e13-a2c5-adfc814d5aeb" ``` @@ -120,7 +120,7 @@ claims enrichment is adapter-owned and cache/freshness governed. ```task id: NK-WP-0014-T5 -status: todo +status: done priority: medium state_hub_task_id: "d35a1356-a9fe-4cf3-8fb0-6f45cfbbccee" ``` @@ -132,7 +132,7 @@ decision IDs and platform audit sinks. ```task id: NK-WP-0014-T6 -status: todo +status: done priority: medium state_hub_task_id: "04fd2f92-3aa4-468d-9e9e-9b092890507e" ``` @@ -157,8 +157,25 @@ NetKingdom documents per ADR-0010. - NetKingdom responsibility-map updates are either applied or explicitly deferred until user-engine becomes a shared platform service. +## Completion Notes + +Completed on 2026-05-22: + +- Added the canonical versioned contract at + `canon/standards/user-engine-boundary-contract_v0.1.md`. +- Promoted `docs/user-engine-interface-guidance.md` to an implementation guide + that points at the canonical contract. +- Confirmed `docs/responsibility-map.md` already includes `user-engine` as an + orchestrated repository and names NetKingdom's boundary responsibilities. +- Confirmed `/home/worsch/user-engine` already carries the required repo-local + preparation artifacts: `SCOPE.md`, `wiki/ArchitectureBlueprint.md`, + `docs/development.md`, `docs/configuration.md`, + `docs/interfaces/netkingdom-integration.md`, and `USER-WP-0001` through + `USER-WP-0006`. + ## Dependencies And Sequencing - Depends on NK-WP-0012 for IAM Profile consumption rules. - Depends on NK-WP-0013 for NetKingdom meta-orchestration conventions. -- Gates NK-WP-0015. +- Gates implementation work in `/home/worsch/user-engine`, now tracked as + `USER-WP-0001` through `USER-WP-0006`.