Skip to content

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:

acme-corp.archivus.ai
globex-industries.archivus.ai
stratusclean.archivus.ai

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):

-- Blocked by RLS, returns zero rows
SELECT * FROM documents WHERE folder_id = '...';

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:

PostgreSQL (90 days hot) → S3 Parquet (7+ years cold)

Cost: $0.023/GB/month (vs $0.125/GB in Postgres)

Penetration Testing

Security Boundaries Tested

  1. Tenant Isolation
  2. Attempt to access other tenant's documents via API
  3. SQL injection to bypass tenant_id filtering
  4. JWT token manipulation

  5. Authentication

  6. Brute force login attempts
  7. Token replay attacks
  8. Session hijacking

  9. Authorization

  10. Privilege escalation (Member → Admin)
  11. Resource access without permissions
  12. Cross-tenant resource references

  13. Data Exfiltration

  14. Bulk export without authorization
  15. Federation query abuse
  16. 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.