Operator setup
Create the first operator account and the first client (organization) on your
server. Verified 2026-09-20 on the development workstation. This is local
experimental onboarding. On a server, follow the quickstart
or Install with Docker (deploy/compose.sh bootstrap) instead; the
runbook is in SERVER-DEPLOYMENT. The installer is
lab/install_server.py (check, plan, install, images, smoke,
supervise, wait-console).
Current compatibility gate
Fresh installations and retained installations whose source and current recovery
target carry a matching HBA generation pin are supported. Retained legacy
databases without that pin require explicit adoption
(lab/adopt-retained.py source|target), and their startup refuses before
credential writes or service startup. Both retained databases in this checkout
are adopted as of 2026-09-20. Do not recreate their containers or remove state to
bypass the gate. See source HBA integration and
recovery-target writers.
Run
- Start the owned upstream runtime:
/usr/bin/python3 lab/durable_runtime.py up. - In your terminal run
/usr/bin/python3 lab/bootstrap.py. Enter an email, organization name and a password of at least 12 characters. Password entry uses no echo and asks for confirmation. Do not put credentials in arguments. - Run
bun lab/upstream-server.tsto start the loopback API. The management Supabase SDK endpoint is its printed base URL plus/management; the public routing key issb_publishable_sbarbase_local_management. Open the printed base URL for the login UI. Runbun run build:uibefore starting the server. - After login,
GET /management/v1/organizationslists the authenticated user's current memberships, including organization ID, name and role. Existing project/environment APIs then operate within that organization.
For automation, bootstrap.py --stdin reads at most 8192 bytes of JSON containing
exactly email, password and organization. Supply it through a private stdin
channel, not an inline shell command containing credentials. The command outputs
only completion state and the resulting identity/organization IDs.
The setup command is for the trusted host operator who already has access to installation secrets. It is not an HTTP endpoint. Auth public signup remains disabled, and POST to organization discovery is not a bootstrap operation. Creating the initial organization owner does not confer cross-organization access or expose the private Auth administrator API.
Interruption and retries
A private, password-free intent journal is atomically written and fsynced before
creating an Auth user. It records a random operation ID, email and organization,
then the confirmed identity ID. An admin-controlled app_metadata operation
marker locates the same Auth user if setup stopped before its ID was journaled.
User-editable metadata is never used to claim this identity.
Retry with the original email, organization and password. The existing password is verified against management Auth; it is never silently reset. Setup uses a single catalog transaction for organization, owner membership, bootstrap record and audit entry. Repeating a completed operation returns the same IDs. It cannot create duplicate organizations or restore ownership that was later revoked.
The wrapper holds an OS file lock throughout the child operation. Concurrent setup attempts fail rather than race. The catalog also enforces a single initial setup record. If the journal is missing after initialization, setup refuses a new operation. If a recorded Auth identity disappears, setup refuses replacement. Manual repair for those cases is a separate operator recovery procedure, pending.
No Auth password or JWT is stored in this journal. Its file mode is 0600 under
ignored .secrets/upstream. The Auth service stores the password using its own
normal password handling. The setup command does not email an invitation; its
private admin creation explicitly confirms the operator-supplied email.
Evidence and limits
18 live checks verify real management Auth user creation, interruption after Auth creation, interruption after catalog commit, wrong-password refusal, identity/organization reuse, journal privacy and SDK login. They also prove organization discovery, first project creation, denial of unauthenticated discovery and denial of access to an unrelated organization.
The live probe uses the same bootstrap core, Auth adapter and file journal as the CLI, with an isolated temporary catalog. The wrapper itself was checked for malformed input, weak/invalid input, oversized input and concurrent-lock refusal. Interactive password entry was not automated. No actual owner account was retained or delivered; the probe removed its generated identity and private files.
Unit tests cover four durable interruption boundaries, password verification before authority, revoked ownership and membership-scoped discovery. Current suite totals are recorded in status; they do not establish production readiness.
Since this page was first written, these have shipped: invitations, sign-in factors (MFA) and login rate limits for the console, HTTPS through the reference TLS proxy (server deployment), encrypted off-site backups (backup and restore) and a password reset from the server's shell (below). Still missing: a supported repair for the two refusals above (a missing journal, a vanished identity), guided first-run onboarding in the console, pagination for very large organization lists and a complete distributable installer. Bootstrap identity lookup paginates the private Auth API and stops with an error after 10,000 users rather than silently assuming no match. Store the original password securely yourself.
A forgotten password
The console has no "forgot password" link: the management sign-in sends no mail by itself. Whoever has a shell on the server sets a new one:
sudo -u sbarbase /opt/sbarbase/deploy/sbarbase reset-password you@example.comIt asks for the new password twice without echo (at least 12 characters), replaces it through the management sign-in service, and ends every console and Studio session the account had, so a session opened with the old password stops at once. An authenticator the account uses stays as it is. The change is in the audit log as management.password_reset. It reads the management realm's secret from .secrets/upstream/runtime.json, which is why it runs only on the server, as the service account.
Sources: Supabase admin createUser, admin listUsers, and the original pinned Auth service exercised locally.