core-multisite
Multi-tenant site isolation with PostgreSQL schema-per-site, domain mapping, and scoped caches.
core-multisite
Primitive Paid
core-multisite turns a single Indigo install into a multi-tenant network.
Each site gets its own PostgreSQL schema, with domain-based resolution and
fully isolated caches, jobs, and real-time channels.
What it does
- Schema-per-site isolation — each site's CMS tables live in their own PG schema
- Domain and slug resolution — the proxy maps an incoming host to a site
- Automatic scoping of caches, Redis keys, WebSocket channels, and BullMQ jobs
- A full site lifecycle — create, suspend, restore, clone, and hard-delete
- Domain management with background DNS TXT verification
- A network admin dashboard at
/dashboard/settings/sites
Installation
core-multisite is a paid module and requires a valid Indigo license.
bun run indigo add core-multisite
bun run db:generate
bun run db:migrate
ℹ️ Info
Without this module installed, Indigo runs single-site with zero overhead:
getScope() returns null, getScopedKey('foo') returns 'foo', and the
PostgreSQL search_path stays on public.
Configuration
DI is configured in config/deps/multisite-deps.ts (scaffolded on install).
The module also ships CLI scripts for site management — site-create,
site-delete, site-list, site-suspend, site-unsuspend, site-restore,
and migrate-sites.
Schema
Shared tables live in the public schema; per-site CMS tables live in each
site's own schema (e.g. site_abc.cms_posts).
| Table | Notable columns |
|---|---|
sites |
slug, schemaName, status, soft-delete fields |
site_domains |
siteId, domain, verification state |
site_members |
siteId, userId, role |
API
The sites router is restricted to network operators — staff for read and
activation, superadmin for all management.
| Endpoint | Access | Purpose |
|---|---|---|
sites.list |
staff | List sites |
sites.getById |
superadmin | Site detail |
sites.stats |
superadmin | Network statistics |
sites.create |
superadmin | Create a site and its schema |
sites.update |
superadmin | Update site settings |
sites.suspend / sites.unsuspend |
superadmin | Toggle suspension |
sites.softDelete / sites.restore |
superadmin | Soft-delete and restore |
sites.hardDelete |
superadmin | Permanently drop a site and its schema |
sites.clone |
superadmin | Clone an existing site |
sites.addDomain / sites.removeDomain / sites.listDomains |
superadmin | Manage domains |
sites.addMember / sites.removeMember |
superadmin | Manage site members |
sites.setActive |
staff | Switch the active site context |
Integration
The request flow threads site context end to end:
- Proxy resolves the domain
applySiteHeaders(request)looks up the host and setsx-site-*headers. - tRPC extracts the context
createContextcallsextractSiteContext(headers)to obtain{ siteId, schemaName }. - Search path is applied
applySiteSearchPath(schemaName)setssearch_path = site_abc, public. - Handler runs scoped
withScope(siteId, ...)wraps the handler so every query, cache, and channel is site-scoped.
The module registers a DNS verification background worker (startDnsVerificationWorker)
and a seedNetworkAdmin step that creates the network admin site on
bun run init. It adds a Sites entry to the dashboard Settings group.
Related
- Module Reference — full module catalog
- Managing Modules — install and update modules
- Module Development — scope-aware module patterns