The first domain
A domain is the level of Airframe’s catalog under a journey and above its contexts and products, and every product belongs to one. Adding a domain starts with a few small catalog files, but it does not end there: a domain also needs a project factory and a GitLab root of its own, which are changes to the platform itself. This page lists every step, says which ones the platform derives on its own, and names who approves the rest.
The words journey, domain and context are explained in how the organization is divided. The example is the demo organization’s settle journey, its billing domain and the tax context inside it.
1. The catalog files
Section titled “1. The catalog files”All the files go in one merge request to the catalog repository.
| File | What it says |
|---|---|
catalog.yaml |
The journey added to journeys, if it is new, and the domain added to domains as a path, such as settle/billing |
journeys/settle.yaml |
The journey’s leads and a one-line description |
domains/settle.billing.yaml |
The domain’s leads, a description, and whether it has a restricted subtree for products that are not open to the rest of the organization |
contexts/settle.billing.tax.yaml |
The context’s leads and a description |
people/<person>.yaml |
Each lead, if they are not in the catalog yet |
teams/<team>.yaml |
A team that lives in the new context, so that it can manage products there |
A domain file looks like this:
domain: billingjourney: settleleads: [noa.brightwater]description: invoices, payments and the taxes on themrestricted_subtree: trueThe leads are people, named by their catalog slugs. Leads may merge on every product below their level, and the leads of a context and a domain approve their products’ entries, so choose people who will review.
The catalog’s lint refuses a file whose name and fields disagree, a level whose parent file does not exist, a lead who is not a person in the catalog or who has departed, and a journey or domain in catalog.yaml without its file (enforced). Names are short and lowercase: a journey has 3 to 10 letters, a domain 3 to 8 and a context 3 to 12 characters. A small domain still declares one context, named after the domain, such as billing/billing.
Who approves: two members of the platform team, because the catalog’s ownership model gives the hierarchy’s files to the platform with two approvals. Today the second approver is the maintainer’s second account: the mechanism is enforced, but the separation between two people is not yet real.
2. What the platform derives on its own
Section titled “2. What the platform derives on its own”When the merge request merges, the catalog’s pipeline starts the chain of reconcilers, which reads the new files:
- landing-zone’s
roots/folderscreates a folder for the journey, the domain and the context; - landing-zone’s
roots/groupscreates a Google group for the leads of each level, with its members; - landing-zone’s
roots/networkgives the domain two subnets in each network, one for its Cloud Run services’ outbound traffic and one for internal services.
The network root numbers each domain’s address block by the sorted order of the domain paths. A new path that sorts before an existing one, such as fulfil/returns before fulfil/shipping, would move that domain’s subnets and every later domain’s, so read the network root’s plan on this merge request before it applies. The plan also has room for eight domains: a ninth would take the block of the private services range that Cloud SQL uses, so the address plan has to change first.
3. The GitLab groups, by the administrator
Section titled “3. The GitLab groups, by the administrator”The groups for the journey, the domain and the context, and the domain’s factory account, the GitLab service account that creates the domain’s product repositories, live in control-plane’s roots/gitlab. That root needs GitLab’s Owner role, so it is applied only under the exceptional procedure: a maintainer applies it from a laptop with a one-day token of the GitLab administrator account, under a time-boxed grant, and revokes the token afterwards.
The same apply creates a secret named gitlab-factory-<journey>-<domain>-token. The secret’s value is added by a maintainer from the root’s output, under a grant, and never displayed. The apply leaves a row in landing-zone’s docs/bootstrap-log.md, with the grant’s identifier.
4. The project factory, in landing-zone
Section titled “4. The project factory, in landing-zone”One merge request in landing-zone, approved by the platform team:
- a new root,
roots/project-factory/settle-billing/, copied from an existing domain’s factory, as the demo organization’s second and third factories were copied from its first; - the factory’s pipeline job in
.gitlab-ci.yml; - the factory’s identity,
factory-settle-billing@, inroots/wif, with its roles on the domain’s folder, Billing Account User on the billing account, and its reads of other roots’ state, plus the grants that let the roots which read every factory’s state read the new one’s: the network, perimeters, GitLab, executor, trusted builder, telemetry and architecture catalog roots; - the new factory added to the lists in
roots/network,roots/perimetersandroots/state, and, for a new journey, the journey added to the network root’sjourney_zones, which gives it a DNS zone.
roots/wif is one of the three roots applied from a laptop: a maintainer applies it under the platform-apply grant and the organization grant, both approved by another member of the platform group, and records the grant identifiers in the log. The rest applies by pipeline.
5. The GitLab domain root, in control-plane
Section titled “5. The GitLab domain root, in control-plane”One merge request in control-plane, approved by the platform team:
- a new root,
roots/gitlab-config-settle-billing/, which creates the domain’s product repositories as the factory account and pushes their first commits; - its apply job and its render job in
.gitlab-ci.yml; - the new factory added to the executor root’s lists: its identity, so that it may publish each product’s Ready output, and its state, which the executor root reads;
- the new factory added to
domain_factory_prefixesinroots/trusted-builder, so that the domain’s products can read the image registries and verify the attestations.
6. Observability, in operations
Section titled “6. Observability, in operations”One merge request in operations, approved by the platform team: the new factory added to the lists of domain factories that roots/telemetry and roots/eventcatalog read.
7. For a new journey, in security-policies
Section titled “7. For a new journey, in security-policies”Only when the journey is new: one merge request in security-policies adds the journey group’s id to the scope of the policy that injects the product checks, in .gitlab/security-policies/policy.yml. Who approves: a maintainer who did not write it. Without it, the products of the new journey run their pipelines without the injected product jobs: the validation of their roots and their build and test evidence.
What this adds up to
Section titled “What this adds up to”| Step | How, who approves, and the record |
|---|---|
| The catalog files | A catalog merge request. Approved by: two of the platform team. Record: the merge request. |
| Folders, leads groups, subnets | Derived by the chain. Approved by: nobody further. Record: the pipelines. |
| GitLab groups and the factory account | The exceptional procedure. Approved by: another member of the platform group, for the grant. Record: docs/bootstrap-log.md. |
| The project factory and its identity | A landing-zone merge request, roots/wif from a laptop. Approved by: the platform team; the grants by another member. Record: docs/bootstrap-log.md for the laptop apply. |
| The GitLab domain root | A control-plane merge request. Approved by: the platform team. Record: the merge request. |
| Observability lists | An operations merge request. Approved by: the platform team. Record: the merge request. |
| A new journey’s product checks | A security-policies merge request. Approved by: a maintainer who did not write it. Record: the merge request. |
The demo organization’s second and third domains were added this way, and the platform’s catalog documentation counts a factory root and a GitLab domain root among what each domain derives. Today they are written by hand, by copying, in merge requests. Once they exist, the first product is a single catalog file.
Where it lives
In the catalog repository:
- docs/model.md: the hierarchy and the lint's rules
- docs/derivations.md: what each file drives
- catalog.yaml: the lists of journeys and domains
In the landing-zone repository:
In the control-plane repository:
