| 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.