Skip to content

Claims and mechanisms

This page lists every claim Airframe makes about its security, cost and records, with the mechanism behind each one and an honest status. A claim is only as strong as what enforces it, so each row says whether something refuses a violation today, whether the claim rests on people following a rule, or whether it is designed and not built yet. The trust boundaries page explains how the pieces fit together.

Each row has a claim, its mechanism, a status and a source: the repository and the files where the mechanism lives. The status is one of three words:

  • Enforced: a mechanism refuses the violation. For a claim that the platform provides something, such as a dashboard, it means the platform’s own code provides it every time, without anyone choosing to.
  • Convention: written down and taught, but nothing refuses a change that ignores it.
  • Designed: specified in the platform’s design or records, and not built.

The examples come from a demo organization, whose projects sit under one folder in Google Cloud. Today one person runs the platform. Wherever a row names a second person, such as an approver, that person is the maintainer’s second account: the mechanism is enforced, but the separation between two people is not yet real.

Every claim of the “why” pages appears here with the same mechanism, and so does every gap the platform’s own reviews and records list.

No person's everyday account holds a standing role that can change production infrastructure.

Mechanism
The folders root and the project factories grant people viewer and log viewer roles, and the one write a team holds is adding a version of its production secrets, never reading one. Administrative roles reach people only through Privileged Access Manager grants. A daily Policy Analyzer job fails, and alerts, if that one administrator account holds any role but Organization Administrator at the organization node, or any other person or group holds a standing role there.
Status
enforced
Source
landing-zone roots/folders/main.tf, roots/project-factory/, .gitlab-ci.yml (audit:policy-analyzer)

Only a job of the product's own repository, on a protected branch or tag, in the right environment, can obtain its apply identity.

Mechanism
The project factory binds each apply identity to one value that combines the repository's path, the environment and whether the ref is protected, with the protected value only. The identity provider refuses any token from outside the projectairframe group in GitLab.
Status
enforced
Source
landing-zone roots/wif/main.tf, roots/project-factory/

Nothing that runs before a merge can change production.

Mechanism
The executor may use every product's plan identity, which reads and changes nothing, and every non-production apply identity, and no production apply identity. A merge request's own pipeline gets no identity at all.
Status
enforced
Source
landing-zone roots/project-factory/ (grants to the executor)

No service account key can be created or uploaded under the demo organization's folder.

Mechanism
Organization policies at the folder disable key creation and upload for every principal, and the deny policy blocks key creation for everyone but the break-glass group, the bootstrap administrator account and the Privileged Access Manager service agent. The policy pack also refuses a key declared in Terraform.
Status
enforced
Source
landing-zone roots/organisation/policies.tf, deny.tf; control-plane policy/airframe/iam.rego

A maintainer's administrative access is temporary, and another member of the platform group approves it.

Mechanism
The Privileged Access Manager entitlements that grant administrative roles last at most two hours and need the approval of another member of the platform group; a requester never approves their own request. Today that member is the maintainer's second account.
Status
enforced
Source
landing-zone roots/pam/main.tf

A billed product's non-production project has a level 1 grant, to read its logs and state for an hour.

Mechanism
The one level 1 entitlement, self-approved for an hour, is on the platform's non-production project, lk-runtime-np. No project factory creates one for a product yet.
Status
designed
Source
landing-zone roots/pam/variables.tf, docs/guides/identity.md

Google Cloud itself refuses the owner and editor roles anywhere below the folder, even outside a pipeline.

Mechanism
A custom organization policy constraint, enforced at the folder since 2026-10-01, refuses a grant of either role from any path, the console included. It allows two cases: a project still in its factory's bootstrap window, and Google's own APIs service agent, which Google grants editor when a project's APIs are turned on. The project factories also remove the owner role Google gives them on each project they create. The policy pack refuses both roles in every Terraform change as well (Policy, below).
Status
enforced
Source
landing-zone roots/organisation/constraints.tf, terraform.tfvars, docs/adr/0026-the-floor-enforced-and-the-audit-record-locked.md

The deny policy also protects the firewall policy and Binary Authorization.

Mechanism
Architecture 9.3 lists both among the deny policy's permissions. The deny policy as built covers key creation, organization policy changes, folder IAM changes and the folder's log sinks, and neither of the two.
Status
designed
Source
landing-zone docs/architecture.md 9.3, roots/organisation/deny.tf

Every product hostname is behind single sign-on.

Mechanism
Identity-Aware Proxy is on for every user-facing service, for the product's users group: the golden path's Cloud Run module turns it on for every product backend, and the policy pack refuses open ingress, or a product backend, without it. Cloud Run's invoker check stays on, with IAP's service agent as an edge service's only invoker, so a call around IAP is refused. The gate and this website are the platform's public exceptions. A route an entry declares public, with external ingress, is designed: nothing builds one yet.
Status
enforced
Source
control-plane policy/airframe/cloud_run.rego; golden-path modules/cloud-run-service/main.tf; landing-zone docs/contracts.md 8

Applications check the signed header IAP sends, and a service's default address is off.

Mechanism
The review of 2026-09-25 recorded both as the next layers after the invoker check. Today the invoker check alone keeps a caller from going around IAP: the golden path's Cloud Run module keeps it on (invoker_iam_disabled = false), and the policy pack only warns when an edge service turns it off.
Status
designed
Source
landing-zone docs/reviews/2026-09-25-implementation-review.md (F06); control-plane policy/airframe/cloud_run.rego

Each preview has its own hostname, behind single sign-on, and no other way in.

