Contentio is a multi-tenant CMS SaaS where each customer gets a site that looks and behaves like their own — their domain, their branding, their content — while running on shared infrastructure. The multi-tenancy model is domain-based rather than subdomain-based: a customer's site lives at their own domain (example.com), not at a subdomain of Contentio.
This creates a specific architectural problem: when a request arrives at the server, how do you know which tenant it belongs to? There's no subdomain prefix to parse, no URL parameter, and the user certainly shouldn't need to identify themselves for the server to find their site. The answer is the domain itself.
Domain-Based Tenant Resolution
Every incoming request passes through TenantMiddleware. The middleware reads the HTTP Host header, looks up the corresponding tenant record in the database, and attaches that tenant to the request context. The lookup result is cached for one hour per domain, so subsequent requests don't hit the database for tenant resolution.
Once the tenant is resolved, a global Eloquent scope — TenantScope — is applied to all content model queries. This scope appends a WHERE tenant_id = ? condition to every query automatically. Content models (pages, announcements, blog posts, galleries, FAQs) use a TenantAware trait that both applies the global scope for reads and automatically sets the tenant_id on creation. Cross-tenant data leakage at the database layer is structurally prevented: a query running in one tenant's context cannot return another tenant's content.
Three-Tier Theme Fallback
Contentio's theming system resolves templates through three levels. First, the system looks for tenant-specific template overrides in resources/views/public/domains/{domain}/. If a template exists there, it's used. If not, the system falls back to the tenant's selected theme in resources/views/public/themes/{theme}/. If that template doesn't exist either, it falls back to the default in resources/views/public/default/.
This hierarchy means that standard customizations — a different color palette, different logo — live entirely in the database and are applied at render time through Blade variables. Structural customizations — a unique layout for a particular page type — can be placed in the domain-specific override folder without touching the shared theme. And entirely new themes can be added to the themes folder and applied to any tenant.
Subscription-Based Feature Flags
Each Contentio tenant has a subscription tier that controls which CMS features are available. The feature flag system is a JSON array on the tenant record — checking a feature is a single array lookup. Base tiers include pages, galleries, announcements, FAQs, contact forms, and SEO configuration. Higher tiers unlock Google Analytics integration, custom CSS injection, custom JavaScript injection, white-label mode (no Contentio attribution), API access, and automated backup.
The Livewire components that power the admin interface check these flags before rendering feature controls. A tenant on a base plan never sees UI for features they don't have access to, rather than seeing disabled controls or error messages.
Security Layer
Beyond tenant isolation, Contentio includes per-tenant security configuration: IP allowlists and blocklists, rate limiting, and access logging. A tenant can restrict their admin panel to specific IP ranges — useful for organizations that manage their CMS from a fixed office network. The access log records all admin actions with user, timestamp, action, and affected resource, providing an audit trail for compliance purposes.
Two-factor authentication is optional per tenant and per user. The 2FA implementation uses TOTP (time-based one-time passwords) compatible with standard authenticator apps.
The Livewire Admin Interface
The admin panel is built with Livewire 3, which gives reactive component behavior without a JavaScript frontend build step. Form submissions update in place, content lists refresh without full page loads, and drag-and-drop ordering works without custom JavaScript. The development workflow stays in PHP and Blade, which was a deliberate choice — Contentio's complexity is in the multi-tenant architecture, not in the admin UI interactions, so keeping the frontend simple reduces the total surface area to maintain.