Multi-Tenancy
How Vaultaris isolates organizations, supports SaaS use cases, and manages license-driven resource freezing.
Tenant isolation model
Hard-partitioned by tenant_id. Every SQL query includes WHERE tenant_id = $1. JWT tokens carry a tenant_id claim enforced by the authentication middleware on every authenticated request. Cross-tenant data access is structurally impossible except from the master tenant.
Creating tenants
POST /api/v1/tenants
{
"name": "Acme Corp",
"slug": "acme",
"display_name": "Acme Corporation"
}
Returns a UUID id. All subsequent resource routes are scoped under /api/v1/tenants/{id}/.
Per-tenant configuration
Each tenant has independent configuration for:
- Token lifetimes (access, refresh, ID, authorization code)
- Password policy (complexity, history, rotation, lockout)
- MFA settings (enabled, required)
- Branding (logo, colors, custom CSS, welcome text)
- Email templates (per-tenant and per-application)
- Plugin activations and configurations
- Identity providers and OAuth client allowlists per group
The master tenant
UUID 00000000-0000-0000-0000-000000000001. Admin users here can manage other tenants — the only context where cross-tenant operations are permitted.
Hosted tenants
A tenant whose license includes features.hosting.enabled = true (granted by the control plane) can act as a host for other tenants on the same Vaultaris instance.
What hosting means
- Each hosted tenant is a regular, fully isolated tenant
- The host tenant gets a "Hosted clients" dashboard section with aggregate stats
- The host cannot read hosted tenant data, only aggregate counts
- Sub-hosting (a hosted tenant hosting others) is not permitted without an explicit control-plane license grant
License capability fields
| Field | Meaning |
|---|---|
features.hosting.enabled | Master switch (default false) |
features.hosting.max_hosted_tenants | Cap on active hosted tenants (null = unlimited) |
features.hosting.allowed_tiers | Tiers the host may assign to hosted clients |
REST surface
| Method | Path | Purpose |
|---|---|---|
GET | /api/v1/tenants/{host_id}/hosted-tenants/capability | Resolved capability + live count |
GET | /api/v1/tenants/{host_id}/hosted-tenants | List active hosted tenants |
POST | /api/v1/tenants/{host_id}/hosted-tenants | Provision a new hosted tenant |
PATCH | /api/v1/tenants/{host_id}/hosted-tenants/{id}/status | Suspend or archive |
GET | /api/v1/tenants/{host_id}/hosted-tenants/{id}/stats | Aggregate counts (no PII) |
All endpoints return 403 if the host license lacks features.hosting.enabled = true.
Billing shadow tenants
Billing shadow tenants (is_billing_shadow = true) are a special variant used by the cloud control plane. They hold only billing RBAC — they are not workload tenants and do not store user authentication data.
Four roles are seeded at creation:
billing.owner— full billing accessbilling.admin— manage members and subscriptionsbilling.finance— view invoices and payment methodsbilling.viewer— read-only
Billing shadow tenants are excluded from OpenAPI docs and cannot be created or queried by normal tenant API calls. The provisioning endpoint is guarded by a service token (BILLING_SHADOW_SERVICE_TOKEN).
Freeze / Unfreeze System
When a license downgrades or expires, Vaultaris brings the instance within the new resource limits by freezing excess resources.
Freeze rules
- Trigger — license downgrade, license expiry, or explicit admin action
- Order — LIFO (Last In, First Out): most recently created resources frozen first
- Scope — Users, OAuthClients, Applications, Identity Providers, Groups
- Exception — System admin users are never frozen (login always works)
- Reason tracking —
FreezeReasonenum:LicenseDowngrade,Expiry,Suspension,AdminAction
Unfreeze rules
- Trigger — license upgrade or resource deletion that frees up capacity
- Order — FIFO (First In, First Out): oldest resources restored first
- Exception — Resources frozen with
AdminActionreason are never automatically unfrozen; explicit admin action required
Viewing frozen resources
GET /api/v1/tenants/{tenant_id}/frozen
Returns a list of frozen resources grouped by type, including the freeze reason and timestamp.
Unfreeze manually
Admin can unfreeze specific resources via the dashboard or directly via the API. Upgrading the license triggers automatic FIFO restoration up to the new limits.
Federated deployment
For organizations running multiple Vaultaris instances (e.g., one per region):
- Set
CONTROL_PLANE_URLon each satellite instance - The control plane manages license assignment, usage telemetry, and grace-period enforcement
- Satellites are fully autonomous if the control plane is unreachable — they operate normally until the grace period (
GRACE_PERIOD_HOURS, default 168 h) expires - License limit changes (from a control-plane heartbeat response) are applied immediately: downgrade → LIFO freeze, upgrade → FIFO unfreeze
Provisioning tenants programmatically
// Node.js example
const tenant = await vaultaris.tenants.create({ name: 'Acme', slug: 'acme' });
const adminRole = await vaultaris.roles.create(tenant.id, { name: 'Admin' });
const adminGroup = await vaultaris.groups.create(tenant.id, { name: 'Admins' });
await vaultaris.groups.assignRole(tenant.id, adminGroup.id, adminRole.id);
const client = await vaultaris.clients.create(tenant.id, {
name: 'Web App', clientType: 'public',
redirectUris: ['https://app.example.com/callback'],
grantTypes: ['authorization_code', 'refresh_token'],
pkceRequired: true,
});