Mechanism
The domain's wildcard certificate and record cover every preview hostname, so nothing is issued per preview. The golden path's preview-substrate module gives each product one router on its non-production project, behind IAP for the product's users group; the router reads the merge request's number from the hostname, and each preview service names the router as its only invoker.
Status
enforced
Source
golden-path modules/preview-substrate/

A removed name is never reused.

Mechanism
The catalog keeps a tombstone for each retired name, and its lint refuses any other product with that name in the domain. An identity is bound to the repository's path, so a new repository cannot inherit an old one's access.
Status
enforced
Source
catalog scripts/lint.mjs, retired-names/

A merge request's pipeline holds no cloud credential.

Mechanism
It runs on the untrusted runner, outside the perimeters, in a network with no route to the platform's ranges; its jobs cannot reach the metadata server, and no identity binding accepts an unprotected branch.
Status
enforced
Source
control-plane roots/runner-untrusted/, docs/adr/0007-an-untrusted-runner-of-our-own.md; landing-zone roots/wif/main.tf

Only the current owners can approve.

Mechanism
The gate reads the ownership model from the catalog's protected main, never from the change it judges, counts no approval by the author or a committer, and binds its own approval to the commit it checked; an approval policy makes that approval a condition of merging.
Status
enforced
Source
control-plane gate/src/core/ownership.ts; security-policies .gitlab/security-policies/policy.yml

Only a named person's approval lets a change merge, never its author's.

Mechanism
The approval policy lists its approvers by name, the catalog's active people with a GitLab account, and excludes the author and the committers. A coding agent with its own account is on no list; one that acts through a person's account or token is that person to GitLab, and nothing tells the two apart. The gate's service account is the one approver of a separate policy and cannot give the person's approval. Today every named approver is one of the maintainer's own accounts.
Status
enforced
Source
security-policies .gitlab/security-policies/policy.yml

Every catalog file is checked before it merges.

Mechanism
control-plane's validate component runs the catalog's lint, validate:catalog, on every merge request and on main: each file against its JSON Schema, each level's parent present, every lead and team member a person who has not departed, and each team's lead one of its members with the team lead role. Each finding names its file. The catalog project lets a merge request merge only when its pipeline has succeeded, so a finding stops the merge; on main, a failed lint stops the chain of reconcilers before it starts.
Status
enforced
Source
control-plane templates/validate/template.yml (validate:catalog), roots/gitlab/projects.tf; catalog scripts/lint.mjs, schema/, .gitlab-ci.yml

Only the current owners can approve a change to the catalog.

Mechanism
The gate reads the ownership rules, the teams and the people from the catalog's protected main, never from the change it judges, and counts only active people with a GitLab account, never the author or the authors of its commits. The required ownership gate policy in security-policies makes the gate's approval a condition of merging in the catalog, as in every other repository the gate guards.
Status
enforced
Source
control-plane gate/src/core/ownership.ts, gate/src/adapters/gitlab-ownership.ts, gate/infra/terraform.tfvars (ownership_enforced); security-policies .gitlab/security-policies/policy.yml; catalog ownership/protected-paths.yaml

A catalog merge reaches the readers with no one running a command.

Mechanism
The catalog's pipeline on its protected main triggers landing-zone's, operations' and control-plane's pipelines in turn, then landing-zone's again, each waiting for the one before; a failure stops the ones after it. In them the tf-apply component runs only the roots that declare reads_catalog, against the catalog's main, each with its own pipeline identity.
Status
enforced
Source
catalog .gitlab-ci.yml (reconcile:landing-zone, reconcile:operations, reconcile:control-plane, reconcile:landing-zone-last); control-plane templates/tf-apply/template.yml (reads_catalog, catalog_ref)

Every reader derives memberships and approvals from active people alone.

Mechanism
The groups root syncs memberships for active people alone; the GitLab roots give memberships and merge rights only to active people with a GitLab username, and admit them to the platform repositories and security-policies through one group, projectairframe/contributors, so a departure takes a person out of all of them on the chain's next run; the Grafana root adds only active people with a Grafana username; the gate counts only active people. The lint refuses a departed person in any team or lead list, so the merge request that marks someone departed also takes them out of every team and lead list.
Status
enforced
Source
landing-zone roots/groups/main.tf; control-plane roots/gitlab-config/locals.tf, roots/gitlab-config/memberships.tf, modules/product-repositories/main.tf, gate/src/core/ownership.ts; operations roots/grafana/members.tf; catalog scripts/lint.mjs

The approval policy's named approvers follow the catalog.

Mechanism
The policy:approvers job compares the list with the catalog's active people with a GitLab account. In security-policies it runs on every merge request and on main, and a failure shows on the merge request. In control-plane it runs after every catalog merge, where a person to add or remove only warns, so the catalog's chain goes on, and every day, where it fails and policy:approvers:report keeps one issue with the exact change to make. A role, a group or an id among the approvers, or any approver but the gate's account on the gate's own rule, fails every run. Until the list follows, a person not yet on it cannot approve, and a departed person still on it reaches no platform repository, since people reach those through the projectairframe/contributors group alone, whose members come from the catalog. What holds the list is a named person's approval of each change in security-policies, never its author's.
Status
convention
Source
security-policies .gitlab-ci.yml (policy:approvers), scripts/approvers.py; control-plane cli/approvers_lag.py, .gitlab-ci.yml (policy:approvers, policy:approvers:report), roots/gitlab-config/memberships.tf, roots/gitlab/modules/ultimate/policies.tf

The gate's approval is required in every repository but one.

Mechanism
The required ownership gate policy covers the repositories the gate enforces, today every one but security-policies, which holds the approval rules. A new repository joins by two merge requests, one in control-plane and one in security-policies; until then it needs only a named person's approval.
Status
enforced
Source
security-policies .gitlab/security-policies/policy.yml; control-plane gate/

