Accept ADR-0009 with tenant-selectable isolation upgrade path
Add shared-realm and dedicated-instance tiers selected by the IAM capability role, binding reference points (issuer invariance, subject preservation, tier-neutral declaration, drill), and NK-WP-0011-T09. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Assistant: claude-code Assistant-Model: sonnet Assistant-Process: 295952@bnt-lap001 Assistant-Session: e93f64ad-516c-46eb-9666-aad8d300c477
This commit is contained in:
parent
7d11ce58ce
commit
5497e2c100
3 changed files with 73 additions and 16 deletions
|
|
@ -2,17 +2,17 @@
|
|||
id: NK-ADR-0009
|
||||
type: architecture-decision-record
|
||||
title: "Expanded-Mode Keycloak: Adoption Trigger and Federation Topology"
|
||||
status: proposed
|
||||
status: accepted
|
||||
owner: net-kingdom
|
||||
revision: "1"
|
||||
proposed: "2026-09-28"
|
||||
decided: "2026-09-28"
|
||||
last_reviewed: "2026-09-28"
|
||||
review_interval: 12m
|
||||
---
|
||||
|
||||
# ADR-0009 - Expanded-Mode Keycloak: Adoption Trigger and Federation Topology
|
||||
|
||||
**Status:** Proposed (awaiting Bernd's decision; NK-WP-0011-T01)
|
||||
**Status:** Accepted 2026-09-28, with the isolation-upgrade amendment in decision 3 (NK-WP-0011-T01)
|
||||
**Date:** 2026-09-28
|
||||
**Deciders:** Bernd Worsch, Claude
|
||||
|
||||
|
|
@ -32,7 +32,7 @@ node with single-instance databases, so no option here buys HA.
|
|||
NK-WP-0001 decision D2 (Keycloak as primary internal user store) predates all
|
||||
of this.
|
||||
|
||||
## Decision (proposed)
|
||||
## Decision
|
||||
|
||||
1. **Trigger.** A tenant moves to expanded mode only when all three hold: a
|
||||
named tenant, a named owner of the upstream IdP, and a concrete federation
|
||||
|
|
@ -45,15 +45,51 @@ of this.
|
|||
only IAM Profile OIDC/PKCE tokens. Emitting SAML assertions to downstream
|
||||
applications is out of scope. Keycloak is not the primary user store
|
||||
(**supersedes D2**), not the PDP, and not a secret store.
|
||||
3. **Isolation.** Realms are the tenant boundary. The `tenant:platform` realm
|
||||
is separate and reserved. A tenant without the `IAM` role gets a realm in
|
||||
the shared broker; a tenant with the `IAM` role gets a dedicated broker
|
||||
instance (ADR-0014). A shared broker gives realm-level, not process-level,
|
||||
isolation, so it is acceptable only for tenants whose federation trust and
|
||||
data classification allow that. Realm admins may not alter IAM Profile
|
||||
semantics, the platform realm, federation trust settings, or audit
|
||||
retention. tenant-engine, not Keycloak, is the source of truth for tenant
|
||||
existence and capability roles.
|
||||
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.
|
||||
|
||||
4. **Coexistence.** Each tenant has exactly one active issuer at a time,
|
||||
recorded by tenant-engine and published as issuer configuration. KeyCape
|
||||
stays unchanged and keeps serving lightweight tenants. Applications target
|
||||
|
|
@ -84,7 +120,7 @@ of this.
|
|||
working tenants; contradicts capability-driven adoption.
|
||||
- **Dedicated instance for every federated tenant.** Strongest isolation, but on
|
||||
one node the cost is multiplied database, memory and patching for tenants
|
||||
that do not need it. Kept for `IAM`-role tenants.
|
||||
that do not need it. Offered as the paid `dedicated-instance` upgrade.
|
||||
- **Realm-per-tenant only, including `IAM` tenants.** Contradicts ADR-0014's
|
||||
meaning of `IAM`.
|
||||
- **Keycloak as SAML IdP downstream too.** No consumer requires it; widens the
|
||||
|
|
@ -101,6 +137,8 @@ of this.
|
|||
- tenant-engine needs an issuer-per-tenant field; this is a dependency to
|
||||
raise with its owner, not something built here.
|
||||
- Shared-broker realms cannot be described as strongly isolated tenants.
|
||||
- The upgrade constrains the design now: immutable realm names, issuer
|
||||
hostnames that can be re-routed, and `sub`-preserving realm migration.
|
||||
- No HA is implied; T02 and T08 must name backup ownership, off-host custody
|
||||
and an isolated restore proof.
|
||||
|
||||
|
|
|
|||
|
|
@ -137,7 +137,9 @@ required that the lightweight stack does not provide — chiefly inbound
|
|||
enterprise federation and SAML brokering (Entra ID, Active Directory,
|
||||
generic SAML IdPs), complex multi-realm topologies, or delegated admin.
|
||||
A deployment climbs to expanded mode because it needs that capability,
|
||||
not because it has more users. The lower resource and operational
|
||||
not because it has more users. Federated tenants start in a shared realm and
|
||||
can upgrade to a dedicated instance via the `IAM` capability role without
|
||||
changing issuer or subjects (ADR-0009, decision 3). The lower resource and operational
|
||||
footprint of the lightweight stack is a consequence of this rule, not the
|
||||
trigger for it. See **Capability Progression** below.
|
||||
|
||||
|
|
|
|||
|
|
@ -117,7 +117,7 @@ Out of scope:
|
|||
```task
|
||||
id: NK-WP-0011-T01
|
||||
state_hub_task_id: "a5807808-fb34-50de-8f67-9128011833d4"
|
||||
status: progress
|
||||
status: done
|
||||
priority: high
|
||||
```
|
||||
|
||||
|
|
@ -251,8 +251,25 @@ audit sink alongside flex-auth/Topaz/OpenBao records, with correlation
|
|||
ids — satisfying the "Audit sink" and "Break-glass" rows of the
|
||||
production-readiness checklist.
|
||||
|
||||
## Prove the isolation upgrade path
|
||||
|
||||
```task
|
||||
id: NK-WP-0011-T09
|
||||
status: todo
|
||||
priority: medium
|
||||
```
|
||||
|
||||
**Shared-realm to dedicated-instance upgrade (ADR-0009, decision 3).** Write
|
||||
the owner-executed upgrade runbook and rehearse it in an isolated environment
|
||||
before any paid tier is offered. The drill must show issuer URL unchanged,
|
||||
every user `sub` and federated link preserved, login working after cutover,
|
||||
rollback by re-routing during the retention window, and per-tier backup and
|
||||
restore evidence. Coordinate with tenant-engine on deriving the tier from the
|
||||
`IAM` capability role; do not add an independent tier field.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- A rehearsed shared-to-dedicated isolation upgrade preserves issuer and subjects.
|
||||
- An ADR records the expanded-mode trigger, federation topology,
|
||||
selected realm/tenant isolation model, and KeyCape/Keycloak issuer coexistence.
|
||||
- A federated user from at least one enterprise IdP (Entra ID) can log in
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue