compliance.tf

How a Module Request Resolves

  • A module source is the whole integration. On an organization's own host, the three path segments after the hostname name a project, an environment, and a module.
  • The credential on the request decides on whose behalf it resolves. On an organization's host the caller must be entitled to act for that organization, or the request is refused rather than quietly served at a lower tier.
  • The project and environment select exactly one binding. That binding names a snapshot and carries the waiver set frozen into it, and those two together are the effective posture.
  • The version list advertises the underlying module's ordinary published versions. Posture is not encoded in the version string — two environments can advertise the same version and receive different modules for it.
  • A step that cannot resolve refuses. It does not fall back to a more permissive posture. What it does serve is whatever the binding resolves to — including nothing, if the snapshot enforces nothing.
  • Both the version listing and the download count against the daily request budget on metered plans, so one terraform init spends more than one request.

Why this page exists

Every other page in this section describes something you author: an organization, a project, a config, a snapshot, a binding. This page describes the one moment where all of that becomes a fact on a developer's machine — the terraform init that pulls a module and gets back the posture your organization decided on.

If you are evaluating whether the model fits your estate, this is the page to read. It is the contract your Terraform code binds to.

The address is the contract

compliance.tf serves modules over two different readings of the same three-segment path, and the hostname decides which reading applies.

Framework or canonical hostYour organization's host
Hostsoc2.compliance.tf, registry.compliance.tf<alias>.compliance.tf
First path segmentnamespaceproject slug
Second path segmentmodule nameenvironment: dev, staging, or prod
Third path segmentprovidermodule token
What decides the postureThe hostname you typed, plus any query parametersThe binding on that project environment
# Framework host: your code names a compliance framework.
module "bucket" {
  source = "soc2.compliance.tf/terraform-aws-modules/s3-bucket/aws"
}

# Organization host: your code names your own project and environment.
module "bucket" {
  source = "bobthecorp.compliance.tf/payments/prod/s3-bucket"
}

The environment segment selects the grammar. It must be exactly dev, staging, or prod — the three environments every project has. That is what tells the registry it is reading a project address rather than an ordinary module path, and it is why a typo in that slot produces a missing-module error rather than a missing-environment one. What it does not do is decide whose posture you receive; the hostname settles that before the path is read at all. Projects and environments owns the grammar of the slug and the module token; organizations and members owns the alias itself; registry endpoints covers the framework-host form and the HTTPS URL variant of both.

What this page owns is what happens after the address is parsed.

The path a request takes

How a terraform init resolves to an enforced posture Sequence diagram: terraform init lists versions from the compliance.tf registry, which maps the host label to an organization, reads the environment binding from platform state and returns a version list; the download request re-reads the binding, resolves the module token, builds or fetches the artifact and answers 204 with a download URL. 1 · LIST VERSIONS 2 · DOWNLOAD LIST VERSIONS · CREDENTIAL HOST LABEL → ORGANIZATION READ ENVIRONMENT BINDING SNAPSHOT + FROZEN WAIVERS VERSION LIST DOWNLOAD CHOSEN VERSION READ BINDING AGAIN EFFECTIVE POSTURE RESOLVE MODULE TOKEN BUILD OR FETCH ARTIFACT 204 · DOWNLOAD URL terraform initclient compliance.tf registryregistry.compliance.tf Platform statebindings · snapshots LEGENDClientRegistry (focal)Platform state HTTP requestReturn

Step by step, in resolution order:

  1. The hostname is read, and it alone decides on whose behalf the request runs. A first label that is neither a reserved subdomain nor a framework endpoint marks the request as arriving on an organization's host. On such a host every credential must prove it belongs to that organization, whatever grammar the rest of the path uses — the check is derived from the host and never from a path segment, so no URL shape can skip it.
  2. The credential is resolved. A registry token or a signed-in session identifies who is asking. On an organization's host the request must be backed by an active membership in that organization, or by a token the organization itself owns. See registry tokens for which credential carries which entitlement.
  3. The project slug and environment select a binding. There is exactly one binding per project environment, and finding it is the only way this path reaches a posture. The bare form of an organization's host names no project: it resolves the organization's own baseline snapshot instead, and adds the organization's live operational rules — its Baseline, and any ruleset the source selects with ?ruleset= — see Baseline rules and rulesets.
  4. The effective posture is assembled from the snapshot the binding names and the waiver set frozen into that binding. Promotion and gates covers how both got there.
  5. The module token is resolved to a module identity — the project's own module aliases first, then the published terraform-aws-modules names.
  6. The artifact is served, built to that posture.