A change can still merge when the gate is running but cannot approve it.

Mechanism
A merge request in security-policies, approved by a named person who did not write it, takes the blocked repository out of the gate policy's scope, and a second one puts it back. This was rehearsed: the rule left a product repository 11 seconds after the first merge and returned 35 seconds after the reversal.
Status
enforced
Source
control-plane docs/security-policies.md; security-policies .gitlab/security-policies/policy.yml

A change to a platform repository can still merge when the gate cannot be reached.

Mechanism
security-policies is outside the group-wide pipeline execution policy, so its own pipelines carry no gate:integrity and its merge requests do not wait on the gate. One merge request there, approved by a named person who did not write it, takes one repository out of the required ownership gate policy's scope and adds it to that execution policy's exclusions; that repository then merges on the non-author approval alone, and a reverse merge request puts both back. In the window the executor plans, builds and promotes nothing for it. The rehearsal of 2026-10-02 took a platform repository out and back with the gate up: the rule left in 10 seconds and returned 16 seconds after the reverse merge. It then made the gate unreachable for two minutes: a pipeline in another repository failed closed 10 seconds after it started, and security-policies' pipeline passed. A product repository cannot merge this way, because its merge also waits for the Airframe cost status check, which only the gate sets; whether an urgent product change waits for the gate or gets a way through the check is not decided. What an outage loses: GitLab does not send again an audit event the gate could not receive. On 2026-10-02, 34 of the 35 pipelines that failed in the group between 09:00 and 15:30 UTC have their failure event in the gate's log; the one without failed during the outage. Since that evening a second streaming destination sends every event to the audit receiver, a separate Cloud Run service running the gate's image with only the audit route, so the locked audit bucket loses an event only when both are unreachable; the detections read the gate's copy alone, so a gate outage still hides its events from them.
Status
enforced
Source
security-policies .gitlab/security-policies/policy.yml; control-plane docs/adr/0032-a-gate-that-cannot-be-reached.md (owed 5 and 7), docs/security-policies.md; landing-zone docs/rehearsals/2026-10-02-the-gate-unreachable.md; control-plane gate/infra/audit-receiver.tf, docs/adr/0021-gitlab-ultimate-deployments-vulnerabilities-compliance-and-audit-events.md

A gate outage is handled by a written procedure, the gate's own recovery first.

Mechanism
control-plane's ADR-0032 sets two stages. First the gate is recovered in Google Cloud, because it cannot redeploy itself through its own pipeline while it is down: under a level 2 Privileged Access Manager grant that another member of the platform group approves, Cloud Run's traffic goes back to the last revision that served, about 22 seconds each way in the rehearsal. Only when that has not restored the gate within 30 minutes, and a change cannot wait, does one repository leave both of the gate's controls, for at most two hours, as above. The approvals are enforced: Privileged Access Manager refuses a requester's own approval, and the approval policy an author's. The order, the 30 minutes and the issue that keeps the timeline are written down and rehearsed, and nothing refuses a break-glass merge request that skips them.
Status
convention
Source
control-plane docs/adr/0032-a-gate-that-cannot-be-reached.md; landing-zone roots/pam/main.tf, docs/rehearsals/2026-10-02-the-gate-unreachable.md

A break-glass window for one repository ends after two hours.

Mechanism
Nothing closes it: ending the window is a person's reverse merge request. What checks the bound is control-plane's scheduled pipeline, which runs once a day, so an overrun shows at the next day's run. breakglass:check compares security-policies' policy file with the steady state, warns while a difference is under two hours old and fails after, naming the repositories, and breakglass:report keeps one issue labeled audit::misconfiguration open until the policies match again.
Status
convention
Source
control-plane cli/breakglass_bound.py, .gitlab-ci.yml (breakglass:check, breakglass:report), docs/adr/0032-a-gate-that-cannot-be-reached.md

No project, group or pipeline variable can point the gate's integrity job elsewhere or empty it.

Mechanism
The job runs wherever the group-wide policy injects it: every repository but security-policies and one taken out for a break-glass window. It defines the gate's address itself, and an empty address fails closed like an unreachable gate. Both pipeline execution policies disallow variable overrides, so no project, group, instance or pipeline variable reaches the jobs they inject. Until 2026-10-02 the job read its address from a group variable and recorded a pass when it was empty; that fallback is gone.
Status
enforced
Source
security-policies templates/airframe.yml, .gitlab/security-policies/policy.yml; control-plane docs/adr/0032-a-gate-that-cannot-be-reached.md

A product is created without anyone running a command.

Mechanism
The catalog's pipeline starts the chain of Terraform roots after the merge, and each root applies with a pipeline identity that only a job of its own repository, on a protected ref in its environment, can obtain.
Status
enforced
Source
catalog .gitlab-ci.yml; landing-zone roots/wif/

Only an approved change gets a preview, and never with production's identity.

Mechanism
The gate starts the executor's preview run only when it approves a product's merge request, and not for a retiring product; the run applies the product's infra/eph root with the non-production apply identity, after the policy pack has checked the plan. Someone who can only open merge requests cannot run up the bill.
Status
enforced
Source
control-plane executor/scripts/preview.sh, docs/adr/0016-the-preview-lifecycle-through-the-executor.md

The merge request shows how far along the product is.

Mechanism
After the merge, the catalog's progress job follows the chain, and the gate keeps one note on the merged merge request until the product answers at its hostname, a job fails, or three hours have passed.
Status
enforced
Source
control-plane templates/progress/, gate/src/adapters/gitlab-progress.ts

