Tenant Isolation & Data Residency
Consystence is multi-tenant by construction: many customer organisations share the same cloud codebase and Orleans cluster, but no tenant can read or touch another tenant’s operational data. This page describes the isolation boundaries — how membership is keyed, how a request’s subdomain and grain key bind it to one org, and how every resource is authorised against the org that owns it — and where customer data physically lives.
What “tenant” means here
Section titled “What “tenant” means here”A tenant is the routed, cell-hosted workspace this page’s boundaries protect. Each tenant is
minted by the account control plane with a stable UUID (org_*), addressed by a human-readable
slug under its own subdomain — {slug}.consystence.com for enterprise-family tenants,
{slug}.consystence.dev for integrator-family tenants and individual sandboxes — and hosted on
exactly one cell. It
is distinct from an account-plane organisation: a free members-and-invites
container with no slug, subdomain, or cell — creating an organisation does not create a tenant or
an isolation boundary. A paid tenant is owned by an organisation; an organisation may also hold its
one free trial (simulation) tenant, convertible in place to an integrator-family paid tier; an
individual’s sandbox tenant has no owning organisation at all. Within a cell, this page uses “org”
for the tenant that owns a resource. A site is a path within a tenant (/sites/{code}), never a top-level domain. See
the org and site model for the full hierarchy.
Membership in an org is the unit of access, and it resolves to exactly one of three roles —
owner, admin, member — described in roles.
Membership is keyed by Entra oid
Section titled “Membership is keyed by Entra oid”Identity comes from Entra External ID (CIAM). The stable membership key is the Entra oid —
the object identifier that is constant for a user across sessions. Email is a verified attribute
used for display and platform-admin detection; it is not identity, and it can change. The
pairwise per-relying-party sub claim is not used as the membership key.
When a request arrives, the caller’s oid is checked against the membership directory for the
org being addressed. Active members pass; pending or revoked members fail closed; non-members
resolve to zero access. See authentication for how each scheme establishes
the caller’s identity (a site-PIN principal carries no oid — it proves site
presence, not org membership).
Isolation boundaries
Section titled “Isolation boundaries”Three boundaries stack on top of each other. Each is independent — a request must clear all of them — so a defect in one does not silently open the tenant boundary.
flowchart TD A["Request to<br/>acme.consystence.com"] --> B["Subdomain boundary<br/>subdomain → org slug 'acme'<br/>caller oid validated vs org"] B --> C["Grain-key scoping<br/>grain keys prefixed 'acme/...'<br/>resources site-scoped under org"] C --> D["Resource-to-org authz<br/>derive owning org from resource id<br/>validate vs caller membership"] D --> E["Tenant-scoped resource grain"]
Subdomain boundary
Section titled “Subdomain boundary”Every org is a distinct subdomain. At the request boundary, org-resolution middleware maps the subdomain to the org slug, validates the caller’s membership against that org, and rejects mismatches before any business logic runs. A user holding a valid token for one org cannot reach another simply by changing the host.
Grain-key tenant scoping
Section titled “Grain-key tenant scoping”Orleans grains that hold tenant state are keyed with the org slug as a prefix (acme/...), and
resources beneath an org are site-scoped ({siteId}). The grain key itself therefore carries the
tenant, so two tenants’ resources are distinct grain activations with no shared state. Sites are
registered into the org → site map at provisioning time, which is what makes a site id resolvable
back to its owning org (used by the authorisation layer below).
Resource-to-org authorisation
Section titled “Resource-to-org authorisation”Subdomain and grain keying are necessary but not sufficient. Role policies answer who you are
(are you an admin or member of some org?) — they do not, on their own, answer which tenant
owns the specific resource this request touches. A route that carries a client-supplied {siteId}
can decouple the addressed resource from the subdomain, so a cross-org member could otherwise
reach another tenant’s site resource while still passing a role check.
The resource-to-org authorisation layer closes this. Every caller-facing surface — HTTP endpoint, SignalR hub method, or gRPC edge service — that reads or streams a tenant-scoped resource must, before touching the resource grain:
- Derive the resource’s owning org from the resource identifier. Resource ids are
site-scoped; membership is org-scoped. A directory grain maps
siteId→ owning org using the map populated at site provisioning. - Validate that org against the caller through a single shared membership chokepoint, keyed by
the caller’s Entra
oidagainst the derived org (active members only). Denial throws.
The invariant, in one sentence: the org validated is the org of the requested resource, derived from the resource id — never the caller’s session or home org, and never merely “has some org context.”
Cross-tenant isolation is enforced structurally in CI, not by review alone.
Platform-admin bypass
Section titled “Platform-admin bypass”Verified consystence.com users (or seed-listed accounts) carry a platform_admin claim that
bypasses the resource-org check for support, diagnostics, and onboarding — exactly as the render
path and the admin API already do. This bypass is deliberate and logged; durable audit is
forthcoming. It is the only path that legitimately crosses the tenant boundary.
Data residency
Section titled “Data residency”Today, all customer data lives in Azure Australia East (Sydney). This is the contractual commitment, not just a default.
| Category | Stored where |
|---|---|
| Tenant configuration (grain state) | Azure Australia East (PostgreSQL) |
| Historian samples, tags, events, lineage, registry | Azure Australia East (PostgreSQL) |
| Auth tables (site users, sessions, signing keys) | Azure Australia East (PostgreSQL) |
| Audit log streams (command audit, event log) | Azure Australia East (Orleans persistence) |
| Container images | Azure Australia East (ACR) |
| Backups | Paid tiers: geo-redundant to the paired region (Australia Southeast), with high availability. Sandbox / Basic: no geo-redundancy or HA. Data does not leave Australia. |
| Telemetry (traces, metrics) | Azure Australia East (Application Insights) |
Identity is the expected exception in kind: Entra External ID is a global Microsoft service, so
user identity records live in Microsoft’s tenant store. Only the identity claims propagated via JWT
(oid, verified email, name) are seen by the platform, and they are persisted only in audit logs.
AI inference and data residency
Section titled “AI inference and data residency”AI inference is the one flow that leaves a tenant’s data cell: advisory requests are handled by the regional advisory plane rather than inside the cell. Advisory inference for the Australian deployment runs on AWS in Australia (Sydney, with Australia-pinned inference profiles), so prompt content stays within Australia — it is processed outside the Azure data cell but never leaves the country. Prompt and response content is not logged or retained by the inference layer and is not used for model training. A tenant that requires no external inference at all can run with the AI tier disabled.
No additional region is stood up speculatively — US, EU, or on-prem-only residency is added only on a signed customer ask, each with its own routing partition.
Related
Section titled “Related”- Roles — owner / admin / member, the membership that the resource-org gate validates against.
- Cell topology — how tenant orgs are provisioned into cells above the four-tier stack.
- Authentication — the MultiAuth schemes that establish the caller’s
oid. - Licensing — tenant tier and seat pools, which degrade but never gate control writes.