ADR-0020: Multi-tenancy & isolation — Universe is the boundary, Realm is a namespace
ADR-0020: Multi-tenancy & isolation — Universe is the boundary, Realm is a namespace
Status
Accepted
Date
2026-07-03
Context
NETW-19 asks for the multi-tenancy and isolation architecture. The substance is decided across
canon (LLD §26.4 tenancy, §16 per-tenant keys, §13.2/§13.3 authority and hierarchies) and was
sharpened while grilling ADR-0018. Two
things force an ADR rather than a doc edit. First, the anti-pattern this must kill: legacy
SmartInventory carried per-customer forks — Airtel, RailTel, Dialog, BharatNet each a code
branch — the ~1,626-branch problem. Second, a canon drift: the LLD models an entity scope key
as a single realm string doing double duty, and §26.4 scopes the federation rule to “cross-realm.”
Both conflate two distinct things the glossary already separates — the customer-world container
(Universe) and the domain namespace (Realm). This ADR pins the model and corrects the
drift; the mechanical reconciliation across lld.md, data-plane.md, and code is downstream
implementation, tracked in TODO.md.
Decision
-
The hierarchy. Tenant (the org — the commercial and security boundary, and the crypto owner) ⊃ Universe (a reality container — one customer-world such as
networkaccess-airtel; a Tenant owns many) ⊃ Realm (a domain namespace within a Universe:network,projects,geo). Cell (the physical shard) is orthogonal — placement, not hierarchy. -
Universe is the boundary; Realm is not sacred. Every read/write is scoped to a Universe, and cross-Universe references require an explicit federation link plus a capability for the peer. Realm is a soft namespace, not an isolation wall: cross-realm references within a Universe are ordinary and constant — a
network.FiberCablesits at ageo.Locationand belongs to aprojects.WorkPackage— lightly typed by declared realm dependencies (runtime cross-realm governance deferred until multiple extension authors), never federation. The entity key isEntityRef = {universe, realm, type, id}whererealmnamespaces the type and is crossed freely; the isolation dimension the key enforces isuniverse. This corrects §26.4’s “cross-realm via federation links,” which must read cross-Universe. -
Tenant is derived, not keyed. A Universe belongs to exactly one Tenant, so Tenant is resolved via Universe→Tenant ownership for policy, KEK selection, and verb budgets — it is not carried in the entity key. The security boundary stays absolute without bloating every key.
-
Isolation is a deployment choice over one logical model. Tiers: shared (row/schema separation; the per-Tenant KEK gives cryptographic isolation even when physically co-tenant) → dedicated database → dedicated Cell / air-gapped spoke — swappable per Tenant without touching the logical model, because the model never assumes co-tenancy. Per-Tenant KEK envelope-encrypts durable payloads and enables crypto-shredding erasure (§16).
-
Noisy-neighbor control is per-Tenant verb budgets (rate predicates at the seam, §13.2).
-
Per-customer forks become configuration. A customer is a Tenant with its own Universe(s) plus a Reality Package — schema, traits, actions, policies, workflows, and driver manifests as versioned, signed data (§13.4). Customer variation is a package + config, never a code branch. The legacy forks map directly: pluggable IdP/OIDC per Tenant (Airtel-OID / Authentik / Keycloak / Azure AD) is P15 Identity configuration; white-label and terminology overrides are tenant-overlay tokens. One codebase, N tenants, zero forks.
Consequences
- The entity key gains a
universedimension — a real change touching storage, the IR, adjacency indexes, and marking predicates. This is why the drift is a decision, not a wording pass; theTODO.mdfollow-up downgrades from “own ADR” to “mechanically reconcile lld/data-plane/code and re-pin the federation rule to Universe per this ADR.” - Feeds ADR-0021 (authorization scopes to Universe; Tenant is the security owner) and ADR-0018 (per-Tenant KEK and credential isolation).
- Revisit trigger: the first real cross-Universe query (federation-link semantics, peer-capability propagation) and the first Tenant demanding a dedicated air-gapped Cell.
Alternatives Considered
- Tenant in the entity key. Rejected: derivable from Universe; keying it bloats every row and index for no isolation gain.
- Realm as a hard boundary (federate to cross realms). Rejected: cross-realm reference within a Universe is the ordinary, constant shape; Realm is a namespace on definitions, not a partition.
- Universe-only scoping, drop Realm from the key. Considered; Realm still namespaces types, so it rides on the key as the type’s namespace rather than a separate isolation dimension — which is what Decision 2 encodes.
- Per-customer branches (the legacy model). Rejected outright: the 1,626-branch grave this architecture exists to prevent.