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.
  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 do not apply. The binding is the whole answer, which is the point: a developer cannot opt out of production's posture by editing a source string.

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 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

The 503 is the only one of these that resolves itself. 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.

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