Two things worth noticing about the shape of that sequence. The binding is consulted on every request rather than baked into anything Terraform holds, so a promotion takes effect on the next terraform init without anyone reconfiguring a pipeline. And the module token is resolved after the posture, not before — which is why an environment with no binding reports a missing binding even for a module that plainly exists.

What gets served

The download answer is the ordinary Terraform module protocol: a 204 whose X-Terraform-Get header points at a short-lived URL that Terraform then fetches. Nothing about the integration is unusual on the Terraform side — the CLI does not need a plugin, a wrapper, or a custom source type.

Three properties of the answer are worth planning around:

Posture is not in the version string. The version list on an organization's host advertises the underlying module's ordinary published versions. Two environments bound to different snapshots advertise identical version lists and receive different content for the same version. This is deliberate: a version list carrying posture hashes would look like a list of pre-release versions to Terraform, and an unconstrained terraform init would report no versions available at all.

Responses are not cacheable, and are marked as such. Different callers receive different modules for the same URL, so download responses carry no-store cache directives and vary on the credential. Do not place a shared caching proxy or a mirroring layer between your build agents and the registry; it would serve one team's posture to another.

Query parameters do not override a binding. On a framework host, ?enable= and ?disable= let a module source adjust which controls apply — that is the feature described in customize controls. On an organization's host they are refused with a 400 rather than dropped. The binding is the whole answer, which is the point: a developer cannot opt out of production's posture by editing a source string. The one query parameter an organization host does accept is ?ruleset=<name>, on the bare <alias>.compliance.tf/<namespace>/<name>/<provider> form only: it selects a named group of rules the organization published, and can add to the posture but never subtract from it — see Baseline rules and rulesets.

When a step cannot resolve

Nothing in this path degrades to a more permissive answer. A step that cannot resolve refuses; it never falls back to an unbound module, another environment's posture, or a lower gate. What it does serve is exactly what the binding resolves to, which is a separate question — an environment bound to a snapshot that enforces nothing receives a module that enforces nothing, because that is what the posture says. It is still rebuilt rather than passed through, so the artifact records that the empty posture was applied.

What Terraform reportsWhat it means
A 404 naming an unknown organization aliasThe hostname's first label is not an assigned alias
A 403 refusalThe credential is not entitled to act for that organization
A 404 naming no binding or snapshot for the project and environmentThe address parsed, but that environment has nothing bound — or the slug is subtly wrong
A 404 saying no compliance baseline is published for your organizationThe bare organization form was used and the organization has never published a baseline. The project-environment form does not use the baseline and does not produce this
A 503 saying the same, with (baseline lookup failed)The baseline exists but could not be read. Retry
A 404 naming a missing moduleThe module token resolved to nothing, or to a module with no published version
A 429 with Retry-AfterThe daily request budget is spent — see plans and entitlements
A 503 asking you to retry shortlyA posture nobody has fetched before is being built; re-run terraform init

Two of these resolve themselves. The build 503 is the common one: the first request for a posture that has never been served has to build the module, and while that is happening other requests for the same posture wait rather than building duplicates. A request that waits too long is told to come back instead of being held past the point where Terraform would give up on it. The baseline 503 is the other, and it means a read failed rather than a build being slow.

The two baseline answers are the organization host's own fail-closed step, and they are worth reading as a pair. A baseline that cannot be resolved is never replaced by the canonical zero-control posture — that substitution would serve nothing under a manifest that looks legitimate, which is the whole failure this step exists to prevent. Both messages name the organization, and both are returned identically by the version listing and by the download, so a missing baseline fails at the first step of terraform init rather than half way through it.

One qualification on "an organization with no baseline gets a 404". compliance.tf may provision an empty baseline for an organization that never authored one. Such an organization resolves and is served — it does not 404 — but the posture enforces nothing, and the build marks the artifact baseline-not-authored in the manifest's degraded list. Read that reason as "nobody approved this", not as a fault.

Behind the gate

The rest of this page is the operator's half: which kind of credential is valid against which host form and what each one yields, exactly where each resolution step fails closed and where one deliberately does not, how the effective posture is assembled from a snapshot and a frozen waiver set, what a refusal carries in its body and headers, the version-string trap on an organization's host, and the fallback rules for a module that compliance.tf has never published.

On this page

Ask AI about this

Help improve this page