Retiring a product takes two approvals besides the author's.

Mechanism
The gate's lifecycle rule asks for two distinct approvers, neither of them the author: one from the product's team and one from the domain's leads or the platform team. The catalog's lifecycle job requires the lifecycle label and the states in order. Today both can be the maintainer's own accounts.
Status
enforced
Source
control-plane gate/src/core/ownership.ts; catalog scripts/lifecycle-check.mjs

A product's Terraform cannot read the pipeline's credentials.

Mechanism
Before any plan, the executor refuses in a product root every template function and every file read that is not a file the commit carries or a pinned module's, and it runs the product's Terraform as a separate user with a scrubbed environment and one token. That second layer is tested in the pipeline and not yet exercised by a live product plan.
Status
enforced
Source
control-plane executor/scripts/security.py, executor/scripts/worker.py, docs/adr/0028-a-products-terraform-runs-as-a-worker-with-one-token.md

A product cannot claim another product's hostname, backend or identity on the shared network.

Mechanism
The network root takes each publication's owner and edition from the object's name, checks every claim in it against them, and rejects an invalid or contested publication on its own: a route's hostname and backend, and a reach's or an egress object's identity, must be the owner's. Keeping the last valid configuration is owed.
Status
enforced
Source
landing-zone docs/adr/0024-the-edge-publications-are-checked-against-their-owner.md, docs/reviews/2026-09-26-implementation-review.md (R02)

A reach's address belongs to its owner's own service.

Mechanism
The network root checks only that a reach's address lies in the edition network's private service access range, not that it is the owner's own database or cache. A product can therefore open its own identity's path to another product's database or cache address there, behind that service's own authentication. A check of the address's owner is owed.
Status
designed
Source
landing-zone docs/adr/0024-the-edge-publications-are-checked-against-their-owner.md, docs/reviews/2026-09-26-implementation-review.md (R02)

An approval is a fresh sign-in, not a session.

Mechanism
The design requires re-authentication at each approval. It is turned off, a recorded departure from the design: until it is on, an approval given through a person's session or token counts as that person's.
Status
designed
Source
landing-zone docs/adaptations.md (row 32); security-policies .gitlab/security-policies/policy.yml

A criticality 1 product is released through verification, a soak and a canary.

Mechanism
Criticality 2 and 3 follow their designed order. Criticality 1 takes criticality 2's order, because the deployment component for the later stages is not built; the demo organization has no criticality 1 product.
Status
designed
Source
landing-zone docs/adaptations.md (row 48)

A product spread over several repositories, each deploying its own part.

Mechanism
The catalog accepts an entry's repositories and the contracts name their state, identities and hostnames, but nothing reads them yet, and the design decision is still a proposal.
Status
designed
Source
landing-zone docs/adr/0017-multi-repository-products-and-adoption-by-moving-the-group.md

The policy pack's tests run on every change to it.

Mechanism
control-plane's policy:test job runs Conftest's unit tests and the pass and fail fixtures on every merge request that changes the pack, and GitLab merges only a merge request whose pipeline succeeds.
Status
enforced
Source
control-plane .gitlab-ci.yml (policy:test), policy/airframe/

Every rule comes with a case it must accept and one it must refuse.

Mechanism
The working conventions and the pack's documentation ask for both. Review holds it; no job checks that a new rule brings its tests.
Status
convention
Source
control-plane docs/policy-pack.md

A product repository cannot skip validation.

Mechanism
The product pipeline execution policy injects the validate and build-test components into every pipeline of the repositories in the journey groups, and GitLab lets no project's own file remove or replace an injected job.
Status
enforced
Source
security-policies .gitlab/security-policies/policy.yml, templates/product.yml

A product's pipeline file includes only the platform's components, and none of its own jobs deploys.

Mechanism
The pack's pipeline rules deny an include that is not one of control-plane's eight named components, a job that declares an environment and a job that triggers another pipeline; the injected validation runs them on the product's file.
Status
enforced
Source
control-plane policy/airframe/pipeline.rego, docs/adr/0026-a-product-job-never-deploys.md

A rule that denies stops the change.

Mechanism
A denial fails the injected validation job, and GitLab merges a product's merge request only when its pipeline succeeds. On main, policy:<root> runs the pack on the plan, and the apply needs it to pass. The cost rules run in the executor instead and decide the Airframe cost status check.
Status
enforced
Source
control-plane templates/validate/template.yml, templates/tf-apply/template.yml

A warning needs the platform's approval and a row in the exception register.

Mechanism
Today a warning appears in the job's log and holds nothing back: Conftest runs without failing on warnings, although the pack's documentation and some messages say approval is required.
Status
designed
Source
control-plane docs/policy-pack.md, templates/validate/template.yml

A new deny rule spends one release as a warning, then denies.

Mechanism
The working conventions ask for it, and nothing turns a warning into a denial. Several rules marked as warnings for one release, such as the sidecar database rules, the edge invoker check and a product file's required includes, are still warnings at release 2026.10.01-1.
Status
convention
Source
control-plane policy/airframe/cloud_run.rego, pipeline.rego, docs/policy-pack.md

Raw resources are allowed, with a warning.

Mechanism
The pack warns on every managed resource a product root declares outside a module, asking for a reason in the merge request, and a warning fails nothing. Its deny rules apply to every resource, raw or not, and the types the platform owns, such as networks, projects and perimeters, are denied in a product root.
Status
enforced
Source
control-plane policy/airframe/naming.rego, platform_owned.rego

No Terraform change grants the owner or editor role, or anything to the public.

