Validation
Each smoke claim runs twice and passes only when both runs hold: as written it must pass, and with its property broken on purpose (the BREAK) it must fail.
By tool, on Forgejo
Swipe for more columns
| Binary | Repo shape | ||||||
|---|---|---|---|---|---|---|---|
| Feature area | OpenTofu | Terraform | choudoufu | Terragrunt | Atmos | Terramate | CDK Terrain |
| Plan and review | Plan and review, OpenTofu42 checks, all proven.
| Plan and review, TerraformNot re-run on Terraform. These 42 checks run the same code on every binary; on OpenTofu, 42 are proven.
| Plan and review, choudoufu1 check, proven.
| Plan and review, Terragrunt10 checks, all proven.
| Plan and review, Atmos2 checks, all proven.
| Plan and review, Terramate2 checks, all proven.
| Plan and review, CDK Terrain4 checks, all proven.
|
| Approve and apply | Approve and apply, OpenTofu51 checks, all proven.
| Approve and apply, Terraform4 checks, all proven.
| Approve and apply, choudoufu6 checks, all proven.
| Approve and apply, Terragrunt14 checks, all proven.
| Approve and apply, Atmos3 checks, all proven.
| Approve and apply, Terramate2 checks, all proven.
| Approve and apply, CDK Terrain1 check, proven.
|
| Locks and safety | Locks and safety, OpenTofu28 checks, all proven.
| Locks and safety, Terraform3 checks, all proven.
| Locks and safety, choudoufu9 checks, all proven.
| Locks and safety, Terragrunt3 checks, all proven.
| Locks and safety, Atmos1 check, proven.
| Locks and safety, Terramate1 check, proven.
| Locks and safety, CDK Terrain1 check, proven.
|
| Policy | Policy, OpenTofu16 checks, all proven.
| Policy, Terraform2 checks, all proven.
| Policy, choudoufu2 checks, all proven.
| Policy, Terragrunt2 checks, all proven.
| Policy, AtmosNo check recorded. | Policy, TerramateNo check recorded. | Policy, CDK TerrainNo check recorded. |
| Drift | Drift, OpenTofu10 checks, all proven.
| Drift, Terraform2 checks, all proven.
| Drift, choudoufu2 checks, all proven.
| Drift, Terragrunt4 checks, all proven.
| Drift, AtmosNo check recorded.The drift pull request is refused: a live value belongs in the stack vars or the component. | Drift, TerramateNo check recorded.The drift pull request is refused: a live value belongs in the stack .tm.hcl or its own Terraform. | Drift, CDK TerrainNo check recorded.The drift pull request is refused: a live value belongs in the app that writes the stacks. |
| Chat and notify | Chat and notify, OpenTofu6 checks, all proven.
| Chat and notify, Terraform1 check, proven.
| Chat and notify, choudoufu1 check, proven.
| Chat and notify, TerragruntNo check recorded. | Chat and notify, AtmosNo check recorded. | Chat and notify, TerramateNo check recorded. | Chat and notify, CDK TerrainNo check recorded. |
| Reports and visibility | Reports and visibility, OpenTofu41 checks, all proven.
| Reports and visibility, Terraform5 checks, all proven.
| Reports and visibility, choudoufu8 checks, all proven.
| Reports and visibility, Terragrunt2 checks, all proven.
| Reports and visibility, AtmosNo check recorded. | Reports and visibility, TerramateNo check recorded. | Reports and visibility, CDK TerrainNo check recorded. |
| Modules | Modules, OpenTofu13 checks, all proven.
| Modules, TerraformNot re-run on Terraform. These 13 checks run the same code on every binary; on OpenTofu, 13 are proven.
| Modules, choudoufuNot re-run on choudoufu. These 13 checks run the same code on every binary; on OpenTofu, 13 are proven.
| Modules, Terragrunt1 check, proven.
| Modules, AtmosNo check recorded.Rollouts are refused: every instance of a component shares its files, so no wave can move a pin alone. | Modules, TerramateNo check recorded.Rollouts are refused: the pin is usually in code terramate generate writes. | Modules, CDK TerrainNo check recorded.Rollouts are refused: the pin is in the app that writes the stacks. |
| State and migration | State and migration, OpenTofu20 checks, all proven.
| State and migration, TerraformNot re-run on Terraform. These 20 checks run the same code on every binary; on OpenTofu, 20 are proven.
| State and migration, choudoufu3 checks, all proven.
| State and migration, Terragrunt3 checks, all proven.
| State and migration, Atmos1 check, proven.
| State and migration, TerramateNo check recorded. | State and migration, CDK Terrain1 check, proven.
|
| Agents and pull request environments | Agents and pull request environments, OpenTofu10 checks, all proven.
| Agents and pull request environments, TerraformNot re-run on Terraform. These 10 checks run the same code on every binary; on OpenTofu, 10 are proven.
| Agents and pull request environments, choudoufuNot re-run on choudoufu. These 10 checks run the same code on every binary; on OpenTofu, 10 are proven.
| Agents and pull request environments, Terragrunt1 check, proven.
| Agents and pull request environments, AtmosNo check recorded.A copy per pull request is refused: the stack backend and workspace name the state of an instance. | Agents and pull request environments, TerramateNo check recorded. | Agents and pull request environments, CDK Terrain1 check, proven.
|
| Setup and runtime | Setup and runtime, OpenTofu39 checks, all proven.
| Setup and runtime, Terraform3 checks, all proven.
| Setup and runtime, choudoufu3 checks, all proven.
| Setup and runtime, Terragrunt8 checks, all proven.
| Setup and runtime, Atmos3 checks, all proven.
| Setup and runtime, TerramateNo check recorded. | Setup and runtime, CDK Terrain2 checks, all proven.
|
By forge
| Feature area | Forgejo | GitHub | GitLab |
|---|---|---|---|
| Plan and review | Plan and review, Forgejo43 checks, all proven.
| Plan and review, GitHub7 checks: 3 proven, 4 pass with nothing cut out.
| Plan and review, GitLab9 checks, all proven.
|
| Approve and apply | Approve and apply, Forgejo53 checks, all proven.
| Approve and apply, GitHub8 checks: 3 proven, 5 pass with nothing cut out.
| Approve and apply, GitLab8 checks, all proven.
|
| Locks and safety | Locks and safety, Forgejo36 checks, all proven.
| Locks and safety, GitHub5 checks, all proven.
| Locks and safety, GitLab6 checks, all proven.
|
| Policy | Policy, Forgejo16 checks, all proven.
| Policy, GitHub2 checks: 0 proven, 2 pass with nothing cut out.
| Policy, GitLab2 checks, all proven.
|
| Drift | Drift, Forgejo12 checks, all proven.
| Drift, GitHub2 checks: 1 proven, 1 passes with nothing cut out.
| Drift, GitLab1 check, proven.
|
| Chat and notify | Chat and notify, Forgejo6 checks, all proven.
| Chat and notify, GitHubNo check recorded. | Chat and notify, GitLabNo check recorded. |
| Reports and visibility | Reports and visibility, Forgejo44 checks, all proven.
| Reports and visibility, GitHubNo check recorded. | Reports and visibility, GitLab3 checks, all proven.
|
| Modules | Modules, Forgejo13 checks, all proven.
| Modules, GitHub2 checks: 0 proven, 2 pass with nothing cut out.
| Modules, GitLab2 checks, all proven.
|
| State and migration | State and migration, Forgejo23 checks, all proven.
| State and migration, GitHubNo check recorded. | State and migration, GitLab3 checks, all proven.
|
| Agents and pull request environments | Agents and pull request environments, Forgejo10 checks, all proven.
| Agents and pull request environments, GitHub2 checks: 0 proven, 2 pass with nothing cut out.
| Agents and pull request environments, GitLab4 checks, all proven.
|
| Setup and runtime | Setup and runtime, Forgejo43 checks, all proven.
| Setup and runtime, GitHub2 checks: 1 proven, 1 passes with nothing cut out.
| Setup and runtime, GitLab3 checks, all proven.
|
- proven passes, and fails with the feature cut out
- passes passes, not run with the feature cut out
- same code not re-run on this binary; its checks run the same code on every binary
- not supported not supported by design; a corner mark means part of the area
- none no check recorded
Every check runs on OpenTofu on Forgejo. Runs on Terraform, choudoufu and github.com are expensive, so they re-run only the checks where the binary or forge changes what happens. On github.com most checks run with nothing cut out, so they pass but are not proven.
Open a cell for the checks behind it. Recorded: example checks, last full run Oct 8, 2026; per-forge checks Oct 7 to Oct 9, 2026; github.com Oct 10, 2026.
What is left
37 cells of the grids above are not proven yet.
| Cell | Now |
|---|---|
| Terraform, Plan and review | same code |
| Terraform, Modules | same code |
| Terraform, State and migration | same code |
| Terraform, Agents and pull request environments | same code |
| choudoufu, Modules | same code |
| choudoufu, Agents and pull request environments | same code |
| Terragrunt, Chat and notify | none |
| Atmos, Policy | none |
| Atmos, Drift | none |
| Atmos, Chat and notify | none |
| Atmos, Reports and visibility | none |
| Atmos, Modules | none |
| Atmos, Agents and pull request environments | none |
| Terramate, Policy | none |
| Terramate, Drift | none |
| Terramate, Chat and notify | none |
| Terramate, Reports and visibility | none |
| Terramate, Modules | none |
| Terramate, State and migration | none |
| Terramate, Agents and pull request environments | none |
| Terramate, Setup and runtime | none |
| CDK Terrain, Policy | none |
| CDK Terrain, Drift | none |
| CDK Terrain, Chat and notify | none |
| CDK Terrain, Reports and visibility | none |
| CDK Terrain, Modules | none |
| GitHub, Plan and review | passes |
| GitHub, Approve and apply | passes |
| GitHub, Policy | passes |
| GitHub, Drift | passes |
| GitHub, Chat and notify | none |
| GitHub, Reports and visibility | none |
| GitHub, Modules | passes |
| GitHub, State and migration | none |
| GitHub, Agents and pull request environments | passes |
| GitHub, Setup and runtime | passes |
| GitLab, Chat and notify | none |
Every check behind the grids is listed in the per-forge claims and the example’s checks below.
| Forge | What runs |
|---|---|
| Forgejo | a Forgejo server and its runner |
| GitHub | the generated workflow, run by act against a mock GitHub API |
| github.com | the published release’s generated workflow on GitHub-hosted runners, in a public plan-only repo |
| GitLab | GitLab CE and its runner |
The per-forge claims
Section titled “The per-forge claims”reconcile follows a control repo’s pull request until it is merged and applied. Pinned CI images run the tg- claims on a two-unit Terragrunt repo and the cdf- claims on a choudoufu estate.
The github.com claims run the newest published release against real GitHub pull request events and statuses, comments and reviews, and OIDC tokens. Every resource there is a terraform_data, so nothing needs a cloud account. The limits:
| Limit | What runs instead |
|---|---|
| no cloud role | oidc checks each job’s token against GitHub’s published keys, its repo, run, audience and role, and trades it with nothing; the trade with STS is proven on Forgejo |
| no bucket | report-keys, report-oidc and estate-job write to floci, the AWS emulator, run in the job beside a stand-in for STS that checks each token and role as oidc does |
| one GitHub account | the refusals of a commenter with no write access and of a pull request from a fork are proven on Forgejo; the apply-before-merge pull requests are opened by a workflow, so the account that runs the claims may approve them |
| no model key | the agent comment and the refused-wave job run a stand-in agent that makes one edit or prints the summary input |
| BREAK for the locking and report claims alone | pr-lock, pr-lock-fmt, apply-serial, pr-apply-lock, pr-apply-stale, report-keys, report-oidc and estate-job run a second time with their property broken (a lock dropped from chant/lifecycle, a guard cut from the committed pipeline, undiverged left out of apply.requires, or the job’s keys or token taken away); the other claims have no BREAK run, so they read “passes” |
Every per-forge claim
| Forge | Claim | What it shows | Result |
|---|---|---|---|
| AWS emulator | s3 | floci starts and answers S3 | proven |
| Forgejo | check | a push that is formatted goes green, and one with an unformatted file goes red naming the file | proven |
| Forgejo | apply | a push to main applies the root, and the bucket exists afterwards | proven |
| Forgejo | tg-check | the generated workflow for a Terragrunt repo fails an unformatted unit file and names it | proven |
| Forgejo | tg-apply | the generated workflow for a Terragrunt repo applies both of its units on the default branch | proven |
| Forgejo | cdf-check | the generated workflow for a choudoufu estate fails a resource that live-check refuses and names it | proven |
| Forgejo | cdf-apply | the generated workflow for a choudoufu estate applies it on the default branch, and the bucket carries the estate marker | proven |
| GitHub | check | the generated workflow fails an unformatted root and names the file | proven |
| GitHub | apply | the generated workflow applies the root on the default branch | proven |
| GitHub | reconcile | a control repo opens one pull request per project that changes, and the merged pipeline applies both roots | proven |
| GitHub | tg-check | the generated workflow for a Terragrunt repo fails an unformatted unit file and names it | proven |
| GitHub | tg-apply | the generated workflow for a Terragrunt repo applies both of its units on the default branch | proven |
| GitHub | cdf-check | the generated workflow for a choudoufu estate fails a resource that live-check refuses and names it | proven |
| GitHub | cdf-apply | the generated workflow for a choudoufu estate applies it on the default branch, and the bucket carries the estate marker | proven |
| github.com | affected | a pull request that changes one root plans that root alone, and its plan note covers it alone | passes |
| github.com | comment-plan | /terragucci plan on a pull request re-plans it in a run of its own, and the re-plan edits the plan note | passes |
| github.com | pr-lock | with locks: plan a pull request locks the root it plans, and a second one reaching that root fails terragucci/lock and is answered with the root and the holder | proven |
| github.com | policy | a pull request that replaces a table fails terragucci/plan under the Rego policy on main, and its plan note names the denial | passes |
| github.com | oidc | the plan job holds a GitHub-signed OIDC token for this repo and run, for the plan role, and the apply job on main one for the apply role; with no cloud account the token is checked, not traded with STS | passes |
| github.com | gate-wait | a merged destroy stops its wave with the approve command, and after a sealed chant approve the re-run applies it | passes |
| github.com | note-footer | the plan note ends with the terragucci footer, and its taco image answers 200 with a PNG | passes |
| github.com | tips | the plan note counts the tips the run report holds, and each tip in the report names its rule and its page | passes |
| github.com | comment-agent | a /terragucci agent comment pushes the commit of the stand-in agent onto the branch of the pull request, which plans again, and the reply links it; an ask whose change touches the pipeline is refused and nothing is pushed | passes |
| github.com | policy-override | a wave the policy denies applies once the approver policy.override lists overrides its plan with terragucci override, and the report names the override; an override by someone it does not list counts for nothing | passes |
| github.com | apply-serial | two merges pushed back to back apply in the order they arrived, the older run stands down once the newer push lands and the newer applies the tree, none is cancelled, and each commit ends with a terragucci/apply success | proven |
| github.com | comment-apply | /terragucci apply on a merged pull request applies it again from its merge commit, and while its wave waits it applies nothing and gives the approve command; on an open pull request it is refused | passes |
| github.com | approve-command | terragucci approve in a clone approves the waiting wave with no digest copied, and the re-run applies it; with --dry-run it records nothing | passes |
| github.com | drift-issue | the drift job opens the drift issue, a second run updates that issue, and a run that finds no drift closes it | passes |
| github.com | pr-apply-lock | with apply.when: pull-request a second pull request that reaches a root an open one applied is refused with the root and the holder named, and applies once the first is unlocked with /terragucci unlock | proven |
| github.com | pr-apply-stale | a comment on an approved pull request whose head is behind main is refused as not up to date, and nothing applies | proven |
| github.com | pr-apply | with apply.merge: auto a comment on an open, approved pull request applies its head in every wave, and pr-merge merges it with the job token | passes |
| github.com | pr-apply-token | with apply.merge: auto and merge_token_env the merge is made with that token, so the merge commit starts its own run on main | passes |
| github.com | publish | with modules.publish: git-tags a conventional commit to a module on main makes the publish job push the module tag | passes |
| github.com | rollout | once the roots pin that tag and the next version is published, terragucci rollout --mode apply opens one pull request for the canary wave that moves those pins alone | passes |
| github.com | explain-refusal | after a refused wave the explain-refusal job of the refused-wave guide runs, and its respond wave-refused step names the root that moved; a stand-in takes the place of the model step | passes |
| github.com | pr-lock-fmt | with locks: plan a pull request whose check job formats it, with a push that starts no workflow, still has its lock answered on the formatted head | proven |
| GitLab | check | the generated pipeline fails an unformatted root and names the file | proven |
| GitLab | apply | the generated pipeline applies the root on the default branch | proven |
| GitLab | reconcile | a control repo opens one merge request per project that changes, and the merged pipeline applies both roots | proven |
| GitLab | tg-check | the generated pipeline for a Terragrunt repo fails an unformatted unit file and names it | proven |
| GitLab | tg-apply | the generated pipeline for a Terragrunt repo applies both of its units on the default branch | proven |
| GitLab | cdf-check | the generated pipeline for a choudoufu estate fails a resource that live-check refuses and names it | proven |
| GitLab | cdf-apply | the generated pipeline for a choudoufu estate applies it on the default branch, and the bucket carries the estate marker | proven |
| GitLab | gl-check-token | with GITLAB_TOKEN an unprotected variable the synth command of a branch runs in the check job with no forge token variable, and the check passes | proven |
| GitLab | gl-managed-state | a root on GitLab-managed state applies with its backend password passed as TF_HTTP_PASSWORD from the job token, and GitLab holds its state | proven |
The example’s checks
Section titled “The example’s checks”These checks run against the tutorial’s example on a local Forgejo with OpenTofu. The GitLab column lists the ones that also run on a local GitLab CE and its runner. There, each gets a small repo holding the pipeline terragucci init writes for GitLab.
The Terraform and choudoufu columns list the core checks run again on that binary in its CI image. They use binary: in each repo’s terragucci.yml, on a copy of the repo written for the binary under names of its own:
| Binary | What the copy changes |
|---|---|
| Terraform | each root’s required_version, which pins OpenTofu’s release line, takes that release or later, and its .terraform.lock.hcl is the one Terraform writes, for Terraform’s registry |
| choudoufu | each root’s backend becomes a live block: an estate per root, its records in an S3 record store; a service reads the platform’s logs bucket through terraform_estate_outputs instead of terraform_remote_state, and its registration in that bucket is a terraform_data, since choudoufu admits no aws_s3_object; drift is left out, since it is a config error there, which the drift row shows |
Every check on the example
| Check | What it shows | Forgejo | Terraform | choudoufu | GitLab |
|---|---|---|---|---|---|
boot | the example boots and deploys locally | proven | proven | proven | |
check | tf-check fails an unformatted root and names the file | proven | |||
affected | only the roots a change touches are planned | proven | proven | ||
grouped | one note groups many plans | proven | |||
report | the report is JSON and HTML, and links every root to its full plan | proven | proven | proven | |
highlight | destroys and outliers are open, identical groups are folded | proven | |||
waves | each wave goes out only once approved | proven | proven | proven | |
refuse | a wave whose plans changed after approval applies nothing | proven | |||
sealed | under approval: sealed a wave counts only an approval sealed by a key the signers file lists | proven | |||
drift | drift is reported by root | proven | proven | provenwith binary: choudoufu and the roots under live resource markers, drift is a config error: init and stage tf-drift exit 2 naming the roots, before any plan | |
rollout | a module version rolls out one pull request per wave | proven | proven | ||
publish | changed modules are published at a new version | proven | proven | ||
publish-attest | with modules.attest each release is signed, attested and recorded in the release ledger with its tag | proven | |||
require-attested | with modules.require: attested tf-plan plans a root that pins an attested release, and refuses one whose tag was moved | proven | |||
require-recorded | with modules.require: attested tf-plan refuses a root that pins a version the release ledger does not record | proven | |||
tg-require-attested | with modules.require: attested in a Terragrunt repo tf-check and tf-plan refuse a unit whose terraform source pins an unattested release, and pass one that pins an attested release | proven | |||
module-registry | with modules.registry each release is written as the module registry protocol to a bucket, and a root that pins ~> 1.0 resolves the newest 1.x from it; with modules.test an untested release is refused | proven | |||
tips | tips are on by default and name their rule | proven | proven | ||
zero-config | with no more than a drift schedule and the canary wave in terragucci.yml, init writes the same pipeline | proven | |||
apply-serial | two pushes to main apply one after the other, and the commit carries one terragucci/apply status | proven | proven | proven | |
reconcile | a control repo opens one pull request per project that changes, and the merged pipeline applies | proven | |||
reconcile-mixed | a control repo with projects on Forgejo, on GitHub (the mock) and on a forge that does not answer opens the Forgejo pull request and the GitHub one, each with its own pipeline, names the project that failed, and exits 1 | proven | proven | ||
traces | each plan run is one trace, with a span per root and the binary spans inside it | proven | |||
metrics | the metrics of a plan run reach Prometheus with the counts in its report | proven | |||
tg-zero-config | init finds Terragrunt and its 15 units on its own and writes the pipeline the Terragrunt example commits | proven | |||
tg-waves | the Terragrunt example boots in five waves, one job each: the dependency layers of the dev canary, then the layers of staging and prod, each with one run --all | proven | |||
tg-check | tf-check fails an unformatted Terragrunt file and names it | proven | |||
tg-affected | only the units a change reaches are planned, including a file a module reads that Terragrunt misses | proven | |||
tg-mock-lint | a dependency whose mock_outputs can stand in for apply is named by a tip | proven | |||
tg-refuse | a unit that reads a new upstream is planned on the planned outputs of that upstream, never on its mock_outputs | proven | |||
tg-mock-trap | a new upstream and its dependent merge together and apply in order, so no mock reaches real state | proven | |||
tg-drift | drift is reported by unit in a Terragrunt repo, with the same tracking issue | proven | |||
respond-refused | a refused wave names each root whose plan moved and the attributes that moved | proven | |||
respond-triage | a failed apply is triaged from the known-error table | proven | |||
respond-drift | drift on a literal becomes a pull request with the live value, and import blocks for what is unmanaged | proven | |||
respond-tips | each tip becomes its own small pull request | proven | |||
respond-fmt | fmt on request commits to the pull request branch and nowhere else | proven | |||
respond-notes | release notes come from the conventional commits that touched the module | proven | |||
fresh-plan | on a fresh estate the plan job holds back a root whose upstream is unapplied, names it in the report, and stays green | proven | |||
forgejo-oidc | a Forgejo job gets an OIDC token Forgejo signed for its repo and ref, and trades it for the plan or apply role | proven | |||
policy | an opt-in policy denies a plan, fails the root in tf-plan, and names the violation | proven | proven | ||
comment-plan | a pull request comment re-plans on request and never applies, and a root outside the configured ones is refused | proven | |||
import-atlantis | terragucci import atlantis writes terragucci.yml from an atlantis.yaml, names what it leaves out, and the pipeline init then writes plans exactly the Atlantis projects | proven | |||
import-spacelift | terragucci import spacelift writes terragucci.yml from spacelift_* code and .spacelift/config.yml, with context secrets to create and hooks as steps, and init then applies the roots in the order the stack dependencies ask | proven | |||
import-env0 | terragucci import env0 writes terragucci.yml from env0_* code, env0.yml and env0-discovery.yml, and init then plans the environment roots, keeps ephemeral copies of the ones with a TTL, and holds every wave for an approval | proven | |||
import-hcp | terragucci import hcp reads the workspaces of an organization over the TFE API, only reading, and writes a root per workspace directory, lists a directory several workspaces run to split, names sensitive variables as secrets without their values, and init then applies in the order the run triggers ask | proven | |||
import-scalr | terragucci import scalr reads the workspaces of a Scalr account over the Scalr API, only reading, and writes their roots on tofu with the sensitive variables as secrets the pipeline passes, without their values, and names the policies of each OPA policy group | proven | |||
import-tg-scale | terragucci import terragrunt-scale writes the plan and apply roles of each Gruntwork Pipelines environment as terragrunt.credentials, and every unit then assumes the roles of its environment, a unit with its own gruntwork.hcl its own | proven | |||
waves-after | waves.after orders plain roots that read nothing of each other: network, database and app apply in three waves, and a pull request that changes network plans all three | proven | |||
comment-atlantis | with atlantis_comments on, atlantis plan re-plans a pull request, and atlantis apply and an Atlantis-only flag are refused as the terragucci forms are | proven | |||
lock-wait | a plan that waits for a state lock another plan holds shows the wait as a State lock wait span, in its report and its trace | proven | provenwith binary: terraform a tf-apply wave whose plan meets a state lock another Terraform plan holds waits for it under the default -lock-timeout terragucci adds, the root's plan time in the report covering the wait, and applies once the lock is released | ||
dash-pipeline | the Pipeline health dashboard init writes shows the runs, errors and results of a plan, a drift run and a gated wave | proven | |||
dash-changes | the Change review dashboard init writes shows the roots, groups and changes by action of a pull request | proven | |||
dash-waves | the Rollouts and waves dashboard init writes shows a wave waiting for its approval, how long, and wave runs by result | proven | |||
dash-drift | the Drift dashboard init writes shows the roots a drift run found drifted and how old the drift is | proven | |||
dash-estate | the Estate dashboard init writes shows the roots of a project and the binary and terragucci versions it runs | proven | |||
dash-runs | the Runs dashboard init writes shows the slowest roots, stage durations and the trace of each run from Tempo | proven | |||
dash-slos | the SLO dashboards init writes are provisioned, and the plan SLO records the plans of a project from the rules init writes | proven | |||
drill-down | the plan note links the report in the bucket, the report links each root plan and the trace of the run, the trace and the dashboards link back to the report, and the index row links the commit, pull request and job | proven | |||
policy-wave | a tf-apply wave whose plan the policy denies applies nothing, and its report keeps the changes of the denied root with the denial and the warnings | proven | proven | proven | |
check-diagnostics | tf-check fails with the cause in its log for a validate error, a failing policy test and a choudoufu live-check refusal | proven | |||
comment-not-affected | a re-plan comment for a root the pull request does not reach is answered that it is not affected and plans nothing, and a re-plan whose forge call fails fails its job with the cause | proven | |||
drift-attribute | with respond.drift: attribute, tf-drift lists who changed each drifted attribute under its root in the drift issue | proven | |||
version-bump-job | with respond.version-bump: suggest, the version-bump job of the pipeline runs after the last apply on the default branch and opens a release pull request with the answer of the decision service | proven | |||
tg-spans | the plan of each Terragrunt unit sends its spans to the report through the TG_TF_PATH wrapper, and waits up to five minutes for the state lock | proven | |||
oidc-clouds | a job with oidc.gcp and oidc.azure gets an external_account file and the ARM_* variables the google and azurerm providers read, with a token for the audience of each cloud | proven | |||
comment-apply | a comment on a merged pull request re-runs its apply from the merge commit, applies a wave under approval: sealed only once its approval is sealed, and refuses an open pull request and a commenter with no write access | proven | |||
comment-agent | a /terragucci agent comment pushes the commit of the stand-in agent to the branch of the pull request, which re-plans it and is linked in the reply, and a forbidden path, a non-writer and a fork push nothing | proven | |||
review-agent | with review.agent on, the review workflow of the default branch runs on pull_request_target after the plan, and its review job runs a stand-in reviewer on the pull request, its plan and the instructions of the default branch, with the key of the model and no forge token, and the note it posts flags a destroy the description does not mention and approves nothing | proven | |||
review-policy | a tf-apply wave gives the policy the risk the review workflow of the default branch kept as its artifact for the head of the merged pull request as input.review; the edited review job of the pull request itself keeping risk low and a forged low-risk note posted with the pipeline token change nothing, and a policy that denies risk high stops the wave | proven | |||
wave-report | the report of a tf-apply wave behind a gate says waiting and links the ledger that holds its record, and approved once an approval of its digest stands | proven | |||
policy-delete-key | a pull request that deletes the policy key from terragucci.yml and adds a change the policy denies still fails tf-plan, checked against the policy of the base branch | proven | |||
report-oidc | with no static keys, the plan job writes its report to the bucket as the role it assumes with its OIDC token through STS, and the index lists the run | proven | proven | ||
tg-gate-wait | a Terragrunt wave waits for an approval of its set digest, and once approved applies its saved plans while the next wave waits at its own gate | proven | |||
tg-gate-refuse | a Terragrunt wave whose plans changed after approval applies nothing and names the unit that moved | proven | |||
tg-sealed | under approval: sealed a Terragrunt wave counts only an approval sealed by a key the signers file lists | proven | |||
pr-apply | with apply.when: pull-request, a comment on an open and approved pull request applies its head in waves and then merges it with apply.merge: auto | proven | proven | proven | |
pr-apply-lock | a second pull request that reaches a root another open pull request has applied is refused with the root and the holder named, and applies once the first is unlocked with /terragucci unlock | proven | proven | ||
pr-apply-stale | a comment on an approved pull request whose head is behind the default branch is refused as not up to date, and nothing applies | proven | |||
tg-comment-apply | a comment on a merged pull request in a Terragrunt repo re-runs its waves of units from the merge commit, applies a wave under approval: sealed only once its approval is sealed, and refuses an open pull request | proven | |||
provider-calls | with binary: choudoufu the report lists the slowest provider calls of a root, each with its method, provider and resource type, from the provider call spans choudoufu sends | proven | |||
summed-timings | with binary: choudoufu past its span budget the report lists the timings choudoufu summed by resource type, and the note of the root says it summed them | proven | |||
foreign-checkout | a job that runs as root in the CI image on a checkout another user owns, with no git setting of its own, plans only the roots a change touches | proven | |||
tg-layers | a Terragrunt repo of three units in a chain goes out in three waves, one job each, every wave waiting for an approval of its own set digest before it applies | proven | |||
atmos-waves | init names one root per Atmos instance, <stack>/<component>, from atmos describe stacks, and cuts a wave per layer of dependencies.components: each dependent instance applies in the wave after its dependency, behind its own gate | proven | |||
atmos-workspace | each Atmos instance plans and applies in the Terraform workspace Atmos names for it, never default: the instances of one component in two stacks keep their own states, and plan no change after the apply | proven | |||
atmos-affected | a pull request that changes one Atmos instance plans that instance and the instances whose dependencies.components name it, and no other | proven | |||
atmos-from-plan | an Atmos wave whose plans changed after approval applies nothing and names the instance that moved; once its new plans are approved it applies the plans whose digest was approved | proven | |||
atmos-upstream-wait | an Atmos instance whose stack reads an unapplied instance with !terraform.state is held back, never planned on a stand-in, and applies on the output of the upstream once it has applied | proven | |||
atmos-check | the check job of an Atmos repo runs atmos validate stacks before it writes the instances, and fails on a manifest Atmos refuses, with the error Atmos gives | proven | |||
atmos-version | atmos.version in terragucci.yml is the Atmos release every job installs | proven | |||
terramate-waves | init takes the stacks of a Terramate repo as roots and cuts a wave per layer of their order: a stack after a tag or before another applies in the wave its order gives it, behind its own gate | proven | |||
terramate-affected | a pull request that changes one Terramate stack plans that stack and the stacks whose after or before put them after it, and no other | proven | |||
terramate-pr-apply-lock | with apply.when: pull-request in a Terramate repo, a pull request applied on a comment locks the stack it changes, and a second pull request that changes that stack is refused with the stack and the holder named, its state left as the first applied it | proven | |||
terramate-sharing | a Terramate stack whose input block reads an output of an unapplied stack is held back, never planned on the mock, and applies on the output of the upstream once it has applied | proven | |||
terramate-stale | the check job of a Terramate repo fails on stale generated code, naming the file terramate generate would change | proven | |||
atmos-roles | oidc.roles by stack glob gives the instances of each Atmos stack roles of their own: config check lists each role with the states of its stack, and each instance plans and applies as the role of its stack | proven | |||
atmos-pr-apply-lock | with apply.when: pull-request in an Atmos repo, a pull request applied on a comment locks the Atmos instances it reaches (every instance, for a change to a stack manifest), and a second pull request that changes one is refused with the instance and the holder named, its state left as the first applied it | proven | |||
atmos-refuse | config check and init refuse the drift pull request of an Atmos repo in the same words, about the vars of the stack, never synth; respond tips proposes lock files for the components and a canary of instances | proven | |||
policy-source | a project of a control repo with no policy directory is checked against the shared policy source the control repo defaults name, at its pinned ref | proven | |||
reconcile-parallelism | a project of a control repo plans with the parallelism its defaults set: reconcile writes the key into the terragucci.yml of the project, and the plan job reads it there | proven | |||
provider-project | the terragucci provider, applied with tofu, writes a project and the defaults into the terragucci.yml of a control repo, plans show a changed setting, and reconcile gives the project its pipeline with the setting | proven | |||
pr-requires | with apply.requires: [approved] an approved pull request behind the default branch applies from its head, the default requirements refuse it as not up to date, and a pull request that conflicts with the default branch is refused as not mergeable | proven | |||
pr-lock | /terragucci lock on an open pull request locks the roots it reaches and applies nothing, and a second pull request that reaches one is refused with the root and the holder named | proven | |||
front-door | the front door template puts CloudFront in front of the private reports bucket at its own domain, reads the bucket through Origin Access Control and runs the sign-in check on every viewer request | proven | |||
ledger-default | with no approval key a wave counts an unsigned approval of its set digest, init declares no gate, and the waiting wave prints the approval command without --sign | proven | |||
approval-at-base | a merge that switches approval from sealed to ledger is judged by the sealed rule of the commit before it, and the next merge by ledger | proven | |||
sealed-migrate | a repo whose chant.workspace.json lists the wave gates and whose config names no approval mode stays sealed, and the wave and config check say so | proven | |||
estate | terragucci estate writes one page to the reports bucket from the index of every project: three projects, a waiting wave with its age and a drift, with a presigned link to the page | proven | |||
behold-view | the behold step of the estate job reads the reports from the bucket and publishes the view of the commit under views/behold/<commit>/ with latest.json and history.json naming it, and headless Chrome, opening views/behold/ from the bucket, draws the drift the drift check found on the card of the deleted queue | proven | |||
behold-serve | behold serve, given the checkout and the reports bucket, draws both roots, marks the queue deleted outside Terraform with the time the drift check finished, gives the terragucci approve line of the waiting wave and refuses a deploy, and refuses a corrupted index.json | proven | |||
terragucci-view | terragucci view, reading the bucket and project from terragucci.yml, starts the pinned behold, which draws both roots, marks the queue deleted outside Terraform with the time the drift check finished and gives the terragucci approve line of the waiting wave, and stops on SIGTERM; a corrupted index.json stops it with one line | proven | |||
drift-overdue | the plan of a pull request says in its note that drift checks are overdue when the drift schedule has come round twice with no drift run | proven | |||
pr-review | with approval: pr-review a pull request approved on its head by a writer other than its author merges, and its gated wave applies with no chant approve, recorded on the ledger as via pr-review | proven | |||
pr-review-moved | with approval: pr-review a wave whose plans changed between the review of the head and the merge applies nothing and prints the chant approve command for its new digest | proven | |||
pr-review-status | with approval: pr-review terragucci/approval on the head of a pull request is pending while a wave waits, and success once a writer other than the author approves the head | proven | |||
cdf-concurrency | with binary: choudoufu two tf-apply waves of one estate that change different resources run at once, both reach their record write together and both apply, with no lock wait and no lock object | proven | |||
cdf-concurrency-time | with binary: choudoufu two tf-apply waves of one estate, each adding a resource whose apply takes 60 seconds, finish both resources in under 90 seconds: the second wave starts while the first is applying and does not wait for it | proven | |||
cdf-write-race | with binary: choudoufu two tf-apply waves of one estate that change the same resource at once: one lands, the other fails its conditional write naming the resource and overwrites nothing, and its re-plan shows the value that landed | proven | |||
cdf-killed-records | with binary: choudoufu a tf-apply wave killed after the apply of one resource returned, while the next one applies, leaves a record for the first and none for the second, and the next plan creates the second only | proven | |||
cdf-resume | with binary: choudoufu a gated wave killed after its approved apply of one resource returned is found by terragucci resume, and the wave run again applies only the other resource under the same approval, with no new one | proven | |||
cdf-live-progress | with binary: choudoufu while a tf-apply wave applies, its run view and its progress.json show the resource whose apply returned done and the slow one after it in flight, read from the record store | proven | |||
cdf-iam | with binary: choudoufu a role granted one estate by its ownership tag applies a change to that estate, and IAM refuses it a change to an instance of another estate | proven | |||
apply-per-root | a second push applies one root while the wave of the first push is still applying another: no apply job waits for another run, and the state lock of the backend keeps the applies of one root apart | proven | |||
apply-stand-down | a wave of a push whose commit is no longer the tip of main stands down once the newer push has landed: it applies nothing, its terragucci/apply says superseded by a newer push, and the newer push applies the root | proven | |||
cdf-rows-overlap | with binary: choudoufu two pushes whose plans change different resources of one estate apply at the same time: each wave holds the resource it changes, both reach their record writes together, both apply, and no row is left held | proven | |||
cdf-rows-wait | with binary: choudoufu two tf-apply waves change the visibility timeout of one SQS queue on floci: the second waits for the first and makes no call while the call of the first is in flight, as the request log of the emulator shows, then plans again and applies its value | proven | |||
cdf-drift | with binary: choudoufu a queue changed outside choudoufu on a root under live resource markers is drift: tf-drift plans the root in full, its report and one drift issue name the root and the attribute, and a run with no change closes the issue | proven | |||
cdf-rows-takeover | with binary: choudoufu a wave killed after its change landed, while it held the queue it changes, leaves its row held; the wave of the next push takes it over with nothing unlocked, and the re-read of its apply refuses the plan the killed run made stale before any call | proven | |||
resume-approve | terragucci approve, given a forge token of the approver, resumes the waiting wave of a merged pull request, which applies with nothing else done | proven | |||
resume-schedule | with apply.resume set, a run of the resume workflow applies a waiting wave once an approval of its digest is on chant/lifecycle | proven | |||
approve-plan | terragucci approve --plan with a digest the plans moved past approves nothing, exits 1 and names the digest waiting | proven | proven | proven | |
approve-command | the plan note of a pull request gives the chant approve command with the digest its gated wave asks for after the merge, and terragucci approve in a checkout approves that wave with no digest copied | proven | |||
tg-pr-apply | with apply.when: pull-request in a Terragrunt repo, a comment on an open and approved pull request applies its waves of units from its head and then merges it with apply.merge: auto | proven | |||
tg-pr-apply-lock | in a Terragrunt repo, a pull request that changes a unit whose dependencies block names a unit another open pull request applied is refused with the unit and the holder named | proven | |||
plan-lock | with locks: plan a pull request locks the roots it reaches from its first plan, a second pull request that reaches one gets a failing terragucci/lock and a reply naming the root and the holder, and after /terragucci unlock its /terragucci plan takes the lock | proven | |||
plan-lock-release | with locks: plan the merge of a pull request releases the lock its first plan took | proven | |||
plan-lock-fmt | with locks: plan a pull request whose push the fmt job formats, with a push that starts no run, has its lock answered on the formatted head | proven | |||
policy-override | a tf-apply wave the policy denies applies once an approver listed under policy.override at base overrides its plan with terragucci override, and its report names the override with who, the rules, the reason and the plan digest | proven | proven | ||
policy-override-moved | an override of an earlier plan digest counts for nothing: once the root plans another digest the wave applies nothing and exits 4 | proven | |||
policy-override-unlisted | an override by someone policy.override at base does not list counts for nothing: the wave applies nothing and names why | proven | |||
policy-override-applied | once a wave applied a root under its override, the next plan of that root the policy denies is recorded as a new denial and waits for its own override, and only an override no wave applied refuses | proven | |||
blob-azure | with reports.bucket az://<account>/<container> and only the Azure OIDC identity of the job, tf-plan writes the report and both indexes to Azure Blob Storage, and terragucci estate writes the page there and prints a user delegation SAS that serves it | proven | |||
blob-gcs | with reports.bucket gs://<bucket> and only the GCP OIDC identity of the job, tf-plan writes the report and both indexes to GCS through its JSON API, and terragucci estate writes the page there and prints a V4 signed URL that the service account signed | proven | |||
note-diff | the plan note on Forgejo shows the diff of a group as the binary prints it, with the value before and after of the attribute that changes, and the whole plan of the root in a collapsed block | proven | |||
note-split | a plan over the comment limit of the forge stays one note within the limit: the largest whole plan is left out and named in a Cut line that links its plan.txt, and the rest stays | proven | |||
note-report-link | with reports.bucket set and no reports.url, the plan note links report.html and each plan.txt in the bucket by presigned links, which open from floci | proven | |||
config-ts | init writes the pipeline from a terragucci.ts folded as data, and config check refuses a terragucci.ts that reads process.env at its line | proven | |||
role-refused | config check and init refuse an oidc block that names one role for plan and apply | proven | |||
description-check | with respond.description: check the plan job flags a destroy the pull request description leaves out, at the top of the note and in the report, and writes intent.json | proven | |||
decide-backends | decide.backend von, decider and jev each answer the description check through the same client, each pinned to its model, jev with its bearer token from token_env | proven | |||
otlp-headers | telemetry.headers_secret maps the collector key into the jobs, spans reach a collector that wants it, and a collector that does not answer leaves the plan green | proven | |||
pinned-install | a pinned binary version the image does not carry is installed in the job and checked against the SHA256SUMS of its release | proven | |||
generate | terragucci generate writes the backend, provider and version files of each root from the repo, directory glob and root levels of terragucci.yml, a changed global reaches the backend file of every root, and tf-check refuses a hand-edited generated file | proven | |||
tg-generate | in a Terragrunt repo terragucci generate writes terragucci.hcl from terragucci.yml, the check step passes it, and each unit that includes it applies with the backend key and provider settings generate gives it, while a unit that does not include it is refused by name | proven | |||
tg-generate-init | with generate.disable_init: false terragucci generate writes disable_init = false in the remote_state of terragucci.hcl, the check step passes it, and the apply job has Terragrunt create the missing state bucket and applies into it | proven | |||
root-pins | two roots of one wave plan on two OpenTofu versions, the one the .opentofu-version of a root pins, installed in the job and checked against its SHA256SUMS, and the one in the image, and the report and the plan note name the binary and version of each root | proven | |||
drift-close | a drift run that finds no drift closes the drift issue an earlier run opened | proven | |||
estate-control | terragucci estate in a control repo reads each project from its own bucket with its own reports.role and writes one page to the bucket under defaults | proven | |||
estate-override | the estate page counts the roots applied under a policy override, in estate.json and estate.html | proven | |||
reader-contracts | the index.json files, estate.json and report.json a run and terragucci estate write to the bucket hold to the JSON Schemas the package ships, and a project named views writes nothing under the prefix kept for viewers | proven | |||
comment-refused | a comment naming approve, merge, destroy, import, state or force-unlock is answered that a comment never runs it, and nothing plans or applies | proven | |||
note-stale | a push to the default branch that changes a root an open pull request planned marks its plan note stale | proven | |||
pr-confirm | with apply.when: pull-request the push of the merge commit runs confirm, which plans every root, posts terragucci/apply success and applies nothing | proven | |||
pr-base-config | with apply.when: pull-request a pull request that sets gate: never still waits at the gate of the default branch | proven | |||
pr-guard | with apply.when: pull-request a pull request that changes the pipeline file, or whose checks failed, is refused and applies nothing | proven | |||
pr-close-release | with apply.when: pull-request closing a pull request releases the roots it locked | proven | proven | ||
tg-lock-fanout | in a Terragrunt repo a change to root.hcl locks every unit and says why, and a Markdown-only change locks none | proven | |||
token-scrub | the binary tf-plan starts gets no forge token by name or by value, and a TF_ variable passes as set | proven | |||
plan-token-free | the plan and re-plan jobs run the code of a pull request with no forge token variable in its environment or that of its parent processes and no credential in the checkout, and the note jobs still post the note and terragucci/plan | proven | |||
fork-no-plan | a pull request from a fork runs check and no plan job | proven | |||
highlight-sensitive | IAM, security group, KMS and DNS changes are open with their reasons, and an import and a forget are named, the forget not counted as a destroy | proven | |||
approval-revoke | removing an approval line from chant/lifecycle makes its wave wait again | proven | |||
pending-expiry | a pending fact past its 48 hours is recorded afresh by the next run of its wave | proven | |||
signer-trust | init --signer writes the signers line from git config user.signingkey, .chant/trust.json moves the signers file, and a sealed approval verifies against it | proven | |||
rollout-control | from a control repo a module rollout opens wave 1 as one pull request per project for its canaries, then each project in turn, each wave once the last applied | proven | |||
rollout-provider | rollout --provider moves that provider alone in the lock file and its exact constraint, one pull request per wave | proven | |||
rollout-pins | rollout moves an oci:// tag and a registry version pin, each in the shape it had | proven | |||
rollout-continue | with rollouts set, once wave 1 merged and applied the rollout job opens wave 2 with no manual run | proven | |||
policy-opa | with policy.engine: opa a tf-apply wave the policy denies applies nothing, and its report keeps the denial and the warnings | proven | |||
policy-hcp | with policy.input: hcp an HCP Terraform policy reads input.plan and input.run and denies the wave | proven | |||
policy-hcl | with a policies.hcl a mandatory policy denies the wave and an advisory one warns | proven | |||
tg-policy | in a Terragrunt repo a unit the policy denies fails tf-plan, and its wave applies nothing | proven | |||
tg-credentials | in a Terragrunt repo each unit assumes the plan role of the first glob its path matches, and a unit with its own iam_role keeps it | proven | |||
tg-dependents | terragrunt.dependents: plan previews the dependents of a change provisional and outside every digest, and terragrunt.exclude leaves a unit out | proven | |||
tg-preview | a pull request previews the later layers of a Terragrunt change: a unit is planned on the planned outputs of its upstream, and one that reads a value known only once its upstream applies is named with that value and wave, never planned on a stand-in | proven | |||
tg-preview-gate | a Terragrunt wave whose plan differs from the preview of it in the pull request says so at its gate, naming what moved, before anyone approves | proven | |||
tf-terraform | with binary: terraform the pipeline runs in the terraform image, check and the plan pass, and a wave waits for its approval and then applies with Terraform | proven | |||
tg-terraform | in a Terragrunt repo with binary: terraform the pipeline installs Terraform and Terragrunt applies every unit with it | proven | |||
tfquery-import | with binary: terraform a root with a .tfquery.hcl gets the drift pull request with the config terraform query generated for what it lists | proven | |||
alerts-fire | with short thresholds every alert init writes fires on its signal, and the apply-success and drift-corrected SLOs record | proven | |||
blob-gcs-key | with a service_account key file the job writes the report and both indexes to GCS, and the estate link is signed with the key | proven | |||
blob-azure-key | with AZURE_STORAGE_KEY the job writes the report and both indexes to Azure Blob Storage, and the estate link is a SAS signed with the account key | proven | |||
index-writes | two plan runs that write one index at once both land in it, and a store that answers 501 to a conditional write gets the row without the condition | proven | |||
note-footer | the plan note on a pull request ends with the terragucci footer, Forgejo renders its taco image, and the image answers 200 with a PNG | proven | proven | ||
replan-dispatch | a workflow_dispatch of the plan workflow with pr set re-plans that pull request: the replan job posts a new terragucci/plan status on its head and updates the same plan note, and Forgejo refuses the dispatch of a user with read access only | proven | |||
cdf-shared-bucket | with binary: choudoufu one tf-apply wave applies two estates into one record store bucket, each under its own prefix and estate tag, and the next plan of both shows no change | proven | |||
cdf-migrate | with binary: choudoufu a migration moves a resource between two estates by rewriting its ownership tags: proved by both plans, approved by digest, after which the next plan of both shows no change and the move is on chant/lifecycle | proven | |||
cdf-adopt | with binary: choudoufu a migration adopts a root on s3 state into an estate: proved against the live system, approved by digest, its resources stamped with no change, and its old state left as the version it was | proven | |||
cdktn-synth | with synth set to npx cdktn synth the pipeline synthesizes the CDK Terrain stacks before check, apply and tf-plan, and tf-plan plans the stack the change reaches | proven | |||
apply-outcome | stage tf-apply writes how its wave ended to TG_OUTCOME_JSON as terragucci.outcome/v1: waiting with its digest, mode and approve command, refused with the digest approved and the root that moved, and failed with the root | proven | |||
audit | terragucci audit writes one record to the bucket: every approval on the ledger with its approver, digest and time and who relayed it, the request, and the apply that names its approval; --check passes and the estate page links the audit page | proven | proven | proven | |
audit-override | the audit record keeps a policy refusal after its report is replaced, and holds the override with its reason and rules and the apply under it | proven | |||
audit-refused | a wave whose plans changed after approval is in the audit record as refused, with the approver, the digest approved and the root that moved | proven | |||
audit-control | terragucci audit in a control repo fetches each project ledger from its url and reads each project reports into one record | proven | |||
inventory | after two apply waves of the example roots the estate page lists every resource of each root by address, type and provider, with the count of each type, and no value | proven | proven | proven | |
estate-graph | the estate page draws the example roots by wave with an edge for each state the example reads and an edge from a root of another project that reads one, and the run view shows the blast radius of wave 1 and a timeline of the plan, gate wait and apply of each wave | proven | proven | proven | |
resource-history | one resource changed by three approved applies has a history that lists the three in order with their approvers from the audit trail, linked from the estate page, and no value | proven | proven | proven | |
query-sql | terragucci query, run on the reports bucket with no server, answers the SQS queues changed in the last 7 days with their approvers with the rows the audit trail holds | proven | |||
state-versions | a root whose state is in a versioned S3 bucket applies twice, and the estate page lists both state version ids newest first, each one the bucket holds, and no state content | proven | |||
state-roles | with oidc.roles each environment root plans and applies as the role of its own environment, config check lists the state key of each role, and it warns when a prod root reads the dev state | proven | |||
state-export | terragucci state export records a request, waits for an approval by someone else, then writes the state version on the machine of the person who asked, recorded on chant/lifecycle and in the audit trail, with no state in the bucket | proven | |||
gcs-state | a root whose state is in a bucket on fake-gcs-server with object versioning applies twice, each wave naming the generation it left, and terragucci state export writes the first generation once someone else approved the request | proven | |||
azurerm-state | a root whose azurerm backend on Azurite takes snapshots applies twice, each wave naming the snapshot it took of the state blob, and terragucci state export writes the first snapshot once someone else approved the request | proven | |||
gcs-unlock | terragucci unlock-state reads the lock a killed apply left on a gcs backend, by the generation of the lock object, and releases it only after an approval of that lock, recording who released it | proven | |||
azurerm-unlock | terragucci unlock-state reads the lease a killed apply left on an azurerm state blob, with the lock info in its metadata, and releases it only after an approval of that lock, recording who released it | proven | |||
blob-migrate | a migration moves a resource from a root whose state is on GCS to one whose state is on Azure Blob Storage: approved by digest, written under the lock object and the blob lease, and each version before and after recorded | proven | |||
cloud-roles | with oidc.gcp.roles and oidc.azure.roles, the roots of each environment plan and apply with the service account and the client of their glob, from the exports of the pipeline, and never the identity of the job | proven | |||
state-edges | the estate page lists a root that reads the state of another with its last plan against the last apply of the producer: stale after the producer alone applied, current once the consumer planned again | proven | |||
migrate-resume | with apply.resume set, a migration that waits in wave 1 of a Forgejo run is approved with terragucci approve and no argument, and one run of the resume workflow writes both states and applies, with nobody running wave 1 again | proven | |||
migrate-backend | a migration moves the state of a root to a new bucket: proved with no change, approved by digest, written under both lock files, the old state left where it was, and both versions recorded | proven | |||
migrate-backend-tfe | a migration moves the state of a workspace from a TFE API, the protocol of the remote backend, to a bucket: read through discovery, proved with no change, approved by digest, written under the workspace lock, the versions of the workspace left as they were, and the version it read recorded | proven | |||
migrate-backend-pg | a migration moves the state of a root between two pg backends: read with state pull, proved with no change, approved by the digest of its contents, written by the binary under the advisory lock, the old row left as it was | proven | |||
migrate-backend-k8s | a migration moves the state of a root between two kubernetes backends: read with state pull, proved with no change, approved by the digest of its contents, written by the binary under its Lease, the old Secret left as it was | proven | |||
migrate-backend-consul | a migration moves the state of a root between two consul paths: read with state pull, proved with no change, approved by the digest of its contents, written by the binary under its session lock, the old key left as it was | proven | |||
migrate-backend-http | a migration moves the state of a root between two http backend addresses: read with state pull, proved with no change, approved by the digest of its contents, written by the binary under the lock of the server, the old state left as it was | proven | |||
migrate-revert | terragucci migrate revert writes the migration that puts back the states a split wrote, and once approved it restores each state to the version the split recorded before, refused when a state moved past the version the split left | proven | |||
migrate-resume-never | with gate: never and apply.resume set, init still writes the resume workflow, and one run of it applies an approved migration that waits in wave 1 | proven | |||
migrate-split | a migration file splits one root into two: the plan proves it with no change, wave 1 waits for its digest, and once approved writes both states under their locks with no change, recording each version before and after | proven | |||
dora | terragucci estate computes the four DORA metrics from the audit trail and the indexes into dora.json and the estate page: deployments, lead time with the share at the gate, a change failure rate counting a failed apply and an applied wave that drifted, and the time to restore each | proven | |||
notify-chat | with notify naming a Slack and a Teams webhook secret and approval: pr-review, a wave of a merged pull request that waits posts the wave, its root, the digest, the approve command, the run and a link to review the pull request to each, and once that review lands the next run applies it | proven | |||
notify-webhook | with notify naming a generic webhook and its key, a wave that waits posts a terragucci.notify/v1 event signed with HMAC-SHA256 over its body, carrying the outcome, digest and approve command | proven | proven | proven | |
cost-estimate | with cost set, the plan note of a pull request gives the monthly cost change of each root and the total, from the estimator run with the key the plan job gets from its secret | proven | |||
cost-gate | with cost.approve_above set, a wave whose monthly change is over the amount waits for an approval under gate: never, its log naming the change, the amount and the commit read, and the plan note of a pull request sets the change of each wave against the amount at base | proven | proven | proven | |
cost-policy | with cost set, an HCP Terraform policy set reads the cost of the root from input.run.cost_estimate and of its wave from input.cost, and its mandatory policy denies the wave | proven | |||
approval-used | once a wave applied under its approval, the next merge that moves its plans waits with the approve command for the new digest, and only an approval of plans that never applied refuses | proven | |||
cdktn-affected | with synth set a pull request that changes one CDK Terrain stack plans that stack alone, and the plan note says how many stacks were unchanged | proven | |||
cdktn-apply | with synth set each apply wave synthesizes the CDK Terrain stacks and applies its stack behind the gate: dev once wave 1 is approved, prod once wave 2 is | proven | |||
cdktn-pr-apply-lock | with synth and apply.when: pull-request, a pull request applied on a comment locks every CDK Terrain stack, since its change to the app can reach any, and a second pull request that changes a stack is refused with the stack and the holder named, its state left as the first applied it | proven | |||
cdktn-tips | with synth set the tips job synthesizes the CDK Terrain stacks and opens the canary tip, and says the pin and lock file tips are left out | proven | |||
cdktn-migrate | a migration moves a resource between two CDK Terrain stacks, whose roots are cdk.tf.json: tf-plan proves it with no change, wave 1 waits for its digest, and once approved writes both states under their lock files | proven | |||
cdktn-refused | with synth set init refuses the drift pull request and rollouts as config errors saying why, and with respond.drift: attribute the drift job runs no pull request | proven | |||
apply-branches | with apply.branches mapping release to canary/*, a push to main applies the fleet roots behind the gate and never canary/one, and a push to release applies canary/one alone, waiting at the same gate until its wave is approved | proven | |||
apply-branches-drift | with apply.branches mapping release to canary, the drift run on main plans canary from release, the branch that applied it, and finds no drift where planning it from main would read the state release left behind and report a false drift | proven | |||
own-jobs-kept | with own_jobs naming a file of jobs in terragucci.yml, init run twice keeps the job in the Forgejo pipeline as the file has it, and the job runs after the check job and passes | proven | |||
wave-jobs | with waves.jobs: 2 a wave of four roots waits at one gate in its own job, and once approved applies in two share jobs of two roots each, under one approval used once | proven | |||
steps-before-plan | a step before plan writes a file the plan reads, read from terragucci.yml at base, and the plan note lists the step | proven | |||
steps-stop | a step before apply that exits 1 fails the wave job before anything applies | proven | |||
steps-gate | a step with on_failure approve that fails holds its wave at the gate under gate never, and an approval of the digest applies it | proven | proven | proven | |
tg-cost-gate | with cost.approve_above set, a Terragrunt wave whose saved unit plans are priced over the amount waits for an approval under gate: never, while a wave within it applies | proven | |||
tg-steps-gate | a step after plan with on_failure approve runs in the unit its glob picks after the run --all plan of the wave, holds the Terragrunt wave at its gate under gate never, and an approval of the digest applies it | proven | |||
tg-wave-jobs | with waves.jobs: 2 a Terragrunt wave of two units waits at one gate in its own job, and once approved each share job plans and applies its own unit with run --all --filter, under one approval used once | proven | |||
tg-respond-fmt | in a Terragrunt repo the fmt job runs terragrunt hcl fmt after a branch fails its check and pushes the formatting commit to the branch, and nowhere else | proven | |||
tg-state-versions | a Terragrunt unit whose state is in a versioned S3 bucket applies twice, its backend read from its remote_state block, and the estate page lists both state version ids newest first, each one the bucket holds | proven | |||
tg-drift-pr | a drifted Terragrunt unit gets a pull request that writes the live value into its own terragrunt.hcl inputs and leaves the module its units share alone | proven | |||
tg-drift-attribute | with respond.drift: attribute in a Terragrunt repo, tf-drift lists who changed each drifted attribute under its unit in the drift issue | proven | |||
tg-migrate-split | a migration file moves a resource from the state of one Terragrunt unit to that of another: the plan proves it with no change, wave 1 waits for its digest, and once approved writes both states under their locks, recording each version before and after | proven | |||
tg-root-pins | three Terragrunt units of one wave plan with their own releases: the tofu the .opentofu-version of a unit pins and the Terragrunt its terragrunt_version_constraint pins, each installed and checked in the job, and the releases of the image for the third, and the report names each | proven | |||
tg-choudoufu | in a Terragrunt repo with binary: choudoufu the jobs run in the choudoufu image with Terragrunt installed beside it, and every unit plans with choudoufu through TG_TF_PATH | proven | |||
tg-estate-graph | in a Terragrunt repo the plan note gives the blast radius of a changed unit through the units that depend on it, and the run view and the estate graph hold each unit by wave with an edge for each dependency block | proven | |||
tg-state-export | terragucci state export of a Terragrunt unit prepares it through Terragrunt, asks for the version of the state its remote_state block names, and once someone else approved it writes that version on the machine of the person who asked, recorded on chant/lifecycle | proven | |||
tg-apply-branches | with apply.branches mapping release to live/canary/*, a push to main applies the fleet units behind the gate and never live/canary/one, and a push to release applies live/canary/one alone, waiting at the same gate until its wave is approved | proven | |||
tg-stacks | the units of an explicit stack are generated before discovery, cut into waves by their dependencies, and applied in order from a checkout that holds none of them | proven | |||
tg-stack-check | the check job of a Terragrunt repo generates its explicit stack and validates the generated units, failing on a unit whose template reads a value the stack file does not feed it, and naming that unit | proven | |||
tg-stack-affected | a pull request that changes one value in a terragrunt.stack.hcl plans only the unit that value feeds, says so, lists the unit that depends on it as waiting, and the report and note name the stack file | proven | |||
tg-stack-gate | the waves of the units of an explicit stack wait at their gates: the first unit applies only once its wave is approved, and the unit generated beside it waits at its own gate | proven | |||
tg-stack-drift | tf-drift from a checkout that holds no generated unit generates the explicit stack, reports the resource deleted outside Terraform under its generated unit, and names the stack file that unit comes from | proven | |||
chat-approve | a click on the Approve button of the Slack message of a waiting wave, signed with the signing secret of the app, reaches the relay, which maps the Slack user to their principal in the signers file, records the approval of that digest as them and says so in the thread; the resume workflow then applies the wave | proven | |||
chat-approve-lambda | the relay built as the AWS Lambda function of the guide, run under the Lambda runtime interface emulator, takes a signed Slack click as a function URL event and records the approval of that digest as the mapped principal; the resume workflow then applies the wave | proven | |||
chat-approve-teams | a Teams reply that approves a waiting wave, signed as an outgoing webhook signs it, reaches the relay, which maps the Teams user to their principal in the signers file and records the approval of that digest as them; the resume workflow then applies the wave | proven | |||
chat-replan | with notify naming a Slack webhook, a drift run that finds drift posts the drifted root with a Re-plan button that opens the drift workflow, which runs on workflow_dispatch | proven | |||
linked-plan | a root that reads the state of another plans in tf-plan on the planned outputs of that root, unknown where unknown, and its wave is marked to plan again once the upstream applies | proven | |||
linked-plan-local | two roots on the local backend, one reading the other through terraform_remote_state by the same path: terragucci orders them into two waves on its own, and the reader plans on the planned outputs of the writer | proven | |||
resource-blast | a pull request that changes a queue gets a plan note whose blast radius lists, under the queue, its policy in the same root and the function and mapping that read its ARN in another root, and leaves out the log group that reads only another output | proven | |||
linked-states | after a pull request changes an output of wave 1, wave 2 plans again once wave 1 applied, shows the new value, and waits for an approval of that plan; the run view in the bucket shows where each wave stands | proven | |||
plan-no-lock | a pull request plan and a drift run plan a root while an apply holds its state lock, and neither waits for it | proven | |||
sensitive-redacted | a change to a sensitive variable and a sensitive output keeps both values out of the plan note, the report, the job log and every object in the reports bucket | proven | |||
provider-cache-once | a wave of eight roots that use one provider downloads it once: the job log names one download and the cache volume holds one copy | proven | |||
unlock-state | terragucci unlock-state refuses to release a state lock while a run that began before it is alive, and once the apply that held it is killed releases it only after an approval of its lock ID, recording who released which lock, and the next wave applies | proven | proven | provenwith binary: choudoufu and a root under live resource markers, terragucci unlock-state finds no state file and no state lock: it says there is nothing to release, runs no binary, asks for no approval and records nothing | |
tip-moved | a resource renamed on a branch plans as a destroy and a create, the plan report tips the moved block, respond tips opens a pull request into the branch that adds it, and once merged the plan moves the resource and destroys nothing | proven | |||
mcp-last-apply | an MCP client of terragucci mcp, which reads the reports bucket with the credentials of its environment, reads the last apply of a root, and the server lists only read-only tools and refuses an approve call and a token argument | proven | |||
drift-agent | with agent.drift on, a drift run that opens the drift issue runs the stand-in agent with no forge token in its step, and the push job opens a pull request with its change, which plans like any other and is linked on the issue | proven | |||
tg-pr-plan | a pull request on the Terragrunt example, five waves and the tips job below the check job, gets its plan note on Forgejo | proven | |||
github-drift-issue | on GitHub, a drift run opens the drift issue, and a second run finds it and updates it instead of opening another | proven | |||
runner-nudge | on the validation stack a job left waiting after the run ahead of it in its concurrency group is cancelled, with nothing running, starts within three minutes: a wait restarts the idle runner | proven | |||
ephemeral-pr | with ephemeral naming canary/*, opening a pull request applies its own copy of canary/one under the state key suffixed -pr-<n>, beside the state of the root itself, and closing it destroys the copy through a planned destroy recorded on chant/lifecycle with the reason closed | proven | |||
ephemeral-ttl | with ephemeral naming canary/* and a TTL of one minute, a run of the sweep workflow once the TTL has passed destroys the copy of an open pull request through a planned destroy recorded with the reason expired | proven | |||
tg-ephemeral-pr | in a Terragrunt repo whose remote_state key reads TERRAGUCCI_EPHEMERAL_SUFFIX, opening a pull request applies its own copy of a unit at the key suffixed -pr-<n>, prepared through Terragrunt, beside the state of the unit itself, and closing it destroys the copy on the record | proven | |||
cdktn-ephemeral | with synth set, opening a pull request runs the synth command in its checkout and applies its own copy of a CDK Terrain stack at the key suffixed -pr-<n>, beside the state of the stack itself, and closing it destroys the copy on the record | proven | |||
cdktn-generate | with synth set init and terragucci generate refuse a generate key as a config error that names the CDK Terrain constructs that set a backend | proven | |||
cdktn-linked | a CDK Terrain stack that reads the state of another through a remote state data source plans in tf-plan on the planned outputs of that stack, read from its cdk.tf.json, unknown where unknown | proven | |||
image | with image naming an image built from the terragucci image, init writes it into every job of the pipeline and the jobs run in it: check passes, a step prints a file only that image holds, and the root applies | proven | |||
env | with env setting TF_VAR_greeting, init writes it into the pipeline, and the apply job applies the root with that value in its state | proven | |||
local-plan | terragucci plan run on a machine plans each root its glob names against the applied state: the changed root plans its one change, the others none, and the --json envelope lists them all with exit 0 | proven | |||
runner-label | with runner.apply naming a label only a second Forgejo runner serves, init writes it as the runs-on of the apply jobs alone, and the push to main runs check on the runner of the stack and the apply job on the labelled one | proven | |||
pass-secrets | with pass naming a repo secret and a repo variable, init writes their names and never their values into the jobs that plan and apply, and the apply job applies the root with both values in its state | proven | |||
mr-widget | the Terraform widget of a merge request counts the creates, updates and deletes of the plan job report, a replacement as a create and a delete | proven | |||
managed-state-parallelism | roots on GitLab-managed state apply at most 3 at once, and the apply job says so | proven | |||
report-keys | with the bucket keys in CI/CD variables, which the pipeline maps nowhere, the plan job writes report.json to the bucket and the index lists the run | proven | |||
drift-issue | the drift schedule opens one drift issue naming the root and attribute that drifted, updates the same issue when more drifts, and closes it when a run finds none | proven | |||
estate-job | the estate job of the see-every-project page, pasted into .gitlab-ci.yml and run by a pipeline schedule with only its OIDC token, writes estate.html listing the project to the bucket and prints a presigned link | proven | |||
explain-refusal | after a wave is refused, the explain-refusal job of the agent-refused-wave page runs wave-refused on the wave reports and a stand-in agent prints a summary naming the root whose plan moved, and a branch pipeline still runs | proven | |||
gl-comments | the comments schedule answers a Developer merge request note once across two polls, carrying its marker, and never a non-member note | proven | |||
gl-comment-plan | a Developer /terragucci plan note starts a merge request pipeline of the same head, whose plan job passes and updates the plan note | proven | |||
gl-protected-token | with gitlab.token protected, the merge request plan job holds no token and passes, and the comments job posts its plan note and terragucci/plan | proven | |||
gl-mr-apply | with apply.when pull-request, /terragucci apply starts a pipeline on main with the merge token whose mr-apply applies the head and pr-merge merges it | proven | |||
gl-pr-review | with approval pr-review, a Developer approval of the merge request lets the merge commit gated wave apply, and the job names the approver | proven | |||
gl-wave-jobs | with waves.jobs on GitLab a wave waits at one gate, then its share jobs apply their own roots under one approval, used once, and leave no lock behind | proven | |||
gl-comment-agent | a /terragucci agent note starts a pipeline on main whose agent sees no forge token and whose push job commits its change to the branch, refusing one to the pipeline file | proven | |||
gl-review-agent | the comments job starts a review on main whose note flags an unmentioned destroy with no forge token in reach, and the merged wave policy reads its risk | proven | |||
gl-state-versions | on GitLab-managed state, each apply wave logs the state address of the root and the serial GitLab holds after it, and GitLab keeps each of those versions | proven | |||
gl-state-export | terragucci state export of a root on GitLab-managed state asks for a serial, and once someone else approved it writes the version GitLab keeps by that serial, recorded on chant/lifecycle | proven | |||
gl-unlock-state | terragucci unlock-state reads the lock a killed apply left on GitLab-managed state, waits for an approval of its ID, then releases it on the GitLab lock endpoint and records it, and the next wave applies | proven | |||
gl-ephemeral | the copy a pull request gets of a root on GitLab-managed state lives in the state named with the suffix -pr-<n>, apart from the state of the root, and its destroy leaves that state as it was | proven | |||
gl-state-edges | a root that reads the GitLab-managed state of another through terraform_remote_state by its address applies in the wave after it, with its output | proven | |||
oidc | the merge request plan job holds a GitLab id_token for sts.amazonaws.com naming this project, pipeline and job, for the plan role, and the apply job on main one for the apply role; the token is verified against the keys the lab publishes, not traded with STS | proven |
The same setup runs on your laptop. The tutorial boots it with one command and walks through each feature.
Running it yourself
Section titled “Running it yourself”See CONTRIBUTING.md.
These docs count page views and clicks with PostHog. They set no cookies, store nothing in your browser, and send nothing when your browser asks not to be tracked.