Bootstrap and First Sign-In¶
Purpose¶
Initialize a fresh or compatible Organization, its active protected owner and Default Team. Bootstrap uses explicit offline operator authority and audits system/bootstrap; it does not impersonate a human session.
Prerequisites¶
- Install/build the workspace (
pnpm install --frozen-lockfileandpnpm buildfromplatform/). -
PostgreSQL 16 or newer, and an empty database owned by a dedicated login role, created by the Postgres superuser with the libc
Ccollation and character type so text sorts bytewise:create role mission_control login password 'replace-with-a-generated-password'; create database mission_control owner mission_control template template0 locale_provider libc lc_collate 'C' lc_ctype 'C';db:migrate, and bootstrap with it, refuses a database created otherwise before applying anything. That includesC.UTF-8, whose character type makeslower()fold non-ASCII letters; uniqueness and ordering rely onlower()folding ASCII letters only, as it did on SQLite. -
DATABASE_URLset to that database for bootstrap, gateway and Console. Stop other writers while bootstrap runs. - An installation that still keeps its data in SQLite files follows Migrate from SQLite instead; bootstrap does not read SQLite files.
- The owner email must receive the configured mail transport.
Procedure¶
Working directory: platform/.
export DATABASE_URL="postgres://mission_control:replace-with-a-generated-password@127.0.0.1:5432/mission_control"
export BOOTSTRAP_ADMIN_EMAIL="owner@example.test"
export BOOTSTRAP_ORGANIZATION_NAME="Acme"
export BOOTSTRAP_ORGANIZATION_SLUG="acme"
pnpm --filter @mission-control/console bootstrap
Bootstrap applies any missing migration (db:migrate) before it writes, so it works on a freshly created database. The gateway and the Console never migrate; start them only after bootstrap or pnpm --filter @mission-control/database db:migrate has run against the same DATABASE_URL.
The slug uses lowercase letters, digits and hyphens. Expected credential-free output:
{"userId":"user_example","organizationId":"org_example","organizationMemberId":"member_example","teamId":"team_example"}
Bootstrap creates canonical membership, the protected organization.owner preset assignment and Default Team. The Team starts with no resource allowance. It creates no Agent principal, Project, Everyone binding or reusable execution key. Repeating bootstrap reuses compatible active state and can explicitly recover protected ownership with an audit event. It refuses inactive/suspended/directory-disabled accounts and never silently reactivates membership.
Ordinary invitation acceptance initializes active canonical Organization membership and the Organization Member preset, with no automatic Team or inference access. Add Team membership/allowance and issue new virtual keys after sign-in.
Start and verify the compatible services¶
Prepare the first catalog as described below, then start the gateway with the split-listener settings from Configure the services. Verify private control liveness and the authenticated inference boundary:
curl --fail http://127.0.0.1:4001/healthz
curl --output /dev/null --write-out '%{http_code}\n' \
http://127.0.0.1:4000/v1/models
Expected results are {"status":"ok"} and HTTP 401. Health and readiness are private control routes; inference deliberately has neither.
Start the Agent runtime, Collector, and Console with their purpose-separated secrets and private control URL. After all three startup heartbeats arrive, verify readiness:
Expected response: {"status":"ready"}. A missing, stale, identity-conflicting, or mismatched Agent runtime, Collector, or Console heartbeat returns 503. These probes do not test model providers or MCP servers.
Request the first magic link¶
- Start the console with
APP_URL, mail settings,BETTER_AUTH_SECRET, and the sameBOOTSTRAP_ADMIN_EMAIL. - Open
/sign-inat the configured application URL. - Enter the bootstrap email.
- Open the link delivered by SMTP. In development, read it from console output or
MAIL_OUTBOX_PATH. - Use the link within five minutes.
The authentication token is stored hashed. The configured bootstrap email, existing accounts, and recipients of unexpired pending invitations may receive magic links.
The first sign-in uses a magic link even where GITHUB_CLIENT_ID and GITHUB_CLIENT_SECRET are configured. Bootstrap creates the owner with an unverified address, and the console refuses to attach a GitHub account to an address it has not verified itself. Continue with GitHub works for the bootstrap owner after this first link is used.
Load the first catalog¶
Before starting gateway and Console, replace the included example organization path with the bootstrapped organization's file. For acme, create organizations/acme.yaml, reference it from the global YAML, and set every required env/NAME in the gateway environment. Bootstrap must already have created acme; unknown organization mappings fail startup.
From platform/, optionally run:
Start gateway and Console with these reviewed files. Open Manage → Settings to inspect the current redacted catalog. No Validate or Apply action is required. Invalid YAML or missing required static credentials exits before serving traffic; correct the safe diagnostic and start again. MCP consent and upstream availability are independent of startup.
Continue with Organization YAML and Configuration catalogs and enablement.
Failure recovery¶
STORAGE_MIGRATION_REQUIRED: run pnpm --filter @mission-control/database db:migrate: the gateway, the Console orconnector:rotate-credentialsfound a migration its release bundles missing from the database. Run that command fromplatform/with the sameDATABASE_URL, then start the process again. Nothing migrates at startup.storage_collation_refused: the database uses the collation … and character type … (locale provider …): the database was not created with the libcCcollation and character type, and bootstrap stopped before migrating or writing anything. As the Postgres superuser, drop the empty database and create it with the statement the message prints, then run bootstrap again.- A message that
DATABASE_URLis required or must be apostgres://orpostgresql://URL: set the Postgres URL in the environment of that process and start it again. - A connection or authentication failure: check that the server is running, that the role can log in with the password in the URL, and that the URL names the right host, port and database.
Offline bootstrap may restore the protected owner's assignment to an active account and audits organization.owner.recover under system/bootstrap. It refuses suspended, deactivated, manually suspended or directory-disabled users and does not silently reactivate them. For inactive owner recovery, follow canonical membership/directory administration before an explicit audited owner recovery. Missing YAML mappings or secrets fail startup before listeners bind. Correct them and restart; no Apply action exists.