Scoping must be the default
One query that forgets its tenant column is a data leak, and it will not throw.
Tenancy is a decision that touches routing, queries, storage, mail, and DNS. Stacks has the pieces in the box: team ownership on the models, gates that scope every read, per-site configuration, and cloud config that can put a customer on their own hostname with its own certificate.
The work that arrives whether you planned for it or not.
One query that forgets its tenant column is a data leak, and it will not throw.
Each customer hostname needs DNS, a certificate, and routing that maps it back to a tenant.
One tenant importing a million rows should not slow down everyone else on the queue.
Branding, mail sender, storage prefix, and feature flags all vary, and each is somewhere different in a naive design.
All of it in the same install, typed against the rest of your application.
Teams and roles ship first-party, so a tenant column and its relationships are a trait rather than a convention.
Gates and query scopes apply the tenant filter centrally, so an action cannot forget it.
config/sites.ts and the router map a domain to a tenant, and config/cloud.ts publishes the DNS and TLS for it.
Per-tenant queues and rate limits keep one heavy customer from starving the rest.
Sender identity, SPF, DKIM, and DMARC records per domain, published with the rest of the infrastructure.
Uploads land under a tenant prefix locally and on S3 through the same storage API.
Every one of these ships in the same install. Follow one to see what it covers.
Buddy scaffolds, runs, and ships this the same way it does the rest of the app.
Projects of this shape usually grow into one of these.