Mechanism
The pack's IAM rules deny owner and editor in any shape, and any grant to allUsers or allAuthenticatedUsers, on the code and on the plan.
Status
enforced
Source
control-plane policy/airframe/iam.rego

Every labeled resource carries the platform's full set of labels.

Mechanism
The label rule denies a resource whose labels miss any of the six the contracts name, on the code and on the plan.
Status
enforced
Source
control-plane policy/airframe/naming.rego; landing-zone docs/contracts.md 7

Every resource carries the platform's labels, even one that declares none.

Mechanism
The contracts ask for labels on every project and resource. The label rule checks the labels a resource declares, so a resource with no labels attribute at all passes it.
Status
designed
Source
landing-zone docs/contracts.md 7; control-plane policy/airframe/naming.rego

The laptop runs the merge request's validation.

Mechanism
airframe validate runs the validate component's formatting, terraform validate and policy pack commands with no backend and no credentials; OpenTofu and tflint run only where they are installed. Running it is a convention, and the merge request runs the checks anyway. It reads the pack from the control-plane clone it is installed from, not from the product's release, and it does not run the catalog lint, the gate, the application's tests, the plan or its price; where Infracost and its key are present, it shows an estimate from the code.
Status
convention
Source
control-plane cli/airframe-validate.sh

airframe validate runs the module tests, the API specification lint and the API compatibility check.

Mechanism
The workspace's working conventions list all three; the script runs none of them.
Status
designed
Source
control-plane cli/airframe-validate.sh

Your own Dockerfile builds on a platform base image.

Mechanism
The pack's Dockerfile rules deny a FROM or a COPY --from outside the platform's base images, one not pinned and one computed from a build argument, in the Dockerfiles .airframe/images.yaml lists. From release 2026.10.01-1 the injected validation runs them on every merge request that changes a listed Dockerfile named Dockerfile, Dockerfile.*, *.Dockerfile or *.dockerfile, and airframe validate runs them locally. The trusted builder builds and attests exactly that list.
Status
enforced
Source
control-plane policy/airframe/dockerfile.rego, templates/validate/template.yml

A merge request that changes only a listed Dockerfile is judged, whatever its name.

Mechanism
A Dockerfile listed under another name, such as Containerfile, is judged only when the merge request also changes another file the validation watches.
Status
designed
Source
control-plane templates/validate/template.yml, docs/adr/0014-approved-base-images-built-in-project-and-checked-on-every-from.md

Each stack is built and tested when its part of the template changes.

Mechanism
golden-path's pipeline re-renders the three examples and requires them to match the committed ones whenever the template changes, and builds and tests a stack's example when that stack's files change. The demo catalog has an active product on each stack.
Status
enforced
Source
golden-path .gitlab-ci.yml (template:render, template:test)

A stack the template does not offer builds on a platform base image, without the platform's tests.

Mechanism
The base image rules apply to every listed Dockerfile. The injected build and test jobs know the template's three stacks only, so another stack runs none of them and fails the coverage gate until its entry lowers quality.minimum_coverage.
Status
enforced
Source
control-plane templates/build-test/template.yml

A product service deployed through the golden path runs only an image the platform built and promoted.

Mechanism
The golden path's Cloud Run module opts every service into the Binary Authorization policy its project carries, which in a billed product's production project is enforced and requires the build and promote attestations; the trusted-builder root lets only the build attestor and the artifact promoter sign.
Status
enforced
Source
golden-path modules/service-project/main.tf, modules/cloud-run-service/main.tf; control-plane roots/trusted-builder/

Binary Authorization holds for every Cloud Run service, as part of the floor.

Mechanism
Architecture 9.3 counts it among the controls that hold if a pipeline is bypassed. What is built is opt-in per service, written only for billed products: no organization policy requires a service to opt in, a raw Cloud Run service passes the pack with a warning, and the platform's telemetry gateway runs without it.
Status
designed
Source
landing-zone docs/architecture.md 9.3, roots/organisation/policies.tf; golden-path modules/cloud-run-service/main.tf

Every image is scanned for known vulnerabilities in the trusted build.

Mechanism
The trusted builder runs grype on every image it builds, against a vulnerability database the platform prepares once a day and uses only when it is at most 48 hours old and matches its checksum; otherwise grype fetches the vendor's database for that build. The scan and the image's software bill of materials are kept with the image's digest as evidence, with a record of which database was used, and the findings reach GitLab's vulnerability report at promotion. A finding warns: it does not block a deploy.
Status
enforced
Source
control-plane executor/build/cloudbuild.yaml, docs/adr/0034-the-scan-database-prepared-once-a-day.md, docs/adr/0021-gitlab-ultimate-deployments-vulnerabilities-compliance-and-audit-events.md

The build attestation is signed apart from the build.

Mechanism
The trusted builder builds and scans; build-attestor@, which only the executor may impersonate, signs the build attestation after the executor has checked the build against Cloud Build's own record. Promotion to production adds a second attestation.
Status
enforced
Source
control-plane roots/trusted-builder/main.tf, docs/adr/0029-the-build-attestation-is-signed-apart-from-the-build.md

Each image is scanned again, apart from the build.

Mechanism
The builder's scan runs inside the build, and its result reaches GitLab's vulnerability report at promotion. An independent scan of each digest outside the build is recorded as owed.
Status
designed
Source
landing-zone docs/reviews/2026-09-26-implementation-review.md (R03)

A revoked release runs no new pipeline.

Mechanism
The gate's integrity job, which the group's pipeline execution policy injects into every pipeline, reads the permitted releases on control-plane's main and fails a pipeline that includes a revoked one; the executor makes the same check before a merge request or release run holds a product's credentials.
Status
enforced
Source
control-plane gate/src/core/releases.ts, executor/scripts/release_check.py, releases/permitted.yaml

