Omnibase

Multi-Tenancy Overview

Learn how OmniBase handles organizations, team management, and data isolation

Multi-Tenancy

OmniBase provides a complete multi-tenant architecture with organization management, team invitations, role-based access control, and automatic data isolation via PostgreSQL Row-Level Security.

Key Features

Architecture

OmniBase implements a Row-Level Security (RLS) based multi-tenancy model with three layers:

┌─────────────────────────────────────────────────────────────────┐
│                        USER REQUEST                              │
│         (Cookie/Session Token + Active Tenant Context)          │
└─────────────────────────────────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────────┐
│                     AUTHENTICATION LAYER                         │
│              OmniBase Auth (Identity + Sessions)                │
│         ┌─────────────────────────────────────────┐             │
│         │ JWT Claims: { user_id, tenant_id, role }│             │
│         └─────────────────────────────────────────┘             │
└─────────────────────────────────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────────┐
│                    AUTHORIZATION LAYER                           │
│                OmniBase Permissions (ReBAC)                     │
│         ┌─────────────────────────────────────────┐             │
│         │  Relation Tuples:                       │             │
│         │  (Tenant, acme-corp, owner, user:alice) │             │
│         └─────────────────────────────────────────┘             │
└─────────────────────────────────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────────┐
│                      DATA LAYER                                  │
│             PostgreSQL with Row-Level Security                  │
│         ┌─────────────────────────────────────────┐             │
│         │  WHERE tenant_id = auth.active_tenant() │             │
│         └─────────────────────────────────────────┘             │
└─────────────────────────────────────────────────────────────────┘

What is a Tenant?

A tenant represents an organization, workspace, or account that groups users together. Each tenant has:

  • Isolated data via PostgreSQL Row-Level Security (RLS)
  • A dedicated Stripe customer for billing
  • Role-based access control (RBAC) for members
  • Invitation system for onboarding users
  • Custom roles with fine-grained permissions

Quick Start

Install the SDK

bun add @omnibase/core-js

Create a Tenant

import { Configuration, V1TenantsApi } from '@omnibase/core-js';

const config = new Configuration({
  basePath: process.env.OMNIBASE_API_URL,
});

const tenantsApi = new V1TenantsApi(config);

const { data } = await tenantsApi.createTenant({
  createTenantRequest: {
    name: 'Acme Corporation',
    billingEmail: 'billing@acme.com',
  },
});

console.log('Tenant ID:', data.data.tenant.id);

Invite Team Members

await tenantsApi.createInvite({
  createTenantUserInviteRequest: {
    email: 'teammate@example.com',
    role: 'member',
    inviteUrl: 'https://app.example.com/auth/onboarding',
  },
});

Tenant Types

TypeDescriptionUse Case
organizationMulti-user tenant with team featuresB2B SaaS, team workspaces
individualSingle-user tenantPersonal accounts, solo users

SDK Options

Best for: Server-side rendering, App Router, full-stack applications

// Server Action for tenant switching
'use server';
import { V1TenantsApi } from '@omnibase/core-js';
import { revalidatePath } from 'next/cache';

export async function switchTenant(formData: FormData) {
  const tenantId = formData.get('tenant_id') as string;
  const tenantsApi = new V1TenantsApi(config);

  await tenantsApi.switchActiveTenant({
    switchTenantRequest: { tenantId },
  });

  revalidatePath('/');
}

Best for: Client-side applications, SPAs

import { useCallback } from 'react';
import { V1TenantsApi } from '@omnibase/core-js';

function TenantSwitcher({ tenants }) {
  const switchTenant = useCallback(async (tenantId: string) => {
    const api = new V1TenantsApi(config);
    await api.switchActiveTenant({
      switchTenantRequest: { tenantId },
    });
    window.location.reload();
  }, []);

  return (
    <select onChange={(e) => switchTenant(e.target.value)}>
      {tenants.map((t) => (
        <option key={t.id} value={t.id}>{t.name}</option>
      ))}
    </select>
  );
}

Best for: Server-to-server, custom implementations

import { Configuration, V1TenantsApi } from '@omnibase/core-js';

const config = new Configuration({
  basePath: process.env.OMNIBASE_API_URL,
  headers: { 'X-Service-Key': process.env.OMNIBASE_SERVICE_KEY },
});

const tenantsApi = new V1TenantsApi(config);

// Create tenant for a user
const { data } = await tenantsApi.createTenant(
  {
    createTenantRequest: { name: 'New Org' },
  },
  { headers: { 'X-User-Id': userId } }
);

Common Patterns

Personal Workspace Pattern

For apps where each user has their own isolated workspace:

  1. Create one tenant per user at signup
  2. Each user owns their own tenant with their own Stripe account
  3. Skip tenant switching UI (unless they create additional workspaces)
  4. RLS isolates each user's data automatically

Personal + Team Workspaces

Support both individual and team contexts:

// Personal workspace
const personal = await tenantsApi.createTenant({
  createTenantRequest: {
    name: 'My Workspace',
    type: 'individual',
  },
});

// Team workspace
const team = await tenantsApi.createTenant({
  createTenantRequest: {
    name: 'Acme Team',
    type: 'organization',
  },
});

What's Next?

On this page