Multi-Tenant Security¶
Archivus operates a single shared infrastructure serving hundreds of organizations. Every organization must be cryptographically isolated from every other.
Security is not a feature—it's the foundation.
Security Layers¶
graph TB
subgraph Layer1["Layer 1: Network & Transport"]
TLS[TLS 1.3 Encryption]
WAF[Web Application Firewall]
DDOS[DDoS Protection]
end
subgraph Layer2["Layer 2: Application"]
JWT[JWT Validation]
SUBDOMAIN[Subdomain Routing]
MIDDLEWARE[Tenant Middleware]
end
subgraph Layer3["Layer 3: Service"]
TENANTCTX[Tenant Context]
VALIDATE[Tenant ID Validation]
AUTHZ[Authorization Checks]
end
subgraph Layer4["Layer 4: Database"]
RLS[Row-Level Security]
TENANTID[Tenant ID Scoping]
INDEXES[Tenant-Scoped Indexes]
end
Layer1 --> Layer2
Layer2 --> Layer3
Layer3 --> Layer4 Defense-in-depth: Every layer enforces tenant isolation independently. Breach one layer, three more protect data.
Layer 1: Network & Transport¶
TLS 1.3 Everywhere¶
All traffic encrypted in transit: - Frontend → Backend (HTTPS) - Backend → Database (TLS) - Backend → Redis (TLS) - Worker → Database (TLS)
No plaintext data in transit, ever.
Web Application Firewall¶
Protection against: - SQL injection attempts - Cross-site scripting (XSS) - CSRF attacks - Malformed requests - Rate limit violations
Subdomain Isolation¶
Each tenant has a unique subdomain:
Benefits: - Cookie isolation (CORS protection) - CSP enforcement per tenant - Easy tenant identification - No tenant ID in URL path
Layer 2: Application¶
JWT Authentication¶
Token structure:
{
"sub": "user_uuid",
"tenant_id": "tenant_uuid",
"email": "user@example.com",
"role": "admin",
"subscription_tier": "enterprise",
"exp": 1234567890,
"iss": "archivus.ai"
}
Security properties: - Signed with RS256 (public/private key) - Short expiration (1 hour) - Refresh token rotation - Secure HttpOnly cookies - CSRF token validation
Tenant Middleware¶
Every HTTP request passes through tenant middleware:
1. Extract subdomain from request
↓
2. Validate JWT token
↓
3. Extract tenant_id from token
↓
4. Verify subdomain matches tenant
↓
5. Set tenant context in request
↓
6. Proceed to handler
Validation failure → 401 Unauthorized (no data exposure)
Role-Based Access Control¶
Four roles per tenant:
| Role | Permissions |
|---|---|
| Owner | Full access, billing, delete tenant |
| Admin | Manage users, configure settings, view all docs |
| Member | Create/edit/delete own documents, view shared |
| Viewer | Read-only access to shared documents |
Enforcement: Every service method checks user.role >= required_role
Layer 3: Service¶
Tenant Context Propagation¶
Service methods receive tenant context:
func (s *DocumentService) GetDocument(
ctx context.Context,
tenantID uuid.UUID, // ALWAYS required
documentID uuid.UUID,
) (*Document, error) {
// Validate tenant context matches request
if !s.ValidateTenantContext(ctx, tenantID) {
return nil, ErrUnauthorized
}
// All queries scoped by tenantID
return s.repo.GetByID(ctx, tenantID, documentID)
}
The rule: Every service method takes tenantID as first parameter (after ctx).
Tenant ID Validation¶
Before every database operation:
func (s *Service) ValidateTenantContext(ctx context.Context, tenantID uuid.UUID) bool {
// Extract tenant from JWT in context
ctxTenantID := ctx.Value("tenant_id").(uuid.UUID)
// MUST match
return ctxTenantID == tenantID
}
Fail-secure: Mismatch → operation aborts, no data returned.
Authorization Checks¶
Resource-level permissions:
func (s *DocumentService) UpdateDocument(
ctx context.Context,
tenantID uuid.UUID,
documentID uuid.UUID,
updates *DocumentUpdate,
) error {
// 1. Check tenant context
if !s.ValidateTenantContext(ctx, tenantID) {
return ErrUnauthorized
}
// 2. Fetch document (already tenant-scoped)
doc, err := s.repo.GetByID(ctx, tenantID, documentID)
if err != nil {
return err
}
// 3. Check ownership or share permissions
userID := ctx.Value("user_id").(uuid.UUID)
if !s.CanEdit(userID, doc) {
return ErrForbidden
}
// 4. Proceed with update
return s.repo.Update(ctx, tenantID, documentID, updates)
}
Three checks: Tenant context, resource exists, user authorized.
Layer 4: Database (Row-Level Security)¶
PostgreSQL RLS¶
Every table has Row-Level Security enabled:
-- Example: documents table
ALTER TABLE documents ENABLE ROW LEVEL SECURITY;
-- Policy: Users can only see their tenant's documents
CREATE POLICY documents_tenant_isolation ON documents
FOR ALL
USING (tenant_id = current_setting('app.current_tenant_id')::UUID);
How it works:
1. Application sets session variable:
SET app.current_tenant_id = 'tenant_uuid';
2. PostgreSQL filters ALL queries:
SELECT * FROM documents WHERE <your conditions>
→
SELECT * FROM documents
WHERE tenant_id = 'tenant_uuid' AND <your conditions>
3. Attempts to access other tenants' data return zero rows
The breakthrough: Database enforces isolation even if application code has bugs.
RLS Coverage¶
149 tables with RLS policies:
| Category | Tables | Policy Type |
|---|---|---|
| Documents | 12 | Tenant + ownership |
| Knowledge Graph | 7 | Tenant isolation |
| Users | 5 | Tenant membership |
| AI Processing | 8 | Tenant + job ownership |
| Voice | 4 | Tenant + session ownership |
| Federation | 6 | Tenant + trust relationship |
| Workflows | 9 | Tenant + DAG ownership |
| Compliance | 3 | Tenant isolation |
| Settings | 8 | Tenant configuration |
| Other | 87 | Tenant isolation |
Service Role Bypass: Background workers use service role to bypass RLS for system operations (e.g., enrichment, batch processing).
Tenant-Scoped Indexes¶
Every foreign key index includes tenant_id:
-- Standard index (not optimal for multi-tenant)
CREATE INDEX idx_documents_folder ON documents(folder_id);
-- Tenant-scoped index (optimal)
CREATE INDEX idx_documents_tenant_folder ON documents(tenant_id, folder_id);
Benefits: - Faster queries (index scan instead of table scan) - Partition-ready (future optimization) - Enforces tenant isolation at index level
Query Performance¶
With tenant scoping:
-- Fast: Uses tenant-scoped index
SELECT * FROM documents
WHERE tenant_id = '...' AND folder_id = '...';
Without tenant scoping (prevented by middleware):
Result: Tenant isolation accelerates queries instead of slowing them down.
Sensitive Data Protection¶
PII Detection¶
Real-time PII scanning using: - Regex patterns: SSN, credit cards, phone numbers - AI detection: Context-aware PII (names in sensitive contexts)
Action on detection:
1. Tag document with sensitivity level
2. Redact PII in previews (masked display)
3. Log detection event (compliance audit)
4. Notify compliance team (configurable)
5. Restrict sharing (prevent accidental exposure)
Field-Level Encryption¶
Sensitive fields encrypted at rest:
-- Encrypted columns
ALTER TABLE users
ADD COLUMN ssn_encrypted BYTEA;
-- Encryption key per tenant
-- Stored in KMS (Key Management Service)
-- Never in application code
Tenant-specific keys: Each tenant has unique encryption keys. Even if database is compromised, data remains encrypted.
Credential Management¶
Secret storage: - API keys: Encrypted in database, decrypted in memory - Passwords: Bcrypt hashed (cost factor 12) - OAuth tokens: Encrypted with rotation policy
No plaintext secrets in code, logs, or environment variables.
Audit Logging¶
What's Logged¶
Every action logged for compliance:
{
"tenant_id": "...",
"user_id": "...",
"action": "document.update",
"resource_id": "doc_uuid",
"changes": {
"field": "classification",
"old_value": "UNKNOWN",
"new_value": "INVOICE"
},
"ip_address": "192.168.1.1",
"user_agent": "Mozilla/5.0...",
"timestamp": "2026-02-07T14:30:00Z"
}
Categories: - Authentication (login, logout, token refresh) - Authorization (permission checks, access denials) - Data access (reads, writes, deletes) - Settings changes (configuration updates) - AI operations (GOLAG decisions, claim verification) - Federation events (cross-tenant queries)
Long-Term Retention¶
Audit logs synced to MotherDuck/S3 Parquet for 7+ year retention:
Cost: $0.023/GB/month (vs $0.125/GB in Postgres)
Penetration Testing¶
Security Boundaries Tested¶
- Tenant Isolation
- Attempt to access other tenant's documents via API
- SQL injection to bypass tenant_id filtering
-
JWT token manipulation
-
Authentication
- Brute force login attempts
- Token replay attacks
-
Session hijacking
-
Authorization
- Privilege escalation (Member → Admin)
- Resource access without permissions
-
Cross-tenant resource references
-
Data Exfiltration
- Bulk export without authorization
- Federation query abuse
- WebSocket message injection
Result: All boundaries hold. RLS prevents data exposure even when application code is bypassed.
Compliance Certifications¶
| Standard | Status | Scope |
|---|---|---|
| SOC 2 Type II | In Progress | Security, availability, confidentiality |
| ISO 27001 | Planned | Information security management |
| GDPR | Compliant | Data privacy, right to erasure |
| HIPAA | Planned (2026) | Protected health information |
| CCPA | Compliant | California privacy rights |
Incident Response¶
Detection¶
Automated alerts for: - Failed authentication attempts (>5 in 5 minutes) - Cross-tenant access attempts - Unusual API usage patterns - Bulk data export without authorization - RLS policy violations
Response Protocol¶
1. Alert triggered → Security team notified
↓
2. Investigate in audit logs
↓
3. Identify affected tenants
↓
4. Isolate compromised resources
↓
5. Notify affected tenants (if required)
↓
6. Remediate vulnerability
↓
7. Post-incident review
SLA: Critical incidents responded to within 1 hour.
Tenant Deletion¶
Right to Erasure (GDPR)¶
When tenant requests deletion:
1. Mark tenant as deleted (soft delete)
↓
2. Revoke all user access tokens
↓
3. Queue background deletion job
↓
4. Delete all tenant data (documents, Knowledge Graph, logs)
↓
5. Scrub audit logs (pseudonymize tenant references)
↓
6. Remove S3 Parquet files
↓
7. Confirm deletion to tenant
↓
8. Retain deletion log (compliance)
Duration: 30 days (grace period), then permanent deletion.
What This Enables¶
| Single-Tenant SaaS | Archivus Multi-Tenant |
|---|---|
| Dedicated infrastructure | Shared infrastructure (cost-effective) |
| Manual scaling | Auto-scaling |
| Deployment per customer | Single deployment, all customers |
| Hard tenant isolation | Cryptographic tenant isolation |
| High cost per tenant | Low marginal cost |
The Result¶
Cryptographic tenant isolation where: - Every layer enforces isolation independently - Database prevents cross-tenant access (RLS) - Application validates every request - Audit logs prove compliance - Penetration testing validates security
Not "we think it's secure"—mathematically proven isolation.
Security through depth, not hope.