Terraform for files.coilysiren.me: asset bucket, ACM cert, CloudFront distribution, Route53 alias #872

Open
opened 2026-08-19 15:24:18 +00:00 by coilyco-ops · 0 comments
Member

Infra half of coilyco-bridge/deploy#738, which asks for files.coilysiren.me on HTTPS as a place to stage assets that the Sirens Dowel lane can link during the 2026-08-19 Temporal stream. That issue describes the AWS resources as CLI calls. This repo already owns most of them in Terraform, so the CLI shape in #738 is wrong for the durable version and this issue is the correction.

Why this is not just a mirror

terraform/aws-inventory/ states it plainly: S3 buckets and the coilysiren.me Route53 zone plus all 17 record sets are managed resources, and Terraform detects drift. So:

  • A new bucket created outside Terraform is unmanaged, and the README's own rule is that "a new bucket gets one line added" to the s3_bucket_names local.
  • A files.coilysiren.me record created by CLI sits outside the managed zone's record set.

Whoever does #738 with the AWS CLI today creates resources this stack then has to import. That is a fine trade for a stream in three hours, but it is a debt and this issue is where it gets paid.

Safety finding that outranks the feature

coilysiren-assets holds the Terraform state for this whole estate. Verified 2026-08-19:

terraform-state/deploy/ser8-signoz-alerts.tfstate
terraform-state/infrastructure/admin-kms.tfstate
terraform-state/infrastructure/aws-iam.tfstate
terraform-state/infrastructure/aws-inventory.tfstate
terraform-state/infrastructure/tailscale.tfstate

#738 already says not to reuse that bucket, but it gives the weak reason (Eco mod archives would go public). The real reason is that opening it would publish five Terraform state files, including the IAM stack's.

Deliverable, not just context: put that in a comment next to the s3_bucket_names local in terraform/aws-inventory/main.tf. This issue will age out and the code will not, and "there is already an assets bucket, why make another" is exactly the shortcut someone reaches for under time pressure. The bucket list is where they will be standing when they think it.

Work

1. Bucket, in terraform/aws-inventory/. Add coilysiren-files to the s3_bucket_names local. Keep block-public-access fully on: with CloudFront and Origin Access Control in front, the bucket never needs to be public, which is the main thing CloudFront buys here beyond TLS.

2. Route53 alias. An aws_route53_record for files.coilysiren.me, A-ALIAS to the distribution, alias hosted zone Z2FDTNDATAQYW2 (a fixed AWS-side constant, not an account value). It joins the 17 managed record sets.

3. ACM certificate, CloudFront distribution, Origin Access Control. The certificate must be requested in us-east-1 regardless of where anything else lives, because CloudFront reads certificates from that region only. That happens to be this stack's region already.

Decisions for ops

None of these have an obvious right answer from where this issue sits. Pick them deliberately and record which way and why, rather than letting the first commit decide.

Decision 1: which stack holds ACM and CloudFront. The aws-inventory README says to keep that stack focused on S3 and Route53 "so the AWS inventory does not turn into a mega-stack," which argues against putting a distribution in it.

  • A new terraform/asset-cdn/, following the existing backend convention (coilysiren-assets, key terraform-state/infrastructure/asset-cdn.tfstate, use_lockfile, encrypt), plus a just terraform-asset-cdn verb and a scripts/k8s/terraform_asset_cdn.py alongside its three siblings. More scaffolding, keeps the stated boundary.
  • Or aws-inventory anyway, on the grounds that one distribution is not a mega-stack and a fourth stack is more overhead than it saves. Cheaper now, erodes the rule.

Decision 2: where the bucket policy lives, and whether it breaks the minimal-import rule. This is the one that needs a real choice. OAC requires an aws_s3_bucket_policy allowing the cloudfront.amazonaws.com service principal s3:GetObject, conditioned on AWS:SourceArn matching the distribution. The aws-inventory README deliberately leaves every bucket sub-resource unmanaged (versioning, encryption, public-access block, lifecycle) so the post-import plan stays clean. A bucket policy would be the first managed sub-resource on any bucket in this repo.

  • Put the policy in the CDN stack next to the distribution whose ARN it references. The dependency reads naturally and aws-inventory keeps its minimal-import property intact. Costs a cross-stack reference to the bucket.
  • Put it in aws-inventory next to the bucket, and amend the README to say the rule is "no sub-resources except where a policy is load bearing." Keeps bucket concerns together, and spends the clean-plan property that was deliberately bought.
  • Manage the policy outside Terraform entirely and say so. Fastest, and the option most likely to rot silently, so take it only with the reason written down.

Whichever way it goes, the README's minimal-import paragraph needs updating to match, because right now it describes an invariant this work is about to change.

Decision 3: permanent or temporary. #738 was written as a stream-day tool with a destroy verb. This repo does not hold temporary things. So either files.coilysiren.me becomes standing infrastructure and the teardown path in #738 stops applying to it, or it dies with the demo and never lands here at all. Deciding this first is worth ten minutes, because it determines whether this issue should be done.

Verified current AWS state

Read 2026-08-19, account 226734851276, us-east-1, AdministratorAccess via SSO:

  • No files.coilysiren.me bucket, no coilysiren-files bucket.
  • No ACM certificates matching coilysiren.
  • Zero CloudFront distributions in the account.
  • Route53 public zone Z06714552N3MO04UBWF33, no files.* record set.
  • Existing buckets are coilysiren-assets and kai-game-backups, which is exactly the s3_bucket_names local, so that list is currently accurate.

Running it

just terraform-aws-inventory plan then apply, per the justfile verb. CI here is config-validation only and does not deploy, so an operator applies this by hand. Terraform is not in any workflow under .forgejo/workflows/.

Scope note

Dowel cannot write to this host. Their Discord grant declares content and message_reference and nothing else, and the lane's Playwright allowlist omits browser_file_upload. The asset host is somewhere Kai stages files and Dowel links them. Do not design an upload path for the agent as part of this.

Sequencing

Independent of today's stream. If #738 ships the CLI version first, this issue becomes terraform import for the bucket, the record, the certificate, and the distribution, on top of Decision 3.

