Skip to content

Separate environments

llms.txtlists every page for an agent
Optional: hand this page to your coding agentThe steps work by hand too.
Show the whole prompt
Read https://intentius.io/terragucci/guides/separate-environments/.
Check this repo's forge and cloud settings and say whether a branch job can reach the prod apply runner, secrets or role.
Read `environments` in terragucci.yml and the output of `npx terragucci config identities`, then the forge's environments, runners, runner groups, secrets or variables and branch protection through its API or CLI, and each apply role's trust through the cloud's CLI.
Answer each row of the page's table for this forge with what is set, and list each gap with the setting that closes it.
Do not change forge settings, runners, secrets, variables or trust policies yourself.
Never apply, approve (a pull request review or `terragucci approve`), override a policy denial (`terragucci override`), use `--mode apply`, or merge; never touch `.chant/allowed_signers` or `chant/lifecycle`.

This page is optional reading for a repo that applies more than one environment, such as dev and prod. terragucci runs without any of it.

A job from a branch, or from another environment, cannot run on prod’s apply runner, read prod’s secrets or assume prod’s apply role. Most of that is set on the forge and in the cloud. terragucci writes each environment’s apply jobs to match those settings, and checks for mistakes.

A check inside a job catches mistakes, such as a hand-edited workflow or a mistyped runner label. Someone who can edit the workflow can delete the check too, so it is the forge’s settings and the cloud’s trust that enforce the separation.

You need Why
Each environment’s roots named in environments, with its own apply role or identity and, where it has one, its own runner each environment then applies in jobs of its own, which the settings below can tell apart
oidc set up the cloud’s trust can only check a token’s claims

Each row is a question, the setting that answers it, and who enforces that setting. Pick your forge; the other forge tabs on the site follow.

Question Setting Enforced by
Which jobs may use the apply runners a runner group for prod’s apply runners, open to selected repositories and, on plans that offer it, selected workflows. A label alone keeps nothing out: a branch can edit its workflow to ask for it forge
Which refs may run an environment’s apply an environment whose deployment branches are the default branch alone, with required reviewers if you want a person on each run. On a private repository, environments depend on your plan forge
Which jobs see an environment’s secrets environment secrets: only a job in that environment, past its branch rule, reads them. A repository secret reaches every workflow run in the repository, a branch’s push included forge
What the cloud trust can require the environment’s subject, repo:<owner>/<repo>:environment:<name> (GitHub’s OIDC claims), in AWS, GCP or Azure; Trust per environment has each cloud’s form cloud
What terragucci renders each environment’s apply jobs with its runner as runs-on (labels or a runner group), its roles, environment: <forge_environment> and its oidc.audience terragucci
What terragucci checks config check tips for shared runners, subjects and roles; check-job at the start of each apply job; config identities prints the subject and audience each role should trust, and forge check reads the environments, their branch rules, the apply runner group and each role’s trust and says which differ terragucci

The environment’s subject does not name a ref, so the deployment branch rule is what keeps another branch’s job from taking it.

config identities prints, for each environment, the subject, audience, forge environment and runner of its plan, deciding, apply and drift jobs. The Terraform provider’s terragucci_environments and terragucci_trust_policy give the same values to the resources that make your roles, environments and runner groups.

A runner that holds credentials of its own gives them to every job it takes, with no token and no trust condition in the way. Keep these off the hosts that run apply jobs, and off every other runner:

Credential Where it comes from Keep it off by
An instance’s role an AWS instance profile, a GCP VM’s service account, an Azure managed identity giving the runner’s machine no role, or one that reaches nothing terragucci manages
The metadata endpoint 169.254.169.254 and metadata.google.internal, which hand out the instance’s credentials blocking it from the job containers; on AWS, requiring IMDSv2 with a hop limit of 1 keeps a container on a bridge network from reaching it
Mounted keys a volume the runner mounts into each job, or a key file on a host that runs jobs without a container mounting nothing the jobs do not need; Forgejo’s container.valid_volumes left empty
Cloud config files ~/.aws, ~/.config/gcloud, ~/.azure in the runner user’s home a runner user with no cloud login

A runner that keeps files or processes between jobs can also carry one job’s state into the next. The tip terragucci-persistent-runner names apply runners that the plan jobs, or another environment’s apply jobs, could also take. Give them labels of their own, and use runners that start clean for each job, such as Forgejo’s ephemeral runners.

A Forgejo token’s subject names the repository and the ref, nothing else. Two environments that apply from the same branch carry the same subject, so no cloud trust can tell their jobs apart: prod’s apply role trusts dev’s apply job as well. The runner and the secrets cannot be held to one environment either, as the table shows. Two ways out:

Way How What it separates
A repository per environment each environment’s roots in a repository of its own, with its own terragucci.yml, a runner registered to that repository and its own secrets; each apply role trusts its own repository’s subject runner, secrets and role
A protected branch per environment apply.branches maps a branch such as release to prod’s roots; protect release, and have prod’s apply role trust ref:refs/heads/release alone the role; the runner and the repository’s secrets stay open to every branch’s workflow

Settings drift after setup: an environment’s branch rule gets widened, a runner stops being protected, a role’s trust grows a wildcard. terragucci forge check reads the forge’s and each cloud’s settings with read-only credentials, compares them with what config identities expects, and exits 1 when one differs. No pipeline job runs it; run it yourself, or from a scheduled job you add. The terragucci_forge_check data source returns the same rows for a Terraform check block.

The prompt at the top of this page asks a coding agent to read the repo’s forge and cloud settings, answer each row of the table, and list the gaps. It changes nothing; you make the changes it lists.

terragucciHelp setting it up: Intentius

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.