-
Notifications
You must be signed in to change notification settings - Fork 0
Multi Tenant README
nself-chat now supports full multi-tenant SaaS deployment with:
-
Subdomain Routing:
tenant1.nchat.app,tenant2.nchat.app -
Custom Domains:
chat.acme.com(optional) - Schema-Level Isolation: Each tenant gets isolated PostgreSQL schema
- Stripe Billing: Integrated subscription management
- Resource Limits: Per-plan usage enforcement
- Usage Tracking: Real-time monitoring and analytics
# .env.local
ENABLE_MULTI_TENANCY=true
NEXT_PUBLIC_BASE_DOMAIN=nchat.appcd .backend
nself db migrate up 030_multi_tenant_system.sql# .env.local
STRIPE_SECRET_KEY=sk_test_...
STRIPE_WEBHOOK_SECRET=whsec_...curl -X POST http://localhost:3000/api/tenants/create \
-H "Content-Type: application/json" \
-d '{
"name": "Acme Corp",
"slug": "acme",
"ownerEmail": "admin@acme.com",
"ownerName": "John Doe",
"ownerPassword": "securepass123",
"plan": "free",
"trial": true
}'Visit: http://acme.localhost:3000 (development)
Or: https://acme.nchat.app (production)
public (global)
βββ tenants β Tenant metadata
βββ tenant_usage β Usage statistics
βββ tenant_settings β Configuration
βββ tenant_invitations β Invite system
βββ tenant_audit_logs β Audit trail
βββ stripe_webhooks β Billing events
tenant_acme (isolated)
βββ nchat_users β Acme's users
βββ nchat_channels β Acme's channels
βββ nchat_messages β Acme's messages
βββ ... β All chat tables
tenant_demo (isolated)
βββ nchat_users
βββ nchat_channels
βββ ...
1. User β acme.nchat.app
2. Middleware β Extract subdomain "acme"
3. Database β Fetch tenant metadata
4. Context β Set X-Tenant-Id, X-Tenant-Schema
5. Queries β All scoped to tenant_acme schema
6. Response β Tenant-branded UI
Core service for tenant CRUD operations:
import { getTenantService } from '@/lib/tenants/tenant-service'
const service = getTenantService()
// Create tenant
const tenant = await service.createTenant({
name: 'Acme Corp',
slug: 'acme',
ownerEmail: 'admin@acme.com',
// ...
})
// Get tenant
const tenant = await service.getTenantBySlug('acme')
// Update tenant
await service.updateTenant(tenant.id, {
name: 'Acme Corporation',
})
// Delete tenant
await service.deleteTenant(tenant.id)Stripe integration for subscriptions:
import { getStripeBillingService } from '@/lib/billing/stripe-service'
const billing = getStripeBillingService()
// Create checkout session
const session = await billing.createCheckoutSession(tenant, 'pro', 'monthly', successUrl, cancelUrl)
// Manage subscription
await billing.updateSubscription(tenant, 'enterprise', 'yearly')
await billing.cancelSubscription(tenant)Resolves tenant from domain:
// Automatic tenant resolution in middleware
export async function middleware(request: NextRequest) {
const config = getDefaultTenantConfig()
const response = await tenantMiddleware(request, config)
return response
}React context for tenant data:
import { useTenant, useTenantFeature } from '@/contexts/tenant-context'
function MyComponent() {
const { tenant, isLoading } = useTenant()
const hasVideoCalls = useTenantFeature('videoCalls')
return (
<div>
<h1>{tenant?.name}</h1>
{hasVideoCalls && <VideoCallButton />}
</div>
)
}- 10 users
- 5 channels
- 1 GB storage
- 1,000 API calls/month
- Basic features only
- 100 users
- 50 channels
- 100 GB storage
- 50,000 API calls/month
- All features + analytics
- Unlimited users
- Unlimited channels
- Unlimited storage
- Unlimited API calls
- All features + SSO + priority support
- Custom limits
- Custom features
- Dedicated support
- SLA guarantees
-
POST /api/tenants/create- Create new tenant -
GET /api/tenants/[id]- Get tenant -
PUT /api/tenants/[id]- Update tenant -
DELETE /api/tenants/[id]- Delete tenant -
GET /api/tenants/by-slug?slug=acme- Get by subdomain -
GET /api/tenants/by-domain?domain=chat.acme.com- Get by custom domain
-
POST /api/billing/checkout- Create Stripe checkout -
POST /api/billing/portal- Access billing portal -
POST /api/billing/webhook- Stripe webhook endpoint
See .env.multi-tenant.example for complete configuration.
Critical Variables:
# Multi-tenancy
ENABLE_MULTI_TENANCY=true
NEXT_PUBLIC_BASE_DOMAIN=nchat.app
# Stripe
STRIPE_SECRET_KEY=sk_...
STRIPE_WEBHOOK_SECRET=whsec_...
# Database
DATABASE_URL=postgresql://...- Wildcard DNS configured (
*.nchat.app) - Wildcard SSL certificate installed
- Stripe webhook endpoint configured
- Database migration run
- Redis cache configured
- Environment variables set
- Rate limiting enabled
- Backups automated
docker build -t nchat-multi:latest .
docker run -d \
--name nchat \
-p 3000:3000 \
--env-file .env.production \
nchat-multi:latestvercel --prod
vercel env add ENABLE_MULTI_TENANCY production
vercel env add STRIPE_SECRET_KEY productionAll tenant data protected by PostgreSQL RLS:
-- Only super admins can see all tenants
CREATE POLICY tenants_super_admin_all ON public.tenants
USING (
EXISTS (
SELECT 1 FROM auth.users
WHERE auth.users.id = auth.uid()
AND auth.users.raw_app_meta_data->>'role' = 'super_admin'
)
);
-- Tenant owners can see their own tenant
CREATE POLICY tenants_owner_select ON public.tenants
FOR SELECT
USING (owner_id = auth.uid());- Each tenant schema completely isolated
- Middleware enforces schema scoping
- No shared tables between tenants
- Cross-tenant queries impossible
Per-tenant rate limits based on plan:
- Free: 60 requests/minute
- Pro: 120 requests/minute
- Enterprise: 300 requests/minute
-- Current month usage
SELECT * FROM public.tenant_usage
WHERE tenant_id = '{id}' AND period = '2026-01';-- Tenant actions
SELECT * FROM public.tenant_audit_logs
WHERE tenant_id = '{id}'
ORDER BY created_at DESC;# Application health
curl https://nchat.app/api/health
# Tenant health
curl https://acme.nchat.app/api/health-
Tenant Not Found
- Check DNS propagation
- Verify tenant exists in database
- Check middleware logs
-
Stripe Webhook Failing
- Verify webhook secret
- Check Stripe Dashboard events
- Review webhook logs table
-
Cross-Tenant Data Leak
- CRITICAL: Suspend all tenants
- Review RLS policies
- Audit recent queries
-- 1. Create default tenant for existing data
INSERT INTO public.tenants (
name, slug, status, owner_email, owner_name,
schema_name, billing_plan
) VALUES (
'Default Organization',
'default',
'active',
'owner@example.com',
'Owner',
'nchat', -- Point to existing nchat schema
'enterprise'
);
-- 2. Enable multi-tenancy
-- .env.local
ENABLE_MULTI_TENANCY=true
DISABLE_MULTI_TENANCY=false
DEFAULT_TENANT_SLUG=default-
Documentation:
/docs/Multi-Tenant-Deployment.md - GitHub Issues: https://github.com/yourusername/nself-chat/issues
- Email: support@nchat.app
Version: 0.5.0 Last Updated: January 30, 2026 Status: Production Ready
nself-chat v0.3.0 | GitHub | Issues | Discussions | Demo
Edit this page | MIT License | Β© 2026
(See π Security section below for 2FA, PIN Lock, and security audits.)
(Search lives in π Reference below.)
- π¬ Advanced Messaging
- π E2EE Setup
- π Search Setup
- π Call Management
- πΊ Live Streaming
- π₯οΈ Screen Sharing
- πΉ Video Calling
- ποΈ Voice Calling
- π± Mobile Optimization
- π§ͺ Testing
- π i18n
- π API Overview
- π Complete Reference
- π» API Examples
- π€ Bot API
- π Auth API
- π GraphQL Schema
- π Deployment Overview
- π³ Docker
- βΈοΈ Kubernetes
- β Helm Charts
- β Production Checklist
- π Production Validation
- π’ Multi-Tenant
- ποΈ Architecture
- π Diagrams
- ποΈ Database Schema
- π Project Structure
- π TypeScript Types
- π SPORT Reference
- π 2FA
- π¬ Messaging
- π Call Management
- π Call State Machine
- π E2EE
- πΊ Live Streaming
- π± Mobile Calls
- π PIN Lock
- π Polls
- π₯οΈ Screen Sharing
- π Search
- π Social Media
- ποΈ Voice Calling
- π Security Overview
- π‘οΈ Security Audit
- β‘ Performance
- π Best Practices
- π 2FA
- π PIN Lock
- π E2EE
- π‘οΈ E2EE Audit
v1.0.0 β’ 2026