Auth Flow
How authentication works in Neutrino — per-platform auth Workers, session cookies, and service-to-service tokens.
Auth Flow
Neutrino uses Better Auth as its authentication framework. The design prioritises complete isolation between platforms: every platform gets its own dedicated auth Worker and its own D1 database. There are no shared user tables.
Per-platform auth Workers
When Neutrino onboards your platform, it deploys a dedicated auth Worker scoped exclusively to your platform:
Platform k3m9p2xw7q
└── Auth Worker: auth.svc.default.k3m9p2xw7q.nno.app
└── D1: k3m9p2xw7q-default-auth-db
├── users, sessions, accounts
├── organizations (= tenants)
├── members, invitations
└── roles, permission grantsThis means your users' credentials and sessions are never co-mingled with another platform's data, even though both platforms run on shared Cloudflare infrastructure.
Login and session flow
Neutrino supports two authentication entry points: a browser flow for users of the console shell, and a CLI flow for developers and CI pipelines.
Browser flow
- The user submits credentials to
https://auth.svc.default.{platformId}.nno.app/api/auth/sign-in - Better Auth validates credentials against the platform's D1 database
- On success, a session cookie is set with domain
.<platformId>.nno.app - The console shell reads the session on every page load via
VITE_AUTH_API_URL - The active tenant (
session.activeOrganizationId) is included in the session and drives theShellContext.tenant
CLI flow
The Neutrino CLI (nno) supports two paths into the same session model:
- Token mode (
nno login --token) — for CI/CD. The provided NNO API token is written verbatim to~/.nno/credentials.json(mode0600) with a 30-day expiry, with no IAM round-trip. - Interactive mode (
nno login) — being rolled out as an OAuth 2.0 device-authorization flow (RFC 8628) against the global IAM service. When live, the CLI requests a device code, displays a short user code plus the verification URLhttps://console.app.nno.app/activate, and polls IAM for completion. The resulting session token is stored in the same~/.nno/credentials.jsonfile.
Both paths land at the same credential schema, so nno whoami and downstream commands behave identically regardless of how the user authenticated.
Cross-stack session sharing
Because the cookie domain is .<platformId>.nno.app, a single login covers every stack and app on that platform. A user who logs into:
dashboard.app.default.k3m9p2xw7q.nno.appis automatically authenticated when they navigate to:
crm.app.x7y8z9w0q1.k3m9p2xw7q.nno.appNo re-login is needed across stacks.
Role model
Neutrino has two role scopes:
Platform-level (on the user record):
| Role | Access |
|---|---|
platform-admin | Full access: all tenants, platform settings, billing |
user | Standard access governed by tenant roles |
Tenant-level (on the membership record):
| Role | Access |
|---|---|
owner | Full tenant control — invite, remove, change roles |
admin | Manage members, cannot delete tenant or change billing |
member | Standard access — feature permissions apply |
A user can hold different roles in different tenants simultaneously.
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.
Service-to-service auth
Neutrino internal services authenticate to each other using short-lived JWTs issued by the IAM service (/api/nno/service-token). These tokens use HMAC-based validation and are cached by the Gateway's ServiceTokenCache to reduce latency on every proxied request.
Platform admins and features interact with Neutrino services using API keys issued by the IAM service (/api/nno/apikey). API keys are scoped to a platform ID and carry no user identity.
Auth template
The per-platform auth Worker is deployed from a template (services/auth) during platform onboarding. The CLI Service substitutes platform-specific values (platform ID, D1 IDs, auth secret) into the template and deploys it to the platform's GitHub repository. You can customise the auth configuration after initial provisioning.