Skip to content

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

  1. 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.

  2. 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.FiberCable sits at a geo.Location and belongs to a projects.WorkPackage — lightly typed by declared realm dependencies (runtime cross-realm governance deferred until multiple extension authors), never federation. The entity key is EntityRef = {universe, realm, type, id} where realm namespaces the type and is crossed freely; the isolation dimension the key enforces is universe. This corrects §26.4’s “cross-realm via federation links,” which must read cross-Universe.

  3. 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.

  4. 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 databasededicated 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).

  5. Noisy-neighbor control is per-Tenant verb budgets (rate predicates at the seam, §13.2).

  6. 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 universe dimension — a real change touching storage, the IR, adjacency indexes, and marking predicates. This is why the drift is a decision, not a wording pass; the TODO.md follow-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.