| OIDC discovery, login, token issuance | `key-cape`, Keycloak | Supply `tenant_roles` via a read API at issuance time; never issues tokens itself |
| Tenant identifier format and grouping vocabulary | NetKingdom (`iam-profile_v0.3.md`, ADR-0013) | Store and validate tenant records against the ratified vocabulary; does not redefine it |
| Tenant capability-role vocabulary | NetKingdom (ADR-0014) | Store role grants against the ratified vocabulary (`PLTF`/`IAM`/`VEN`/`CUS`); does not redefine it |
| Users, profiles, memberships | `user-engine` | References `tenant_id` as an external key only; never stores user or membership records |
| Fine-grained authorization decisions | `flex-auth` | Serves as an authoritative data source `flex-auth` queries live for high-stakes checks; makes no authorization decisions itself |
| Pricing-model / plan definitions | `adaptive-pricing` | References plan definitions by id; never forks or duplicates plan terms locally |
| Runtime secrets | OpenBao / `secrets-engine` | Tenant-scoped OpenBao paths (e.g. `tenants/<name>/...`) reference `tenant-engine`'s tenant registry as the naming authority; `tenant-engine` never stores secret material |
| Credential routing/discovery | `ops-warden` | `tenant-engine`'s own service credentials (if any) are routed the same way as any other lane; `tenant-engine` is not a routing authority |
## Source-Of-Truth Matrix
| Resource kind | Source of truth | tenant-engine relation | Boundary rule |
| --- | --- | --- | --- |
| Tenant record (existence, grouping, name/slug) | `tenant-engine` | Canonical owner | Grouping value must be one of ADR-0013's taxonomy, or the reserved ungrouped `platform`/`coulomb` identifiers |
| Tenant capability roles | `tenant-engine` | Canonical owner | Non-exclusive; grant/revoke are audited mutations (see Grant Contract below), never free-form field updates |
| Plan/subscription assignment | `tenant-engine` | Canonical owner | References an `adaptive-pricing` plan id; does not define plan terms |
| Pricing-model / plan definitions | `adaptive-pricing` | Consume by reference only | `tenant-engine` must not cache plan terms beyond what's needed to resolve which roles a plan currently grants |
| Guardrail/quota policy (spend limits, entity/action counts) | `tenant-engine` (reserved) | Canonical owner once designed | Not implemented by this contract version — reserved namespace only, see Guardrail Policy section |
| `tenant_roles` token claim | `tenant-engine` (live) / `key-cape` (cached copy) | `tenant-engine` is authoritative; the token claim is a point-in-time cache | Privileged/high-stakes decisions MUST re-query `tenant-engine` live — see IAM Profile v0.3, "Tenant Roles" section — never trust the cached claim alone |
| Tenant identifier claim shape (`tenant`) | NetKingdom / IAM Profile contract | `tenant-engine` validates against it; does not mint the claim format | The wire format stays owned by the profile; `tenant-engine` owns which concrete tenant values currently exist |
| User/membership records scoped by a tenant | `user-engine` | No relation | `tenant_id` is the only key shared between the two services; `tenant-engine` never stores or reads user data |
| Authorization decisions | `flex-auth` | Data source only | `tenant-engine` never enforces access itself; it answers queries `flex-auth`'s policy packages issue |
## Tenant Role & Plan Grant Contract
Every role grant or revocation is an audited mutation, not a direct field
write, and must carry:
| Field | Requirement |
| --- | --- |
| `tenant_id` | Stable tenant identifier |
| `role` | One of `PLTF`, `IAM`, `VEN`, `CUS` |
| `grant_reason` | `plan_assignment`, `manual_grant`, or `platform_default` |
| `plan_id` | `adaptive-pricing` plan id; required when `grant_reason: plan_assignment`, otherwise null |
| `granted_by` | Subject or system that performed the grant |
| `granted_at` | Timestamp |
| `revoked_at` | Nullable timestamp |
| `correlation_id` | Request/import correlation id |
Rules:
- Plan-linked roles (starting with `IAM`) are granted and revoked
automatically as a consequence of plan-assignment changes in
`tenant-engine`; manual grants are the exception path and must always
record `granted_by`.
-`trial`-grouped tenants may hold any role without a plan requirement —
`grant_reason: platform_default` is valid for trial-eligible roles with no
`plan_id` (ADR-0014).
- Every grant or revocation emits a domain event. After a revocation,
`key-cape`'s previously-cached `tenant_roles` claim is considered stale
until the token naturally expires — v0.1 relies on the profile's short
token lifetimes for bounded staleness, not push invalidation.
- Cross-tenant grants are rejected; a grant always targets exactly one
`tenant_id`.
## Authorization Contract
`tenant-engine` is a data source, not a policy enforcement point.
`flex-auth` remains the policy decision point for authorization outcomes;
`key-cape` remains the token issuer. `tenant-engine` exposes:
- a **read API**`key-cape` calls at token-issuance time to source the
cached `tenant_roles` claim;
- a **live-lookup API**`flex-auth` calls for high-stakes decisions
(`assurance.level >= aal2`-class actions, per IAM Profile v0.3), returning
current roles, grouping, and plan state;
- a **write API**, authorization-gated by `flex-auth` itself, for grant/
revoke/plan-assignment mutations — `tenant-engine` does not self-authorize