Skip to content

Documentation

Multi-Tenant Security Architecture

In-depth documentation of the security boundaries and tenant isolation in SveltyCMS.

4/10/2026
4 min read Edit on GitHub

SveltyCMS employs a rigorous “Defense in Depth” strategy for multi-tenant environments. This document outlines the architectural boundaries, isolation mechanisms, and security protocols that prevent cross-tenant data leakage and unauthorized access.

1. Core Isolation Principles (Vite+ Optimized)

SveltyCMS leverages the Vite+ middleware pipeline to enforce hard isolation at every request:

  • Middleware Gating: All requests pass through handleSystemState, handleAuthentication, and the central API dispatcher.

  • Tenant Context: The tenantId is resolved early and injected into the SvelteKit locals and GraphQL context.

  • Hardened TBAC: The authentication hook (handleAuthentication.ts) now dynamically fetches and populates locals.roles specifically for the current tenantId context. This ensures that permissions granted in one tenant do not leak into another, even for the same user ID.

  • Adapter Scoping: Every database operation is automatically scoped by the resolved tenantId.

  • 🧪 Test Isolation (CI/CD): During automated testing, SveltyCMS uses the x-test-worker-index header to dynamically route requests to isolated test database shards. This prevents race conditions and data leakage between parallel test workers in the CI pipeline.

2. API Security Boundaries

Both REST and GraphQL APIs are subject to tenant-level security checks:

  • REST API Dispatcher: All routes under /api/[...path] are centrally gated by a Fail-Closed security layer. It validates the required Permission ID against the user’s locals.roles for the current tenant before any adapter logic is executed. This pre-calculated route-level RBAC ensures that granular permissions never degrade database performance, making SveltyCMS immune to the SQL query bloat (e.g., 4000+ line injected WHERE clauses) that commonly throttles multi-tenant competitors.
  • GraphQL Yoga: The schema is filtered based on the requester’s tenant permissions via the same robust TBAC layer.
  • Performance Quotas: Rate limiting is applied per-tenant to prevent resource exhaustion.

3.1 GraphQL Schema Isolation & Protection

SveltyCMS prevents “Schema Leakage” by maintaining isolated YogaServer instances per tenant. This ensures that custom collections or fields defined by one tenant are never visible in the introspection query of another.

Furthermore, SveltyCMS protects the GraphQL interface with:

  • AST Depth Limitation: Limits query depth to 7 via createDepthLimitRule wired into the Yoga onValidate hook, preventing nested relation Denial of Service (DoS) attacks.
  • Max Aliases Limitation: Limits query aliases to 15 via createMaxAliasesRule, preventing alias-based batching DoS attacks.
  • Strict Tenant Enforcement: All GraphQL resolvers strictly check that the request’s context tenantId precisely matches the entity’s tenantId. Mismatches result in immediate query termination and an audit log event, unless triggered by a Super-Admin with bypassTenantIsolation.
  • Tenant-Scoped Rate Limits: Leverages Redis-backed rate-limiters scoped directly to rate:tenant:${tenantId}:graphql:minute ensuring fair usage and protecting infrastructure from tenant-specific floods.

3.2 Real-Time Event Stream (SSE)

The Server-Sent Events (SSE) stream at /api/events performs real-time filtering. The eventBus wildcard listener verifies the payload.tenantId against the user’s locals.tenantId before enqueuing data to the stream controller.

3.3 Memory & Resource Hygiene

  • Cleanup: SSE streams utilize the cancel() method to clear setInterval heartbeats and eventBus listeners, preventing memory leaks that could lead to Denial of Service (DoS).
  • Caching: Services like AutomationService and WebhookService use a Map<tenantId, Cache> structure to ensure that cached data is never shared across tenant boundaries.
  • Batch Operation Guard: The core batch-module.ts (execute()) validates that all operations in a batch share the same tenantId before coalescing. Mixed-tenant batches are rejected with a security error — preventing cross-tenant data leakage when multiple tenants share a database instance.

4. Data Portability & Encryption

4.1 Export Security

All data exports (Standard and Full) are tenant-scoped. Sensitive fields (API keys, secrets) are automatically detected via pattern matching and encrypted using AES-256-GCM with an Argon2 derived key before being included in the export JSON.

4.2 Import Integrity

The import system validates the scope of incoming settings. It dynamically looks up setting definitions to ensure that user-specific preferences are never imported into the global system scope, preventing database corruption.

5. Summary of Boundaries

Boundary Mechanism Enforcement Point
Data Explicit tenantId filters Database Adapters
CRUD Guard Auto-inject + warn on missing tenantId crud-tenant-guard.ts
File Server Tenant-scoped path ACL /files/[...path]/+server.ts
Batch Mixed-tenant rejection guard batch-module.ts
Schema Isolated Yoga Instances GraphQL Handler
Real-Time Payload Filtering SSE Controller
Secrets AES-256-GCM Encryption Export Utility
UI/Prefs Compound tenantId:userId keys Preferences API
Infrastructure Super-Admin Restrictons RBAC Middleware

This architecture ensures that SveltyCMS remains secure, compliant, and performant even when hosting thousands of isolated customers on a single installation.


Related

securitymulti-tenancyarchitecture
Was this page helpful?