Multi-Tenancy
How Neutrino isolates resources across platforms and tenants using naming conventions and per-platform provisioning.
Multi-Tenancy
Neutrino is built for multi-tenant applications from the ground up. Every construct — from DNS hostnames to Cloudflare resource names to session cookies — is designed so that multiple platforms can run on shared infrastructure without any data leakage between them.
Platform = your product
A platform is the top-level container in Neutrino. It represents one client product or application. Each platform is identified by a 10-character nano-ID (e.g. k3m9p2xw7q) that appears in every resource name and URL belonging to it.
When Neutrino onboards a new platform, it provisions a completely independent set of Cloudflare resources for that platform. Two platforms on the same Neutrino instance never share a database, auth service, or Workers — they share only the underlying Cloudflare account (Phase 1) and the Neutrino-operated gateway and registry.
Resource isolation by naming
Neutrino enforces isolation through resource naming rather than separate Cloudflare accounts (in Phase 1). Every Cloudflare resource name encodes the platform ID:
{platformId}-default-auth ← auth Worker
{platformId}-default-auth-db ← auth D1 database
{platformId}-{stackId}-db ← stack shared D1
{platformId}-{stackId}-storage ← stack shared R2
{platformId}-{stackId}-kv ← stack shared KVBecause all resource names are unique by construction, no cross-platform access is possible at the Cloudflare layer. The registry tracks the mapping from logical names to actual Cloudflare resource IDs.
Tenants within a platform
Within a platform, tenants provide logical isolation. Tenants map directly to Better Auth organisations and represent distinct business units, customer accounts, or teams. A user can belong to multiple tenants with different roles in each.
Sub-tenants nest under tenants for deeper hierarchies when needed.
The active tenant drives the shell context — ShellContext.tenant reflects which tenant the user is currently operating in. Feature permissions are evaluated per-tenant, not per-platform.
Shared Neutrino infrastructure
Some services are operated once by Neutrino and shared across all platforms:
| Shared service | Isolation boundary |
|---|---|
| Gateway | Routing is per-platform by API key / auth token |
| Registry | Each record belongs to exactly one platform ID |
| Billing | Stripe customer per platform |
| IAM | API keys scoped to a platform ID |
These services never return data across platform boundaries. The registry enforces platform-scoped queries at the query level.
Important: D1 does NOT enforce row-level security. Cross-platform isolation is application-level discipline — every query that touches a tenanted table must include the
platformId(or equivalent) in its WHERE clause. The Drizzle schema doesn't enforce this; it's a contract maintained by code review and the standard query helpers inservices/{iam,registry}/src/.
DNS and session scope
Every resource for a platform lives under *.{platformId}.nno.app:
auth.svc.default.k3m9p2xw7q.nno.app ← auth Worker
dashboard.app.x7y8z9w0q1.k3m9p2xw7q.nno.app ← console app in a stackAuth session cookies use the domain .<platformId>.nno.app. This means a single login gives the user access to all stacks and apps within their platform — no re-authentication when navigating between stacks.
Suspension enforcement window: Gateway caches IAM session validations for 5 minutes (in-memory per Worker isolate). A platform suspension may take up to 5 minutes to fully propagate to running sessions; the Gateway's KV-backed
platform:{id}:statuslookup (60s TTL) is the faster path for real-time suspension.
Naming utilities
When building features, never construct platform or resource identifiers by hand. Use the utilities from @neutrino-io/core/naming:
import { generateId, buildResourceName } from "@neutrino-io/core/naming";
const platformId = generateId(); // → 'k3m9p2xw7q'
buildResourceName(platformId, "default", "auth"); // → 'k3m9p2xw7q-default-auth'These functions encode the naming conventions and prevent format drift.