Documentation Index
Fetch the complete documentation index at: https://docs.jacobpevans.com/llms.txt
Use this file to discover all available pages before exploring further.
Static checks belong in pre-commit (mirrored in CI). Credential-requiring operations belong in CI only, via OIDC. aws-vault never appears in hooks, workflows, Makefiles, or scripts.
The placement rules below apply to every Terraform / OpenTofu / Terragrunt repo across the org. Static checks run on every commit locally and are mirrored as a CI job; anything that touches AWS runs in CI only, authenticated with OIDC.
Canonical pattern
| Check | Pre-commit (local + CI mirror) | CI only (OIDC) | Never |
|---|---|---|---|
terraform fmt -check | yes | mirrored | — |
terraform validate (with init -backend=false) | yes | mirrored | — |
tflint | yes | mirrored | — |
terraform_docs | yes | mirrored | — |
gitleaks / detect-private-key | yes | mirrored | — |
| Generic hygiene (whitespace, EOL, YAML, large files) | yes | mirrored | — |
terraform plan | — | yes (OIDC, post via tfcmt) | wrapped in aws-vault |
terraform apply | — | yes (OIDC, gated environment) | wrapped in aws-vault |
trivy / checkov (security) | optional | dedicated job | wrapped in aws-vault |
terragrunt validate / terragrunt plan | — (delete the hook) | yes (OIDC) | as a hook |
Hard rules
No credentials in hooks, ever.aws-vault, doppler, AWS SDK calls, and anything requiring a keychain or injected secret are forbidden in entry: fields, args:, Makefile targets, justfile recipes, and scripts called from any of these. Pre-commit must run from a fresh checkout with no AWS_* env vars set.
terraform validate runs with init -backend=false. No backend creds, no provider creds needed. This is how hashicorp/terraform-provider-aws and opentofu/opentofu run their own pre-commit hooks.
Delete terragrunt-validate and terragrunt-plan hooks entirely. Do not retain them under stages: [manual]. If a developer wants to run terragrunt plan locally, they invoke it from the dev shell outside pre-commit. Matches gruntwork-io/terragrunt practice (zero credentialed hooks).
No stages: [manual] for static checks. If a hook is too slow to run on every commit, fix the hook (add caching, scope to changed files). Hiding it behind stages: [manual] is equivalent to deleting it.
CI auth is OIDC exclusively. Every job that needs AWS access must use permissions: id-token: write and aws-actions/configure-aws-credentials@v4 with role-to-assume. No long-lived AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY in repo secrets.
No continue-on-error: true on format or validation steps. Letting a broken fmt check silently pass defeats the purpose of the check.
aws-vault is allowed only in:
- READMEs and docs (user-facing examples of how to run things manually)
.nixfiles undernix-darwin,nix-home,nix-ai,nix-devenv(aliases, package inputs)agentsmd/permissions/allow/core.json(agent runtime permission — not embedded automation)
CI output is public
GitHub Actions logs and PR comments are world-readable on public repos. Every workflow that handles real credentials or plan output must apply these controls.terraform plan output is sensitive. It can reveal AWS account IDs, ARNs, internal IPv4/IPv6 ranges, internal hostnames, resource names, security-group rules, S3 bucket names, IAM principals, EC2 AMI IDs, Proxmox node names, VM/container counts. Do not stream raw tofu plan -no-color to a run: step without masking. Use tfcmt --patch (compact diff mode) when posting to PRs; gate comments to pull_request from same-repo branches only — never from public forks.
Mask before logging, not after. The first step of every job that handles real creds must ::add-mask:: the AWS account ID and any environment-derived domain or IP prefix. Read the account ID from aws sts get-caller-identity and pipe through ::add-mask:: before any other step runs.
No TF_LOG=DEBUG or TF_LOG=TRACE in CI. Those leak provider request and response bodies — tokens, signed URLs, full request payloads. Set TF_LOG=ERROR or leave unset.
No set -x or set -o xtrace. Bash trace mode echoes every command including variable expansions; masking only fires on stdout content, not the command line itself.
No terraform show -json artifact uploads. Plan binary files (tfplan) contain resolved variable values including sensitive = true strings. Default position: do not upload plan files; re-plan in the apply job.
pull_request_target is forbidden for terraform jobs. Use pull_request (same-repo branches only) or workflow_run for fork-PR plans with output written to a downloadable artifact.
Trivy / Checkov SARIF uploads are safe (rule violations only), but their stdout is not — apply the same masking.
Audit grep
Run before opening any PR that touches hooks, workflows, Makefiles, or scripts:Industry evidence
These patterns are adopted by every major Terraform ecosystem project:terraform-aws-modules/terraform-aws-vpc.pre-commit-config.yaml—fmt/validate/tflint/docson every commit, zero credentialsantonbabenko/pre-commit-terraformREADME — all static hooks useinit -backend=false; plan/apply hooks explicitly documented as “not recommended in pre-commit”hashicorp/setup-terraformREADME — OIDC is the documented path for CI; noaws-vaultanywhere in the actiongruntwork-io/terragrunt— zero credentialed hooks in any example
Anti-patterns
These shapes are violations of the rule, regardless of framing.cd — no nix develop wrapper is needed inside hooks.
Where to go next
CI/CD policy
Marketplace actions, release-please, version pinning, runner choice.
Golden laws
Why credentials never enter hooks and how the boundary is enforced.
aws-vault
Where aws-vault legitimately runs — and where it never does.