Tenants
Purpose
Section titled “Purpose”Explain what a tenant is in Get2Dial and how tenant isolation is enforced.
Overview
Section titled “Overview”A tenant is an isolated customer account, modeled in the tenants table
(id UUID, unique domain, status, JSONB settings/limits/features). It
owns its users, campaigns, routing, data and a dedicated edge.
The shared control plane serves all tenants but scopes every request to exactly
one, derived from the caller’s JWT tid claim.
Two reserved identities exist:
- Platform tenant
00000000-0000-0000-0000-000000000000(is_platform=true, domainplatform.get2dial.local) — whereplatform_adminusers live. - Dev tenant
a0000000-0000-0000-0000-000000000001(dev.get2dial.local) — seeded for development; the all-zero platform id can never collide with thea0000000-…app-tenant range.
Configuration
Section titled “Configuration”Tenant identity belongs on the customer edge, not the shared control plane:
# On the edge call engine (single-tenant):TENANT_ID=<the customer edge tenant UUID># On the shared control API: NO TENANT_ID — scoped per request from the JWT.Tenants are created by the bootstrap flow or
POST /api/v1/platform/tenants. Platform admins can set per-tenant limits
(resource quotas) and features (boolean entitlements, deny-by-default — e.g.
self_service_carriers).
Examples
Section titled “Examples”A CDR written by the edge carries g2d_tenant_id (from a SIP header, falling
back to the node’s NODE_TENANT_ID). If a bridged leg arrives without it, the
call engine’s TENANT_ID is the fallback — which is why that value must be the
real customer tenant, never a shared default.
- Pinning a tenant on the shared control plane causes data to land under the wrong tenant. Don’t.
- Isolation is defense-in-depth: JWT scoping, Postgres RLS (
app.tenant_id), per-tenant Redis ACLs and NATS subject permissions. - Each edge serves exactly one tenant.