Multi-Tenancy
How NNO isolates resources across platforms and tenants using naming conventions and per-platform provisioning.
Multi-Tenancy
NNO 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 NNO. 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 NNO onboards a new platform, it provisions a completely independent set of Cloudflare resources for that platform. Two platforms on the same NNO instance never share a database, auth service, or Workers — they share only the underlying Cloudflare account (Phase 1) and the NNO-operated gateway and registry.
Resource isolation by naming
NNO 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 NNO infrastructure
Some services are operated once by NNO 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.
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.
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.