The meshstack-hub is the canonical Terraform module registry for meshStack integrations — an Artifactory-like catalog with a UI at hub.meshcloud.io. It is the monorepo for all IaC building blocks that can be imported into any meshStack instance.
CI runs
tf validateandterraform-docson every module — it does not runtf plan. Planning and applying happens in IaC runtimes (LCF/ICF) that consume modules from this repo.
Every module follows a two-tier layout. The backplane tier is optional and should be omitted for simple building blocks that require no cloud-side setup (e.g. those that receive all credentials as static inputs).
modules/<cloud-provider>/<service-name>/
├── backplane/ # optional — Infrastructure/permissions setup (run by platform team)
│ ├── main.tf # Omit entirely for simple building blocks that need no cloud-side setup
│ ├── variables.tf
│ ├── outputs.tf
│ ├── versions.tf
│ └── README.md
├── buildingblock/ # Actual service resources (run by meshStack per tenant)
│ ├── main.tf
│ ├── variables.tf
│ ├── outputs.tf
│ ├── versions.tf
│ ├── provider.tf
│ ├── README.md # YAML front-matter required (see below)
│ ├── logo.png
│ └── *.tftest.hcl
└── meshstack_integration.tf # Example wiring into a meshStack instance
These files are examples showing how to integrate building block and platform modules with a meshStack instance. They are starting points that should cover the simplest use case. A secondary purpose of these files is to serve as a ready-to-use Terraform module root that IaC runtimes can source directly.
- Must use variables for required user inputs.
- Must include
required_providersblock at the bottom of the file. - Keep variable blocks at the top of the file, followed immediately by output blocks; keep
variable "meshstack"andvariable "hub"at the end of the variable section. - Cloud-provider-specific variables must be flat with a provider prefix (e.g.
azure_tenant_id,aws_region). Do not group them into a single provider object likevariable "azure" { type = object({...}) }. - Cross-cutting concerns (e.g. workload identity federation) may use an
object({})variable when the fields are logically inseparable. localsblocks are allowed when they improve readability/reuse, but place them below variable and output sections.- Avoid top-of-file banner comments in
meshstack_integration.tf. - Never include
providerconfiguration. - Reference modules using Git URLs with
?ref=${var.hub.git_ref}. This keeps both thebuildingblockimplementation path and the optionalbackplanemodule source pinned by a single variable. Example:Themodule "backplane" { source = "github.com/meshcloud/meshstack-hub//modules/<provider>/<service>/backplane?ref=${var.hub.git_ref}" }
const = trueattribute onvar.huballows Terraform/OpenTofu to resolve the interpolation atinittime.
Every meshstack_integration.tf must declare the meshcloud/meshstack provider in a
required_providers block. Use a minimum version constraint with >= (e.g. >= 0.20.0).
Root configurations (ICF/LCF) that source hub modules are responsible for strict version
pinning via their .terraform.lock.hcl files.
terraform {
required_providers {
meshstack = {
source = "meshcloud/meshstack"
version = ">= 0.20.0"
}
}
}The following variables must appear in every meshstack_integration.tf.
To source modules from the hub, include a hub variable which determines the git reference to use.
You may extend variable "hub" with additional fields as needed (e.g. base_url), but git_ref
is always required.
# Shared Hub reference — always include this variable
variable "hub" {
type = object({
git_ref = optional(string, "main")
bbd_draft = optional(bool, true)
})
const = true
default = {}
description = <<-EOT
`git_ref`: Hub release reference. Set to a tag (e.g. 'v1.2.3') or branch or commit sha of the meshstack-hub repo.
`bbd_draft`: If true, the building block definition version is kept in draft mode.
EOT
}The const = true attribute (OpenTofu ≥ 1.12 / Terraform ≥ 1.15) marks var.hub for early static evaluation during terraform init, which is required to interpolate var.hub.git_ref inside module source strings. variable "hub" must satisfy all const constraints:
- Its value must come from a
default,.tfvarsfile, orTF_VAR_*environment variable — never from a resource, data source, or dynamic local. - It must not have
sensitive = trueorephemeral = true.
Always use var.hub.bbd_draft for the draft field of version_spec in meshstack_building_block_definition resources.
When a meshstack_integration.tf exposes building block definition references for compositions, use a single object output named building_block_definition:
output "building_block_definition" {
description = "BBD is consumed in building block compositions."
value = {
uuid = meshstack_building_block_definition.this.metadata.uuid
version_ref = var.hub.bbd_draft ? meshstack_building_block_definition.this.version_latest : meshstack_building_block_definition.this.version_latest_release
}
}Integrating with meshStack requires context, like a workspace where the resource will be managed.
# Shared meshStack context — always include this variable
variable "meshstack" {
type = object({
owning_workspace_identifier = string
tags = optional(map(list(string)), {})
})
description = "Shared meshStack context. Tags are optional and propagated to building block definition metadata."
}Use these variables in the implementation block of building block definitions. Always forward var.meshstack.tags to the BBD metadata.tags field so that workspace-level tags are propagated to the building block definition.
resource "meshstack_building_block_definition" "this" {
metadata = {
owned_by_workspace = var.meshstack.owning_workspace_identifier
tags = var.meshstack.tags
}
# ... other required fields ...
implementation = {
terraform = {
repository_url = "https://github.com/meshcloud/meshstack-hub.git"
repository_path = "modules/<provider>/<service>/buildingblock"
ref_name = var.hub.git_ref # always use var.hub.git_ref, never hardcode "main"
}
}
# ...
}- Always use
snake_casefor variable names:monthly_budget_amount, notmonthlyBudgetAmount - Cloud-provider-specific variables in
meshstack_integration.tfmust be flat (not grouped into a single object) and prefixed with the cloud provider name:azure_tenant_id,aws_region,gcp_project_id,stackit_project_id - Cross-cutting concerns like workload identity federation settings may be grouped into an
object({})typed variable (e.g.variable "workload_identity") when the fields are logically inseparable - Only
variable "meshstack"andvariable "hub"use sharedobject({})conventions across all integrations - Use minimum version constraints (
>= X.Y.Z) for all providers inversions.tfandmeshstack_integration.tf. Strict pinning is the responsibility of root configurations (ICF/LCF) via.terraform.lock.hcl. - Terraform baseline:
>= 1.12.0to cover OpenTofu v1.12.0 withconstvariable support (requires OpenTofu ≥ 1.12 or Terraform ≥ 1.15)
The repository includes a scorecard tool that checks module maturity across four categories: Core Structure, Integration, Azure Backplane, and Testing.
# Full report
node tools/scorecard/scorecard.mjs
# Single module
node tools/scorecard/scorecard.mjs --module=<provider>/<service>
# Generate a fix prompt for a module's violations
node tools/scorecard/scorecard.mjs --module=<provider>/<service> --fixTo fix violations, see .agents/skills/fix-scorecard.md.
See .agents/skills/aws-backplane.md for the full AWS backplane identity conventions, including WIF (OIDC + IAM role) and cross-account (IAM user + CloudFormation StackSet) patterns, required variables/outputs, and the AWS backplane checklist.
See .agents/skills/azure-backplane.md for the full Azure backplane identity conventions, including UAMI patterns, WIF wiring, required variables/outputs, and the Azure backplane checklist.
See .agents/skills/bbd-readme.md for the complete BBD readme specification, template, and checklist.
buildingblock/README.md — must include YAML front-matter:
---
name: <Human-readable name>
supportedPlatforms:
- <platform-id> # e.g. aws, azure, stackit
description: One-sentence description of what the module provisions.
---User-facing readme — two patterns depending on module completeness:
-
Modules with
meshstack_integration.tf(full building blocks): user-facing readme lives in thereadmefield ofmeshstack_building_block_definition.spec. Always usechomp(<<-EOT)inline — neverfile()or a separate file (one-file copy/paste requirement). See .agents/skills/bbd-readme.md for full spec. -
Modules without
meshstack_integration.tf(standalone building blocks): place the user-facing readme atbuildingblock/APP_TEAM_README.md. meshStack uses this file as a fallback when no inline readme is available. The same content requirements apply (plain-text description first, usage motivation, examples, shared responsibility table).
The readme (inline or APP_TEAM_README.md) must include:
- A plain-text description as the first content — no leading
#heading. - Usage motivation: who this building block is for and when to use it.
- Usage examples: 1–2 concrete developer scenarios.
- Shared responsibility matrix: markdown table with
✅/❌emojis.
backplane/README.md — documentation relevant to platform engineers deploying the backplane. Include an overview of what the backplane provisions, required permissions/roles, and operational notes.
Anti-pattern: documentation_md output — do not add a documentation_md output to backplane modules. This is a legacy pattern. Documentation must instead be split into:
- User-facing content → BBD
readmefield inmeshstack_integration.tf(orAPP_TEAM_README.mdif no integration file) - Platform-engineer-facing content →
backplane/README.md
Modules in this repo are consumed by IaC runtimes (LCF, ICF, customer deployments). Those runtimes are shim layers — they reference Hub modules and should not re-implement logic here.
When prototyping locally in an IaC runtime, use relative module includes to avoid constant branch pushes:
# In LCF/ICF terragrunt.hcl — for local prototyping only
source = "../../../meshstack-hub/modules/stackit/git-repository/buildingblock"Do not commit these relative paths; switch back to the Hub GitHub URL before merging.
Reference architectures are curated, end-to-end blueprints that show how multiple Hub building blocks
fit together to deliver a complete platform capability. They live in the reference-architectures/
directory at the repo root as Markdown files with YAML front-matter.
---
name: Human-Readable Architecture Name
description: >
A concise paragraph explaining the architecture's purpose and value proposition.
cloudProviders:
- azure
buildingBlocks:
- path: azure/aks
role: Short description of this block's role in the architecture.
- path: aks/github-connector
role: Short description of this block's role in the architecture.
---
# Architecture Title
Markdown body with overview, architecture diagram, how-it-works walkthrough,
getting-started steps, and shared responsibility matrix.- File name:
<cloud>-<capability>.md(e.g.azure-kubernetes.md,stackit-kubernetes.md). buildingBlocks[].pathmust match a module path undermodules/(e.g.azure/aks).- The Markdown body should include a Mermaid diagram showing how blocks relate.
- Include a shared responsibility matrix (platform team vs. application team) with ✅ / ❌ emojis.
- Include Getting Started steps with prerequisites and deployment order.
- Markdown file in
reference-architectures/with YAML front-matter -
name,description,cloudProviders, andbuildingBlocksfields present - Every
buildingBlocks[].pathreferences an existing module inmodules/ - Every
buildingBlocks[].rolehas a one-sentence description - Body includes: overview, architecture diagram, how-it-works, getting started, shared responsibilities
- No trailing whitespace
Modules that can be smoke-tested against a live meshStack instance should include an e2e/ directory alongside the module root.
See .agents/skills/write-e2e-test/SKILL.md (the write-e2e-test skill) for the full e2e testing conventions, including the e2e/ structure, test_context wiring, e2e/main.tf and *.tftest.hcl conventions, the new-test checklist, and how to run and debug tests via the smoke-test runner.
-
backplane/(optional) andbuildingblock/with all required files -
meshstack_integration.tfpresent at the module root - Provider versions use minimum constraint (
>=) inversions.tfandmeshstack_integration.tf - Variables in
snake_casewith cloud-provider prefix inmeshstack_integration.tf(e.g.azure_tenant_id) -
buildingblock/README.mdwith YAML front-matter - BBD
readmefield useschomp(<<-EOT)inline (nofile()), starts with plain-text description (no#heading), and includes usage motivation, 1–2 examples, and a shared responsibility table with ✅ / ❌ — see .agents/skills/bbd-readme.md - If no
meshstack_integration.tf:buildingblock/APP_TEAM_README.mdis present with the same content requirements (plain-text description first, motivation, examples, shared responsibility table) -
meshstack_integration.tfdeclaresmeshcloud/meshstackinrequired_providers -
meshstack_integration.tfusesvariable "hub" { type = object({git_ref = string}) }andvariable "meshstack" { type = object({owning_workspace_identifier = string}) } -
meshstack_integration.tfreferences backplane via GitHub URL with?ref=${var.hub.git_ref}(e.g.github.com/meshcloud/meshstack-hub//modules/<provider>/<service>/backplane?ref=${var.hub.git_ref}) — never a hardcoded commit SHA or relative./backplanepath -
variable "hub"hasconst = true -
ref_nameusesvar.hub.git_ref— no hardcoded"main" -
version_spec.draftusesvar.hub.bbd_draft -
metadata.tags = var.meshstack.tagsinmeshstack_building_block_definitionresource - Tags are modeled via
var.meshstack.tags(no separate top-levelvariable "tags"in integrations) -
building_block_definitionoutput is exposed as{ uuid, version_ref }withversion_refusingbbd_draft ? version_latest : version_latest_release -
localsblocks (if used) appear below variables and outputs -
terraform { required_providers { ... } }block is at the bottom ofmeshstack_integration.tf -
meshstackandhubvariables are at the end of the variable section -
logo.pngincluded inbuildingblock/ - No
documentation_mdoutput inbackplane/— use BBDreadmefield andbackplane/README.mdinstead - No trailing whitespace
- Azure modules: also follow the Azure Backplane Checklist