vaultaris /docs

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

FieldMeaning
features.hosting.enabledMaster switch (default false)
features.hosting.max_hosted_tenantsCap on active hosted tenants (null = unlimited)
features.hosting.allowed_tiersTiers the host may assign to hosted clients

REST surface

MethodPathPurpose
GET/api/v1/tenants/{host_id}/hosted-tenants/capabilityResolved capability + live count
GET/api/v1/tenants/{host_id}/hosted-tenantsList active hosted tenants
POST/api/v1/tenants/{host_id}/hosted-tenantsProvision a new hosted tenant
PATCH/api/v1/tenants/{host_id}/hosted-tenants/{id}/statusSuspend or archive
GET/api/v1/tenants/{host_id}/hosted-tenants/{id}/statsAggregate 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 access
  • billing.admin — manage members and subscriptions
  • billing.finance — view invoices and payment methods
  • billing.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 trackingFreezeReason enum: 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 AdminAction reason 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_URL on 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,
});