A release is a fixed set of commits, verified together.

Mechanism
A release is a manifest of every part's commit, never changed once merged, verified at those commits before it is permitted; a component fetches its policy pack at its own commit and names every image by digest. A pipeline file that includes a component by branch or tag is warned, not refused.
Status
enforced
Source
control-plane releases/, docs/adr/0030-shared-references-are-pinned-to-commits-and-digests.md, docs/adr/0031-the-platform-release-a-manifest-and-a-permitted-list.md

A change over the budget cannot merge.

Mechanism
The gate sets each product merge request's Airframe cost status check from the executor's priced plans of every root at its head: failed when the roots' total or any root is above the entry's monthly_budget, or when a root has no price. The product project's rule requires status checks to pass. A plan on main stops on a recorded refusal too. Previews and usage-based resources are outside the figure.
Status
enforced
Source
control-plane gate/src/adapters/gitlab-status-checks.ts, executor/scripts/cost.sh, docs/adr/0025-the-merge-waits-for-the-cost-verdict.md

Raising a budget takes a domain lead.

Mechanism
The gate's ownership rule requires a domain lead's approval of a catalog change that raises monthly_budget; where the domain's leads have no GitLab account, as in the demo, the platform team stands in.
Status
enforced
Source
control-plane gate/src/core/ownership.ts

Every product has a cost dashboard against its budget.

Mechanism
operations' Grafana root, which runs when the catalog changes, creates a cost dashboard in each product's folder: month to date and the forecast against monthly_budget, orange at 80 percent and red at 100, from the billing export through the daily cost:report job.
Status
enforced
Source
operations roots/grafana/pack.tf, roots/cost/

The Google Cloud budgets follow the same number.

Mechanism
The domain's project factory creates a budget on each billed product project from monthly_budget, 70 percent for production and 30 for non-production, with alerts at 80 and 100 percent. No demo product is billed today, so none has its project budgets yet.
Status
enforced
Source
landing-zone roots/project-factory/

A budget alert reaches the product's team.

Mechanism
When a product is billed, its budgets' alerts go by email to the billing account's administrators and users, Google's default, not to the team.
Status
designed
Source
landing-zone roots/project-factory/

A cost rise above USD 10 or 25 percent a month needs the platform's approval.

Mechanism
Today such a rise is a warning in the root's note on the merge request and holds nothing back.
Status
designed
Source
control-plane policy/airframe/cost.rego

A preview is destroyed when its merge request ends.

Mechanism
The gate learns of the merge or the close from GitLab's webhook and starts the executor's destroy run, which checks the merge request's state itself. Each preview's record carries a lock, so an apply and a destroy never run at once, and a reopened merge request starts a new generation, so a late destroy cannot remove the new preview.
Status
enforced
Source
control-plane executor/scripts/preview.sh, executor/scripts/preview_record.py

An expired or missed preview is collected.

Mechanism
preview:collect, in control-plane's daily scheduled pipeline, lists the previews the executor's records say are still up and destroys the merged, the closed and those past preview_ttl_hours (72 by default, at most 168, counted from the latest apply), with a note on each merge request.
Status
enforced
Source
control-plane executor/scripts/preview.sh

A preview costs next to nothing while idle, and never blocks a merge.

Mechanism
The router and the preview's services scale to zero, the default sidecar database lives in the service's own revision, and the golden path's Cloud Run module runs a preview's service as one instance at most. The executor prices each preview's plan and shows it in its note, and the cost check that holds a merge leaves previews out.
Status
enforced
Source
golden-path modules/preview-substrate/, modules/cloud-run-service/; control-plane executor/scripts/preview.sh

Every grant, and every change made under it, is recorded.

Mechanism
Privileged Access Manager keeps each grant with its requester, justification, approver and reason, and Google Cloud's audit log keeps every change; Data Access logs at the folder also record reads of Secret Manager, Cloud SQL, Cloud Storage, IAM and Cloud KMS.
Status
enforced
Source
landing-zone roots/pam/, roots/audit/main.tf

Each use of a grant has a row in the bootstrap log.

Mechanism
The row names the grant, who requested and approved it, the root and commit applied, and the empty plan after it. It is written down and taught; nothing refuses a grant without one.
Status
convention
Source
landing-zone docs/bootstrap-log.md

Every Google Cloud audit log reaches one bucket, and no pipeline can reroute it.

Mechanism
An aggregated sink at the folder routes every project's audit logs, and GitLab's audit events through the gate and the audit receiver, into a bucket in lk-audit encrypted with a key the platform holds. The deny policy refuses any change to the folder's sinks to everyone but the break-glass group, the bootstrap administrator account and the Privileged Access Manager service agent. The folder's IAM policy, which holds the audit configuration, has the same exceptions and a fourth, the identity that configures the grants; it acts only from reviewed code, and the condition on its access requires a change to modify at least one grant and only grants of the roles the entitlements give, so a call that changes only the audit configuration is refused. A call that changes a permitted grant and the audit configuration together still passes: at the folder, a rule in Grafana pages when an audit configuration changes, and at the organization there is no sink. The sink leaves out VPC Service Controls' dry-run entries, which record what a perimeter would have refused and not a refusal; each project's own log keeps them. GitLab's audit events reach the bucket through two streaming destinations, the gate and the audit receiver, each writing every event; GitLab does not send an event again, so an event created while both are unreachable is missing from the bucket, and if its type is one GitLab only streams it is recorded nowhere.
Status
enforced
Source
landing-zone roots/audit/main.tf, roots/organisation/deny.tf, roots/wif/grants.tf, docs/adr/0007-pam-companion-condition.md; operations roots/grafana/detections.tf; control-plane docs/adr/0032-a-gate-that-cannot-be-reached.md (owed 7), gate/infra/audit-receiver.tf

