Onboarding
Follow these steps to create a new tenant from scratch and onboard into the CI platform.
Create a tenant
Pick your control plane API URL; we'll reference this in
$NA_CTRL_URL.
| Tenant | Product | Region | URL |
|---|---|---|---|
| Internal | SaaS | US | https://ctrl.dev.null.autos/api/v1 |
| Internal | SaaS | EU | https://ctrl.eu.null.autos/api/v1 |
Use the nullctl CLI to create the tenant.
nullctl auth api \
--base $NA_CTRL_URL
--json '{
"name": "test-eu",
"keycloak_realm": "test-eu"
}'
This returns details about the tenant as well as a temp admin password.
{
"tenant": {
"id": "e6e1c60f-8f28-47d3-a318-f3258b9e7672",
"name": "test-eu",
"description": null,
"keycloak_realm": "test-eu",
"keycloak_admin_user_id": "4ae62f2a-df7e-4bcd-88b5-6be0ca83cc74",
"created_at": "2026-01-22T16:04:05.282355Z",
"updated_at": "2026-01-22T16:04:05.282355Z",
"deleted_at": null
},
"temp_admin_password": "<random chars>"
}
TODO: tenant creation should also create a client in Keycloak, which needs to be included in the redirect flow. Client ID needs to be associated with the tenant in the ctrl database.
Setup OIDC Login
We build a link to the console which directs the user to login for the first time on their newly created realm with the admin user and temp password.
TODO: how are we going to share this?
https://app.eu.null.autos/onboarding/init#tenantId=$TENANT_ID&clientId=$CLIENT_ID&tempAdminPassword=$TEMP_ADMIN_PASSWORD
Setup VCS Provider
Once the user fills out the Keycloak admin account page, they're redirected back to the console to configure their VCS for the first time. The user has to fill out a form:
| Input Type | Name | Default | Description | Value(s) |
|---|---|---|---|---|
| Radio | vcs_type | gitlab_saas | the VCS type configured for the tenant | gitlab_saas, gitlab_selfhosted |
| TextInput | vcs_url | https://gitlab.com | the url for the VCS | disabled if vcs_type is gitlab_saas |
| TextInput | vcs_oauth_client_id | our client ID for gitlab.com | the client ID for the GitLab app | |
| Password | vcs_oauth_client_secret | our client secret for gitlab.com | the client secret for the GitLab app |
This creates the following JSON payload:
{
"provider_type": "gitlab",
"name": "gitlab",
"base_url": vcs_url,
"client_id": vcs_oauth_client_id,
"client_secret": vcs_oauth_client_secret,
"enabled": true,
"scopes": [
"api", "read_user", "create_runner", "manage_runner",
"read_repository", "openid", "profile", "email",
"write_repository"
]
}
When the user clicks submit, we post this JSON to the control plane API:
curl -s \
-X POST \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $NA_TOKEN" \
--data $JSON_PAYLOAD \
$NA_CTRL_URL/tenants/$TENANT_ID/vcs-providers
This returns an ID for the newly created VCS.
Authenticate Against VCS
Now that the user has configured their VCS provider, we authenticate them the first time. This will take them to an onboarding status page.
$NA_CTRL_URL/tenants/$TENANT_ID/link/$VCS_ID?redirect_uri=https://app.eu.null.autos/onboarding/status
Onboarding Wizard
We need to collect some additional information from the user before we start to onboard:
| Input Type | Name | Default | Description | Value(s) |
|---|---|---|---|---|
| AutocompleteTextInput | runner_group_id | User types in the name of a group we can see in their GitLab, we use that to find an ID and store it | ||
| AutocompleteTextInput | null_parent_group_id | User picks a group under which we create a null-autos group to hold projects we create |
The status page looks like a wizard with a series of timeline steps (really the entire onboarding flow should work this way).
Bootstrap
Once we come back to the status page, we'll hit this API to confirm setup:
$NA_CTRL_URL/tenants/$TENANT_ID/bootstrap/operations
This consists of several steps:
| Name | Description | Success |
|---|---|---|
| CreateRunners | Creates GitLab runners under runner_group_id | Runner Helm chart installs in our infra without issues |
| CreateNullAutosGroup | Creates GitLab subgroup under null_parent_group_id | New group exists and can be read via API |
| CreateNullAutosAgentsProject | Creates GitLab agent configs under new project in Null Autos subgroup | Agent configs are commited in main branch in project |
| InstallNullAutosAgentHelmChart | Installs agent helm charts in our K8s cluster for these agents | Helm chart installed successfully |
| ApproveWorkspacesAccessForAgent | We need to approve agent access for workspaces in the group | Agent is approved |
NOTE: we only need one agent config with access to both deploy and create workspaces.
TODO: we need to figure out the build + deploy flows for Android + Cuttlefish across ours and customer's infra.