generated from coulomb/repo-seed
329 lines
18 KiB
Markdown
329 lines
18 KiB
Markdown
---
|
|
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
|
|
- docs/user-engine-netkingdom-integration-assessment.md (current intent/scope fit, gaps, and recommendations)
|
|
---
|
|
|
|
# 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.
|
|
|