The audit bucket's retention is locked.

Mechanism
A retention policy keeps every object for 365 days, and none can be deleted or overwritten before then. The policy is locked since 2026-10-01, which Google makes irreversible: nobody, an administrator under a grant included, can shorten or remove it, and the audit project cannot be deleted while the bucket holds objects.
Status
enforced
Source
landing-zone roots/audit/main.tf, roots/audit/terraform.tfvars, docs/bootstrap-log.md

Data cannot leave the platform's projects through Google's APIs, even with valid credentials.

Mechanism
Three VPC Service Controls perimeters, production, non-production and audit, are applied as dry-run specifications: a would-be violation is logged, and nothing is blocked. Enforcing them is one variable. The organization root's daily reads that a perimeter would refuse come from inside the production perimeter, from the trusted runner on the production network's VPC, because the perimeter's list of services reachable from that VPC does not include those services; they are not calls from outside.
Status
designed
Source
landing-zone roots/perimeters/

Suspicious use raises an alert: a key creation attempt, a deny policy hit, a grant, an identity used outside its pipeline.

Mechanism
A second, narrow sink at the folder sends only these entries to Loki: a key creation attempt, a deny or organization policy refusal, a refused token, a change to the folder's sinks, deny policies or audit configuration, a grant, and GitLab's audit events. Rules in Grafana page the platform on the first five and open a ticket for a grant or a burst of GitLab changes. An event at the organization itself, such as an organization-level grant, is outside the folder's sinks; the approver's notification is its only record besides the audit log. GitLab's events arrive through the gate, so those created while it cannot be reached never arrive, and no rule sees them.
Status
enforced
Source
landing-zone roots/audit/main.tf, docs/adr/0027-the-audit-detections-alert-in-grafana.md; operations roots/grafana/detections.tf

Drift is reported.

Mechanism
The tf-apply component's drift job plans each root on the repository's daily schedule, and the drift-report component, as the audit identity, keeps one issue labeled audit::drift per drifting root, rewrites it and closes it when the plan is empty again.
Status
enforced
Source
control-plane templates/tf-apply/, templates/drift-report/

A coding agent's work is labeled.

Mechanism
The instructions for a coding agent and the airframe-platform skill ask for a Generated-by trailer on every commit and the agent label on the merge request, and the platform's GitLab configuration defines the label. No job or rule checks either, so they record what a coding agent or its user declares.
Status
convention
Source
golden-path template/; platform-skills skills/

An audit can tell a coding agent's work from a person's, and measure its share.

Mechanism
That is what the trailer and the label are designed for; with nothing checking them, they cannot prove it today.
Status
designed
Source
landing-zone docs/architecture.md 9.7

Every product has its dashboards and alert rules from its entry.

Mechanism
operations' Grafana root, which runs when the catalog changes, creates each product's folder, its service and SLO dashboards and its baseline alert rules, with thresholds from the entry's slo; the rules select production by the env label, and a missing series is not an alert.
Status
enforced
Source
operations roots/grafana/pack.tf, alerts.tf

An alert reaches the product's team, and today that means the maintainer.

Mechanism
Each rule routes by the team label to the team's contact point, which emails the team's members who have a Grafana account, and a page repeats every four hours until it is resolved. Grafana Cloud accepts only its own members' addresses, and today those members are the maintainer's own accounts, so every team's alerts, and the platform's, reach the maintainer.
Status
enforced
Source
operations roots/grafana/alerts.tf, members.tf

An application needs no setup and holds no Grafana credential.

Mechanism
The project factory's Ready output carries the address of the gateway of the product's edition, and the golden path's Cloud Run module injects it into every service. Traces and metrics reach the OTLP gateway with the service's identity token; logs reach the logs gateway through the project's sink and a Pub/Sub buffer. The gateways hold the edition's write-only token, the only Grafana credential on that side.
Status
enforced
Source
operations roots/telemetry/; golden-path modules/cloud-run-service/main.tf

Every signal says which edition it came from.

Mechanism
The OTLP gateway keeps a signal's env only when it is valid for its own edition and replaces a missing or foreign value with unknown; the logs gateway keeps a valid env label and otherwise gives the line its perimeter's, so only audit entries stay unknown. Both stamp the edition as perimeter. The golden path's Cloud Run module gives a service's traces and metrics its service name, its domain and its environment, and the platform agent labels each billed product project's Cloud resource metrics with the product's path and its journey and domain.
Status
enforced
Source
operations roots/telemetry/config/

An application's signals, the platform agent's resource metrics and the platform's scheduled jobs' series carry the environment they came from.

