For service implementation, CI configuration, or deployment work, also read that service's services/<service>/AGENTS.md, including when editing related files outside its directory.
Infrastructure
Use tofu, never terraform.
Run OpenTofu only through repository just recipes; never invoke tofu directly.
Run just check after OpenTofu edits.
Run just check read before planning.
Agents may run just tofu plan without separate approval.
Review every plan result before apply.
Run just apply only after explicit human approval and only for the reviewed saved tfplan.
Treat apply as remote deployment: provisioners copy files to klops and restart services.
Do not upgrade providers or .terraform.lock.hcl incidentally.
Keep relay input and forwarding default-deny.
Declare every public relay exposure in both the Hetzner firewall and nftables.
Do not expose mail, DNS, or arbitrary ports without an architecture change.
just check mutates files because it runs formatters.
Use just check read [PATH...] for read-only checks.
Local automation
Keep the justfile as a thin task dispatcher; put complex logic in scripts/local.
Write repository scripts in Bash; do not add Python.
Prefer a few action-oriented tasks that accept scopes over many narrow recipes.
Manage Go dev tools as pinned tool dependencies in the owning module using go get -tool <package>@<version>; invoke via go tool <name>, not global binaries or mise shims.
OpenTofu style
Use two-space formatting from just check.
Use snake_case resource, variable, and output names.
Give every variable and output an explicit type and description.
Mark secret inputs sensitive = true.
Set nullable = false when null is not handled.
Prefer relative local modules and pinned external module revisions.
Secrets and state
Never commit or publish secret values, variable-value files, saved plans, state, or generated sensitive files.
Treat OpenTofu state as plaintext secret storage.
Let operator SSH configuration and agents resolve authentication; never hard-code private-key paths.
Services
Define long-running klops containers as repository-managed Quadlets unless a spec says otherwise.
Keep host changes under root and application lifecycle under the designated unprivileged user.
Do not claim a revision is deployed without live deployment evidence.
Prefer registry auto-updates for deployed upstream containers until replacement update management exists.
Preserve selected release tracks; do not silently switch major versions.
Do not disable updates or pin image digests merely to avoid compatibility risk; require explicit approval and an update path.
Keep locally built images and Toad-managed digest rollouts explicit.
Verification
Regenerate Nugu assets with just nugu; do not hand-edit generated palette outputs.
Validate public-site changes with just verify sites <output-directory> at desktop and mobile viewports.
# RULES
For service implementation, CI configuration, or deployment work, also read that service's `services/<service>/AGENTS.md`, including when editing related files outside its directory.
## Infrastructure
- Use `tofu`, never `terraform`.
- Run OpenTofu only through repository `just` recipes; never invoke `tofu` directly.
- Run `just check` after OpenTofu edits.
- Run `just check read` before planning.
- Agents may run `just tofu plan` without separate approval.
- Review every plan result before apply.
- Run `just apply` only after explicit human approval and only for the reviewed saved `tfplan`.
- Treat apply as remote deployment: provisioners copy files to klops and restart services.
- Do not upgrade providers or `.terraform.lock.hcl` incidentally.
- Keep relay input and forwarding default-deny.
- Declare every public relay exposure in both the Hetzner firewall and nftables.
- Do not expose mail, DNS, or arbitrary ports without an architecture change.
`just check` mutates files because it runs formatters.
Use `just check read [PATH...]` for read-only checks.
## Local automation
- Keep the `justfile` as a thin task dispatcher; put complex logic in `scripts/local`.
- Write repository scripts in Bash; do not add Python.
- Prefer a few action-oriented tasks that accept scopes over many narrow recipes.
- Manage Go dev tools as pinned tool dependencies in the owning module using `go get -tool <package>@<version>`; invoke via `go tool <name>`, not global binaries or mise shims.
## OpenTofu style
- Use two-space formatting from `just check`.
- Use `snake_case` resource, variable, and output names.
- Give every variable and output an explicit type and description.
- Mark secret inputs `sensitive = true`.
- Set `nullable = false` when null is not handled.
- Prefer relative local modules and pinned external module revisions.
## Secrets and state
- Never commit or publish secret values, variable-value files, saved plans, state, or generated sensitive files.
- Treat OpenTofu state as plaintext secret storage.
- Let operator SSH configuration and agents resolve authentication; never hard-code private-key paths.
## Services
- Define long-running klops containers as repository-managed Quadlets unless a spec says otherwise.
- Keep host changes under root and application lifecycle under the designated unprivileged user.
- Do not claim a revision is deployed without live deployment evidence.
- Prefer registry auto-updates for deployed upstream containers until replacement update management exists.
- Preserve selected release tracks; do not silently switch major versions.
- Do not disable updates or pin image digests merely to avoid compatibility risk; require explicit approval and an update path.
- Keep locally built images and Toad-managed digest rollouts explicit.
## Verification
- Regenerate Nugu assets with `just nugu`; do not hand-edit generated palette outputs.
- Validate public-site changes with `just verify sites <output-directory>` at desktop and mobile viewports.