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