Multi-Tenant Architecture
Enterprise-Grade Isolation with Zero Compromise
Overview
Archivus implements a sophisticated multi-tenant architecture with 5 layers of security ensuring complete data isolation. Every query, every AI interaction, and every operation is scoped to your tenant, mathematically guaranteeing data separation.
5-Layer Security Model
┌─────────────────────────────────────────────────────────────────┐
│ Incoming Request │
└──────────────────────────────┬──────────────────────────────────┘
│
┌──────────────────────────────▼──────────────────────────────────┐
│ Layer 1: Authentication │
│ ├─ JWT Token Verification (Supabase Auth) │
│ ├─ API Key Validation │
│ └─ Session Verification │
└──────────────────────────────┬──────────────────────────────────┘
│
┌──────────────────────────────▼──────────────────────────────────┐
│ Layer 2: Authorization │
│ ├─ Role-Based Access Control (RBAC) │
│ ├─ Resource Permissions │
│ └─ Feature Tier Verification │
└──────────────────────────────┬──────────────────────────────────┘
│
┌──────────────────────────────▼──────────────────────────────────┐
│ Layer 3: Tenant Context │
│ ├─ Tenant ID Extraction from Auth │
│ ├─ Request Context Injection │
│ └─ Cross-Tenant Request Rejection │
└──────────────────────────────┬──────────────────────────────────┘
│
┌──────────────────────────────▼──────────────────────────────────┐
│ Layer 4: Service Layer Validation │
│ ├─ Business Logic Tenant Checks │
│ ├─ Resource Ownership Verification │
│ └─ AI Context Filtering │
└──────────────────────────────┬──────────────────────────────────┘
│
┌──────────────────────────────▼──────────────────────────────────┐
│ Layer 5: Database RLS (Row-Level Security) │
│ ├─ PostgreSQL RLS Policies on 40+ Tables │
│ ├─ Automatic Query Filtering │
│ └─ Database-Level Backstop │
└──────────────────────────────┬──────────────────────────────────┘
│
┌──────────────────────────────▼──────────────────────────────────┐
│ Your Isolated Data │
└─────────────────────────────────────────────────────────────────┘
Layer Details
Layer 1: Authentication
Every request must be authenticated:
JWT Authentication:
// Token validation flow
token := extractToken(request)
claims, err := supabase.VerifyJWT(token)
if err != nil {
return Unauthorized("Invalid token")
}
// Extract tenant_id from claims
tenantID := claims.TenantID
API Key Authentication:
// API key validation
apiKey := request.Header.Get("X-API-Key")
keyRecord, err := validateAPIKey(apiKey)
if err != nil {
return Unauthorized("Invalid API key")
}
// Tenant embedded in key
tenantID := keyRecord.TenantID
Layer 2: Authorization
Role-based access control:
| Role | Permissions |
|---|---|
owner |
Full access, billing, user management |
admin |
All features, user management |
editor |
Create, read, update documents |
viewer |
Read-only access |
// Permission check
if !hasPermission(user, "documents:write") {
return Forbidden("Insufficient permissions")
}
Layer 3: Tenant Context
Every request carries tenant context:
// Middleware injects tenant context
func TenantMiddleware(c *gin.Context) {
tenantID := extractTenantID(c)
// Set in request context
ctx := context.WithValue(c.Request.Context(), "tenant_id", tenantID)
c.Request = c.Request.WithContext(ctx)
c.Next()
}
Layer 4: Service Layer Validation
Business logic re-validates ownership:
func (s *DocumentService) GetDocument(ctx context.Context, docID string) (*Document, error) {
tenantID := ctx.Value("tenant_id").(string)
doc, err := s.repo.FindByID(ctx, docID)
if err != nil {
return nil, err
}
// Service-level tenant check (defense in depth)
if doc.TenantID != tenantID {
return nil, ErrNotFound // Don't leak existence
}
return doc, nil
}
Layer 5: Database RLS
PostgreSQL Row-Level Security as the final backstop:
-- Example RLS policy on documents table
CREATE POLICY "tenant_isolation" ON documents
FOR ALL
USING (tenant_id = current_setting('app.tenant_id')::uuid);
-- Every query automatically filtered
SELECT * FROM documents;
-- Becomes: SELECT * FROM documents WHERE tenant_id = '...'
RLS Coverage
Tables with RLS Policies (40+)
| Category | Tables |
|---|---|
| Core | documents, folders, tags, collections |
| AI | chat_sessions, chat_messages, ai_analysis |
| Collaboration | shares, invitations, workspaces |
| Automation | pipelines, pipeline_runs, workflows |
| DAG | dag_workflows, dag_executions, dag_tasks |
| Audit | audit_logs, api_key_usage, ai_traffic_logs |
| Settings | tenant_settings, user_preferences, mcp_servers |
RLS Policy Pattern
-- Standard tenant isolation policy
ALTER TABLE table_name ENABLE ROW LEVEL SECURITY;
CREATE POLICY "tenant_isolation" ON table_name
FOR ALL
USING (tenant_id = current_setting('app.tenant_id')::uuid);
-- Insert policy ensures tenant_id is set
CREATE POLICY "tenant_insert" ON table_name
FOR INSERT
WITH CHECK (tenant_id = current_setting('app.tenant_id')::uuid);
AI Isolation
Context Filtering
AI never receives cross-tenant data:
func (s *ArchieService) Chat(ctx context.Context, message string) (*Response, error) {
tenantID := ctx.Value("tenant_id").(string)
// Search only within tenant
relevantDocs, err := s.searchService.Search(ctx, SearchParams{
Query: message,
TenantID: tenantID, // Always filtered
Limit: 10,
})
// Build AI context from tenant-filtered results only
context := buildContext(relevantDocs)
// Send to Claude
response, err := s.claude.Chat(message, context)
return response, err
}
What AI Can Access
| Can Access | Cannot Access |
|---|---|
| Your documents | Other tenants’ documents |
| Your chat history | Other users’ history |
| Your collections | System internals |
| Your tenant config | Database schemas |
Tenant Data Model
┌─────────────────────────────────────────────────────────────────┐
│ Tenant │
│ ┌─────────────────────────────────────────────────────────────┐│
│ │ tenant_id: uuid (partition key) ││
│ │ subdomain: "acme-corp" ││
│ │ name: "Acme Corporation" ││
│ │ tier: "team" ││
│ │ settings: { ... } ││
│ └─────────────────────────────────────────────────────────────┘│
│ │ │
│ ┌───────────────┼───────────────┐ │
│ │ │ │ │
│ ┌─────▼─────┐ ┌─────▼─────┐ ┌─────▼─────┐ │
│ │ Users │ │ Documents │ │Collections│ │
│ │tenant_id │ │tenant_id │ │tenant_id │ │
│ └───────────┘ └───────────┘ └───────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
Cross-Tenant Protection
Request Validation
// Cross-tenant access attempt detection
func validateResourceAccess(ctx context.Context, resourceTenantID string) error {
requestTenantID := ctx.Value("tenant_id").(string)
if resourceTenantID != requestTenantID {
// Log security event
securityLog.Warn("cross_tenant_attempt",
"request_tenant", requestTenantID,
"resource_tenant", resourceTenantID,
)
// Return generic error (don't leak info)
return ErrNotFound
}
return nil
}
Audit Logging
All access attempts logged:
{
"event": "document_access",
"tenant_id": "tenant_123",
"user_id": "user_456",
"document_id": "doc_789",
"action": "read",
"success": true,
"timestamp": "2026-01-18T10:30:00Z",
"ip_address": "192.168.1.1",
"user_agent": "..."
}
Storage Isolation
Shared Storage (Default)
supabase-bucket/
├── tenant_abc123/
│ └── documents/
│ └── ...
├── tenant_def456/
│ └── documents/
│ └── ...
Dedicated Storage (Team+)
Each Team+ tenant gets isolated bucket:
archivus-tenant-abc123/ ← Dedicated bucket
├── documents/
├── analytics/
└── exports/
BYOB Storage (Enterprise)
Customer-controlled infrastructure:
customer-s3-bucket/ ← Customer's AWS account
├── archivus/
│ └── tenant_abc123/
│ └── ...
Performance Optimization
RLS Performance
Optimized policies for O(1) per-row overhead:
-- Optimized: Uses index efficiently
CREATE POLICY "tenant_isolation" ON documents
USING (tenant_id = current_setting('app.tenant_id')::uuid);
-- Index supports RLS filtering
CREATE INDEX idx_documents_tenant ON documents(tenant_id);
Query Planning
EXPLAIN ANALYZE SELECT * FROM documents WHERE name LIKE '%contract%';
-- Output shows RLS filter applied first:
-- Index Scan using idx_documents_tenant on documents
-- Index Cond: (tenant_id = '...')
-- Filter: (name ~~ '%contract%'::text)
Compliance Alignment
| Standard | Multi-Tenant Support |
|---|---|
| SOC 2 | Logical separation, access controls |
| GDPR | Data isolation, right to deletion |
| HIPAA | PHI isolation, audit logging |
| ISO 27001 | Access control, segregation |
Verification
Tenant Isolation Test
# Attempt cross-tenant access (should fail)
curl -X GET "https://api.archivus.app/api/v1/documents/doc_from_other_tenant" \
-H "Authorization: Bearer YOUR_TOKEN"
# Response: 404 Not Found (not 403, to avoid leaking existence)
RLS Verification
-- As tenant_a
SET app.tenant_id = 'tenant_a_uuid';
SELECT COUNT(*) FROM documents; -- Returns: 150
-- As tenant_b
SET app.tenant_id = 'tenant_b_uuid';
SELECT COUNT(*) FROM documents; -- Returns: 89
-- No way to access other tenant's data
Related Documentation
Questions about security? Contact security@ubiship.com