3.**Isolation tiers, with a defined upgrade path.** The realm is the tenant
boundary and `tenant:platform` is a separate, reserved realm. Isolation is
a tenant-selectable tier, expected to be a paid upgrade:
| Tier | Name | Boundary | Selected by |
| --- | --- | --- | --- |
| `shared-realm` | Default | Own realm in the shared broker: realm-level isolation only (shared process, database and admin plane) | Default for any federated tenant |
| `dedicated-instance` | Upgrade | Own broker instance and database: process-level isolation, own scale and performance | Tenant acquires the `IAM` capability role (ADR-0014) |
Realm admins in either tier may not alter IAM Profile semantics, the
platform realm, federation trust settings, or audit retention. Shared-realm
tenants must not be described as strongly isolated. tenant-engine owns
tenant existence, capability roles and plan; the tier is *derived* from
the `IAM` role, never stored as a second independent fact.
**Reference points that make the upgrade non-breaking.** These are
binding on implementation and documentation:
1.**Issuer invariance.** The tenant's issuer URL (host plus
`/realms/<tenant>`) is the same in both tiers. An upgrade moves the
routing behind the hostname (Traefik), not the issuer, so applications,
audiences and account bindings do not change. The realm name is
therefore chosen once, is immutable, and is derived from the tenant id.
2.**Subject preservation.** Keycloak-local user ids are the `sub`. Upgrade
migrates the realm with users and their original ids preserved, and
federated-identity links intact. A migration that changes any `sub`
fails the drill and is not permitted.
3.**Tier-neutral declaration.** Each tenant realm is declared in git as
sanitized realm configuration plus a small tenant manifest naming the
tenant id, realm name and issuer. The manifest carries no tier field; the
tier is derived from tenant-engine, and the same realm declaration
deploys to either tier.
4.**Owner-executed runbook and drill.** A documented
shared-to-dedicated procedure exists with a rehearsed drill before
any paid tier is offered: export, import into an isolated instance,
verify `sub` preservation and login, cut routing, keep the source
realm read-only for a retention window, then remove it. Rollback is
re-routing to the still-intact source realm until that window ends.
5.**Per-tier evidence.** Backup ownership, off-host custody and an
isolated restore proof are recorded per tier; the dedicated tier
has its own database consumer and OpenBao role.
6.**Billing is out of scope here.** Plan and pricing are tenant-engine
facts; this ADR only fixes the technical contract that the `IAM` role
selects. Downgrade is not defined and needs its own decision.