ADR-0014 + tenant-engine boundary contract + IAM Profile v0.3
Ratifies the tenant capability-role model (PLTF/IAM/VEN/CUS, non-exclusive, independent of ADR-0013's grouping axis), a hybrid carrying mechanism (tenant-engine authoritative, key-cape caches a tenant_roles claim at issuance, flex-auth re-validates live for aal2-class decisions), and tenant-engine as a new, separate service owning tenant existence, grouping, capability roles, and plan/subscription assignment -- not a module inside user-engine, whose own boundary contract already scopes it to consuming tenant identifiers, not owning them. canon/standards/tenant-engine-boundary-contract_v0.1.md defines that ownership boundary before the repo exists, mirroring how user-engine-boundary-contract_v0.1.md was sequenced. canon/standards/iam-profile_v0.3.md (minor version per ADR-0011's own governance -- optional claim addition, no breaking change) adds the tenant_roles claim, folds in ADR-0013's tenant-identifier vocabulary, and documents the live-revalidation requirement. docs/platform-identity- security-architecture.md's Tenant Model section and SCOPE.md's canonical spec pointer updated to match; other historical citations of v0.2 left as version-pinned references, not bulk-updated. Records Bernd's trial-tenant policy: trial-grouped tenants may hold any capability role (showcase/test/explore), with safety enforced through tenant-engine-owned resource guardrails (spend limits, entity/action counts) rather than role gating -- guardrail design is reserved, explicitly not specified by this change. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
a18f83c4af
commit
9026d7f904
5 changed files with 838 additions and 7 deletions
2
SCOPE.md
2
SCOPE.md
|
|
@ -21,7 +21,7 @@ NetKingdom is a self-optimizing security platform for Kubernetes-based IT infras
|
|||
## In Scope
|
||||
|
||||
- NetKingdom IAM Profile specification (versioned OIDC/PKCE contract;
|
||||
canonical spec: `canon/standards/iam-profile_v0.2.md`)
|
||||
canonical spec: `canon/standards/iam-profile_v0.3.md`)
|
||||
- SSO/MFA Platform: Keycloak with LDAP/Entra federation, enterprise identity (NK-WP-0001, finished)
|
||||
- Local Identity: file-based user store + minimal OIDC server for bootstrap phase (NK-WP-0002, finished)
|
||||
- User Engine Boundary Contract: source-of-truth, membership,
|
||||
|
|
|
|||
473
canon/standards/iam-profile_v0.3.md
Normal file
473
canon/standards/iam-profile_v0.3.md
Normal file
|
|
@ -0,0 +1,473 @@
|
|||
---
|
||||
id: netkingdom-iam-profile
|
||||
type: standard
|
||||
title: "NetKingdom IAM Profile v0.3"
|
||||
domain: netkingdom
|
||||
status: accepted
|
||||
version: "0.3"
|
||||
created: "2026-07-23"
|
||||
updated: "2026-07-23"
|
||||
scope: core-platform
|
||||
supersedes:
|
||||
- canon/standards/iam-profile_v0.2.md
|
||||
adr:
|
||||
- docs/adr/ADR-0011-iam-profile-ownership-and-version-governance.md
|
||||
- docs/adr/ADR-0013-tenant-onboarding-grouping-taxonomy.md
|
||||
- docs/adr/ADR-0014-tenant-capability-roles-and-tenant-engine-ownership.md
|
||||
---
|
||||
|
||||
# NetKingdom IAM Profile v0.3
|
||||
|
||||
> Minor version. Per ADR-0011's versioning rule, this adds an optional claim
|
||||
> and clarifies non-normative guidance — no required claim, validation rule,
|
||||
> or previously-issued token is invalidated. Existing v0.2 implementations
|
||||
> remain conformant; `tenant_roles` and the revised Tenant Claim guidance are
|
||||
> additive.
|
||||
|
||||
## Purpose
|
||||
|
||||
The NetKingdom IAM Profile is the provider-neutral OIDC contract that
|
||||
identity implementations issue and applications consume.
|
||||
|
||||
It defines:
|
||||
|
||||
- OIDC discovery and endpoint requirements;
|
||||
- Authorization Code + PKCE for human login;
|
||||
- service-account and workload identity token requirements;
|
||||
- human, service, and agent principal representation;
|
||||
- tenant, tenant-grouping, and platform-boundary claims;
|
||||
- tenant capability roles and their carrying mechanism;
|
||||
- explicit assurance evidence;
|
||||
- the identity-to-authorization claim contract consumed by flex-auth;
|
||||
- local-development and emergency-access behavior;
|
||||
- executable conformance expectations.
|
||||
|
||||
Applications target this profile, not a concrete identity provider.
|
||||
key-cape is the lightweight implementation. Keycloak is the expanded-mode
|
||||
implementation. Both are interchangeable at the application and
|
||||
authorization boundary when they conform to this document.
|
||||
|
||||
## Ownership
|
||||
|
||||
NetKingdom owns the core/platform profile. See ADR-0011.
|
||||
|
||||
Downstream systems may define extension scopes, roles, resource names,
|
||||
and tenant policy vocabularies. Those extensions are not part of the core
|
||||
profile unless a future version explicitly adopts them. Extension
|
||||
vocabularies must map back to the core claims in this document before
|
||||
flex-auth or applications consume them.
|
||||
|
||||
## Design Principles
|
||||
|
||||
- Consumers trust signed OIDC tokens, not provider-specific sessions.
|
||||
- Identity providers assert identity and authentication evidence; they do
|
||||
not make final resource authorization decisions.
|
||||
- The same profile works in lightweight key-cape mode and expanded
|
||||
Keycloak mode.
|
||||
- Tenancy is explicit. `tenant:platform` is distinct from tenant planes
|
||||
such as `tenant:coulomb` and from later tenants grouped per ADR-0013.
|
||||
- A tenant's onboarding **grouping** (ADR-0013) and its **capability
|
||||
roles** (ADR-0014) are independent axes. Neither is encoded in the other;
|
||||
a tenant's roles may change without renaming its identifier.
|
||||
- Human, service, and agent principals are distinguishable.
|
||||
- Assurance evidence is explicit enough for flex-auth policy.
|
||||
- Local-development issuers are useful but never accepted by production.
|
||||
- Emergency access is auditable, time-bounded, and reviewable.
|
||||
|
||||
## Discovery Contract
|
||||
|
||||
Every IAM Profile implementation MUST expose OIDC discovery at:
|
||||
|
||||
```text
|
||||
GET <issuer>/.well-known/openid-configuration
|
||||
```
|
||||
|
||||
The discovery response MUST include:
|
||||
|
||||
| Field | Requirement |
|
||||
| --- | --- |
|
||||
| `issuer` | Exact issuer identifier used in tokens |
|
||||
| `authorization_endpoint` | Required for human Authorization Code + PKCE |
|
||||
| `token_endpoint` | Required for token exchange and service accounts |
|
||||
| `jwks_uri` | Required for signature validation |
|
||||
| `userinfo_endpoint` | Required when userinfo is supported by the flow |
|
||||
| `scopes_supported` | MUST include `openid`; SHOULD include `profile` and `email` |
|
||||
| `response_types_supported` | MUST include `code` |
|
||||
| `grant_types_supported` | MUST include `authorization_code`; MUST include `client_credentials` or a documented workload-token exchange for service identities |
|
||||
| `id_token_signing_alg_values_supported` | MUST include the implementation signing algorithm; RS256 is required for v0.2+ conformance |
|
||||
| `code_challenge_methods_supported` | MUST include `S256` |
|
||||
|
||||
The response SHOULD include `end_session_endpoint` where logout is
|
||||
supported and `claims_supported` listing the core claims below.
|
||||
|
||||
Consumers MUST discover endpoints and key material from the issuer
|
||||
metadata instead of hardcoding provider-specific paths.
|
||||
|
||||
## Required Flows
|
||||
|
||||
### Human Interactive Flow
|
||||
|
||||
Human users authenticate with Authorization Code + PKCE.
|
||||
|
||||
Required properties:
|
||||
|
||||
- PKCE with `S256` is mandatory for browser and CLI clients.
|
||||
- Implicit flow is not part of the profile.
|
||||
- MFA or equivalent strong assurance is mandatory for privileged,
|
||||
destructive, platform-root, and emergency access in production.
|
||||
- Access tokens are short-lived.
|
||||
- Refresh tokens are allowed only for trusted clients with explicit
|
||||
rotation and revocation.
|
||||
|
||||
### Service Account Flow
|
||||
|
||||
Service-to-service traffic uses client credentials or a deployment's
|
||||
documented workload identity token-exchange equivalent.
|
||||
|
||||
Required properties:
|
||||
|
||||
- Service subjects are stable and named for service plus environment.
|
||||
- Secrets or workload credentials are delivered through the
|
||||
credential-management standard, not plaintext configuration.
|
||||
- Tokens include an audience that identifies the target service.
|
||||
- Tokens carry `principal_type: service`.
|
||||
- Service accounts receive only required scopes and roles.
|
||||
- Credentials are rotated and never shared between environments.
|
||||
|
||||
### Agent Principal Flow
|
||||
|
||||
Agents are automation principals that may act autonomously or under
|
||||
delegated authority.
|
||||
|
||||
Required properties:
|
||||
|
||||
- Tokens carry `principal_type: agent`.
|
||||
- Tokens include an `agent` object with `id` and `mode`.
|
||||
- `agent.mode` is `autonomous` or `delegated`.
|
||||
- Delegated agents MUST identify the delegating actor using `actor_sub`
|
||||
or an equivalent `act.sub` claim.
|
||||
- Agent tokens MUST carry the tenant they operate within.
|
||||
- Agent tokens MUST include assurance evidence for both the agent
|
||||
credential and any delegated human authority when policy needs it.
|
||||
|
||||
## Core Claims
|
||||
|
||||
Access tokens accepted by production consumers MUST provide the following
|
||||
claims after provider mapping or normalization:
|
||||
|
||||
| Claim | Type | Meaning |
|
||||
| --- | --- | --- |
|
||||
| `iss` | string | OIDC issuer URL or issuer identifier |
|
||||
| `sub` | string | Stable subject identifier unique within `iss` |
|
||||
| `aud` | string or array | Intended audience; MUST include the receiving service |
|
||||
| `exp` | number | Expiry timestamp |
|
||||
| `iat` | number | Issued-at timestamp |
|
||||
| `nbf` | number | Not-before timestamp, recommended for production tokens |
|
||||
| `jti` | string | Token identifier, recommended for audit and replay controls |
|
||||
| `tenant` | string | Tenant identifier such as `tenant:platform` or `tenant:friendly:binky` |
|
||||
| `principal_type` | string | `human`, `service`, or `agent` |
|
||||
| `groups` | array | Group memberships, possibly empty |
|
||||
| `roles` | array | Coarse **identity** roles for the subject, possibly empty |
|
||||
| `scope` or `scp` | string or array | Granted OAuth scopes |
|
||||
| `assurance` | object | Authentication and credential assurance evidence |
|
||||
|
||||
Recommended human claims:
|
||||
|
||||
| Claim | Meaning |
|
||||
| --- | --- |
|
||||
| `preferred_username` | Human-readable username |
|
||||
| `email` | Contact identity |
|
||||
| `name` | Display name |
|
||||
|
||||
Recommended service claims:
|
||||
|
||||
| Claim | Meaning |
|
||||
| --- | --- |
|
||||
| `azp` or `client_id` | Authorized client/service identifier |
|
||||
| `service` | Object naming the service and environment |
|
||||
|
||||
Recommended agent claims:
|
||||
|
||||
| Claim | Meaning |
|
||||
| --- | --- |
|
||||
| `agent.id` | Stable agent identifier |
|
||||
| `agent.mode` | `autonomous` or `delegated` |
|
||||
| `actor_sub` or `act.sub` | Delegating subject for delegated agents |
|
||||
|
||||
Optional claims (new in v0.3):
|
||||
|
||||
| Claim | Type | Meaning |
|
||||
| --- | --- | --- |
|
||||
| `tenant_roles` | array | Cached tenant **capability** roles (`PLTF`/`IAM`/`VEN`/`CUS`), possibly empty. See "Tenant Roles" below — this is a point-in-time cache, not the authoritative source. |
|
||||
|
||||
### Role Claim
|
||||
|
||||
The canonical **subject**-level role claim is `roles`, an array of strings.
|
||||
This is distinct from `tenant_roles` (below) — a subject's own coarse
|
||||
identity roles are not the same fact as which capability roles the
|
||||
subject's tenant holds.
|
||||
|
||||
Expanded-mode Keycloak deployments may also expose provider-native roles
|
||||
such as `realm_access.roles`, but conforming tokens consumed by flex-auth
|
||||
or applications MUST either emit `roles` directly or pass through a
|
||||
normalizing adapter that produces `roles`.
|
||||
|
||||
### Scope Vocabulary
|
||||
|
||||
The core profile defines only OAuth/OIDC base scopes:
|
||||
|
||||
| Scope | Meaning |
|
||||
| --- | --- |
|
||||
| `openid` | Required for OIDC login |
|
||||
| `profile` | Basic profile claims |
|
||||
| `email` | Email claim where appropriate |
|
||||
| `offline_access` | Refresh-token capable access where explicitly allowed |
|
||||
|
||||
Hub-, application-, and resource-specific scopes such as `hub:*`,
|
||||
`ops:*`, `fin:*`, or storage actions are downstream extensions. They are
|
||||
valid only when the consuming system defines them and maps them to
|
||||
flex-auth resource/action semantics.
|
||||
|
||||
## Tenant Claim
|
||||
|
||||
`tenant` is required for every token accepted by profile consumers.
|
||||
|
||||
Tenant identifiers follow `tenant:<grouping>:<name>`, where `<grouping>` is
|
||||
one of the taxonomy ratified by ADR-0013:
|
||||
|
||||
```text
|
||||
trial - test/trial/showcase tenants only
|
||||
friendly - known, easily reached, tolerant of experimentation/instability
|
||||
single - one-person business entities (freelance consultants)
|
||||
small - up to 10 employees at time of onboarding (attoo)
|
||||
medium - up to 100 employees (attoo)
|
||||
large - up to 1000 employees (attoo)
|
||||
enterprise - 1001+ employees (attoo)
|
||||
consumer - private individuals
|
||||
family - a legal family
|
||||
community - a non-legal group of people
|
||||
association - a legal association of people
|
||||
agentic - financially enabled AI entities
|
||||
```
|
||||
|
||||
`tenant:platform` and `tenant:coulomb` remain **reserved, ungrouped**
|
||||
identifiers outside this taxonomy: `tenant:platform` is the platform
|
||||
control-plane tenant, not a business entity being onboarded; `tenant:coulomb`
|
||||
is the first internal/reference tenant established by ADR-0006, predating
|
||||
this taxonomy. Tenant administration for `tenant:coulomb` or any grouped
|
||||
tenant must never imply platform-root authority.
|
||||
|
||||
Subjects may have access to multiple tenants, but a token used for a
|
||||
request MUST identify the tenant context for that request. If a client
|
||||
needs to switch tenant context, it obtains a new token or uses an
|
||||
approved token-exchange flow that records the target tenant.
|
||||
|
||||
The grouping segment is **onboarding-risk / entity-shape** classification
|
||||
only. It does not gate which capability roles (below) a tenant may hold —
|
||||
see Tenant Roles.
|
||||
|
||||
## Tenant Roles
|
||||
|
||||
Tenant **capability roles** are a separate fact from the grouping above and
|
||||
from the subject-level `roles` claim: `PLTF`, `IAM`, `VEN`, `CUS`
|
||||
(ratified by ADR-0014), non-exclusive — a tenant may hold several
|
||||
simultaneously, and holding one does not require or restrict any grouping.
|
||||
|
||||
**Source of truth:** `tenant-engine`
|
||||
(`canon/standards/tenant-engine-boundary-contract_v0.1.md`), not this
|
||||
profile and not any token. `tenant-engine` records role grants/revocations,
|
||||
their link (if any) to a plan/subscription, and emits domain events on
|
||||
change.
|
||||
|
||||
**Carrying mechanism — hybrid, not claim-only:**
|
||||
|
||||
- `key-cape` (or Keycloak) MAY stamp a cached `tenant_roles` claim onto an
|
||||
issued token at issuance time, sourced from `tenant-engine`.
|
||||
- Consumers MAY trust the cached claim for ordinary, non-privileged
|
||||
decisions.
|
||||
- Consumers **MUST NOT** trust the cached claim for privileged, destructive,
|
||||
platform-root, secret, credential-vending, or otherwise `assurance.level
|
||||
>= aal2`-class decisions. Those decisions **MUST** query `tenant-engine`
|
||||
live for current role state before authorizing the action.
|
||||
- This bounds staleness for ordinary actions to the issuing token's
|
||||
lifetime (5-30 minutes for service/agent tokens, see Token Lifecycle)
|
||||
while guaranteeing freshness exactly where a stale grant (e.g. `VEN`
|
||||
surviving a plan cancellation) would matter most.
|
||||
|
||||
`trial`-grouped tenants may hold any capability role without restriction —
|
||||
the grouping exists to showcase, test, and explore every role. Safety for
|
||||
`trial` tenants is enforced through `tenant-engine`-owned resource
|
||||
guardrails (spend limits, entity/action counts — reserved, not yet
|
||||
specified), not through role gating.
|
||||
|
||||
## Assurance Evidence
|
||||
|
||||
The canonical assurance claim is `assurance`.
|
||||
|
||||
It is an object with these fields:
|
||||
|
||||
| Field | Type | Meaning |
|
||||
| --- | --- | --- |
|
||||
| `level` | string | `aal0`, `aal1`, `aal2`, `aal3`, or `break_glass` |
|
||||
| `methods` | array | Authentication methods, e.g. `pwd`, `otp`, `webauthn`, `client_secret`, `workload_identity`, `upstream_mfa` |
|
||||
| `mfa` | boolean | Whether the authentication included multiple factors or equivalent upstream evidence |
|
||||
| `source` | string | Provider of the evidence, e.g. `key-cape`, `keycloak`, `privacyidea`, `entra`, `local-identity` |
|
||||
| `at` | number | Authentication time, recommended |
|
||||
|
||||
Level meanings:
|
||||
|
||||
| Level | Meaning |
|
||||
| --- | --- |
|
||||
| `aal0` | Local/dev or unauthenticated bootstrap evidence; never production privileged |
|
||||
| `aal1` | Single-factor or service credential evidence |
|
||||
| `aal2` | MFA or equivalent strong upstream assurance |
|
||||
| `aal3` | Phishing-resistant or hardware-backed assurance |
|
||||
| `break_glass` | Time-bounded emergency access with post-event review |
|
||||
|
||||
Privileged, destructive, platform-root, secret, credential-vending, and
|
||||
emergency flows require `aal2` or stronger unless a policy explicitly
|
||||
permits a narrower service or workload identity path. Emergency access
|
||||
MUST use `break_glass` and short token lifetimes. This is the same
|
||||
threshold class that gates when `tenant_roles` MUST be re-validated live
|
||||
against `tenant-engine` rather than trusted from the token — see Tenant
|
||||
Roles.
|
||||
|
||||
Provider-native claims such as `acr` and `amr` may be present, but
|
||||
consumers use `assurance` as the normalized profile claim.
|
||||
|
||||
## Identity To Authorization Contract
|
||||
|
||||
flex-auth consumes IAM Profile tokens as normative identity input.
|
||||
flex-auth MUST NOT re-derive identity, tenant, group, role, or assurance
|
||||
facts from provider-specific session state.
|
||||
|
||||
The profile guarantees these inputs for authorization decision envelopes:
|
||||
|
||||
| Decision input | Source claim |
|
||||
| --- | --- |
|
||||
| Subject | `sub` |
|
||||
| Issuer | `iss` |
|
||||
| Audience | `aud` |
|
||||
| Tenant | `tenant` |
|
||||
| Principal type | `principal_type` |
|
||||
| Groups | `groups` |
|
||||
| Subject roles | `roles` |
|
||||
| Tenant capability roles | `tenant_roles` (cached); `tenant-engine` live lookup required for high-stakes decisions |
|
||||
| Scopes | `scope` or `scp` |
|
||||
| Assurance | `assurance` |
|
||||
| Authorized client | `azp` or `client_id`, where present |
|
||||
| Agent/delegation context | `agent`, `actor_sub`, or `act`, where present |
|
||||
| Token lifetime/audit ids | `iat`, `nbf`, `exp`, `jti`, where present |
|
||||
|
||||
Authorization decisions are made by flex-auth and its delegated PDP
|
||||
adapters. Identity providers may assert roles or scopes, but those claims
|
||||
are inputs to policy, not final permission to act on a resource.
|
||||
|
||||
## Token Lifecycle
|
||||
|
||||
Recommended production defaults:
|
||||
|
||||
| Token | Lifetime | Notes |
|
||||
| --- | --- | --- |
|
||||
| Human access token | 5-15 minutes | Short-lived bearer token |
|
||||
| Refresh token | 8-12 hours | Rotated and revoked on logout or suspicion |
|
||||
| Service token | 5-30 minutes | Reissued by client credentials or workload identity |
|
||||
| Agent token | 5-30 minutes | Shorter when delegated or platform-scoped |
|
||||
| Emergency token | 5-15 minutes | Requires incident/review record |
|
||||
|
||||
Consumers MUST reject expired tokens and tokens with invalid issuer,
|
||||
audience, signature, `nbf`, or algorithm. Clock skew tolerance SHOULD be
|
||||
small, normally no more than 60 seconds.
|
||||
|
||||
JWKS material may be cached, but consumers MUST tolerate key rotation by
|
||||
refreshing JWKS when a token uses an unknown `kid`.
|
||||
|
||||
## Local Development Profile
|
||||
|
||||
A local file-backed provider may be used for development, tests, and
|
||||
bootstrap contexts where the full platform is unavailable.
|
||||
|
||||
It MUST:
|
||||
|
||||
- expose OIDC discovery;
|
||||
- issue signed JWTs;
|
||||
- support deterministic test users and service accounts;
|
||||
- use local-only issuer URLs or a clearly local issuer identifier;
|
||||
- mark tokens as local/development through issuer, audience, or
|
||||
assurance evidence;
|
||||
- be rejected by production consumers.
|
||||
|
||||
Production consumers MUST reject:
|
||||
|
||||
- issuer `local-identity`;
|
||||
- `http://` issuers;
|
||||
- loopback issuers such as `localhost` or `127.0.0.1`;
|
||||
- tokens with `assurance.level: aal0`;
|
||||
- tokens where the environment marks the issuer as local/dev.
|
||||
|
||||
## Emergency And Break-Glass Access
|
||||
|
||||
Emergency access is allowed only as a break-glass path.
|
||||
|
||||
Requirements:
|
||||
|
||||
- Emergency identities are disabled by default.
|
||||
- Activation requires an incident, decision, or human-recorded review
|
||||
reference.
|
||||
- Tokens are short-lived and carry the `emergency` role.
|
||||
- Tokens carry `assurance.level: break_glass`.
|
||||
- Every emergency action emits an audit/progress/incident event.
|
||||
- Emergency access is reviewed after use and then disabled again.
|
||||
|
||||
Emergency access MUST NOT bypass audit logging or flex-auth policy.
|
||||
|
||||
## Conformance
|
||||
|
||||
An implementation conforms to IAM Profile v0.3 when it passes the
|
||||
executable conformance suite in:
|
||||
|
||||
```text
|
||||
tools/iam-profile-conformance/
|
||||
```
|
||||
|
||||
The suite validates:
|
||||
|
||||
- discovery document completeness;
|
||||
- PKCE `S256` advertisement and rejection of authorization requests that
|
||||
omit a code challenge;
|
||||
- JWKS structure and key ids;
|
||||
- token issuer, audience, expiry, `nbf`, `iat`, and RS256 signature;
|
||||
- tenant, principal type, groups, roles, scopes, and assurance claim
|
||||
shape;
|
||||
- `tenant_roles` claim shape when present (array of ratified role strings);
|
||||
- agent and delegated-agent claim shape;
|
||||
- local-development issuer rejection in production mode.
|
||||
|
||||
Conformance must be runnable against both key-cape lightweight issuers
|
||||
and Keycloak expanded-mode issuers. Implementations may add provider
|
||||
adapters, but the token consumed by applications and flex-auth must match
|
||||
the core claim contract above. `tenant_roles` conformance does not require
|
||||
an implementation to emit the claim (it is optional); when emitted, it
|
||||
must match the ratified vocabulary.
|
||||
|
||||
## Validation Checklist
|
||||
|
||||
A service or implementation is profile-ready when:
|
||||
|
||||
- it reads OIDC discovery rather than hardcoding endpoints;
|
||||
- it validates issuer, audience, expiry, `nbf`, algorithm, and
|
||||
signature;
|
||||
- it refreshes JWKS on unknown `kid`;
|
||||
- it supports Authorization Code + PKCE for human login;
|
||||
- it supports service-account or workload identity tokens;
|
||||
- it emits `tenant`, `principal_type`, `groups`, `roles`,
|
||||
`scope`/`scp`, and `assurance`;
|
||||
- it uses the ADR-0013 grouping vocabulary for new tenant identifiers;
|
||||
- if it consumes `tenant_roles`, it treats the claim as a cache and
|
||||
re-validates live against `tenant-engine` before any `aal2`-class
|
||||
decision;
|
||||
- it maps provider-native claims into the canonical core claims;
|
||||
- it rejects local-development issuers in production;
|
||||
- it logs emergency access with a durable audit trail;
|
||||
- flex-auth receives identity facts from the profile, not from
|
||||
provider-specific sessions.
|
||||
163
canon/standards/tenant-engine-boundary-contract_v0.1.md
Normal file
163
canon/standards/tenant-engine-boundary-contract_v0.1.md
Normal file
|
|
@ -0,0 +1,163 @@
|
|||
---
|
||||
id: netkingdom-tenant-engine-boundary-contract
|
||||
type: standard
|
||||
title: "NetKingdom Tenant Engine Boundary Contract v0.1"
|
||||
domain: netkingdom
|
||||
status: accepted
|
||||
version: "0.1"
|
||||
created: "2026-07-23"
|
||||
updated: "2026-07-23"
|
||||
scope: tenant-domain-boundaries
|
||||
adr:
|
||||
- docs/adr/ADR-0006-recursive-multi-tenant-identity-authorization.md
|
||||
- docs/adr/ADR-0013-tenant-onboarding-grouping-taxonomy.md
|
||||
- docs/adr/ADR-0014-tenant-capability-roles-and-tenant-engine-ownership.md
|
||||
related:
|
||||
- canon/standards/iam-profile_v0.3.md
|
||||
- canon/standards/user-engine-boundary-contract_v0.1.md
|
||||
- canon/standards/credential-management_v0.2.md
|
||||
- docs/platform-identity-security-architecture.md
|
||||
---
|
||||
|
||||
# NetKingdom Tenant Engine Boundary Contract v0.1
|
||||
|
||||
## Purpose
|
||||
|
||||
This contract defines how `tenant-engine` integrates into the NetKingdom
|
||||
landscape without duplicating identity, authorization, user/membership,
|
||||
pricing, deployment, or secret-custody responsibilities that other services
|
||||
already own.
|
||||
|
||||
`tenant-engine` owns **tenant-as-an-entity** facts: existence, identifier
|
||||
and grouping (ADR-0013), capability roles (ADR-0014), plan/subscription
|
||||
assignment, and — reserved, not yet implemented — guardrail/quota policy.
|
||||
It is not an identity provider, not a policy decision point, not a billing
|
||||
processor, not a pricing-model engine, and not the user/membership domain.
|
||||
NetKingdom owns this cross-repo boundary contract; `tenant-engine` and its
|
||||
adapters must preserve the ownership rules below even in local/standalone
|
||||
deployments.
|
||||
|
||||
## Ownership Model
|
||||
|
||||
| Concern | Owner | tenant-engine responsibility |
|
||||
| --- | --- | --- |
|
||||
| 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 |
|
||||
| Deployment mechanics | Railiance | Publishes readiness signals only |
|
||||
| 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
|
||||
its own writes.
|
||||
|
||||
Performance model:
|
||||
|
||||
- the live-lookup API must be fast and highly available, since `flex-auth`
|
||||
calls it synchronously on the request path for privileged actions; slow
|
||||
or unavailable `tenant-engine` lookups fail closed (deny), never fail open;
|
||||
- the cached-claim read API used by `key-cape` may be slower, since it only
|
||||
runs at token issuance, not per-request;
|
||||
- request-scoped memoization within a single `flex-auth` decision is
|
||||
allowed; cross-request caching of live-lookup results is not, since it
|
||||
would recreate the staleness risk the live path exists to avoid.
|
||||
|
||||
## Guardrail Policy (Reserved, Not Implemented)
|
||||
|
||||
Named here so the shape exists before it's designed, matching ADR-0014:
|
||||
|
||||
- Spend limits — `trial`-grouped tenants default to a **zero** spend budget.
|
||||
- Entity count limits — reserved, not yet specified.
|
||||
- Action count limits — reserved, not yet specified.
|
||||
- Enforcement point, override process, and how guardrails interact with
|
||||
capability roles (a `trial` tenant holding `VEN` is still bound by `trial`
|
||||
guardrails) are all open design questions for `tenant-engine`'s own
|
||||
workplan, not resolved by this contract.
|
||||
|
||||
## Audit Correlation Contract
|
||||
|
||||
Every `tenant-engine` mutation (tenant creation, role grant/revoke, plan
|
||||
assignment) must create or reference a correlation bundle:
|
||||
|
||||
| Field | Requirement |
|
||||
| --- | --- |
|
||||
| `correlation_id` | Stable id propagated across request, decision, audit, and event records |
|
||||
| `actor` | IAM Profile actor envelope (who performed the mutation) |
|
||||
| `tenant_id` | Tenant affected |
|
||||
| `resource` and `action` | e.g. `tenant-engine:role`, `grant` / `revoke` |
|
||||
| `authorization_decision_id` | `flex-auth` decision id backing the write |
|
||||
| `outbox_event_id` | Event id emitted for the mutation |
|
||||
| `change_summary` | Redacted mutation summary |
|
||||
|
||||
## Non-Goals
|
||||
|
||||
- `tenant-engine` does not process payments or hold financial instruments —
|
||||
it records which plan a tenant is assigned to, sourced from whatever
|
||||
system actually processes payment (not yet identified; out of scope for
|
||||
this contract).
|
||||
- `tenant-engine` does not define pricing models, tiers, or terms —
|
||||
`adaptive-pricing` does.
|
||||
- `tenant-engine` does not store user accounts, profiles, or memberships —
|
||||
`user-engine` does.
|
||||
- `tenant-engine` does not make authorization decisions — `flex-auth` does.
|
||||
|
|
@ -0,0 +1,171 @@
|
|||
# ADR-0014 - Tenant Capability Roles, Carrying Mechanism, and Tenant-Engine Ownership
|
||||
|
||||
**Status:** Accepted
|
||||
**Date:** 2026-07-23
|
||||
**Deciders:** Bernd Worsch, Codex
|
||||
|
||||
## Context
|
||||
|
||||
ADR-0013 introduced the tenant onboarding grouping taxonomy
|
||||
(`trial`/`friendly`/`single`/.../`agentic`), deliberately orthogonal to a
|
||||
separate, unratified **capability-role** model sketched in
|
||||
`docs/princedom-isolation-exploration.md`: `PLTF` (operates the platform),
|
||||
`IAM` (organizes its own users/auth/secrets), `VEN` (provides apps/services
|
||||
to others), `CUS` (consumes apps/services from `PLTF` or `VEN` tenants) —
|
||||
non-exclusive, a tenant may hold several at once.
|
||||
|
||||
That exploration left open where capability roles actually live (a per-token
|
||||
claim vs. a registry), who owns them, how they're granted or revoked, and
|
||||
how this interacts with the IAM Profile's existing `roles` claim — which is
|
||||
a *per-subject* claim ("coarse identity roles" for the human/service/agent
|
||||
holding the token), a different concept from a *per-tenant* capability fact.
|
||||
Conflating the two would be a category error: `roles: ["VEN"]` on a token
|
||||
would ambiguously mean "this subject has vendor-role" vs. "this subject's
|
||||
tenant is a vendor."
|
||||
|
||||
No existing service owns tenant-as-an-entity facts (existence, grouping,
|
||||
capability roles, plan/subscription state) as a queryable resource.
|
||||
`user-engine`'s own boundary contract
|
||||
(`canon/standards/user-engine-boundary-contract_v0.1.md`) explicitly scopes
|
||||
`user-engine` to *consuming* tenant identifiers and *storing tenant-scoped
|
||||
records*, not owning tenant identity or capability facts.
|
||||
|
||||
Bernd's direction (2026-07-23):
|
||||
|
||||
- Implement the previously-discussed hybrid carrying approach: cache a
|
||||
tenant's capability roles on the token for ordinary decisions, but
|
||||
require a live check for critical/high-stakes actions.
|
||||
- Role grants are usually tied to a payment plan — most concretely, `IAM`
|
||||
means the tenant has its own dedicated key-cape/Keycloak instance for
|
||||
isolation, scale, and performance, which is itself a paid capability.
|
||||
- `trial`-grouped tenants may hold **any** capability role without
|
||||
restriction, specifically so the platform can showcase, test, and explore
|
||||
every role. Safety for trial tenants comes from resource guardrails
|
||||
(spend limits defaulting to zero budget, entity/action count limits), not
|
||||
from role gating — guardrail design itself is future work, not this ADR.
|
||||
- A new service, `tenant-engine`, will be built (Bernd) as the owner of this
|
||||
domain, organized beside `user-engine` rather than inside it — smaller,
|
||||
single-purpose services are easier to reason about and drift less, matching
|
||||
the fleet's existing convention (`activity-core`, `audit-core`, `user-engine`,
|
||||
and others).
|
||||
|
||||
## Decision
|
||||
|
||||
1. **Capability-role vocabulary ratified as core NetKingdom vocabulary**:
|
||||
`PLTF`, `IAM`, `VEN`, `CUS`, non-exclusive. `IAM` specifically means: the
|
||||
tenant operates its own dedicated IAM implementation instance (lightweight
|
||||
key-cape or expanded Keycloak) rather than sharing the platform's, for
|
||||
isolation/scale/performance — not "any tenant that happens to have users."
|
||||
|
||||
2. **`tenant-engine` is the canonical owner** of tenant-domain facts: tenant
|
||||
existence, grouping (ADR-0013), capability roles (this ADR), plan/
|
||||
subscription assignment, and — reserved for future design, not built now —
|
||||
guardrail/quota policy. It is a new, separate service, not a module inside
|
||||
`user-engine`. Its ownership boundary is defined in the companion contract,
|
||||
`canon/standards/tenant-engine-boundary-contract_v0.1.md`.
|
||||
|
||||
3. **Carrying mechanism: hybrid cache + live re-validation.** `tenant-engine`
|
||||
is the single source of truth. `key-cape` stamps a cached, optional
|
||||
`tenant_roles` claim onto issued tokens at issuance time, sourced from
|
||||
`tenant-engine` (added to the IAM Profile as a new optional claim —
|
||||
`canon/standards/iam-profile_v0.3.md`). Consumers may trust the cached
|
||||
claim for ordinary decisions. `flex-auth` **MUST** re-validate live
|
||||
against `tenant-engine` — never trust the cached claim alone — before
|
||||
authorizing privileged or high-stakes actions, using the same threshold
|
||||
class the profile already defines for `assurance.level >= aal2`
|
||||
(privileged, destructive, platform-root, secret, credential-vending
|
||||
flows). This bounds staleness risk for ordinary actions to a token's
|
||||
short lifetime (5–30 minutes for service/agent tokens, per the profile's
|
||||
Token Lifecycle table) while guaranteeing freshness exactly where it
|
||||
matters most.
|
||||
|
||||
4. **Role governance is plan-linked.** Granting a role is normally a
|
||||
consequence of a tenant's payment-plan state in `tenant-engine`, not a
|
||||
separate manual workflow — starting with `IAM`. `tenant-engine` records
|
||||
which plan grants which role(s); `adaptive-pricing` remains the source of
|
||||
plan/pricing-model *definitions*, `tenant-engine` owns the tenant's
|
||||
*current* plan/subscription assignment, referenced by id, never
|
||||
duplicated locally. Whether `VEN` needs an approval gate beyond payment
|
||||
(reselling access carries legal/compliance exposure a payment alone
|
||||
doesn't cover) is **not resolved by this ADR** — left to `tenant-engine`'s
|
||||
own workplan.
|
||||
|
||||
5. **Trial tenants may hold any capability role, unrestricted.** The `trial`
|
||||
grouping's purpose (showcase, test, explore) requires demonstrating every
|
||||
role. Safety is enforced through resource guardrails instead: trial
|
||||
tenants default to a **spend budget of zero**, with entity and action
|
||||
count limits to follow. Guardrail policy design (exact limits,
|
||||
enforcement point, override process) is real, near-term future work,
|
||||
reserved as `tenant-engine`'s to own once designed — not specified by
|
||||
this ADR.
|
||||
|
||||
6. **Grouping and capability role are independent axes** recorded on the
|
||||
same tenant record in `tenant-engine`. Neither constrains the other
|
||||
except where a future guardrail policy explicitly says so.
|
||||
|
||||
## Consequences
|
||||
|
||||
- `canon/standards/iam-profile_v0.3.md` adds the optional `tenant_roles`
|
||||
claim and folds in ADR-0013's tenant-identifier vocabulary update (both
|
||||
non-breaking per ADR-0011's own minor-version rule — no existing
|
||||
implementation is invalidated by either change). Supersedes
|
||||
`iam-profile_v0.2.md`.
|
||||
- `tenant-engine` becomes a new repository with its own workplans (Bernd).
|
||||
`canon/standards/tenant-engine-boundary-contract_v0.1.md` defines its
|
||||
ownership boundary now, before code exists — the same sequencing
|
||||
`user-engine`'s contract followed.
|
||||
- `flex-auth` policy packages gating high-stakes actions must add a
|
||||
`tenant-engine` live-lookup step; they cannot trust `tenant_roles` alone
|
||||
for those decisions.
|
||||
- `key-cape` needs a `tenant-engine` integration at token-issuance time to
|
||||
source the cached claim — tracked in `key-cape`'s own workplans, not here.
|
||||
- `docs/platform-identity-security-architecture.md`'s Tenant Model section
|
||||
is updated to reflect the grouping + role split and `tenant-engine`'s role
|
||||
(companion change alongside this ADR).
|
||||
- Guardrail/quota policy is named as required near-term work and given an
|
||||
owner (`tenant-engine`), but is explicitly not designed by this ADR.
|
||||
|
||||
## Alternatives Considered
|
||||
|
||||
### Token-claim-only, no live re-validation
|
||||
|
||||
Rejected: staleness would be unbounded within a token's lifetime for
|
||||
genuinely high-stakes actions. A stale `VEN` grant surviving a plan
|
||||
downgrade or cancellation is not an acceptable risk for money-movement or
|
||||
credential-vending flows — exactly the class the profile already treats as
|
||||
requiring the strongest assurance.
|
||||
|
||||
### Registry-only, no cached claim
|
||||
|
||||
Rejected: every ordinary request would pay a `tenant-engine` round-trip even
|
||||
for non-critical checks, adding latency and a hard runtime dependency for
|
||||
every consumer, not just the ones handling privileged actions.
|
||||
|
||||
### Restrict role eligibility by tenant grouping (e.g., `trial` cannot hold `VEN`)
|
||||
|
||||
Rejected per Bernd's direction: `trial` tenants exist specifically to
|
||||
showcase every role. Resource guardrails are the intended safety mechanism
|
||||
instead, keeping the two axes (grouping, role) independent.
|
||||
|
||||
### Put tenant-role/plan storage inside `user-engine`
|
||||
|
||||
Rejected. `user-engine`'s own boundary contract scopes it to consuming
|
||||
tenant identifiers and storing tenant-*scoped* user records, not owning
|
||||
tenant-as-an-entity facts. A dedicated service avoids coupling a
|
||||
security-critical, high-frequency lookup (used by `flex-auth` on every
|
||||
privileged decision, and by `key-cape` on every token issuance) to
|
||||
`user-engine`'s much larger surface (registration flows, factor models,
|
||||
family dataspace onboarding) that has nothing to do with tenant capability
|
||||
state.
|
||||
|
||||
## Follow-Up
|
||||
|
||||
- `tenant-engine` repository creation and its own workplan (Bernd).
|
||||
- `key-cape` integration: source `tenant_roles` from `tenant-engine` at
|
||||
token issuance.
|
||||
- `flex-auth` policy package updates: live `tenant-engine` re-validation
|
||||
gate for privileged actions.
|
||||
- Guardrail/quota policy design for `trial` (and eventually all) tenants:
|
||||
spend limits, entity/action count limits, enforcement point, override
|
||||
process.
|
||||
- Resolve whether `VEN` needs an approval gate beyond payment-plan state.
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
# Platform Identity and Security Architecture
|
||||
|
||||
Status: implemented architecture baseline for NetKingdom/Railiance/Coulomb
|
||||
Date: 2026-05-24
|
||||
Date: 2026-05-24 (Tenant Model updated 2026-07-23, ADR-0013/ADR-0014)
|
||||
|
||||
## Purpose
|
||||
|
||||
|
|
@ -296,15 +296,39 @@ Examples of platform-root actions:
|
|||
Every protected resource should belong to a tenant or to the platform
|
||||
control plane.
|
||||
|
||||
Suggested identifiers:
|
||||
Tenant identifiers are `tenant:<grouping>:<name>`, where `<grouping>` is an
|
||||
onboarding-risk / entity-shape classification ratified by ADR-0013
|
||||
(`trial`, `friendly`, `single`, `small`, `medium`, `large`, `enterprise`,
|
||||
`consumer`, `family`, `community`, `association`, `agentic`):
|
||||
|
||||
```text
|
||||
tenant:platform # platform control plane resources
|
||||
tenant:coulomb # first internal/reference tenant
|
||||
tenant:sandbox:<name> # sandbox tenants
|
||||
tenant:customer:<name> # future customer tenants
|
||||
tenant:platform # platform control plane — reserved, ungrouped
|
||||
tenant:coulomb # first internal/reference tenant — reserved, ungrouped
|
||||
tenant:friendly:binky # first tenant onboarded under this taxonomy
|
||||
tenant:<grouping>:<name> # general shape for tenants onboarded going forward
|
||||
```
|
||||
|
||||
`tenant:platform` and `tenant:coulomb` predate the taxonomy and stay
|
||||
reserved outside it — see `canon/standards/iam-profile_v0.3.md`'s Tenant
|
||||
Claim section for the full rationale.
|
||||
|
||||
A tenant separately holds **capability roles** — `PLTF`, `IAM`, `VEN`, `CUS`
|
||||
(ADR-0014), non-exclusive — describing what it *does* on the platform,
|
||||
independent of its grouping (what kind of entity it *is*). A `trial`-grouped
|
||||
tenant may hold any capability role, since `trial` exists to showcase every
|
||||
role; safety instead comes from tenant-level resource guardrails (spend
|
||||
limits, entity/action counts — reserved, not yet designed).
|
||||
|
||||
`tenant-engine` (new service, `canon/standards/tenant-engine-boundary-contract_v0.1.md`)
|
||||
is the canonical owner of both tenant grouping and capability-role facts,
|
||||
plus plan/subscription assignment. `key-cape` caches a tenant's roles on
|
||||
issued tokens (`tenant_roles` claim) for ordinary decisions; `flex-auth`
|
||||
re-validates live against `tenant-engine` before authorizing privileged,
|
||||
destructive, platform-root, or otherwise `aal2`-class actions — the same
|
||||
threshold the assurance model below already uses. `user-engine` continues
|
||||
to own users and memberships *scoped by* a tenant, not the tenant record
|
||||
itself.
|
||||
|
||||
Tenant membership and platform membership are distinct. A subject may be
|
||||
an administrator in `tenant:coulomb` without being a platform operator.
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue