# Terraform-generated internal secrets — values with no meaning outside this # deployment, so Terraform owns generating them instead of requiring the operator # to hand-generate and paste each one into terraform.tfvars. # # Every resource here is pinned with a static `keepers` value so `terraform # apply` never regenerates a value already in use — regeneration would be a real # incident (every session invalidated, the app<->DB password mismatched, a # bookmarked dashboard token broken). Rotation is a deliberate edit to a # resource's `keepers` value here, never a side effect of anything else # changing. See docs/architecture/repo-topology-and-infrastructure.md §6. # # The corresponding variable (variables.tf) defaults to "" (Terraform # generates); supply an explicit value only to seed an EXISTING live secret # during a migration onto Terraform (hop-1 cutover runbook Stage 0) — # `terraform plan` must show no change to the local below once the value # matches what's already deployed. # # VAPID is deliberately NOT here: it's an EC keypair (var.vapid_private_key / # vapid_public_key), generated once out-of-band and held as a stable REQUIRED # input. Unlike an opaque token, there is no safe "Terraform regenerates it" # story — regeneration breaks every existing push subscription outright. # # Generated values land in tfstate. tfstate must never be committed or synced in # cleartext — stand up an encrypted backend (or keep these out of Terraform and # supply them as pre-created Swarm secrets instead, per the hop-1 runbook's # hazard #15) before relying on this in a shared or remote-state setup. resource "random_password" "postgres_password" { length = 32 special = false # lands raw (no URL-encoding) in THERMOGRAPH_DATABASE_URL keepers = { rotation = "1" } } resource "random_id" "auth_secret" { byte_length = 32 # matches users.py's own secrets.token_urlsafe(32) fallback shape keepers = { rotation = "1" } } resource "random_password" "metrics_token" { length = 32 special = false # may travel as a query param (?token=...); stay URL-safe keepers = { rotation = "1" } } resource "random_id" "indexnow_key" { byte_length = 16 # 32 hex chars, matching indexnow.py's own secrets.token_hex(16) keepers = { rotation = "1" } } locals { # var.X wins when explicitly supplied (migration: seed the existing live # value); otherwise the Terraform-generated one — see the file header. postgres_password = var.postgres_password != "" ? var.postgres_password : random_password.postgres_password.result auth_secret = var.auth_secret != "" ? var.auth_secret : random_id.auth_secret.b64_url metrics_token = var.metrics_token != "" ? var.metrics_token : random_password.metrics_token.result indexnow_key = var.indexnow_key != "" ? var.indexnow_key : random_id.indexnow_key.hex }