Mechanism
The OTLP gateway gives a resource without env the value of deployment.environment.name, then of deployment.environment, the attribute the golden path's Cloud Run module sets from the deployment (prod, np, sandbox or eph-<n>); a datapoint keeps its own valid env, and a value that is not valid for the edition becomes unknown. The platform agent labels each project's series with env: platform for the platform's projects, the edition for a product's, and in non-production the Cloud Run service's name narrows it to sandbox or eph-<n>. The logs gateway gives a line without a valid env its perimeter's, narrowed the same way; a platform project's lines are platform, and audit entries keep unknown. The platform's own scheduled jobs (the cost, delivery and coverage reports and the daily Policy Analyzer check) declare deployment.environment=platform on their resource, so their series carry env platform; the cost report keeps the environment of the project a cost belongs to in a separate label, project_env (contracts section 7). The drift report's series still arrive as unknown until the next platform release, because each repository runs the drift-report component of its release. The pipeline applies the telemetry root from operations' main, and its telemetry:test job renders the real configurations and runs them in the pinned Alloy whenever they change, failing rather than skipping when the binary is missing.
Status
enforced
Source
operations commit 872681a: roots/telemetry/config/otlp.alloy.tftpl, logs.alloy.tftpl, agent.alloy.tftpl, roots/telemetry/agent.tf, tests/telemetry_test.py, .gitlab-ci.yml, jobs/delivery_report.py, jobs/coverage-report.sh, jobs/cost_report.py; control-plane templates/drift-report/template.yml; landing-zone .gitlab-ci.yml, docs/contracts.md 7; golden-path modules/airframe-context/main.tf, modules/cloud-run-service/main.tf

An application's signals carry its team and criticality.

Mechanism
The design gives every signal the catalog's labels, team and criticality among them. They carry no such label today: a service's traces and metrics have service.name, service.namespace, which is its domain, and deployment.environment, and the logs gateway keeps a line's project, service, resource type, log name and severity. Only some of the platform's own series carry a team, such as the delivery and coverage reports'.
Status
designed
Source
landing-zone docs/guides/observability.md (Labels); golden-path modules/cloud-run-service/main.tf; operations roots/telemetry/config/logs.alloy.tftpl

An alert pages an on-call person, under a schedule and an escalation chain.

Mechanism
Grafana's on-call and incident tool would hold both for each team, from the catalog. Today an alert is an email.
Status
designed
Source
landing-zone docs/architecture.md 13.5

An outage of the gate is noticed without waiting for red pipelines.

Mechanism
A Grafana Cloud Synthetic Monitoring HTTP check, airframe-gate, requests https://gate.projectairframe.dev/health every 60 seconds from two probes. A rule in the airframe-platform group pages the platform's contact point when more than one probe fails for two minutes, or when the check reports nothing. The check covers the gate only; no other hostname has one. Today a page is an email to the maintainer.
Status
enforced
Source
operations roots/grafana/synthetic.tf, alerts.tf, docs/adr/0012-the-gate-uptime-check-in-synthetic-monitoring.md

The baseline covers every failure the design names.

Mechanism
Cloud Run at its maximum instances, restarts or out of memory, a contract check blocked on main, and uptime checks through each product's production hostname are designed; the Grafana root does not create these rules yet. The gate's uptime check, above, is the only one built.
Status
designed
Source
landing-zone docs/architecture.md 13.4, 13.6; operations roots/grafana/pack.tf

An operations agent investigates an alert, and an audit agent looks for waste and orphans.

Mechanism
The first would act within what the entry's agentic_ops allows; the second would look across the organization, and find a preview with no record or a failed destroy. Neither exists today.
Status
designed
Source
landing-zone docs/architecture.md 14.3

A skill says what the platform's checks do.

Mechanism
The platform-skills pipeline's skills:policy, skills:validate, skills:catalog and skills:references jobs check each skill's examples against the policy pack at the control-plane commit the skills pin, terraform validate and the catalog's lint, and check the paths a skill names; GitLab merges a change only when they pass. control-plane's release:check requires that pin to be the release's. Whether a coding agent loads a skill is up to the agent and its user.
Status
enforced
Source
platform-skills .gitlab-ci.yml; control-plane .gitlab-ci.yml (release:check)

Every skill ships examples that the checks must accept or refuse.

Mechanism
platform-skills' README says each skill comes with its tests and a skill without tests is not merged, and four of the five skills ship pass and fail examples. The overview skill, airframe-platform, ships none: its one test, tests/references, checks that the paths it names exist. Nothing refuses a skill without examples.
Status
convention
Source
platform-skills README.md, skills/airframe-platform/

Each product publishes its own part of the map, and only its own.

Mechanism
The catalog-publish component's validate:fragment job builds the fragment on the merge request with no credential; on the protected main, release:catalog publishes it with the identity the repository already holds. A product's production apply identity may write only under its product's prefix. The four platform repositories share one identity, platform-docs@, whose grant covers the whole platform prefix, so nothing stops one of them from writing another's part.
Status
enforced
Source
control-plane templates/catalog-publish/template.yml, roots/executor/variables.tf

The map rebuilds itself from the latest fragments.

Mechanism
The aggregator, a Cloud Run job, runs when a publishing identity asks after a publication and hourly on a schedule. It reads the catalog's list of products, skips a broken fragment with its reasons in the run's record and keeps the last three versions.
Status
enforced
Source
operations eventcatalog/, roots/eventcatalog/

A coding agent can query the map through the catalog's MCP server.

Mechanism
The server is designed; the machine-readable index it would read is published today beside every build.
Status
designed
Source
landing-zone docs/guides/architecture-catalog.md

The platform's documents match its code.

Mechanism
A change carries its documents in the same merge request and review holds it; no job compares the two. Known differences today: the security guide puts Binary Authorization on every project; the observability guide gives each team an on-call schedule; ADR-0016 says a preview's destroy removes its state, which the executor keeps; and the contracts count a preview's time to live from its last push, the executor from its latest apply.
Status
convention
Source
landing-zone docs/guides/security.md, docs/guides/identity.md, docs/guides/observability.md, docs/contracts.md 8; control-plane docs/adr/0016-the-preview-lifecycle-through-the-executor.md, executor/scripts/preview.sh

Where it lives

In the landing-zone repository:

In the control-plane repository:

MIT license. Copyright Airframe contributors.

This site describes platform release 2026.10.20-1.