NNO Docs
Concepts

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 KV

Because 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 serviceIsolation boundary
GatewayRouting is per-platform by API key / auth token
RegistryEach record belongs to exactly one platform ID
BillingStripe customer per platform
IAMAPI 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 stack

Auth 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.

On this page