Infra half of [coilyco-bridge/deploy#738](https://forgejo.coilysiren.me/coilyco-bridge/deploy/issues/738), which asks for `files.coilysiren.me` on HTTPS as a place to stage assets that the Sirens Dowel lane can link during the 2026-08-19 Temporal stream. That issue describes the AWS resources as CLI calls. **This repo already owns most of them in Terraform**, so the CLI shape in #738 is wrong for the durable version and this issue is the correction. ## Why this is not just a mirror `terraform/aws-inventory/` states it plainly: S3 buckets and the `coilysiren.me` Route53 zone plus all 17 record sets are **managed resources, and Terraform detects drift**. So: * A new bucket created outside Terraform is unmanaged, and the README's own rule is that "a new bucket gets one line added" to the `s3_bucket_names` local. * A `files.coilysiren.me` record created by CLI sits outside the managed zone's record set. Whoever does #738 with the AWS CLI today creates resources this stack then has to import. That is a fine trade for a stream in three hours, but it is a debt and this issue is where it gets paid. ## Safety finding that outranks the feature **`coilysiren-assets` holds the Terraform state for this whole estate.** Verified 2026-08-19: ``` terraform-state/deploy/ser8-signoz-alerts.tfstate terraform-state/infrastructure/admin-kms.tfstate terraform-state/infrastructure/aws-iam.tfstate terraform-state/infrastructure/aws-inventory.tfstate terraform-state/infrastructure/tailscale.tfstate ``` #738 already says not to reuse that bucket, but it gives the weak reason (Eco mod archives would go public). **The real reason is that opening it would publish five Terraform state files, including the IAM stack's.** **Deliverable, not just context:** put that in a comment next to the `s3_bucket_names` local in `terraform/aws-inventory/main.tf`. This issue will age out and the code will not, and "there is already an assets bucket, why make another" is exactly the shortcut someone reaches for under time pressure. The bucket list is where they will be standing when they think it. ## Work **1. Bucket, in `terraform/aws-inventory/`.** Add `coilysiren-files` to the `s3_bucket_names` local. Keep block-public-access fully on: with CloudFront and Origin Access Control in front, the bucket never needs to be public, which is the main thing CloudFront buys here beyond TLS. **2. Route53 alias.** An `aws_route53_record` for `files.coilysiren.me`, A-ALIAS to the distribution, alias hosted zone `Z2FDTNDATAQYW2` (a fixed AWS-side constant, not an account value). It joins the 17 managed record sets. **3. ACM certificate, CloudFront distribution, Origin Access Control.** The certificate must be requested **in us-east-1** regardless of where anything else lives, because CloudFront reads certificates from that region only. That happens to be this stack's region already. ## Decisions for ops None of these have an obvious right answer from where this issue sits. Pick them deliberately and record which way and why, rather than letting the first commit decide. **Decision 1: which stack holds ACM and CloudFront.** The `aws-inventory` README says to keep that stack focused on S3 and Route53 "so the AWS inventory does not turn into a mega-stack," which argues against putting a distribution in it. * A new `terraform/asset-cdn/`, following the existing backend convention (`coilysiren-assets`, key `terraform-state/infrastructure/asset-cdn.tfstate`, `use_lockfile`, `encrypt`), plus a `just terraform-asset-cdn` verb and a `scripts/k8s/terraform_asset_cdn.py` alongside its three siblings. More scaffolding, keeps the stated boundary. * Or `aws-inventory` anyway, on the grounds that one distribution is not a mega-stack and a fourth stack is more overhead than it saves. Cheaper now, erodes the rule. **Decision 2: where the bucket policy lives, and whether it breaks the minimal-import rule.** This is the one that needs a real choice. OAC requires an `aws_s3_bucket_policy` allowing the `cloudfront.amazonaws.com` service principal `s3:GetObject`, conditioned on `AWS:SourceArn` matching the distribution. The `aws-inventory` README deliberately leaves every bucket sub-resource unmanaged (versioning, encryption, public-access block, lifecycle) so the post-import plan stays clean. **A bucket policy would be the first managed sub-resource on any bucket in this repo.** * Put the policy in the CDN stack next to the distribution whose ARN it references. The dependency reads naturally and `aws-inventory` keeps its minimal-import property intact. Costs a cross-stack reference to the bucket. * Put it in `aws-inventory` next to the bucket, and amend the README to say the rule is "no sub-resources except where a policy is load bearing." Keeps bucket concerns together, and spends the clean-plan property that was deliberately bought. * Manage the policy outside Terraform entirely and say so. Fastest, and the option most likely to rot silently, so take it only with the reason written down. Whichever way it goes, the README's minimal-import paragraph needs updating to match, because right now it describes an invariant this work is about to change. **Decision 3: permanent or temporary.** #738 was written as a stream-day tool with a `destroy` verb. This repo does not hold temporary things. So either `files.coilysiren.me` becomes standing infrastructure and the teardown path in #738 stops applying to it, or it dies with the demo and never lands here at all. Deciding this first is worth ten minutes, because it determines whether this issue should be done. ## Verified current AWS state Read 2026-08-19, account `226734851276`, us-east-1, `AdministratorAccess` via SSO: * No `files.coilysiren.me` bucket, no `coilysiren-files` bucket. * No ACM certificates matching `coilysiren`. * Zero CloudFront distributions in the account. * Route53 public zone `Z06714552N3MO04UBWF33`, no `files.*` record set. * Existing buckets are `coilysiren-assets` and `kai-game-backups`, which is exactly the `s3_bucket_names` local, so that list is currently accurate. ## Running it `just terraform-aws-inventory plan` then `apply`, per the justfile verb. CI here is config-validation only and does not deploy, so an operator applies this by hand. Terraform is not in any workflow under `.forgejo/workflows/`. ## Scope note Dowel cannot write to this host. Their Discord grant declares `content` and `message_reference` and nothing else, and the lane's Playwright allowlist omits `browser_file_upload`. The asset host is somewhere Kai stages files and Dowel links them. Do not design an upload path for the agent as part of this. ## Sequencing Independent of today's stream. If #738 ships the CLI version first, this issue becomes `terraform import` for the bucket, the record, the certificate, and the distribution, on top of Decision 3.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
coilyco-flight-deck/infrastructure#872
No description provided.