Multi-Tenancy Support
AgentSea provides enterprise-grade multi-tenancy support, enabling you to serve multiple customers with isolated data, quotas, and configurations.
🆕 Enterprise Feature
Multi-tenancy support provides production-ready tenant isolation, API key authentication, and quota management. Types are available from @lov3kaizen/agentsea-types or re-exported from the core package.
Overview
The multi-tenancy system provides:
- Tenant Isolation: Complete data separation between tenants
- API Key Authentication: Secure SHA256-hashed API keys per tenant
- Quota Management: Track and enforce usage limits across hourly, daily, and monthly periods
- Settings Management: Per-tenant configuration for agents, conversations, rate limits, and more
- Status Control: Activate, suspend, or deactivate tenants dynamically
- Memory Isolation: Tenant-scoped conversation history and memory stores
Tenant Manager
The TenantManager class provides complete lifecycle management for tenants.
Creating a Tenant
import { TenantManager, MemoryTenantStorage } from '@lov3kaizen/agentsea-core';
import type { Tenant, TenantSettings } from '@lov3kaizen/agentsea-types';
const storage = new MemoryTenantStorage();
const tenantManager = new TenantManager(storage);
// Create a new tenant
const tenant = await tenantManager.createTenant({
name: 'Acme Corporation',
slug: 'acme-corp',
metadata: {
industry: 'Technology',
plan: 'enterprise',
},
settings: {
maxAgents: 50,
maxConversations: 1000,
rateLimit: { requestsPerMinute: 100 },
dataRetentionDays: 90,
allowedProviders: ['anthropic', 'openai'],
},
});
console.log('Tenant created:', tenant.id);Generating API Keys
Each tenant can have one or more API keys with scoped permissions and expiration dates.
// Generate a new API key for the tenant
const apiKey = await tenantManager.generateApiKey(tenant.id, {
scopes: ['agents:read', 'agents:write', 'conversations:read'],
expiresAt: new Date(Date.now() + 365 * 24 * 60 * 60 * 1000), // 1 year
});
console.log('API Key:', apiKey.key); // Save this - it won't be shown again!
console.log('Key ID:', apiKey.id);
// Verify an API key
const verified = await tenantManager.verifyApiKey(apiKey.key);
if (verified) {
console.log('Valid API key for tenant:', verified.tenantId);
}⚠️ Security Note
API keys are hashed using SHA256 before storage. The plaintext key is only available during generation. Store it securely!
Tenant Configuration
Tenant Settings
Each tenant has customizable settings that control resource limits and behavior:
| Setting | Type | Description |
|---|---|---|
| maxAgents | number | Maximum number of agents allowed |
| maxConversations | number | Maximum concurrent conversations |
| rateLimit | object | Requests per minute limit |
| dataRetentionDays | number | How long to retain conversation data |
| allowedProviders | string[] | Permitted LLM providers |
Tenant Status
Tenants can have one of three statuses:
- ACTIVE: Fully operational, can use all features
- SUSPENDED: Temporarily disabled, typically for non-payment or policy violations
- INACTIVE: Permanently disabled, retains data but no access
// Suspend a tenant
await tenantManager.updateTenant(tenant.id, {
status: 'SUSPENDED',
});
// Reactivate a tenant
await tenantManager.updateTenant(tenant.id, {
status: 'ACTIVE',
});Quota Management
Track and enforce usage quotas across different time periods.
Recording Usage
// Record API usage
await tenantManager.recordQuotaUsage(tenant.id, {
resource: 'api_calls',
amount: 1,
period: 'hourly',
});
// Record token usage
await tenantManager.recordQuotaUsage(tenant.id, {
resource: 'tokens',
amount: 5000,
period: 'daily',
});
// Check quota status
const quotas = await storage.getQuotas(tenant.id);
for (const quota of quotas) {
console.log(`${quota.resource}: ${quota.used}/${quota.limit} (${quota.period})`);
}Quota Periods
Quotas can be tracked across three time periods:
- hourly: Resets every hour
- daily: Resets every day
- monthly: Resets every month
Tenant-Aware Memory
Use TenantBufferMemory to ensure conversation data is isolated per tenant.
import { TenantBufferMemory } from '@lov3kaizen/agentsea-core';
const memory = new TenantBufferMemory({ maxMessages: 50 });
// Save conversation for a specific tenant
await memory.save('tenant-123', 'conv-456', [
{ role: 'user', content: 'Hello!' },
{ role: 'assistant', content: 'Hi! How can I help?' },
]);
// Load only this tenant's conversations
const history = await memory.load('tenant-123', 'conv-456');
// Clear tenant data
await memory.clear('tenant-123', 'conv-456');Storage Backends
AgentSea provides multiple storage implementations for tenant data:
Memory Storage (Development)
In-memory storage, ideal for development and testing.
import { MemoryTenantStorage, TenantManager } from '@lov3kaizen/agentsea-core';
const storage = new MemoryTenantStorage();
const tenantManager = new TenantManager(storage);Database Storage (Production)
For production deployments, implement the TenantStorage interface with your preferred database.
import { TenantStorage, Tenant } from '@lov3kaizen/agentsea-core';
class PostgresTenantStorage implements TenantStorage {
async create(tenant: Omit<Tenant, 'id' | 'createdAt' | 'updatedAt'>): Promise<Tenant> {
// Implement with your database
}
async get(id: string): Promise<Tenant | null> {
// Implement with your database
}
async update(id: string, updates: Partial<Tenant>): Promise<Tenant> {
// Implement with your database
}
async delete(id: string): Promise<void> {
// Implement with your database
}
async getBySlug(slug: string): Promise<Tenant | null> {
// Implement with your database
}
async list(): Promise<Tenant[]> {
// Implement with your database
}
// ... implement remaining methods
}NestJS Integration
AgentSea provides decorators and guards for seamless multi-tenancy in NestJS applications.
Tenant Guard
import { Controller, Get, UseGuards } from '@nestjs/common';
import { TenantGuard, Tenant } from '@lov3kaizen/agentsea-nestjs';
@Controller('agents')
@UseGuards(TenantGuard)
export class AgentsController {
@Get()
async listAgents(@Tenant() tenant: TenantContext) {
// tenant.id, tenant.name, tenant.settings are available
return this.agentService.getAgentsByTenant(tenant.id);
}
}Tenant Decorator
Extract tenant context from requests using the @Tenant() decorator.
import { Tenant } from '@lov3kaizen/agentsea-nestjs';
@Post('execute')
async executeAgent(
@Tenant() tenant: TenantContext,
@Body() dto: ExecuteAgentDto,
) {
// Use tenant.id for data isolation
return this.agentService.execute(tenant.id, dto);
}Complete Example
import {
TenantManager,
MemoryTenantStorage,
TenantBufferMemory,
Agent,
AnthropicProvider,
ToolRegistry,
} from '@lov3kaizen/agentsea-core';
// Initialize multi-tenancy system
const storage = new MemoryTenantStorage();
const tenantManager = new TenantManager(storage);
// Create tenant
const tenant = await tenantManager.createTenant({
name: 'Acme Corp',
slug: 'acme-corp',
settings: {
maxAgents: 10,
maxConversations: 100,
allowedProviders: ['anthropic'],
},
});
// Generate API key
const apiKey = await tenantManager.generateApiKey(tenant.id);
console.log('Save this API key:', apiKey.key);
// Setup tenant-isolated memory
const memory = new TenantBufferMemory();
// Create agent for tenant
const agent = new Agent(
{
name: 'support-agent',
model: 'claude-sonnet-4-20250514',
provider: 'anthropic',
systemPrompt: 'You are a helpful customer support assistant.',
},
new AnthropicProvider(process.env.ANTHROPIC_API_KEY),
new ToolRegistry(),
{ type: 'custom', store: memory },
);
// Execute with tenant context
const conversationId = 'conv-1';
const response = await agent.execute('How do I reset my password?', {
conversationId,
sessionData: { tenantId: tenant.id },
history: await memory.load(tenant.id, conversationId),
});
// Save tenant conversation
await memory.save(tenant.id, conversationId, [
{ role: 'user', content: 'How do I reset my password?' },
{ role: 'assistant', content: response.content },
]);
// Record usage
await tenantManager.recordQuotaUsage(tenant.id, {
resource: 'api_calls',
amount: 1,
period: 'hourly',
});
await tenantManager.recordQuotaUsage(tenant.id, {
resource: 'tokens',
amount: response.metadata.tokensUsed || 0,
period: 'daily',
});
console.log('Response:', response.content);Best Practices
- API Key Security: Always use HTTPS and never log plaintext API keys
- Quota Monitoring: Set up alerts when tenants approach quota limits
- Data Retention: Respect tenant data retention policies and automate cleanup
- Resource Limits: Set conservative defaults and allow upgrades
- Audit Logging: Track all tenant operations for compliance
- Graceful Degradation: Handle quota exceeded scenarios with clear error messages
- Testing: Test with multiple tenants to ensure complete isolation
Next Steps
- NestJS Integration - Build multi-tenant APIs with NestJS
- Memory Stores - Learn about tenant-aware memory systems
- Observability - Monitor tenant usage and performance
- Examples - See complete multi-tenancy examples