The bootstrap
The bootstrap is the short list of things a person creates by hand to start Airframe, before any pipeline exists to create them. Each step makes something the next one needs, and each leaves a written record, so the start of the platform can be audited like everything after it. This page walks the fourteen steps in order and says why each one cannot wait for a reconciler.
The rule, and the record
Section titled “The rule, and the record”The first time something is created by hand is the last: a later step puts it under management, usually by importing it into a Terraform root. The runbook is landing-zone’s docs/bootstrap.md. Its record is docs/bootstrap-log.md, one row per step: the date, the account used, what was created (identifiers, never secret values), where any secret is kept (by its name in Secret Manager), and the commit that put it under management. Where the runbook and the log disagree, the log is right and the runbook is amended.
The log also records where practice departed from the plan. The runbook asks for security keys as the only second factor on the administrative accounts; the log’s first rows say two-step verification was enforced for everyone and the security-key-only setting deferred. Airframe’s own bootstrap ran on 2026-09-04 and 2026-09-05.
Two administrative accounts do the work: a Workspace account that holds Organization Administrator, and the GitLab account that owns the top-level group. Both are used only here and in exceptional procedures, and are otherwise signed out.
In consoles, before any code runs
Section titled “In consoles, before any code runs”- Preconditions. The organization, the billing account, the GitLab group, the Grafana Cloud stack and the domain already exist, as the prerequisites list. They are what the code runs in, so no code can make them. Their identifiers are the log’s first row.
- Administrative accounts and groups, in the Workspace admin console. Creates the administrative accounts, the platform group and the break-glass group. A reconciler needs an identity to run as, and these accounts are where identity starts. The groups are imported later by landing-zone’s groups root.
- Roles for the bootstrap window, from the command line. The administrator account receives organization-level roles that expire after fourteen days. The first applies need roles no pipeline identity holds yet. The runbook’s list missed four roles, found by failing applies, and landing-zone ADR-0002 records the complete set.
- Billing quota and budget, in the Cloud console. A request to raise the billing account’s project quota, and a budget on the account. Google takes the request through a form, and Airframe asked on the first day. The budget is imported into the folders root at step 7.
- The first project and the state bucket, with the command line. One runtime project and one bucket for Terraform state. Terraform needs somewhere to keep state before its first root can run. The folders root takes over the project at step 7, and the state root the bucket at step 8, adding its encryption key.
- The GitLab owner and a bootstrap token. The group owner’s account and a personal access token that expires in fourteen days. GitLab.com creates no administrator for you, and the GitLab root’s first apply needs a credential. The token is revoked at step 14.
From a laptop, inside the window
Section titled “From a laptop, inside the window”These steps are Terraform applies from a maintainer’s laptop, because no runner exists yet. Their state lives in the platform’s state bucket, and the log records the commit each one applied.
- The GitLab root, control-plane’s
roots/gitlab, with the bootstrap token: the groups, the platform repositories and their protections, the custom roles, the service accounts and the security policies, written but not yet linked to the group. - Folders, policies and the platform’s projects, landing-zone’s
roots/folders,roots/organisationand the platform’s project factory: the folders, the organization policies and deny policies, and the platform’s own projects. - State and identity,
roots/stateandroots/wif: the state buckets with their encryption keys, and the workload identity federation provider with the bindings of the platform roots and the executor. - The network and the runners, the network root’s first version, then the trusted runner. Registering a runner in GitLab gives an authentication token, which a person places in Secret Manager: that is the second token by hand, and the untrusted runner’s is the third. The step is done when a pipeline on control-plane’s main runs a job on the trusted runner and gets a short-lived credential.
From then on, roots are applied by pipeline. Three stay applied from a laptop, under a time-boxed grant that another member of the platform group approves: organisation, audit and wif. They need organization-level or IAM administration roles that no pipeline identity holds, and wif, which grants every other identity its roles, would make its pipeline the most powerful on the platform (landing-zone ADR-0002 and ADR-0008).
By pipeline, and the last hands
Section titled “By pipeline, and the last hands”- Every other root, each through a normal merge request: groups, the full network, the perimeters in dry-run mode, the grants, the catalog’s first entries, operations’ roots, the executor and the gate. The audit root is the exception: it was applied from a laptop inside the window, like the other two above. One role is assigned by hand in the Workspace admin console: Groups Administrator for the group factory’s identity, which the factory needs before it can run at all. The log records it.
- Grafana Cloud tokens, in Grafana’s portal. The access policies and their tokens, and the single sign-on client. The Free stack is made by hand, an access policy’s scopes change only in the portal, and no secret value may enter Terraform state: the roots create each secret by name, and a maintainer adds the value.
- Grants, and the window closed. The
pamroot applies by pipeline and creates the Privileged Access Manager entitlements. One grant is requested, approved by a second person, used and left to expire, as a rehearsal. Then every window role is removed by hand from the administrator account, which keeps Organization Administrator alone. Airframe closed its window on 2026-09-05, thirteen days early. - Name servers at the registrar. The network root has already created the public DNS zone; a person sets the domain’s name servers and the signing record at the registrar, which is outside Google Cloud, then checks that mail still arrives. Once the gate answers at its hostname, the GitLab administrator account links the security policies to the group from a laptop, by a second apply of the GitLab root: the group-wide pipeline policy injects
gate:integrityinto every pipeline, and the job fails closed until the gate serves, so linking earlier would stop the pipeline that deploys the gate. - The step-down. The bootstrap token is revoked, the GitLab administrator account is the group’s only Owner, no daily account holds Maintainer or Owner anywhere, and the daily check of who holds which organization role runs for the first time. The scheduled pipelines are created by hand by a maintainer allowed to merge, because a schedule runs with its owner’s permissions. The administrative accounts are signed out.
After the bootstrap
Section titled “After the bootstrap”The log does not close. Every later step by hand adds a row, such as a laptop apply of one of the three roots under a grant, or a change to the GitLab root under the exceptional procedure: a one-day token of the GitLab administrator account, used from a laptop and revoked afterwards. The runbook also lists what is never done by hand, such as product projects, repositories, groups and DNS records: needing to do one of those by hand is filed as a bug in a reconciler.
Running the bootstrap again is designed: the runbook says steps 1 to 5 can be checked by inspection, the later applies converge, and the log names what a rerun must import. A full rebuild in a second, empty test organization is planned in the runbook, and no record of one exists yet.
Where it lives
In the landing-zone repository:
