Four search-and-read passes against AWS Well-Architected SaaS Lens, Azure Architecture Center, OWASP, the OpenID Foundation, and PostgreSQL practitioner literature. Each digest holds one of our ladders against outside practice and lists its sources. Headline: the draft-1 -> draft-2 reframe is corroborated. Microsoft states "isolation is a spectrum, not a discrete property" and derives our E/P coupling independently. The shape is sound; the details are not. Five corrections draft-2 needs: - E3 overclaims. Any session can re-SET the GUC, so RLS is strong against a forgotten predicate and useless against SQL injection or a compromised process. The ADR says "the application cannot trivially route around" - the exact overclaim it prohibits elsewhere. - The pooling claim in section 16 is backwards. Transaction-scoped SET LOCAL is what makes E3 safe under pooling; statement-level pooling is what serves other tenants' rows, only under production concurrency. - E3 layers on E2 rather than superseding it. - Placement cannot be expressed per tenant, but the commercial section requires exactly that - a tier bought by some tenants puts a service at two P levels at once, which the posture vector cannot represent. - FORCE ROW LEVEL SECURITY is mandatory: ADR-0001 already established that our migration role owns the tables it creates, so it would silently bypass every policy. Also: pgTAP and pgrls make the E3 evidence artifact far cheaper than assumed; OWASP publishes a Multi-Tenant Security Cheat Sheet we reference nowhere; our tenant-boundary gap is BOLA, OWASP API1, top of that list since it launched; AuthZEN 1.0 went Final in January and flex-auth is pre-standard; and no plane covers tenant offboarding, which OWASP makes a MUST. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
14 KiB
Plane P — Placement: prior knowledge and best practices
Research artefact — 2026-08-17. Supports
canon/architecture/adr-008-multi-tenancy-model.md(draft-2), plane P of four. Surveys external practice for which substrate holds a tenant's data, and holds ourP0–P4ladder against it. Contains strong external validation of draft-2's reframe, and one expressiveness failure in the P ladder itself. Companions: plane I (identity), plane A (authorization), plane E (enforcement).
1. What the plane covers
Which physical substrate holds a tenant's data, who decides, and what moving costs. Our ladder:
| Level | Shape |
|---|---|
| P0 | Shares a database with another consumer |
| P1 | Database per consumer, shared cluster |
| P2 | Dedicated cluster per consumer |
| P3 | Dedicated cluster per tenant |
| P4 | P3 plus separate region or jurisdiction |
2. Validation: "isolation is a spectrum, not a discrete property"
The operator's instruction to reframe draft-1 from a fixed model into a graduated framework is not merely defensible — it is Microsoft's stated position, in almost the same words:
"Instead of viewing isolation as a discrete property, consider it a spectrum. You can deploy components of your architecture that are more isolated or less isolated than other components in the same architecture, depending on your requirements." — Azure Architecture Center — Tenancy models for a multitenant solution
The same page decomposes isolation across tiers exactly as our planes do — "you can use different levels of isolation for each tier", with a shared UI tier, a shared middle tier, and isolated databases given as the worked example.
Against our model: draft-2's core move is externally corroborated by the most rigorous public taxonomy available. Worth citing in ADR §15, because it converts "we decided to do it this way" into "this matches the reference guidance", which matters for reviewers who will ask whether we invented a private vocabulary.
3. Validation: our E↔P coupling claim is independently confirmed
ADR §3.2 asserts that the planes couple at the top — E4 is only reachable at
P3 or above — and §4.4 asserts that P0 → P1 → P2 does not raise the E
level. Microsoft states the mechanism plainly:
"A key difference between tenants and deployments is how isolation is enforced. When multiple tenants share a single deployment (a set of infrastructure), you typically rely on your application code and a tenant identifier that's in a database to keep each tenant's data separate. When tenants have their own dedicated deployments, they have their own infrastructure, so it might be less important for your code to account for a multitenant environment." (ibid.)
That is our E/P relationship derived independently: shared deployment ⇒ enforcement is application code (our E1–E3); dedicated deployment ⇒ enforcement is structural (our E4).
4. Finding: our P ladder cannot express per-tenant placement, and our own commercial section requires it
This is the significant gap.
Azure names two partitioning axes that our single ladder collapses:
- Vertically partitioned deployments — "a combination of single-tenant and multitenant deployments. For example, you might have most of your customers' data and application tiers on multitenant infrastructures, but you deploy single-tenant infrastructures for customers who require higher performance or data isolation." Explicitly monetisable: "You can even charge customers a higher rate to use a single-tenant deployment." (ibid.)
- Horizontally partitioned deployments — shared application tier, dedicated databases per tenant, targeted at the component that carries the load: "if you identify that specific components cause most of the load on your system, then you can deploy separate components for each tenant." (ibid.)
Our P ladder assigns one level per service. tenant-engine is P1.
audit-core is P1. There is no way to say "P1 for most tenants, P3 for the
three enterprise tenants who bought isolation" — which is precisely the
vertically partitioned model, and precisely what ADR §11's minimum-level plan
tiers imply.
The contradiction is internal to draft-2: §11.1 says "a tier may require
E3 P2", but if only some tenants hold that tier, the delivering service
occupies two P levels at once. The posture vector as specified cannot represent
that state, so a service in it would have to either overclaim (declare P2 for
everyone) or underclaim (declare P1 and misrepresent what the tier delivers).
Both are §6 violations.
Proposed amendment. Either:
- Make P a per-tenant-class level rather than a per-service one — the
vector carries a default plus exceptions:
tenancy: current: { I: 2, A: 3, E: 2, P: 1 } placement_exceptions: - tenants: ["tenant:enterprise:*"] P: 3 reason: "isolation tier" - Or declare per-tenant placement out of scope for the framework and say so, accepting that plan tiers selling isolation are then unmodellable.
Option 1 is more work and is the honest one, because the estate has already anticipated tiered isolation. Azure also flags the cost: "your codebase needs to be designed to support both multitenant and single-tenant deployments", and migration between them must be planned for (ibid.) — which is exactly what our §7 portability rules already provide, so we are better positioned for this than most.
5. Cell-based architecture: the pattern our ladder skips
ADR §18.8 asks whether to evaluate cells. The literature gives enough to sharpen the question.
- A cell is "a complete, independent instance of the service" with a fixed maximum size — compute, storage, supporting services and configuration for a defined blast radius (Rackspace — cell-based architecture on AWS, AWS guidance for cell-based architecture).
- Cells are logical partitions on a partition key — customer ID ranges or tenant ID — and "the data required for or generated by each cell is also partitioned, so that there is no replication of data between cells" (ibid.).
- The sizing argument is the point: "by capping the size of each cell… you can potentially predict and address failures more easily. This leads to a higher MTBF" (ibid.). And "it is important to identify the proper partition key to minimize cross-cell interactions" (ibid.).
- Cells are the bulkhead pattern applied to tenancy — AWS files it under fault isolation, REL10-BP04 (AWS Well-Architected — use bulkhead architectures to limit scope of impact).
- Azure's equivalent is the Deployment Stamps pattern: "each stamp serves a predefined number of tenants, allowing you to scale almost linearly and serve increasing numbers of tenants" (Azure — Deployment Stamps pattern). Azure also calls deployments "supertenants or stamps" and describes the tenant→deployment mapping table as a first-class component.
Against our model: a cell is not a rung on our P ladder — it is a different
axis. P measures how much is shared; a cell measures how large a shared unit
is allowed to get before you make another one. Our platform-pg is, in cell
terms, an uncapped cell: §16 of the ADR computes a ceiling of roughly six
consumers but nothing enforces it, and there is no second cell to overflow
into.
That reframes §18.8 usefully. The question is not "should we adopt cells" but
"what is platform-pg's declared maximum size, and what happens at the
boundary?" — a question we can answer now, cheaply, and which the connection
ceiling already forces.
Azure also names bin packing as the assignment strategy and stresses maintaining "a record of your customers and the infrastructure where their data and applications reside so that you can route their traffic to the correct location" — a tenant→deployment mapping table. We have no such record; the placement facts in ADR §1 were assembled by inspection.
6. Placement is a commercial decision, and the guidance says so first
Our §11 treats commercial expression as a constraint to be managed. Azure puts it the other way round:
"Selecting a tenancy model isn't only a technical decision. It's also a commercial decision." — considering business objectives, compliance, scale, automation capacity, and SLAs (ibid.).
And on the economics, matching our §14 rejection of R3/P3-everywhere: "if a single tenant requires a specific infrastructure cost, 100 tenants probably require 100 times that cost", with ongoing maintenance time-consuming at scale (ibid.). Conversely single-tenant is appropriate "if you expect that your business will have only a few customers… even though it's more costly".
Against our model: ADR §8.2 proposes placement ownership to
railiance-platform, an operations-shaped repo. This guidance suggests
placement policy is jointly commercial and technical — which strengthens the
case that adaptive-pricing must be a co-signer on §18.2, not only on §18.5.
7. Our tenant taxonomy is a recognised hard case
Azure's B2C guidance describes our exact situation:
"If your customers are consumers, it's often more complicated to relate customers, tenants, and users… consider whether your solution might be used by families, groups of friends, clubs, associations, or other groups that might need to access and manage their data together. For example, a music streaming service might support both individual users and families, and it might treat each of these account types differently when it separates them into tenants." (ibid.)
Our ADR-0013 taxonomy contains consumer, family, community, and
association verbatim. It also notes that if tenants are individuals, "you
might need to consider how you handle personal data and about the data
sovereignty laws in each jurisdiction" — which is our P4 and, combined with
plane A §6, the tenant-deletion gap.
8. Testing placement is a named obligation
"Whichever isolation model you choose, be sure to test your solution to verify that one tenant's data isn't accidentally leaked to another and that any noisy neighbor outcomes are acceptable." (ibid.)
Two evidence artifacts, not one. ADR §13 covers the leak half (P1–P4: "provisioning declaration plus the platform's own isolation probes") and says nothing about noisy-neighbour behaviour. Azure suggests deliberate fault injection (Chaos Studio) to verify resilience.
Proposed amendment to §13: add a noisy-neighbour evidence artifact for P1
and P2 — a demonstration that one consumer saturating its connection or CPU
allowance does not breach another's. rapp-postgres already sets
database-scoped connection_limit, statement_timeout and
idle_in_transaction_session_timeout precisely for this, and ADR-0001 §3 argues
resource exhaustion is an isolation failure — but nothing proves the limits
hold under load.
9. Summary against our ladder
| Finding | Verdict |
|---|---|
| "Isolation is a spectrum, not a discrete property"; different tiers at different levels | Validates draft-2. Cite in §15. |
| Shared deployment ⇒ app-code enforcement; dedicated ⇒ structural | Confirms E↔P coupling independently. |
| Vertical partitioning: some tenants pooled, some dedicated, explicitly monetised | Gap. P is per-service; cannot express per-tenant placement that §11 requires. |
| Horizontal partitioning: shared app tier, per-tenant DB for the hot component | Unmodelled. Our P assumes whole-service granularity. |
| A cell has a fixed maximum size; stamps scale near-linearly | Reframes §18.8. Ask "what is platform-pg's declared max size?", not "should we adopt cells". |
| Tenant→deployment mapping table is a first-class component | Missing. Our placement facts were assembled by inspection. |
| Tenancy model is a commercial decision as much as technical | Amend §8.2. adaptive-pricing should co-sign placement ownership. |
| B2C/family/association tenancy is a recognised hard case | Our taxonomy hits it verbatim; jurisdiction concerns feed P4. |
| Test for leakage and for acceptable noisy-neighbour outcomes | Amend §13. No noisy-neighbour evidence artifact exists. |
Sources
- Azure Architecture Center — Tenancy models for a multitenant solution
- Azure Architecture Center — Architectural approaches for a multitenant solution
- Azure Architecture Center — Deployment Stamps pattern
- Azure Architecture Center — Considerations for updating a multitenant solution
- AWS Well-Architected — REL10-BP04 Use bulkhead architectures to limit scope of impact
- AWS Well-Architected — How do you use fault isolation to protect your workload?
- AWS Solutions Library — Guidance for cell-based architecture on AWS
- Rackspace — Cell-based architecture on AWS
- Ryan Harrison — Cell-based architecture engineering guidelines
- Anudeep Balla — The guide to cell-based architecture
- AWS Well-Architected SaaS Lens (PDF)
- hidekazu-konishi — AWS SaaS multi-tenant architecture guide: tenant isolation, pool and silo models